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
parent 355637717d
commit 017037ff3d
2 changed files with 96 additions and 32 deletions

View File

@ -35,7 +35,10 @@ OPTIONS
-e::
--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
need to escape it.
@ -135,9 +138,6 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
--kernel-syscall-graph::
Show the kernel callchains on the syscall exit path.
--event::
Trace other events, see 'perf list' for a complete list.
--max-stack::
Set the stack depth limit when parsing the callchain, anything
beyond the specified depth will be ignored. Note that at this point

View File

@ -40,6 +40,7 @@
#include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */
#include <stdlib.h>
#include <string.h>
#include <linux/err.h>
#include <linux/filter.h>
#include <linux/audit.h>
@ -2699,6 +2700,91 @@ static void evlist__set_evsel_handler(struct perf_evlist *evlist, void *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)
{
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,
};
const char *output_name = NULL;
const char *ev_qualifier_str = NULL;
const struct option trace_options[] = {
OPT_CALLBACK(0, "event", &trace.evlist, "event",
"event selector. use 'perf list' to list available events",
parse_events_option),
OPT_CALLBACK('e', "event", &trace, "event",
"event/syscall selector. use 'perf list' to list available events",
trace__parse_events_option),
OPT_BOOLEAN(0, "comm", &trace.show_comm,
"show the thread COMM next to its id"),
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('i', "input", &input_name, "file", "Analyze events in file"),
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;
}
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");
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");
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);
if (err) {
target__strerror(&trace.opts.target, err, bf, sizeof(bf));