[PATCH 1/3] perf, tool: Add parser generator for events parsing

From: Jiri Olsa
Date: Thu Dec 15 2011 - 10:31:09 EST


Changing event parsing to use flex/bison parse generator.
The event syntax stays as it is.

grammar description:

events: events ',' event | event

event: event_tracepoint |
event_raw |
event_numeric |
event_symbolic |
event_generic_hw |
event_breakpoint

event_tracepoint: PE_NAME_TP ':' PE_NAME_TP modifier
event_raw: PE_SEP_RAW PE_VALUE modifier
event_numeric: PE_VALUE ':' PE_VALUE modifier
event_symbolic: PE_NAME_SYM modifier
event_generic_hw: PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT modifier |
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT modifier |
PE_NAME_CACHE_TYPE modifier
event_breakpoint: PE_SEP_BP ':' PE_VALUE event_breakpoint_type modifier
event_breakpoint_type: PE_MODIFIER_BPTYPE | empty
modifier: PE_MODIFIER_EVENT | empty

PE_NAME_SYM: cpu-cycles|cycles |
stalled-cycles-frontend|idle-cycles-frontend |
stalled-cycles-backend|idle-cycles-backend |
instructions |
cache-references |
cache-misses |
branch-instructions|branches |
branch-misses |
bus-cycles |
cpu-clock |
task-clock |
page-faults|faults |
minor-faults |
major-faults |
context-switches|cs |
cpu-migrations|migrations |
alignment-faults |
emulation-faults

PE_NAME_CACHE_TYPE: L1-dcache|l1-d|l1d|L1-data |
L1-icache|l1-i|l1i|L1-instruction |
LLC|L2 |
dTLB|d-tlb|Data-TLB |
iTLB|i-tlb|Instruction-TLB |
branch|branches|bpu|btb|bpc |
node

PE_NAME_CACHE_OP_RESULT: load|loads|read |
store|stores|write |
prefetch|prefetches |
speculative-read|speculative-load |
refs|Reference|ops|access |
misses|miss

PE_SEP_RAW: 'r'
PE_SEP_BP: 'mem'
PE_MODIFIER_EVENT: :[ukhp]{1,2}
PE_MODIFIER_BPTYPE: :[rwx]{1,3}
PE_NAME_TP: [a-zA-Z_*?]+
PE_VALUE: number

Added flex/bison files for event grammar parsing. Added
flex/bison Makefile rules plus few special dependencies.

Signed-off-by: Jiri Olsa <jolsa@xxxxxxxxxx>
---
tools/perf/Makefile | 21 ++
tools/perf/util/parse-events-bison.y | 119 +++++++++
tools/perf/util/parse-events-flex.l | 111 ++++++++
tools/perf/util/parse-events.c | 459 +++++++++++-----------------------
tools/perf/util/parse-events.h | 9 +
5 files changed, 411 insertions(+), 308 deletions(-)
create mode 100644 tools/perf/util/parse-events-bison.y
create mode 100644 tools/perf/util/parse-events-flex.l

diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index ac86d67..ef6b621 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -47,6 +47,8 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \

CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
+FLEX = $(CROSS_COMPILE)flex
+BISON= $(CROSS_COMPILE)bison

# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
@@ -341,6 +343,8 @@ LIB_OBJS += $(OUTPUT)util/session.o
LIB_OBJS += $(OUTPUT)util/thread.o
LIB_OBJS += $(OUTPUT)util/thread_map.o
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
+LIB_OBJS += $(OUTPUT)util/parse-events-flex.o
+LIB_OBJS += $(OUTPUT)util/parse-events-bison.o
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
@@ -627,6 +631,8 @@ ifndef V
QUIET_LINK = @echo ' ' LINK $@;
QUIET_MKDIR = @echo ' ' MKDIR $@;
QUIET_GEN = @echo ' ' GEN $@;
+ QUIET_FLEX = @echo ' ' FLEX $@;
+ QUIET_BISON = @echo ' ' BISON $@;
endif
endif

@@ -713,6 +719,10 @@ $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
$(OUTPUT)%.o: %.S
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+$(OUTPUT)%.c: %.l
+ $(QUIET_FLEX)$(FLEX) --header-file=$*.h -t $< > $@
+$(OUTPUT)%.c: %.y
+ $(QUIET_BISON)$(BISON) -v $< -d -o $@

