diff options
author | Ben Cheng <bccheng@google.com> | 2009-09-14 16:00:41 -0700 |
---|---|---|
committer | Ben Cheng <bccheng@google.com> | 2009-09-14 16:00:41 -0700 |
commit | 5a4eb4eb367eccd4b976d1feae96cea96d2c50f2 (patch) | |
tree | 48df6856be9cb61d09a63d04cb6195c3855ed167 /libop | |
parent | 9eda82a51a8ecbd15c9f76b8e191341df55861b8 (diff) | |
download | oprofile-5a4eb4eb367eccd4b976d1feae96cea96d2c50f2.tar.gz |
Refresh OProfile code running on the target side to 0.9.5 for ARMV7.
Diffstat (limited to 'libop')
-rw-r--r-- | libop/Android.mk | 4 | ||||
-rw-r--r-- | libop/op_alloc_counter.c | 58 | ||||
-rw-r--r-- | libop/op_config.h | 6 | ||||
-rw-r--r-- | libop/op_cpu_type.c | 14 | ||||
-rw-r--r-- | libop/op_cpu_type.h | 7 | ||||
-rw-r--r-- | libop/op_events.c | 362 | ||||
-rw-r--r-- | libop/op_events.h | 8 | ||||
-rw-r--r-- | libop/op_hw_specific.h | 107 | ||||
-rw-r--r-- | libop/op_parse_event.c | 1 | ||||
-rw-r--r-- | libop/op_parse_event.h | 1 | ||||
-rw-r--r-- | libop/op_xml_events.c | 93 | ||||
-rw-r--r-- | libop/op_xml_events.h | 20 | ||||
-rw-r--r-- | libop/op_xml_out.c | 233 | ||||
-rw-r--r-- | libop/op_xml_out.h | 72 |
14 files changed, 894 insertions, 92 deletions
diff --git a/libop/Android.mk b/libop/Android.mk index 8fbd1e6..e935a45 100644 --- a/libop/Android.mk +++ b/libop/Android.mk @@ -8,7 +8,9 @@ LOCAL_SRC_FILES:= \ op_events.c \ op_get_interface.c \ op_mangle.c \ - op_parse_event.c + op_parse_event.c \ + op_xml_events.c \ + op_xml_out.c LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/.. \ diff --git a/libop/op_alloc_counter.c b/libop/op_alloc_counter.c index 353100a..bb2bd6e 100644 --- a/libop/op_alloc_counter.c +++ b/libop/op_alloc_counter.c @@ -113,6 +113,9 @@ static void delete_counter_arc(counter_arc_head * ctr_arc, int nr_events) * a bitmask of already allocated counter. Walking through node is done in * preorder left to right. * + * In case of extended events (required no phisical counters), the associated + * counter_map entry will be -1. + * * Possible improvment if neccessary: partition counters in class of counter, * two counter belong to the same class if they allow exactly the same set of * event. Now using a variant of the backtrack algo can works on class of @@ -128,18 +131,27 @@ allocate_counter(counter_arc_head const * ctr_arc, int max_depth, int depth, if (depth == max_depth) return 1; - list_for_each(pos, &ctr_arc[depth].next) { - counter_arc const * arc = list_entry(pos, counter_arc, next); - - if (allocated_mask & (1 << arc->counter)) - continue; - - counter_map[depth] = arc->counter; - + /* If ctr_arc is not available, counter_map is -1 */ + if((&ctr_arc[depth].next)->next == &ctr_arc[depth].next) { + counter_map[depth] = -1; if (allocate_counter(ctr_arc, max_depth, depth + 1, - allocated_mask | (1 << arc->counter), + allocated_mask, counter_map)) return 1; + } else { + list_for_each(pos, &ctr_arc[depth].next) { + counter_arc const * arc = list_entry(pos, counter_arc, next); + + if (allocated_mask & (1 << arc->counter)) + continue; + + counter_map[depth] = arc->counter; + + if (allocate_counter(ctr_arc, max_depth, depth + 1, + allocated_mask | (1 << arc->counter), + counter_map)) + return 1; + } } return 0; @@ -167,7 +179,8 @@ static int op_get_counter_mask(u32 * mask) /* assume nothing is available */ u32 available=0; - count = scandir("/dev/oprofile", &counterlist, perfcounterdir, alphasort); + count = scandir("/dev/oprofile", &counterlist, perfcounterdir, + alphasort); if (count < 0) /* unable to determine bit mask */ return -1; @@ -186,21 +199,36 @@ size_t * map_event_to_counter(struct op_event const * pev[], int nr_events, { counter_arc_head * ctr_arc; size_t * counter_map; - int nr_counters; + int i, nr_counters, nr_pmc_events; + op_cpu curr_cpu_type; u32 unavailable_counters = 0; - nr_counters = op_get_counter_mask(&unavailable_counters); + /* Either ophelp or one of the libop tests may invoke this + * function with a non-native cpu_type. If so, we should not + * call op_get_counter_mask because that will look for real counter + * information in oprofilefs. + */ + curr_cpu_type = op_get_cpu_type(); + if (cpu_type != curr_cpu_type) + nr_counters = op_get_nr_counters(cpu_type); + else + nr_counters = op_get_counter_mask(&unavailable_counters); + /* no counters then probably perfmon managing perfmon hw */ if (nr_counters <= 0) { nr_counters = op_get_nr_counters(cpu_type); unavailable_counters = (~0) << nr_counters; } - if (nr_counters < nr_events) - return 0; + + /* Check to see if we have enough physical counters to map events*/ + for (i = 0, nr_pmc_events = 0; i < nr_events; i++) + if(pev[i]->ext == NULL) + if (++nr_pmc_events > nr_counters) + return 0; ctr_arc = build_counter_arc(pev, nr_events); - counter_map = xmalloc(nr_counters * sizeof(size_t)); + counter_map = xmalloc(nr_events * sizeof(size_t)); if (!allocate_counter(ctr_arc, nr_events, 0, unavailable_counters, counter_map)) { diff --git a/libop/op_config.h b/libop/op_config.h index b384497..12e4b96 100644 --- a/libop/op_config.h +++ b/libop/op_config.h @@ -25,6 +25,10 @@ extern "C" { */ void init_op_config_dirs(char const * session_dir); +#ifndef ANDROID +#define OP_SESSION_DIR_DEFAULT "/var/lib/oprofile/" +#endif + /* * various paths, corresponding to opcontrol, that should be * initialized by init_op_config_dirs() above. @@ -37,8 +41,10 @@ extern char op_log_file[]; extern char op_pipe_file[]; extern char op_dump_status[]; +#if ANDROID #define OP_DRIVER_BASE "/dev/oprofile" #define OP_DATA_DIR "/data/oprofile" +#endif /* Global directory that stores debug files */ #ifndef DEBUGDIR diff --git a/libop/op_cpu_type.c b/libop/op_cpu_type.c index b9d13de..e168b43 100644 --- a/libop/op_cpu_type.c +++ b/libop/op_cpu_type.c @@ -14,6 +14,7 @@ #include <string.h> #include "op_cpu_type.h" +#include "op_hw_specific.h" struct cpu_descr { char const * pretty; @@ -74,6 +75,13 @@ static struct cpu_descr const cpu_descrs[MAX_CPU_TYPE] = { { "ppc64 POWER5++", "ppc64/power5++", CPU_PPC64_POWER5pp, 6 }, { "e300", "ppc/e300", CPU_PPC_E300, 4 }, { "AVR32", "avr32", CPU_AVR32, 3 }, + { "ARM V7 PMNC", "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 }, }; static size_t const nr_cpu_descrs = sizeof(cpu_descrs) / sizeof(struct cpu_descr); @@ -151,8 +159,14 @@ char const * op_get_cpu_name(op_cpu cpu_type) 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 cpu_descrs[cpu_type].nr_counters; } diff --git a/libop/op_cpu_type.h b/libop/op_cpu_type.h index be95ae2..133a4f8 100644 --- a/libop/op_cpu_type.h +++ b/libop/op_cpu_type.h @@ -72,6 +72,13 @@ typedef enum { CPU_PPC64_POWER5pp, /**< ppc64 Power5++ family */ CPU_PPC_E300, /**< e300 */ CPU_AVR32, /**< AVR32 */ + CPU_ARM_V7, /**< ARM V7 */ + CPU_ARCH_PERFMON, /**< Intel architectural perfmon */ + CPU_FAMILY11H, /**< AMD family 11h */ + CPU_PPC64_POWER7, /**< ppc64 POWER7 family */ + CPU_PPC64_IBM_COMPAT_V1, /**< IBM PPC64 processor compat mode version 1 */ + CPU_CORE_I7, /* Intel Core i7, Nehalem */ + CPU_ATOM, /* First generation Intel Atom */ MAX_CPU_TYPE } op_cpu; diff --git a/libop/op_events.c b/libop/op_events.c index b4a10e7..ad95d86 100644 --- a/libop/op_events.c +++ b/libop/op_events.c @@ -16,6 +16,7 @@ #include "op_fileio.h" #include "op_string.h" #include "op_cpufreq.h" +#include "op_hw_specific.h" #include <string.h> #include <stdlib.h> @@ -27,6 +28,24 @@ static LIST_HEAD(um_list); static char const * filename; static unsigned int line_nr; +static void delete_event(struct op_event * event); +static void read_events(char const * file); +static void read_unit_masks(char const * file); +static void free_unit_mask(struct op_unit_mask * um); + +static char *build_fn(const char *cpu_name, const char *fn) +{ + char *s; + static const char *dir; + if (dir == NULL) + dir = getenv("OPROFILE_EVENTS_DIR"); + if (dir == NULL) + dir = OP_DATADIR; + s = xmalloc(strlen(dir) + strlen(cpu_name) + strlen(fn) + 5); + sprintf(s, "%s/%s/%s", dir, cpu_name, fn); + return s; +} + static void parse_error(char const * context) { fprintf(stderr, "oprofile: parse error in %s, line %u\n", @@ -69,6 +88,23 @@ static u64 parse_long_hex(char const * str) return value; } +static void include_um(const char *start, const char *end) +{ + char *s; + char cpu[end - start + 1]; + int old_line_nr; + const char *old_filename; + + strncpy(cpu, start, end - start); + cpu[end - start] = 0; + s = build_fn(cpu, "unit_masks"); + old_line_nr = line_nr; + old_filename = filename; + read_unit_masks(s); + line_nr = old_line_nr; + filename = old_filename; + free(s); +} /* name:MESI type:bitmask default:0x0f */ static void parse_um(struct op_unit_mask * um, char const * line) @@ -94,6 +130,14 @@ static void parse_um(struct op_unit_mask * um, char const * line) ++tagend; + if (strisprefix(start, "include")) { + if (seen_name + seen_type + seen_default > 0) + parse_error("include must be on its own"); + free_unit_mask(um); + include_um(tagend, valueend); + return; + } + if (strisprefix(start, "name")) { if (seen_name) parse_error("duplicate name: tag"); @@ -125,6 +169,11 @@ static void parse_um(struct op_unit_mask * um, char const * line) tagend = valueend; start = valueend; } + + if (!um->name) + parse_error("Missing name for unit mask"); + if (!seen_type) + parse_error("Missing type for unit mask"); } @@ -158,6 +207,11 @@ static struct op_unit_mask * new_unit_mask(void) return um; } +static void free_unit_mask(struct op_unit_mask * um) +{ + list_del(&um->um_next); + free(um); +} /* * name:zero type:mandatory default:0x0 @@ -227,21 +281,68 @@ static u32 parse_counter_mask(char const * str) return mask; } - -static struct op_unit_mask * find_um(char const * value) +static struct op_unit_mask * try_find_um(char const * value) { struct list_head * pos; list_for_each(pos, &um_list) { struct op_unit_mask * um = list_entry(pos, struct op_unit_mask, um_next); - if (strcmp(value, um->name) == 0) + if (strcmp(value, um->name) == 0) { + um->used = 1; return um; + } } + return NULL; +} +static struct op_unit_mask * find_um(char const * value) +{ + struct op_unit_mask * um = try_find_um(value); + if (um) + return um; fprintf(stderr, "oprofile: could not find unit mask %s\n", value); exit(EXIT_FAILURE); } +/* um:a,b,c,d merge multiple unit masks */ +static struct op_unit_mask * merge_um(char * value) +{ + int num; + char *s; + struct op_unit_mask *new, *um; + enum unit_mask_type type = -1U; + + um = try_find_um(value); + if (um) + return um; + + new = new_unit_mask(); + new->name = xstrdup(value); + new->used = 1; + num = 0; + while ((s = strsep(&value, ",")) != NULL) { + unsigned c; + um = find_um(s); + if (type == -1U) + type = um->unit_type_mask; + if (um->unit_type_mask != type) + parse_error("combined unit mask must be all the same types"); + if (type != utm_bitmask && type != utm_exclusive) + parse_error("combined unit mask must be all bitmasks or exclusive"); + new->default_mask |= um->default_mask; + new->num += um->num; + if (new->num > MAX_UNIT_MASK) + parse_error("too many members in combined unit mask"); + for (c = 0; c < um->num; c++, num++) { + new->um[num] = um->um[c]; + new->um[num].desc = xstrdup(new->um[num].desc); + } + } + if (type == -1U) + parse_error("Empty unit mask"); + new->unit_type_mask = type; + return new; +} /* parse either a "tag:value" or a ": trailing description string" */ static int next_token(char const ** cp, char ** name, char ** value) @@ -287,6 +388,20 @@ static int next_token(char const ** cp, char ** name, char ** value) return 1; } +static void include_events (char *value) +{ + char * event_file; + const char *old_filename; + int old_line_nr; + + event_file = build_fn(value, "events"); + old_line_nr = line_nr; + old_filename = filename; + read_events(event_file); + line_nr = old_line_nr; + filename = old_filename; + free(event_file); +} static struct op_event * new_event(void) { @@ -297,8 +412,14 @@ static struct op_event * new_event(void) return event; } +static void free_event(struct op_event * event) +{ + list_del(&event->event_next); + free(event); +} /* event:0x00 counters:0 um:zero minimum:4096 name:ISSUES : Total issues */ +/* event:0x00 ext:xxxxxx um:zero minimum:4096 name:ISSUES : Total issues */ static void read_events(char const * file) { struct op_event * event = NULL; @@ -306,8 +427,9 @@ static void read_events(char const * file) char * name; char * value; char const * c; - int seen_event, seen_counters, seen_um, seen_minimum, seen_name; + int seen_event, seen_counters, seen_um, seen_minimum, seen_name, seen_ext; FILE * fp = fopen(file, "r"); + int tags; if (!fp) { fprintf(stderr, "oprofile: could not open event description file %s\n", file); @@ -323,13 +445,17 @@ static void read_events(char const * file) if (empty_line(line) || comment_line(line)) goto next; + tags = 0; seen_name = 0; seen_event = 0; seen_counters = 0; + seen_ext = 0; seen_um = 0; seen_minimum = 0; event = new_event(); - + event->filter = -1; + event->ext = NULL; + c = line; while (next_token(&c, &name, &value)) { if (strcmp(name, "name") == 0) { @@ -351,14 +477,24 @@ static void read_events(char const * file) if (seen_counters) parse_error("duplicate counters: tag"); seen_counters = 1; - event->counter_mask = parse_counter_mask(value); + if (!strcmp(value, "cpuid")) + event->counter_mask = arch_get_counter_mask(); + else + event->counter_mask = parse_counter_mask(value); free(value); + } else if (strcmp(name, "ext") == 0) { + if (seen_ext) + parse_error("duplicate ext: tag"); + seen_ext = 1; + event->ext = value; } else if (strcmp(name, "um") == 0) { if (seen_um) parse_error("duplicate um: tag"); seen_um = 1; - event->unit = find_um(value); - event->unit->used = 1; + if (strchr(value, ',')) + event->unit = merge_um(value); + else + event->unit = find_um(value); free(value); } else if (strcmp(name, "minimum") == 0) { if (seen_minimum) @@ -368,9 +504,22 @@ static void read_events(char const * file) free(value); } else if (strcmp(name, "desc") == 0) { event->desc = value; + } else if (strcmp(name, "filter") == 0) { + event->filter = parse_int(value); + free(value); + } else if (strcmp(name, "include") == 0) { + if (tags > 0) + parse_error("tags before include:"); + free_event(event); + include_events(value); + free(value); + c = skip_ws(c); + if (*c != '\0' && *c != '#') + parse_error("non whitespace after include:"); } else { parse_error("unknown tag"); } + tags++; free(name); } @@ -385,20 +534,21 @@ next: /* usefull for make check */ -static void check_unit_mask(struct op_unit_mask const * um, +static int check_unit_mask(struct op_unit_mask const * um, char const * cpu_name) { u32 i; + int err = 0; if (!um->used) { fprintf(stderr, "um %s is not used\n", um->name); - exit(EXIT_FAILURE); + err = EXIT_FAILURE; } if (um->unit_type_mask == utm_mandatory && um->num != 1) { fprintf(stderr, "mandatory um %s doesn't contain exactly one " "entry (%s)\n", um->name, cpu_name); - exit(EXIT_FAILURE); + err = EXIT_FAILURE; } else if (um->unit_type_mask == utm_bitmask) { u32 default_mask = um->default_mask; for (i = 0; i < um->num; ++i) @@ -407,7 +557,7 @@ static void check_unit_mask(struct op_unit_mask const * um, if (default_mask) { fprintf(stderr, "um %s default mask is not valid " "(%s)\n", um->name, cpu_name); - exit(EXIT_FAILURE); + err = EXIT_FAILURE; } } else { for (i = 0; i < um->num; ++i) { @@ -418,63 +568,66 @@ static void check_unit_mask(struct op_unit_mask const * um, if (i == um->num) { fprintf(stderr, "exclusive um %s default value is not " "valid (%s)\n", um->name, cpu_name); - exit(EXIT_FAILURE); + err = EXIT_FAILURE; } } + return err; } +static void arch_filter_events(op_cpu cpu_type) +{ + struct list_head * pos, * pos2; + unsigned filter = arch_get_filter(cpu_type); + if (!filter) + return; + list_for_each_safe (pos, pos2, &events_list) { + struct op_event * event = list_entry(pos, struct op_event, event_next); + if (event->filter >= 0 && ((1U << event->filter) & filter)) + delete_event(event); + } +} -static void load_events(op_cpu cpu_type) +static void load_events_name(const char *cpu_name) { - char const * cpu_name = op_get_cpu_name(cpu_type); - char * event_dir; char * event_file; char * um_file; - char * dir; - struct list_head * pos; - if (!list_empty(&events_list)) - return; + event_file = build_fn(cpu_name, "events"); + um_file = build_fn(cpu_name, "unit_masks"); - dir = getenv("OPROFILE_EVENTS_DIR"); - if (dir == NULL) - dir = OP_DATADIR; - - event_dir = xmalloc(strlen(dir) + strlen("/") + strlen(cpu_name) + - strlen("/") + 1); - strcpy(event_dir, dir); - strcat(event_dir, "/"); + read_unit_masks(um_file); + read_events(event_file); + + free(um_file); + free(event_file); +} - strcat(event_dir, cpu_name); - strcat(event_dir, "/"); +static void load_events(op_cpu cpu_type) +{ + const char * cpu_name = op_get_cpu_name(cpu_type); + struct list_head * pos; + int err = 0; - event_file = xmalloc(strlen(event_dir) + strlen("events") + 1); - strcpy(event_file, event_dir); - strcat(event_file, "events"); + if (!list_empty(&events_list)) + return; - um_file = xmalloc(strlen(event_dir) + strlen("unit_masks") + 1); - strcpy(um_file, event_dir); - strcat(um_file, "unit_masks"); + load_events_name(cpu_name); - read_unit_masks(um_file); - read_events(event_file); + arch_filter_events(cpu_type); /* sanity check: all unit mask must be used */ list_for_each(pos, &um_list) { struct op_unit_mask * um = list_entry(pos, struct op_unit_mask, um_next); - - check_unit_mask(um, cpu_name); + err |= check_unit_mask(um, cpu_name); } - - free(um_file); - free(event_file); - free(event_dir); + if (err) + exit(err); } - struct list_head * op_events(op_cpu cpu_type) { load_events(cpu_type); + arch_filter_events(cpu_type); return &events_list; } @@ -521,8 +674,8 @@ void op_free_events(void) } } - -static struct op_event * find_event(u32 nr) +/* There can be actually multiple events here, so this is not quite correct */ +static struct op_event * find_event_any(u32 nr) { struct list_head * pos; @@ -535,8 +688,25 @@ static struct op_event * find_event(u32 nr) return NULL; } +static struct op_event * find_event_um(u32 nr, u32 um) +{ + struct list_head * pos; + unsigned int i; -static FILE * open_event_mapping_file(char const * cpu_name) + list_for_each(pos, &events_list) { + struct op_event * event = list_entry(pos, struct op_event, event_next); + if (event->val == nr) { + for (i = 0; i < event->unit->num; i++) { + if (event->unit->um[i].value == um) + return event; + } + } + } + + return NULL; +} + +static FILE * open_event_mapping_file(char const * cpu_name) { char * ev_map_file; char * dir; @@ -560,7 +730,7 @@ static FILE * open_event_mapping_file(char const * cpu_name) /** * This function is PPC64-specific. */ -static char const * get_mapping(u32 nr, FILE * fp) +static char const * get_mapping(u32 nr, FILE * fp) { char * line; char * name; @@ -655,6 +825,8 @@ char const * find_mapping_for_event(u32 nr, op_cpu cpu_type) case CPU_PPC64_POWER5p: case CPU_PPC64_POWER5pp: case CPU_PPC64_POWER6: + case CPU_PPC64_POWER7: + case CPU_PPC64_IBM_COMPAT_V1: if (!fp) { fprintf(stderr, "oprofile: could not open event mapping file %s\n", filename); exit(EXIT_FAILURE); @@ -672,67 +844,102 @@ char const * find_mapping_for_event(u32 nr, op_cpu cpu_type) return map; } +static int match_event(int i, struct op_event *event, unsigned um) +{ + unsigned v = event->unit->um[i].value; + + switch (event->unit->unit_type_mask) { + case utm_exclusive: + case utm_mandatory: + return v == um; -struct op_event * find_event_by_name(char const * name) + case utm_bitmask: + return (v & um) || (!v && v == 0); + } + + abort(); +} + +struct op_event * find_event_by_name(char const * name, unsigned um, int um_valid) { struct list_head * pos; list_for_each(pos, &events_list) { struct op_event * event = list_entry(pos, struct op_event, event_next); - if (strcmp(event->name, name) == 0) + if (strcmp(event->name, name) == 0) { + if (um_valid) { + unsigned i; + + for (i = 0; i < event->unit->num; i++) + if (match_event(i, event, um)) + return event; + continue; + } return event; + } } return NULL; } -struct op_event * op_find_event(op_cpu cpu_type, u32 nr) +struct op_event * op_find_event(op_cpu cpu_type, u32 nr, u32 um) { struct op_event * event; load_events(cpu_type); - event = find_event(nr); + event = find_event_um(nr, um); return event; } +struct op_event * op_find_event_any(op_cpu cpu_type, u32 nr) +{ + load_events(cpu_type); + + return find_event_any(nr); +} int op_check_events(int ctr, u32 nr, u32 um, op_cpu cpu_type) { - int ret = OP_OK_EVENT; - struct op_event * event; + int ret = OP_INVALID_EVENT; size_t i; u32 ctr_mask = 1 << ctr; + struct list_head * pos; load_events(cpu_type); - event = find_event(nr); + list_for_each(pos, &events_list) { + struct op_event * event = list_entry(pos, struct op_event, event_next); + if (event->val != nr) + continue; - if (!event) { - ret |= OP_INVALID_EVENT; - return ret; - } + ret = OP_OK_EVENT; - if ((event->counter_mask & ctr_mask) == 0) - ret |= OP_INVALID_COUNTER; + if ((event->counter_mask & ctr_mask) == 0) + ret |= OP_INVALID_COUNTER; - if (event->unit->unit_type_mask == utm_bitmask) { - for (i = 0; i < event->unit->num; ++i) - um &= ~(event->unit->um[i].value); - - if (um) - ret |= OP_INVALID_UM; + if (event->unit->unit_type_mask == utm_bitmask) { + for (i = 0; i < event->unit->num; ++i) + um &= ~(event->unit->um[i].value); + + if (um) + ret |= OP_INVALID_UM; + + } else { + for (i = 0; i < event->unit->num; ++i) { + if (event->unit->um[i].value == um) + break; + } + + if (i == event->unit->num) + ret |= OP_INVALID_UM; - } else { - for (i = 0; i < event->unit->num; ++i) { - if (event->unit->um[i].value == um) - break; } - if (i == event->unit->num) - ret |= OP_INVALID_UM; + if (ret == OP_OK_EVENT) + return ret; } return ret; @@ -759,6 +966,10 @@ void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr) case CPU_ATHLON: case CPU_HAMMER: case CPU_FAMILY10: + case CPU_ARCH_PERFMON: + case CPU_FAMILY11H: + case CPU_ATOM: + case CPU_CORE_I7: descr->name = "CPU_CLK_UNHALTED"; break; @@ -793,6 +1004,7 @@ void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr) case CPU_ARM_XSCALE2: case CPU_ARM_MPCORE: case CPU_ARM_V6: + case CPU_ARM_V7: case CPU_AVR32: descr->name = "CPU_CYCLES"; break; @@ -807,6 +1019,8 @@ void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr) case CPU_PPC64_POWER5p: case CPU_PPC64_POWER5pp: case CPU_PPC64_CELL: + case CPU_PPC64_POWER7: + case CPU_PPC64_IBM_COMPAT_V1: descr->name = "CYCLES"; break; diff --git a/libop/op_events.h b/libop/op_events.h index f6462fc..9ffdc49 100644 --- a/libop/op_events.h +++ b/libop/op_events.h @@ -56,6 +56,8 @@ struct op_event { char * name; /**< the event name */ char * desc; /**< the event description */ int min_count; /**< minimum counter value allowed */ + int filter; /**< architecture specific filter or -1 */ + char * ext; /**< extended events */ struct list_head event_next; /**< next event in list */ }; @@ -63,10 +65,12 @@ struct op_event { struct list_head * op_events(op_cpu cpu_type); /** Find a given event, returns NULL on error */ -struct op_event * op_find_event(op_cpu cpu_type, u32 nr); +struct op_event * op_find_event(op_cpu cpu_type, u32 nr, u32 um); +struct op_event * op_find_event_any(op_cpu cpu_type, u32 nr); /** Find a given event by name */ -struct op_event * find_event_by_name(char const * name); +struct op_event * find_event_by_name(char const * name, unsigned um, + int um_valid); /** * Find a mapping for a given event ID for architectures requiring additional information diff --git a/libop/op_hw_specific.h b/libop/op_hw_specific.h new file mode 100644 index 0000000..35080ad --- /dev/null +++ b/libop/op_hw_specific.h @@ -0,0 +1,107 @@ +/* + * @file architecture specific interfaces + * @remark Copyright 2008 Intel Corporation + * @remark Read the file COPYING + * @author Andi Kleen + */ + +#if defined(__i386__) || defined(__x86_64__) + +/* Assume we run on the same host as the profilee */ + +#define num_to_mask(x) ((1U << (x)) - 1) + +static inline int cpuid_vendor(char *vnd) +{ + union { + struct { + unsigned b,d,c; + }; + char v[12]; + } v; + unsigned eax; + asm("cpuid" : "=a" (eax), "=b" (v.b), "=c" (v.c), "=d" (v.d) : "0" (0)); + return !strncmp(v.v, vnd, 12); +} + +/* Work around Nehalem spec update AAJ79: CPUID incorrectly indicates + unhalted reference cycle architectural event is supported. We assume + steppings after C0 report correct data in CPUID. */ +static inline void workaround_nehalem_aaj79(unsigned *ebx) +{ + union { + unsigned eax; + struct { + unsigned stepping : 4; + unsigned model : 4; + unsigned family : 4; + unsigned type : 2; + unsigned res : 2; + unsigned ext_model : 4; + unsigned ext_family : 8; + unsigned res2 : 4; + }; + } v; + unsigned model; + + if (!cpuid_vendor("GenuineIntel")) + return; + asm("cpuid" : "=a" (v.eax) : "0" (1) : "ecx","ebx","edx"); + model = (v.ext_model << 4) + v.model; + if (v.family != 6 || model != 26 || v.stepping > 4) + return; + *ebx |= (1 << 2); /* disable unsupported event */ +} + +static inline unsigned arch_get_filter(op_cpu cpu_type) +{ + if (cpu_type == CPU_ARCH_PERFMON) { + unsigned ebx, eax; + asm("cpuid" : "=a" (eax), "=b" (ebx) : "0" (0xa) : "ecx","edx"); + workaround_nehalem_aaj79(&ebx); + return ebx & num_to_mask(eax >> 24); + } + return -1U; +} + +static inline int arch_num_counters(op_cpu cpu_type) +{ + if (cpu_type == CPU_ARCH_PERFMON) { + unsigned v; + asm("cpuid" : "=a" (v) : "0" (0xa) : "ebx","ecx","edx"); + return (v >> 8) & 0xff; + } + return -1; +} + +static inline unsigned arch_get_counter_mask(void) +{ + unsigned v; + asm("cpuid" : "=a" (v) : "0" (0xa) : "ebx","ecx","edx"); + return num_to_mask((v >> 8) & 0xff); +} + +#else + +static inline unsigned arch_get_filter(op_cpu cpu_type) +{ + /* Do something with passed arg to shut up the compiler warning */ + if (cpu_type != CPU_NO_GOOD) + return 0; + return 0; +} + +static inline int arch_num_counters(op_cpu cpu_type) +{ + /* Do something with passed arg to shut up the compiler warning */ + if (cpu_type != CPU_NO_GOOD) + return -1; + return -1; +} + +static inline unsigned arch_get_counter_mask(void) +{ + return 0; +} + +#endif diff --git a/libop/op_parse_event.c b/libop/op_parse_event.c index 920d617..eb99a20 100644 --- a/libop/op_parse_event.c +++ b/libop/op_parse_event.c @@ -93,6 +93,7 @@ size_t parse_events(struct parsed_event * parsed_events, size_t max_events, part = next_part(&cp); if (part) { + parsed_events[i].unit_mask_valid = 1; parsed_events[i].unit_mask = parse_ulong(part); free(part); } diff --git a/libop/op_parse_event.h b/libop/op_parse_event.h index 247a355..c8d4144 100644 --- a/libop/op_parse_event.h +++ b/libop/op_parse_event.h @@ -22,6 +22,7 @@ struct parsed_event { int unit_mask; int kernel; int user; + int unit_mask_valid; }; /** diff --git a/libop/op_xml_events.c b/libop/op_xml_events.c new file mode 100644 index 0000000..5b9ac7d --- /dev/null +++ b/libop/op_xml_events.c @@ -0,0 +1,93 @@ +/** + * @file op_xml_events.c + * routines for generating event files in XML + * + * @remark Copyright 2008 OProfile authors + * @remark Read the file COPYING + * + * @author Dave Nomura + */ + +#include <stdio.h> +#include <string.h> +#include "op_events.h" +#include "op_list.h" +#include "op_cpu_type.h" +#include "op_xml_out.h" + +static op_cpu cpu_type; +#define MAX_BUFFER 4096 +void open_xml_events(char const * title, char const * doc, op_cpu the_cpu_type) +{ + char const * schema_version = "1.0"; + char buffer[MAX_BUFFER]; + + buffer[0] = '\0'; + cpu_type = the_cpu_type; + open_xml_element(HELP_EVENTS, 0, buffer); + open_xml_element(HELP_HEADER, 1, buffer); + init_xml_str_attr(HELP_TITLE, title, buffer); + init_xml_str_attr(SCHEMA_VERSION, schema_version, buffer); + init_xml_str_attr(HELP_DOC, doc, buffer); + close_xml_element(NONE, 0, buffer); + printf("%s", buffer); +} + +void close_xml_events(void) +{ + char buffer[MAX_BUFFER]; + + buffer[0] = '\0'; + close_xml_element(HELP_EVENTS, 0, buffer); + printf("%s", buffer); +} + +static void xml_do_arch_specific_event_help(struct op_event const * event, + char * buffer) +{ + switch (cpu_type) { + case CPU_PPC64_CELL: + init_xml_int_attr(HELP_EVENT_GROUP, event->val / 100, buffer); + break; + default: + break; + } +} + + +void xml_help_for_event(struct op_event const * event) +{ + uint i; + int nr_counters; + int has_nested = strcmp(event->unit->name, "zero"); + char buffer[MAX_BUFFER]; + + buffer[0] = '\0'; + open_xml_element(HELP_EVENT, 1, buffer); + init_xml_str_attr(HELP_EVENT_NAME, event->name, buffer); + xml_do_arch_specific_event_help(event, buffer); + init_xml_str_attr(HELP_EVENT_DESC, event->desc, buffer); + + nr_counters = op_get_nr_counters(cpu_type); + init_xml_int_attr(HELP_COUNTER_MASK, event->counter_mask, buffer); + init_xml_int_attr(HELP_MIN_COUNT, event->min_count, buffer); + + if (has_nested) { + close_xml_element(NONE, 1, buffer); + open_xml_element(HELP_UNIT_MASKS, 1, buffer); + init_xml_int_attr(HELP_DEFAULT_MASK, event->unit->default_mask, buffer); + close_xml_element(NONE, 1, buffer); + for (i = 0; i < event->unit->num; i++) { + open_xml_element(HELP_UNIT_MASK, 1, buffer); + init_xml_int_attr(HELP_UNIT_MASK_VALUE, + event->unit->um[i].value, buffer); + init_xml_str_attr(HELP_UNIT_MASK_DESC, + event->unit->um[i].desc, buffer); + close_xml_element(NONE, 0, buffer); + } + close_xml_element(HELP_UNIT_MASKS, 0, buffer); + } + close_xml_element(has_nested ? HELP_EVENT : NONE, has_nested, buffer); + printf("%s", buffer); +} + diff --git a/libop/op_xml_events.h b/libop/op_xml_events.h new file mode 100644 index 0000000..e1e092e --- /dev/null +++ b/libop/op_xml_events.h @@ -0,0 +1,20 @@ +/** + * @file op_xml_events.h + * routines for generating event files in XML + * + * @remark Copyright 2008 OProfile authors + * @remark Read the file COPYING + * + * @author Dave Nomura + */ + +#ifndef OP_XML_EVENTS_H +#define OP_XML_EVENTS_H + +#include "op_events.h" + +void xml_help_for_event(struct op_event const * event); +void open_xml_events(char const * title, char const * doc, op_cpu cpu_type); +void close_xml_events(void); + +#endif /* OP_XML_EVENTS_H */ diff --git a/libop/op_xml_out.c b/libop/op_xml_out.c new file mode 100644 index 0000000..d779c45 --- /dev/null +++ b/libop/op_xml_out.c @@ -0,0 +1,233 @@ +/** + * @file op_xml_out.c + * C utility routines for writing XML + * + * @remark Copyright 2008 OProfile authors + * @remark Read the file COPYING + * + * @author Dave Nomura + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "op_xml_out.h" + +char const * xml_tag_map[] = { + "NONE", + "id", + "profile", + "processor", + "cputype", + "title", + "schemaversion", + "mhz", + "setup", + "timersetup", + "rtcinterrupts", + "eventsetup", + "eventname", + "unitmask", + "setupcount", + "separatedcpus", + "options", + "session", "debuginfo", "details", "excludedependent", + "excludesymbols", "imagepath", "includesymbols", "merge", + "classes", + "class", + "cpu", + "event", + "mask", + "process", + "pid", + "thread", + "tid", + "binary", + "module", + "name", + "callers", + "callees", + "symbol", + "idref", + "self", + "detaillo", + "detailhi", + "symboltable", + "symboldata", + "startingaddr", + "file", + "line", + "codelength", + "summarydata", + "sampledata", + "count", + "detailtable", + "symboldetails", + "detaildata", + "vmaoffset", + "bytestable", + "bytes", + "help_events", + "header", + "title", + "doc", + "event", + "event_name", + "group", + "desc", + "counter_mask", + "min_count", + "unit_masks", + "default", + "unit_mask", + "mask", + "desc" +}; + +#define MAX_BUF_LEN 2048 +char const * xml_tag_name(tag_t tag) +{ + return xml_tag_map[tag]; +} + + +void open_xml_element(tag_t tag, int with_attrs, char * buffer) +{ + char const * tag_name = xml_tag_name(tag); + unsigned int const max_len = strlen(tag_name) + 3; + char tmp_buf[MAX_BUF_LEN]; + + if (max_len >= sizeof(tmp_buf)) + fprintf(stderr,"Warning: open_xml_element: buffer overflow %d\n", max_len); + + if (snprintf(tmp_buf, sizeof(tmp_buf), "<%s%s", tag_name, + (with_attrs ? " " : ">\n")) < 0) { + fprintf(stderr,"open_xml_element: snprintf failed\n"); + exit(EXIT_FAILURE); + } + strncat(buffer, tmp_buf, sizeof(tmp_buf)); +} + + +void close_xml_element(tag_t tag, int has_nested, char * buffer) +{ + char const * tag_name = xml_tag_name(tag); + unsigned int const max_len = strlen(tag_name) + 3; + char tmp_buf[MAX_BUF_LEN]; + + if (max_len >= sizeof(tmp_buf)) + fprintf(stderr,"Warning: close_xml_element: buffer overflow %d\n", max_len); + + if (tag == NONE) { + if (snprintf(tmp_buf, sizeof(tmp_buf), "%s\n", (has_nested ? ">" : "/>")) < 0) { + fprintf(stderr, "close_xml_element: snprintf failed\n"); + exit(EXIT_FAILURE); + } + } else { + if (snprintf(tmp_buf, sizeof(tmp_buf), "</%s>\n", tag_name) < 0) { + fprintf(stderr, "close_xml_element: snprintf failed\n"); + exit(EXIT_FAILURE); + } + } + strncat(buffer, tmp_buf, sizeof(tmp_buf)); +} + + +void init_xml_int_attr(tag_t attr, int value, char * buffer) +{ + char const * attr_name = xml_tag_name(attr); + char tmp_buf[MAX_BUF_LEN]; + unsigned int const max_len = strlen(attr_name) + 50; + + if (max_len >= sizeof(tmp_buf)) { + fprintf(stderr, + "Warning: init_xml_int_attr: buffer overflow %d\n", max_len); + } + + + if (snprintf(tmp_buf, sizeof(tmp_buf), " %s=\"%d\"", attr_name, value) < 0) { + fprintf(stderr,"init_xml_int_attr: snprintf failed\n"); + exit(EXIT_FAILURE); + } + strncat(buffer, tmp_buf, sizeof(tmp_buf)); +} + + +void init_xml_dbl_attr(tag_t attr, double value, char * buffer) +{ + char const * attr_name = xml_tag_name(attr); + unsigned int const max_len = strlen(attr_name) + 50; + char tmp_buf[MAX_BUF_LEN]; + + if (max_len >= sizeof(tmp_buf)) + fprintf(stderr, "Warning: init_xml_dbl_attr: buffer overflow %d\n", max_len); + + if (snprintf(tmp_buf, sizeof(tmp_buf), " %s=\"%.2f\"", attr_name, value) < 0) { + fprintf(stderr, "init_xml_dbl_attr: snprintf failed\n"); + exit(EXIT_FAILURE); + } + strncat(buffer, tmp_buf, sizeof(tmp_buf)); +} + + +static char * xml_quote(char const * str, char * quote_buf) +{ + int i; + int pos = 0; + int len = strlen(str); + + + quote_buf[pos++] = '"'; + + for (i = 0; i < len; i++) { + if (pos >= MAX_BUF_LEN - 10) { + fprintf(stderr,"quote_str: buffer overflow %d\n", pos); + exit(EXIT_FAILURE); + } + + switch(str[i]) { + case '&': + strncpy(quote_buf + pos, "&", 5); + pos += 5; + break; + case '<': + strncpy(quote_buf + pos, "<", 4); + pos += 4; + break; + case '>': + strncpy(quote_buf + pos, ">", 4); + pos += 4; + break; + case '"': + strncpy(quote_buf + pos, """, 6); + pos += 6; + break; + default: + quote_buf[pos++] = str[i]; + break; + } + } + + quote_buf[pos++] = '"'; + quote_buf[pos++] = '\0'; + return quote_buf; +} + + +void init_xml_str_attr(tag_t attr, char const * str, char * buffer) +{ + char tmp_buf[MAX_BUF_LEN]; + char quote_buf[MAX_BUF_LEN]; + char const * attr_name = xml_tag_name(attr); + char const * quote_str = xml_quote(str, quote_buf); + const unsigned int max_len = strlen(attr_name) + strlen(quote_str) + 10; + + if (max_len >= sizeof(tmp_buf)) + fprintf(stderr, "Warning: init_xml_str_attr: buffer overflow %d\n", max_len); + + if (snprintf(tmp_buf, sizeof(tmp_buf), " %s=""%s""", attr_name, quote_str) < 0) { + fprintf(stderr,"init_xml_str_attr: snprintf failed\n"); + exit(EXIT_FAILURE); + } + strncat(buffer, tmp_buf, sizeof(tmp_buf)); +} diff --git a/libop/op_xml_out.h b/libop/op_xml_out.h new file mode 100644 index 0000000..52e8d8f --- /dev/null +++ b/libop/op_xml_out.h @@ -0,0 +1,72 @@ +/** + * @file op_xml_out.h + * utility routines for writing XML + * + * @remark Copyright 2008 OProfile authors + * @remark Read the file COPYING + * + * @author Dave Nomura + */ + +#ifndef OP_XML_OUT_H +#define OP_XML_OUT_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + NONE=0, TABLE_ID, PROFILE, + PROCESSOR, CPU_NAME, TITLE, SCHEMA_VERSION, MHZ, + SETUP, + TIMER_SETUP, RTC_INTERRUPTS, + EVENT_SETUP, EVENT_NAME, UNIT_MASK, SETUP_COUNT, SEPARATED_CPUS, + OPTIONS, SESSION, DEBUG_INFO, DETAILS, EXCLUDE_DEPENDENT, EXCLUDE_SYMBOLS, + IMAGE_PATH, INCLUDE_SYMBOLS, MERGE, + CLASSES, + CLASS, + CPU_NUM, + EVENT_NUM, + EVENT_MASK, + PROCESS, PROC_ID, + THREAD, THREAD_ID, + BINARY, + MODULE, NAME, + CALLERS, CALLEES, + SYMBOL, ID_REF, SELFREF, DETAIL_LO, DETAIL_HI, + SYMBOL_TABLE, + SYMBOL_DATA, STARTING_ADDR, + SOURCE_FILE, SOURCE_LINE, CODE_LENGTH, + SUMMARY, SAMPLE, + COUNT, + DETAIL_TABLE, SYMBOL_DETAILS, DETAIL_DATA, VMA, + BYTES_TABLE, BYTES, + HELP_EVENTS, + HELP_HEADER, + HELP_TITLE, + HELP_DOC, + HELP_EVENT, + HELP_EVENT_NAME, + HELP_EVENT_GROUP, + HELP_EVENT_DESC, + HELP_COUNTER_MASK, + HELP_MIN_COUNT, + HELP_UNIT_MASKS, + HELP_DEFAULT_MASK, + HELP_UNIT_MASK, + HELP_UNIT_MASK_VALUE, + HELP_UNIT_MASK_DESC + } tag_t; + +char const * xml_tag_name(tag_t tag); +void open_xml_element(tag_t tag, int with_attrs, char * result); +void close_xml_element(tag_t tag, int has_nested, char * result); +void init_xml_int_attr(tag_t attr, int value, char * result); +void init_xml_dbl_attr(tag_t attr, double value, char * result); +void init_xml_str_attr(tag_t attr, char const * str, char * result); + +#ifdef __cplusplus +} +#endif + +#endif /* OP_XML_OUT_H */ |