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.c165
1 files changed, 133 insertions, 32 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index d264c143b592..56ebefd075f2 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -407,6 +407,7 @@ void evsel__init(struct evsel *evsel,
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
}
@@ -1091,6 +1092,71 @@ 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,
+ 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)
{
@@ -1104,6 +1170,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) {
@@ -1174,6 +1241,9 @@ static void evsel__apply_config_terms(struct evsel *evsel,
break;
case EVSEL__CONFIG_TERM_CFG_CHG:
break;
+ case EVSEL__CONFIG_TERM_RATIO_TO_PREV:
+ rtp_buf = term->val.str;
+ break;
default:
break;
}
@@ -1225,6 +1295,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)
@@ -1249,6 +1321,11 @@ void __weak arch__post_evsel_config(struct evsel *evsel __maybe_unused,
{
}
+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(struct record_opts *opts,
struct perf_event_attr *attr)
{
@@ -1941,7 +2018,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;
/*
@@ -1955,7 +2032,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
@@ -2573,12 +2650,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 evsel__tpebs_open(evsel);
+ 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;
@@ -2598,19 +2677,22 @@ fallback_missing_features:
display_attr(&evsel->core.attr);
if (evsel__is_tool(evsel)) {
- return evsel__tool_pmu_open(evsel, threads,
- start_cpu_map_idx,
- end_cpu_map_idx);
+ err = evsel__tool_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);
+ err = evsel__hwmon_pmu_open(evsel, threads,
+ start_cpu_map_idx,
+ end_cpu_map_idx);
+ goto out;
}
if (evsel__is_drm(evsel)) {
- return evsel__drm_pmu_open(evsel, threads,
- start_cpu_map_idx,
- end_cpu_map_idx);
+ 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++) {
@@ -2689,7 +2771,8 @@ retry_open:
}
}
- return 0;
+ err = 0;
+ goto out;
try_fallback:
if (evsel__ignore_missing_thread(evsel, perf_cpu_map__nr(cpus),
@@ -2728,6 +2811,9 @@ out_close:
thread = nthreads;
} while (--idx >= 0);
errno = old_errno;
+out:
+ if (err)
+ evsel->supported = false;
return err;
}
@@ -3562,7 +3648,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, '/') ||
@@ -3570,7 +3656,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;
@@ -3593,17 +3679,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;
return true;
}
-
+no_fallback:
+ scnprintf(msg, msgsize, "No fallback found for '%s' for error %d",
+ evsel__name(evsel), err);
return false;
}
@@ -3716,6 +3804,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)
{
@@ -3725,6 +3814,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)
{
+ struct perf_pmu *pmu;
char sbuf[STRERR_BUFSIZE];
int printed = 0, enforced = 0;
int ret;
@@ -3840,7 +3930,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));
@@ -3853,7 +3944,7 @@ 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;
@@ -3935,6 +4026,8 @@ bool evsel__is_hybrid(const struct evsel *evsel)
struct evsel *evsel__leader(const struct evsel *evsel)
{
+ if (evsel->core.leader == NULL)
+ return NULL;
return container_of(evsel->core.leader, struct evsel, core);
}
@@ -4048,9 +4141,9 @@ bool evsel__set_needs_uniquify(struct evsel *counter, const struct perf_stat_con
void evsel__uniquify_counter(struct evsel *counter)
{
- const char *name, *pmu_name;
- char *new_name, *config;
- int ret;
+ const char *name, *pmu_name, *config;
+ char *new_name;
+ int len, ret;
/* No uniquification necessary. */
if (!counter->needs_uniquify)
@@ -4064,15 +4157,23 @@ void evsel__uniquify_counter(struct evsel *counter)
counter->uniquified_name = true;
name = evsel__name(counter);
+ config = strchr(name, '/');
pmu_name = counter->pmu->name;
- /* Already prefixed by the PMU name. */
- if (!strncmp(name, pmu_name, strlen(pmu_name)))
- return;
- config = strchr(name, '/');
- if (config) {
- int len = config - 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);
@@ -4084,7 +4185,7 @@ void evsel__uniquify_counter(struct evsel *counter)
config = strchr(name, ':');
if (config) {
/* case: event:.. */
- int len = config - name;
+ len = config - name;
ret = asprintf(&new_name, "%s/%.*s/%s", pmu_name, len, name, config + 1);
} else {