perf record: Add --synth option

Add an option to control the synthesizing behavior.

    --synth <no|all|task|mmap|cgroup>
                      Fine-tune event synthesis: default=all

This can be useful when we know it doesn't need some synthesis like
in a specific usecase and/or when using pipe:

  $ perf record -a --all-cgroups --synth cgroup -o- sleep 1 | \
  > perf report -i- -s cgroup

Committer notes:

Added a clarification to the man page entry for --synth that this is
about pre-existing threads.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: https //lore.kernel.org/r/20210811044658.1313391-2-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Namhyung Kim 2021-08-10 21:46:58 -07:00 committed by Arnaldo Carvalho de Melo
parent 84111b9c95
commit 41b740b6e8
5 changed files with 96 additions and 9 deletions

View File

@ -596,6 +596,22 @@ options.
'perf record --dry-run -e' can act as a BPF script compiler if llvm.dump-obj 'perf record --dry-run -e' can act as a BPF script compiler if llvm.dump-obj
in config file is set to true. in config file is set to true.
--synth=TYPE::
Collect and synthesize given type of events (comma separated). Note that
this option controls the synthesis from the /proc filesystem which represent
task status for pre-existing threads.
Kernel (and some other) events are recorded regardless of the
choice in this option. For example, --synth=no would have MMAP events for
kernel and modules.
Available types are:
'task' - synthesize FORK and COMM events for each task
'mmap' - synthesize MMAP events for each process (implies 'task')
'cgroup' - synthesize CGROUP events for each cgroup
'all' - synthesize all events (default)
'no' - do not synthesize any of the above events
--tail-synthesize:: --tail-synthesize::
Instead of collecting non-sample events (for example, fork, comm, mmap) at Instead of collecting non-sample events (for example, fork, comm, mmap) at
the beginning of record, collect them during finalizing an output file. the beginning of record, collect them during finalizing an output file.

View File

