blob: 44d6809db8326a99ce16aab08d6eeb4342bcbcb9 [file] [log] [blame]
/**
* @file op_cpu_type.c
* CPU type determination
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon
* @author Philippe Elie
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <errno.h>
#include <fnmatch.h>
#include <elf.h>
#include <link.h>
#include "op_cpu_type.h"
#include "op_hw_specific.h"
struct cpu_descr {
char const * pretty;
char const * name;
op_cpu cpu;
unsigned int nr_counters;
};
static struct cpu_descr const cpu_descrs[MAX_CPU_TYPE] = {
{ "Pentium Pro", "i386/ppro", CPU_PPRO, 2 },
{ "PII", "i386/pii", CPU_PII, 2 },
{ "PIII", "i386/piii", CPU_PIII, 2 },
{ "Athlon", "i386/athlon", CPU_ATHLON, 4 },
{ "CPU with timer interrupt", "timer", CPU_TIMER_INT, 1 },
{ "CPU with RTC device", "rtc", CPU_RTC, 1 },
{ "P4 / Xeon", "i386/p4", CPU_P4, 8 },
{ "IA64", "ia64/ia64", CPU_IA64, 4 },
{ "Itanium", "ia64/itanium", CPU_IA64_1, 4 },
{ "Itanium 2", "ia64/itanium2", CPU_IA64_2, 4 },
{ "AMD64 processors", "x86-64/hammer", CPU_HAMMER, 4 },
{ "P4 / Xeon with 2 hyper-threads", "i386/p4-ht", CPU_P4_HT2, 4 },
{ "Alpha EV4", "alpha/ev4", CPU_AXP_EV4, 2 },
{ "Alpha EV5", "alpha/ev5", CPU_AXP_EV5, 3 },
{ "Alpha PCA56", "alpha/pca56", CPU_AXP_PCA56, 3 },
{ "Alpha EV6", "alpha/ev6", CPU_AXP_EV6, 2 },
{ "Alpha EV67", "alpha/ev67", CPU_AXP_EV67, 20 },
{ "Pentium M (P6 core)", "i386/p6_mobile", CPU_P6_MOBILE, 2 },
{ "ARM/XScale PMU1", "arm/xscale1", CPU_ARM_XSCALE1, 3 },
{ "ARM/XScale PMU2", "arm/xscale2", CPU_ARM_XSCALE2, 5 },
{ "ppc64 POWER4", "ppc64/power4", CPU_PPC64_POWER4, 8 },
{ "ppc64 POWER5", "ppc64/power5", CPU_PPC64_POWER5, 6 },
{ "ppc64 POWER5+", "ppc64/power5+", CPU_PPC64_POWER5p, 6 },
{ "ppc64 970", "ppc64/970", CPU_PPC64_970, 8 },
{ "MIPS 20K", "mips/20K", CPU_MIPS_20K, 1},
{ "MIPS 24K", "mips/24K", CPU_MIPS_24K, 2},
{ "MIPS 25K", "mips/25K", CPU_MIPS_25K, 2},
{ "MIPS 34K", "mips/34K", CPU_MIPS_34K, 2},
{ "MIPS 5K", "mips/5K", CPU_MIPS_5K, 2},
{ "MIPS R10000", "mips/r10000", CPU_MIPS_R10000, 2 },
{ "MIPS R12000", "mips/r12000", CPU_MIPS_R12000, 4 },
{ "QED RM7000", "mips/rm7000", CPU_MIPS_RM7000, 1 },
{ "PMC-Sierra RM9000", "mips/rm9000", CPU_MIPS_RM9000, 2 },
{ "Sibyte SB1", "mips/sb1", CPU_MIPS_SB1, 4 },
{ "NEC VR5432", "mips/vr5432", CPU_MIPS_VR5432, 2 },
{ "NEC VR5500", "mips/vr5500", CPU_MIPS_VR5500, 2 },
{ "e500", "ppc/e500", CPU_PPC_E500, 4 },
{ "e500v2", "ppc/e500v2", CPU_PPC_E500_2, 4 },
{ "Core Solo / Duo", "i386/core", CPU_CORE, 2 },
{ "PowerPC G4", "ppc/7450", CPU_PPC_7450, 6 },
{ "Core 2", "i386/core_2", CPU_CORE_2, 2 },
{ "ppc64 POWER6", "ppc64/power6", CPU_PPC64_POWER6, 4 },
{ "ppc64 970MP", "ppc64/970MP", CPU_PPC64_970MP, 8 },
{ "ppc64 Cell Broadband Engine", "ppc64/cell-be", CPU_PPC64_CELL, 8 },
{ "AMD64 family10", "x86-64/family10", CPU_FAMILY10, 4 },
{ "ppc64 PA6T", "ppc64/pa6t", CPU_PPC64_PA6T, 6 },
{ "ARM 11MPCore", "arm/mpcore", CPU_ARM_MPCORE, 2 },
{ "ARM V6 PMU", "arm/armv6", CPU_ARM_V6, 3 },
{ "ppc64 POWER5++", "ppc64/power5++", CPU_PPC64_POWER5pp, 6 },
{ "e300", "ppc/e300", CPU_PPC_E300, 4 },
{ "AVR32", "avr32", CPU_AVR32, 3 },
{ "ARM Cortex-A8", "arm/armv7", CPU_ARM_V7, 5 },
{ "Intel Architectural Perfmon", "i386/arch_perfmon", CPU_ARCH_PERFMON, 0},
{ "AMD64 family11h", "x86-64/family11h", CPU_FAMILY11H, 4 },
{ "ppc64 POWER7", "ppc64/power7", CPU_PPC64_POWER7, 6 },
{ "ppc64 compat version 1", "ppc64/ibm-compat-v1", CPU_PPC64_IBM_COMPAT_V1, 4 },
{ "Intel Core/i7", "i386/core_i7", CPU_CORE_I7, 4 },
{ "Intel Atom", "i386/atom", CPU_ATOM, 2 },
{ "Loongson2", "mips/loongson2", CPU_MIPS_LOONGSON2, 2 },
{ "Intel Nehalem microarchitecture", "i386/nehalem", CPU_NEHALEM, 4 },
{ "ARM Cortex-A9", "arm/armv7-ca9", CPU_ARM_V7_CA9, 7 },
{ "MIPS 74K", "mips/74K", CPU_MIPS_74K, 4},
{ "MIPS 1004K", "mips/1004K", CPU_MIPS_1004K, 2},
{ "AMD64 family12h", "x86-64/family12h", CPU_FAMILY12H, 4 },
{ "AMD64 family14h", "x86-64/family14h", CPU_FAMILY14H, 4 },
{ "AMD64 family15h", "x86-64/family15h", CPU_FAMILY15H, 6 },
{ "Intel Westmere microarchitecture", "i386/westmere", CPU_WESTMERE, 4 },
{ "ARMv7 Scorpion", "arm/armv7-scorpion", CPU_ARM_SCORPION, 5 },
{ "ARMv7 ScorpionMP", "arm/armv7-scorpionmp", CPU_ARM_SCORPIONMP, 5 },
{ "Intel Sandy Bridge microarchitecture", "i386/sandybridge", CPU_SANDYBRIDGE, 8 },
{ "TILE64", "tile/tile64", CPU_TILE_TILE64, 2 },
{ "TILEPro", "tile/tilepro", CPU_TILE_TILEPRO, 4 },
{ "TILE-GX", "tile/tilegx", CPU_TILE_TILEGX, 4 },
{ "IBM System z10", "s390/z10", CPU_S390_Z10, 1 },
{ "IBM zEnterprise z196", "s390/z196", CPU_S390_Z196, 1 },
{ "Intel Ivy Bridge microarchitecture", "i386/ivybridge", CPU_IVYBRIDGE, 8 },
{ "ARM Cortex-A5", "arm/armv7-ca5", CPU_ARM_V7_CA5, 3 },
{ "ARM Cortex-A7", "arm/armv7-ca7", CPU_ARM_V7_CA7, 5 },
{ "ARM Cortex-A15", "arm/armv7-ca15", CPU_ARM_V7_CA15, 7 },
{ "Intel Haswell microarchitecture", "i386/haswell", CPU_HASWELL, 4 },
{ "IBM zEnterprise EC12", "s390/zEC12", CPU_S390_ZEC12, 1 },
{ "AMD64 generic", "x86-64/generic", CPU_AMD64_GENERIC, 4 },
{ "IBM Power Architected Events V1", "ppc64/architected_events_v1", CPU_PPC64_ARCH_V1, 6 },
{ "ppc64 POWER8", "ppc64/power8", CPU_PPC64_POWER8, 6 },
};
static size_t const nr_cpu_descrs = sizeof(cpu_descrs) / sizeof(struct cpu_descr);
static char * _get_cpuinfo_cpu_type_line(char * buf, int len, const char * prefix, int token)
{
char * ret = NULL;
char * end = NULL;
int prefix_len = strlen(prefix);
FILE * fp = fopen("/proc/cpuinfo", "r");
if (!fp) {
perror("Unable to open /proc/cpuinfo\n");
return ret;
}
memset(buf, 0, len);
while (!ret) {
if (fgets(buf, len, fp) == NULL) {
fprintf(stderr, "Did not find processor type in /proc/cpuinfo.\n");
break;
}
if (!strncmp(buf, prefix, prefix_len)) {
ret = buf + prefix_len;
/* Strip leading whitespace and ':' delimiter */
while (*ret && (*ret == ':' || isspace(*ret)))
++ret;
buf = ret;
/* if token param 0 then read the whole line else
* first token only. */
if (token == 0) {
/* Trim trailing whitespace */
end = buf + strlen(buf) - 1;
while (isspace(*end))
--end;
*(++end) = '\0';
break;
} else {
/* Scan ahead to the end of the token */
while (*buf && !isspace(*buf))
++buf;
/* Trim trailing whitespace */
*buf = '\0';
break;
}
}
}
fclose(fp);
return ret;
}
static char * _get_cpuinfo_cpu_type(char * buf, int len, const char * prefix)
{
return _get_cpuinfo_cpu_type_line(buf, len, prefix, 1);
}
// The aux vector stuff below is currently only used by ppc64 arch
static ElfW(auxv_t) * auxv_buf = NULL;
static ElfW(auxv_t) * _auxv_fetch()
{
ElfW(auxv_t) * auxv_temp = (ElfW(auxv_t) *)auxv_buf;
int auxv_f;
size_t page_size = getpagesize();
ssize_t bytes;
if(auxv_temp == NULL) {
auxv_f = open("/proc/self/auxv", O_RDONLY);
if(auxv_f == -1) {
perror("Cannot open /proc/self/auxv");
fprintf(stderr, "Assuming native platform profiling is supported.\n");
return NULL;
}
else {
auxv_temp = (ElfW(auxv_t) *)malloc(page_size);
if (!auxv_temp) {
perror("Allocation of space for auxv failed.");
close(auxv_f);
return NULL;
}
bytes = read(auxv_f, (void *)auxv_temp, page_size);
if (bytes <= 0) {
free(auxv_temp);
close(auxv_f);
perror("Error /proc/self/auxv read failed");
return NULL;
}
if (close(auxv_f)) {
perror("Error close failed");
fprintf(stderr, "Recoverable error. Continuing.\n");
}
}
auxv_buf = auxv_temp;
}
return (ElfW(auxv_t) *)auxv_temp;
}
static const char * fetch_at_hw_platform(ElfW(Addr) type)
{
int i = 0;
const char * platform = NULL;
ElfW(auxv_t) * my_auxv = NULL;
if ((my_auxv = (ElfW(auxv_t)*) _auxv_fetch()) == NULL)
return NULL;
do {
if(my_auxv[i].a_type == type) {
platform = (const char *)my_auxv[i].a_un.a_val;
break;
}
i++;
} while (my_auxv[i].a_type != AT_NULL);
return platform;
}
static void release_at_hw_platform(void)
{
if (auxv_buf) {
free(auxv_buf);
auxv_buf = NULL;
}
}
static op_cpu _try_ppc64_arch_generic_cpu(void)
{
const char * platform, * base_platform;
op_cpu cpu_type = CPU_NO_GOOD;
platform = fetch_at_hw_platform(AT_PLATFORM);
base_platform = fetch_at_hw_platform(AT_BASE_PLATFORM);
if (!platform || !base_platform) {
fprintf(stderr, "NULL returned for one or both of AT_PLATFORM/AT_BASE_PLATFORM\n");
fprintf(stderr, "AT_PLATFORM: %s; \tAT_BASE_PLATFORM: %s\n", platform, base_platform);
release_at_hw_platform();
return cpu_type;
}
// FIXME whenever a new IBM Power processor is added -- need to ensure
// we're returning the correct version of the architected events file.
if (strcmp(platform, base_platform)) {
// If platform and base_platform differ by only a "+" at the end of the name, we
// consider these equal.
int platforms_are_equivalent = 0;
size_t p1_len, p2_len;
p1_len = strlen(platform);
p2_len = strlen(base_platform);
if (p2_len == (p1_len + 1)) {
if ((strncmp(platform, base_platform, p1_len) == 0) &&
(base_platform[p2_len - 1] == '+')) {
platforms_are_equivalent = 1;
}
}
if (!platforms_are_equivalent) {
if (strcmp(platform, "power7") == 0)
cpu_type = CPU_PPC64_ARCH_V1;
}
}
release_at_hw_platform();
return cpu_type;
}
static op_cpu _get_ppc64_cpu_type(void)
{
int i;
size_t len;
char line[100], cpu_type_str[64], cpu_name_lowercase[64], * cpu_name;
op_cpu cpu_type = CPU_NO_GOOD;
cpu_type = _try_ppc64_arch_generic_cpu();
if (cpu_type != CPU_NO_GOOD)
return cpu_type;
cpu_name = _get_cpuinfo_cpu_type(line, 100, "cpu");
if (!cpu_name)
return CPU_NO_GOOD;
len = strlen(cpu_name);
for (i = 0; i < (int)len ; i++)
cpu_name_lowercase[i] = tolower(cpu_name[i]);
cpu_type_str[0] = '\0';
strcat(cpu_type_str, "ppc64/");
strncat(cpu_type_str, cpu_name_lowercase, len);
cpu_type = op_get_cpu_number(cpu_type_str);
return cpu_type;
}
static op_cpu _get_arm_cpu_type(void)
{
unsigned long cpuid, vendorid;
char line[100];
char * cpu_part, * cpu_implementer;
cpu_implementer = _get_cpuinfo_cpu_type(line, 100, "CPU implementer");
if (!cpu_implementer)
return CPU_NO_GOOD;
errno = 0;
vendorid = strtoul(cpu_implementer, NULL, 16);
if (errno) {
fprintf(stderr, "Unable to parse CPU implementer %s\n", cpu_implementer);
return CPU_NO_GOOD;
}
cpu_part = _get_cpuinfo_cpu_type(line, 100, "CPU part");
if (!cpu_part)
return CPU_NO_GOOD;
errno = 0;
cpuid = strtoul(cpu_part, NULL, 16);
if (errno) {
fprintf(stderr, "Unable to parse CPU part %s\n", cpu_part);
return CPU_NO_GOOD;
}
if (vendorid == 0x41) { /* ARM Ltd. */
switch (cpuid) {
case 0xb36:
case 0xb56:
case 0xb76:
return op_get_cpu_number("arm/armv6");
case 0xb02:
return op_get_cpu_number("arm/mpcore");
case 0xc05:
return op_get_cpu_number("arm/armv7-ca5");
case 0xc07:
return op_get_cpu_number("arm/armv7-ca7");
case 0xc08:
return op_get_cpu_number("arm/armv7");
case 0xc09:
return op_get_cpu_number("arm/armv7-ca9");
case 0xc0f:
return op_get_cpu_number("arm/armv7-ca15");
}
} else if (vendorid == 0x69) { /* Intel xscale */
switch (cpuid >> 9) {
case 1:
return op_get_cpu_number("arm/xscale1");
case 2:
return op_get_cpu_number("arm/xscale2");
}
}
return CPU_NO_GOOD;
}
static op_cpu _get_tile_cpu_type(void)
{
int i;
size_t len;
char line[100], cpu_type_str[64], cpu_name_lowercase[64], * cpu_name;
cpu_name = _get_cpuinfo_cpu_type(line, 100, "model name");
if (!cpu_name)
return CPU_NO_GOOD;
len = strlen(cpu_name);
for (i = 0; i < (int)len ; i++)
cpu_name_lowercase[i] = tolower(cpu_name[i]);
cpu_type_str[0] = '\0';
strcat(cpu_type_str, "tile/");
strncat(cpu_type_str, cpu_name_lowercase, len);
return op_get_cpu_number(cpu_type_str);
}
#if defined(__x86_64__) || defined(__i386__)
int op_is_cpu_vendor(char * vendor)
{
return cpuid_vendor(vendor);
}
static unsigned cpuid_eax(unsigned func)
{
cpuid_data d;
cpuid(func, &d);
return d.eax;
}
static inline int perfmon_available(void)
{
unsigned eax;
if (cpuid_eax(0) < 10)
return 0;
eax = cpuid_eax(10);
if ((eax & 0xff) == 0)
return 0;
return (eax >> 8) & 0xff;
}
static int cpu_info_number(char *name, unsigned long *number)
{
char buf[100];
char *end;
if (!_get_cpuinfo_cpu_type(buf, sizeof buf, name))
return 0;
*number = strtoul(buf, &end, 0);
return end > buf;
}
static op_cpu _get_intel_cpu_type(void)
{
unsigned eax, family, model;
if (perfmon_available())
return op_cpu_specific_type(CPU_ARCH_PERFMON);
/* Handle old non arch perfmon CPUs */
eax = cpuid_signature();
family = cpu_family(eax);
model = cpu_model(eax);
if (family == 6) {
/* Reproduce kernel p6_init logic. Only for non arch perfmon cpus */
switch (model) {
case 0 ... 2:
return op_get_cpu_number("i386/ppro");
case 3 ... 5:
return op_get_cpu_number("i386/pii");
case 6 ... 8:
case 10 ... 11:
return op_get_cpu_number("i386/piii");
case 9:
case 13:
return op_get_cpu_number("i386/p6_mobile");
}
} else if (family == 15) {
unsigned long siblings;
/* Reproduce kernel p4_init() logic */
if (model > 6 || model == 5)
return CPU_NO_GOOD;
if (!cpu_info_number("siblings", &siblings) ||
siblings == 1)
return op_get_cpu_number("i386/p4");
if (siblings == 2)
return op_get_cpu_number("i386/p4-ht");
}
return CPU_NO_GOOD;
}
static op_cpu _get_amd_cpu_type(void)
{
unsigned eax, family;
op_cpu ret = CPU_NO_GOOD;
char buf[20] = {'\0'};
eax = cpuid_signature();
family = cpu_family(eax);
/* These family does not exist in the past.*/
if (family < 0x0f || family == 0x13)
return ret;
switch (family) {
case 0x0f:
ret = op_get_cpu_number("x86-64/hammer");
break;
case 0x10:
ret = op_get_cpu_number("x86-64/family10");
break;
case 0x11:
case 0x12:
case 0x14:
case 0x15:
/* From family11h and forward, we use the same naming scheme */
snprintf(buf, 20, "x86-64/family%xh", family);
ret = op_get_cpu_number(buf);
break;
default:
/* Future processors */
snprintf(buf, 20, "x86-64/generic");
ret = op_get_cpu_number(buf);
break;
}
return ret;
}
static op_cpu _get_x86_64_cpu_type(void)
{
op_cpu ret = CPU_NO_GOOD;
if (cpuid_vendor("GenuineIntel")) {
ret = _get_intel_cpu_type();
} else if (cpuid_vendor("AuthenticAMD")) {
ret = _get_amd_cpu_type();
}
return ret;
}
#else
static op_cpu _get_x86_64_cpu_type(void)
{
return CPU_NO_GOOD;
}
#endif
struct mips_cpu_descr
{
const char * key;
const char * value;
};
static struct mips_cpu_descr mips_cpu_descrs[] = {
{ .key = "MIPS 5Kc", .value = "mips/5K" }, /* CPU_5KC */
{ .key = "MIPS 20Kc", .value = "mips/20K" }, /* CPU_20KC */
{ .key = "MIPS 24Kc", .value = "mips/24K" }, /* CPU_24K */
{ .key = "MIPS 25Kc", .value = "mips/25K" }, /* CPU_25KF */
{ .key = "MIPS 34Kc", .value = "mips/34K" }, /* CPU_34K */
{ .key = "MIPS 74Kc", .value = "mips/74K" }, /* CPU_74K */
{ .key = "MIPS M14Kc", .value = "mips/M14Kc" }, /* CPU_M14KC */
{ .key = "RM9000", .value = "mips/rm9000" }, /* CPU_RM9000 */
{ .key = "R10000", .value = "mips/r10000" }, /* CPU_R10000 */
{ .key = "R12000", .value = "mips/r12000" }, /* CPU_R12000 */
{ .key = "R14000", .value = "mips/r12000" }, /* CPU_R14000 */
{ .key = "ICT Loongson-2", .value = "mips/loongson2" }, /* CPU_LOONGSON2 */
{ .key = NULL, .value = NULL }
};
static const char * _get_mips_op_name(const char * key)
{
struct mips_cpu_descr * p_it = mips_cpu_descrs;
size_t len;
while (p_it->key != NULL) {
len = strlen(p_it->key);
if (0 == strncmp(key, p_it->key, len))
return p_it->value;
++p_it;
}
return NULL;
}
static op_cpu _get_mips_cpu_type(void)
{
char line[100];
char * cpu_model;
const char * op_name = NULL;
cpu_model = _get_cpuinfo_cpu_type_line(line, 100, "cpu model", 0);
if (!cpu_model)
return CPU_NO_GOOD;
op_name = _get_mips_op_name(cpu_model);
if (op_name)
return op_get_cpu_number(op_name);
return CPU_NO_GOOD;
}
static op_cpu __get_cpu_type_alt_method(void)
{
struct utsname uname_info;
if (uname(&uname_info) < 0) {
perror("uname failed");
return CPU_NO_GOOD;
}
if (strncmp(uname_info.machine, "x86_64", 6) == 0 ||
fnmatch("i?86", uname_info.machine, 0) == 0) {
return _get_x86_64_cpu_type();
}
if (strncmp(uname_info.machine, "ppc64", 5) == 0) {
return _get_ppc64_cpu_type();
}
if (strncmp(uname_info.machine, "arm", 3) == 0) {
return _get_arm_cpu_type();
}
if (strncmp(uname_info.machine, "tile", 4) == 0) {
return _get_tile_cpu_type();
}
if (strncmp(uname_info.machine, "mips", 4) == 0) {
return _get_mips_cpu_type();
}
return CPU_NO_GOOD;
}
int op_cpu_variations(op_cpu cpu_type)
{
switch (cpu_type) {
case CPU_ARCH_PERFMON:
return 1;
default:
return 0;
}
}
op_cpu op_cpu_base_type(op_cpu cpu_type)
{
/* All the processors that support CPU_ARCH_PERFMON */
switch (cpu_type) {
case CPU_CORE_2:
case CPU_CORE_I7:
case CPU_ATOM:
case CPU_NEHALEM:
case CPU_HASWELL:
case CPU_WESTMERE:
case CPU_SANDYBRIDGE:
case CPU_IVYBRIDGE:
return CPU_ARCH_PERFMON;
default:
/* assume processor in a class by itself */
return cpu_type;
}
}
op_cpu op_get_cpu_type(void)
{
int cpu_type = CPU_NO_GOOD;
char str[100];
FILE * fp;
fp = fopen("/proc/sys/dev/oprofile/cpu_type", "r");
if (!fp) {
/* Try 2.6's oprofilefs one instead. */
fp = fopen("/dev/oprofile/cpu_type", "r");
if (!fp) {
if ((cpu_type = __get_cpu_type_alt_method()) == CPU_NO_GOOD) {
fprintf(stderr, "Unable to open cpu_type file for reading\n");
fprintf(stderr, "Make sure you have done opcontrol --init\n");
}
return cpu_type;
}
}
if (!fgets(str, 99, fp)) {
fprintf(stderr, "Could not read cpu type.\n");
fclose(fp);
return cpu_type;
}
cpu_type = op_get_cpu_number(str);
if (op_cpu_variations(cpu_type))
cpu_type = op_cpu_specific_type(cpu_type);
fclose(fp);
return cpu_type;
}
op_cpu op_get_cpu_number(char const * cpu_string)
{
int cpu_type = CPU_NO_GOOD;
size_t i;
for (i = 0; i < nr_cpu_descrs; ++i) {
if (!strcmp(cpu_descrs[i].name, cpu_string)) {
cpu_type = cpu_descrs[i].cpu;
break;
}
}
/* Attempt to convert into a number */
if (cpu_type == CPU_NO_GOOD)
sscanf(cpu_string, "%d\n", &cpu_type);
if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
cpu_type = CPU_NO_GOOD;
return cpu_type;
}
char const * op_get_cpu_type_str(op_cpu cpu_type)
{
if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
return "invalid cpu type";
return cpu_descrs[cpu_type].pretty;
}
char const * op_get_cpu_name(op_cpu cpu_type)
{
if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
return "invalid cpu type";
return cpu_descrs[cpu_type].name;
}
int op_get_nr_counters(op_cpu cpu_type)
{
int cnt;
if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
return 0;
cnt = arch_num_counters(cpu_type);
if (cnt >= 0)
return cnt;
return op_cpu_has_timer_fs()
? cpu_descrs[cpu_type].nr_counters + 1
: cpu_descrs[cpu_type].nr_counters;
}
int op_cpu_has_timer_fs(void)
{
static int cached_has_timer_fs_p = -1;
FILE * fp;
if (cached_has_timer_fs_p != -1)
return cached_has_timer_fs_p;
fp = fopen("/dev/oprofile/timer", "r");
cached_has_timer_fs_p = !!fp;
if (fp)
fclose(fp);
return cached_has_timer_fs_p;
}