$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
@@ -739,6 +749,16 @@ $(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<

+
+.SECONDARY: util/parse-events-flex.c util/parse-events-flex.h
+.SECONDARY: util/parse-events-bison.c util/parse-events-bison.h
+
+util/parse-events.o: util/parse-events-flex.c
+util/parse-events-flex.c: util/parse-events-bison.c
+
+$(OUTPUT)util/parse-events-flex.o: util/parse-events-flex.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
+
$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<

@@ -910,6 +930,7 @@ clean:
$(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS)
$(RM) $(ALL_PROGRAMS) perf
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
+ $(RM) util/parse-events-flex.[ch] util/parse-events-bison.[cho]*
$(MAKE) -C Documentation/ clean
$(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS
$(python-clean)
diff --git a/tools/perf/util/parse-events-bison.y b/tools/perf/util/parse-events-bison.y
new file mode 100644
index 0000000..403cbf6
--- /dev/null
+++ b/tools/perf/util/parse-events-bison.y
@@ -0,0 +1,119 @@
+
+%name-prefix "parse_events_"
+
+%{
+
+#define YYDEBUG 1
+
+#include <linux/compiler.h>
+#include "types.h"
+#include "util.h"
+#include "parse-events.h"
+
+extern int parse_events_lex (void);
+
+#define ABORT_ON(val) \
+do { \
+ if (val) \
+ YYABORT; \
+} while (0)
+
+%}
+
+%token PE_VALUE
+%token PE_MODIFIER_BPTYPE PE_MODIFIER_EVENT
+%token PE_NAME_TP PE_NAME_SYM
+%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
+%token PE_SEP_RAW PE_SEP_BP
+%token PE_ERROR
+%type <l> PE_VALUE
+%type <s> PE_NAME_TP
+%type <s> PE_NAME_SYM
+%type <s> PE_NAME_CACHE_TYPE
+%type <s> PE_NAME_CACHE_OP_RESULT
+%type <s> PE_MODIFIER_EVENT
+%type <s> modifier
+%type <s> PE_MODIFIER_BPTYPE
+%type <s> event_breakpoint_type
+
+%union
+{
+ char *s;
+ long l;
+}
+
+%%
+
+events: events ',' event | event
+
+event: event_tracepoint |
+ event_raw |
+ event_numeric |
+ event_symbolic |
+ event_generic_hw |
+ event_breakpoint
+
+event_tracepoint: PE_NAME_TP ':' PE_NAME_TP modifier
+{
+ ABORT_ON(parse_events_add_tracepoint($1, $3, $4));
+}
+
+event_raw: PE_SEP_RAW PE_VALUE modifier
+{
+ ABORT_ON(parse_events_add_raw($2, $3));
+}
+
+event_numeric: PE_VALUE ':' PE_VALUE modifier
+{
+ ABORT_ON(parse_events_add_numeric($1, $3, $4));
+}
+
+event_symbolic: PE_NAME_SYM modifier
+{
+ ABORT_ON(parse_events_add_symbolic($1, $2));
+}
+
+event_generic_hw:
+PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT modifier
+{
+ ABORT_ON(parse_events_add_generic_hw($1, $3, $5, $6));
+}
+|
+PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT modifier
+{
+ ABORT_ON(parse_events_add_generic_hw($1, $3, NULL, $4));
+}
+|
+PE_NAME_CACHE_TYPE modifier
+{
+ ABORT_ON(parse_events_add_generic_hw($1, NULL, NULL, $2));
+}
+
+event_breakpoint: PE_SEP_BP ':' PE_VALUE event_breakpoint_type modifier
+{
+ ABORT_ON(parse_events_add_breakpoint((void *) $3, $4, $5))
+}
+
+event_breakpoint_type: PE_MODIFIER_BPTYPE
+{
+ $$ = $1
+}
+|
+{
+ $$ = NULL;
+}
+
+modifier: PE_MODIFIER_EVENT
+{
+ $$ = $1;
+}
+|
+{
+ $$ = NULL;
+}
+
+%%
+
+void parse_events_error(char const *msg __used)
+{
+}
diff --git a/tools/perf/util/parse-events-flex.l b/tools/perf/util/parse-events-flex.l
new file mode 100644
index 0000000..84b9b0e
--- /dev/null
+++ b/tools/perf/util/parse-events-flex.l
@@ -0,0 +1,111 @@
+
+%option prefix="parse_events_"
+
+%{
+#include <errno.h>
+#include "parse-events-bison.h"
+
+enum {
+ VALUE_DEC,
+ VALUE_HEX,
+};
+
+static int value(int base)
+{
+ long num;
+
+ errno = 0;
+ num = strtol(parse_events_text, NULL, base);
+ if (errno)
+ return PE_ERROR;
+
+ parse_events_lval.l = num;
+ return PE_VALUE;
+}
+
+static int __str(char *s, int token)
+{
+ parse_events_lval.s = strdup(s);
+ return token;
+}
+
+static int str(int token)
+{
+ return __str(parse_events_text, token);
+}
+
+static int mod(int token)
+{
+ return __str(parse_events_text + 1, token);
+}
+
+%}
+
+num_dec [0-9]+
+num_hex 0x[a-fA-F0-9]+
+name_tp [a-zA-Z_*?]+
+
+%x BP
+
+%%
+
+cpu-cycles|cycles |
+stalled-cycles-frontend|idle-cycles-frontend |
+stalled-cycles-backend|idle-cycles-backend |
+instructions |
+cache-references |
+cache-misses |
+branch-instructions|branches |
+branch-misses |
+bus-cycles |
+cpu-clock |
+task-clock |
+page-faults|faults |
+minor-faults |
+major-faults |
+context-switches|cs |
+cpu-migrations|migrations |
+alignment-faults |
+emulation-faults { return str(PE_NAME_SYM); }
+
+L1-dcache|l1-d|l1d|L1-data |
+L1-icache|l1-i|l1i|L1-instruction |
+LLC|L2 |
+dTLB|d-tlb|Data-TLB |
+iTLB|i-tlb|Instruction-TLB |
+branch|branches|bpu|btb|bpc |
+node { return str(PE_NAME_CACHE_TYPE); }
+
+load|loads|read |
+store|stores|write |
+prefetch|prefetches |
+speculative-read|speculative-load |
+refs|Reference|ops|access |
+misses|miss { return str(PE_NAME_CACHE_OP_RESULT); }
+
+{num_dec} { return value(10); }
+{num_hex} { return value(16); }
+
+r { return PE_SEP_RAW; }
+
+mem { BEGIN(BP); return PE_SEP_BP; }
+<BP>:[rwx]{1,3} { return mod(PE_MODIFIER_BPTYPE); }
+<BP>:[ukhp]{1,2} { return mod(PE_MODIFIER_EVENT); }
+<BP>: { return ':'; }
+<BP>{num_dec} { return value(10); }
+<BP>{num_hex} { return value(16); }
+<BP><<EOF>> { BEGIN(INITIAL); }
+<BP>. { BEGIN(INITIAL); }
+
+:[ukhp]{1,2} { return mod(PE_MODIFIER_EVENT); }
+{name_tp} { return str(PE_NAME_TP); }
+- { return '-'; }
+, { return ','; }
+: { return ':'; }
+
+%%
+
+int parse_events_wrap(void)
+{
+ return 1;
+}
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 586ab3f..b9c4189 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -11,6 +11,9 @@
#include "cache.h"
#include "header.h"
#include "debugfs.h"
+#include "parse-events-flex.h"
+
+#define MAX_NAME_LEN 100

struct event_symbol {
u8 type;
@@ -19,11 +22,9 @@ struct event_symbol {
const char *alias;
};

-enum event_result {
- EVT_FAILED,
- EVT_HANDLED,
- EVT_HANDLED_ALL
-};
+int parse_events_parse(void);
+
+static struct perf_evlist *__evlist;

#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
@@ -352,7 +353,31 @@ const char *__event_name(int type, u64 config)
return "unknown";
}

-static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
+static void
+parse_event_modifier(char *mod, struct perf_event_attr *attr);
+
+static int add_event(struct perf_event_attr *attr, char *name, char *mod)
+{
+ struct perf_evsel *evsel;
+ int len = strlen(name);
+
+ parse_event_modifier(mod, attr);
+
+ evsel = perf_evsel__new(attr, __evlist->nr_entries);
+ if (!evsel)
+ return -ENOMEM;
+
+ perf_evlist__add(__evlist, evsel);
+
+ evsel->name = zalloc(len);
+ if (!evsel->name)
+ return -ENOMEM;
+
+ strncpy(evsel->name, name, len);
+ return 0;
+}
+
+static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
{
int i, j;
int n, longest = -1;
@@ -360,58 +385,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int
for (i = 0; i < size; i++) {
for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
n = strlen(names[i][j]);
- if (n > longest && !strncasecmp(*str, names[i][j], n))
+ if (n > longest && !strncasecmp(str, names[i][j], n))
longest = n;
}
- if (longest > 0) {
- *str += longest;
+ if (longest > 0)
return i;
- }
}

return -1;
}

-static enum event_result
-parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
+int parse_events_add_generic_hw(char *type, char *op_result1, char *op_result2,
+ char *mod)
{
- const char *s = *str;
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];
int cache_type = -1, cache_op = -1, cache_result = -1;
+ char *op_result[2] = { op_result1, op_result2 };
+ int i, n;

- cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
/*
* No fallback - if we cannot get a clear cache type
* then bail out:
*/
+ cache_type = parse_aliases(type, hw_cache,
+ PERF_COUNT_HW_CACHE_MAX);
if (cache_type == -1)
- return EVT_FAILED;
+ return -EINVAL;

- while ((cache_op == -1 || cache_result == -1) && *s == '-') {
- ++s;
+ n = snprintf(name, MAX_NAME_LEN, "%s", type);
+
+ for (i = 0; (i < 2) && (op_result[i]); i++) {
+ char *str = op_result[i];
+
+ snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);

if (cache_op == -1) {
- cache_op = parse_aliases(&s, hw_cache_op,
- PERF_COUNT_HW_CACHE_OP_MAX);
+ cache_op = parse_aliases(str, hw_cache_op,
+ PERF_COUNT_HW_CACHE_OP_MAX);
if (cache_op >= 0) {
if (!is_cache_op_valid(cache_type, cache_op))
- return EVT_FAILED;
+ return -EINVAL;
continue;
}
}

if (cache_result == -1) {
- cache_result = parse_aliases(&s, hw_cache_result,
+ cache_result = parse_aliases(str, hw_cache_result,
PERF_COUNT_HW_CACHE_RESULT_MAX);
if (cache_result >= 0)
continue;
}
-
- /*
- * Can't parse this as a cache op or result, so back up
- * to the '-'.
- */
- --s;
- break;
}

/*
@@ -426,20 +450,16 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
if (cache_result == -1)
cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;

- attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
- attr->type = PERF_TYPE_HW_CACHE;
-
- *str = s;
- return EVT_HANDLED;
+ memset(&attr, 0, sizeof(attr));
+ attr.config = cache_type | (cache_op << 8) | (cache_result << 16);
+ attr.type = PERF_TYPE_HW_CACHE;
+ return add_event(&attr, name, mod);
}

-static enum event_result
-parse_single_tracepoint_event(char *sys_name,
- const char *evt_name,
- unsigned int evt_length,
- struct perf_event_attr *attr,
- const char **strp)
+static int add_tracepoint(char *sys_name, char *evt_name, char *mod)
{
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];
char evt_path[MAXPATHLEN];
char id_buf[4];
u64 id;
@@ -450,130 +470,78 @@ parse_single_tracepoint_event(char *sys_name,

fd = open(evt_path, O_RDONLY);
if (fd < 0)
- return EVT_FAILED;
+ return -1;

if (read(fd, id_buf, sizeof(id_buf)) < 0) {
close(fd);
- return EVT_FAILED;
+ return -1;
}

close(fd);
id = atoll(id_buf);
- attr->config = id;
- attr->type = PERF_TYPE_TRACEPOINT;
- *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */
-
- attr->sample_type |= PERF_SAMPLE_RAW;
- attr->sample_type |= PERF_SAMPLE_TIME;
- attr->sample_type |= PERF_SAMPLE_CPU;

- attr->sample_period = 1;
+ memset(&attr, 0, sizeof(attr));
+ attr.config = id;
+ attr.type = PERF_TYPE_TRACEPOINT;
+ attr.sample_type |= PERF_SAMPLE_RAW;
+ attr.sample_type |= PERF_SAMPLE_TIME;
+ attr.sample_type |= PERF_SAMPLE_CPU;
+ attr.sample_period = 1;

-
- return EVT_HANDLED;
+ snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
+ return add_event(&attr, name, mod);
}

-/* sys + ':' + event + ':' + flags*/
-#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
-static enum event_result
-parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name,
- const char *evt_exp, char *flags)
+static int add_tracepoint_multi(char *sys_name, char *evt_name, char *mod)
{
char evt_path[MAXPATHLEN];
struct dirent *evt_ent;
DIR *evt_dir;
+ int ret = 0;

snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
evt_dir = opendir(evt_path);
-
if (!evt_dir) {
perror("Can't open event dir");
- return EVT_FAILED;
+ return -1;
}

- while ((evt_ent = readdir(evt_dir))) {
- char event_opt[MAX_EVOPT_LEN + 1];
- int len;
-
+ while (!ret && (evt_ent = readdir(evt_dir))) {
if (!strcmp(evt_ent->d_name, ".")
|| !strcmp(evt_ent->d_name, "..")
|| !strcmp(evt_ent->d_name, "enable")
|| !strcmp(evt_ent->d_name, "filter"))
continue;

- if (!strglobmatch(evt_ent->d_name, evt_exp))
+ if (!strglobmatch(evt_ent->d_name, evt_name))
continue;

- len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
- evt_ent->d_name, flags ? ":" : "",
- flags ?: "");
- if (len < 0)
- return EVT_FAILED;
-
- if (parse_events(evlist, event_opt, 0))
- return EVT_FAILED;
+ ret = add_tracepoint(sys_name, evt_ent->d_name, mod);
}

- return EVT_HANDLED_ALL;
+ return ret;
}

