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:
Родитель
355637717d
Коммит
017037ff3d
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
Загрузка…
Ссылка в новой задаче