diff options
author | Ingo Molnar <mingo@kernel.org> | 2012-10-09 15:31:17 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-10-09 15:31:17 +0200 |
commit | 6fcdb1ed2ef3548d5a9428d6ae60158ddd46a608 (patch) | |
tree | 2a8bb14cee4c4bc0ec526b5796f318dff9e8c4e9 /tools/perf/builtin-trace.c | |
parent | 1d787d37c8ff6612b8151c6dff15bfa7347bcbdf (diff) | |
parent | 7747e2f4fb5fb840994613dd1474c17cddb7836b (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
* Handle PERF_RECORD_EXIT events in sched and annotate.
* struct machine refactorings to help in top and trace.
* Add on_exit implementation for systems without one, e.g. Android, from
Bernhard Rosenkraenzer.
* Only process events for vcpus of interest, helps handling large number
of events, from David Ahern.
* Cross compilation fixes for Android, from Irina Tirdea.
* Add documentation on compiling for Android, from Irina Tirdea.
* perf diff improvements from Jiri Olsa.
* Target (task/user/cpu/syswide) handling improvements, from Namhyung Kim.
* Add support in 'trace' for tracing workload given by command line, from
Namhyung Kim.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 187 |
1 files changed, 128 insertions, 59 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8f113dab8bf..4e9320bf11e 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -52,6 +52,13 @@ struct trace { struct perf_record_opts opts; }; +static bool done = false; + +static void sig_handler(int sig __maybe_unused) +{ + done = true; +} + static int trace__read_syscall_info(struct trace *trace, int id) { char tp_name[128]; @@ -114,33 +121,99 @@ static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FIL return printed; } -static int trace__run(struct trace *trace) +typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, + struct perf_sample *sample); + +static struct syscall *trace__syscall_info(struct trace *trace, + struct perf_evsel *evsel, + struct perf_sample *sample) +{ + int id = perf_evsel__intval(evsel, sample, "id"); + + if (id < 0) { + printf("Invalid syscall %d id, skipping...\n", id); + return NULL; + } + + if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && + trace__read_syscall_info(trace, id)) + goto out_cant_read; + + if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) + goto out_cant_read; + + return &trace->syscalls.table[id]; + +out_cant_read: + printf("Problems reading syscall %d information\n", id); + return NULL; +} + +static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, + struct perf_sample *sample) +{ + void *args; + struct syscall *sc = trace__syscall_info(trace, evsel, sample); + + if (sc == NULL) + return -1; + + args = perf_evsel__rawptr(evsel, sample, "args"); + if (args == NULL) { + printf("Problems reading syscall arguments\n"); + return -1; + } + + printf("%s(", sc->name); + syscall__fprintf_args(sc, args, stdout); + + return 0; +} + +static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, + struct perf_sample *sample) +{ + int ret; + struct syscall *sc = trace__syscall_info(trace, evsel, sample); + + if (sc == NULL) + return -1; + + ret = perf_evsel__intval(evsel, sample, "ret"); + + if (ret < 0 && sc->fmt && sc->fmt->errmsg) { + char bf[256]; + const char *emsg = strerror_r(-ret, bf, sizeof(bf)), + *e = audit_errno_to_name(-ret); + + printf(") = -1 %s %s", e, emsg); + } else if (ret == 0 && sc->fmt && sc->fmt->timeout) + printf(") = 0 Timeout"); + else + printf(") = %d", ret); + + putchar('\n'); + return 0; +} + +static int trace__run(struct trace *trace, int argc, const char **argv) { struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); - struct perf_evsel *evsel, *evsel_enter, *evsel_exit; + struct perf_evsel *evsel; int err = -1, i, nr_events = 0, before; + const bool forks = argc > 0; if (evlist == NULL) { printf("Not enough memory to run!\n"); goto out; } - evsel_enter = perf_evsel__newtp("raw_syscalls", "sys_enter", 0); - if (evsel_enter == NULL) { - printf("Couldn't read the raw_syscalls:sys_enter tracepoint information!\n"); + if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || + perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { + printf("Couldn't read the raw_syscalls tracepoints information!\n"); goto out_delete_evlist; } - perf_evlist__add(evlist, evsel_enter); - - evsel_exit = perf_evsel__newtp("raw_syscalls", "sys_exit", 1); - if (evsel_exit == NULL) { - printf("Couldn't read the raw_syscalls:sys_exit tracepoint information!\n"); - goto out_delete_evlist; - } - - perf_evlist__add(evlist, evsel_exit); - err = perf_evlist__create_maps(evlist, &trace->opts.target); if (err < 0) { printf("Problems parsing the target to trace, check your options!\n"); @@ -149,6 +222,17 @@ static int trace__run(struct trace *trace) perf_evlist__config_attrs(evlist, &trace->opts); + signal(SIGCHLD, sig_handler); + signal(SIGINT, sig_handler); + + if (forks) { + err = perf_evlist__prepare_workload(evlist, &trace->opts, argv); + if (err < 0) { + printf("Couldn't run the workload!\n"); + goto out_delete_evlist; + } + } + err = perf_evlist__open(evlist); if (err < 0) { printf("Couldn't create the events: %s\n", strerror(errno)); @@ -162,6 +246,10 @@ static int trace__run(struct trace *trace) } perf_evlist__enable(evlist); + + if (forks) + perf_evlist__start_workload(evlist); + again: before = nr_events; @@ -170,9 +258,8 @@ again: while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { const u32 type = event->header.type; - struct syscall *sc; + tracepoint_handler handler; struct perf_sample sample; - int id; ++nr_events; @@ -200,50 +287,23 @@ again: continue; } - id = perf_evsel__intval(evsel, &sample, "id"); - if (id < 0) { - printf("Invalid syscall %d id, skipping...\n", id); - continue; - } - - if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && - trace__read_syscall_info(trace, id)) - continue; - - if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) - continue; - - sc = &trace->syscalls.table[id]; - if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1) printf("%d ", sample.tid); - if (evsel == evsel_enter) { - void *args = perf_evsel__rawptr(evsel, &sample, "args"); - - printf("%s(", sc->name); - syscall__fprintf_args(sc, args, stdout); - } else if (evsel == evsel_exit) { - int ret = perf_evsel__intval(evsel, &sample, "ret"); - - if (ret < 0 && sc->fmt && sc->fmt->errmsg) { - char bf[256]; - const char *emsg = strerror_r(-ret, bf, sizeof(bf)), - *e = audit_errno_to_name(-ret); - - printf(") = -1 %s %s", e, emsg); - } else if (ret == 0 && sc->fmt && sc->fmt->timeout) - printf(") = 0 Timeout"); - else - printf(") = %d", ret); - - putchar('\n'); - } + handler = evsel->handler.func; + handler(trace, evsel, &sample); } } - if (nr_events == before) + if (nr_events == before) { + if (done) + goto out_delete_evlist; + poll(evlist->pollfd, evlist->nr_fds, -1); + } + + if (done) + perf_evlist__disable(evlist); goto again; @@ -256,7 +316,8 @@ out: int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const trace_usage[] = { - "perf trace [<options>]", + "perf trace [<options>] [<command>]", + "perf trace [<options>] -- <command> [<options>]", NULL }; struct trace trace = { @@ -293,18 +354,26 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) OPT_END() }; int err; + char bf[BUFSIZ]; argc = parse_options(argc, argv, trace_options, trace_usage, 0); - if (argc) - usage_with_options(trace_usage, trace_options); + + err = perf_target__validate(&trace.opts.target); + if (err) { + perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); + printf("%s", bf); + return err; + } err = perf_target__parse_uid(&trace.opts.target); if (err) { - char bf[BUFSIZ]; perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); printf("%s", bf); return err; } - return trace__run(&trace); + if (!argc && perf_target__none(&trace.opts.target)) + trace.opts.target.system_wide = true; + + return trace__run(&trace, argc, argv); } |