Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 017037ff authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo
Browse files

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 35563771
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -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
+92 −28
Original line number Diff line number Diff line
@@ -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));