perf trace: Allow specifying list of syscalls and events in -e/--expr/--event

Makes it easier to specify both events and syscalls (to be formatter
strace-like), i.e. previously one would have to do:

  # perf trace -e nanosleep --event sched:sched_switch usleep 1

Now it is possible to do:

  # perf trace -e nanosleep,sched:sched_switch usleep 1
     0.000 ( 0.021 ms): usleep/17962 nanosleep(rqtp: 0x7ffdedd61ec0) ...
     0.021 (         ): sched:sched_switch:usleep:17962 [120] S ==> swapper/1:0 [120])
     0.000 ( 0.066 ms): usleep/17962  ... [continued]: nanosleep()) = 0
  #

The old style --expr and using both -e and --event continues to work.

Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Milian Wolff <milian.wolff@kdab.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-ieg6bakub4657l9e6afn85r4@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2017-01-09 17:26:26 -03:00
Родитель 355637717d
Коммит 017037ff3d
2 изменённых файлов: 96 добавлений и 32 удалений

Просмотреть файл

@ -35,7 +35,10 @@ OPTIONS
-e:: -e::
--expr:: --expr::
List of syscalls to show, currently only syscall names. --event::
List of syscalls and other perf events (tracepoints, HW cache events,
etc) to show.
See 'perf list' for a complete list of events.
Prefixing with ! shows all syscalls but the ones specified. You may Prefixing with ! shows all syscalls but the ones specified. You may
need to escape it. need to escape it.
@ -135,9 +138,6 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
--kernel-syscall-graph:: --kernel-syscall-graph::
Show the kernel callchains on the syscall exit path. Show the kernel callchains on the syscall exit path.
--event::
Trace other events, see 'perf list' for a complete list.
--max-stack:: --max-stack::
Set the stack depth limit when parsing the callchain, anything Set the stack depth limit when parsing the callchain, anything
beyond the specified depth will be ignored. Note that at this point beyond the specified depth will be ignored. Note that at this point

Просмотреть файл

@ -40,6 +40,7 @@
#include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */ #include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/filter.h> #include <linux/filter.h>
#include <linux/audit.h> #include <linux/audit.h>
@ -2699,6 +2700,91 @@ static void evlist__set_evsel_handler(struct perf_evlist *evlist, void *handler)
evsel->handler = handler; evsel->handler = handler;
} }
/*
* XXX: Hackish, just splitting the combined -e+--event (syscalls
* (raw_syscalls:{sys_{enter,exit}} + events (tracepoints, HW, SW, etc) to use
* existing facilities unchanged (trace->ev_qualifier + parse_options()).
*
* It'd be better to introduce a parse_options() variant that would return a
* list with the terms it didn't match to an event...
*/
static int trace__parse_events_option(const struct option *opt, const char *str,
int unset __maybe_unused)
{
struct trace *trace = (struct trace *)opt->value;
const char *s = str;
char *sep = NULL, *lists[2] = { NULL, NULL, };
int len = strlen(str), err = -1, list;
char *strace_groups_dir = system_path(STRACE_GROUPS_DIR);
char group_name[PATH_MAX];
if (strace_groups_dir == NULL)
return -1;
if (*s == '!') {
++s;
trace->not_ev_qualifier = true;
}
while (1) {
if ((sep = strchr(s, ',')) != NULL)
*sep = '\0';
list = 0;
if (syscalltbl__id(trace->sctbl, s) >= 0) {
list = 1;
} else {
path__join(group_name, sizeof(group_name), strace_groups_dir, s);
if (access(group_name, R_OK) == 0)
list = 1;
}
if (lists[list]) {
sprintf(lists[list] + strlen(lists[list]), ",%s", s);
} else {
lists[list] = malloc(len);
if (lists[list] == NULL)
goto out;
strcpy(lists[list], s);
}
if (!sep)
break;
*sep = ',';
s = sep + 1;
}
if (lists[1] != NULL) {
struct strlist_config slist_config = {
.dirname = strace_groups_dir,
};
trace->ev_qualifier = strlist__new(lists[1], &slist_config);
if (trace->ev_qualifier == NULL) {
fputs("Not enough memory to parse event qualifier", trace->output);
goto out;
}
if (trace__validate_ev_qualifier(trace))
goto out;
}
err = 0;
if (lists[0]) {
struct option o = OPT_CALLBACK('e', "event", &trace->evlist, "event",
"event selector. use 'perf list' to list available events",
parse_events_option);
err = parse_events_option(&o, lists[0], 0);
}
out:
if (sep)
*sep = ',';
return err;
}
int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
{ {
const char *trace_usage[] = { const char *trace_usage[] = {
@ -2730,15 +2816,15 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
.max_stack = UINT_MAX, .max_stack = UINT_MAX,
}; };
const char *output_name = NULL; const char *output_name = NULL;
const char *ev_qualifier_str = NULL;
const struct option trace_options[] = { const struct option trace_options[] = {
OPT_CALLBACK(0, "event", &trace.evlist, "event", OPT_CALLBACK('e', "event", &trace, "event",
"event selector. use 'perf list' to list available events", "event/syscall selector. use 'perf list' to list available events",
parse_events_option), trace__parse_events_option),
OPT_BOOLEAN(0, "comm", &trace.show_comm, OPT_BOOLEAN(0, "comm", &trace.show_comm,
"show the thread COMM next to its id"), "show the thread COMM next to its id"),
OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"), OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"),
OPT_STRING('e', "expr", &ev_qualifier_str, "expr", "list of syscalls to trace"), OPT_CALLBACK(0, "expr", &trace, "expr", "list of syscalls/events to trace",
trace__parse_events_option),
OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_STRING('o', "output", &output_name, "file", "output file name"),
OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"), OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"),
OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
@ -2863,7 +2949,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
return -1; return -1;
} }
if (!trace.trace_syscalls && ev_qualifier_str) { if (!trace.trace_syscalls && trace.ev_qualifier) {
pr_err("The -e option can't be used with --no-syscalls.\n"); pr_err("The -e option can't be used with --no-syscalls.\n");
goto out; goto out;
} }
@ -2878,28 +2964,6 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
trace.open_id = syscalltbl__id(trace.sctbl, "open"); trace.open_id = syscalltbl__id(trace.sctbl, "open");
if (ev_qualifier_str != NULL) {
const char *s = ev_qualifier_str;
struct strlist_config slist_config = {
.dirname = system_path(STRACE_GROUPS_DIR),
};
trace.not_ev_qualifier = *s == '!';
if (trace.not_ev_qualifier)
++s;
trace.ev_qualifier = strlist__new(s, &slist_config);
if (trace.ev_qualifier == NULL) {
fputs("Not enough memory to parse event qualifier",
trace.output);
err = -ENOMEM;
goto out_close;
}
err = trace__validate_ev_qualifier(&trace);
if (err)
goto out_close;
}
err = target__validate(&trace.opts.target); err = target__validate(&trace.opts.target);
if (err) { if (err) {
target__strerror(&trace.opts.target, err, bf, sizeof(bf)); target__strerror(&trace.opts.target, err, bf, sizeof(bf));