-static enum event_result
-parse_tracepoint_event(struct perf_evlist *evlist, const char **strp,
- struct perf_event_attr *attr)
+int parse_events_add_tracepoint(char *sys, char *event, char *mod)
{
- const char *evt_name;
- char *flags = NULL, *comma_loc;
- char sys_name[MAX_EVENT_LENGTH];
- unsigned int sys_length, evt_length;
-
- if (debugfs_valid_mountpoint(tracing_events_path))
- return 0;
+ int ret;

- evt_name = strchr(*strp, ':');
- if (!evt_name)
- return EVT_FAILED;
-
- sys_length = evt_name - *strp;
- if (sys_length >= MAX_EVENT_LENGTH)
- return 0;
+ ret = debugfs_valid_mountpoint(tracing_events_path);
+ if (ret)
+ return ret;

- strncpy(sys_name, *strp, sys_length);
- sys_name[sys_length] = '\0';
- evt_name = evt_name + 1;
-
- comma_loc = strchr(evt_name, ',');
- if (comma_loc) {
- /* take the event name up to the comma */
- evt_name = strndup(evt_name, comma_loc - evt_name);
- }
- flags = strchr(evt_name, ':');
- if (flags) {
- /* split it out: */
- evt_name = strndup(evt_name, flags - evt_name);
- flags++;
- }
-
- evt_length = strlen(evt_name);
- if (evt_length >= MAX_EVENT_LENGTH)
- return EVT_FAILED;
- if (strpbrk(evt_name, "*?")) {
- *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */
- return parse_multiple_tracepoint_event(evlist, sys_name,
- evt_name, flags);
- } else {
- return parse_single_tracepoint_event(sys_name, evt_name,
- evt_length, attr, strp);
- }
+ return strpbrk(event, "*?") ?
+ add_tracepoint_multi(sys, event, mod) :
+ add_tracepoint(sys, event, mod);
}

