diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 205 |
1 files changed, 157 insertions, 48 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 227b6ae99785..f95840d04e4c 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -208,7 +208,7 @@ fallback_missing_features: if (opts->exclude_guest_missing) attr->exclude_guest = attr->exclude_host = 0; retry_sample_id: - attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0; + attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; try_again: if (perf_evsel__open(pos, evlist->cpus, evlist->threads, opts->group, group_fd) < 0) { @@ -217,7 +217,7 @@ try_again: if (err == EPERM || err == EACCES) { ui__error_paranoid(); exit(EXIT_FAILURE); - } else if (err == ENODEV && opts->cpu_list) { + } else if (err == ENODEV && opts->target.cpu_list) { die("No such device - did you specify" " an out-of-range profile CPU?\n"); } else if (err == EINVAL) { @@ -227,11 +227,11 @@ try_again: "guest or host samples.\n"); opts->exclude_guest_missing = true; goto fallback_missing_features; - } else if (opts->sample_id_all_avail) { + } else if (!opts->sample_id_all_missing) { /* * Old kernel, no attr->sample_id_type_all field */ - opts->sample_id_all_avail = false; + opts->sample_id_all_missing = true; if (!opts->sample_time && !opts->raw_samples && !time_needed) attr->sample_type &= ~PERF_SAMPLE_TIME; @@ -242,9 +242,13 @@ try_again: /* * If it's cycles then fall back to hrtimer * based cpu-clock-tick sw counter, which - * is always available even if no PMU support: + * is always available even if no PMU support. + * + * PPC returns ENXIO until 2.6.37 (behavior changed + * with commit b0a873e). */ - if (attr->type == PERF_TYPE_HARDWARE + if ((err == ENOENT || err == ENXIO) + && attr->type == PERF_TYPE_HARDWARE && attr->config == PERF_COUNT_HW_CPU_CYCLES) { if (verbose) @@ -252,11 +256,15 @@ try_again: "trying to fall back to cpu-clock-ticks\n"); attr->type = PERF_TYPE_SOFTWARE; attr->config = PERF_COUNT_SW_CPU_CLOCK; + if (pos->name) { + free(pos->name); + pos->name = NULL; + } goto try_again; } if (err == ENOENT) { - ui__warning("The %s event is not supported.\n", + ui__error("The %s event is not supported.\n", event_name(pos)); exit(EXIT_FAILURE); } @@ -388,7 +396,7 @@ static void perf_record__mmap_read_all(struct perf_record *rec) perf_record__mmap_read(rec, &rec->evlist->mmap[i]); } - if (perf_header__has_feat(&rec->session->header, HEADER_TRACE_INFO)) + if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA)) write_output(rec, &finished_round_event, sizeof(finished_round_event)); } @@ -396,7 +404,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) { struct stat st; int flags; - int err, output; + int err, output, feat; unsigned long waking = 0; const bool forks = argc > 0; struct machine *machine; @@ -463,8 +471,17 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) rec->session = session; - if (!rec->no_buildid) - perf_header__set_feat(&session->header, HEADER_BUILD_ID); + for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) + perf_header__set_feat(&session->header, feat); + + if (rec->no_buildid) + perf_header__clear_feat(&session->header, HEADER_BUILD_ID); + + if (!have_tracepoints(&evsel_list->entries)) + perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); + + if (!rec->opts.branch_stack) + perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); if (!rec->file_new) { err = perf_session__read_header(session, output); @@ -472,22 +489,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) goto out_delete_session; } - if (have_tracepoints(&evsel_list->entries)) - perf_header__set_feat(&session->header, HEADER_TRACE_INFO); - - perf_header__set_feat(&session->header, HEADER_HOSTNAME); - perf_header__set_feat(&session->header, HEADER_OSRELEASE); - perf_header__set_feat(&session->header, HEADER_ARCH); - perf_header__set_feat(&session->header, HEADER_CPUDESC); - perf_header__set_feat(&session->header, HEADER_NRCPUS); - perf_header__set_feat(&session->header, HEADER_EVENT_DESC); - perf_header__set_feat(&session->header, HEADER_CMDLINE); - perf_header__set_feat(&session->header, HEADER_VERSION); - perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); - perf_header__set_feat(&session->header, HEADER_TOTAL_MEM); - perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY); - perf_header__set_feat(&session->header, HEADER_CPUID); - if (forks) { err = perf_evlist__prepare_workload(evsel_list, opts, argv); if (err < 0) { @@ -584,7 +585,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) perf_session__process_machines(session, tool, perf_event__synthesize_guest_os); - if (!opts->system_wide) + if (!opts->target.system_wide) perf_event__synthesize_thread_map(tool, evsel_list->threads, process_synthesized_event, machine); @@ -647,6 +648,90 @@ out_delete_session: return err; } +#define BRANCH_OPT(n, m) \ + { .name = n, .mode = (m) } + +#define BRANCH_END { .name = NULL } + +struct branch_mode { + const char *name; + int mode; +}; + +static const struct branch_mode branch_modes[] = { + BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER), + BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL), + BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV), + BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY), + BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL), + BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN), + BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL), + BRANCH_END +}; + +static int +parse_branch_stack(const struct option *opt, const char *str, int unset) +{ +#define ONLY_PLM \ + (PERF_SAMPLE_BRANCH_USER |\ + PERF_SAMPLE_BRANCH_KERNEL |\ + PERF_SAMPLE_BRANCH_HV) + + uint64_t *mode = (uint64_t *)opt->value; + const struct branch_mode *br; + char *s, *os = NULL, *p; + int ret = -1; + + if (unset) + return 0; + + /* + * cannot set it twice, -b + --branch-filter for instance + */ + if (*mode) + return -1; + + /* str may be NULL in case no arg is passed to -b */ + if (str) { + /* because str is read-only */ + s = os = strdup(str); + if (!s) + return -1; + + for (;;) { + p = strchr(s, ','); + if (p) + *p = '\0'; + + for (br = branch_modes; br->name; br++) { + if (!strcasecmp(s, br->name)) + break; + } + if (!br->name) { + ui__warning("unknown branch filter %s," + " check man page\n", s); + goto error; + } + + *mode |= br->mode; + + if (!p) + break; + + s = p + 1; + } + } + ret = 0; + + /* default to any branch */ + if ((*mode & ~ONLY_PLM) == 0) { + *mode = PERF_SAMPLE_BRANCH_ANY; + } +error: + free(os); + return ret; +} + static const char * const record_usage[] = { "perf record [<options>] [<command>]", "perf record [<options>] -- <command> [<options>]", @@ -665,13 +750,13 @@ static const char * const record_usage[] = { */ static struct perf_record record = { .opts = { - .target_pid = -1, - .target_tid = -1, .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, - .freq = 1000, - .sample_id_all_avail = true, + .freq = 4000, + .target = { + .uses_mmap = true, + }, }, .write_mode = WRITE_FORCE, .file_new = true, @@ -690,9 +775,9 @@ const struct option record_options[] = { parse_events_option), OPT_CALLBACK(0, "filter", &record.evlist, "filter", "event filter", parse_filter), - OPT_INTEGER('p', "pid", &record.opts.target_pid, + OPT_STRING('p', "pid", &record.opts.target.pid, "pid", "record events on existing process id"), - OPT_INTEGER('t', "tid", &record.opts.target_tid, + OPT_STRING('t', "tid", &record.opts.target.tid, "tid", "record events on existing thread id"), OPT_INTEGER('r', "realtime", &record.realtime_prio, "collect data with this RT SCHED_FIFO priority"), @@ -700,11 +785,11 @@ const struct option record_options[] = { "collect data without buffering"), OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples, "collect raw sample records from all opened counters"), - OPT_BOOLEAN('a', "all-cpus", &record.opts.system_wide, + OPT_BOOLEAN('a', "all-cpus", &record.opts.target.system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('A', "append", &record.append_file, "append to the output file to do incremental profiling"), - OPT_STRING('C', "cpu", &record.opts.cpu_list, "cpu", + OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", "list of cpus to monitor"), OPT_BOOLEAN('f', "force", &record.force, "overwrite existing data file (deprecated)"), @@ -738,6 +823,16 @@ const struct option record_options[] = { OPT_CALLBACK('G', "cgroup", &record.evlist, "name", "monitor event in cgroup name only", parse_cgroups), + OPT_STRING('u', "uid", &record.opts.target.uid_str, "user", + "user to profile"), + + OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack, + "branch any", "sample any taken branches", + parse_branch_stack), + + OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack, + "branch filter mask", "branch stack filter modes", + parse_branch_stack), OPT_END() }; @@ -747,6 +842,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) struct perf_evsel *pos; struct perf_evlist *evsel_list; struct perf_record *rec = &record; + char errbuf[BUFSIZ]; perf_header__set_cmdline(argc, argv); @@ -758,13 +854,12 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) argc = parse_options(argc, argv, record_options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 && - !rec->opts.system_wide && !rec->opts.cpu_list) + if (!argc && perf_target__none(&rec->opts.target)) usage_with_options(record_usage, record_options); if (rec->force && rec->append_file) { - fprintf(stderr, "Can't overwrite and append at the same time." - " You need to choose between -f and -A"); + ui__error("Can't overwrite and append at the same time." + " You need to choose between -f and -A"); usage_with_options(record_usage, record_options); } else if (rec->append_file) { rec->write_mode = WRITE_APPEND; @@ -772,9 +867,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) rec->write_mode = WRITE_FORCE; } - if (nr_cgroups && !rec->opts.system_wide) { - fprintf(stderr, "cgroup monitoring only available in" - " system-wide mode\n"); + if (nr_cgroups && !rec->opts.target.system_wide) { + ui__error("cgroup monitoring only available in" + " system-wide mode\n"); usage_with_options(record_usage, record_options); } @@ -799,11 +894,25 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) goto out_symbol_exit; } - if (rec->opts.target_pid != -1) - rec->opts.target_tid = rec->opts.target_pid; + err = perf_target__validate(&rec->opts.target); + if (err) { + perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + } + + err = perf_target__parse_uid(&rec->opts.target); + if (err) { + int saved_errno = errno; + + perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); + ui__error("%s", errbuf); + + err = -saved_errno; + goto out_free_fd; + } - if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, - rec->opts.target_tid, rec->opts.cpu_list) < 0) + err = -ENOMEM; + if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) usage_with_options(record_usage, record_options); list_for_each_entry(pos, &evsel_list->entries, node) { @@ -824,7 +933,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) else if (rec->opts.freq) { rec->opts.default_interval = rec->opts.freq; } else { - fprintf(stderr, "frequency and count are zero, aborting\n"); + ui__error("frequency and count are zero, aborting\n"); err = -EINVAL; goto out_free_fd; } |