perf trace: Allow specifying list of syscalls and events in -e/--expr/--event
authorArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 9 Jan 2017 20:26:26 +0000 (17:26 -0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 11 Jan 2017 19:48:01 +0000 (16:48 -0300)
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>
tools/perf/Documentation/perf-trace.txt
tools/perf/builtin-trace.c

index 781b019..afd7286 100644 (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
index 206bf72..40ef9b2 100644 (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));