-static enum event_result
-parse_breakpoint_type(const char *type, const char **strp,
- struct perf_event_attr *attr)
+static int
+parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
{
int i;

for (i = 0; i < 3; i++) {
- if (!type[i])
+ if (!type || !type[i])
break;

switch (type[i]) {
@@ -587,65 +555,40 @@ parse_breakpoint_type(const char *type, const char **strp,
attr->bp_type |= HW_BREAKPOINT_X;
break;
default:
- return EVT_FAILED;
+ return -EINVAL;
}
}
+
if (!attr->bp_type) /* Default */
attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;

- *strp = type + i;
-
- return EVT_HANDLED;
+ return 0;
}

-static enum event_result
-parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_breakpoint(void *ptr, char *type, char *mod)
{
- const char *target;
- const char *type;
- char *endaddr;
- u64 addr;
- enum event_result err;
-
- target = strchr(*strp, ':');
- if (!target)
- return EVT_FAILED;
-
- if (strncmp(*strp, "mem", target - *strp) != 0)
- return EVT_FAILED;
-
- target++;
-
- addr = strtoull(target, &endaddr, 0);
- if (target == endaddr)
- return EVT_FAILED;
-
- attr->bp_addr = addr;
- *strp = endaddr;
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];

- type = strchr(target, ':');
+ memset(&attr, 0, sizeof(attr));
+ attr.bp_addr = (u64) ptr;

- /* If no type is defined, just rw as default */
- if (!type) {
- attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
- } else {
- err = parse_breakpoint_type(++type, strp, attr);
- if (err == EVT_FAILED)
- return EVT_FAILED;
- }
+ if (parse_breakpoint_type(type, &attr))
+ return -EINVAL;

/*
* We should find a nice way to override the access length
* Provide some defaults for now
*/
- if (attr->bp_type == HW_BREAKPOINT_X)
- attr->bp_len = sizeof(long);
+ if (attr.bp_type == HW_BREAKPOINT_X)
+ attr.bp_len = sizeof(long);
else
- attr->bp_len = HW_BREAKPOINT_LEN_4;
+ attr.bp_len = HW_BREAKPOINT_LEN_4;

- attr->type = PERF_TYPE_BREAKPOINT;
+ attr.type = PERF_TYPE_BREAKPOINT;

- return EVT_HANDLED;
+ snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw");
+ return add_event(&attr, name, mod);
}

