diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 181 |
1 files changed, 136 insertions, 45 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index ba20bf7c011d..7ea3a11aca70 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -26,6 +26,7 @@ #include "util/target.h" #include "util/session.h" #include "util/tool.h" +#include "util/stat.h" #include "util/symbol.h" #include "util/record.h" #include "util/cpumap.h" @@ -51,6 +52,7 @@ #include "util/clockid.h" #include "util/off_cpu.h" #include "util/bpf-filter.h" +#include "util/strbuf.h" #include "asm/bug.h" #include "perf.h" #include "cputopo.h" @@ -169,10 +171,12 @@ struct record { bool no_buildid_cache_set; bool buildid_all; bool buildid_mmap; + bool buildid_mmap_set; bool timestamp_filename; bool timestamp_boundary; bool off_cpu; const char *filter_action; + const char *uid_str; struct switch_output switch_output; unsigned long long samples; unsigned long output_max_size; /* = 0: unlimited */ @@ -648,14 +652,27 @@ static int record__pushfn(struct mmap *map, void *to, void *bf, size_t size) struct record *rec = to; if (record__comp_enabled(rec)) { + struct perf_record_compressed2 *event = map->data; + size_t padding = 0; + u8 pad[8] = {0}; ssize_t compressed = zstd_compress(rec->session, map, map->data, mmap__mmap_len(map), bf, size); if (compressed < 0) return (int)compressed; - size = compressed; - bf = map->data; + bf = event; + thread->samples++; + + /* + * The record from `zstd_compress` is not 8 bytes aligned, which would cause asan + * error. We make it aligned here. + */ + event->data_size = compressed - sizeof(struct perf_record_compressed2); + event->header.size = PERF_ALIGN(compressed, sizeof(u64)); + padding = event->header.size - compressed; + return record__write(rec, map, bf, compressed) || + record__write(rec, map, &pad, padding); } thread->samples++; @@ -758,7 +775,9 @@ static int record__auxtrace_mmap_read(struct record *rec, { int ret; - ret = auxtrace_mmap__read(map, rec->itr, &rec->tool, + ret = auxtrace_mmap__read(map, rec->itr, + perf_session__env(rec->session), + &rec->tool, record__process_auxtrace); if (ret < 0) return ret; @@ -774,7 +793,9 @@ static int record__auxtrace_mmap_read_snapshot(struct record *rec, { int ret; - ret = auxtrace_mmap__read_snapshot(map, rec->itr, &rec->tool, + ret = auxtrace_mmap__read_snapshot(map, rec->itr, + perf_session__env(rec->session), + &rec->tool, record__process_auxtrace, rec->opts.auxtrace_snapshot_size); if (ret < 0) @@ -1534,7 +1555,7 @@ static void record__adjust_affinity(struct record *rec, struct mmap *map) static size_t process_comp_header(void *record, size_t increment) { - struct perf_record_compressed *event = record; + struct perf_record_compressed2 *event = record; size_t size = sizeof(*event); if (increment) { @@ -1542,7 +1563,7 @@ static size_t process_comp_header(void *record, size_t increment) return increment; } - event->header.type = PERF_RECORD_COMPRESSED; + event->header.type = PERF_RECORD_COMPRESSED2; event->header.size = size; return size; @@ -1552,7 +1573,7 @@ static ssize_t zstd_compress(struct perf_session *session, struct mmap *map, void *dst, size_t dst_size, void *src, size_t src_size) { ssize_t compressed; - size_t max_record_size = PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_record_compressed) - 1; + size_t max_record_size = PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_record_compressed2) - 1; struct zstd_data *zstd_data = &session->zstd_data; if (map && map->file) @@ -1795,6 +1816,7 @@ record__finish_output(struct record *rec) data->dir.files[i].size = lseek(data->dir.files[i].fd, 0, SEEK_CUR); } + /* Buildid scanning disabled or build ID in kernel and synthesized map events. */ if (!rec->no_buildid) { process_buildids(rec); @@ -2146,6 +2168,14 @@ out: return err; } +static void record__synthesize_final_bpf_metadata(struct record *rec __maybe_unused) +{ +#ifdef HAVE_LIBBPF_SUPPORT + perf_event__synthesize_final_bpf_metadata(rec->session, + process_synthesized_event); +#endif +} + static int record__process_signal_event(union perf_event *event __maybe_unused, void *data) { struct record *rec = data; @@ -2177,7 +2207,7 @@ static int record__setup_sb_evlist(struct record *rec) } } - if (evlist__add_bpf_sb_event(rec->sb_evlist, &rec->session->header.env)) { + if (evlist__add_bpf_sb_event(rec->sb_evlist, perf_session__env(rec->session))) { pr_err("Couldn't ask for PERF_RECORD_BPF_EVENT side band events.\n."); return -1; } @@ -2196,15 +2226,16 @@ static int record__init_clock(struct record *rec) struct perf_session *session = rec->session; struct timespec ref_clockid; struct timeval ref_tod; + struct perf_env *env = perf_session__env(session); u64 ref; if (!rec->opts.use_clockid) return 0; if (rec->opts.use_clockid && rec->opts.clockid_res_ns) - session->header.env.clock.clockid_res_ns = rec->opts.clockid_res_ns; + env->clock.clockid_res_ns = rec->opts.clockid_res_ns; - session->header.env.clock.clockid = rec->opts.clockid; + env->clock.clockid = rec->opts.clockid; if (gettimeofday(&ref_tod, NULL) != 0) { pr_err("gettimeofday failed, cannot set reference time.\n"); @@ -2219,12 +2250,12 @@ static int record__init_clock(struct record *rec) ref = (u64) ref_tod.tv_sec * NSEC_PER_SEC + (u64) ref_tod.tv_usec * NSEC_PER_USEC; - session->header.env.clock.tod_ns = ref; + env->clock.tod_ns = ref; ref = (u64) ref_clockid.tv_sec * NSEC_PER_SEC + (u64) ref_clockid.tv_nsec; - session->header.env.clock.clockid_ns = ref; + env->clock.clockid_ns = ref; return 0; } @@ -2370,6 +2401,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) int fd; float ratio = 0; enum evlist_ctl_cmd cmd = EVLIST_CTL_CMD_UNSUPPORTED; + struct perf_env *env; atexit(record__sig_exit); signal(SIGCHLD, sig_handler); @@ -2411,7 +2443,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) pr_err("Perf session creation failed.\n"); return PTR_ERR(session); } - + env = perf_session__env(session); if (record__threads_enabled(rec)) { if (perf_data__is_pipe(&rec->data)) { pr_err("Parallel trace streaming is not available in pipe mode.\n"); @@ -2445,8 +2477,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } #endif // HAVE_EVENTFD_SUPPORT - session->header.env.comp_type = PERF_COMP_ZSTD; - session->header.env.comp_level = rec->opts.comp_level; + env->comp_type = PERF_COMP_ZSTD; + env->comp_level = rec->opts.comp_level; if (rec->opts.kcore && !record__kcore_readable(&session->machines.host)) { @@ -2483,7 +2515,11 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) pr_warning("WARNING: --timestamp-filename option is not available in pipe mode.\n"); } - evlist__uniquify_name(rec->evlist); + /* + * Use global stat_config that is zero meaning aggr_mode is AGGR_NONE + * and hybrid_merge is false. + */ + evlist__uniquify_evsel_names(rec->evlist, &stat_config); evlist__config(rec->evlist, opts, &callchain_param); @@ -2495,7 +2531,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } /* Debug message used by test scripts */ pr_debug3("perf record done opening and mmapping events\n"); - session->header.env.comp_mmap_len = session->evlist->core.mmap_len; + env->comp_mmap_len = session->evlist->core.mmap_len; if (rec->opts.kcore) { err = record__kcore_copy(&session->machines.host, data); @@ -2569,6 +2605,13 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) evlist__enable(rec->evlist); /* + * offcpu-time does not call execve, so enable_on_exe wouldn't work + * when recording a workload, do it manually + */ + if (rec->off_cpu) + evlist__enable_evsel(rec->evlist, (char *)OFFCPU_EVENT); + + /* * Let the child rip */ if (forks) { @@ -2780,17 +2823,21 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) trigger_off(&auxtrace_snapshot_trigger); trigger_off(&switch_output_trigger); + record__synthesize_final_bpf_metadata(rec); + if (opts->auxtrace_snapshot_on_exit) record__auxtrace_snapshot_exit(rec); if (forks && workload_exec_errno) { - char msg[STRERR_BUFSIZE], strevsels[2048]; + char msg[STRERR_BUFSIZE]; const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); + struct strbuf sb = STRBUF_INIT; - evlist__scnprintf_evsels(rec->evlist, sizeof(strevsels), strevsels); + evlist__format_evsels(rec->evlist, &sb, 2048); pr_err("Failed to collect '%s' for the '%s' workload: %s\n", - strevsels, argv[0], emsg); + sb.buf, argv[0], emsg); + strbuf_release(&sb); err = -1; goto out_child; } @@ -2814,7 +2861,7 @@ out_free_threads: if (rec->session->bytes_transferred && rec->session->bytes_compressed) { ratio = (float)rec->session->bytes_transferred/(float)rec->session->bytes_compressed; - session->header.env.comp_ratio = ratio + 0.5; + env->comp_ratio = ratio + 0.5; } if (forks) { @@ -2966,6 +3013,8 @@ static int perf_record_config(const char *var, const char *value, void *cb) rec->no_buildid = true; else if (!strcmp(value, "mmap")) rec->buildid_mmap = true; + else if (!strcmp(value, "no-mmap")) + rec->buildid_mmap = false; else return -1; return 0; @@ -3155,6 +3204,28 @@ out_free: return ret; } +static int record__parse_off_cpu_thresh(const struct option *opt, + const char *str, + int unset __maybe_unused) +{ + struct record_opts *opts = opt->value; + char *endptr; + u64 off_cpu_thresh_ms; + + if (!str) + return -EINVAL; + + off_cpu_thresh_ms = strtoull(str, &endptr, 10); + + /* the threshold isn't string "0", yet strtoull() returns 0, parsing failed */ + if (*endptr || (off_cpu_thresh_ms == 0 && strcmp(str, "0"))) + return -EINVAL; + else + opts->off_cpu_thresh_ns = off_cpu_thresh_ms * NSEC_PER_MSEC; + + return 0; +} + void __weak arch__add_leaf_frame_record_opts(struct record_opts *opts __maybe_unused) { } @@ -3348,7 +3419,9 @@ static struct record record = { .ctl_fd = -1, .ctl_fd_ack = -1, .synth = PERF_SYNTH_ALL, + .off_cpu_thresh_ns = OFFCPU_THRESH, }, + .buildid_mmap = true, }; const char record_callchain_help[] = CALLCHAIN_RECORD_HELP @@ -3436,6 +3509,8 @@ static struct option __record_options[] = { "Record the sampled data address data page size"), OPT_BOOLEAN(0, "code-page-size", &record.opts.sample_code_page_size, "Record the sampled code address (ip) page size"), + OPT_BOOLEAN(0, "sample-mem-info", &record.opts.sample_data_src, + "Record the data source for memory operations"), OPT_BOOLEAN(0, "sample-cpu", &record.opts.sample_cpu, "Record the sample cpu"), OPT_BOOLEAN(0, "sample-identifier", &record.opts.sample_identifier, "Record the sample identifier"), @@ -3460,8 +3535,7 @@ static struct option __record_options[] = { "or ranges of time to enable events e.g. '-D 10-20,30-40'", record__parse_event_enable_time), OPT_BOOLEAN(0, "kcore", &record.opts.kcore, "copy /proc/kcore"), - OPT_STRING('u', "uid", &record.opts.target.uid_str, "user", - "user to profile"), + OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"), OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack, "branch any", "sample any taken branches", @@ -3480,7 +3554,7 @@ static struct option __record_options[] = { "sample selected machine registers on interrupt," " use '-I?' to list register names", parse_intr_regs), OPT_CALLBACK_OPTARG(0, "user-regs", &record.opts.sample_user_regs, NULL, "any register", - "sample selected machine registers on interrupt," + "sample selected machine registers in user space," " use '--user-regs=?' to list register names", parse_user_regs), OPT_BOOLEAN(0, "running-time", &record.opts.running_time, "Record running/enabled time of read (:S) events"), @@ -3514,8 +3588,8 @@ static struct option __record_options[] = { "file", "vmlinux pathname"), OPT_BOOLEAN(0, "buildid-all", &record.buildid_all, "Record build-id of all DSOs regardless of hits"), - OPT_BOOLEAN(0, "buildid-mmap", &record.buildid_mmap, - "Record build-id in map events"), + OPT_BOOLEAN_SET(0, "buildid-mmap", &record.buildid_mmap, &record.buildid_mmap_set, + "Record build-id in mmap events and skip build-id processing."), OPT_BOOLEAN(0, "timestamp-filename", &record.timestamp_filename, "append timestamp to output filename"), OPT_BOOLEAN(0, "timestamp-boundary", &record.timestamp_boundary, @@ -3573,6 +3647,9 @@ static struct option __record_options[] = { OPT_BOOLEAN(0, "off-cpu", &record.off_cpu, "Enable off-cpu analysis"), OPT_STRING(0, "setup-filter", &record.filter_action, "pin|unpin", "BPF filter action"), + OPT_CALLBACK(0, "off-cpu-thresh", &record.opts, "ms", + "Dump off-cpu samples if off-cpu time exceeds this threshold (in milliseconds). (Default: 500ms)", + record__parse_off_cpu_thresh), OPT_END() }; @@ -4042,19 +4119,24 @@ int cmd_record(int argc, const char **argv) record.opts.record_switch_events = true; } + if (!rec->buildid_mmap) { + pr_debug("Disabling build id in synthesized mmap2 events.\n"); + symbol_conf.no_buildid_mmap2 = true; + } else if (rec->buildid_mmap_set) { + /* + * Explicitly passing --buildid-mmap disables buildid processing + * and cache generation. + */ + rec->no_buildid = true; + } + if (rec->buildid_mmap && !perf_can_record_build_id()) { + pr_warning("Missing support for build id in kernel mmap events.\n" + "Disable this warning with --no-buildid-mmap\n"); + rec->buildid_mmap = false; + } if (rec->buildid_mmap) { - if (!perf_can_record_build_id()) { - pr_err("Failed: no support to record build id in mmap events, update your kernel.\n"); - err = -EINVAL; - goto out_opts; - } - pr_debug("Enabling build id in mmap2 events.\n"); - /* Enable mmap build id synthesizing. */ - symbol_conf.buildid_mmap2 = true; /* Enable perf_event_attr::build_id bit. */ rec->opts.build_id = true; - /* Disable build id cache. */ - rec->no_buildid = true; } if (rec->opts.record_cgroup && !perf_can_record_cgroup()) { @@ -4130,6 +4212,10 @@ int cmd_record(int argc, const char **argv) goto out_opts; } + /* For backward compatibility, -d implies --mem-info */ + if (rec->opts.sample_address) + rec->opts.sample_data_src = true; + /* * Allow aliases to facilitate the lookup of symbols for address * filters. Refer to auxtrace_parse_filters(). @@ -4196,19 +4282,24 @@ int cmd_record(int argc, const char **argv) ui__warning("%s\n", errbuf); } - err = target__parse_uid(&rec->opts.target); - if (err) { - int saved_errno = errno; + if (rec->uid_str) { + uid_t uid = parse_uid(rec->uid_str); - target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); - ui__error("%s", errbuf); + if (uid == UINT_MAX) { + ui__error("Invalid User: %s", rec->uid_str); + err = -EINVAL; + goto out; + } + err = parse_uid_filter(rec->evlist, uid); + if (err) + goto out; - err = -saved_errno; - goto out; + /* User ID filtering implies system wide. */ + rec->opts.target.system_wide = true; } - /* 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; + /* Enable ignoring missing threads when -p option is defined. */ + rec->opts.ignore_missing_thread = rec->opts.target.pid; evlist__warn_user_requested_cpus(rec->evlist, rec->opts.target.cpu_list); |