@ -1255,6 +1255,7 @@ static int record__synthesize_workload(struct record *rec, bool tail)
{ {
int err; int err;
struct perf_thread_map *thread_map; struct perf_thread_map *thread_map;
bool needs_mmap = rec->opts.synth & PERF_SYNTH_MMAP;
if (rec->opts.tail_synthesize != tail) if (rec->opts.tail_synthesize != tail)
return 0; return 0;
@ -1266,7 +1267,7 @@ static int record__synthesize_workload(struct record *rec, bool tail)
err = perf_event__synthesize_thread_map(&rec->tool, thread_map, err = perf_event__synthesize_thread_map(&rec->tool, thread_map,
process_synthesized_event, process_synthesized_event,
&rec->session->machines.host, &rec->session->machines.host,
true, needs_mmap,
rec->opts.sample_address); rec->opts.sample_address);
perf_thread_map__put(thread_map); perf_thread_map__put(thread_map);
return err; return err;
@ -1471,20 +1472,26 @@ static int record__synthesize(struct record *rec, bool tail)
if (err < 0) if (err < 0)
pr_warning("Couldn't synthesize bpf events.\n"); pr_warning("Couldn't synthesize bpf events.\n");
err = perf_event__synthesize_cgroups(tool, process_synthesized_event, if (rec->opts.synth & PERF_SYNTH_CGROUP) {
machine); err = perf_event__synthesize_cgroups(tool, process_synthesized_event,
if (err < 0) machine);
pr_warning("Couldn't synthesize cgroup events.\n"); if (err < 0)
pr_warning("Couldn't synthesize cgroup events.\n");
}
if (rec->opts.nr_threads_synthesize > 1) { if (rec->opts.nr_threads_synthesize > 1) {
perf_set_multithreaded(); perf_set_multithreaded();
f = process_locked_synthesized_event; f = process_locked_synthesized_event;
} }
err = __machine__synthesize_threads(machine, tool, &opts->target, if (rec->opts.synth & PERF_SYNTH_TASK) {
rec->evlist->core.threads, bool needs_mmap = rec->opts.synth & PERF_SYNTH_MMAP;
f, true, opts->sample_address,
rec->opts.nr_threads_synthesize); err = __machine__synthesize_threads(machine, tool, &opts->target,
rec->evlist->core.threads,
f, needs_mmap, opts->sample_address,
rec->opts.nr_threads_synthesize);
}
if (rec->opts.nr_threads_synthesize > 1) if (rec->opts.nr_threads_synthesize > 1)
perf_set_singlethreaded(); perf_set_singlethreaded();
@ -2393,6 +2400,26 @@ static int process_timestamp_boundary(struct perf_tool *tool,
return 0; return 0;
} }
static int parse_record_synth_option(const struct option *opt,
const char *str,
int unset __maybe_unused)
{
struct record_opts *opts = opt->value;
char *p = strdup(str);
if (p == NULL)
return -1;
opts->synth = parse_synth_opt(p);
free(p);
if (opts->synth < 0) {
pr_err("Invalid synth option: %s\n", str);
return -1;
}
return 0;
}
/* /*
* XXX Ideally would be local to cmd_record() and passed to a record__new * XXX Ideally would be local to cmd_record() and passed to a record__new
* because we need to have access to it in record__exit, that is called * because we need to have access to it in record__exit, that is called
@ -2418,6 +2445,7 @@ static struct record record = {
.nr_threads_synthesize = 1, .nr_threads_synthesize = 1,
.ctl_fd = -1, .ctl_fd = -1,
.ctl_fd_ack = -1, .ctl_fd_ack = -1,
.synth = PERF_SYNTH_ALL,
}, },
.tool = { .tool = {
.sample = process_sample_event, .sample = process_sample_event,
@ -2633,6 +2661,8 @@ static struct option __record_options[] = {
"\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.\n" "\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.\n"
"\t\t\t Alternatively, ctl-fifo / ack-fifo will be opened and used as ctl-fd / ack-fd.", "\t\t\t Alternatively, ctl-fifo / ack-fifo will be opened and used as ctl-fd / ack-fd.",
parse_control_option), parse_control_option),
OPT_CALLBACK(0, "synth", &record.opts, "no|all|task|mmap|cgroup",
"Fine-tune event synthesis: default=all", parse_record_synth_option),
OPT_END() OPT_END()
}; };

View File

@ -77,6 +77,7 @@ struct record_opts {
int ctl_fd; int ctl_fd;
int ctl_fd_ack; int ctl_fd_ack;
bool ctl_fd_close; bool ctl_fd_close;
int synth;
}; };
extern const char * const *record_usage; extern const char * const *record_usage;

View File

@ -2237,3 +2237,31 @@ int perf_event__synthesize_for_pipe(struct perf_tool *tool,
return ret; return ret;
} }
int parse_synth_opt(char *synth)
{
char *p, *q;
int ret = 0;
if (synth == NULL)
return -1;
for (q = synth; (p = strsep(&q, ",")); p = q) {
if (!strcasecmp(p, "no") || !strcasecmp(p, "none"))
return 0;
if (!strcasecmp(p, "all"))
return PERF_SYNTH_ALL;
if (!strcasecmp(p, "task"))
ret |= PERF_SYNTH_TASK;
else if (!strcasecmp(p, "mmap"))
ret |= PERF_SYNTH_TASK | PERF_SYNTH_MMAP;
else if (!strcasecmp(p, "cgroup"))
ret |= PERF_SYNTH_CGROUP;
else
return -1;
}
return ret;
}

View File

@ -27,6 +27,18 @@ struct target;
union perf_event; union perf_event;
enum perf_record_synth {
PERF_SYNTH_TASK = 1 << 0,
PERF_SYNTH_MMAP = 1 << 1,
PERF_SYNTH_CGROUP = 1 << 2,
/* last element */
PERF_SYNTH_MAX = 1 << 3,
};
#define PERF_SYNTH_ALL (PERF_SYNTH_MAX - 1)
int parse_synth_opt(char *str);
typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event, typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event,
struct perf_sample *sample, struct machine *machine); struct perf_sample *sample, struct machine *machine);