static int check_events(const char *str, unsigned int i)
@@ -665,85 +608,59 @@ static int check_events(const char *str, unsigned int i)
return 0;
}

-static enum event_result
-parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_symbolic(char *symbol, char *mod)
{
- const char *str = *strp;
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];
unsigned int i;
- int n;

for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
- n = check_events(str, i);
- if (n > 0) {
- attr->type = event_symbols[i].type;
- attr->config = event_symbols[i].config;
- *strp = str + n;
- return EVT_HANDLED;
- }
+ if (!check_events(symbol, i))
+ continue;
+ memset(&attr, 0, sizeof(attr));
+ attr.type = event_symbols[i].type;
+ attr.config = event_symbols[i].config;
+ snprintf(name, MAX_NAME_LEN, "%s", event_symbols[i].symbol);
+ return add_event(&attr, name, mod);
}
- return EVT_FAILED;
+
+ return -EINVAL;
}

-static enum event_result
-parse_raw_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_raw(long code, char *mod)
{
- const char *str = *strp;
- u64 config;
- int n;
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];

- if (*str != 'r')
- return EVT_FAILED;
- n = hex2u64(str + 1, &config);
- if (n > 0) {
- const char *end = str + n + 1;
- if (*end != '\0' && *end != ',' && *end != ':')
- return EVT_FAILED;
-
- *strp = end;
- attr->type = PERF_TYPE_RAW;
- attr->config = config;
- return EVT_HANDLED;
- }
- return EVT_FAILED;
+ memset(&attr, 0, sizeof(attr));
+ attr.type = PERF_TYPE_RAW;
+ attr.config = code;
+
+ snprintf(name, MAX_NAME_LEN, "r%lx", code);
+ return add_event(&attr, name, mod);
}

