diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 155 |
1 files changed, 110 insertions, 45 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 003255910c05..22ebeb92ac51 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -45,13 +45,13 @@ #include <errno.h> #include <inttypes.h> +#include <locale.h> #include <poll.h> #include <unistd.h> #include <sched.h> #include <signal.h> #include <sys/mman.h> #include <sys/wait.h> -#include <asm/bug.h> #include <linux/time64.h> struct switch_output { @@ -71,7 +71,6 @@ struct record { struct auxtrace_record *itr; struct perf_evlist *evlist; struct perf_session *session; - const char *progname; int realtime_prio; bool no_buildid; bool no_buildid_set; @@ -79,6 +78,7 @@ struct record { bool no_buildid_cache_set; bool buildid_all; bool timestamp_filename; + bool timestamp_boundary; struct switch_output switch_output; unsigned long long samples; }; @@ -273,6 +273,24 @@ static void record__read_auxtrace_snapshot(struct record *rec) } } +static int record__auxtrace_init(struct record *rec) +{ + int err; + + if (!rec->itr) { + rec->itr = auxtrace_record__init(rec->evlist, &err); + if (err) + return err; + } + + err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts, + rec->opts.auxtrace_snapshot_opts); + if (err) + return err; + + return auxtrace_parse_filters(rec->evlist); +} + #else static inline @@ -293,6 +311,11 @@ int auxtrace_record__snapshot_start(struct auxtrace_record *itr __maybe_unused) return 0; } +static int record__auxtrace_init(struct record *rec __maybe_unused) +{ + return 0; +} + #endif static int record__mmap_evlist(struct record *rec, @@ -301,7 +324,7 @@ static int record__mmap_evlist(struct record *rec, struct record_opts *opts = &rec->opts; char msg[512]; - if (perf_evlist__mmap_ex(evlist, opts->mmap_pages, false, + if (perf_evlist__mmap_ex(evlist, opts->mmap_pages, opts->auxtrace_mmap_pages, opts->auxtrace_snapshot_mode) < 0) { if (errno == EPERM) { @@ -372,6 +395,8 @@ try_again: ui__error("%s\n", msg); goto out; } + + pos->supported = true; } if (perf_evlist__apply_filters(evlist, &pos)) { @@ -408,8 +433,15 @@ static int process_sample_event(struct perf_tool *tool, { struct record *rec = container_of(tool, struct record, tool); - rec->samples++; + if (rec->evlist->first_sample_time == 0) + rec->evlist->first_sample_time = sample->time; + + rec->evlist->last_sample_time = sample->time; + if (rec->buildid_all) + return 0; + + rec->samples++; return build_id__mark_dso_hit(tool, event, sample, evsel, machine); } @@ -434,9 +466,11 @@ static int process_buildids(struct record *rec) /* * If --buildid-all is given, it marks all DSO regardless of hits, - * so no need to process samples. + * so no need to process samples. But if timestamp_boundary is enabled, + * it still needs to walk on all samples to get the timestamps of + * first/last samples. */ - if (rec->buildid_all) + if (rec->buildid_all && !rec->timestamp_boundary) rec->tool.sample = NULL; return perf_session__process_events(session); @@ -477,7 +511,7 @@ static struct perf_event_header finished_round_event = { }; static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evlist, - bool backward) + bool overwrite) { u64 bytes_written = rec->bytes_written; int i; @@ -487,18 +521,18 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli if (!evlist) return 0; - maps = backward ? evlist->backward_mmap : evlist->mmap; + maps = overwrite ? evlist->overwrite_mmap : evlist->mmap; if (!maps) return 0; - if (backward && evlist->bkw_mmap_state != BKW_MMAP_DATA_PENDING) + if (overwrite && evlist->bkw_mmap_state != BKW_MMAP_DATA_PENDING) return 0; for (i = 0; i < evlist->nr_mmaps; i++) { struct auxtrace_mmap *mm = &maps[i].auxtrace_mmap; if (maps[i].base) { - if (perf_mmap__push(&maps[i], evlist->overwrite, backward, rec, record__pushfn) != 0) { + if (perf_mmap__push(&maps[i], rec, record__pushfn) != 0) { rc = -1; goto out; } @@ -518,7 +552,7 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli if (bytes_written != rec->bytes_written) rc = record__write(rec, &finished_round_event, sizeof(finished_round_event)); - if (backward) + if (overwrite) perf_evlist__toggle_bkw_mmap(evlist, BKW_MMAP_EMPTY); out: return rc; @@ -690,8 +724,8 @@ perf_evlist__pick_pc(struct perf_evlist *evlist) if (evlist) { if (evlist->mmap && evlist->mmap[0].base) return evlist->mmap[0].base; - if (evlist->backward_mmap && evlist->backward_mmap[0].base) - return evlist->backward_mmap[0].base; + if (evlist->overwrite_mmap && evlist->overwrite_mmap[0].base) + return evlist->overwrite_mmap[0].base; } return NULL; } @@ -720,13 +754,10 @@ static int record__synthesize(struct record *rec, bool tail) return 0; if (data->is_pipe) { - err = perf_event__synthesize_features( - tool, session, rec->evlist, process_synthesized_event); - if (err < 0) { - pr_err("Couldn't synthesize features.\n"); - return err; - } - + /* + * We need to synthesize events first, because some + * features works on top of them (on report side). + */ err = perf_event__synthesize_attrs(tool, session, process_synthesized_event); if (err < 0) { @@ -734,6 +765,13 @@ static int record__synthesize(struct record *rec, bool tail) goto out; } + err = perf_event__synthesize_features(tool, session, rec->evlist, + process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize features.\n"); + return err; + } + if (have_tracepoints(&rec->evlist->entries)) { /* * FIXME err <= 0 here actually means that @@ -784,6 +822,28 @@ static int record__synthesize(struct record *rec, bool tail) perf_event__synthesize_guest_os, tool); } + err = perf_event__synthesize_extra_attr(&rec->tool, + rec->evlist, + process_synthesized_event, + data->is_pipe); + if (err) + goto out; + + err = perf_event__synthesize_thread_map2(&rec->tool, rec->evlist->threads, + process_synthesized_event, + NULL); + if (err < 0) { + pr_err("Couldn't synthesize thread map.\n"); + return err; + } + + err = perf_event__synthesize_cpu_map(&rec->tool, rec->evlist->cpus, + process_synthesized_event, NULL); + if (err < 0) { + pr_err("Couldn't synthesize cpu map.\n"); + return err; + } + err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, process_synthesized_event, opts->sample_address, opts->proc_map_timeout, 1); @@ -797,7 +857,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) int status = 0; unsigned long waking = 0; const bool forks = argc > 0; - struct machine *machine; struct perf_tool *tool = &rec->tool; struct record_opts *opts = &rec->opts; struct perf_data *data = &rec->data; @@ -805,8 +864,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) bool disabled = false, draining = false; int fd; - rec->progname = argv[0]; - atexit(record__sig_exit); signal(SIGCHLD, sig_handler); signal(SIGINT, sig_handler); @@ -848,6 +905,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } } + /* + * If we have just single event and are sending data + * through pipe, we need to force the ids allocation, + * because we synthesize event name through the pipe + * and need the id for that. + */ + if (data->is_pipe && rec->evlist->nr_entries == 1) + rec->opts.sample_id = true; + if (record__open(rec) != 0) { err = -1; goto out_child; @@ -893,8 +959,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } - machine = &session->machines.host; - err = record__synthesize(rec, false); if (err < 0) goto out_child; @@ -922,6 +986,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) * Let the child rip */ if (forks) { + struct machine *machine = &session->machines.host; union perf_event *event; pid_t tgid; @@ -1218,10 +1283,12 @@ static int perf_record_config(const char *var, const char *value, void *cb) return -1; return 0; } - if (!strcmp(var, "record.call-graph")) - var = "call-graph.record-mode"; /* fall-through */ + if (!strcmp(var, "record.call-graph")) { + var = "call-graph.record-mode"; + return perf_default_config(var, value, cb); + } - return perf_default_config(var, value, cb); + return 0; } struct clockid_map { @@ -1509,7 +1576,11 @@ static struct option __record_options[] = { OPT_BOOLEAN(0, "tail-synthesize", &record.opts.tail_synthesize, "synthesize non-sample events at the end of output"), OPT_BOOLEAN(0, "overwrite", &record.opts.overwrite, "use overwrite mode"), - OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), + OPT_BOOLEAN(0, "strict-freq", &record.opts.strict_freq, + "Fail if the specified frequency can't be used"), + OPT_CALLBACK('F', "freq", &record.opts, "freq or 'max'", + "profile at this frequency", + record__parse_freq), OPT_CALLBACK('m', "mmap-pages", &record.opts, "pages[,pages]", "number of mmap data pages and AUX area tracing mmap pages", record__parse_mmap_pages), @@ -1533,7 +1604,8 @@ static struct option __record_options[] = { OPT_BOOLEAN_SET('T', "timestamp", &record.opts.sample_time, &record.opts.sample_time_set, "Record the sample timestamps"), - OPT_BOOLEAN('P', "period", &record.opts.period, "Record the sample period"), + OPT_BOOLEAN_SET('P', "period", &record.opts.period, &record.opts.period_set, + "Record the sample period"), OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples, "don't sample"), OPT_BOOLEAN_SET('N', "no-buildid-cache", &record.no_buildid_cache, @@ -1598,6 +1670,8 @@ static struct option __record_options[] = { "Record build-id of all DSOs regardless of hits"), OPT_BOOLEAN(0, "timestamp-filename", &record.timestamp_filename, "append timestamp to output filename"), + OPT_BOOLEAN(0, "timestamp-boundary", &record.timestamp_boundary, + "Record timestamp boundary (time of first/last samples)"), OPT_STRING_OPTARG_SET(0, "switch-output", &record.switch_output.str, &record.switch_output.set, "signal,size,time", "Switch output when receive SIGUSR2 or cross size,time threshold", @@ -1615,6 +1689,8 @@ int cmd_record(int argc, const char **argv) struct record *rec = &record; char errbuf[BUFSIZ]; + setlocale(LC_ALL, ""); + #ifndef HAVE_LIBBPF_SUPPORT # define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, "NO_LIBBPF=1", c) set_nobuild('\0', "clang-path", true); @@ -1675,17 +1751,6 @@ int cmd_record(int argc, const char **argv) alarm(rec->switch_output.time); } - if (!rec->itr) { - rec->itr = auxtrace_record__init(rec->evlist, &err); - if (err) - goto out; - } - - err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts, - rec->opts.auxtrace_snapshot_opts); - if (err) - goto out; - /* * Allow aliases to facilitate the lookup of symbols for address * filters. Refer to auxtrace_parse_filters(). @@ -1694,7 +1759,7 @@ int cmd_record(int argc, const char **argv) symbol__init(NULL); - err = auxtrace_parse_filters(rec->evlist); + err = record__auxtrace_init(rec); if (err) goto out; @@ -1767,7 +1832,7 @@ int cmd_record(int argc, const char **argv) err = target__validate(&rec->opts.target); if (err) { target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); - ui__warning("%s", errbuf); + ui__warning("%s\n", errbuf); } err = target__parse_uid(&rec->opts.target); @@ -1781,8 +1846,8 @@ int cmd_record(int argc, const char **argv) goto out; } - /* Enable ignoring missing threads when -u option is defined. */ - rec->opts.ignore_missing_thread = rec->opts.target.uid != UINT_MAX; + /* Enable ignoring missing threads when -u/-p option is defined. */ + rec->opts.ignore_missing_thread = rec->opts.target.uid != UINT_MAX || rec->opts.target.pid; err = -ENOMEM; if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0) |