aboutsummaryrefslogtreecommitdiff
path: root/libop
diff options
context:
space:
mode:
authorBen Cheng <bccheng@google.com>2009-09-14 16:00:41 -0700
committerBen Cheng <bccheng@google.com>2009-09-14 16:00:41 -0700
commit5a4eb4eb367eccd4b976d1feae96cea96d2c50f2 (patch)
tree48df6856be9cb61d09a63d04cb6195c3855ed167 /libop
parent9eda82a51a8ecbd15c9f76b8e191341df55861b8 (diff)
downloadoprofile-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.mk4
-rw-r--r--libop/op_alloc_counter.c58
-rw-r--r--libop/op_config.h6
-rw-r--r--libop/op_cpu_type.c14
-rw-r--r--libop/op_cpu_type.h7
-rw-r--r--libop/op_events.c362
-rw-r--r--libop/op_events.h8
-rw-r--r--libop/op_hw_specific.h107
-rw-r--r--libop/op_parse_event.c1
-rw-r--r--libop/op_parse_event.h1
-rw-r--r--libop/op_xml_events.c93
-rw-r--r--libop/op_xml_events.h20
-rw-r--r--libop/op_xml_out.c233
-rw-r--r--libop/op_xml_out.h72
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, "&amp;", 5);
+ pos += 5;
+ break;
+ case '<':
+ strncpy(quote_buf + pos, "&lt;", 4);
+ pos += 4;
+ break;
+ case '>':
+ strncpy(quote_buf + pos, "&gt;", 4);
+ pos += 4;
+ break;
+ case '"':
+ strncpy(quote_buf + pos, "&quot;", 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 */