-static enum event_result
-parse_numeric_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_numeric(long type, long config, char *mod)
{
- const char *str = *strp;
- char *endp;
- unsigned long type;
- u64 config;
-
- type = strtoul(str, &endp, 0);
- if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
- str = endp + 1;
- config = strtoul(str, &endp, 0);
- if (endp > str) {
- attr->type = type;
- attr->config = config;
- *strp = endp;
- return EVT_HANDLED;
- }
- }
- return EVT_FAILED;
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];
+
+ memset(&attr, 0, sizeof(attr));
+ attr.type = type;
+ attr.config = config;
+
+ snprintf(name, MAX_NAME_LEN, "%lx:%lx", type, config);
+ return add_event(&attr, name, mod);
}

-static int
-parse_event_modifier(const char **strp, struct perf_event_attr *attr)
+static void
+parse_event_modifier(char *str, struct perf_event_attr *attr)
{
- const char *str = *strp;
int exclude = 0;
int eu = 0, ek = 0, eh = 0, precise = 0;

- if (!*str)
- return 0;
-
- if (*str == ',')
- return 0;
-
- if (*str++ != ':')
- return -1;
+ if (str == NULL)
+ return;

while (*str) {
if (*str == 'u') {
@@ -765,108 +682,36 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)

++str;
}
- if (str < *strp + 2)
- return -1;
-
- *strp = str;

attr->exclude_user = eu;
attr->exclude_kernel = ek;
attr->exclude_hv = eh;
attr->precise_ip = precise;
-
- return 0;
+ return;
}

