summaryrefslogtreecommitdiff
path: root/tools/perf/util/evsel.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/evsel.c')
-rw-r--r--tools/perf/util/evsel.c862
1 files changed, 693 insertions, 169 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index bc144388f892..2ee87fd84d3e 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -34,6 +34,7 @@
#include "callchain.h"
#include "cgroup.h"
#include "counts.h"
+#include "dwarf-regs.h"
#include "event.h"
#include "evsel.h"
#include "time-utils.h"
@@ -48,6 +49,7 @@
#include "record.h"
#include "debug.h"
#include "trace-event.h"
+#include "session.h"
#include "stat.h"
#include "string2.h"
#include "memswap.h"
@@ -56,8 +58,10 @@
#include "off_cpu.h"
#include "pmu.h"
#include "pmus.h"
+#include "drm_pmu.h"
#include "hwmon_pmu.h"
#include "tool_pmu.h"
+#include "tp_pmu.h"
#include "rlimit.h"
#include "../perf-sys.h"
#include "util/parse-branch-options.h"
@@ -237,6 +241,16 @@ set_methods:
return 0;
}
+const char *evsel__pmu_name(const struct evsel *evsel)
+{
+ struct perf_pmu *pmu = evsel__find_pmu(evsel);
+
+ if (pmu)
+ return pmu->name;
+
+ return event_type(evsel->core.attr.type);
+}
+
#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
int __evsel__sample_size(u64 sample_type)
@@ -389,11 +403,11 @@ void evsel__init(struct evsel *evsel,
evsel->sample_size = __evsel__sample_size(attr->sample_type);
evsel__calc_id_pos(evsel);
evsel->cmdline_group_boundary = false;
- evsel->metric_events = NULL;
evsel->per_pkg_mask = NULL;
evsel->collect_stat = false;
evsel->group_pmu_name = NULL;
evsel->skippable = false;
+ evsel->supported = true;
evsel->alternate_hw_config = PERF_COUNT_HW_MAX;
evsel->script_output_type = -1; // FIXME: OUTPUT_TYPE_UNSET, see builtin-script.c
}
@@ -477,7 +491,7 @@ struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig)
return NULL;
evsel->core.cpus = perf_cpu_map__get(orig->core.cpus);
- evsel->core.own_cpus = perf_cpu_map__get(orig->core.own_cpus);
+ evsel->core.pmu_cpus = perf_cpu_map__get(orig->core.pmu_cpus);
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;
@@ -511,10 +525,21 @@ struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig)
}
evsel->cgrp = cgroup__get(orig->cgrp);
#ifdef HAVE_LIBTRACEEVENT
+ if (orig->tp_sys) {
+ evsel->tp_sys = strdup(orig->tp_sys);
+ if (evsel->tp_sys == NULL)
+ goto out_err;
+ }
+ if (orig->tp_name) {
+ evsel->tp_name = strdup(orig->tp_name);
+ if (evsel->tp_name == NULL)
+ goto out_err;
+ }
evsel->tp_format = orig->tp_format;
#endif
evsel->handler = orig->handler;
evsel->core.leader = orig->core.leader;
+ evsel->metric_leader = orig->metric_leader;
evsel->max_events = orig->max_events;
zfree(&evsel->unit);
@@ -532,11 +557,11 @@ struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig)
evsel->exclude_GH = orig->exclude_GH;
evsel->sample_read = orig->sample_read;
- 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;
+ evsel->first_wildcard_match = orig->first_wildcard_match;
if (evsel__copy_config_terms(evsel, orig) < 0)
goto out_err;
@@ -550,24 +575,6 @@ out_err:
return NULL;
}
-static int trace_event__id(const char *sys, const char *name)
-{
- char *tp_dir = get_events_file(sys);
- char path[PATH_MAX];
- int id, err;
-
- if (!tp_dir)
- return -1;
-
- scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name);
- put_events_file(tp_dir);
- err = filename__read_int(path, &id);
- if (err)
- return err;
-
- return id;
-}
-
/*
* Returns pointer with encoded error via <linux/err.h> interface.
*/
@@ -601,7 +608,7 @@ struct evsel *evsel__newtp_idx(const char *sys, const char *name, int idx, bool
event_attr_init(&attr);
if (format) {
- id = trace_event__id(sys, name);
+ id = tp_pmu__id(sys, name);
if (id < 0) {
err = id;
goto out_free;
@@ -634,12 +641,17 @@ struct tep_event *evsel__tp_format(struct evsel *evsel)
if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
return NULL;
- tp_format = trace_event__tp_format(evsel->tp_sys, evsel->tp_name);
+ if (!evsel->tp_sys)
+ tp_format = trace_event__tp_format_id(evsel->core.attr.config);
+ else
+ tp_format = trace_event__tp_format(evsel->tp_sys, evsel->tp_name);
+
if (IS_ERR(tp_format)) {
int err = -PTR_ERR(evsel->tp_format);
- pr_err("Error getting tracepoint format '%s' '%s'(%d)\n",
- evsel__name(evsel), strerror(err), err);
+ errno = err;
+ pr_err("Error getting tracepoint format '%s': %m\n",
+ evsel__name(evsel));
return NULL;
}
evsel->tp_format = tp_format;
@@ -914,7 +926,8 @@ const char *evsel__name(struct evsel *evsel)
break;
case PERF_TYPE_TRACEPOINT:
- scnprintf(bf, sizeof(bf), "%s", "unknown tracepoint");
+ scnprintf(bf, sizeof(bf), "unknown tracepoint id=%#"PRIx64,
+ evsel->core.attr.config);
break;
case PERF_TYPE_BREAKPOINT:
@@ -926,8 +939,8 @@ const char *evsel__name(struct evsel *evsel)
break;
default:
- scnprintf(bf, sizeof(bf), "unknown attr type: %d",
- evsel->core.attr.type);
+ scnprintf(bf, sizeof(bf), "unknown event PMU=%d config=%#"PRIx64,
+ evsel->core.attr.type, evsel->core.attr.config);
break;
}
@@ -996,12 +1009,24 @@ int evsel__group_desc(struct evsel *evsel, char *buf, size_t size)
return ret;
}
-static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *opts,
- struct callchain_param *param)
+uint16_t evsel__e_machine(struct evsel *evsel, uint32_t *e_flags)
+{
+ struct perf_session *session = evsel__session(evsel);
+
+ return perf_session__e_machine(session, e_flags);
+}
+
+static void __evsel__config_callchain(struct evsel *evsel, const struct record_opts *opts,
+ const struct callchain_param *param)
{
bool function = evsel__is_function_event(evsel);
struct perf_event_attr *attr = &evsel->core.attr;
+ if (EM_HOST == EM_S390 && param->record_mode == CALLCHAIN_FP) {
+ pr_warning_once(
+ "Framepointer unwinding lacks kernel support. Use '--call-graph dwarf'\n");
+ }
+
evsel__set_sample_bit(evsel, CALLCHAIN);
attr->sample_max_stack = param->max_stack;
@@ -1031,18 +1056,18 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
if (param->record_mode == CALLCHAIN_DWARF) {
if (!function) {
- const char *arch = perf_env__arch(evsel__env(evsel));
+ uint16_t e_machine = evsel__e_machine(evsel, /*e_flags=*/NULL);
evsel__set_sample_bit(evsel, REGS_USER);
evsel__set_sample_bit(evsel, STACK_USER);
if (opts->sample_user_regs &&
- DWARF_MINIMAL_REGS(arch) != arch__user_reg_mask()) {
- attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch);
+ DWARF_MINIMAL_REGS(e_machine) != perf_user_reg_mask(EM_HOST)) {
+ attr->sample_regs_user |= DWARF_MINIMAL_REGS(e_machine);
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 |= arch__user_reg_mask();
+ attr->sample_regs_user |= perf_user_reg_mask(EM_HOST);
}
attr->sample_stack_user = param->dump_size;
attr->exclude_callchain_user = 1;
@@ -1056,16 +1081,19 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
pr_info("Disabling user space callchains for function trace event.\n");
attr->exclude_callchain_user = 1;
}
+
+ if (param->defer && !attr->exclude_callchain_user)
+ attr->defer_callchain = 1;
}
-void evsel__config_callchain(struct evsel *evsel, struct record_opts *opts,
- struct callchain_param *param)
+void evsel__config_callchain(struct evsel *evsel, const struct record_opts *opts,
+ const struct callchain_param *param)
{
if (param->enabled)
return __evsel__config_callchain(evsel, opts, param);
}
-static void evsel__reset_callgraph(struct evsel *evsel, struct callchain_param *param)
+static void evsel__reset_callgraph(struct evsel *evsel, const struct callchain_param *param)
{
struct perf_event_attr *attr = &evsel->core.attr;
@@ -1082,8 +1110,73 @@ static void evsel__reset_callgraph(struct evsel *evsel, struct callchain_param *
}
}
+static void evsel__apply_ratio_to_prev(struct evsel *evsel,
+ struct perf_event_attr *attr,
+ const struct record_opts *opts,
+ const char *buf)
+{
+ struct perf_event_attr *prev_attr = NULL;
+ struct evsel *evsel_prev = NULL;
+ u64 type = evsel->core.attr.sample_type;
+ u64 prev_type = 0;
+ double rtp;
+
+ rtp = strtod(buf, NULL);
+ if (rtp <= 0) {
+ pr_err("Invalid ratio-to-prev value %lf\n", rtp);
+ return;
+ }
+ if (evsel == evsel__leader(evsel)) {
+ pr_err("Invalid use of ratio-to-prev term without preceding element in group\n");
+ return;
+ }
+ if (!evsel->pmu->is_core) {
+ pr_err("Event using ratio-to-prev term must have a core PMU\n");
+ return;
+ }
+
+ evsel_prev = evsel__prev(evsel);
+ if (!evsel_prev) {
+ pr_err("Previous event does not exist.\n");
+ return;
+ }
+
+ if (evsel_prev->pmu->type != evsel->pmu->type) {
+ pr_err("Compared events (\"%s\", \"%s\") must have same PMU\n",
+ evsel->name, evsel_prev->name);
+ return;
+ }
+
+ prev_attr = &evsel_prev->core.attr;
+ prev_type = evsel_prev->core.attr.sample_type;
+
+ if (!(prev_type & PERF_SAMPLE_PERIOD)) {
+ attr->sample_period = prev_attr->sample_period * rtp;
+ attr->freq = 0;
+ evsel__reset_sample_bit(evsel, PERIOD);
+ } else if (!(type & PERF_SAMPLE_PERIOD)) {
+ prev_attr->sample_period = attr->sample_period / rtp;
+ prev_attr->freq = 0;
+ evsel__reset_sample_bit(evsel_prev, PERIOD);
+ } else {
+ if (opts->user_interval != ULLONG_MAX) {
+ prev_attr->sample_period = opts->user_interval;
+ attr->sample_period = prev_attr->sample_period * rtp;
+ prev_attr->freq = 0;
+ attr->freq = 0;
+ evsel__reset_sample_bit(evsel_prev, PERIOD);
+ evsel__reset_sample_bit(evsel, PERIOD);
+ } else {
+ pr_err("Event period term or count (-c) must be set when using ratio-to-prev term.\n");
+ return;
+ }
+ }
+
+ arch_evsel__apply_ratio_to_prev(evsel, attr);
+}
+
static void evsel__apply_config_terms(struct evsel *evsel,
- struct record_opts *opts, bool track)
+ const struct record_opts *opts, bool track)
{
struct evsel_config_term *term;
struct list_head *config_terms = &evsel->config_terms;
@@ -1095,6 +1188,7 @@ static void evsel__apply_config_terms(struct evsel *evsel,
u32 dump_size = 0;
int max_stack = 0;
const char *callgraph_buf = NULL;
+ const char *rtp_buf = NULL;
list_for_each_entry(term, config_terms, list) {
switch (term->type) {
@@ -1163,7 +1257,14 @@ static void evsel__apply_config_terms(struct evsel *evsel,
case EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE:
/* Already applied by auxtrace */
break;
- case EVSEL__CONFIG_TERM_CFG_CHG:
+ case EVSEL__CONFIG_TERM_USR_CHG_CONFIG:
+ case EVSEL__CONFIG_TERM_USR_CHG_CONFIG1:
+ case EVSEL__CONFIG_TERM_USR_CHG_CONFIG2:
+ case EVSEL__CONFIG_TERM_USR_CHG_CONFIG3:
+ case EVSEL__CONFIG_TERM_USR_CHG_CONFIG4:
+ break;
+ case EVSEL__CONFIG_TERM_RATIO_TO_PREV:
+ rtp_buf = term->val.str;
break;
default:
break;
@@ -1216,6 +1317,8 @@ static void evsel__apply_config_terms(struct evsel *evsel,
evsel__config_callchain(evsel, opts, &param);
}
}
+ if (rtp_buf)
+ evsel__apply_ratio_to_prev(evsel, attr, opts, rtp_buf);
}
struct evsel_config_term *__evsel__get_config_term(struct evsel *evsel, enum evsel_term_type type)
@@ -1230,6 +1333,109 @@ struct evsel_config_term *__evsel__get_config_term(struct evsel *evsel, enum evs
return found_term;
}
+/*
+ * Set @config_name to @val as long as the user hasn't already set or cleared it
+ * by passing a config term on the command line.
+ *
+ * @val is the value to put into the bits specified by @config_name rather than
+ * the bit pattern. It is shifted into position by this function, so to set
+ * something to true, pass 1 for val rather than a pre shifted value.
+ */
+void evsel__set_config_if_unset(struct evsel *evsel, const char *config_name,
+ u64 val)
+{
+ u64 user_bits = 0;
+ struct evsel_config_term *term = evsel__get_config_term(evsel,
+ USR_CHG_CONFIG);
+ struct perf_pmu_format *format = pmu_find_format(&evsel->pmu->format,
+ config_name);
+ int fbit;
+ __u64 *vp;
+
+ if (!format)
+ return;
+
+ switch (format->value) {
+ case PERF_PMU_FORMAT_VALUE_CONFIG:
+ term = evsel__get_config_term(evsel, USR_CHG_CONFIG);
+ vp = &evsel->core.attr.config;
+ break;
+ case PERF_PMU_FORMAT_VALUE_CONFIG1:
+ term = evsel__get_config_term(evsel, USR_CHG_CONFIG1);
+ vp = &evsel->core.attr.config1;
+ break;
+ case PERF_PMU_FORMAT_VALUE_CONFIG2:
+ term = evsel__get_config_term(evsel, USR_CHG_CONFIG2);
+ vp = &evsel->core.attr.config2;
+ break;
+ case PERF_PMU_FORMAT_VALUE_CONFIG3:
+ term = evsel__get_config_term(evsel, USR_CHG_CONFIG3);
+ vp = &evsel->core.attr.config3;
+ break;
+ case PERF_PMU_FORMAT_VALUE_CONFIG4:
+ term = evsel__get_config_term(evsel, USR_CHG_CONFIG4);
+ vp = &evsel->core.attr.config4;
+ break;
+ default:
+ pr_err("Unknown format value: %d\n", format->value);
+ return;
+ }
+
+ if (!format)
+ return;
+
+ if (term)
+ user_bits = term->val.cfg_chg;
+
+ /* Do nothing if the user changed the value */
+ for_each_set_bit(fbit, format->bits, PERF_PMU_FORMAT_BITS)
+ if ((1ULL << fbit) & user_bits)
+ return;
+
+ /* Otherwise replace it */
+ perf_pmu__format_pack(format->bits, val, vp, /*zero=*/true);
+}
+
+
+int evsel__get_config_val(const struct evsel *evsel, const char *config_name,
+ u64 *val)
+{
+ struct perf_pmu_format *format = pmu_find_format(&evsel->pmu->format, config_name);
+
+ if (!format || bitmap_empty(format->bits, PERF_PMU_FORMAT_BITS)) {
+ pr_err("Unknown/empty format name: %s\n", config_name);
+ *val = 0;
+ return -EINVAL;
+ }
+
+ switch (format->value) {
+ case PERF_PMU_FORMAT_VALUE_CONFIG:
+ *val = perf_pmu__format_unpack(format->bits,
+ evsel->core.attr.config);
+ return 0;
+ case PERF_PMU_FORMAT_VALUE_CONFIG1:
+ *val = perf_pmu__format_unpack(format->bits,
+ evsel->core.attr.config1);
+ return 0;
+ case PERF_PMU_FORMAT_VALUE_CONFIG2:
+ *val = perf_pmu__format_unpack(format->bits,
+ evsel->core.attr.config2);
+ return 0;
+ case PERF_PMU_FORMAT_VALUE_CONFIG3:
+ *val = perf_pmu__format_unpack(format->bits,
+ evsel->core.attr.config3);
+ return 0;
+ case PERF_PMU_FORMAT_VALUE_CONFIG4:
+ *val = perf_pmu__format_unpack(format->bits,
+ evsel->core.attr.config4);
+ return 0;
+ default:
+ pr_err("Unknown format value: %d\n", format->value);
+ *val = 0;
+ return -EINVAL;
+ }
+}
+
void __weak arch_evsel__set_sample_weight(struct evsel *evsel)
{
evsel__set_sample_bit(evsel, WEIGHT);
@@ -1240,7 +1446,12 @@ void __weak arch__post_evsel_config(struct evsel *evsel __maybe_unused,
{
}
-static void evsel__set_default_freq_period(struct record_opts *opts,
+void __weak arch_evsel__apply_ratio_to_prev(struct evsel *evsel __maybe_unused,
+ struct perf_event_attr *attr __maybe_unused)
+{
+}
+
+static void evsel__set_default_freq_period(const struct record_opts *opts,
struct perf_event_attr *attr)
{
if (opts->freq) {
@@ -1251,9 +1462,10 @@ static void evsel__set_default_freq_period(struct record_opts *opts,
}
}
-static bool evsel__is_offcpu_event(struct evsel *evsel)
+bool evsel__is_offcpu_event(struct evsel *evsel)
{
- return evsel__is_bpf_output(evsel) && evsel__name_is(evsel, OFFCPU_EVENT);
+ return evsel__is_bpf_output(evsel) && evsel__name_is(evsel, OFFCPU_EVENT) &&
+ evsel->core.attr.sample_type & PERF_SAMPLE_RAW;
}
/*
@@ -1284,8 +1496,8 @@ static bool evsel__is_offcpu_event(struct evsel *evsel)
* enable/disable events specifically, as there's no
* initial traced exec call.
*/
-void evsel__config(struct evsel *evsel, struct record_opts *opts,
- struct callchain_param *callchain)
+void evsel__config(struct evsel *evsel, const struct record_opts *opts,
+ const struct callchain_param *callchain)
{
struct evsel *leader = evsel__leader(evsel);
struct perf_event_attr *attr = &evsel->core.attr;
@@ -1355,10 +1567,11 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
attr->inherit_stat = 1;
}
- if (opts->sample_address) {
+ if (opts->sample_address)
evsel__set_sample_bit(evsel, ADDR);
+
+ if (opts->record_data_mmap)
attr->mmap_data = track;
- }
/*
* We don't allow user space callchains for function trace
@@ -1401,7 +1614,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
evsel__set_sample_bit(evsel, CPU);
}
- if (opts->sample_address)
+ if (opts->sample_data_src)
evsel__set_sample_bit(evsel, DATA_SRC);
if (opts->sample_phys_addr)
@@ -1416,14 +1629,16 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
attr->branch_sample_type = opts->branch_stack;
}
- if (opts->sample_weight)
+ if (opts->sample_weight || evsel->retire_lat) {
arch_evsel__set_sample_weight(evsel);
-
+ evsel->retire_lat = false;
+ }
attr->task = track;
attr->mmap = track;
attr->mmap2 = track && !perf_missing_features.mmap2;
attr->comm = track;
attr->build_id = track && opts->build_id;
+ attr->defer_output = track && callchain && callchain->defer;
/*
* ksymbol is tracked separately with text poke because it needs to be
@@ -1500,7 +1715,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
attr->exclude_user = 1;
}
- if (evsel->core.own_cpus || evsel->unit)
+ if (evsel->core.pmu_cpus || evsel->unit)
evsel->core.attr.read_format |= PERF_FORMAT_ID;
/*
@@ -1530,8 +1745,10 @@ 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))
+ if (evsel__is_offcpu_event(evsel)) {
evsel->core.attr.sample_type &= OFFCPU_SAMPLE_TYPES;
+ attr->inherit = 0;
+ }
arch__post_evsel_config(evsel, attr);
}
@@ -1628,10 +1845,21 @@ static void evsel__free_config_terms(struct evsel *evsel)
free_config_terms(&evsel->config_terms);
}
+static void (*evsel__priv_destructor)(void *priv);
+
+void evsel__set_priv_destructor(void (*destructor)(void *priv))
+{
+ assert(evsel__priv_destructor == NULL);
+
+ evsel__priv_destructor = destructor;
+}
+
void evsel__exit(struct evsel *evsel)
{
assert(list_empty(&evsel->core.node));
assert(evsel->evlist == NULL);
+ if (evsel__is_retire_lat(evsel))
+ evsel__tpebs_close(evsel);
bpf_counter__destroy(evsel);
perf_bpf_filter__destroy(evsel);
evsel__free_counts(evsel);
@@ -1639,9 +1867,7 @@ void evsel__exit(struct evsel *evsel)
perf_evsel__free_id(&evsel->core);
evsel__free_config_terms(evsel);
cgroup__put(evsel->cgrp);
- perf_cpu_map__put(evsel->core.cpus);
- perf_cpu_map__put(evsel->core.own_cpus);
- perf_thread_map__put(evsel->core.threads);
+ perf_evsel__exit(&evsel->core);
zfree(&evsel->group_name);
zfree(&evsel->name);
#ifdef HAVE_LIBTRACEEVENT
@@ -1655,7 +1881,8 @@ void evsel__exit(struct evsel *evsel)
evsel__zero_per_pkg(evsel);
hashmap__free(evsel->per_pkg_mask);
evsel->per_pkg_mask = NULL;
- zfree(&evsel->metric_events);
+ if (evsel__priv_destructor)
+ evsel__priv_destructor(evsel->priv);
perf_evsel__object.fini(evsel);
if (evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME ||
evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME)
@@ -1694,11 +1921,6 @@ static int evsel__read_one(struct evsel *evsel, int cpu_map_idx, int thread)
return perf_evsel__read(&evsel->core, cpu_map_idx, thread, count);
}
-static int evsel__read_retire_lat(struct evsel *evsel, int cpu_map_idx, int thread)
-{
- return tpebs_set_evsel(evsel, cpu_map_idx, thread);
-}
-
static void evsel__set_count(struct evsel *counter, int cpu_map_idx, int thread,
u64 val, u64 ena, u64 run, u64 lost)
{
@@ -1706,8 +1928,8 @@ static void evsel__set_count(struct evsel *counter, int cpu_map_idx, int thread,
count = perf_counts(counter->counts, cpu_map_idx, thread);
- if (counter->retire_lat) {
- evsel__read_retire_lat(counter, cpu_map_idx, thread);
+ if (evsel__is_retire_lat(counter)) {
+ evsel__tpebs_read(counter, cpu_map_idx, thread);
perf_counts__set_loaded(counter->counts, cpu_map_idx, thread, true);
return;
}
@@ -1844,16 +2066,19 @@ bool __evsel__match(const struct evsel *evsel, u32 type, u64 config)
u32 e_type = evsel->core.attr.type;
u64 e_config = evsel->core.attr.config;
- if (e_type != type) {
- return type == PERF_TYPE_HARDWARE && evsel->pmu && evsel->pmu->is_core &&
- evsel->alternate_hw_config == config;
- }
-
- if ((type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE) &&
- perf_pmus__supports_extended_type())
+ if (e_type == type && e_config == config)
+ return true;
+ if (type != PERF_TYPE_HARDWARE && type != PERF_TYPE_HW_CACHE)
+ return false;
+ if ((e_type == PERF_TYPE_HARDWARE || e_type == PERF_TYPE_HW_CACHE) &&
+ perf_pmus__supports_extended_type())
e_config &= PERF_HW_EVENT_MASK;
-
- return e_config == config;
+ if (e_type == type && e_config == config)
+ return true;
+ if (type == PERF_TYPE_HARDWARE && evsel->pmu && evsel->pmu->is_core &&
+ evsel->alternate_hw_config == config)
+ return true;
+ return false;
}
int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread)
@@ -1864,8 +2089,11 @@ int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread)
if (evsel__is_hwmon(evsel))
return evsel__hwmon_pmu_read(evsel, cpu_map_idx, thread);
+ if (evsel__is_drm(evsel))
+ return evsel__drm_pmu_read(evsel, cpu_map_idx, thread);
+
if (evsel__is_retire_lat(evsel))
- return evsel__read_retire_lat(evsel, cpu_map_idx, thread);
+ return evsel__tpebs_read(evsel, cpu_map_idx, thread);
if (evsel->core.attr.read_format & PERF_FORMAT_GROUP)
return evsel__read_group(evsel, cpu_map_idx, thread);
@@ -1919,7 +2147,7 @@ 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))
+ if (!evsel->supported || evsel__is_group_leader(evsel))
return -1;
/*
@@ -1933,7 +2161,7 @@ static int get_group_fd(struct evsel *evsel, int cpu_map_idx, int thread)
return -1;
fd = FD(leader, cpu_map_idx, thread);
- BUG_ON(fd == -1 && !leader->skippable);
+ BUG_ON(fd == -1 && leader->supported);
/*
* When the leader has been skipped, return -2 to distinguish from no
@@ -2099,6 +2327,10 @@ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,
static void evsel__disable_missing_features(struct evsel *evsel)
{
+ if (perf_missing_features.defer_callchain && evsel->core.attr.defer_callchain)
+ evsel->core.attr.defer_callchain = 0;
+ if (perf_missing_features.defer_callchain && evsel->core.attr.defer_output)
+ evsel->core.attr.defer_output = 0;
if (perf_missing_features.inherit_sample_read && evsel->core.attr.inherit &&
(evsel->core.attr.sample_type & PERF_SAMPLE_READ))
evsel->core.attr.inherit = 0;
@@ -2373,8 +2605,15 @@ static bool evsel__detect_missing_features(struct evsel *evsel, struct perf_cpu
/* Please add new feature detection here. */
+ attr.defer_callchain = true;
+ if (has_attr_feature(&attr, /*flags=*/0))
+ goto found;
+ perf_missing_features.defer_callchain = true;
+ pr_debug2("switching off deferred callchain support\n");
+ attr.defer_callchain = false;
+
attr.inherit = true;
- attr.sample_type = PERF_SAMPLE_READ;
+ attr.sample_type = PERF_SAMPLE_READ | PERF_SAMPLE_TID;
if (has_attr_feature(&attr, /*flags=*/0))
goto found;
perf_missing_features.inherit_sample_read = true;
@@ -2484,6 +2723,10 @@ found:
errno = old_errno;
check:
+ if ((evsel->core.attr.defer_callchain || evsel->core.attr.defer_output) &&
+ perf_missing_features.defer_callchain)
+ return true;
+
if (evsel->core.attr.inherit &&
(evsel->core.attr.sample_type & PERF_SAMPLE_READ) &&
perf_missing_features.inherit_sample_read)
@@ -2542,25 +2785,6 @@ check:
return false;
}
-static bool evsel__handle_error_quirks(struct evsel *evsel, int error)
-{
- /*
- * AMD core PMU tries to forward events with precise_ip to IBS PMU
- * implicitly. But IBS PMU has more restrictions so it can fail with
- * supported event attributes. Let's forward it back to the core PMU
- * by clearing precise_ip only if it's from precise_max (:P).
- */
- if ((error == -EINVAL || error == -ENOENT) && x86__is_amd_cpu() &&
- evsel->core.attr.precise_ip && evsel->precise_max) {
- evsel->core.attr.precise_ip = 0;
- pr_debug2_peo("removing precise_ip on AMD\n");
- display_attr(&evsel->core.attr);
- return true;
- }
-
- 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)
@@ -2570,12 +2794,14 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
enum rlimit_action set_rlimit = NO_CHANGE;
struct perf_cpu cpu;
- if (evsel__is_retire_lat(evsel))
- return tpebs_start(evsel->evlist);
+ if (evsel__is_retire_lat(evsel)) {
+ err = evsel__tpebs_open(evsel);
+ goto out;
+ }
err = __evsel__prepare_open(evsel, cpus, threads);
if (err)
- return err;
+ goto out;
if (cpus == NULL)
cpus = empty_cpu_map;
@@ -2595,14 +2821,22 @@ fallback_missing_features:
display_attr(&evsel->core.attr);
if (evsel__is_tool(evsel)) {
- return evsel__tool_pmu_open(evsel, threads,
+ err = evsel__tool_pmu_open(evsel, threads,
+ start_cpu_map_idx,
+ end_cpu_map_idx);
+ goto out;
+ }
+ if (evsel__is_hwmon(evsel)) {
+ err = evsel__hwmon_pmu_open(evsel, threads,
start_cpu_map_idx,
end_cpu_map_idx);
+ goto out;
}
- if (evsel__is_hwmon(evsel)) {
- return evsel__hwmon_pmu_open(evsel, threads,
- start_cpu_map_idx,
- end_cpu_map_idx);
+ if (evsel__is_drm(evsel)) {
+ err = evsel__drm_pmu_open(evsel, threads,
+ start_cpu_map_idx,
+ end_cpu_map_idx);
+ goto out;
}
for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
@@ -2660,8 +2894,8 @@ retry_open:
PERF_EVENT_IOC_SET_BPF,
bpf_fd);
if (err && errno != EEXIST) {
- pr_err("failed to attach bpf fd %d: %s\n",
- bpf_fd, strerror(errno));
+ pr_err("failed to attach bpf fd %d: %m\n",
+ bpf_fd);
err = -EINVAL;
goto out_close;
}
@@ -2681,7 +2915,8 @@ retry_open:
}
}
- return 0;
+ err = 0;
+ goto out;
try_fallback:
if (evsel__ignore_missing_thread(evsel, perf_cpu_map__nr(cpus),
@@ -2706,9 +2941,6 @@ try_fallback:
if (evsel__precise_ip_fallback(evsel))
goto retry_open;
- if (evsel__handle_error_quirks(evsel, err))
- goto retry_open;
-
out_close:
if (err)
threads->err_thread = thread;
@@ -2723,6 +2955,9 @@ out_close:
thread = nthreads;
} while (--idx >= 0);
errno = old_errno;
+out:
+ if (err)
+ evsel->supported = false;
return err;
}
@@ -2735,22 +2970,37 @@ int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus,
void evsel__close(struct evsel *evsel)
{
if (evsel__is_retire_lat(evsel))
- tpebs_delete();
+ evsel__tpebs_close(evsel);
perf_evsel__close(&evsel->core);
perf_evsel__free_id(&evsel->core);
}
-int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu_map_idx)
+int evsel__open_per_cpu_and_thread(struct evsel *evsel,
+ struct perf_cpu_map *cpus, int cpu_map_idx,
+ struct perf_thread_map *threads)
{
if (cpu_map_idx == -1)
- return evsel__open_cpu(evsel, cpus, NULL, 0, perf_cpu_map__nr(cpus));
+ return evsel__open_cpu(evsel, cpus, threads, 0, perf_cpu_map__nr(cpus));
+
+ return evsel__open_cpu(evsel, cpus, threads, cpu_map_idx, cpu_map_idx + 1);
+}
- return evsel__open_cpu(evsel, cpus, NULL, cpu_map_idx, cpu_map_idx + 1);
+int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu_map_idx)
+{
+ struct perf_thread_map *threads = thread_map__new_by_tid(-1);
+ int ret = evsel__open_per_cpu_and_thread(evsel, cpus, cpu_map_idx, threads);
+
+ perf_thread_map__put(threads);
+ return ret;
}
int evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads)
{
- return evsel__open(evsel, NULL, threads);
+ struct perf_cpu_map *cpus = perf_cpu_map__new_any_cpu();
+ int ret = evsel__open_per_cpu_and_thread(evsel, cpus, -1, threads);
+
+ perf_cpu_map__put(cpus);
+ return ret;
}
static int perf_evsel__parse_id_sample(const struct evsel *evsel,
@@ -2823,7 +3073,7 @@ static inline bool overflow(const void *endp, u16 max_size, const void *offset,
#define OVERFLOW_CHECK(offset, size, max_size) \
do { \
if (overflow(endp, (max_size), (offset), (size))) \
- return -EFAULT; \
+ goto out_efault; \
} while (0)
#define OVERFLOW_CHECK_u64(offset) \
@@ -2843,11 +3093,18 @@ 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)
+static void perf_parse_sample_weight(struct perf_sample *data, const __u64 *array, u64 type)
{
- data->weight = *array;
+ union perf_sample_weight weight;
+
+ weight.full = *array;
+ if (type & PERF_SAMPLE_WEIGHT_STRUCT) {
+ data->weight = weight.var1_dw;
+ data->ins_lat = weight.var2_w;
+ data->weight3 = weight.var3_w;
+ } else {
+ data->weight = weight.full;
+ }
}
u64 evsel__bitfield_swap_branch_flags(u64 value)
@@ -2921,6 +3178,37 @@ static inline bool evsel__has_branch_counters(const struct evsel *evsel)
return false;
}
+static int __set_offcpu_sample(struct perf_sample *data)
+{
+ u64 *array = data->raw_data;
+ u32 max_size = data->raw_size, *p32;
+ const void *endp = (void *)array + max_size;
+
+ if (array == NULL)
+ return -EFAULT;
+
+ OVERFLOW_CHECK_u64(array);
+ p32 = (void *)array++;
+ data->pid = p32[0];
+ data->tid = p32[1];
+
+ OVERFLOW_CHECK_u64(array);
+ data->period = *array++;
+
+ OVERFLOW_CHECK_u64(array);
+ data->callchain = (struct ip_callchain *)array++;
+ OVERFLOW_CHECK(array, data->callchain->nr * sizeof(u64), max_size);
+ data->ip = data->callchain->ips[1];
+ array += data->callchain->nr;
+
+ OVERFLOW_CHECK_u64(array);
+ data->cgroup = *array;
+
+ return 0;
+out_efault:
+ return -EFAULT;
+}
+
int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
struct perf_sample *data)
{
@@ -2937,7 +3225,8 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
*/
union u64_swap u;
- memset(data, 0, sizeof(*data));
+ perf_sample__init(data, /*all=*/true);
+ data->evsel = evsel;
data->cpu = data->pid = data->tid = -1;
data->stream_id = data->id = data->time = -1ULL;
data->period = evsel->core.attr.sample_period;
@@ -2946,16 +3235,31 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
data->data_src = PERF_MEM_DATA_SRC_NONE;
data->vcpu = -1;
+ if (event->header.type == PERF_RECORD_CALLCHAIN_DEFERRED) {
+ const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
+
+ data->callchain = (struct ip_callchain *)&event->callchain_deferred.nr;
+ if (data->callchain->nr > max_callchain_nr)
+ goto out_efault;
+
+ data->deferred_cookie = event->callchain_deferred.cookie;
+
+ if (evsel->core.attr.sample_id_all)
+ perf_evsel__parse_id_sample(evsel, event, data);
+
+ return 0;
+ }
+
if (event->header.type != PERF_RECORD_SAMPLE) {
- if (!evsel->core.attr.sample_id_all)
- return 0;
- return perf_evsel__parse_id_sample(evsel, event, data);
+ if (evsel->core.attr.sample_id_all)
+ perf_evsel__parse_id_sample(evsel, event, data);
+ return 0;
}
array = event->sample.array;
if (perf_event__check_size(event, evsel->sample_size))
- return -EFAULT;
+ goto out_efault;
if (type & PERF_SAMPLE_IDENTIFIER) {
data->id = *array;
@@ -3048,7 +3352,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
sizeof(struct sample_read_value);
if (data->read.group.nr > max_group_nr)
- return -EFAULT;
+ goto out_efault;
sz = data->read.group.nr * sample_read_value_size(read_format);
OVERFLOW_CHECK(array, sz, max_size);
@@ -3070,12 +3374,25 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
if (type & PERF_SAMPLE_CALLCHAIN) {
const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
+ u64 callchain_nr;
OVERFLOW_CHECK_u64(array);
data->callchain = (struct ip_callchain *)array++;
- if (data->callchain->nr > max_callchain_nr)
- return -EFAULT;
- sz = data->callchain->nr * sizeof(u64);
+ callchain_nr = data->callchain->nr;
+ if (callchain_nr > max_callchain_nr)
+ goto out_efault;
+ sz = callchain_nr * sizeof(u64);
+ /*
+ * Save the cookie for the deferred user callchain. The last 2
+ * entries in the callchain should be the context marker and the
+ * cookie. The cookie will be used to match PERF_RECORD_
+ * CALLCHAIN_DEFERRED later.
+ */
+ if (evsel->core.attr.defer_callchain && callchain_nr >= 2 &&
+ data->callchain->ips[callchain_nr - 2] == PERF_CONTEXT_USER_DEFERRED) {
+ data->deferred_cookie = data->callchain->ips[callchain_nr - 1];
+ data->deferred_callchain = true;
+ }
OVERFLOW_CHECK(array, sz, max_size);
array = (void *)array + sz;
}
@@ -3121,7 +3438,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
data->branch_stack = (struct branch_stack *)array++;
if (data->branch_stack->nr > max_branch_nr)
- return -EFAULT;
+ goto out_efault;
sz = data->branch_stack->nr * sizeof(struct branch_entry);
if (evsel__has_branch_hw_idx(evsel)) {
@@ -3164,17 +3481,19 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
}
if (type & PERF_SAMPLE_REGS_USER) {
+ struct regs_dump *regs = perf_sample__user_regs(data);
+
OVERFLOW_CHECK_u64(array);
- data->user_regs.abi = *array;
+ regs->abi = *array;
array++;
- if (data->user_regs.abi) {
+ if (regs->abi) {
u64 mask = evsel->core.attr.sample_regs_user;
sz = hweight64(mask) * sizeof(u64);
OVERFLOW_CHECK(array, sz, max_size);
- data->user_regs.mask = mask;
- data->user_regs.regs = (u64 *)array;
+ regs->mask = mask;
+ regs->regs = (u64 *)array;
array = (void *)array + sz;
}
}
@@ -3196,13 +3515,13 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
data->user_stack.size = *array++;
if (WARN_ONCE(data->user_stack.size > sz,
"user stack dump failure\n"))
- return -EFAULT;
+ goto out_efault;
}
}
if (type & PERF_SAMPLE_WEIGHT_TYPE) {
OVERFLOW_CHECK_u64(array);
- arch_perf_parse_sample_weight(data, array, type);
+ perf_parse_sample_weight(data, array, type);
array++;
}
@@ -3218,19 +3537,20 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
array++;
}
- data->intr_regs.abi = PERF_SAMPLE_REGS_ABI_NONE;
if (type & PERF_SAMPLE_REGS_INTR) {
+ struct regs_dump *regs = perf_sample__intr_regs(data);
+
OVERFLOW_CHECK_u64(array);
- data->intr_regs.abi = *array;
+ regs->abi = *array;
array++;
- if (data->intr_regs.abi != PERF_SAMPLE_REGS_ABI_NONE) {
+ if (regs->abi != PERF_SAMPLE_REGS_ABI_NONE) {
u64 mask = evsel->core.attr.sample_regs_intr;
sz = hweight64(mask) * sizeof(u64);
OVERFLOW_CHECK(array, sz, max_size);
- data->intr_regs.mask = mask;
- data->intr_regs.regs = (u64 *)array;
+ regs->mask = mask;
+ regs->regs = (u64 *)array;
array = (void *)array + sz;
}
}
@@ -3272,7 +3592,15 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
array = (void *)array + sz;
}
+ if (evsel__is_offcpu_event(evsel)) {
+ if (__set_offcpu_sample(data))
+ goto out_efault;
+ }
+
return 0;
+out_efault:
+ perf_sample__exit(data);
+ return -EFAULT;
}
int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event,
@@ -3472,25 +3800,42 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err,
{
int paranoid;
- if ((err == ENOENT || err == ENXIO || err == ENODEV) &&
- evsel->core.attr.type == PERF_TYPE_HARDWARE &&
- evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES) {
+ if ((err == ENODEV || err == ENOENT || err == ENXIO) &&
+ evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
/*
- * 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).
+ * If it's the legacy hardware cycles event fails then fall back
+ * to hrtimer based cpu-clock sw counter, which is always
+ * available even if no PMU support. PPC returned ENXIO rather
+ * than ENODEV or ENOENT until 2.6.37.
*/
- evsel->core.attr.type = PERF_TYPE_SOFTWARE;
+ evsel->pmu = perf_pmus__find_by_type(PERF_TYPE_SOFTWARE);
+ assert(evsel->pmu); /* software is a "well-known" and can't fail PMU type. */
+
+ /* Configure the event. */
+ evsel->core.attr.type = PERF_TYPE_SOFTWARE;
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");
+ evsel->core.is_pmu_core = false;
+ /* Remove excludes for new event. */
+ if (evsel->fallenback_eacces) {
+ evsel->core.attr.exclude_kernel = 0;
+ evsel->core.attr.exclude_hv = 0;
+ evsel->fallenback_eacces = false;
+ }
+ if (evsel->fallenback_eopnotsupp) {
+ evsel->core.attr.exclude_guest = 0;
+ evsel->fallenback_eopnotsupp = false;
+ }
+
+ /* Name is recomputed by evsel__name. */
zfree(&evsel->name);
+
+ /* Log message. */
+ scnprintf(msg, msgsize,
+ "The cycles event is not supported, trying to fall back to %s",
+ evsel__name(evsel));
return true;
} else if (err == EACCES && !evsel->core.attr.exclude_kernel &&
(paranoid = perf_event_paranoid()) > 1) {
@@ -3500,7 +3845,7 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err,
/* If event has exclude user then don't exclude kernel. */
if (evsel->core.attr.exclude_user)
- return false;
+ goto no_fallback;
/* Is there already the separator in the name. */
if (strchr(name, '/') ||
@@ -3508,7 +3853,7 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err,
sep = "";
if (asprintf(&new_name, "%s%su", name, sep) < 0)
- return false;
+ goto no_fallback;
free(evsel->name);
evsel->name = new_name;
@@ -3517,7 +3862,7 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err,
" samples", paranoid);
evsel->core.attr.exclude_kernel = 1;
evsel->core.attr.exclude_hv = 1;
-
+ evsel->fallenback_eacces = true;
return true;
} else if (err == EOPNOTSUPP && !evsel->core.attr.exclude_guest &&
!evsel->exclude_GH) {
@@ -3531,17 +3876,19 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err,
sep = "";
if (asprintf(&new_name, "%s%sH", name, sep) < 0)
- return false;
+ goto no_fallback;
free(evsel->name);
evsel->name = new_name;
/* Apple M1 requires exclude_guest */
- scnprintf(msg, msgsize, "trying to fall back to excluding guest samples");
+ scnprintf(msg, msgsize, "Trying to fall back to excluding guest samples");
evsel->core.attr.exclude_guest = 1;
-
+ evsel->fallenback_eopnotsupp = true;
return true;
}
-
+no_fallback:
+ scnprintf(msg, msgsize, "No fallback found for '%s' for error %d",
+ evsel__name(evsel), err);
return false;
}
@@ -3654,6 +4001,7 @@ static int dump_perf_event_processes(char *msg, size_t size)
}
int __weak arch_evsel__open_strerror(struct evsel *evsel __maybe_unused,
+ int err __maybe_unused,
char *msg __maybe_unused,
size_t size __maybe_unused)
{
@@ -3663,7 +4011,7 @@ int __weak arch_evsel__open_strerror(struct evsel *evsel __maybe_unused,
int evsel__open_strerror(struct evsel *evsel, struct target *target,
int err, char *msg, size_t size)
{
- char sbuf[STRERR_BUFSIZE];
+ struct perf_pmu *pmu;
int printed = 0, enforced = 0;
int ret;
@@ -3747,6 +4095,10 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target,
return scnprintf(msg, size, "%s",
"No hardware sampling interrupt available.\n");
#endif
+ if (!target__has_cpu(target))
+ return scnprintf(msg, size,
+ "Unsupported event (%s) in per-thread mode, enable system wide with '-a'.",
+ evsel__name(evsel));
break;
case EBUSY:
if (find_process("oprofiled"))
@@ -3774,7 +4126,8 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target,
return scnprintf(msg, size, "The 'aux_action' feature is not supported, update the kernel.");
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))
+ pmu = evsel__find_pmu(evsel);
+ if (!pmu->is_core && !target__has_cpu(target))
return scnprintf(msg, size,
"Invalid event (%s) in per-thread mode, enable system wide with '-a'.",
evsel__name(evsel));
@@ -3787,21 +4140,27 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target,
break;
}
- ret = arch_evsel__open_strerror(evsel, msg, size);
+ ret = arch_evsel__open_strerror(evsel, err, msg, size);
if (ret)
return ret;
+ errno = err;
return scnprintf(msg, size,
- "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n"
- "\"dmesg | grep -i perf\" may provide additional information.\n",
- err, str_error_r(err, sbuf, sizeof(sbuf)), evsel__name(evsel));
+ "The sys_perf_event_open() syscall failed for event (%s): %m\n"
+ "\"dmesg | grep -i perf\" may provide additional information.\n",
+ evsel__name(evsel));
+}
+
+struct perf_session *evsel__session(struct evsel *evsel)
+{
+ return evsel && evsel->evlist ? evsel->evlist->session : NULL;
}
struct perf_env *evsel__env(struct evsel *evsel)
{
- if (evsel && evsel->evlist && evsel->evlist->env)
- return evsel->evlist->env;
- return &perf_env;
+ struct perf_session *session = evsel__session(evsel);
+
+ return session ? perf_session__env(session) : NULL;
}
static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist)
@@ -3811,6 +4170,9 @@ static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist)
if (evsel__is_retire_lat(evsel))
return 0;
+ if (perf_pmu__kind(evsel->pmu) != PERF_PMU_KIND_PE)
+ return 0;
+
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++) {
@@ -3856,14 +4218,16 @@ void evsel__zero_per_pkg(struct evsel *evsel)
*/
bool evsel__is_hybrid(const struct evsel *evsel)
{
- if (perf_pmus__num_core_pmus() == 1)
+ if (!evsel->core.is_pmu_core)
return false;
- return evsel->core.is_pmu_core;
+ return perf_pmus__num_core_pmus() > 1;
}
struct evsel *evsel__leader(const struct evsel *evsel)
{
+ if (evsel->core.leader == NULL)
+ return NULL;
return container_of(evsel->core.leader, struct evsel, core);
}
@@ -3882,6 +4246,17 @@ void evsel__set_leader(struct evsel *evsel, struct evsel *leader)
evsel->core.leader = &leader->core;
}
+bool evsel__is_aux_event(const struct evsel *evsel)
+{
+ struct perf_pmu *pmu;
+
+ if (evsel->needs_auxtrace_mmap)
+ return true;
+
+ pmu = evsel__find_pmu(evsel);
+ return pmu && pmu->auxtrace;
+}
+
int evsel__source_count(const struct evsel *evsel)
{
struct evsel *pos;
@@ -3912,3 +4287,152 @@ void evsel__remove_from_group(struct evsel *evsel, struct evsel *leader)
leader->core.nr_members--;
}
}
+
+bool evsel__set_needs_uniquify(struct evsel *counter, const struct perf_stat_config *config)
+{
+ struct evsel *evsel;
+
+ if (counter->needs_uniquify) {
+ /* Already set. */
+ return true;
+ }
+
+ if (counter->use_config_name || counter->is_libpfm_event) {
+ /* Original name will be used. */
+ return false;
+ }
+
+ if (!config->hybrid_merge && evsel__is_hybrid(counter)) {
+ /* Unique hybrid counters necessary. */
+ counter->needs_uniquify = true;
+ return true;
+ }
+
+ if (counter->core.attr.type < PERF_TYPE_MAX && counter->core.attr.type != PERF_TYPE_RAW) {
+ /* Legacy event, don't uniquify. */
+ return false;
+ }
+
+ if (counter->pmu && counter->pmu->is_core &&
+ counter->alternate_hw_config != PERF_COUNT_HW_MAX) {
+ /* A sysfs or json event replacing a legacy event, don't uniquify. */
+ return false;
+ }
+
+ if (config->aggr_mode == AGGR_NONE) {
+ /* Always unique with no aggregation. */
+ counter->needs_uniquify = true;
+ return true;
+ }
+
+ if (counter->first_wildcard_match != NULL) {
+ /*
+ * If stats are merged then only the first_wildcard_match is
+ * displayed, there is no need to uniquify this evsel as the
+ * name won't be shown.
+ */
+ return false;
+ }
+
+ /*
+ * Do other non-merged events in the evlist have the same name? If so
+ * uniquify is necessary.
+ */
+ evlist__for_each_entry(counter->evlist, evsel) {
+ if (evsel == counter || evsel->first_wildcard_match || evsel->pmu == counter->pmu)
+ continue;
+
+ if (evsel__name_is(counter, evsel__name(evsel))) {
+ counter->needs_uniquify = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+void evsel__uniquify_counter(struct evsel *counter)
+{
+ const char *name, *pmu_name, *config;
+ char *new_name;
+ int len, ret;
+
+ /* No uniquification necessary. */
+ if (!counter->needs_uniquify)
+ return;
+
+ /* The evsel was already uniquified. */
+ if (counter->uniquified_name)
+ return;
+
+ /* Avoid checking to uniquify twice. */
+ counter->uniquified_name = true;
+
+ name = evsel__name(counter);
+ config = strchr(name, '/');
+ pmu_name = counter->pmu->name;
+
+ /* Already prefixed by the PMU name? */
+ len = pmu_name_len_no_suffix(pmu_name);
+
+ if (!strncmp(name, pmu_name, len)) {
+ /*
+ * If the PMU name is there, then there is no sense in not
+ * having a slash. Do this for robustness.
+ */
+ if (config == NULL)
+ config = name - 1;
+
+ ret = asprintf(&new_name, "%s/%s", pmu_name, config + 1);
+ } else if (config) {
+ len = config - name;
+ if (config[1] == '/') {
+ /* case: event// */
+ ret = asprintf(&new_name, "%s/%.*s/%s", pmu_name, len, name, config + 2);
+ } else {
+ /* case: event/.../ */
+ ret = asprintf(&new_name, "%s/%.*s,%s", pmu_name, len, name, config + 1);
+ }
+ } else {
+ config = strchr(name, ':');
+ if (config) {
+ /* case: event:.. */
+ len = config - name;
+
+ ret = asprintf(&new_name, "%s/%.*s/%s", pmu_name, len, name, config + 1);
+ } else {
+ /* case: event */
+ ret = asprintf(&new_name, "%s/%s/", pmu_name, name);
+ }
+ }
+ if (ret > 0) {
+ free(counter->name);
+ counter->name = new_name;
+ } else {
+ /* ENOMEM from asprintf. */
+ counter->uniquified_name = false;
+ }
+}
+
+void evsel__warn_user_requested_cpus(struct evsel *evsel, struct perf_cpu_map *user_requested_cpus)
+{
+ struct perf_cpu_map *intersect, *online = NULL;
+ const struct perf_pmu *pmu = evsel__find_pmu(evsel);
+
+ if (pmu && pmu->is_core) {
+ intersect = perf_cpu_map__intersect(pmu->cpus, user_requested_cpus);
+ } else {
+ online = cpu_map__online();
+ intersect = perf_cpu_map__intersect(online, user_requested_cpus);
+ }
+ if (!perf_cpu_map__equal(intersect, user_requested_cpus)) {
+ char buf1[128];
+ char buf2[128];
+
+ cpu_map__snprint(user_requested_cpus, buf1, sizeof(buf1));
+ cpu_map__snprint(online ?: pmu->cpus, buf2, sizeof(buf2));
+ pr_warning("WARNING: A requested CPU in '%s' is not supported by PMU '%s' (CPUs %s) for event '%s'\n",
+ buf1, pmu ? pmu->name : "cpu", buf2, evsel__name(evsel));
+ }
+ perf_cpu_map__put(intersect);
+ perf_cpu_map__put(online);
+}