00001
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <string.h>
00015 #include <sys/utsname.h>
00016 #include <ctype.h>
00017 #include <errno.h>
00018 #include <fnmatch.h>
00019
00020 #include "op_cpu_type.h"
00021 #include "op_hw_specific.h"
00022
00023 struct cpu_descr {
00024 char const * pretty;
00025 char const * name;
00026 op_cpu cpu;
00027 unsigned int nr_counters;
00028 };
00029
00030 static struct cpu_descr const cpu_descrs[MAX_CPU_TYPE] = {
00031 { "Pentium Pro", "i386/ppro", CPU_PPRO, 2 },
00032 { "PII", "i386/pii", CPU_PII, 2 },
00033 { "PIII", "i386/piii", CPU_PIII, 2 },
00034 { "Athlon", "i386/athlon", CPU_ATHLON, 4 },
00035 { "CPU with timer interrupt", "timer", CPU_TIMER_INT, 1 },
00036 { "CPU with RTC device", "rtc", CPU_RTC, 1 },
00037 { "P4 / Xeon", "i386/p4", CPU_P4, 8 },
00038 { "IA64", "ia64/ia64", CPU_IA64, 4 },
00039 { "Itanium", "ia64/itanium", CPU_IA64_1, 4 },
00040 { "Itanium 2", "ia64/itanium2", CPU_IA64_2, 4 },
00041 { "AMD64 processors", "x86-64/hammer", CPU_HAMMER, 4 },
00042 { "P4 / Xeon with 2 hyper-threads", "i386/p4-ht", CPU_P4_HT2, 4 },
00043 { "Alpha EV4", "alpha/ev4", CPU_AXP_EV4, 2 },
00044 { "Alpha EV5", "alpha/ev5", CPU_AXP_EV5, 3 },
00045 { "Alpha PCA56", "alpha/pca56", CPU_AXP_PCA56, 3 },
00046 { "Alpha EV6", "alpha/ev6", CPU_AXP_EV6, 2 },
00047 { "Alpha EV67", "alpha/ev67", CPU_AXP_EV67, 20 },
00048 { "Pentium M (P6 core)", "i386/p6_mobile", CPU_P6_MOBILE, 2 },
00049 { "ARM/XScale PMU1", "arm/xscale1", CPU_ARM_XSCALE1, 3 },
00050 { "ARM/XScale PMU2", "arm/xscale2", CPU_ARM_XSCALE2, 5 },
00051 { "ppc64 POWER4", "ppc64/power4", CPU_PPC64_POWER4, 8 },
00052 { "ppc64 POWER5", "ppc64/power5", CPU_PPC64_POWER5, 6 },
00053 { "ppc64 POWER5+", "ppc64/power5+", CPU_PPC64_POWER5p, 6 },
00054 { "ppc64 970", "ppc64/970", CPU_PPC64_970, 8 },
00055 { "MIPS 20K", "mips/20K", CPU_MIPS_20K, 1},
00056 { "MIPS 24K", "mips/24K", CPU_MIPS_24K, 2},
00057 { "MIPS 25K", "mips/25K", CPU_MIPS_25K, 2},
00058 { "MIPS 34K", "mips/34K", CPU_MIPS_34K, 2},
00059 { "MIPS 5K", "mips/5K", CPU_MIPS_5K, 2},
00060 { "MIPS R10000", "mips/r10000", CPU_MIPS_R10000, 2 },
00061 { "MIPS R12000", "mips/r12000", CPU_MIPS_R12000, 4 },
00062 { "QED RM7000", "mips/rm7000", CPU_MIPS_RM7000, 1 },
00063 { "PMC-Sierra RM9000", "mips/rm9000", CPU_MIPS_RM9000, 2 },
00064 { "Sibyte SB1", "mips/sb1", CPU_MIPS_SB1, 4 },
00065 { "NEC VR5432", "mips/vr5432", CPU_MIPS_VR5432, 2 },
00066 { "NEC VR5500", "mips/vr5500", CPU_MIPS_VR5500, 2 },
00067 { "e500", "ppc/e500", CPU_PPC_E500, 4 },
00068 { "e500v2", "ppc/e500v2", CPU_PPC_E500_2, 4 },
00069 { "Core Solo / Duo", "i386/core", CPU_CORE, 2 },
00070 { "PowerPC G4", "ppc/7450", CPU_PPC_7450, 6 },
00071 { "Core 2", "i386/core_2", CPU_CORE_2, 2 },
00072 { "ppc64 POWER6", "ppc64/power6", CPU_PPC64_POWER6, 4 },
00073 { "ppc64 970MP", "ppc64/970MP", CPU_PPC64_970MP, 8 },
00074 { "ppc64 Cell Broadband Engine", "ppc64/cell-be", CPU_PPC64_CELL, 8 },
00075 { "AMD64 family10", "x86-64/family10", CPU_FAMILY10, 4 },
00076 { "ppc64 PA6T", "ppc64/pa6t", CPU_PPC64_PA6T, 6 },
00077 { "ARM 11MPCore", "arm/mpcore", CPU_ARM_MPCORE, 2 },
00078 { "ARM V6 PMU", "arm/armv6", CPU_ARM_V6, 3 },
00079 { "ppc64 POWER5++", "ppc64/power5++", CPU_PPC64_POWER5pp, 6 },
00080 { "e300", "ppc/e300", CPU_PPC_E300, 4 },
00081 { "AVR32", "avr32", CPU_AVR32, 3 },
00082 { "ARM Cortex-A8", "arm/armv7", CPU_ARM_V7, 5 },
00083 { "Intel Architectural Perfmon", "i386/arch_perfmon", CPU_ARCH_PERFMON, 0},
00084 { "AMD64 family11h", "x86-64/family11h", CPU_FAMILY11H, 4 },
00085 { "ppc64 POWER7", "ppc64/power7", CPU_PPC64_POWER7, 6 },
00086 { "ppc64 compat version 1", "ppc64/ibm-compat-v1", CPU_PPC64_IBM_COMPAT_V1, 4 },
00087 { "Intel Core/i7", "i386/core_i7", CPU_CORE_I7, 4 },
00088 { "Intel Atom", "i386/atom", CPU_ATOM, 2 },
00089 { "Loongson2", "mips/loongson2", CPU_MIPS_LOONGSON2, 2 },
00090 { "Intel Nehalem microarchitecture", "i386/nehalem", CPU_NEHALEM, 4 },
00091 { "ARM Cortex-A9", "arm/armv7-ca9", CPU_ARM_V7_CA9, 7 },
00092 { "MIPS 74K", "mips/74K", CPU_MIPS_74K, 4},
00093 { "MIPS 1004K", "mips/1004K", CPU_MIPS_1004K, 2},
00094 { "AMD64 family12h", "x86-64/family12h", CPU_FAMILY12H, 4 },
00095 { "AMD64 family14h", "x86-64/family14h", CPU_FAMILY14H, 4 },
00096 { "AMD64 family15h", "x86-64/family15h", CPU_FAMILY15H, 6 },
00097 { "Intel Westmere microarchitecture", "i386/westmere", CPU_WESTMERE, 4 },
00098 { "ARMv7 Scorpion", "arm/armv7-scorpion", CPU_ARM_SCORPION, 5 },
00099 { "ARMv7 ScorpionMP", "arm/armv7-scorpionmp", CPU_ARM_SCORPIONMP, 5 },
00100 { "Intel Sandy Bridge microarchitecture", "i386/sandybridge", CPU_SANDYBRIDGE, 8 },
00101 { "TILE64", "tile/tile64", CPU_TILE_TILE64, 2 },
00102 { "TILEPro", "tile/tilepro", CPU_TILE_TILEPRO, 4 },
00103 { "TILE-GX", "tile/tilegx", CPU_TILE_TILEGX, 4 },
00104 { "IBM System z10", "s390/z10", CPU_S390_Z10, 1 },
00105 { "IBM zEnterprise z196", "s390/z196", CPU_S390_Z196, 1 },
00106 { "Intel Ivy Bridge microarchitecture", "i386/ivybridge", CPU_IVYBRIDGE, 8 },
00107 { "ARM Cortex-A5", "arm/armv7-ca5", CPU_ARM_V7_CA5, 3 },
00108 { "ARM Cortex-A7", "arm/armv7-ca7", CPU_ARM_V7_CA7, 5 },
00109 { "ARM Cortex-A15", "arm/armv7-ca15", CPU_ARM_V7_CA15, 7 },
00110 };
00111
00112 static size_t const nr_cpu_descrs = sizeof(cpu_descrs) / sizeof(struct cpu_descr);
00113
00114 static char * _get_cpuinfo_cpu_type_line(char * buf, int len, const char * prefix, int token)
00115 {
00116 char * ret = NULL;
00117 char * end = NULL;
00118 int prefix_len = strlen(prefix);
00119 FILE * fp = fopen("/proc/cpuinfo", "r");
00120
00121 if (!fp) {
00122 perror("Unable to open /proc/cpuinfo\n");
00123 return ret;
00124 }
00125
00126 memset(buf, 0, len);
00127
00128 while (!ret) {
00129 if (fgets(buf, len, fp) == NULL) {
00130 fprintf(stderr, "Did not find processor type in /proc/cpuinfo.\n");
00131 break;
00132 }
00133 if (!strncmp(buf, prefix, prefix_len)) {
00134 ret = buf + prefix_len;
00135
00136 while (*ret && (*ret == ':' || isspace(*ret)))
00137 ++ret;
00138 buf = ret;
00139
00140
00141 if (token == 0) {
00142
00143 end = buf + strlen(buf) - 1;
00144 while (isspace(*end))
00145 --end;
00146 *(++end) = '\0';
00147 break;
00148 } else {
00149
00150 while (*buf && !isspace(*buf))
00151 ++buf;
00152
00153 *buf = '\0';
00154 break;
00155 }
00156 }
00157 }
00158
00159 fclose(fp);
00160 return ret;
00161 }
00162
00163 static char * _get_cpuinfo_cpu_type(char * buf, int len, const char * prefix)
00164 {
00165 return _get_cpuinfo_cpu_type_line(buf, len, prefix, 1);
00166 }
00167
00168 static op_cpu _get_ppc64_cpu_type(void)
00169 {
00170 int i;
00171 size_t len;
00172 char line[100], cpu_type_str[64], cpu_name_lowercase[64], * cpu_name;
00173
00174 cpu_name = _get_cpuinfo_cpu_type(line, 100, "cpu");
00175 if (!cpu_name)
00176 return CPU_NO_GOOD;
00177
00178 len = strlen(cpu_name);
00179 for (i = 0; i < (int)len ; i++)
00180 cpu_name_lowercase[i] = tolower(cpu_name[i]);
00181
00182 cpu_type_str[0] = '\0';
00183 strcat(cpu_type_str, "ppc64/");
00184 strncat(cpu_type_str, cpu_name_lowercase, len);
00185 return op_get_cpu_number(cpu_type_str);
00186 }
00187
00188 static op_cpu _get_arm_cpu_type(void)
00189 {
00190 unsigned long cpuid, vendorid;
00191 char line[100];
00192 char * cpu_part, * cpu_implementer;
00193
00194 cpu_implementer = _get_cpuinfo_cpu_type(line, 100, "CPU implementer");
00195 if (!cpu_implementer)
00196 return CPU_NO_GOOD;
00197
00198 errno = 0;
00199 vendorid = strtoul(cpu_implementer, NULL, 16);
00200 if (errno) {
00201 fprintf(stderr, "Unable to parse CPU implementer %s\n", cpu_implementer);
00202 return CPU_NO_GOOD;
00203 }
00204
00205 cpu_part = _get_cpuinfo_cpu_type(line, 100, "CPU part");
00206 if (!cpu_part)
00207 return CPU_NO_GOOD;
00208
00209 errno = 0;
00210 cpuid = strtoul(cpu_part, NULL, 16);
00211 if (errno) {
00212 fprintf(stderr, "Unable to parse CPU part %s\n", cpu_part);
00213 return CPU_NO_GOOD;
00214 }
00215
00216 if (vendorid == 0x41) {
00217 switch (cpuid) {
00218 case 0xb36:
00219 case 0xb56:
00220 case 0xb76:
00221 return op_get_cpu_number("arm/armv6");
00222 case 0xb02:
00223 return op_get_cpu_number("arm/mpcore");
00224 case 0xc05:
00225 return op_get_cpu_number("arm/armv7-ca5");
00226 case 0xc07:
00227 return op_get_cpu_number("arm/armv7-ca7");
00228 case 0xc08:
00229 return op_get_cpu_number("arm/armv7");
00230 case 0xc09:
00231 return op_get_cpu_number("arm/armv7-ca9");
00232 case 0xc0f:
00233 return op_get_cpu_number("arm/armv7-ca15");
00234 }
00235 } else if (vendorid == 0x69) {
00236 switch (cpuid >> 9) {
00237 case 1:
00238 return op_get_cpu_number("arm/xscale1");
00239 case 2:
00240 return op_get_cpu_number("arm/xscale2");
00241 }
00242 }
00243
00244 return CPU_NO_GOOD;
00245 }
00246
00247 static op_cpu _get_tile_cpu_type(void)
00248 {
00249 int i;
00250 size_t len;
00251 char line[100], cpu_type_str[64], cpu_name_lowercase[64], * cpu_name;
00252
00253 cpu_name = _get_cpuinfo_cpu_type(line, 100, "model name");
00254 if (!cpu_name)
00255 return CPU_NO_GOOD;
00256
00257 len = strlen(cpu_name);
00258 for (i = 0; i < (int)len ; i++)
00259 cpu_name_lowercase[i] = tolower(cpu_name[i]);
00260
00261 cpu_type_str[0] = '\0';
00262 strcat(cpu_type_str, "tile/");
00263 strncat(cpu_type_str, cpu_name_lowercase, len);
00264 return op_get_cpu_number(cpu_type_str);
00265 }
00266
00267 #if defined(__x86_64__) || defined(__i386__)
00268 int op_is_cpu_vendor(char * vendor)
00269 {
00270 return cpuid_vendor(vendor);
00271 }
00272
00273 static unsigned cpuid_eax(unsigned func)
00274 {
00275 cpuid_data d;
00276
00277 cpuid(func, &d);
00278 return d.eax;
00279 }
00280
00281 static inline int perfmon_available(void)
00282 {
00283 unsigned eax;
00284 if (cpuid_eax(0) < 10)
00285 return 0;
00286 eax = cpuid_eax(10);
00287 if ((eax & 0xff) == 0)
00288 return 0;
00289 return (eax >> 8) & 0xff;
00290 }
00291
00292 static int cpu_info_number(char *name, unsigned long *number)
00293 {
00294 char buf[100];
00295 char *end;
00296
00297 if (!_get_cpuinfo_cpu_type(buf, sizeof buf, name))
00298 return 0;
00299 *number = strtoul(buf, &end, 0);
00300 return end > buf;
00301 }
00302
00303 static op_cpu _get_intel_cpu_type(void)
00304 {
00305 unsigned eax, family, model;
00306
00307 if (perfmon_available())
00308 return op_cpu_specific_type(CPU_ARCH_PERFMON);
00309
00310
00311 eax = cpuid_signature();
00312 family = cpu_family(eax);
00313 model = cpu_model(eax);
00314
00315 if (family == 6) {
00316
00317 switch (model) {
00318 case 0 ... 2:
00319 return op_get_cpu_number("i386/ppro");
00320 case 3 ... 5:
00321 return op_get_cpu_number("i386/pii");
00322 case 6 ... 8:
00323 case 10 ... 11:
00324 return op_get_cpu_number("i386/piii");
00325 case 9:
00326 case 13:
00327 return op_get_cpu_number("i386/p6_mobile");
00328 }
00329 } else if (family == 15) {
00330 unsigned long siblings;
00331
00332
00333 if (model > 6 || model == 5)
00334 return CPU_NO_GOOD;
00335 if (!cpu_info_number("siblings", &siblings) ||
00336 siblings == 1)
00337 return op_get_cpu_number("i386/p4");
00338 if (siblings == 2)
00339 return op_get_cpu_number("i386/p4-ht");
00340 }
00341 return CPU_NO_GOOD;
00342 }
00343
00344 static op_cpu _get_amd_cpu_type(void)
00345 {
00346 unsigned eax, family, model;
00347 op_cpu ret = CPU_NO_GOOD;
00348
00349 eax = cpuid_signature();
00350 family = cpu_family(eax);
00351 model = cpu_model(eax);
00352
00353 switch (family) {
00354 case 0x0f:
00355 ret = op_get_cpu_number("x86-64/hammer");
00356 break;
00357 case 0x10:
00358 ret = op_get_cpu_number("x86-64/family10");
00359 break;
00360 case 0x11:
00361 ret = op_get_cpu_number("x86-64/family11h");
00362 break;
00363 case 0x12:
00364 ret = op_get_cpu_number("x86-64/family12h");
00365 break;
00366 case 0x14:
00367 ret = op_get_cpu_number("x86-64/family14h");
00368 break;
00369 case 0x15:
00370 switch (model) {
00371 case 0x00 ... 0x0f:
00372 ret = op_get_cpu_number("x86-64/family15h");
00373 break;
00374 default:
00375 break;
00376 }
00377 break;
00378 default:
00379 break;
00380 }
00381
00382 return ret;
00383 }
00384
00385 static op_cpu _get_x86_64_cpu_type(void)
00386 {
00387 op_cpu ret = CPU_NO_GOOD;
00388
00389 if (cpuid_vendor("GenuineIntel")) {
00390 ret = _get_intel_cpu_type();
00391 } else if (cpuid_vendor("AuthenticAMD")) {
00392 ret = _get_amd_cpu_type();
00393 }
00394
00395 return ret;
00396 }
00397
00398 #else
00399 static op_cpu _get_x86_64_cpu_type(void)
00400 {
00401 return CPU_NO_GOOD;
00402 }
00403 #endif
00404
00405 struct mips_cpu_descr
00406 {
00407 const char * key;
00408 const char * value;
00409 };
00410
00411 static struct mips_cpu_descr mips_cpu_descrs[] = {
00412 { .key = "MIPS 5Kc", .value = "mips/5K" },
00413 { .key = "MIPS 20Kc", .value = "mips/20K" },
00414 { .key = "MIPS 24Kc", .value = "mips/24K" },
00415 { .key = "MIPS 25Kc", .value = "mips/25K" },
00416 { .key = "MIPS 34Kc", .value = "mips/34K" },
00417 { .key = "MIPS 74Kc", .value = "mips/74K" },
00418 { .key = "MIPS M14Kc", .value = "mips/M14Kc" },
00419 { .key = "RM9000", .value = "mips/rm9000" },
00420 { .key = "R10000", .value = "mips/r10000" },
00421 { .key = "R12000", .value = "mips/r12000" },
00422 { .key = "R14000", .value = "mips/r12000" },
00423 { .key = "ICT Loongson-2", .value = "mips/loongson2" },
00424 { .key = NULL, .value = NULL }
00425 };
00426
00427 static const char * _get_mips_op_name(const char * key)
00428 {
00429 struct mips_cpu_descr * p_it = mips_cpu_descrs;
00430 size_t len;
00431
00432
00433 while (p_it->key != NULL) {
00434 len = strlen(p_it->key);
00435 if (0 == strncmp(key, p_it->key, len))
00436 return p_it->value;
00437 ++p_it;
00438 }
00439 return NULL;
00440 }
00441
00442 static op_cpu _get_mips_cpu_type(void)
00443 {
00444 char line[100];
00445 char * cpu_model;
00446 const char * op_name = NULL;
00447
00448 cpu_model = _get_cpuinfo_cpu_type_line(line, 100, "cpu model", 0);
00449 if (!cpu_model)
00450 return CPU_NO_GOOD;
00451
00452 op_name = _get_mips_op_name(cpu_model);
00453
00454 if (op_name)
00455 return op_get_cpu_number(op_name);
00456 return CPU_NO_GOOD;
00457 }
00458
00459 static op_cpu __get_cpu_type_alt_method(void)
00460 {
00461 struct utsname uname_info;
00462 if (uname(&uname_info) < 0) {
00463 perror("uname failed");
00464 return CPU_NO_GOOD;
00465 }
00466 if (strncmp(uname_info.machine, "x86_64", 6) == 0 ||
00467 fnmatch("i?86", uname_info.machine, 0) == 0) {
00468 return _get_x86_64_cpu_type();
00469 }
00470 if (strncmp(uname_info.machine, "ppc64", 5) == 0) {
00471 return _get_ppc64_cpu_type();
00472 }
00473 if (strncmp(uname_info.machine, "arm", 3) == 0) {
00474 return _get_arm_cpu_type();
00475 }
00476 if (strncmp(uname_info.machine, "tile", 4) == 0) {
00477 return _get_tile_cpu_type();
00478 }
00479 if (strncmp(uname_info.machine, "mips", 4) == 0) {
00480 return _get_mips_cpu_type();
00481 }
00482 return CPU_NO_GOOD;
00483 }
00484
00485 int op_cpu_variations(op_cpu cpu_type)
00486 {
00487 switch (cpu_type) {
00488 case CPU_ARCH_PERFMON:
00489 return 1;
00490 default:
00491 return 0;
00492 }
00493 }
00494
00495
00496 op_cpu op_cpu_base_type(op_cpu cpu_type)
00497 {
00498
00499 switch (cpu_type) {
00500 case CPU_CORE_2:
00501 case CPU_CORE_I7:
00502 case CPU_ATOM:
00503 case CPU_NEHALEM:
00504 case CPU_WESTMERE:
00505 case CPU_SANDYBRIDGE:
00506 case CPU_IVYBRIDGE:
00507 return CPU_ARCH_PERFMON;
00508 default:
00509
00510 return cpu_type;
00511 }
00512 }
00513
00514 op_cpu op_get_cpu_type(void)
00515 {
00516 int cpu_type = CPU_NO_GOOD;
00517 char str[100];
00518 FILE * fp;
00519
00520 fp = fopen("/proc/sys/dev/oprofile/cpu_type", "r");
00521 if (!fp) {
00522
00523 fp = fopen("/dev/oprofile/cpu_type", "r");
00524 if (!fp) {
00525 if ((cpu_type = __get_cpu_type_alt_method()) == CPU_NO_GOOD) {
00526 fprintf(stderr, "Unable to open cpu_type file for reading\n");
00527 fprintf(stderr, "Make sure you have done opcontrol --init\n");
00528 }
00529 return cpu_type;
00530 }
00531 }
00532
00533 if (!fgets(str, 99, fp)) {
00534 fprintf(stderr, "Could not read cpu type.\n");
00535 fclose(fp);
00536 return cpu_type;
00537 }
00538
00539 cpu_type = op_get_cpu_number(str);
00540
00541 if (op_cpu_variations(cpu_type))
00542 cpu_type = op_cpu_specific_type(cpu_type);
00543
00544 fclose(fp);
00545
00546 return cpu_type;
00547 }
00548
00549
00550 op_cpu op_get_cpu_number(char const * cpu_string)
00551 {
00552 int cpu_type = CPU_NO_GOOD;
00553 size_t i;
00554
00555 for (i = 0; i < nr_cpu_descrs; ++i) {
00556 if (!strcmp(cpu_descrs[i].name, cpu_string)) {
00557 cpu_type = cpu_descrs[i].cpu;
00558 break;
00559 }
00560 }
00561
00562
00563 if (cpu_type == CPU_NO_GOOD)
00564 sscanf(cpu_string, "%d\n", &cpu_type);
00565
00566 if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
00567 cpu_type = CPU_NO_GOOD;
00568
00569 return cpu_type;
00570 }
00571
00572
00573 char const * op_get_cpu_type_str(op_cpu cpu_type)
00574 {
00575 if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
00576 return "invalid cpu type";
00577
00578 return cpu_descrs[cpu_type].pretty;
00579 }
00580
00581
00582 char const * op_get_cpu_name(op_cpu cpu_type)
00583 {
00584 if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
00585 return "invalid cpu type";
00586
00587 return cpu_descrs[cpu_type].name;
00588 }
00589
00590
00591 int op_get_nr_counters(op_cpu cpu_type)
00592 {
00593 int cnt;
00594
00595 if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
00596 return 0;
00597
00598 cnt = arch_num_counters(cpu_type);
00599 if (cnt >= 0)
00600 return cnt;
00601
00602 return op_cpu_has_timer_fs()
00603 ? cpu_descrs[cpu_type].nr_counters + 1
00604 : cpu_descrs[cpu_type].nr_counters;
00605 }
00606
00607 int op_cpu_has_timer_fs(void)
00608 {
00609 static int cached_has_timer_fs_p = -1;
00610 FILE * fp;
00611
00612 if (cached_has_timer_fs_p != -1)
00613 return cached_has_timer_fs_p;
00614
00615 fp = fopen("/dev/oprofile/timer", "r");
00616 cached_has_timer_fs_p = !!fp;
00617 if (fp)
00618 fclose(fp);
00619
00620 return cached_has_timer_fs_p;
00621 }