diff options
Diffstat (limited to 'tools/perf/util/evlist.c')
| -rw-r--r-- | tools/perf/util/evlist.c | 377 |
1 files changed, 238 insertions, 139 deletions
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f0dd174e2deb..ee971d15b3c6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -13,6 +13,7 @@ #include "util/mmap.h" #include "thread_map.h" #include "target.h" +#include "dwarf-regs.h" #include "evlist.h" #include "evsel.h" #include "record.h" @@ -35,6 +36,8 @@ #include "util/util.h" #include "util/env.h" #include "util/intel-tpebs.h" +#include "util/metricgroup.h" +#include "util/strbuf.h" #include <signal.h> #include <unistd.h> #include <sched.h> @@ -82,6 +85,8 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, evlist->ctl_fd.ack = -1; evlist->ctl_fd.pos = -1; evlist->nr_br_cntr = -1; + metricgroup__rblist_init(&evlist->metric_events); + INIT_LIST_HEAD(&evlist->deferred_samples); } struct evlist *evlist__new(void) @@ -94,30 +99,47 @@ struct evlist *evlist__new(void) return evlist; } -struct evlist *evlist__new_default(void) +struct evlist *evlist__new_default(const struct target *target, bool sample_callchains) { struct evlist *evlist = evlist__new(); bool can_profile_kernel; + struct perf_pmu *pmu = NULL; + struct evsel *evsel; + char buf[256]; int err; if (!evlist) return NULL; can_profile_kernel = perf_event_paranoid_check(1); - err = parse_event(evlist, can_profile_kernel ? "cycles:P" : "cycles:Pu"); - if (err) { - evlist__delete(evlist); - return NULL; + + if (EM_HOST == EM_S390 && sample_callchains) { + snprintf(buf, sizeof(buf), "software/%s/%s", + target__has_cpu(target) ? "cpu-clock" : "task-clock", + can_profile_kernel ? "P" : "Pu"); + err = parse_event(evlist, buf); + if (err) + goto out_err; + } else { + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + snprintf(buf, sizeof(buf), "%s/cycles/%s", pmu->name, + can_profile_kernel ? "P" : "Pu"); + err = parse_event(evlist, buf); + if (err) + goto out_err; + } } + /* If there is only 1 event a sample identifier isn't necessary. */ if (evlist->core.nr_entries > 1) { - struct evsel *evsel; - evlist__for_each_entry(evlist, evsel) evsel__set_sample_id(evsel, /*can_sample_identifier=*/false); } return evlist; +out_err: + evlist__delete(evlist); + return NULL; } struct evlist *evlist__new_dummy(void) @@ -172,6 +194,7 @@ static void evlist__purge(struct evlist *evlist) void evlist__exit(struct evlist *evlist) { + metricgroup__rblist_exit(&evlist->metric_events); event_enable_timer__exit(&evlist->eet); zfree(&evlist->mmap); zfree(&evlist->overwrite_mmap); @@ -183,7 +206,6 @@ void evlist__delete(struct evlist *evlist) if (evlist == NULL) return; - tpebs_delete(); evlist__free_stats(evlist); evlist__munmap(evlist); evlist__close(evlist); @@ -347,36 +369,107 @@ int evlist__add_newtp(struct evlist *evlist, const char *sys, const char *name, } #endif -struct evlist_cpu_iterator evlist__cpu_begin(struct evlist *evlist, struct affinity *affinity) +/* + * Should sched_setaffinity be used with evlist__for_each_cpu? Determine if + * migrating the thread will avoid possibly numerous IPIs. + */ +static bool evlist__use_affinity(struct evlist *evlist) { - struct evlist_cpu_iterator itr = { + struct evsel *pos; + struct perf_cpu_map *used_cpus = NULL; + bool ret = false; + + if (evlist->no_affinity || !evlist->core.user_requested_cpus || + cpu_map__is_dummy(evlist->core.user_requested_cpus)) + return false; + + evlist__for_each_entry(evlist, pos) { + struct perf_cpu_map *intersect; + + if (!perf_pmu__benefits_from_affinity(pos->pmu)) + continue; + + if (evsel__is_dummy_event(pos)) { + /* + * The dummy event is opened on all CPUs so assume >1 + * event with shared CPUs. + */ + ret = true; + break; + } + if (evsel__is_retire_lat(pos)) { + /* + * Retirement latency events are similar to tool ones in + * their implementation, and so don't require affinity. + */ + continue; + } + if (perf_cpu_map__is_empty(used_cpus)) { + /* First benefitting event, we want >1 on a common CPU. */ + used_cpus = perf_cpu_map__get(pos->core.cpus); + continue; + } + if ((pos->core.attr.read_format & PERF_FORMAT_GROUP) && + evsel__leader(pos) != pos) { + /* Skip members of the same sample group. */ + continue; + } + intersect = perf_cpu_map__intersect(used_cpus, pos->core.cpus); + if (!perf_cpu_map__is_empty(intersect)) { + /* >1 event with shared CPUs. */ + perf_cpu_map__put(intersect); + ret = true; + break; + } + perf_cpu_map__put(intersect); + perf_cpu_map__merge(&used_cpus, pos->core.cpus); + } + perf_cpu_map__put(used_cpus); + return ret; +} + +void evlist_cpu_iterator__init(struct evlist_cpu_iterator *itr, struct evlist *evlist) +{ + *itr = (struct evlist_cpu_iterator){ .container = evlist, .evsel = NULL, .cpu_map_idx = 0, .evlist_cpu_map_idx = 0, .evlist_cpu_map_nr = perf_cpu_map__nr(evlist->core.all_cpus), .cpu = (struct perf_cpu){ .cpu = -1}, - .affinity = affinity, + .affinity = NULL, }; if (evlist__empty(evlist)) { /* Ensure the empty list doesn't iterate. */ - itr.evlist_cpu_map_idx = itr.evlist_cpu_map_nr; - } else { - itr.evsel = evlist__first(evlist); - if (itr.affinity) { - itr.cpu = perf_cpu_map__cpu(evlist->core.all_cpus, 0); - affinity__set(itr.affinity, itr.cpu.cpu); - itr.cpu_map_idx = perf_cpu_map__idx(itr.evsel->core.cpus, itr.cpu); - /* - * If this CPU isn't in the evsel's cpu map then advance - * through the list. - */ - if (itr.cpu_map_idx == -1) - evlist_cpu_iterator__next(&itr); - } + itr->evlist_cpu_map_idx = itr->evlist_cpu_map_nr; + return; + } + + if (evlist__use_affinity(evlist)) { + if (affinity__setup(&itr->saved_affinity) == 0) + itr->affinity = &itr->saved_affinity; } - return itr; + itr->evsel = evlist__first(evlist); + itr->cpu = perf_cpu_map__cpu(evlist->core.all_cpus, 0); + if (itr->affinity) + affinity__set(itr->affinity, itr->cpu.cpu); + itr->cpu_map_idx = perf_cpu_map__idx(itr->evsel->core.cpus, itr->cpu); + /* + * If this CPU isn't in the evsel's cpu map then advance + * through the list. + */ + if (itr->cpu_map_idx == -1) + evlist_cpu_iterator__next(itr); +} + +void evlist_cpu_iterator__exit(struct evlist_cpu_iterator *itr) +{ + if (!itr->affinity) + return; + + affinity__cleanup(itr->affinity); + itr->affinity = NULL; } void evlist_cpu_iterator__next(struct evlist_cpu_iterator *evlist_cpu_itr) @@ -406,14 +499,11 @@ void evlist_cpu_iterator__next(struct evlist_cpu_iterator *evlist_cpu_itr) */ if (evlist_cpu_itr->cpu_map_idx == -1) evlist_cpu_iterator__next(evlist_cpu_itr); + } else { + evlist_cpu_iterator__exit(evlist_cpu_itr); } } -bool evlist_cpu_iterator__end(const struct evlist_cpu_iterator *evlist_cpu_itr) -{ - return evlist_cpu_itr->evlist_cpu_map_idx >= evlist_cpu_itr->evlist_cpu_map_nr; -} - static int evsel__strcmp(struct evsel *pos, char *evsel_name) { if (!evsel_name) @@ -441,19 +531,11 @@ static void __evlist__disable(struct evlist *evlist, char *evsel_name, bool excl { struct evsel *pos; struct evlist_cpu_iterator evlist_cpu_itr; - struct affinity saved_affinity, *affinity = NULL; bool has_imm = false; - // See explanation in evlist__close() - if (!cpu_map__is_dummy(evlist->core.user_requested_cpus)) { - if (affinity__setup(&saved_affinity) < 0) - return; - affinity = &saved_affinity; - } - /* Disable 'immediate' events last */ for (int imm = 0; imm <= 1; imm++) { - evlist__for_each_cpu(evlist_cpu_itr, evlist, affinity) { + evlist__for_each_cpu(evlist_cpu_itr, evlist) { pos = evlist_cpu_itr.evsel; if (evsel__strcmp(pos, evsel_name)) continue; @@ -471,7 +553,6 @@ static void __evlist__disable(struct evlist *evlist, char *evsel_name, bool excl break; } - affinity__cleanup(affinity); evlist__for_each_entry(evlist, pos) { if (evsel__strcmp(pos, evsel_name)) continue; @@ -511,16 +592,8 @@ static void __evlist__enable(struct evlist *evlist, char *evsel_name, bool excl_ { struct evsel *pos; struct evlist_cpu_iterator evlist_cpu_itr; - struct affinity saved_affinity, *affinity = NULL; - - // See explanation in evlist__close() - if (!cpu_map__is_dummy(evlist->core.user_requested_cpus)) { - if (affinity__setup(&saved_affinity) < 0) - return; - affinity = &saved_affinity; - } - evlist__for_each_cpu(evlist_cpu_itr, evlist, affinity) { + evlist__for_each_cpu(evlist_cpu_itr, evlist) { pos = evlist_cpu_itr.evsel; if (evsel__strcmp(pos, evsel_name)) continue; @@ -530,7 +603,6 @@ static void __evlist__enable(struct evlist *evlist, char *evsel_name, bool excl_ continue; evsel__enable_cpu(pos, evlist_cpu_itr.cpu_map_idx); } - affinity__cleanup(affinity); evlist__for_each_entry(evlist, pos) { if (evsel__strcmp(pos, evsel_name)) continue; @@ -753,9 +825,8 @@ static struct mmap *evlist__alloc_mmap(struct evlist *evlist, bool overwrite) { int i; - struct mmap *map; + struct mmap *map = calloc(evlist->core.nr_mmaps, sizeof(struct mmap)); - map = zalloc(evlist->core.nr_mmaps * sizeof(struct mmap)); if (!map) return NULL; @@ -1006,8 +1077,7 @@ int evlist__create_maps(struct evlist *evlist, struct target *target) * per-thread data. thread_map__new_str will call * thread_map__new_all_cpus to enumerate all threads. */ - threads = thread_map__new_str(target->pid, target->tid, target->uid, - all_threads); + threads = thread_map__new_str(target->pid, target->tid, all_threads); if (!threads) return -1; @@ -1328,28 +1398,14 @@ void evlist__close(struct evlist *evlist) { struct evsel *evsel; struct evlist_cpu_iterator evlist_cpu_itr; - struct affinity affinity; - /* - * With perf record core.user_requested_cpus is usually NULL. - * Use the old method to handle this for now. - */ - if (!evlist->core.user_requested_cpus || - cpu_map__is_dummy(evlist->core.user_requested_cpus)) { - evlist__for_each_entry_reverse(evlist, evsel) - evsel__close(evsel); - return; - } - - if (affinity__setup(&affinity) < 0) - return; - - evlist__for_each_cpu(evlist_cpu_itr, evlist, &affinity) { + evlist__for_each_cpu(evlist_cpu_itr, evlist) { + if (evlist_cpu_itr.cpu_map_idx == 0 && evsel__is_retire_lat(evlist_cpu_itr.evsel)) + evsel__tpebs_close(evlist_cpu_itr.evsel); perf_evsel__close_cpu(&evlist_cpu_itr.evsel->core, evlist_cpu_itr.cpu_map_idx); } - affinity__cleanup(&affinity); evlist__for_each_entry_reverse(evlist, evsel) { perf_evsel__free_fd(&evsel->core); perf_evsel__free_id(&evsel->core); @@ -1373,19 +1429,18 @@ static int evlist__create_syswide_maps(struct evlist *evlist) */ cpus = perf_cpu_map__new_online_cpus(); if (!cpus) - goto out; + return -ENOMEM; threads = perf_thread_map__new_dummy(); - if (!threads) - goto out_put; + if (!threads) { + perf_cpu_map__put(cpus); + return -ENOMEM; + } perf_evlist__set_maps(&evlist->core, cpus, threads); - perf_thread_map__put(threads); -out_put: perf_cpu_map__put(cpus); -out: - return -ENOMEM; + return 0; } int evlist__open(struct evlist *evlist) @@ -1576,8 +1631,11 @@ int evlist__parse_sample(struct evlist *evlist, union perf_event *event, struct struct evsel *evsel = evlist__event2evsel(evlist, event); int ret; - if (!evsel) + if (!evsel) { + /* Ensure the sample is okay for perf_sample__exit. */ + perf_sample__init(sample, /*all=*/false); return -EFAULT; + } ret = evsel__parse_sample(evsel, event, sample); if (ret) return ret; @@ -1604,14 +1662,14 @@ int evlist__parse_sample_timestamp(struct evlist *evlist, union perf_event *even int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size) { int printed, value; - char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf)); switch (err) { case EACCES: case EPERM: + errno = err; printed = scnprintf(buf, size, - "Error:\t%s.\n" - "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); + "Error:\t%m.\n" + "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting."); value = perf_event_paranoid(); @@ -1638,16 +1696,18 @@ int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size if (first->core.attr.sample_freq < (u64)max_freq) goto out_default; + errno = err; printed = scnprintf(buf, size, - "Error:\t%s.\n" + "Error:\t%m.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_max_sample_rate.\n" "Hint:\tThe current value is %d and %" PRIu64 " is being requested.", - emsg, max_freq, first->core.attr.sample_freq); + max_freq, first->core.attr.sample_freq); break; } default: out_default: - scnprintf(buf, size, "%s", emsg); + errno = err; + scnprintf(buf, size, "%m"); break; } @@ -1656,17 +1716,17 @@ out_default: int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size) { - char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf)); int pages_attempted = evlist->core.mmap_len / 1024, pages_max_per_user, printed = 0; switch (err) { case EPERM: sysctl__read_int("kernel/perf_event_mlock_kb", &pages_max_per_user); + errno = err; printed += scnprintf(buf + printed, size - printed, - "Error:\t%s.\n" + "Error:\t%m.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_mlock_kb (%d kB) setting.\n" "Hint:\tTried using %zd kB.\n", - emsg, pages_max_per_user, pages_attempted); + pages_max_per_user, pages_attempted); if (pages_attempted >= pages_max_per_user) { printed += scnprintf(buf + printed, size - printed, @@ -1678,7 +1738,8 @@ int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size "Hint:\tTry using a smaller -m/--mmap-pages value."); break; default: - scnprintf(buf, size, "%s", emsg); + errno = err; + scnprintf(buf, size, "%m"); break; } @@ -1910,8 +1971,8 @@ static int evlist__parse_control_fifo(const char *str, int *ctl_fd, int *ctl_fd_ */ fd = open(s, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - pr_err("Failed to open '%s'\n", s); ret = -errno; + pr_err("Failed to open '%s': %m\n", s); goto out_free; } *ctl_fd = fd; @@ -1921,7 +1982,7 @@ static int evlist__parse_control_fifo(const char *str, int *ctl_fd, int *ctl_fd_ /* O_RDWR | O_NONBLOCK means the other end need not be open */ fd = open(p, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - pr_err("Failed to open '%s'\n", p); + pr_err("Failed to open '%s': %m\n", p); ret = -errno; goto out_free; } @@ -1935,7 +1996,8 @@ out_free: int evlist__parse_control(const char *str, int *ctl_fd, int *ctl_fd_ack, bool *ctl_fd_close) { - char *comma = NULL, *endptr = NULL; + const char *comma = NULL; + char *endptr = NULL; *ctl_fd_close = false; @@ -2353,7 +2415,7 @@ int evlist__parse_event_enable_time(struct evlist *evlist, struct record_opts *o eet->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (eet->timerfd == -1) { err = -errno; - pr_err("timerfd_create failed: %s\n", strerror(errno)); + pr_err("timerfd_create failed: %m\n"); goto free_eet_times; } @@ -2388,7 +2450,7 @@ static int event_enable_timer__set_timer(struct event_enable_timer *eet, int ms) if (timerfd_settime(eet->timerfd, 0, &its, NULL) < 0) { err = -errno; - pr_err("timerfd_settime failed: %s\n", strerror(errno)); + pr_err("timerfd_settime failed: %m\n"); } return err; } @@ -2469,23 +2531,36 @@ struct evsel *evlist__find_evsel(struct evlist *evlist, int idx) return NULL; } -int evlist__scnprintf_evsels(struct evlist *evlist, size_t size, char *bf) +void evlist__format_evsels(struct evlist *evlist, struct strbuf *sb, size_t max_length) { - struct evsel *evsel; - int printed = 0; + struct evsel *evsel, *leader = NULL; + bool first = true; evlist__for_each_entry(evlist, evsel) { + struct evsel *new_leader = evsel__leader(evsel); + if (evsel__is_dummy_event(evsel)) continue; - if (size > (strlen(evsel__name(evsel)) + (printed ? 2 : 1))) { - printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "," : "", evsel__name(evsel)); - } else { - printed += scnprintf(bf + printed, size - printed, "%s...", printed ? "," : ""); - break; + + if (leader != new_leader && leader && leader->core.nr_members > 1) + strbuf_addch(sb, '}'); + + if (!first) + strbuf_addch(sb, ','); + + if (sb->len > max_length) { + strbuf_addstr(sb, "..."); + return; } - } + if (leader != new_leader && new_leader->core.nr_members > 1) + strbuf_addch(sb, '{'); - return printed; + strbuf_addstr(sb, evsel__name(evsel)); + first = false; + leader = new_leader; + } + if (leader && leader->core.nr_members > 1) + strbuf_addch(sb, '}'); } void evlist__check_mem_load_aux(struct evlist *evlist) @@ -2535,51 +2610,61 @@ void evlist__warn_user_requested_cpus(struct evlist *evlist, const char *cpu_lis return; evlist__for_each_entry(evlist, pos) { - struct perf_cpu_map *intersect, *to_test; - const struct perf_pmu *pmu = evsel__find_pmu(pos); + evsel__warn_user_requested_cpus(pos, user_requested_cpus); + } + perf_cpu_map__put(user_requested_cpus); +} - to_test = pmu && pmu->is_core ? pmu->cpus : cpu_map__online(); - intersect = perf_cpu_map__intersect(to_test, user_requested_cpus); - if (!perf_cpu_map__equal(intersect, user_requested_cpus)) { - char buf[128]; +/* Should uniquify be disabled for the evlist? */ +static bool evlist__disable_uniquify(const struct evlist *evlist) +{ + struct evsel *counter; + struct perf_pmu *last_pmu = NULL; + bool first = true; - cpu_map__snprint(to_test, buf, sizeof(buf)); - pr_warning("WARNING: A requested CPU in '%s' is not supported by PMU '%s' (CPUs %s) for event '%s'\n", - cpu_list, pmu ? pmu->name : "cpu", buf, evsel__name(pos)); + evlist__for_each_entry(evlist, counter) { + /* If PMUs vary then uniquify can be useful. */ + if (!first && counter->pmu != last_pmu) + return false; + first = false; + if (counter->pmu) { + /* Allow uniquify for uncore PMUs. */ + if (!counter->pmu->is_core) + return false; + /* Keep hybrid event names uniquified for clarity. */ + if (perf_pmus__num_core_pmus() > 1) + return false; } - perf_cpu_map__put(intersect); + last_pmu = counter->pmu; } - perf_cpu_map__put(user_requested_cpus); + return true; } -void evlist__uniquify_name(struct evlist *evlist) +static bool evlist__set_needs_uniquify(struct evlist *evlist, const struct perf_stat_config *config) { - char *new_name, empty_attributes[2] = ":", *attributes; - struct evsel *pos; - - if (perf_pmus__num_core_pmus() == 1) - return; + struct evsel *counter; + bool needs_uniquify = false; - evlist__for_each_entry(evlist, pos) { - if (!evsel__is_hybrid(pos)) - continue; + if (evlist__disable_uniquify(evlist)) { + evlist__for_each_entry(evlist, counter) + counter->uniquified_name = true; + return false; + } - if (strchr(pos->name, '/')) - continue; + evlist__for_each_entry(evlist, counter) { + if (evsel__set_needs_uniquify(counter, config)) + needs_uniquify = true; + } + return needs_uniquify; +} - attributes = strchr(pos->name, ':'); - if (attributes) - *attributes = '\0'; - else - attributes = empty_attributes; +void evlist__uniquify_evsel_names(struct evlist *evlist, const struct perf_stat_config *config) +{ + if (evlist__set_needs_uniquify(evlist, config)) { + struct evsel *pos; - if (asprintf(&new_name, "%s/%s/%s", pos->pmu ? pos->pmu->name : "", - pos->name, attributes + 1)) { - free(pos->name); - pos->name = new_name; - } else { - *attributes = ':'; - } + evlist__for_each_entry(evlist, pos) + evsel__uniquify_counter(pos); } } @@ -2594,3 +2679,17 @@ bool evlist__has_bpf_output(struct evlist *evlist) return false; } + +bool evlist__needs_bpf_sb_event(struct evlist *evlist) +{ + struct evsel *evsel; + + evlist__for_each_entry(evlist, evsel) { + if (evsel__is_dummy_event(evsel)) + continue; + if (!evsel->core.attr.exclude_kernel) + return true; + } + + return false; +} |
