aboutsummaryrefslogtreecommitdiff
path: root/libop
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:30 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:30 -0800
commit10e23eebca4175a8dfe3a788b2bebacb1fcfce54 (patch)
treee7b15dddb89338d148474c4e4c16acb087e322c7 /libop
parentdfb3f050a7cebd2030ea23dc6fa8964530e4ddcc (diff)
downloadoprofile-10e23eebca4175a8dfe3a788b2bebacb1fcfce54.tar.gz
Diffstat (limited to 'libop')
-rw-r--r--libop/Android.mk19
-rw-r--r--libop/op_alloc_counter.c213
-rw-r--r--libop/op_alloc_counter.h43
-rw-r--r--libop/op_config.c77
-rw-r--r--libop/op_config.h58
-rw-r--r--libop/op_config_24.h79
-rw-r--r--libop/op_cpu_type.c158
-rw-r--r--libop/op_cpu_type.h139
-rw-r--r--libop/op_events.c862
-rw-r--r--libop/op_events.h125
-rw-r--r--libop/op_get_interface.c32
-rw-r--r--libop/op_hw_config.h30
-rw-r--r--libop/op_interface.h87
-rw-r--r--libop/op_mangle.c104
-rw-r--r--libop/op_mangle.h66
-rw-r--r--libop/op_parse_event.c120
-rw-r--r--libop/op_parse_event.h42
-rw-r--r--libop/op_sample_file.h42
18 files changed, 2296 insertions, 0 deletions
diff --git a/libop/Android.mk b/libop/Android.mk
new file mode 100644
index 0000000..8fbd1e6
--- /dev/null
+++ b/libop/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ op_alloc_counter.c \
+ op_config.c \
+ op_cpu_type.c \
+ op_events.c \
+ op_get_interface.c \
+ op_mangle.c \
+ op_parse_event.c
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/.. \
+ $(LOCAL_PATH)/../libutil
+
+LOCAL_MODULE := libop
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libop/op_alloc_counter.c b/libop/op_alloc_counter.c
new file mode 100644
index 0000000..353100a
--- /dev/null
+++ b/libop/op_alloc_counter.c
@@ -0,0 +1,213 @@
+/**
+ * @file op_alloc_counter.c
+ * hardware counter allocation
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <dirent.h>
+
+#include "op_events.h"
+#include "op_libiberty.h"
+
+
+typedef struct counter_arc_head {
+ /** the head of allowed counter for this event */
+ struct list_head next;
+} counter_arc_head;
+
+
+typedef struct counter_arc {
+ /** counter nr */
+ int counter;
+ /** the next counter allowed for this event */
+ struct list_head next;
+} counter_arc;
+
+
+/**
+ * @param pev an array of event
+ * @param nr_events number of entry in pev
+ *
+ * build an array of counter list allowed for each events
+ * counter_arc_head[i] is the list of allowed counter for pev[i] events
+ * The returned pointer is an array of nr_events entry
+ */
+static counter_arc_head *
+build_counter_arc(struct op_event const * pev[], int nr_events)
+{
+ counter_arc_head * ctr_arc;
+ int i;
+
+ ctr_arc = xmalloc(nr_events * sizeof(*ctr_arc));
+
+ for (i = 0; i < nr_events; ++i) {
+ int j;
+ u32 mask = pev[i]->counter_mask;
+
+ list_init(&ctr_arc[i].next);
+ for (j = 0; mask; ++j) {
+ if (mask & (1 << j)) {
+ counter_arc * arc =
+ xmalloc(sizeof(counter_arc));
+ arc->counter = j;
+ /* we are looping by increasing counter number,
+ * allocation use a left to right tree walking
+ * so we add at end to ensure counter will
+ * be allocated by increasing number: it's not
+ * required but a bit less surprising when
+ * debugging code
+ */
+ list_add_tail(&arc->next, &ctr_arc[i].next);
+ mask &= ~(1 << j);
+ }
+ }
+ }
+
+ return ctr_arc;
+}
+
+
+/**
+ * @param ctr_arc the array to deallocate
+ * @param nr_events number of entry in array
+ *
+ * deallocate all previously allocated resource by build_counter_arc()
+ */
+static void delete_counter_arc(counter_arc_head * ctr_arc, int nr_events)
+{
+ int i;
+ for (i = 0; i < nr_events; ++i) {
+ struct list_head * pos, * pos2;
+ list_for_each_safe(pos, pos2, &ctr_arc[i].next) {
+ counter_arc * arc = list_entry(pos, counter_arc, next);
+ list_del(&arc->next);
+ free(arc);
+ }
+ }
+ free(ctr_arc);
+}
+
+
+/**
+ * @param ctr_arc tree description, ctr_arc[i] is the i-th level of tree.
+ * @param max_depth number of entry in array ctr_arc == depth of tree
+ * @param depth current level we are exploring
+ * @param allocated_mask current counter already allocated mask
+ * @param counter_map array of counter number mapping, returned results go
+ * here
+ *
+ * return non zero on succees, in this case counter_map is set to the counter
+ * mapping number.
+ *
+ * Solution is searched through a simple backtracking exploring recursively all
+ * possible solution until one is found, prunning is done in O(1) by tracking
+ * a bitmask of already allocated counter. Walking through node is done in
+ * preorder left to right.
+ *
+ * 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
+ * counter rather on counter (this is not an improvment if each counter goes
+ * in it's own class)
+ */
+static int
+allocate_counter(counter_arc_head const * ctr_arc, int max_depth, int depth,
+ u32 allocated_mask, size_t * counter_map)
+{
+ struct list_head * pos;
+
+ 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 (allocate_counter(ctr_arc, max_depth, depth + 1,
+ allocated_mask | (1 << arc->counter),
+ counter_map))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* determine which directories are counter directories
+ */
+static int perfcounterdir(const struct dirent * entry)
+{
+ return (isdigit(entry->d_name[0]));
+}
+
+
+/**
+ * @param mask pointer where to place bit mask of unavailable counters
+ *
+ * return >= 0 number of counters that are available
+ * < 0 could not determine number of counters
+ *
+ */
+static int op_get_counter_mask(u32 * mask)
+{
+ struct dirent **counterlist;
+ int count, i;
+ /* assume nothing is available */
+ u32 available=0;
+
+ count = scandir("/dev/oprofile", &counterlist, perfcounterdir, alphasort);
+ if (count < 0)
+ /* unable to determine bit mask */
+ return -1;
+ /* convert to bit map (0 where counter exists) */
+ for (i=0; i<count; ++i) {
+ available |= 1 << atoi(counterlist[i]->d_name);
+ free(counterlist[i]);
+ }
+ *mask=~available;
+ free(counterlist);
+ return count;
+}
+
+size_t * map_event_to_counter(struct op_event const * pev[], int nr_events,
+ op_cpu cpu_type)
+{
+ counter_arc_head * ctr_arc;
+ size_t * counter_map;
+ int nr_counters;
+ u32 unavailable_counters = 0;
+
+ 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;
+
+ ctr_arc = build_counter_arc(pev, nr_events);
+
+ counter_map = xmalloc(nr_counters * sizeof(size_t));
+
+ if (!allocate_counter(ctr_arc, nr_events, 0, unavailable_counters,
+ counter_map)) {
+ free(counter_map);
+ counter_map = 0;
+ }
+
+ delete_counter_arc(ctr_arc, nr_events);
+ return counter_map;
+}
diff --git a/libop/op_alloc_counter.h b/libop/op_alloc_counter.h
new file mode 100644
index 0000000..40b4ebf
--- /dev/null
+++ b/libop/op_alloc_counter.h
@@ -0,0 +1,43 @@
+/**
+ * @file op_alloc_counter.h
+ * hardware counter allocation
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_ALLOC_COUNTER_H
+#define OP_ALLOC_COUNTER_H
+
+#include <stddef.h>
+
+#include "op_cpu_type.h"
+
+struct op_event;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @param pev array of selected event we want to bind to counter
+ * @param nr_events size of pev array
+ * @param cpu_type cpu type
+ *
+ * Try to calculate a binding between passed event in pev and counter number.
+ * The binding is returned in a size_t * where returned ptr[i] is the counter
+ * number bound to pev[i]
+ */
+size_t * map_event_to_counter(struct op_event const * pev[], int nr_events,
+ op_cpu cpu_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !OP_ALLOC_COUNTER_H */
diff --git a/libop/op_config.c b/libop/op_config.c
new file mode 100644
index 0000000..837242b
--- /dev/null
+++ b/libop/op_config.c
@@ -0,0 +1,77 @@
+/**
+ * @file op_config.c
+ * Oprofile configuration parameters.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Nathan Tallent
+ * @Modifications Daniel Hansel
+ */
+
+#include "op_config.h"
+#include "op_config_24.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/* paths in op_config.h */
+char op_session_dir[PATH_MAX];
+char op_samples_dir[PATH_MAX];
+char op_samples_current_dir[PATH_MAX];
+char op_lock_file[PATH_MAX];
+char op_log_file[PATH_MAX];
+char op_pipe_file[PATH_MAX];
+char op_dump_status[PATH_MAX];
+
+/* paths in op_config_24.h */
+char op_device[PATH_MAX];
+char op_note_device[PATH_MAX];
+char op_hash_device[PATH_MAX];
+
+void
+init_op_config_dirs(char const * session_dir)
+{
+ int session_dir_len;
+
+ assert(session_dir);
+ session_dir_len = strlen(session_dir);
+
+ if (session_dir_len + strlen("/samples/oprofiled.log") > PATH_MAX) {
+ fprintf(stderr, "Session_dir string \"%s\" is too large.\n",
+ session_dir);
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(op_session_dir, session_dir);
+
+ strcpy(op_samples_dir, op_session_dir);
+ strcat(op_samples_dir, "/samples/");
+
+ strcpy(op_samples_current_dir, op_samples_dir);
+ strcat(op_samples_current_dir, "/current/");
+
+ strcpy(op_lock_file, op_session_dir);
+ strcat(op_lock_file, "/lock");
+
+ strcpy(op_pipe_file, op_session_dir);
+ strcat(op_pipe_file, "/opd_pipe");
+
+ strcpy(op_log_file, op_samples_dir);
+ strcat(op_log_file, "oprofiled.log");
+
+ strcpy(op_dump_status, op_session_dir);
+ strcat(op_dump_status, "/complete_dump");
+
+ strcpy(op_device, op_session_dir);
+ strcat(op_device, "/opdev");
+
+ strcpy(op_note_device, op_session_dir);
+ strcat(op_note_device, "/opnotedev");
+
+ strcpy(op_hash_device, op_session_dir);
+ strcat(op_hash_device, "/ophashmapdev");
+}
diff --git a/libop/op_config.h b/libop/op_config.h
new file mode 100644
index 0000000..b384497
--- /dev/null
+++ b/libop/op_config.h
@@ -0,0 +1,58 @@
+/**
+ * @file op_config.h
+ *
+ * Parameters a user may want to change. See
+ * also op_config_24.h
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * @Modifications Daniel Hansel
+ */
+
+#ifndef OP_CONFIG_H
+#define OP_CONFIG_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+ * must be called to initialize the paths below.
+ * @param session_dir the non-NULL value of the base session directory
+ */
+void init_op_config_dirs(char const * session_dir);
+
+/*
+ * various paths, corresponding to opcontrol, that should be
+ * initialized by init_op_config_dirs() above.
+ */
+extern char op_session_dir[];
+extern char op_samples_dir[];
+extern char op_samples_current_dir[];
+extern char op_lock_file[];
+extern char op_log_file[];
+extern char op_pipe_file[];
+extern char op_dump_status[];
+
+#define OP_DRIVER_BASE "/dev/oprofile"
+#define OP_DATA_DIR "/data/oprofile"
+
+/* Global directory that stores debug files */
+#ifndef DEBUGDIR
+#define DEBUGDIR "/usr/lib/debug"
+#endif
+
+#define OPD_MAGIC "DAE\n"
+#define OPD_VERSION 0x11
+
+#define OP_MIN_CPU_BUF_SIZE 2048
+#define OP_MAX_CPU_BUF_SIZE 131072
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* OP_CONFIG_H */
diff --git a/libop/op_config_24.h b/libop/op_config_24.h
new file mode 100644
index 0000000..1786fae
--- /dev/null
+++ b/libop/op_config_24.h
@@ -0,0 +1,79 @@
+/**
+ * @file op_config_24.h
+ *
+ * Parameters a user may want to change
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_CONFIG_24_H
+#define OP_CONFIG_24_H
+
+#define OP_MOUNT "/proc/sys/dev/oprofile/"
+
+extern char op_device[];
+extern char op_note_device[];
+extern char op_hash_device[];
+
+/*@{\name module default/min/max settings */
+
+/** 65536 * sizeof(op_sample) */
+#define OP_DEFAULT_BUF_SIZE 65536
+/**
+ * we don't try to wake-up daemon until it remains more than this free entry
+ * in eviction buffer
+ */
+#define OP_PRE_WATERMARK(buffer_size) \
+ (((buffer_size) / 8) < OP_MIN_PRE_WATERMARK \
+ ? OP_MIN_PRE_WATERMARK \
+ : (buffer_size) / 8)
+/** minimal buffer water mark before we try to wakeup daemon */
+#define OP_MIN_PRE_WATERMARK 8192
+/** maximum number of entry in samples eviction buffer */
+#define OP_MAX_BUF_SIZE 1048576
+/** minimum number of entry in samples eviction buffer */
+#define OP_MIN_BUF_SIZE (32768 + OP_PRE_WATERMARK(32768))
+
+/** 16384 * sizeof(op_note) = 273680 bytes default */
+#define OP_DEFAULT_NOTE_SIZE 16384
+/**
+ * we don't try to wake-up daemon until it remains more than this free entry
+ * in note buffer
+ */
+#define OP_PRE_NOTE_WATERMARK(note_size) \
+ (((note_size) / 32) < OP_MIN_NOTE_PRE_WATERMARK \
+ ? OP_MIN_NOTE_PRE_WATERMARK \
+ : (note_size) / 32)
+/** minimal note buffer water mark before we try to wakeup daemon */
+#define OP_MIN_NOTE_PRE_WATERMARK 512
+/** maximum number of entry in note buffer */
+#define OP_MAX_NOTE_TABLE_SIZE 1048576
+/** minimum number of entry in note buffer */
+#define OP_MIN_NOTE_TABLE_SIZE (1024 + OP_PRE_NOTE_WATERMARK(1024))
+
+/** maximum sampling rate when using RTC */
+#define OP_MAX_RTC_COUNT 4096
+/** minimum sampling rate when using RTC */
+#define OP_MIN_RTC_COUNT 2
+
+/*@}*/
+
+/**
+ * nr entries in hash map. This is the maximum number of name components
+ * allowed. Must be a prime number
+ */
+#define OP_HASH_MAP_NR 4093
+
+/** size of string pool in bytes */
+#define POOL_SIZE 65536
+
+#ifndef NR_CPUS
+/** maximum number of cpus present in the box */
+#define NR_CPUS 32
+#endif
+
+#endif /* OP_CONFIG_24_H */
diff --git a/libop/op_cpu_type.c b/libop/op_cpu_type.c
new file mode 100644
index 0000000..b9d13de
--- /dev/null
+++ b/libop/op_cpu_type.c
@@ -0,0 +1,158 @@
+/**
+ * @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 <string.h>
+
+#include "op_cpu_type.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, 4},
+ { "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 MPCore", "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 },
+};
+
+static size_t const nr_cpu_descrs = sizeof(cpu_descrs) / sizeof(struct cpu_descr);
+
+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) {
+ 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");
+ return CPU_NO_GOOD;
+ }
+
+ cpu_type = op_get_cpu_number(str);
+
+ 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)
+{
+ if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
+ return 0;
+
+ return cpu_descrs[cpu_type].nr_counters;
+}
diff --git a/libop/op_cpu_type.h b/libop/op_cpu_type.h
new file mode 100644
index 0000000..be95ae2
--- /dev/null
+++ b/libop/op_cpu_type.h
@@ -0,0 +1,139 @@
+/**
+ * @file op_cpu_type.h
+ * CPU type determination
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_CPU_TYPE_H
+#define OP_CPU_TYPE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Supported cpu type. Always add new CPU types at the very end.
+ */
+typedef enum {
+ CPU_NO_GOOD = -1, /**< unsupported CPU type */
+ CPU_PPRO, /**< Pentium Pro */
+ CPU_PII, /**< Pentium II series */
+ CPU_PIII, /**< Pentium III series */
+ CPU_ATHLON, /**< AMD P6 series */
+ CPU_TIMER_INT, /**< CPU using the timer interrupt */
+ CPU_RTC, /**< other CPU to use the RTC */
+ CPU_P4, /**< Pentium 4 / Xeon series */
+ CPU_IA64, /**< Generic IA64 */
+ CPU_IA64_1, /**< IA64 Merced */
+ CPU_IA64_2, /**< IA64 McKinley */
+ CPU_HAMMER, /**< AMD Hammer family */
+ CPU_P4_HT2, /**< Pentium 4 / Xeon series with 2 hyper-threads */
+ CPU_AXP_EV4, /**< Alpha EV4 family */
+ CPU_AXP_EV5, /**< Alpha EV5 family */
+ CPU_AXP_PCA56, /**< Alpha PCA56 family */
+ CPU_AXP_EV6, /**< Alpha EV6 family */
+ CPU_AXP_EV67, /**< Alpha EV67 family */
+ CPU_P6_MOBILE, /**< Pentium M series */
+ CPU_ARM_XSCALE1, /**< ARM XScale 1 */
+ CPU_ARM_XSCALE2, /**< ARM XScale 2 */
+ CPU_PPC64_POWER4, /**< ppc64 POWER4 family */
+ CPU_PPC64_POWER5, /**< ppc64 POWER5 family */
+ CPU_PPC64_POWER5p, /**< ppc64 Power5+ family */
+ CPU_PPC64_970, /**< ppc64 970 family */
+ CPU_MIPS_20K, /**< MIPS 20K */
+ CPU_MIPS_24K, /**< MIPS 24K */
+ CPU_MIPS_25K, /**< MIPS 25K */
+ CPU_MIPS_34K, /**< MIPS 34K */
+ CPU_MIPS_5K, /**< MIPS 5K */
+ CPU_MIPS_R10000, /**< MIPS R10000 */
+ CPU_MIPS_R12000, /**< MIPS R12000 */
+ CPU_MIPS_RM7000, /**< QED RM7000 */
+ CPU_MIPS_RM9000, /**< PMC-Sierra RM9000 */
+ CPU_MIPS_SB1, /**< Broadcom SB1 */
+ CPU_MIPS_VR5432, /**< NEC VR5432 */
+ CPU_MIPS_VR5500, /**< MIPS VR5500, VR5532 and VR7701 */
+ CPU_PPC_E500, /**< e500 */
+ CPU_PPC_E500_2, /**< e500v2 */
+ CPU_CORE, /**< Core Solo / Duo series */
+ CPU_PPC_7450, /**< PowerPC G4 */
+ CPU_CORE_2, /**< Intel Core 2 */
+ CPU_PPC64_POWER6, /**< ppc64 POWER6 family */
+ CPU_PPC64_970MP, /**< ppc64 970MP */
+ CPU_PPC64_CELL, /**< ppc64 Cell Broadband Engine*/
+ CPU_FAMILY10, /**< AMD family 10 */
+ CPU_PPC64_PA6T, /**< ppc64 PA6T */
+ CPU_ARM_MPCORE, /**< ARM MPCore */
+ CPU_ARM_V6, /**< ARM V6 */
+ CPU_PPC64_POWER5pp, /**< ppc64 Power5++ family */
+ CPU_PPC_E300, /**< e300 */
+ CPU_AVR32, /**< AVR32 */
+ MAX_CPU_TYPE
+} op_cpu;
+
+/**
+ * get the CPU type from the kernel
+ *
+ * returns CPU_NO_GOOD if the CPU could not be identified.
+ * This function can not work if the module is not loaded
+ */
+op_cpu op_get_cpu_type(void);
+
+/**
+ * get the cpu number based on string
+ * @param cpu_string with either the cpu type identifier or cpu type number
+ *
+ * The function returns CPU_NO_GOOD if no matching string was found.
+ */
+op_cpu op_get_cpu_number(char const * cpu_string);
+
+/**
+ * get the cpu string.
+ * @param cpu_type the cpu type identifier
+ *
+ * The function always return a valid char const * the core cpu denomination
+ * or "invalid cpu type" if cpu_type is not valid.
+ */
+char const * op_get_cpu_type_str(op_cpu cpu_type);
+
+/**
+ * op_get_cpu_name - get the cpu name
+ * @param cpu_type the cpu identifier name
+ *
+ * The function always return a valid char const *
+ * Return the OProfile CPU name, e.g. "i386/pii"
+ */
+char const * op_get_cpu_name(op_cpu cpu_type);
+
+/**
+ * compute the number of counters available
+ * @param cpu_type numeric processor type
+ *
+ * returns 0 if the CPU could not be identified
+ */
+int op_get_nr_counters(op_cpu cpu_type);
+
+typedef enum {
+ OP_INTERFACE_NO_GOOD = -1,
+ OP_INTERFACE_24,
+ OP_INTERFACE_26
+} op_interface;
+
+/**
+ * get the INTERFACE used to communicate between daemon and the kernel
+ *
+ * returns OP_INTERFACE_NO_GOOD if the INTERFACE could not be identified.
+ * This function will identify the interface as OP_INTERFACE_NO_GOOD if
+ * the module is not loaded.
+ */
+op_interface op_get_interface(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_CPU_TYPE_H */
diff --git a/libop/op_events.c b/libop/op_events.c
new file mode 100644
index 0000000..b4a10e7
--- /dev/null
+++ b/libop/op_events.c
@@ -0,0 +1,862 @@
+/**
+ * @file op_events.c
+ * Details of PMC profiling events
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "op_events.h"
+#include "op_libiberty.h"
+#include "op_fileio.h"
+#include "op_string.h"
+#include "op_cpufreq.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static LIST_HEAD(events_list);
+static LIST_HEAD(um_list);
+
+static char const * filename;
+static unsigned int line_nr;
+
+static void parse_error(char const * context)
+{
+ fprintf(stderr, "oprofile: parse error in %s, line %u\n",
+ filename, line_nr);
+ fprintf(stderr, "%s\n", context);
+ exit(EXIT_FAILURE);
+}
+
+
+static int parse_int(char const * str)
+{
+ int value;
+ if (sscanf(str, "%d", &value) != 1)
+ parse_error("expected decimal value");
+
+ return value;
+}
+
+
+static int parse_hex(char const * str)
+{
+ int value;
+ /* 0x/0X to force the use of hexa notation for field intended to
+ be in hexadecimal */
+ if (sscanf(str, "0x%x", &value) != 1 &&
+ sscanf(str, "0X%x", &value) != 1)
+ parse_error("expected hexadecimal value");
+
+ return value;
+}
+
+
+static u64 parse_long_hex(char const * str)
+{
+ u64 value;
+ if (sscanf(str, "%Lx", &value) != 1)
+ parse_error("expected long hexadecimal value");
+
+ fflush(stderr);
+ return value;
+}
+
+
+/* name:MESI type:bitmask default:0x0f */
+static void parse_um(struct op_unit_mask * um, char const * line)
+{
+ int seen_name = 0;
+ int seen_type = 0;
+ int seen_default = 0;
+ char const * valueend = line + 1;
+ char const * tagend = line + 1;
+ char const * start = line;
+
+ while (*valueend) {
+ valueend = skip_nonws(valueend);
+
+ while (*tagend != ':' && *tagend)
+ ++tagend;
+
+ if (valueend == tagend)
+ break;
+
+ if (!*tagend)
+ parse_error("parse_um() expected :value");
+
+ ++tagend;
+
+ if (strisprefix(start, "name")) {
+ if (seen_name)
+ parse_error("duplicate name: tag");
+ seen_name = 1;
+ um->name = op_xstrndup(tagend, valueend - tagend);
+ } else if (strisprefix(start, "type")) {
+ if (seen_type)
+ parse_error("duplicate type: tag");
+ seen_type = 1;
+ if (strisprefix(tagend, "mandatory")) {
+ um->unit_type_mask = utm_mandatory;
+ } else if (strisprefix(tagend, "bitmask")) {
+ um->unit_type_mask = utm_bitmask;
+ } else if (strisprefix(tagend, "exclusive")) {
+ um->unit_type_mask = utm_exclusive;
+ } else {
+ parse_error("invalid unit mask type");
+ }
+ } else if (strisprefix(start, "default")) {
+ if (seen_default)
+ parse_error("duplicate default: tag");
+ seen_default = 1;
+ um->default_mask = parse_hex(tagend);
+ } else {
+ parse_error("invalid unit mask tag");
+ }
+
+ valueend = skip_ws(valueend);
+ tagend = valueend;
+ start = valueend;
+ }
+}
+
+
+/* \t0x08 (M)odified cache state */
+static void parse_um_entry(struct op_described_um * entry, char const * line)
+{
+ char const * c = line;
+
+ c = skip_ws(c);
+ entry->value = parse_hex(c);
+ c = skip_nonws(c);
+
+ if (!*c)
+ parse_error("invalid unit mask entry");
+
+ c = skip_ws(c);
+
+ if (!*c)
+ parse_error("invalid unit mask entry");
+
+ entry->desc = xstrdup(c);
+}
+
+
+static struct op_unit_mask * new_unit_mask(void)
+{
+ struct op_unit_mask * um = xmalloc(sizeof(struct op_unit_mask));
+ memset(um, '\0', sizeof(struct op_unit_mask));
+ list_add_tail(&um->um_next, &um_list);
+
+ return um;
+}
+
+
+/*
+ * name:zero type:mandatory default:0x0
+ * \t0x0 No unit mask
+ */
+static void read_unit_masks(char const * file)
+{
+ struct op_unit_mask * um = NULL;
+ char * line;
+ FILE * fp = fopen(file, "r");
+
+ if (!fp) {
+ fprintf(stderr,
+ "oprofile: could not open unit mask description file %s\n", file);
+ exit(EXIT_FAILURE);
+ }
+
+ filename = file;
+ line_nr = 1;
+
+ line = op_get_line(fp);
+
+ while (line) {
+ if (empty_line(line) || comment_line(line))
+ goto next;
+
+ if (line[0] != '\t') {
+ um = new_unit_mask();
+ parse_um(um, line);
+ } else {
+ if (!um)
+ parse_error("no unit mask name line");
+ if (um->num >= MAX_UNIT_MASK)
+ parse_error("oprofile: maximum unit mask entries exceeded");
+
+ parse_um_entry(&um->um[um->num], line);
+ ++(um->num);
+ }
+
+next:
+ free(line);
+ line = op_get_line(fp);
+ ++line_nr;
+ }
+
+ fclose(fp);
+}
+
+
+static u32 parse_counter_mask(char const * str)
+{
+ u32 mask = 0;
+ char const * numstart = str;
+
+ while (*numstart) {
+ mask |= 1 << parse_int(numstart);
+
+ while (*numstart && *numstart != ',')
+ ++numstart;
+ /* skip , unless we reach eos */
+ if (*numstart)
+ ++numstart;
+
+ numstart = skip_ws(numstart);
+ }
+
+ return mask;
+}
+
+
+static struct op_unit_mask * 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)
+ return um;
+ }
+
+ fprintf(stderr, "oprofile: could not find unit mask %s\n", value);
+ exit(EXIT_FAILURE);
+}
+
+
+/* parse either a "tag:value" or a ": trailing description string" */
+static int next_token(char const ** cp, char ** name, char ** value)
+{
+ size_t tag_len;
+ size_t val_len;
+ char const * c = *cp;
+ char const * end;
+ char const * colon;
+
+ c = skip_ws(c);
+ end = colon = c;
+ end = skip_nonws(end);
+
+ colon = strchr(colon, ':');
+
+ if (!colon) {
+ if (*c)
+ parse_error("next_token(): garbage at end of line");
+ return 0;
+ }
+
+ if (colon >= end)
+ parse_error("next_token() expected ':'");
+
+ tag_len = colon - c;
+ val_len = end - (colon + 1);
+
+ if (!tag_len) {
+ /* : trailing description */
+ end = skip_ws(end);
+ *name = xstrdup("desc");
+ *value = xstrdup(end);
+ end += strlen(end);
+ } else {
+ /* tag:value */
+ *name = op_xstrndup(c, tag_len);
+ *value = op_xstrndup(colon + 1, val_len);
+ end = skip_ws(end);
+ }
+
+ *cp = end;
+ return 1;
+}
+
+
+static struct op_event * new_event(void)
+{
+ struct op_event * event = xmalloc(sizeof(struct op_event));
+ memset(event, '\0', sizeof(struct op_event));
+ list_add_tail(&event->event_next, &events_list);
+
+ return event;
+}
+
+
+/* event:0x00 counters:0 um:zero minimum:4096 name:ISSUES : Total issues */
+static void read_events(char const * file)
+{
+ struct op_event * event = NULL;
+ char * line;
+ char * name;
+ char * value;
+ char const * c;
+ int seen_event, seen_counters, seen_um, seen_minimum, seen_name;
+ FILE * fp = fopen(file, "r");
+
+ if (!fp) {
+ fprintf(stderr, "oprofile: could not open event description file %s\n", file);
+ exit(EXIT_FAILURE);
+ }
+
+ filename = file;
+ line_nr = 1;
+
+ line = op_get_line(fp);
+
+ while (line) {
+ if (empty_line(line) || comment_line(line))
+ goto next;
+
+ seen_name = 0;
+ seen_event = 0;
+ seen_counters = 0;
+ seen_um = 0;
+ seen_minimum = 0;
+ event = new_event();
+
+ c = line;
+ while (next_token(&c, &name, &value)) {
+ if (strcmp(name, "name") == 0) {
+ if (seen_name)
+ parse_error("duplicate name: tag");
+ seen_name = 1;
+ if (strchr(value, '/') != NULL)
+ parse_error("invalid event name");
+ if (strchr(value, '.') != NULL)
+ parse_error("invalid event name");
+ event->name = value;
+ } else if (strcmp(name, "event") == 0) {
+ if (seen_event)
+ parse_error("duplicate event: tag");
+ seen_event = 1;
+ event->val = parse_hex(value);
+ free(value);
+ } else if (strcmp(name, "counters") == 0) {
+ if (seen_counters)
+ parse_error("duplicate counters: tag");
+ seen_counters = 1;
+ event->counter_mask = parse_counter_mask(value);
+ free(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;
+ free(value);
+ } else if (strcmp(name, "minimum") == 0) {
+ if (seen_minimum)
+ parse_error("duplicate minimum: tag");
+ seen_minimum = 1;
+ event->min_count = parse_int(value);
+ free(value);
+ } else if (strcmp(name, "desc") == 0) {
+ event->desc = value;
+ } else {
+ parse_error("unknown tag");
+ }
+
+ free(name);
+ }
+next:
+ free(line);
+ line = op_get_line(fp);
+ ++line_nr;
+ }
+
+ fclose(fp);
+}
+
+
+/* usefull for make check */
+static void check_unit_mask(struct op_unit_mask const * um,
+ char const * cpu_name)
+{
+ u32 i;
+
+ if (!um->used) {
+ fprintf(stderr, "um %s is not used\n", um->name);
+ exit(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);
+ } else if (um->unit_type_mask == utm_bitmask) {
+ u32 default_mask = um->default_mask;
+ for (i = 0; i < um->num; ++i)
+ default_mask &= ~um->um[i].value;
+
+ if (default_mask) {
+ fprintf(stderr, "um %s default mask is not valid "
+ "(%s)\n", um->name, cpu_name);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ for (i = 0; i < um->num; ++i) {
+ if (um->default_mask == um->um[i].value)
+ break;
+ }
+
+ if (i == um->num) {
+ fprintf(stderr, "exclusive um %s default value is not "
+ "valid (%s)\n", um->name, cpu_name);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+
+static void load_events(op_cpu cpu_type)
+{
+ 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;
+
+ 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, "/");
+
+ strcat(event_dir, cpu_name);
+ strcat(event_dir, "/");
+
+ event_file = xmalloc(strlen(event_dir) + strlen("events") + 1);
+ strcpy(event_file, event_dir);
+ strcat(event_file, "events");
+
+ um_file = xmalloc(strlen(event_dir) + strlen("unit_masks") + 1);
+ strcpy(um_file, event_dir);
+ strcat(um_file, "unit_masks");
+
+ read_unit_masks(um_file);
+ read_events(event_file);
+
+ /* 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);
+ }
+
+ free(um_file);
+ free(event_file);
+ free(event_dir);
+}
+
+
+struct list_head * op_events(op_cpu cpu_type)
+{
+ load_events(cpu_type);
+ return &events_list;
+}
+
+
+static void delete_unit_mask(struct op_unit_mask * unit)
+{
+ u32 cur;
+ for (cur = 0 ; cur < unit->num ; ++cur) {
+ if (unit->um[cur].desc)
+ free(unit->um[cur].desc);
+ }
+
+ if (unit->name)
+ free(unit->name);
+
+ list_del(&unit->um_next);
+ free(unit);
+}
+
+
+static void delete_event(struct op_event * event)
+{
+ if (event->name)
+ free(event->name);
+ if (event->desc)
+ free(event->desc);
+
+ list_del(&event->event_next);
+ free(event);
+}
+
+
+void op_free_events(void)
+{
+ struct list_head * pos, * pos2;
+ list_for_each_safe(pos, pos2, &events_list) {
+ struct op_event * event = list_entry(pos, struct op_event, event_next);
+ delete_event(event);
+ }
+
+ list_for_each_safe(pos, pos2, &um_list) {
+ struct op_unit_mask * unit = list_entry(pos, struct op_unit_mask, um_next);
+ delete_unit_mask(unit);
+ }
+}
+
+
+static struct op_event * find_event(u32 nr)
+{
+ struct list_head * pos;
+
+ list_for_each(pos, &events_list) {
+ struct op_event * event = list_entry(pos, struct op_event, event_next);
+ if (event->val == nr)
+ return event;
+ }
+
+ return NULL;
+}
+
+
+static FILE * open_event_mapping_file(char const * cpu_name)
+{
+ char * ev_map_file;
+ char * dir;
+ dir = getenv("OPROFILE_EVENTS_DIR");
+ if (dir == NULL)
+ dir = OP_DATADIR;
+
+ ev_map_file = xmalloc(strlen(dir) + strlen("/") + strlen(cpu_name) +
+ strlen("/") + + strlen("event_mappings") + 1);
+ strcpy(ev_map_file, dir);
+ strcat(ev_map_file, "/");
+
+ strcat(ev_map_file, cpu_name);
+ strcat(ev_map_file, "/");
+ strcat(ev_map_file, "event_mappings");
+ filename = ev_map_file;
+ return (fopen(ev_map_file, "r"));
+}
+
+
+/**
+ * This function is PPC64-specific.
+ */
+static char const * get_mapping(u32 nr, FILE * fp)
+{
+ char * line;
+ char * name;
+ char * value;
+ char const * c;
+ char * map = NULL;
+ int seen_event = 0, seen_mmcr0 = 0, seen_mmcr1 = 0, seen_mmcra = 0;
+ u32 mmcr0 = 0;
+ u64 mmcr1 = 0;
+ u32 mmcra = 0;
+ int event_found = 0;
+
+ line_nr = 1;
+ line = op_get_line(fp);
+ while (line && !event_found) {
+ if (empty_line(line) || comment_line(line))
+ goto next;
+
+ seen_event = 0;
+ seen_mmcr0 = 0;
+ seen_mmcr1 = 0;
+ seen_mmcra = 0;
+ mmcr0 = 0;
+ mmcr1 = 0;
+ mmcra = 0;
+
+ c = line;
+ while (next_token(&c, &name, &value)) {
+ if (strcmp(name, "event") == 0) {
+ u32 evt;
+ if (seen_event)
+ parse_error("duplicate event tag");
+ seen_event = 1;
+ evt = parse_hex(value);
+ if (evt == nr)
+ event_found = 1;
+ free(value);
+ } else if (strcmp(name, "mmcr0") == 0) {
+ if (seen_mmcr0)
+ parse_error("duplicate mmcr0 tag");
+ seen_mmcr0 = 1;
+ mmcr0 = parse_hex(value);
+ free(value);
+ } else if (strcmp(name, "mmcr1") == 0) {
+ if (seen_mmcr1)
+ parse_error("duplicate mmcr1: tag");
+ seen_mmcr1 = 1;
+ mmcr1 = parse_long_hex(value);
+ free(value);
+ } else if (strcmp(name, "mmcra") == 0) {
+ if (seen_mmcra)
+ parse_error("duplicate mmcra: tag");
+ seen_mmcra = 1;
+ mmcra = parse_hex(value);
+ free(value);
+ } else {
+ parse_error("unknown tag");
+ }
+
+ free(name);
+ }
+next:
+ free(line);
+ line = op_get_line(fp);
+ ++line_nr;
+ }
+ if (event_found) {
+ if (!seen_mmcr0 || !seen_mmcr1 || !seen_mmcra) {
+ fprintf(stderr, "Error: Missing information in line %d of event mapping file %s\n", line_nr, filename);
+ exit(EXIT_FAILURE);
+ }
+ map = xmalloc(70);
+ snprintf(map, 70, "mmcr0:%u mmcr1:%Lu mmcra:%u",
+ mmcr0, mmcr1, mmcra);
+ }
+
+ return map;
+}
+
+
+char const * find_mapping_for_event(u32 nr, op_cpu cpu_type)
+{
+ char const * cpu_name = op_get_cpu_name(cpu_type);
+ FILE * fp = open_event_mapping_file(cpu_name);
+ char const * map = NULL;
+ switch (cpu_type) {
+ case CPU_PPC64_PA6T:
+ case CPU_PPC64_970:
+ case CPU_PPC64_970MP:
+ case CPU_PPC64_POWER4:
+ case CPU_PPC64_POWER5:
+ case CPU_PPC64_POWER5p:
+ case CPU_PPC64_POWER5pp:
+ case CPU_PPC64_POWER6:
+ if (!fp) {
+ fprintf(stderr, "oprofile: could not open event mapping file %s\n", filename);
+ exit(EXIT_FAILURE);
+ } else {
+ map = get_mapping(nr, fp);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (fp)
+ fclose(fp);
+
+ return map;
+}
+
+
+struct op_event * find_event_by_name(char const * name)
+{
+ 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)
+ return event;
+ }
+
+ return NULL;
+}
+
+
+struct op_event * op_find_event(op_cpu cpu_type, u32 nr)
+{
+ struct op_event * event;
+
+ load_events(cpu_type);
+
+ event = find_event(nr);
+
+ return event;
+}
+
+
+int op_check_events(int ctr, u32 nr, u32 um, op_cpu cpu_type)
+{
+ int ret = OP_OK_EVENT;
+ struct op_event * event;
+ size_t i;
+ u32 ctr_mask = 1 << ctr;
+
+ load_events(cpu_type);
+
+ event = find_event(nr);
+
+ if (!event) {
+ ret |= OP_INVALID_EVENT;
+ return ret;
+ }
+
+ 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;
+
+ } 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;
+ }
+
+ return ret;
+}
+
+
+void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr)
+{
+ descr->name = "";
+ descr->um = 0x0;
+ /* A fixed value of CPU cycles; this should ensure good
+ * granulity even on faster CPUs, though it will generate more
+ * interrupts.
+ */
+ descr->count = 100000;
+
+ switch (cpu_type) {
+ case CPU_PPRO:
+ case CPU_PII:
+ case CPU_PIII:
+ case CPU_P6_MOBILE:
+ case CPU_CORE:
+ case CPU_CORE_2:
+ case CPU_ATHLON:
+ case CPU_HAMMER:
+ case CPU_FAMILY10:
+ descr->name = "CPU_CLK_UNHALTED";
+ break;
+
+ case CPU_RTC:
+ descr->name = "RTC_INTERRUPTS";
+ descr->count = 1024;
+ break;
+
+ case CPU_P4:
+ case CPU_P4_HT2:
+ descr->name = "GLOBAL_POWER_EVENTS";
+ descr->um = 0x1;
+ break;
+
+ case CPU_IA64:
+ case CPU_IA64_1:
+ case CPU_IA64_2:
+ descr->count = 1000000;
+ descr->name = "CPU_CYCLES";
+ break;
+
+ case CPU_AXP_EV4:
+ case CPU_AXP_EV5:
+ case CPU_AXP_PCA56:
+ case CPU_AXP_EV6:
+ case CPU_AXP_EV67:
+ descr->name = "CYCLES";
+ break;
+
+ // we could possibly use the CCNT
+ case CPU_ARM_XSCALE1:
+ case CPU_ARM_XSCALE2:
+ case CPU_ARM_MPCORE:
+ case CPU_ARM_V6:
+ case CPU_AVR32:
+ descr->name = "CPU_CYCLES";
+ break;
+
+ case CPU_PPC64_PA6T:
+ case CPU_PPC64_970:
+ case CPU_PPC64_970MP:
+ case CPU_PPC_7450:
+ case CPU_PPC64_POWER4:
+ case CPU_PPC64_POWER5:
+ case CPU_PPC64_POWER6:
+ case CPU_PPC64_POWER5p:
+ case CPU_PPC64_POWER5pp:
+ case CPU_PPC64_CELL:
+ descr->name = "CYCLES";
+ break;
+
+ case CPU_MIPS_20K:
+ descr->name = "CYCLES";
+ break;
+
+ case CPU_MIPS_24K:
+ descr->name = "INSTRUCTIONS";
+ break;
+
+ case CPU_MIPS_34K:
+ descr->name = "INSTRUCTIONS";
+ break;
+
+ case CPU_MIPS_5K:
+ case CPU_MIPS_25K:
+ descr->name = "CYCLES";
+ break;
+
+ case CPU_MIPS_R10000:
+ case CPU_MIPS_R12000:
+ descr->name = "INSTRUCTIONS_GRADUATED";
+ break;
+
+ case CPU_MIPS_RM7000:
+ case CPU_MIPS_RM9000:
+ descr->name = "INSTRUCTIONS_ISSUED";
+ break;
+
+ case CPU_MIPS_SB1:
+ descr->name = "INSN_SURVIVED_STAGE7";
+ break;
+
+ case CPU_MIPS_VR5432:
+ case CPU_MIPS_VR5500:
+ descr->name = "INSTRUCTIONS_EXECUTED";
+ break;
+
+ case CPU_PPC_E500:
+ case CPU_PPC_E500_2:
+ case CPU_PPC_E300:
+ descr->name = "CPU_CLK";
+ break;
+
+ // don't use default, if someone add a cpu he wants a compiler
+ // warning if he forgets to handle it here.
+ case CPU_TIMER_INT:
+ case CPU_NO_GOOD:
+ case MAX_CPU_TYPE:
+ break;
+ }
+}
diff --git a/libop/op_events.h b/libop/op_events.h
new file mode 100644
index 0000000..f6462fc
--- /dev/null
+++ b/libop/op_events.h
@@ -0,0 +1,125 @@
+/**
+ * @file op_events.h
+ * Details of PMC profiling events
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_EVENTS_H
+#define OP_EVENTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "op_cpu_type.h"
+#include "op_types.h"
+#include "op_list.h"
+
+/** Describe an unit mask type. Events can optionally use a filter called
+ * the unit mask. the mask type can be a bitmask or a discrete value */
+enum unit_mask_type {
+ utm_mandatory, /**< useless but required by the hardware */
+ utm_exclusive, /**< only one of the values is allowed */
+ utm_bitmask /**< bitmask */
+};
+
+/** up to thirty two allowed unit masks */
+#define MAX_UNIT_MASK 32
+
+
+/** Describe an unit mask. */
+struct op_unit_mask {
+ char * name; /**< name of unit mask type */
+ u32 num; /**< number of possible unit masks */
+ enum unit_mask_type unit_type_mask;
+ u32 default_mask; /**< only the gui use it */
+ struct op_described_um {
+ u32 value;
+ char * desc;
+ } um[MAX_UNIT_MASK];
+ struct list_head um_next; /**< next um in list */
+ int used; /**< used by events file parser */
+};
+
+
+/** Describe an event. */
+struct op_event {
+ u32 counter_mask; /**< bitmask of allowed counter */
+ u32 val; /**< event number */
+ /** which unit mask if any allowed */
+ struct op_unit_mask * unit;
+ char * name; /**< the event name */
+ char * desc; /**< the event description */
+ int min_count; /**< minimum counter value allowed */
+ struct list_head event_next; /**< next event in list */
+};
+
+/** Return the known events list. Idempotent */
+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);
+
+/** Find a given event by name */
+struct op_event * find_event_by_name(char const * name);
+
+/**
+ * Find a mapping for a given event ID for architectures requiring additional information
+ * from what is held in the events file.
+ */
+char const * find_mapping_for_event(u32 val, op_cpu cpu_type);
+
+
+/** op_check_events() return code */
+enum op_event_check {
+ OP_OK_EVENT = 0, /**< event is valid and allowed */
+ OP_INVALID_EVENT = 1, /**< event number is invalid */
+ OP_INVALID_UM = 2, /**< unit mask is invalid */
+ OP_INVALID_COUNTER = 4 /**< event is not allowed for the given counter */
+};
+
+/**
+ * sanity check event values
+ * @param ctr counter number
+ * @param event value for counter
+ * @param um unit mask for counter
+ * @param cpu_type processor type
+ *
+ * Check that the counter event and unit mask values are allowed.
+ *
+ * The function returns bitmask of failure cause 0 otherwise
+ *
+ * \sa op_cpu, OP_EVENTS_OK
+ */
+int op_check_events(int ctr, u32 event, u32 um, op_cpu cpu_type);
+
+/**
+ * free memory used by any call to above function. Need to be called only once
+ */
+void op_free_events(void);
+
+struct op_default_event_descr {
+ char * name;
+ unsigned long count;
+ unsigned long um;
+};
+
+/**
+ * op_default_event - return the details of the default event
+ * @param cpu_type cpu type
+ * @param descr filled event description
+ *
+ * Fills in the event description if applicable
+ */
+void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_EVENTS_H */
diff --git a/libop/op_get_interface.c b/libop/op_get_interface.c
new file mode 100644
index 0000000..bdf72a5
--- /dev/null
+++ b/libop/op_get_interface.c
@@ -0,0 +1,32 @@
+/**
+ * @file op_get_interface.c
+ * Determine which oprofile kernel interface used
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Will Cohen
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "op_cpu_type.h"
+#include "op_file.h"
+
+op_interface op_get_interface(void)
+{
+ static op_interface current_interface = OP_INTERFACE_NO_GOOD;
+
+ if (current_interface != OP_INTERFACE_NO_GOOD)
+ return current_interface;
+
+ if (op_file_readable("/proc/sys/dev/oprofile/cpu_type")) {
+ current_interface = OP_INTERFACE_24;
+ } else if (op_file_readable("/dev/oprofile/cpu_type")) {
+ current_interface = OP_INTERFACE_26;
+ }
+
+ return current_interface;
+}
diff --git a/libop/op_hw_config.h b/libop/op_hw_config.h
new file mode 100644
index 0000000..169b36b
--- /dev/null
+++ b/libop/op_hw_config.h
@@ -0,0 +1,30 @@
+/**
+ * @file op_hw_config.h
+ * Configuration parameters that are dependent on CPU/architecture
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_HW_CONFIG_H
+#define OP_HW_CONFIG_H
+
+/** maximum number of counters, up to 4 for Athlon (18 for P4). The primary
+ * use of this variable is for static/local array dimension. Never use it in
+ * loop or in array index access/index checking unless you know what you
+ * made. */
+#ifdef __alpha__
+#define OP_MAX_COUNTERS 20
+#else
+#define OP_MAX_COUNTERS 8
+#endif
+
+/** maximum number of events between interrupts. Counters are 40 bits, but
+ * for convenience we only use 32 bits. The top bit is used for overflow
+ * detection, so user can set up to (2^31)-1 */
+#define OP_MAX_PERF_COUNT 2147483647UL
+
+#endif /* OP_HW_CONFIG_H */
diff --git a/libop/op_interface.h b/libop/op_interface.h
new file mode 100644
index 0000000..fa2ecbd
--- /dev/null
+++ b/libop/op_interface.h
@@ -0,0 +1,87 @@
+/**
+ * @file op_interface.h
+ *
+ * Module / user space interface for 2.4
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_INTERFACE_H
+#define OP_INTERFACE_H
+
+#include "op_config.h"
+#include "op_types.h"
+
+/*@{\name notifications types encoded in op_note::type */
+/** fork(),vfork(),clone() */
+#define OP_FORK 1
+/** mapping */
+#define OP_MAP 2
+/** execve() */
+#define OP_EXEC 4
+/** init_module() */
+#define OP_DROP_MODULES 8
+/** exit() */
+#define OP_EXIT 16
+/*@}*/
+
+/** Data type to transfer samples counts from the module to the daemon */
+struct op_sample {
+ unsigned long eip; /**< eip value where occur interrupt */
+ u32 counter; /**< counter nr */
+ u32 pid; /**< 32 bits can hold any pid */
+ u32 tgid; /**< always equal to pid for kernel < 2.4.0 */
+};
+
+/** the current kernel-side profiler state */
+enum oprof_state {
+ STOPPED = 0,
+ STOPPING = 1,
+ RUNNING = 2
+};
+
+/**
+ * The head structure of a kernel sample buffer.
+ */
+struct op_buffer_head {
+ int cpu_nr; /**< the CPU number of this buffer */
+ size_t count; /**< number of samples in this buffer */
+ enum oprof_state state; /**< current profiler state */
+ struct op_sample buffer[0]; /**< the sample buffer */
+} __attribute__((__packed__));
+
+/**
+ * Data type used by the module to notify daemon of fork/exit/mapping etc.
+ * Meanings of fields depend on the type of notification encoded in the type
+ * field.
+ * \sa OP_FORK, OP_EXEC, OP_MAP, OP_DROP_MODULES and OP_EXIT
+ */
+struct op_note {
+ unsigned long addr;
+ unsigned long len;
+ unsigned long offset;
+ unsigned int hash;
+ unsigned int pid;
+ unsigned int tgid;
+ unsigned short type;
+};
+
+/**
+ * A path component. Directory name are stored as a stack of path components.
+ * Note than the name index acts also as an unique identifier
+ */
+struct op_hash_index {
+ /** index inside the string pool */
+ u32 name;
+ /** parent component, zero if this component is the root */
+ u32 parent;
+} __attribute__((__packed__));
+
+/** size of hash map in bytes */
+#define OP_HASH_MAP_SIZE (OP_HASH_MAP_NR * sizeof(struct op_hash_index) + POOL_SIZE)
+
+#endif /* OP_INTERFACE_H */
diff --git a/libop/op_mangle.c b/libop/op_mangle.c
new file mode 100644
index 0000000..1efe5b1
--- /dev/null
+++ b/libop/op_mangle.c
@@ -0,0 +1,104 @@
+/**
+ * @file op_mangle.c
+ * Mangling of sample file names
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "op_mangle.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include "op_libiberty.h"
+
+#include "op_sample_file.h"
+#include "op_config.h"
+
+static void append_image(char * dest, int flags, int anon, char const * name, char const * anon_name)
+{
+ if ((flags & MANGLE_KERNEL) && !strchr(name, '/')) {
+ strcat(dest, "{kern}/");
+ } else if (anon) {
+ strcat(dest, "{anon:");
+ strcat(dest, anon_name);
+ strcat(dest,"}/");
+ } else {
+ strcat(dest, "{root}/");
+ }
+
+ strcat(dest, name);
+ strcat(dest, "/");
+}
+
+char * op_mangle_filename(struct mangle_values const * values)
+{
+ char * mangled;
+ size_t len;
+ int anon = values->flags & MANGLE_ANON;
+ int cg_anon = values->flags & MANGLE_CG_ANON;
+ /* if dep_name != image_name we need to invert them (and so revert them
+ * unconditionally because if they are equal it doesn't hurt to invert
+ * them), see P:3, FIXME: this is a bit weirds, we prolly need to
+ * reword pp_interface */
+ char const * image_name = values->dep_name;
+ char const * anon_name = values->anon_name;
+ char const * dep_name = values->image_name;
+ char const * cg_image_name = values->cg_image_name;
+
+ len = strlen(op_samples_current_dir) + strlen(dep_name) + 1
+ + strlen(values->event_name) + 1 + strlen(image_name) + 1;
+
+ if (values->flags & MANGLE_CALLGRAPH)
+ len += strlen(cg_image_name) + 1;
+
+ if (anon || cg_anon)
+ len += strlen(anon_name);
+
+ /* provision for tgid, tid, unit_mask, cpu and some {root}, {dep},
+ * {kern}, {anon} and {cg} marker */
+ /* FIXME: too ugly */
+ len += 256;
+
+ mangled = xmalloc(len);
+
+ strcpy(mangled, op_samples_current_dir);
+ append_image(mangled, values->flags, 0, image_name, anon_name);
+
+ strcat(mangled, "{dep}" "/");
+ append_image(mangled, values->flags, anon, dep_name, anon_name);
+
+ if (values->flags & MANGLE_CALLGRAPH) {
+ strcat(mangled, "{cg}" "/");
+ append_image(mangled, values->flags, cg_anon,
+ cg_image_name, anon_name);
+ }
+
+ strcat(mangled, values->event_name);
+ sprintf(mangled + strlen(mangled), ".%d.%d.",
+ values->count, values->unit_mask);
+
+ if (values->flags & MANGLE_TGID) {
+ sprintf(mangled + strlen(mangled), "%d.", values->tgid);
+ } else {
+ sprintf(mangled + strlen(mangled), "%s.", "all");
+ }
+
+ if (values->flags & MANGLE_TID) {
+ sprintf(mangled + strlen(mangled), "%d.", values->tid);
+ } else {
+ sprintf(mangled + strlen(mangled), "%s.", "all");
+ }
+
+ if (values->flags & MANGLE_CPU) {
+ sprintf(mangled + strlen(mangled), "%d", values->cpu);
+ } else {
+ sprintf(mangled + strlen(mangled), "%s", "all");
+ }
+
+ return mangled;
+}
diff --git a/libop/op_mangle.h b/libop/op_mangle.h
new file mode 100644
index 0000000..9b600dc
--- /dev/null
+++ b/libop/op_mangle.h
@@ -0,0 +1,66 @@
+/**
+ * @file op_mangle.h
+ * Mangling of sample file names
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_MANGLE_H
+#define OP_MANGLE_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum mangle_flags {
+ MANGLE_NONE = 0,
+ MANGLE_CPU = (1 << 0),
+ MANGLE_TGID = (1 << 1),
+ MANGLE_TID = (1 << 2),
+ MANGLE_KERNEL = (1 << 3),
+ MANGLE_CALLGRAPH = (1 << 4),
+ MANGLE_ANON = (1 << 5),
+ MANGLE_CG_ANON = (1 << 6),
+};
+
+/**
+ * Temporary structure for passing parameters to
+ * op_mangle_filename.
+ */
+struct mangle_values {
+ int flags;
+
+ char const * image_name;
+ char const * anon_name;
+ char const * dep_name;
+ char const * cg_image_name;
+ char const * event_name;
+ int count;
+ unsigned int unit_mask;
+ pid_t tgid;
+ pid_t tid;
+ int cpu;
+};
+
+/**
+ * op_mangle_filename - mangle a sample filename
+ * @param values parameters to use as mangling input
+ *
+ * See also PP:3 for the encoding scheme
+ *
+ * Returns a char* pointer to the mangled string. Caller
+ * is responsible for freeing this string.
+ */
+char * op_mangle_filename(struct mangle_values const * values);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_MANGLE_H */
diff --git a/libop/op_parse_event.c b/libop/op_parse_event.c
new file mode 100644
index 0000000..920d617
--- /dev/null
+++ b/libop/op_parse_event.c
@@ -0,0 +1,120 @@
+/**
+ * @file op_parse_event.c
+ * event parsing
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "op_parse_event.h"
+#include "op_string.h"
+
+static char * next_part(char const ** str)
+{
+ char const * c;
+ char * ret;
+
+ if ((*str)[0] == '\0')
+ return NULL;
+
+ if ((*str)[0] == ':')
+ ++(*str);
+
+ c = *str;
+
+ while (*c != '\0' && *c != ':')
+ ++c;
+
+ if (c == *str)
+ return NULL;
+
+ ret = op_xstrndup(*str, c - *str);
+ *str += c - *str;
+ return ret;
+}
+
+
+static int parse_ulong(char const * str)
+{
+ unsigned long value;
+ char * end;
+ value = strtoul(str, &end, 0);
+ if (end && *end) {
+ fprintf(stderr, "Invalid event part %s\n", str);
+ exit(EXIT_FAILURE);
+ }
+
+ return value;
+}
+
+
+size_t parse_events(struct parsed_event * parsed_events, size_t max_events,
+ char const * const * events)
+{
+ size_t i = 0;
+
+ while (events[i]) {
+ char const * cp = events[i];
+ char * part = next_part(&cp);
+
+ if (i >= max_events) {
+ fprintf(stderr, "Too many events specified: CPU "
+ "only has %lu counters.\n",
+ (unsigned long) max_events);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!part) {
+ fprintf(stderr, "Invalid event %s\n", cp);
+ exit(EXIT_FAILURE);
+ }
+
+ parsed_events[i].name = part;
+
+ part = next_part(&cp);
+
+ if (!part) {
+ fprintf(stderr, "Invalid count for event %s\n", events[i]);
+ exit(EXIT_FAILURE);
+ }
+
+ parsed_events[i].count = parse_ulong(part);
+ free(part);
+
+ parsed_events[i].unit_mask = 0;
+ part = next_part(&cp);
+
+ if (part) {
+ parsed_events[i].unit_mask = parse_ulong(part);
+ free(part);
+ }
+
+ parsed_events[i].kernel = 1;
+ part = next_part(&cp);
+
+ if (part) {
+ parsed_events[i].kernel = parse_ulong(part);
+ free(part);
+ }
+
+ parsed_events[i].user = 1;
+ part = next_part(&cp);
+
+ if (part) {
+ parsed_events[i].user = parse_ulong(part);
+ free(part);
+ }
+
+ ++i;
+ }
+
+ return i;
+}
diff --git a/libop/op_parse_event.h b/libop/op_parse_event.h
new file mode 100644
index 0000000..247a355
--- /dev/null
+++ b/libop/op_parse_event.h
@@ -0,0 +1,42 @@
+/**
+ * @file op_parse_event.h
+ * event parsing
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_PARSE_EVENT_H
+#define OP_PARSE_EVENT_H
+
+#include <stddef.h>
+
+struct parsed_event {
+ char * name;
+ int count;
+ int unit_mask;
+ int kernel;
+ int user;
+};
+
+/**
+ * @param parsed_events array of events to fill in
+ * @param max_events size of parsed_events
+ * @param events null terminated array of events string on the form
+ * event_name:count[:unit_mask:kernel:user]
+ *
+ * parse events given by the nil terminated array events and fill in
+ * parsed_events with results. Events validity are not checked except.
+ * A fatal error occur if number of events is greater than max_events.
+ *
+ * Return the number of events parsed.
+ */
+size_t parse_events(struct parsed_event * parsed_events, size_t max_events,
+ char const * const * events);
+
+#endif /* !OP_PARSE_EVENT_H */
diff --git a/libop/op_sample_file.h b/libop/op_sample_file.h
new file mode 100644
index 0000000..4f9f1d0
--- /dev/null
+++ b/libop/op_sample_file.h
@@ -0,0 +1,42 @@
+/**
+ * @file op_sample_file.h
+ * Sample file format
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_SAMPLE_FILE_H
+#define OP_SAMPLE_FILE_H
+
+#include "op_types.h"
+
+#include <stdint.h>
+#include <time.h>
+
+/* header of the sample files */
+struct opd_header {
+ u8 magic[4];
+ u32 version;
+ u32 cpu_type;
+ u32 ctr_event;
+ u32 ctr_um;
+ u32 ctr_count;
+ // for cg file the from_cg_is_kernel
+ u32 is_kernel;
+ double cpu_speed;
+ time_t mtime;
+ u32 cg_to_is_kernel;
+ /* spu_profile=1 says sample file contains Cell BE SPU profile data */
+ u32 spu_profile;
+ uint64_t embedded_offset;
+ u64 anon_start;
+ u64 cg_to_anon_start;
+ /* binary compatibility reserve */
+ u32 reserved1[1];
+};
+
+#endif /* OP_SAMPLE_FILE_H */