diff options
Diffstat (limited to '')
-rw-r--r-- | tools/perf/util/evsel.c | 1552 |
1 files changed, 1141 insertions, 411 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1cad6051d8b0..bc603193c477 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -10,9 +10,9 @@ #include <errno.h> #include <inttypes.h> #include <linux/bitops.h> +#include <api/io.h> #include <api/fs/fs.h> #include <api/fs/tracing_path.h> -#include <traceevent/event-parse.h> #include <linux/hw_breakpoint.h> #include <linux/perf_event.h> #include <linux/compiler.h> @@ -25,11 +25,13 @@ #include <stdlib.h> #include <perf/evsel.h> #include "asm/bug.h" +#include "bpf_counter.h" #include "callchain.h" #include "cgroup.h" #include "counts.h" #include "event.h" #include "evsel.h" +#include "time-utils.h" #include "util/env.h" #include "util/evsel_config.h" #include "util/evsel_fprintf.h" @@ -45,17 +47,56 @@ #include "string2.h" #include "memswap.h" #include "util.h" +#include "util/hashmap.h" +#include "off_cpu.h" +#include "pmu.h" +#include "pmus.h" +#include "rlimit.h" #include "../perf-sys.h" #include "util/parse-branch-options.h" +#include "util/bpf-filter.h" +#include "util/hist.h" #include <internal/xyarray.h> #include <internal/lib.h> +#include <internal/threadmap.h> #include <linux/ctype.h> +#ifdef HAVE_LIBTRACEEVENT +#include <traceevent/event-parse.h> +#endif + struct perf_missing_features perf_missing_features; static clockid_t clockid; +static const char *const perf_tool_event__tool_names[PERF_TOOL_MAX] = { + NULL, + "duration_time", + "user_time", + "system_time", +}; + +const char *perf_tool_event__to_str(enum perf_tool_event ev) +{ + if (ev > PERF_TOOL_NONE && ev < PERF_TOOL_MAX) + return perf_tool_event__tool_names[ev]; + + return NULL; +} + +enum perf_tool_event perf_tool_event__from_str(const char *str) +{ + int i; + + perf_tool_event__for_each_event(i) { + if (!strcmp(str, perf_tool_event__tool_names[i])) + return i; + } + return PERF_TOOL_NONE; +} + + static int evsel__no_extra_init(struct evsel *evsel __maybe_unused) { return 0; @@ -236,27 +277,27 @@ bool evsel__is_function_event(struct evsel *evsel) void evsel__init(struct evsel *evsel, struct perf_event_attr *attr, int idx) { - perf_evsel__init(&evsel->core, attr); - evsel->idx = idx; + perf_evsel__init(&evsel->core, attr, idx); evsel->tracking = !idx; - evsel->leader = evsel; - evsel->unit = ""; + evsel->unit = strdup(""); evsel->scale = 1.0; evsel->max_events = ULONG_MAX; evsel->evlist = NULL; evsel->bpf_obj = NULL; evsel->bpf_fd = -1; INIT_LIST_HEAD(&evsel->config_terms); + INIT_LIST_HEAD(&evsel->bpf_counter_list); + INIT_LIST_HEAD(&evsel->bpf_filters); perf_evsel__object.init(evsel); evsel->sample_size = __evsel__sample_size(attr->sample_type); evsel__calc_id_pos(evsel); evsel->cmdline_group_boundary = false; - evsel->metric_expr = NULL; - evsel->metric_name = NULL; evsel->metric_events = NULL; evsel->per_pkg_mask = NULL; evsel->collect_stat = false; evsel->pmu_name = NULL; + evsel->group_pmu_name = NULL; + evsel->skippable = false; } struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx) @@ -267,75 +308,26 @@ struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx) return NULL; evsel__init(evsel, attr, idx); - if (evsel__is_bpf_output(evsel)) { - evsel->core.attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | + if (evsel__is_bpf_output(evsel) && !attr->sample_type) { + evsel->core.attr.sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD), evsel->core.attr.sample_period = 1; } if (evsel__is_clock(evsel)) { - /* - * The evsel->unit points to static alias->unit - * so it's ok to use static string in here. - */ - static const char *unit = "msec"; - - evsel->unit = unit; + free((char *)evsel->unit); + evsel->unit = strdup("msec"); evsel->scale = 1e-6; } return evsel; } -static bool perf_event_can_profile_kernel(void) -{ - return perf_event_paranoid_check(1); -} - -struct evsel *evsel__new_cycles(bool precise) -{ - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, - .config = PERF_COUNT_HW_CPU_CYCLES, - .exclude_kernel = !perf_event_can_profile_kernel(), - }; - struct evsel *evsel; - - event_attr_init(&attr); - - if (!precise) - goto new_event; - - /* - * Now let the usual logic to set up the perf_event_attr defaults - * to kick in when we return and before perf_evsel__open() is called. - */ -new_event: - evsel = evsel__new(&attr); - if (evsel == NULL) - goto out; - - evsel->precise_max = true; - - /* use asprintf() because free(evsel) assumes name is allocated */ - if (asprintf(&evsel->name, "cycles%s%s%.*s", - (attr.precise_ip || attr.exclude_kernel) ? ":" : "", - attr.exclude_kernel ? "u" : "", - attr.precise_ip ? attr.precise_ip + 1 : 0, "ppp") < 0) - goto error_free; -out: - return evsel; -error_free: - evsel__delete(evsel); - evsel = NULL; - goto out; -} - -static int evsel__copy_config_terms(struct evsel *dst, struct evsel *src) +int copy_config_terms(struct list_head *dst, struct list_head *src) { struct evsel_config_term *pos, *tmp; - list_for_each_entry(pos, &src->config_terms, list) { + list_for_each_entry(pos, src, list) { tmp = malloc(sizeof(*tmp)); if (tmp == NULL) return -ENOMEM; @@ -348,11 +340,16 @@ static int evsel__copy_config_terms(struct evsel *dst, struct evsel *src) return -ENOMEM; } } - list_add_tail(&tmp->list, &dst->config_terms); + list_add_tail(&tmp->list, dst); } return 0; } +static int evsel__copy_config_terms(struct evsel *dst, struct evsel *src) +{ + return copy_config_terms(&dst->config_terms, &src->config_terms); +} + /** * evsel__clone - create a new evsel copied from @orig * @orig: original evsel @@ -382,6 +379,8 @@ struct evsel *evsel__clone(struct evsel *orig) evsel->core.threads = perf_thread_map__get(orig->core.threads); evsel->core.nr_members = orig->core.nr_members; evsel->core.system_wide = orig->core.system_wide; + evsel->core.requires_cpu = orig->core.requires_cpu; + evsel->core.is_pmu_core = orig->core.is_pmu_core; if (orig->name) { evsel->name = strdup(orig->name); @@ -398,25 +397,40 @@ struct evsel *evsel__clone(struct evsel *orig) if (evsel->pmu_name == NULL) goto out_err; } + if (orig->group_pmu_name) { + evsel->group_pmu_name = strdup(orig->group_pmu_name); + if (evsel->group_pmu_name == NULL) + goto out_err; + } if (orig->filter) { evsel->filter = strdup(orig->filter); if (evsel->filter == NULL) goto out_err; } + if (orig->metric_id) { + evsel->metric_id = strdup(orig->metric_id); + if (evsel->metric_id == NULL) + goto out_err; + } evsel->cgrp = cgroup__get(orig->cgrp); +#ifdef HAVE_LIBTRACEEVENT evsel->tp_format = orig->tp_format; +#endif evsel->handler = orig->handler; - evsel->leader = orig->leader; + evsel->core.leader = orig->core.leader; evsel->max_events = orig->max_events; evsel->tool_event = orig->tool_event; - evsel->unit = orig->unit; + free((char *)evsel->unit); + evsel->unit = strdup(orig->unit); + if (evsel->unit == NULL) + goto out_err; + evsel->scale = orig->scale; evsel->snapshot = orig->snapshot; evsel->per_pkg = orig->per_pkg; evsel->percore = orig->percore; evsel->precise_max = orig->precise_max; - evsel->use_uncore_alias = orig->use_uncore_alias; evsel->is_libpfm_event = orig->is_libpfm_event; evsel->exclude_GH = orig->exclude_GH; @@ -424,6 +438,8 @@ struct evsel *evsel__clone(struct evsel *orig) evsel->auto_merge_stats = orig->auto_merge_stats; evsel->collect_stat = orig->collect_stat; evsel->weak_group = orig->weak_group; + evsel->use_config_name = orig->use_config_name; + evsel->pmu = orig->pmu; if (evsel__copy_config_terms(evsel, orig) < 0) goto out_err; @@ -438,7 +454,8 @@ out_err: /* * Returns pointer with encoded error via <linux/err.h> interface. */ -struct evsel *evsel__newtp_idx(const char *sys, const char *name, int idx) +#ifdef HAVE_LIBTRACEEVENT +struct evsel *evsel__newtp_idx(const char *sys, const char *name, int idx, bool format) { struct evsel *evsel = zalloc(perf_evsel__object.size); int err = -ENOMEM; @@ -455,14 +472,20 @@ struct evsel *evsel__newtp_idx(const char *sys, const char *name, int idx) if (asprintf(&evsel->name, "%s:%s", sys, name) < 0) goto out_free; - evsel->tp_format = trace_event__tp_format(sys, name); - if (IS_ERR(evsel->tp_format)) { - err = PTR_ERR(evsel->tp_format); - goto out_free; + event_attr_init(&attr); + + if (format) { + evsel->tp_format = trace_event__tp_format(sys, name); + if (IS_ERR(evsel->tp_format)) { + err = PTR_ERR(evsel->tp_format); + goto out_free; + } + attr.config = evsel->tp_format->id; + } else { + attr.config = (__u64) -1; } - event_attr_init(&attr); - attr.config = evsel->tp_format->id; + attr.sample_period = 1; evsel__init(evsel, &attr, idx); } @@ -475,8 +498,9 @@ out_free: out_err: return ERR_PTR(err); } +#endif -const char *evsel__hw_names[PERF_COUNT_HW_MAX] = { +const char *const evsel__hw_names[PERF_COUNT_HW_MAX] = { "cycles", "instructions", "cache-references", @@ -489,6 +513,28 @@ const char *evsel__hw_names[PERF_COUNT_HW_MAX] = { "ref-cycles", }; +char *evsel__bpf_counter_events; + +bool evsel__match_bpf_counter_events(const char *name) +{ + int name_len; + bool match; + char *ptr; + + if (!evsel__bpf_counter_events) + return false; + + ptr = strstr(evsel__bpf_counter_events, name); + name_len = strlen(name); + + /* check name matches a full token in evsel__bpf_counter_events */ + match = (ptr != NULL) && + ((ptr == evsel__bpf_counter_events) || (*(ptr - 1) == ',')) && + ((*(ptr + name_len) == ',') || (*(ptr + name_len) == '\0')); + + return match; +} + static const char *__evsel__hw_name(u64 config) { if (config < PERF_COUNT_HW_MAX && evsel__hw_names[config]) @@ -497,7 +543,7 @@ static const char *__evsel__hw_name(u64 config) return "unknown-hardware"; } -static int perf_evsel__add_modifiers(struct evsel *evsel, char *bf, size_t size) +static int evsel__add_modifiers(struct evsel *evsel, char *bf, size_t size) { int colon = 0, r = 0; struct perf_event_attr *attr = &evsel->core.attr; @@ -533,13 +579,18 @@ static int perf_evsel__add_modifiers(struct evsel *evsel, char *bf, size_t size) return r; } +int __weak arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size) +{ + return scnprintf(bf, size, "%s", __evsel__hw_name(evsel->core.attr.config)); +} + static int evsel__hw_name(struct evsel *evsel, char *bf, size_t size) { - int r = scnprintf(bf, size, "%s", __evsel__hw_name(evsel->core.attr.config)); - return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); + int r = arch_evsel__hw_name(evsel, bf, size); + return r + evsel__add_modifiers(evsel, bf + r, size - r); } -const char *evsel__sw_names[PERF_COUNT_SW_MAX] = { +const char *const evsel__sw_names[PERF_COUNT_SW_MAX] = { "cpu-clock", "task-clock", "page-faults", @@ -562,7 +613,12 @@ static const char *__evsel__sw_name(u64 config) static int evsel__sw_name(struct evsel *evsel, char *bf, size_t size) { int r = scnprintf(bf, size, "%s", __evsel__sw_name(evsel->core.attr.config)); - return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); + return r + evsel__add_modifiers(evsel, bf + r, size - r); +} + +static int evsel__tool_name(enum perf_tool_event ev, char *bf, size_t size) +{ + return scnprintf(bf, size, "%s", perf_tool_event__to_str(ev)); } static int __evsel__bp_name(char *bf, size_t size, u64 addr, u64 type) @@ -587,10 +643,10 @@ static int evsel__bp_name(struct evsel *evsel, char *bf, size_t size) { struct perf_event_attr *attr = &evsel->core.attr; int r = __evsel__bp_name(bf, size, attr->bp_addr, attr->bp_type); - return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); + return r + evsel__add_modifiers(evsel, bf + r, size - r); } -const char *evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX][EVSEL__MAX_ALIASES] = { +const char *const evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX][EVSEL__MAX_ALIASES] = { { "L1-dcache", "l1-d", "l1d", "L1-data", }, { "L1-icache", "l1-i", "l1i", "L1-instruction", }, { "LLC", "L2", }, @@ -600,13 +656,13 @@ const char *evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX][EVSEL__MAX_ALIASES] = { { "node", }, }; -const char *evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][EVSEL__MAX_ALIASES] = { +const char *const evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][EVSEL__MAX_ALIASES] = { { "load", "loads", "read", }, { "store", "stores", "write", }, { "prefetch", "prefetches", "speculative-read", "speculative-load", }, }; -const char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX][EVSEL__MAX_ALIASES] = { +const char *const evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX][EVSEL__MAX_ALIASES] = { { "refs", "Reference", "ops", "access", }, { "misses", "miss", }, }; @@ -618,11 +674,11 @@ const char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX][EVSEL__MAX_AL #define COP(x) (1 << x) /* - * cache operartion stat + * cache operation stat * L1I : Read and prefetch only * ITLB and BPU : Read-only */ -static unsigned long evsel__hw_cache_stat[C(MAX)] = { +static const unsigned long evsel__hw_cache_stat[C(MAX)] = { [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), [C(L1I)] = (CACHE_READ | CACHE_PREFETCH), [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), @@ -682,19 +738,13 @@ out_err: static int evsel__hw_cache_name(struct evsel *evsel, char *bf, size_t size) { int ret = __evsel__hw_cache_name(evsel->core.attr.config, bf, size); - return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret); + return ret + evsel__add_modifiers(evsel, bf + ret, size - ret); } static int evsel__raw_name(struct evsel *evsel, char *bf, size_t size) { int ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->core.attr.config); - return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret); -} - -static int evsel__tool_name(char *bf, size_t size) -{ - int ret = scnprintf(bf, size, "duration_time"); - return ret; + return ret + evsel__add_modifiers(evsel, bf + ret, size - ret); } const char *evsel__name(struct evsel *evsel) @@ -721,8 +771,8 @@ const char *evsel__name(struct evsel *evsel) break; case PERF_TYPE_SOFTWARE: - if (evsel->tool_event) - evsel__tool_name(bf, sizeof(bf)); + if (evsel__is_tool(evsel)) + evsel__tool_name(evsel->tool_event, bf, sizeof(bf)); else evsel__sw_name(evsel, bf, sizeof(bf)); break; @@ -749,6 +799,22 @@ out_unknown: return "unknown"; } +bool evsel__name_is(struct evsel *evsel, const char *name) +{ + return !strcmp(evsel__name(evsel), name); +} + +const char *evsel__metric_id(const struct evsel *evsel) +{ + if (evsel->metric_id) + return evsel->metric_id; + + if (evsel__is_tool(evsel)) + return perf_tool_event__to_str(evsel->tool_event); + + return "unknown"; +} + const char *evsel__group_name(struct evsel *evsel) { return evsel->group_name ?: "anon group"; @@ -767,16 +833,22 @@ const char *evsel__group_name(struct evsel *evsel) int evsel__group_desc(struct evsel *evsel, char *buf, size_t size) { int ret = 0; + bool first = true; struct evsel *pos; const char *group_name = evsel__group_name(evsel); if (!evsel->forced_leader) ret = scnprintf(buf, size, "%s { ", group_name); - ret += scnprintf(buf + ret, size - ret, "%s", evsel__name(evsel)); + for_each_group_evsel(pos, evsel) { + if (symbol_conf.skip_empty && + evsel__hists(pos)->stats.nr_samples == 0) + continue; - for_each_group_member(pos, evsel) - ret += scnprintf(buf + ret, size - ret, ", %s", evsel__name(pos)); + ret += scnprintf(buf + ret, size - ret, "%s%s", + first ? "" : ", ", evsel__name(pos)); + first = false; + } if (!evsel->forced_leader) ret += scnprintf(buf + ret, size - ret, " }"); @@ -789,6 +861,7 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o { bool function = evsel__is_function_event(evsel); struct perf_event_attr *attr = &evsel->core.attr; + const char *arch = perf_env__arch(evsel__env(evsel)); evsel__set_sample_bit(evsel, CALLCHAIN); @@ -821,13 +894,14 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o if (!function) { evsel__set_sample_bit(evsel, REGS_USER); evsel__set_sample_bit(evsel, STACK_USER); - if (opts->sample_user_regs && DWARF_MINIMAL_REGS != PERF_REGS_MASK) { - attr->sample_regs_user |= DWARF_MINIMAL_REGS; + if (opts->sample_user_regs && + DWARF_MINIMAL_REGS(arch) != arch__user_reg_mask()) { + attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch); pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, " "specifying a subset with --user-regs may render DWARF unwinding unreliable, " "so the minimal registers set (IP, SP) is explicitly forced.\n"); } else { - attr->sample_regs_user |= PERF_REGS_MASK; + attr->sample_regs_user |= arch__user_reg_mask(); } attr->sample_stack_user = param->dump_size; attr->exclude_callchain_user = 1; @@ -850,9 +924,7 @@ void evsel__config_callchain(struct evsel *evsel, struct record_opts *opts, return __evsel__config_callchain(evsel, opts, param); } -static void -perf_evsel__reset_callgraph(struct evsel *evsel, - struct callchain_param *param) +static void evsel__reset_callgraph(struct evsel *evsel, struct callchain_param *param) { struct perf_event_attr *attr = &evsel->core.attr; @@ -988,7 +1060,7 @@ static void evsel__apply_config_terms(struct evsel *evsel, /* If global callgraph set, clear it */ if (callchain_param.enabled) - perf_evsel__reset_callgraph(evsel, &callchain_param); + evsel__reset_callgraph(evsel, &callchain_param); /* set perf-event callgraph */ if (param.enabled) { @@ -1014,6 +1086,32 @@ struct evsel_config_term *__evsel__get_config_term(struct evsel *evsel, enum evs return found_term; } +void __weak arch_evsel__set_sample_weight(struct evsel *evsel) +{ + evsel__set_sample_bit(evsel, WEIGHT); +} + +void __weak arch__post_evsel_config(struct evsel *evsel __maybe_unused, + struct perf_event_attr *attr __maybe_unused) +{ +} + +static void evsel__set_default_freq_period(struct record_opts *opts, + struct perf_event_attr *attr) +{ + if (opts->freq) { + attr->freq = 1; + attr->sample_freq = opts->freq; + } else { + attr->sample_period = opts->default_interval; + } +} + +static bool evsel__is_offcpu_event(struct evsel *evsel) +{ + return evsel__is_bpf_output(evsel) && evsel__name_is(evsel, OFFCPU_EVENT); +} + /* * The enable_on_exec/disabled value strategy: * @@ -1045,7 +1143,7 @@ struct evsel_config_term *__evsel__get_config_term(struct evsel *evsel, enum evs void evsel__config(struct evsel *evsel, struct record_opts *opts, struct callchain_param *callchain) { - struct evsel *leader = evsel->leader; + struct evsel *leader = evsel__leader(evsel); struct perf_event_attr *attr = &evsel->core.attr; int track = evsel->tracking; bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; @@ -1053,6 +1151,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; attr->inherit = !opts->no_inherit; attr->write_backward = opts->overwrite ? 1 : 0; + attr->read_format = PERF_FORMAT_LOST; evsel__set_sample_bit(evsel, IP); evsel__set_sample_bit(evsel, TID); @@ -1080,14 +1179,12 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, * We default some events to have a default interval. But keep * it a weak assumption overridable by the user. */ - if (!attr->sample_period) { - if (opts->freq) { - attr->freq = 1; - attr->sample_freq = opts->freq; - } else { - attr->sample_period = opts->default_interval; - } - } + if ((evsel->is_libpfm_event && !attr->sample_period) || + (!evsel->is_libpfm_event && (!attr->sample_period || + opts->user_freq != UINT_MAX || + opts->user_interval != ULLONG_MAX))) + evsel__set_default_freq_period(opts, attr); + /* * If attr->freq was set (here or earlier), ask for period * to be sampled. @@ -1168,12 +1265,14 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, } if (opts->sample_weight) - evsel__set_sample_bit(evsel, WEIGHT); + arch_evsel__set_sample_weight(evsel); + + attr->task = track; + attr->mmap = track; + attr->mmap2 = track && !perf_missing_features.mmap2; + attr->comm = track; + attr->build_id = track && opts->build_id; - attr->task = track; - attr->mmap = track; - attr->mmap2 = track && !perf_missing_features.mmap2; - attr->comm = track; /* * ksymbol is tracked separately with text poke because it needs to be * system wide and enabled immediately. @@ -1190,6 +1289,12 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, evsel__set_sample_bit(evsel, CGROUP); } + if (opts->sample_data_page_size) + evsel__set_sample_bit(evsel, DATA_PAGE_SIZE); + + if (opts->sample_code_page_size) + evsel__set_sample_bit(evsel, CODE_PAGE_SIZE); + if (opts->record_switch_events) attr->context_switch = track; @@ -1216,7 +1321,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, * group leaders for traced executed by perf. */ if (target__none(&opts->target) && evsel__is_group_leader(evsel) && - !opts->initial_delay) + !opts->target.initial_delay) attr->enable_on_exec = 1; if (evsel->immediate) { @@ -1272,6 +1377,11 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, */ if (evsel__is_dummy_event(evsel)) evsel__reset_sample_bit(evsel, BRANCH_STACK); + + if (evsel__is_offcpu_event(evsel)) + evsel->core.attr.sample_type &= OFFCPU_SAMPLE_TYPES; + + arch__post_evsel_config(evsel, attr); } int evsel__set_filter(struct evsel *evsel, const char *filter) @@ -1314,9 +1424,9 @@ int evsel__append_addr_filter(struct evsel *evsel, const char *filter) } /* Caller has to clear disabled after going through all CPUs. */ -int evsel__enable_cpu(struct evsel *evsel, int cpu) +int evsel__enable_cpu(struct evsel *evsel, int cpu_map_idx) { - return perf_evsel__enable_cpu(&evsel->core, cpu); + return perf_evsel__enable_cpu(&evsel->core, cpu_map_idx); } int evsel__enable(struct evsel *evsel) @@ -1329,9 +1439,9 @@ int evsel__enable(struct evsel *evsel) } /* Caller has to set disabled after going through all CPUs. */ -int evsel__disable_cpu(struct evsel *evsel, int cpu) +int evsel__disable_cpu(struct evsel *evsel, int cpu_map_idx) { - return perf_evsel__disable_cpu(&evsel->core, cpu); + return perf_evsel__disable_cpu(&evsel->core, cpu_map_idx); } int evsel__disable(struct evsel *evsel) @@ -1349,11 +1459,11 @@ int evsel__disable(struct evsel *evsel) return err; } -static void evsel__free_config_terms(struct evsel *evsel) +void free_config_terms(struct list_head *config_terms) { struct evsel_config_term *term, *h; - list_for_each_entry_safe(term, h, &evsel->config_terms, list) { + list_for_each_entry_safe(term, h, config_terms, list) { list_del_init(&term->list); if (term->free_str) zfree(&term->val.str); @@ -1361,10 +1471,17 @@ static void evsel__free_config_terms(struct evsel *evsel) } } +static void evsel__free_config_terms(struct evsel *evsel) +{ + free_config_terms(&evsel->config_terms); +} + void evsel__exit(struct evsel *evsel) { assert(list_empty(&evsel->core.node)); assert(evsel->evlist == NULL); + bpf_counter__destroy(evsel); + perf_bpf_filter__destroy(evsel); evsel__free_counts(evsel); perf_evsel__free_fd(&evsel->core); perf_evsel__free_id(&evsel->core); @@ -1375,19 +1492,31 @@ void evsel__exit(struct evsel *evsel) perf_thread_map__put(evsel->core.threads); zfree(&evsel->group_name); zfree(&evsel->name); + zfree(&evsel->filter); zfree(&evsel->pmu_name); - zfree(&evsel->per_pkg_mask); + zfree(&evsel->group_pmu_name); + zfree(&evsel->unit); + zfree(&evsel->metric_id); + evsel__zero_per_pkg(evsel); + hashmap__free(evsel->per_pkg_mask); + evsel->per_pkg_mask = NULL; zfree(&evsel->metric_events); perf_evsel__object.fini(evsel); + if (evsel->tool_event == PERF_TOOL_SYSTEM_TIME || + evsel->tool_event == PERF_TOOL_USER_TIME) + xyarray__delete(evsel->start_times); } void evsel__delete(struct evsel *evsel) { + if (!evsel) + return; + evsel__exit(evsel); free(evsel); } -void evsel__compute_deltas(struct evsel *evsel, int cpu, int thread, +void evsel__compute_deltas(struct evsel *evsel, int cpu_map_idx, int thread, struct perf_counts_values *count) { struct perf_counts_values tmp; @@ -1395,67 +1524,41 @@ void evsel__compute_deltas(struct evsel *evsel, int cpu, int thread, if (!evsel->prev_raw_counts) return; - if (cpu == -1) { - tmp = evsel->prev_raw_counts->aggr; - evsel->prev_raw_counts->aggr = *count; - } else { - tmp = *perf_counts(evsel->prev_raw_counts, cpu, thread); - *perf_counts(evsel->prev_raw_counts, cpu, thread) = *count; - } + tmp = *perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread); + *perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread) = *count; count->val = count->val - tmp.val; count->ena = count->ena - tmp.ena; count->run = count->run - tmp.run; } -void perf_counts_values__scale(struct perf_counts_values *count, - bool scale, s8 *pscaled) -{ - s8 scaled = 0; - - if (scale) { - if (count->run == 0) { - scaled = -1; - count->val = 0; - } else if (count->run < count->ena) { - scaled = 1; - count->val = (u64)((double) count->val * count->ena / count->run); - } - } - - if (pscaled) - *pscaled = scaled; -} - -static int evsel__read_one(struct evsel *evsel, int cpu, int thread) +static int evsel__read_one(struct evsel *evsel, int cpu_map_idx, int thread) { - struct perf_counts_values *count = perf_counts(evsel->counts, cpu, thread); + struct perf_counts_values *count = perf_counts(evsel->counts, cpu_map_idx, thread); - return perf_evsel__read(&evsel->core, cpu, thread, count); + return perf_evsel__read(&evsel->core, cpu_map_idx, thread, count); } -static void -perf_evsel__set_count(struct evsel *counter, int cpu, int thread, - u64 val, u64 ena, u64 run) +static void evsel__set_count(struct evsel *counter, int cpu_map_idx, int thread, + u64 val, u64 ena, u64 run, u64 lost) { struct perf_counts_values *count; - count = perf_counts(counter->counts, cpu, thread); + count = perf_counts(counter->counts, cpu_map_idx, thread); count->val = val; count->ena = ena; count->run = run; + count->lost = lost; - perf_counts__set_loaded(counter->counts, cpu, thread, true); + perf_counts__set_loaded(counter->counts, cpu_map_idx, thread, true); } -static int -perf_evsel__process_group_data(struct evsel *leader, - int cpu, int thread, u64 *data) +static int evsel__process_group_data(struct evsel *leader, int cpu_map_idx, int thread, u64 *data) { u64 read_format = leader->core.attr.read_format; struct sample_read_value *v; - u64 nr, ena = 0, run = 0, i; + u64 nr, ena = 0, run = 0, lost = 0; nr = *data++; @@ -1468,26 +1571,24 @@ perf_evsel__process_group_data(struct evsel *leader, if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) run = *data++; - v = (struct sample_read_value *) data; - - perf_evsel__set_count(leader, cpu, thread, - v[0].value, ena, run); - - for (i = 1; i < nr; i++) { + v = (void *)data; + sample_read_group__for_each(v, nr, read_format) { struct evsel *counter; - counter = perf_evlist__id2evsel(leader->evlist, v[i].id); + counter = evlist__id2evsel(leader->evlist, v->id); if (!counter) return -EINVAL; - perf_evsel__set_count(counter, cpu, thread, - v[i].value, ena, run); + if (read_format & PERF_FORMAT_LOST) + lost = v->lost; + + evsel__set_count(counter, cpu_map_idx, thread, v->value, ena, run, lost); } return 0; } -static int evsel__read_group(struct evsel *leader, int cpu, int thread) +static int evsel__read_group(struct evsel *leader, int cpu_map_idx, int thread) { struct perf_stat_evsel *ps = leader->stats; u64 read_format = leader->core.attr.read_format; @@ -1508,48 +1609,231 @@ static int evsel__read_group(struct evsel *leader, int cpu, int thread) ps->group_data = data; } - if (FD(leader, cpu, thread) < 0) + if (FD(leader, cpu_map_idx, thread) < 0) return -EINVAL; - if (readn(FD(leader, cpu, thread), data, size) <= 0) + if (readn(FD(leader, cpu_map_idx, thread), data, size) <= 0) return -errno; - return perf_evsel__process_group_data(leader, cpu, thread, data); + return evsel__process_group_data(leader, cpu_map_idx, thread, data); +} + +static bool read_until_char(struct io *io, char e) +{ + int c; + + do { + c = io__get_char(io); + if (c == -1) + return false; + } while (c != e); + return true; +} + +static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val) +{ + char buf[256]; + struct io io; + int i; + + io__init(&io, fd, buf, sizeof(buf)); + + /* Skip lines to relevant CPU. */ + for (i = -1; i < cpu.cpu; i++) { + if (!read_until_char(&io, '\n')) + return -EINVAL; + } + /* Skip to "cpu". */ + if (io__get_char(&io) != 'c') return -EINVAL; + if (io__get_char(&io) != 'p') return -EINVAL; + if (io__get_char(&io) != 'u') return -EINVAL; + + /* Skip N of cpuN. */ + if (!read_until_char(&io, ' ')) + return -EINVAL; + + i = 1; + while (true) { + if (io__get_dec(&io, val) != ' ') + break; + if (field == i) + return 0; + i++; + } + return -EINVAL; +} + +static int read_pid_stat_field(int fd, int field, __u64 *val) +{ + char buf[256]; + struct io io; + int c, i; + + io__init(&io, fd, buf, sizeof(buf)); + if (io__get_dec(&io, val) != ' ') + return -EINVAL; + if (field == 1) + return 0; + + /* Skip comm. */ + if (io__get_char(&io) != '(' || !read_until_char(&io, ')')) + return -EINVAL; + if (field == 2) + return -EINVAL; /* String can't be returned. */ + + /* Skip state */ + if (io__get_char(&io) != ' ' || io__get_char(&io) == -1) + return -EINVAL; + if (field == 3) + return -EINVAL; /* String can't be returned. */ + + /* Loop over numeric fields*/ + if (io__get_char(&io) != ' ') + return -EINVAL; + + i = 4; + while (true) { + c = io__get_dec(&io, val); + if (c == -1) + return -EINVAL; + if (c == -2) { + /* Assume a -ve was read */ + c = io__get_dec(&io, val); + *val *= -1; + } + if (c != ' ') + return -EINVAL; + if (field == i) + return 0; + i++; + } + return -EINVAL; +} + +static int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread) +{ + __u64 *start_time, cur_time, delta_start; + int fd, err = 0; + struct perf_counts_values *count; + bool adjust = false; + + count = perf_counts(evsel->counts, cpu_map_idx, thread); + + switch (evsel->tool_event) { + case PERF_TOOL_DURATION_TIME: + /* + * Pretend duration_time is only on the first CPU and thread, or + * else aggregation will scale duration_time by the number of + * CPUs/threads. + */ + start_time = &evsel->start_time; + if (cpu_map_idx == 0 && thread == 0) + cur_time = rdclock(); + else + cur_time = *start_time; + break; + case PERF_TOOL_USER_TIME: + case PERF_TOOL_SYSTEM_TIME: { + bool system = evsel->tool_event == PERF_TOOL_SYSTEM_TIME; + + start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread); + fd = FD(evsel, cpu_map_idx, thread); + lseek(fd, SEEK_SET, 0); + if (evsel->pid_stat) { + /* The event exists solely on 1 CPU. */ + if (cpu_map_idx == 0) + err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time); + else + cur_time = 0; + } else { + /* The event is for all threads. */ + if (thread == 0) { + struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus, + cpu_map_idx); + + err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time); + } else { + cur_time = 0; + } + } + adjust = true; + break; + } + case PERF_TOOL_NONE: + case PERF_TOOL_MAX: + default: + err = -EINVAL; + } + if (err) + return err; + + delta_start = cur_time - *start_time; + if (adjust) { + __u64 ticks_per_sec = sysconf(_SC_CLK_TCK); + + delta_start *= 1000000000 / ticks_per_sec; + } + count->val = delta_start; + count->ena = count->run = delta_start; + count->lost = 0; + return 0; } -int evsel__read_counter(struct evsel *evsel, int cpu, int thread) +int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread) { - u64 read_format = evsel->core.attr.read_format; + if (evsel__is_tool(evsel)) + return evsel__read_tool(evsel, cpu_map_idx, thread); - if (read_format & PERF_FORMAT_GROUP) - return evsel__read_group(evsel, cpu, thread); + if (evsel->core.attr.read_format & PERF_FORMAT_GROUP) + return evsel__read_group(evsel, cpu_map_idx, thread); - return evsel__read_one(evsel, cpu, thread); + return evsel__read_one(evsel, cpu_map_idx, thread); } -int __evsel__read_on_cpu(struct evsel *evsel, int cpu, int thread, bool scale) +int __evsel__read_on_cpu(struct evsel *evsel, int cpu_map_idx, int thread, bool scale) { struct perf_counts_values count; size_t nv = scale ? 3 : 1; - if (FD(evsel, cpu, thread) < 0) + if (FD(evsel, cpu_map_idx, thread) < 0) return -EINVAL; - if (evsel->counts == NULL && evsel__alloc_counts(evsel, cpu + 1, thread + 1) < 0) + if (evsel->counts == NULL && evsel__alloc_counts(evsel) < 0) return -ENOMEM; - if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) <= 0) + if (readn(FD(evsel, cpu_map_idx, thread), &count, nv * sizeof(u64)) <= 0) return -errno; - evsel__compute_deltas(evsel, cpu, thread, &count); + evsel__compute_deltas(evsel, cpu_map_idx, thread, &count); perf_counts_values__scale(&count, scale, NULL); - *perf_counts(evsel->counts, cpu, thread) = count; + *perf_counts(evsel->counts, cpu_map_idx, thread) = count; return 0; } -static int get_group_fd(struct evsel *evsel, int cpu, int thread) +static int evsel__match_other_cpu(struct evsel *evsel, struct evsel *other, + int cpu_map_idx) { - struct evsel *leader = evsel->leader; + struct perf_cpu cpu; + + cpu = perf_cpu_map__cpu(evsel->core.cpus, cpu_map_idx); + return perf_cpu_map__idx(other->core.cpus, cpu); +} + +static int evsel__hybrid_group_cpu_map_idx(struct evsel *evsel, int cpu_map_idx) +{ + struct evsel *leader = evsel__leader(evsel); + + if ((evsel__is_hybrid(evsel) && !evsel__is_hybrid(leader)) || + (!evsel__is_hybrid(evsel) && evsel__is_hybrid(leader))) { + return evsel__match_other_cpu(evsel, leader, cpu_map_idx); + } + + return cpu_map_idx; +} + +static int get_group_fd(struct evsel *evsel, int cpu_map_idx, int thread) +{ + struct evsel *leader = evsel__leader(evsel); int fd; if (evsel__is_group_leader(evsel)) @@ -1561,15 +1845,21 @@ static int get_group_fd(struct evsel *evsel, int cpu, int thread) */ BUG_ON(!leader->core.fd); - fd = FD(leader, cpu, thread); - BUG_ON(fd == -1); + cpu_map_idx = evsel__hybrid_group_cpu_map_idx(evsel, cpu_map_idx); + if (cpu_map_idx == -1) + return -1; + + fd = FD(leader, cpu_map_idx, thread); + BUG_ON(fd == -1 && !leader->skippable); - return fd; + /* + * When the leader has been skipped, return -2 to distinguish from no + * group leader case. + */ + return fd == -1 ? -2 : fd; } -static void perf_evsel__remove_fd(struct evsel *pos, - int nr_cpus, int nr_threads, - int thread_idx) +static void evsel__remove_fd(struct evsel *pos, int nr_cpus, int nr_threads, int thread_idx) { for (int cpu = 0; cpu < nr_cpus; cpu++) for (int thread = thread_idx; thread < nr_threads - 1; thread++) @@ -1577,18 +1867,18 @@ static void perf_evsel__remove_fd(struct evsel *pos, } static int update_fds(struct evsel *evsel, - int nr_cpus, int cpu_idx, + int nr_cpus, int cpu_map_idx, int nr_threads, int thread_idx) { struct evsel *pos; - if (cpu_idx >= nr_cpus || thread_idx >= nr_threads) + if (cpu_map_idx >= nr_cpus || thread_idx >= nr_threads) return -EINVAL; evlist__for_each_entry(evsel->evlist, pos) { - nr_cpus = pos != evsel ? nr_cpus : cpu_idx; + nr_cpus = pos != evsel ? nr_cpus : cpu_map_idx; - perf_evsel__remove_fd(pos, nr_cpus, nr_threads, thread_idx); + evsel__remove_fd(pos, nr_cpus, nr_threads, thread_idx); /* * Since fds for next evsel has not been created, @@ -1600,10 +1890,10 @@ static int update_fds(struct evsel *evsel, return 0; } -static bool ignore_missing_thread(struct evsel *evsel, - int nr_cpus, int cpu, - struct perf_thread_map *threads, - int thread, int err) +static bool evsel__ignore_missing_thread(struct evsel *evsel, + int nr_cpus, int cpu_map_idx, + struct perf_thread_map *threads, + int thread, int err) { pid_t ignore_pid = perf_thread_map__pid(threads, thread); @@ -1626,7 +1916,7 @@ static bool ignore_missing_thread(struct evsel *evsel, * We should remove fd for missing_thread first * because thread_map__remove() will decrease threads->nr. */ - if (update_fds(evsel, nr_cpus, cpu, threads->nr, thread)) + if (update_fds(evsel, nr_cpus, cpu_map_idx, threads->nr, thread)) return false; if (thread_map__remove(threads, thread)) @@ -1653,61 +1943,45 @@ static void display_attr(struct perf_event_attr *attr) } } -static int perf_event_open(struct evsel *evsel, - pid_t pid, int cpu, int group_fd, - unsigned long flags) +bool evsel__precise_ip_fallback(struct evsel *evsel) { - int precise_ip = evsel->core.attr.precise_ip; - int fd; - - while (1) { - pr_debug2_peo("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx", - pid, cpu, group_fd, flags); - - fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, group_fd, flags); - if (fd >= 0) - break; - - /* Do not try less precise if not requested. */ - if (!evsel->precise_max) - break; - - /* - * We tried all the precise_ip values, and it's - * still failing, so leave it to standard fallback. - */ - if (!evsel->core.attr.precise_ip) { - evsel->core.attr.precise_ip = precise_ip; - break; - } + /* Do not try less precise if not requested. */ + if (!evsel->precise_max) + return false; - pr_debug2_peo("\nsys_perf_event_open failed, error %d\n", -ENOTSUP); - evsel->core.attr.precise_ip--; - pr_debug2_peo("decreasing precise_ip by one (%d)\n", evsel->core.attr.precise_ip); - display_attr(&evsel->core.attr); + /* + * We tried all the precise_ip values, and it's + * still failing, so leave it to standard fallback. + */ + if (!evsel->core.attr.precise_ip) { + evsel->core.attr.precise_ip = evsel->precise_ip_original; + return false; } - return fd; + if (!evsel->precise_ip_original) + evsel->precise_ip_original = evsel->core.attr.precise_ip; + + evsel->core.attr.precise_ip--; + pr_debug2_peo("decreasing precise_ip by one (%d)\n", evsel->core.attr.precise_ip); + display_attr(&evsel->core.attr); + return true; } -static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, - struct perf_thread_map *threads, - int start_cpu, int end_cpu) +static struct perf_cpu_map *empty_cpu_map; +static struct perf_thread_map *empty_thread_map; + +static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, + struct perf_thread_map *threads) { - int cpu, thread, nthreads; - unsigned long flags = PERF_FLAG_FD_CLOEXEC; - int pid = -1, err, old_errno; - enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; + int nthreads = perf_thread_map__nr(threads); if ((perf_missing_features.write_backward && evsel->core.attr.write_backward) || (perf_missing_features.aux_output && evsel->core.attr.aux_output)) return -EINVAL; if (cpus == NULL) { - static struct perf_cpu_map *empty_cpu_map; - if (empty_cpu_map == NULL) { - empty_cpu_map = perf_cpu_map__dummy_new(); + empty_cpu_map = perf_cpu_map__new_any_cpu(); if (empty_cpu_map == NULL) return -ENOMEM; } @@ -1716,8 +1990,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, } if (threads == NULL) { - static struct perf_thread_map *empty_thread_map; - if (empty_thread_map == NULL) { empty_thread_map = thread_map__new_by_tid(-1); if (empty_thread_map == NULL) @@ -1727,21 +1999,35 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, threads = empty_thread_map; } - if (evsel->core.system_wide) - nthreads = 1; - else - nthreads = threads->nr; - if (evsel->core.fd == NULL && - perf_evsel__alloc_fd(&evsel->core, cpus->nr, nthreads) < 0) + perf_evsel__alloc_fd(&evsel->core, perf_cpu_map__nr(cpus), nthreads) < 0) return -ENOMEM; - if (evsel->cgrp) { - flags |= PERF_FLAG_PID_CGROUP; - pid = evsel->cgrp->fd; + if ((evsel->tool_event == PERF_TOOL_SYSTEM_TIME || + evsel->tool_event == PERF_TOOL_USER_TIME) && + !evsel->start_times) { + evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64)); + if (!evsel->start_times) + return -ENOMEM; } -fallback_missing_features: + evsel->open_flags = PERF_FLAG_FD_CLOEXEC; + if (evsel->cgrp) + evsel->open_flags |= PERF_FLAG_PID_CGROUP; + + return 0; +} + +static void evsel__disable_missing_features(struct evsel *evsel) +{ + if (perf_missing_features.branch_counters) + evsel->core.attr.branch_sample_type &= ~PERF_SAMPLE_BRANCH_COUNTERS; + if (perf_missing_features.read_lost) + evsel->core.attr.read_format &= ~PERF_FORMAT_LOST; + if (perf_missing_features.weight_struct) { + evsel__set_sample_bit(evsel, WEIGHT); + evsel__reset_sample_bit(evsel, WEIGHT_STRUCT); + } if (perf_missing_features.clockid_wrong) evsel->core.attr.clockid = CLOCK_MONOTONIC; /* should always work */ if (perf_missing_features.clockid) { @@ -1749,10 +2035,10 @@ fallback_missing_features: evsel->core.attr.clockid = 0; } if (perf_missing_features.cloexec) - flags &= ~(unsigned long)PERF_FLAG_FD_CLOEXEC; + evsel->open_flags &= ~(unsigned long)PERF_FLAG_FD_CLOEXEC; if (perf_missing_features.mmap2) evsel->core.attr.mmap2 = 0; - if (perf_missing_features.exclude_guest) + if (evsel->pmu && evsel->pmu->missing_features.exclude_guest) evsel->core.attr.exclude_guest = evsel->core.attr.exclude_host = 0; if (perf_missing_features.lbr_flags) evsel->core.attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS | @@ -1765,56 +2051,264 @@ fallback_missing_features: evsel->core.attr.bpf_event = 0; if (perf_missing_features.branch_hw_idx) evsel->core.attr.branch_sample_type &= ~PERF_SAMPLE_BRANCH_HW_INDEX; -retry_sample_id: if (perf_missing_features.sample_id_all) evsel->core.attr.sample_id_all = 0; +} +int evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, + struct perf_thread_map *threads) +{ + int err; + + err = __evsel__prepare_open(evsel, cpus, threads); + if (err) + return err; + + evsel__disable_missing_features(evsel); + + return err; +} + +bool evsel__detect_missing_features(struct evsel *evsel) +{ + /* + * Must probe features in the order they were added to the + * perf_event_attr interface. + */ + if (!perf_missing_features.branch_counters && + (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS)) { + perf_missing_features.branch_counters = true; + pr_debug2("switching off branch counters support\n"); + return true; + } else if (!perf_missing_features.read_lost && + (evsel->core.attr.read_format & PERF_FORMAT_LOST)) { + perf_missing_features.read_lost = true; + pr_debug2("switching off PERF_FORMAT_LOST support\n"); + return true; + } else if (!perf_missing_features.weight_struct && + (evsel->core.attr.sample_type & PERF_SAMPLE_WEIGHT_STRUCT)) { + perf_missing_features.weight_struct = true; + pr_debug2("switching off weight struct support\n"); + return true; + } else if (!perf_missing_features.code_page_size && + (evsel->core.attr.sample_type & PERF_SAMPLE_CODE_PAGE_SIZE)) { + perf_missing_features.code_page_size = true; + pr_debug2_peo("Kernel has no PERF_SAMPLE_CODE_PAGE_SIZE support, bailing out\n"); + return false; + } else if (!perf_missing_features.data_page_size && + (evsel->core.attr.sample_type & PERF_SAMPLE_DATA_PAGE_SIZE)) { + perf_missing_features.data_page_size = true; + pr_debug2_peo("Kernel has no PERF_SAMPLE_DATA_PAGE_SIZE support, bailing out\n"); + return false; + } else if (!perf_missing_features.cgroup && evsel->core.attr.cgroup) { + perf_missing_features.cgroup = true; + pr_debug2_peo("Kernel has no cgroup sampling support, bailing out\n"); + return false; + } else if (!perf_missing_features.branch_hw_idx && + (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX)) { + perf_missing_features.branch_hw_idx = true; + pr_debug2("switching off branch HW index support\n"); + return true; + } else if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) { + perf_missing_features.aux_output = true; + pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n"); + return false; + } else if (!perf_missing_features.bpf && evsel->core.attr.bpf_event) { + perf_missing_features.bpf = true; + pr_debug2_peo("switching off bpf_event\n"); + return true; + } else if (!perf_missing_features.ksymbol && evsel->core.attr.ksymbol) { + perf_missing_features.ksymbol = true; + pr_debug2_peo("switching off ksymbol\n"); + return true; + } else if (!perf_missing_features.write_backward && evsel->core.attr.write_backward) { + perf_missing_features.write_backward = true; + pr_debug2_peo("switching off write_backward\n"); + return false; + } else if (!perf_missing_features.clockid_wrong && evsel->core.attr.use_clockid) { + perf_missing_features.clockid_wrong = true; + pr_debug2_peo("switching off clockid\n"); + return true; + } else if (!perf_missing_features.clockid && evsel->core.attr.use_clockid) { + perf_missing_features.clockid = true; + pr_debug2_peo("switching off use_clockid\n"); + return true; + } else if (!perf_missing_features.cloexec && (evsel->open_flags & PERF_FLAG_FD_CLOEXEC)) { + perf_missing_features.cloexec = true; + pr_debug2_peo("switching off cloexec flag\n"); + return true; + } else if (!perf_missing_features.mmap2 && evsel->core.attr.mmap2) { + perf_missing_features.mmap2 = true; + pr_debug2_peo("switching off mmap2\n"); + return true; + } else if (evsel->core.attr.exclude_guest || evsel->core.attr.exclude_host) { + if (evsel->pmu == NULL) + evsel->pmu = evsel__find_pmu(evsel); + + if (evsel->pmu) + evsel->pmu->missing_features.exclude_guest = true; + else { + /* we cannot find PMU, disable attrs now */ + evsel->core.attr.exclude_host = false; + evsel->core.attr.exclude_guest = false; + } + + if (evsel->exclude_GH) { + pr_debug2_peo("PMU has no exclude_host/guest support, bailing out\n"); + return false; + } + if (!perf_missing_features.exclude_guest) { + perf_missing_features.exclude_guest = true; + pr_debug2_peo("switching off exclude_guest, exclude_host\n"); + } + return true; + } else if (!perf_missing_features.sample_id_all) { + perf_missing_features.sample_id_all = true; + pr_debug2_peo("switching off sample_id_all\n"); + return true; + } else if (!perf_missing_features.lbr_flags && + (evsel->core.attr.branch_sample_type & + (PERF_SAMPLE_BRANCH_NO_CYCLES | + PERF_SAMPLE_BRANCH_NO_FLAGS))) { + perf_missing_features.lbr_flags = true; + pr_debug2_peo("switching off branch sample type no (cycles/flags)\n"); + return true; + } else if (!perf_missing_features.group_read && + evsel->core.attr.inherit && + (evsel->core.attr.read_format & PERF_FORMAT_GROUP) && + evsel__is_group_leader(evsel)) { + perf_missing_features.group_read = true; + pr_debug2_peo("switching off group read\n"); + return true; + } else { + return false; + } +} + +static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, + struct perf_thread_map *threads, + int start_cpu_map_idx, int end_cpu_map_idx) +{ + int idx, thread, nthreads; + int pid = -1, err, old_errno; + enum rlimit_action set_rlimit = NO_CHANGE; + + if (evsel->tool_event == PERF_TOOL_DURATION_TIME) { + if (evsel->core.attr.sample_period) /* no sampling */ + return -EINVAL; + evsel->start_time = rdclock(); + return 0; + } + + err = __evsel__prepare_open(evsel, cpus, threads); + if (err) + return err; + + if (cpus == NULL) + cpus = empty_cpu_map; + + if (threads == NULL) + threads = empty_thread_map; + + nthreads = perf_thread_map__nr(threads); + + if (evsel->cgrp) + pid = evsel->cgrp->fd; + +fallback_missing_features: + evsel__disable_missing_features(evsel); + + pr_debug3("Opening: %s\n", evsel__name(evsel)); display_attr(&evsel->core.attr); - for (cpu = start_cpu; cpu < end_cpu; cpu++) { + for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { for (thread = 0; thread < nthreads; thread++) { int fd, group_fd; +retry_open: + if (thread >= nthreads) + break; if (!evsel->cgrp && !evsel->core.system_wide) pid = perf_thread_map__pid(threads, thread); - group_fd = get_group_fd(evsel, cpu, thread); -retry_open: - test_attr__ready(); + if (evsel->tool_event == PERF_TOOL_USER_TIME || + evsel->tool_event == PERF_TOOL_SYSTEM_TIME) { + bool system = evsel->tool_event == PERF_TOOL_SYSTEM_TIME; + __u64 *start_time = NULL; - fd = perf_event_open(evsel, pid, cpus->map[cpu], - group_fd, flags); + if (evsel->core.attr.sample_period) { + /* no sampling */ + err = -EINVAL; + goto out_close; + } + if (pid > -1) { + char buf[64]; + + snprintf(buf, sizeof(buf), "/proc/%d/stat", pid); + fd = open(buf, O_RDONLY); + evsel->pid_stat = true; + } else { + fd = open("/proc/stat", O_RDONLY); + } + FD(evsel, idx, thread) = fd; + if (fd < 0) { + err = -errno; + goto out_close; + } + start_time = xyarray__entry(evsel->start_times, idx, thread); + if (pid > -1) { + err = read_pid_stat_field(fd, system ? 15 : 14, + start_time); + } else { + struct perf_cpu cpu; + + cpu = perf_cpu_map__cpu(evsel->core.cpus, idx); + err = read_stat_field(fd, cpu, system ? 3 : 1, + start_time); + } + if (err) + goto out_close; + continue; + } - FD(evsel, cpu, thread) = fd; + group_fd = get_group_fd(evsel, idx, thread); - if (unlikely(test_attr__enabled)) { - test_attr__open(&evsel->core.attr, pid, cpus->map[cpu], - fd, group_fd, flags); + if (group_fd == -2) { + pr_debug("broken group leader for %s\n", evsel->name); + err = -EINVAL; + goto out_close; } + test_attr__ready(); + + /* Debug message used by test scripts */ + pr_debug2_peo("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx", + pid, perf_cpu_map__cpu(cpus, idx).cpu, group_fd, evsel->open_flags); + + fd = sys_perf_event_open(&evsel->core.attr, pid, + perf_cpu_map__cpu(cpus, idx).cpu, + group_fd, evsel->open_flags); + + FD(evsel, idx, thread) = fd; + if (fd < 0) { err = -errno; - if (ignore_missing_thread(evsel, cpus->nr, cpu, threads, thread, err)) { - /* - * We just removed 1 thread, so take a step - * back on thread index and lower the upper - * nthreads limit. - */ - nthreads--; - thread--; - - /* ... and pretend like nothing have happened. */ - err = 0; - continue; - } - pr_debug2_peo("\nsys_perf_event_open failed, error %d\n", err); goto try_fallback; } + bpf_counter__install_pe(evsel, idx, fd); + + if (unlikely(test_attr__enabled)) { + test_attr__open(&evsel->core.attr, pid, + perf_cpu_map__cpu(cpus, idx), + fd, group_fd, evsel->open_flags); + } + + /* Debug message used by test scripts */ pr_debug2_peo(" = %d\n", fd); if (evsel->bpf_fd >= 0) { @@ -1849,102 +2343,30 @@ retry_open: return 0; try_fallback: + if (evsel__precise_ip_fallback(evsel)) + goto retry_open; + + if (evsel__ignore_missing_thread(evsel, perf_cpu_map__nr(cpus), + idx, threads, thread, err)) { + /* We just removed 1 thread, so lower the upper nthreads limit. */ + nthreads--; + + /* ... and pretend like nothing have happened. */ + err = 0; + goto retry_open; + } /* * perf stat needs between 5 and 22 fds per CPU. When we run out * of them try to increase the limits. */ - if (err == -EMFILE && set_rlimit < INCREASED_MAX) { - struct rlimit l; - - old_errno = errno; - if (getrlimit(RLIMIT_NOFILE, &l) == 0) { - if (set_rlimit == NO_CHANGE) - l.rlim_cur = l.rlim_max; - else { - l.rlim_cur = l.rlim_max + 1000; - l.rlim_max = l.rlim_cur; - } - if (setrlimit(RLIMIT_NOFILE, &l) == 0) { - set_rlimit++; - errno = old_errno; - goto retry_open; - } - } - errno = old_errno; - } + if (err == -EMFILE && rlimit__increase_nofile(&set_rlimit)) + goto retry_open; - if (err != -EINVAL || cpu > 0 || thread > 0) + if (err != -EINVAL || idx > 0 || thread > 0) goto out_close; - /* - * Must probe features in the order they were added to the - * perf_event_attr interface. - */ - if (!perf_missing_features.cgroup && evsel->core.attr.cgroup) { - perf_missing_features.cgroup = true; - pr_debug2_peo("Kernel has no cgroup sampling support, bailing out\n"); - goto out_close; - } else if (!perf_missing_features.branch_hw_idx && - (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX)) { - perf_missing_features.branch_hw_idx = true; - pr_debug2("switching off branch HW index support\n"); - goto fallback_missing_features; - } else if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) { - perf_missing_features.aux_output = true; - pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n"); - goto out_close; - } else if (!perf_missing_features.bpf && evsel->core.attr.bpf_event) { - perf_missing_features.bpf = true; - pr_debug2_peo("switching off bpf_event\n"); - goto fallback_missing_features; - } else if (!perf_missing_features.ksymbol && evsel->core.attr.ksymbol) { - perf_missing_features.ksymbol = true; - pr_debug2_peo("switching off ksymbol\n"); - goto fallback_missing_features; - } else if (!perf_missing_features.write_backward && evsel->core.attr.write_backward) { - perf_missing_features.write_backward = true; - pr_debug2_peo("switching off write_backward\n"); - goto out_close; - } else if (!perf_missing_features.clockid_wrong && evsel->core.attr.use_clockid) { - perf_missing_features.clockid_wrong = true; - pr_debug2_peo("switching off clockid\n"); - goto fallback_missing_features; - } else if (!perf_missing_features.clockid && evsel->core.attr.use_clockid) { - perf_missing_features.clockid = true; - pr_debug2_peo("switching off use_clockid\n"); - goto fallback_missing_features; - } else if (!perf_missing_features.cloexec && (flags & PERF_FLAG_FD_CLOEXEC)) { - perf_missing_features.cloexec = true; - pr_debug2_peo("switching off cloexec flag\n"); - goto fallback_missing_features; - } else if (!perf_missing_features.mmap2 && evsel->core.attr.mmap2) { - perf_missing_features.mmap2 = true; - pr_debug2_peo("switching off mmap2\n"); - goto fallback_missing_features; - } else if (!perf_missing_features.exclude_guest && - (evsel->core.attr.exclude_guest || evsel->core.attr.exclude_host)) { - perf_missing_features.exclude_guest = true; - pr_debug2_peo("switching off exclude_guest, exclude_host\n"); - goto fallback_missing_features; - } else if (!perf_missing_features.sample_id_all) { - perf_missing_features.sample_id_all = true; - pr_debug2_peo("switching off sample_id_all\n"); - goto retry_sample_id; - } else if (!perf_missing_features.lbr_flags && - (evsel->core.attr.branch_sample_type & - (PERF_SAMPLE_BRANCH_NO_CYCLES | - PERF_SAMPLE_BRANCH_NO_FLAGS))) { - perf_missing_features.lbr_flags = true; - pr_debug2_peo("switching off branch sample type no (cycles/flags)\n"); + if (evsel__detect_missing_features(evsel)) goto fallback_missing_features; - } else if (!perf_missing_features.group_read && - evsel->core.attr.inherit && - (evsel->core.attr.read_format & PERF_FORMAT_GROUP) && - evsel__is_group_leader(evsel)) { - perf_missing_features.group_read = true; - pr_debug2_peo("switching off group read\n"); - goto fallback_missing_features; - } out_close: if (err) threads->err_thread = thread; @@ -1952,12 +2374,12 @@ out_close: old_errno = errno; do { while (--thread >= 0) { - if (FD(evsel, cpu, thread) >= 0) - close(FD(evsel, cpu, thread)); - FD(evsel, cpu, thread) = -1; + if (FD(evsel, idx, thread) >= 0) + close(FD(evsel, idx, thread)); + FD(evsel, idx, thread) = -1; } thread = nthreads; - } while (--cpu >= 0); + } while (--idx >= 0); errno = old_errno; return err; } @@ -1965,7 +2387,7 @@ out_close: int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, struct perf_thread_map *threads) { - return evsel__open_cpu(evsel, cpus, threads, 0, cpus ? cpus->nr : 1); + return evsel__open_cpu(evsel, cpus, threads, 0, perf_cpu_map__nr(cpus)); } void evsel__close(struct evsel *evsel) @@ -1974,13 +2396,12 @@ void evsel__close(struct evsel *evsel) perf_evsel__free_id(&evsel->core); } -int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu) +int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu_map_idx) { - if (cpu == -1) - return evsel__open_cpu(evsel, cpus, NULL, 0, - cpus ? cpus->nr : 1); + if (cpu_map_idx == -1) + return evsel__open_cpu(evsel, cpus, NULL, 0, perf_cpu_map__nr(cpus)); - return evsel__open_cpu(evsel, cpus, NULL, cpu, cpu + 1); + return evsel__open_cpu(evsel, cpus, NULL, cpu_map_idx, cpu_map_idx + 1); } int evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads) @@ -2078,6 +2499,83 @@ perf_event__check_size(union perf_event *event, unsigned int sample_size) return 0; } +void __weak arch_perf_parse_sample_weight(struct perf_sample *data, + const __u64 *array, + u64 type __maybe_unused) +{ + data->weight = *array; +} + +u64 evsel__bitfield_swap_branch_flags(u64 value) +{ + u64 new_val = 0; + + /* + * branch_flags + * union { + * u64 values; + * struct { + * mispred:1 //target mispredicted + * predicted:1 //target predicted + * in_tx:1 //in transaction + * abort:1 //transaction abort + * cycles:16 //cycle count to last branch + * type:4 //branch type + * spec:2 //branch speculation info + * new_type:4 //additional branch type + * priv:3 //privilege level + * reserved:31 + * } + * } + * + * Avoid bswap64() the entire branch_flag.value, + * as it has variable bit-field sizes. Instead the + * macro takes the bit-field position/size, + * swaps it based on the host endianness. + */ + if (host_is_bigendian()) { + new_val = bitfield_swap(value, 0, 1); + new_val |= bitfield_swap(value, 1, 1); + new_val |= bitfield_swap(value, 2, 1); + new_val |= bitfield_swap(value, 3, 1); + new_val |= bitfield_swap(value, 4, 16); + new_val |= bitfield_swap(value, 20, 4); + new_val |= bitfield_swap(value, 24, 2); + new_val |= bitfield_swap(value, 26, 4); + new_val |= bitfield_swap(value, 30, 3); + new_val |= bitfield_swap(value, 33, 31); + } else { + new_val = bitfield_swap(value, 63, 1); + new_val |= bitfield_swap(value, 62, 1); + new_val |= bitfield_swap(value, 61, 1); + new_val |= bitfield_swap(value, 60, 1); + new_val |= bitfield_swap(value, 44, 16); + new_val |= bitfield_swap(value, 40, 4); + new_val |= bitfield_swap(value, 38, 2); + new_val |= bitfield_swap(value, 34, 4); + new_val |= bitfield_swap(value, 31, 3); + new_val |= bitfield_swap(value, 0, 31); + } + + return new_val; +} + +static inline bool evsel__has_branch_counters(const struct evsel *evsel) +{ + struct evsel *cur, *leader = evsel__leader(evsel); + + /* The branch counters feature only supports group */ + if (!leader || !evsel->evlist) + return false; + + evlist__for_each_entry(evsel->evlist, cur) { + if ((leader == evsel__leader(cur)) && + (cur->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS)) + return true; + } + return false; +} + int evsel__parse_sample(struct evsel *evsel, union perf_event *event, struct perf_sample *data) { @@ -2100,8 +2598,8 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, data->period = evsel->core.attr.sample_period; data->cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; data->misc = event->header.misc; - data->id = -1ULL; data->data_src = PERF_MEM_DATA_SRC_NONE; + data->vcpu = -1; if (event->header.type != PERF_RECORD_SAMPLE) { if (!evsel->core.attr.sample_id_all) @@ -2206,8 +2704,8 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, if (data->read.group.nr > max_group_nr) return -EFAULT; - sz = data->read.group.nr * - sizeof(struct sample_read_value); + + sz = data->read.group.nr * sample_read_value_size(read_format); OVERFLOW_CHECK(array, sz, max_size); data->read.group.values = (struct sample_read_value *)array; @@ -2216,6 +2714,12 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, OVERFLOW_CHECK_u64(array); data->read.one.id = *array; array++; + + if (read_format & PERF_FORMAT_LOST) { + OVERFLOW_CHECK_u64(array); + data->read.one.lost = *array; + array++; + } } } @@ -2238,7 +2742,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, /* * Undo swap of u64, then swap on individual u32s, * get the size of the raw area and undo all of the - * swap. The pevent interface handles endianity by + * swap. The pevent interface handles endianness by * itself. */ if (swapped) { @@ -2265,6 +2769,8 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, if (type & PERF_SAMPLE_BRANCH_STACK) { const u64 max_branch_nr = UINT64_MAX / sizeof(struct branch_entry); + struct branch_entry *e; + unsigned int i; OVERFLOW_CHECK_u64(array); data->branch_stack = (struct branch_stack *)array++; @@ -2273,12 +2779,45 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, return -EFAULT; sz = data->branch_stack->nr * sizeof(struct branch_entry); - if (evsel__has_branch_hw_idx(evsel)) + if (evsel__has_branch_hw_idx(evsel)) { sz += sizeof(u64); - else + e = &data->branch_stack->entries[0]; + } else { data->no_hw_idx = true; + /* + * if the PERF_SAMPLE_BRANCH_HW_INDEX is not applied, + * only nr and entries[] will be output by kernel. + */ + e = (struct branch_entry *)&data->branch_stack->hw_idx; + } + + if (swapped) { + /* + * struct branch_flag does not have endian + * specific bit field definition. And bswap + * will not resolve the issue, since these + * are bit fields. + * + * evsel__bitfield_swap_branch_flags() uses a + * bitfield_swap macro to swap the bit position + * based on the host endians. + */ + for (i = 0; i < data->branch_stack->nr; i++, e++) + e->flags.value = evsel__bitfield_swap_branch_flags(e->flags.value); + } + OVERFLOW_CHECK(array, sz, max_size); array = (void *)array + sz; + + if (evsel__has_branch_counters(evsel)) { + OVERFLOW_CHECK_u64(array); + + data->branch_stack_cntr = (u64 *)array; + sz = data->branch_stack->nr * sizeof(u64); + + OVERFLOW_CHECK(array, sz, max_size); + array = (void *)array + sz; + } } if (type & PERF_SAMPLE_REGS_USER) { @@ -2318,9 +2857,9 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, } } - if (type & PERF_SAMPLE_WEIGHT) { + if (type & PERF_SAMPLE_WEIGHT_TYPE) { OVERFLOW_CHECK_u64(array); - data->weight = *array; + arch_perf_parse_sample_weight(data, array, type); array++; } @@ -2365,6 +2904,18 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, array++; } + data->data_page_size = 0; + if (type & PERF_SAMPLE_DATA_PAGE_SIZE) { + data->data_page_size = *array; + array++; + } + + data->code_page_size = 0; + if (type & PERF_SAMPLE_CODE_PAGE_SIZE) { + data->code_page_size = *array; + array++; + } + if (type & PERF_SAMPLE_AUX) { OVERFLOW_CHECK_u64(array); sz = *array++; @@ -2424,11 +2975,43 @@ int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event, return 0; } +u16 evsel__id_hdr_size(struct evsel *evsel) +{ + u64 sample_type = evsel->core.attr.sample_type; + u16 size = 0; + + if (sample_type & PERF_SAMPLE_TID) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_TIME) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_ID) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_STREAM_ID) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_CPU) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_IDENTIFIER) + size += sizeof(u64); + + return size; +} + +#ifdef HAVE_LIBTRACEEVENT struct tep_format_field *evsel__field(struct evsel *evsel, const char *name) { return tep_find_field(evsel->tp_format, name); } +struct tep_format_field *evsel__common_field(struct evsel *evsel, const char *name) +{ + return tep_find_common_field(evsel->tp_format, name); +} + void *evsel__rawptr(struct evsel *evsel, struct perf_sample *sample, const char *name) { struct tep_format_field *field = evsel__field(evsel, name); @@ -2442,6 +3025,8 @@ void *evsel__rawptr(struct evsel *evsel, struct perf_sample *sample, const char if (field->flags & TEP_FIELD_IS_DYNAMIC) { offset = *(int *)(sample->raw_data + field->offset); offset &= 0xffff; + if (tep_field_is_relative(field->flags)) + offset += field->offset + field->size; } return sample->raw_data + offset; @@ -2490,13 +3075,53 @@ u64 evsel__intval(struct evsel *evsel, struct perf_sample *sample, const char *n { struct tep_format_field *field = evsel__field(evsel, name); - if (!field) - return 0; + return field ? format_field__intval(field, sample, evsel->needs_swap) : 0; +} + +u64 evsel__intval_common(struct evsel *evsel, struct perf_sample *sample, const char *name) +{ + struct tep_format_field *field = evsel__common_field(evsel, name); return field ? format_field__intval(field, sample, evsel->needs_swap) : 0; } -bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize) +char evsel__taskstate(struct evsel *evsel, struct perf_sample *sample, const char *name) +{ + static struct tep_format_field *prev_state_field; + static const char *states; + struct tep_format_field *field; + unsigned long long val; + unsigned int bit; + char state = '?'; /* '?' denotes unknown task state */ + + field = evsel__field(evsel, name); + + if (!field) + return state; + + if (!states || field != prev_state_field) { + states = parse_task_states(field); + if (!states) + return state; + prev_state_field = field; + } + + /* + * Note since the kernel exposes TASK_REPORT_MAX to userspace + * to denote the 'preempted' state, we might as welll report + * 'R' for this case, which make senses to users as well. + * + * We can change this if we have a good reason in the future. + */ + val = evsel__intval(evsel, sample, name); + bit = val ? ffs(val) : 0; + state = (!bit || bit > strlen(states)) ? 'R' : states[bit-1]; + return state; +} +#endif + +bool evsel__fallback(struct evsel *evsel, struct target *target, int err, + char *msg, size_t msgsize) { int paranoid; @@ -2504,18 +3129,19 @@ bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize) evsel->core.attr.type == PERF_TYPE_HARDWARE && evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES) { /* - * If it's cycles then fall back to hrtimer based - * cpu-clock-tick sw counter, which is always available even if - * no PMU support. + * If it's cycles then fall back to hrtimer based cpu-clock sw + * counter, which is always available even if no PMU support. * * PPC returns ENXIO until 2.6.37 (behavior changed with commit * b0a873e). */ - scnprintf(msg, msgsize, "%s", -"The cycles event is not supported, trying to fall back to cpu-clock-ticks"); - evsel->core.attr.type = PERF_TYPE_SOFTWARE; - evsel->core.attr.config = PERF_COUNT_SW_CPU_CLOCK; + evsel->core.attr.config = target__has_cpu(target) + ? PERF_COUNT_SW_CPU_CLOCK + : PERF_COUNT_SW_TASK_CLOCK; + scnprintf(msg, msgsize, + "The cycles event is not supported, trying to fall back to %s", + target__has_cpu(target) ? "cpu-clock" : "task-clock"); zfree(&evsel->name); return true; @@ -2537,8 +3163,7 @@ bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize) if (asprintf(&new_name, "%s%su", name, sep) < 0) return false; - if (evsel->name) - free(evsel->name); + free(evsel->name); evsel->name = new_name; scnprintf(msg, msgsize, "kernel.perf_event_paranoid=%d, trying " "to fall back to excluding kernel and hypervisor " @@ -2588,11 +3213,19 @@ static bool find_process(const char *name) return ret ? false : true; } +int __weak arch_evsel__open_strerror(struct evsel *evsel __maybe_unused, + char *msg __maybe_unused, + size_t size __maybe_unused) +{ + return 0; +} + int evsel__open_strerror(struct evsel *evsel, struct target *target, int err, char *msg, size_t size) { char sbuf[STRERR_BUFSIZE]; int printed = 0, enforced = 0; + int ret; switch (err) { case EPERM: @@ -2650,6 +3283,10 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, "No such device - did you specify an out-of-range profile CPU?"); break; case EOPNOTSUPP: + if (evsel->core.attr.sample_type & PERF_SAMPLE_BRANCH_STACK) + return scnprintf(msg, size, + "%s: PMU Hardware or event type doesn't support branch stack sampling.", + evsel__name(evsel)); if (evsel->core.attr.aux_output) return scnprintf(msg, size, "%s: PMU Hardware doesn't support 'aux_output' feature", @@ -2674,6 +3311,10 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, "We found oprofile daemon running, please stop it and try again."); break; case EINVAL: + if (evsel->core.attr.sample_type & PERF_SAMPLE_CODE_PAGE_SIZE && perf_missing_features.code_page_size) + return scnprintf(msg, size, "Asking for the code page size isn't supported by this kernel."); + if (evsel->core.attr.sample_type & PERF_SAMPLE_DATA_PAGE_SIZE && perf_missing_features.data_page_size) + return scnprintf(msg, size, "Asking for the data page size isn't supported by this kernel."); if (evsel->core.attr.write_backward && perf_missing_features.write_backward) return scnprintf(msg, size, "Reading from overwrite event is not supported by this kernel."); if (perf_missing_features.clockid) @@ -2682,11 +3323,23 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, return scnprintf(msg, size, "wrong clockid (%d).", clockid); if (perf_missing_features.aux_output) return scnprintf(msg, size, "The 'aux_output' feature is not supported, update the kernel."); + if (!target__has_cpu(target)) + return scnprintf(msg, size, + "Invalid event (%s) in per-thread mode, enable system wide with '-a'.", + evsel__name(evsel)); + break; + case ENODATA: + return scnprintf(msg, size, "Cannot collect data source with the load latency event alone. " + "Please add an auxiliary event in front of the load latency event."); default: break; } + ret = arch_evsel__open_strerror(evsel, msg, size); + if (ret) + return ret; + return scnprintf(msg, size, "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" "/bin/dmesg | grep -i perf may provide additional information.\n", @@ -2695,22 +3348,22 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, struct perf_env *evsel__env(struct evsel *evsel) { - if (evsel && evsel->evlist) + if (evsel && evsel->evlist && evsel->evlist->env) return evsel->evlist->env; return &perf_env; } static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist) { - int cpu, thread; + int cpu_map_idx, thread; - for (cpu = 0; cpu < xyarray__max_x(evsel->core.fd); cpu++) { + for (cpu_map_idx = 0; cpu_map_idx < xyarray__max_x(evsel->core.fd); cpu_map_idx++) { for (thread = 0; thread < xyarray__max_y(evsel->core.fd); thread++) { - int fd = FD(evsel, cpu, thread); + int fd = FD(evsel, cpu_map_idx, thread); if (perf_evlist__id_add_fd(&evlist->core, &evsel->core, - cpu, thread, fd) < 0) + cpu_map_idx, thread, fd) < 0) return -1; } } @@ -2723,8 +3376,85 @@ int evsel__store_ids(struct evsel *evsel, struct evlist *evlist) struct perf_cpu_map *cpus = evsel->core.cpus; struct perf_thread_map *threads = evsel->core.threads; - if (perf_evsel__alloc_id(&evsel->core, cpus->nr, threads->nr)) + if (perf_evsel__alloc_id(&evsel->core, perf_cpu_map__nr(cpus), threads->nr)) return -ENOMEM; return store_evsel_ids(evsel, evlist); } + +void evsel__zero_per_pkg(struct evsel *evsel) +{ + struct hashmap_entry *cur; + size_t bkt; + + if (evsel->per_pkg_mask) { + hashmap__for_each_entry(evsel->per_pkg_mask, cur, bkt) + zfree(&cur->pkey); + + hashmap__clear(evsel->per_pkg_mask); + } +} + +/** + * evsel__is_hybrid - does the evsel have a known PMU that is hybrid. Note, this + * will be false on hybrid systems for hardware and legacy + * cache events. + */ +bool evsel__is_hybrid(const struct evsel *evsel) +{ + if (perf_pmus__num_core_pmus() == 1) + return false; + + return evsel->core.is_pmu_core; +} + +struct evsel *evsel__leader(const struct evsel *evsel) +{ + return container_of(evsel->core.leader, struct evsel, core); +} + +bool evsel__has_leader(struct evsel *evsel, struct evsel *leader) +{ + return evsel->core.leader == &leader->core; +} + +bool evsel__is_leader(struct evsel *evsel) +{ + return evsel__has_leader(evsel, evsel); +} + +void evsel__set_leader(struct evsel *evsel, struct evsel *leader) +{ + evsel->core.leader = &leader->core; +} + +int evsel__source_count(const struct evsel *evsel) +{ + struct evsel *pos; + int count = 0; + + evlist__for_each_entry(evsel->evlist, pos) { + if (pos->metric_leader == evsel) + count++; + } + return count; +} + +bool __weak arch_evsel__must_be_in_group(const struct evsel *evsel __maybe_unused) +{ + return false; +} + +/* + * Remove an event from a given group (leader). + * Some events, e.g., perf metrics Topdown events, + * must always be grouped. Ignore the events. + */ +void evsel__remove_from_group(struct evsel *evsel, struct evsel *leader) +{ + if (!arch_evsel__must_be_in_group(evsel) && evsel != leader) { + evsel__set_leader(evsel, evsel); + evsel->core.nr_members = 0; + leader->core.nr_members--; + } +} |