-/*
- * Each event can have multiple symbolic names.
- * Symbolic names are (almost) exactly matched.
- */
-static enum event_result
-parse_event_symbols(struct perf_evlist *evlist, const char **str,
- struct perf_event_attr *attr)
+int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
{
- enum event_result ret;
-
- ret = parse_tracepoint_event(evlist, str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- ret = parse_raw_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ YY_BUFFER_STATE buffer;
+ int ret;

- ret = parse_numeric_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ __evlist = evlist;

- ret = parse_symbolic_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ buffer = parse_events__scan_string(str);

- ret = parse_generic_hw_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ ret = parse_events_parse();

- ret = parse_breakpoint_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ parse_events__flush_buffer(buffer);
+ parse_events__delete_buffer(buffer);

- fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
- fprintf(stderr, "Run 'perf list' for a list of valid events\n");
- return EVT_FAILED;
-
-modifier:
- if (parse_event_modifier(str, attr) < 0) {
- fprintf(stderr, "invalid event modifier: '%s'\n", *str);
- fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n");
-
- return EVT_FAILED;
+ if (ret) {
+ fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
+ fprintf(stderr, "Run 'perf list' for a list of valid events\n");
}

return ret;
}

-int parse_events(struct perf_evlist *evlist , const char *str, int unset __used)
-{
- struct perf_event_attr attr;
- enum event_result ret;
- const char *ostr;
-
- for (;;) {
- ostr = str;
- memset(&attr, 0, sizeof(attr));
- ret = parse_event_symbols(evlist, &str, &attr);
- if (ret == EVT_FAILED)
- return -1;
-
- if (!(*str == 0 || *str == ',' || isspace(*str)))
- return -1;
-
- if (ret != EVT_HANDLED_ALL) {
- struct perf_evsel *evsel;
- evsel = perf_evsel__new(&attr, evlist->nr_entries);
- if (evsel == NULL)
- return -1;
- perf_evlist__add(evlist, evsel);
-
- evsel->name = calloc(str - ostr + 1, 1);
- if (!evsel->name)
- return -1;
- strncpy(evsel->name, ostr, str - ostr);
- }
-
- if (*str == 0)
- break;
- if (*str == ',')
- ++str;
- while (isspace(*str))
- ++str;
- }
-
- return 0;
-}
-
int parse_events_option(const struct option *opt, const char *str,
int unset __used)
{
@@ -1039,8 +884,6 @@ int print_hwcache_events(const char *event_glob)
return printed;
}

-#define MAX_NAME_LEN 100
-
/*
* Print the help text for the event symbols:
*/
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 7e0cbe7..fa3ac7f 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -33,6 +33,15 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);

#define EVENTS_HELP_MAX (128*1024)

+int parse_events_add_tracepoint(char *sys, char *event, char *mod);
+int parse_events_add_raw(long code, char *mod);
+int parse_events_add_numeric(long type, long config, char *mod);
+int parse_events_add_symbolic(char *symbol, char *mod);
+int parse_events_add_generic_hw(char *type, char *op_result1, char *op_result2,
+ char *mod);
+int parse_events_add_breakpoint(void *ptr, char *type, char *mod);
+void parse_events_error(char const *msg);
+
void print_events(const char *event_glob);
void print_events_type(u8 type);
void print_tracepoint_events(const char *subsys_glob, const char *event_glob);
--
1.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/