diff options
author | Steven Rostedt <srostedt@redhat.com> | 2012-04-06 00:47:56 +0200 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2012-04-25 13:28:48 +0200 |
commit | aaf045f72335653b24784d6042be8e4aee114403 (patch) | |
tree | 55c11335e23759e56e0a5ae2daf1c31bb9769662 /tools/perf/util/trace-event-parse.c | |
parent | 668fe01f1cea2154da479dd12946eeb53413396e (diff) |
perf: Have perf use the new libtraceevent.a library
The event parsing code in perf was originally copied from trace-cmd
but never was kept up-to-date with the changes that was done there.
The trace-cmd libtraceevent.a code is much more mature than what is
currently in perf.
This updates the code to use wrappers to handle the calls to the
new event parsing code. The new code requires a handle to be pass
around, which removes the global event variables and allows
more than one event structure to be read from different files
(and different machines).
But perf still has the old global events and the code throughout
perf does not yet have a nice way to pass around a handle.
A global 'pevent' has been made for perf and the old calls have
been created as wrappers to the new event parsing code that uses
the global pevent.
With this change, perf can later incorporate the pevent handle into
the perf structures and allow more than one file to be read and
compared, that contains different events.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Arun Sharma <asharma@fb.com>
Cc: Namhyung Kim <namhyung.kim@lge.com>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Diffstat (limited to 'tools/perf/util/trace-event-parse.c')
-rw-r--r-- | tools/perf/util/trace-event-parse.c | 350 |
1 files changed, 345 insertions, 5 deletions
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 94775199644e..4ec165a334e2 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -1,18 +1,358 @@ +/* + * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + #include "../perf.h" #include "util.h" #include "trace-event.h" -int common_pc(struct scripting_context *context) +int header_page_size_size; +int header_page_ts_size; +int header_page_data_offset; + +struct pevent *perf_pevent; +static struct pevent *pevent; + +bool latency_format; + +int read_trace_init(int file_bigendian, int host_bigendian) { - return parse_common_pc(context->event_data); + if (pevent) + return 0; + + perf_pevent = pevent_alloc(); + pevent = perf_pevent; + + pevent_set_file_bigendian(pevent, file_bigendian); + pevent_set_host_bigendian(pevent, host_bigendian); + + return 0; } -int common_flags(struct scripting_context *context) +static int get_common_field(struct scripting_context *context, + int *offset, int *size, const char *type) { - return parse_common_flags(context->event_data); + struct event_format *event; + struct format_field *field; + + if (!*size) { + if (!pevent->events) + return 0; + + event = pevent->events[0]; + field = pevent_find_common_field(event, type); + if (!field) + return 0; + *offset = field->offset; + *size = field->size; + } + + return pevent_read_number(pevent, context->event_data + *offset, *size); } int common_lock_depth(struct scripting_context *context) { - return parse_common_lock_depth(context->event_data); + static int offset; + static int size; + int ret; + + ret = get_common_field(context, &size, &offset, + "common_lock_depth"); + if (ret < 0) + return -1; + + return ret; +} + +int common_flags(struct scripting_context *context) +{ + static int offset; + static int size; + int ret; + + ret = get_common_field(context, &size, &offset, + "common_flags"); + if (ret < 0) + return -1; + + return ret; +} + +int common_pc(struct scripting_context *context) +{ + static int offset; + static int size; + int ret; + + ret = get_common_field(context, &size, &offset, + "common_preempt_count"); + if (ret < 0) + return -1; + + return ret; +} + +unsigned long long +raw_field_value(struct event_format *event, const char *name, void *data) +{ + struct format_field *field; + unsigned long long val; + + field = pevent_find_any_field(event, name); + if (!field) + return 0ULL; + + pevent_read_number_field(field, data, &val); + + return val; +} + +void *raw_field_ptr(struct event_format *event, const char *name, void *data) +{ + struct format_field *field; + + field = pevent_find_any_field(event, name); + if (!field) + return NULL; + + if (field->flags & FIELD_IS_DYNAMIC) { + int offset; + + offset = *(int *)(data + field->offset); + offset &= 0xffff; + + return data + offset; + } + + return data + field->offset; +} + +int trace_parse_common_type(void *data) +{ + struct record record; + + record.data = data; + return pevent_data_type(pevent, &record); +} + +int trace_parse_common_pid(void *data) +{ + struct record record; + + record.data = data; + return pevent_data_pid(pevent, &record); +} + +unsigned long long read_size(void *ptr, int size) +{ + return pevent_read_number(pevent, ptr, size); +} + +struct event_format *trace_find_event(int type) +{ + return pevent_find_event(pevent, type); +} + + +void print_trace_event(int cpu, void *data, int size) +{ + struct event_format *event; + struct record record; + struct trace_seq s; + int type; + + type = trace_parse_common_type(data); + + event = trace_find_event(type); + if (!event) { + warning("ug! no event found for type %d", type); + return; + } + + memset(&record, 0, sizeof(record)); + record.cpu = cpu; + record.size = size; + record.data = data; + + trace_seq_init(&s); + pevent_print_event(pevent, &s, &record); + trace_seq_do_printf(&s); + printf("\n"); +} + +void print_event(int cpu, void *data, int size, unsigned long long nsecs, + char *comm) +{ + struct record record; + struct trace_seq s; + int pid; + + pevent->latency_format = latency_format; + + record.ts = nsecs; + record.cpu = cpu; + record.size = size; + record.data = data; + pid = pevent_data_pid(pevent, &record); + + if (!pevent_pid_is_registered(pevent, pid)) + pevent_register_comm(pevent, comm, pid); + + trace_seq_init(&s); + pevent_print_event(pevent, &s, &record); + trace_seq_do_printf(&s); + printf("\n"); +} + +void parse_proc_kallsyms(char *file, unsigned int size __unused) +{ + unsigned long long addr; + char *func; + char *line; + char *next = NULL; + char *addr_str; + char *mod; + char ch; + + line = strtok_r(file, "\n", &next); + while (line) { + mod = NULL; + sscanf(line, "%as %c %as\t[%as", + (float *)(void *)&addr_str, /* workaround gcc warning */ + &ch, (float *)(void *)&func, (float *)(void *)&mod); + addr = strtoull(addr_str, NULL, 16); + free(addr_str); + + /* truncate the extra ']' */ + if (mod) + mod[strlen(mod) - 1] = 0; + + pevent_register_function(pevent, func, addr, mod); + free(func); + free(mod); + + line = strtok_r(NULL, "\n", &next); + } +} + +void parse_ftrace_printk(char *file, unsigned int size __unused) +{ + unsigned long long addr; + char *printk; + char *line; + char *next = NULL; + char *addr_str; + char *fmt; + + line = strtok_r(file, "\n", &next); + while (line) { + addr_str = strtok_r(line, ":", &fmt); + if (!addr_str) { + warning("printk format with empty entry"); + break; + } + addr = strtoull(addr_str, NULL, 16); + /* fmt still has a space, skip it */ + printk = strdup(fmt+1); + line = strtok_r(NULL, "\n", &next); + pevent_register_print_string(pevent, printk, addr); + } +} + +int parse_ftrace_file(char *buf, unsigned long size) +{ + return pevent_parse_event(pevent, buf, size, "ftrace"); +} + +int parse_event_file(char *buf, unsigned long size, char *sys) +{ + return pevent_parse_event(pevent, buf, size, sys); +} + +struct event_format *trace_find_next_event(struct event_format *event) +{ + static int idx; + + if (!pevent->events) + return NULL; + + if (!event) { + idx = 0; + return pevent->events[0]; + } + + if (idx < pevent->nr_events && event == pevent->events[idx]) { + idx++; + if (idx == pevent->nr_events) + return NULL; + return pevent->events[idx]; + } + + for (idx = 1; idx < pevent->nr_events; idx++) { + if (event == pevent->events[idx - 1]) + return pevent->events[idx]; + } + return NULL; +} + +struct flag { + const char *name; + unsigned long long value; +}; + +static const struct flag flags[] = { + { "HI_SOFTIRQ", 0 }, + { "TIMER_SOFTIRQ", 1 }, + { "NET_TX_SOFTIRQ", 2 }, + { "NET_RX_SOFTIRQ", 3 }, + { "BLOCK_SOFTIRQ", 4 }, + { "BLOCK_IOPOLL_SOFTIRQ", 5 }, + { "TASKLET_SOFTIRQ", 6 }, + { "SCHED_SOFTIRQ", 7 }, + { "HRTIMER_SOFTIRQ", 8 }, + { "RCU_SOFTIRQ", 9 }, + + { "HRTIMER_NORESTART", 0 }, + { "HRTIMER_RESTART", 1 }, +}; + +unsigned long long eval_flag(const char *flag) +{ + int i; + + /* + * Some flags in the format files do not get converted. + * If the flag is not numeric, see if it is something that + * we already know about. + */ + if (isdigit(flag[0])) + return strtoull(flag, NULL, 0); + + for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++) + if (strcmp(flags[i].name, flag) == 0) + return flags[i].value; + + return 0; } |