Commit 7d45f402 authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo
Browse files

perf evlist: Make uniquifying counter names consistent



'perf stat' has different uniquification logic to 'perf record' and perf
top. In the case of perf record and 'perf top' all hybrid event names
are uniquified.

'perf stat' is more disciplined respecting name config terms, libpfm4
events, etc.

'perf stat' will uniquify hybrid events and the non-core PMU cases
shouldn't apply to perf record or 'perf top'.

For consistency, remove the uniquification for 'perf record' and 'perf
top' and reuse the 'perf stat' uniquification, making the code more
globally visible for this.

Fix the detection of cross-PMU for disabling uniquify by correctly
setting last_pmu.

When setting uniquify on an evsel, make sure the PMUs between the 2
considered events differ otherwise the uniquify isn't adding value.

Signed-off-by: default avatarIan Rogers <irogers@google.com>
Tested-by: default avatarChun-Tse Shao <ctshao@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Levi Yun <yeoreum.yun@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Weilin Wang <weilin.wang@intel.com>
Link: https://lore.kernel.org/r/20250513215401.2315949-2-ctshao@google.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent ef60b8f5
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include "util/target.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/stat.h"
#include "util/symbol.h"
#include "util/record.h"
#include "util/cpumap.h"
@@ -2484,7 +2485,11 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
		pr_warning("WARNING: --timestamp-filename option is not available in pipe mode.\n");
	}

	evlist__uniquify_name(rec->evlist);
	/*
	 * Use global stat_config that is zero meaning aggr_mode is AGGR_NONE
	 * and hybrid_merge is false.
	 */
	evlist__uniquify_evsel_names(rec->evlist, &stat_config);

	evlist__config(rec->evlist, opts, &callchain_param);

+6 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
#include "util/mmap.h"
#include "util/session.h"
#include "util/thread.h"
#include "util/stat.h"
#include "util/symbol.h"
#include "util/synthetic-events.h"
#include "util/top.h"
@@ -1309,7 +1310,11 @@ static int __cmd_top(struct perf_top *top)
		}
	}

	evlist__uniquify_name(top->evlist);
	/*
	 * Use global stat_config that is zero meaning aggr_mode is AGGR_NONE
	 * and hybrid_merge is false.
	 */
	evlist__uniquify_evsel_names(top->evlist, &stat_config);
	ret = perf_top__start_counters(top);
	if (ret)
		return ret;
+44 −22
Original line number Diff line number Diff line
@@ -2565,34 +2565,56 @@ void evlist__warn_user_requested_cpus(struct evlist *evlist, const char *cpu_lis
	perf_cpu_map__put(user_requested_cpus);
}

void evlist__uniquify_name(struct evlist *evlist)
/* Should uniquify be disabled for the evlist? */
static bool evlist__disable_uniquify(const struct evlist *evlist)
{
	char *new_name, empty_attributes[2] = ":", *attributes;
	struct evsel *pos;

	if (perf_pmus__num_core_pmus() == 1)
		return;
	struct evsel *counter;
	struct perf_pmu *last_pmu = NULL;
	bool first = true;

	evlist__for_each_entry(evlist, pos) {
		if (!evsel__is_hybrid(pos))
			continue;
	evlist__for_each_entry(evlist, counter) {
		/* If PMUs vary then uniquify can be useful. */
		if (!first && counter->pmu != last_pmu)
			return false;
		first = false;
		if (counter->pmu) {
			/* Allow uniquify for uncore PMUs. */
			if (!counter->pmu->is_core)
				return false;
			/* Keep hybrid event names uniquified for clarity. */
			if (perf_pmus__num_core_pmus() > 1)
				return false;
		}
		last_pmu = counter->pmu;
	}
	return true;
}

		if (strchr(pos->name, '/'))
			continue;
static bool evlist__set_needs_uniquify(struct evlist *evlist, const struct perf_stat_config *config)
{
	struct evsel *counter;
	bool needs_uniquify = false;

		attributes = strchr(pos->name, ':');
		if (attributes)
			*attributes = '\0';
		else
			attributes = empty_attributes;
	if (evlist__disable_uniquify(evlist)) {
		evlist__for_each_entry(evlist, counter)
			counter->uniquified_name = true;
		return false;
	}

		if (asprintf(&new_name, "%s/%s/%s", pos->pmu ? pos->pmu->name : "",
			     pos->name, attributes + 1)) {
			free(pos->name);
			pos->name = new_name;
		} else {
			*attributes = ':';
	evlist__for_each_entry(evlist, counter) {
		if (evsel__set_needs_uniquify(counter, config))
			needs_uniquify = true;
	}
	return needs_uniquify;
}

void evlist__uniquify_evsel_names(struct evlist *evlist, const struct perf_stat_config *config)
{
	if (evlist__set_needs_uniquify(evlist, config)) {
		struct evsel *pos;

		evlist__for_each_entry(evlist, pos)
			evsel__uniquify_counter(pos);
	}
}

+2 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
struct pollfd;
struct thread_map;
struct perf_cpu_map;
struct perf_stat_config;
struct record_opts;
struct strbuf;
struct target;
@@ -434,7 +435,7 @@ struct evsel *evlist__find_evsel(struct evlist *evlist, int idx);
void evlist__format_evsels(struct evlist *evlist, struct strbuf *sb, size_t max_length);
void evlist__check_mem_load_aux(struct evlist *evlist);
void evlist__warn_user_requested_cpus(struct evlist *evlist, const char *cpu_list);
void evlist__uniquify_name(struct evlist *evlist);
void evlist__uniquify_evsel_names(struct evlist *evlist, const struct perf_stat_config *config);
bool evlist__has_bpf_output(struct evlist *evlist);
bool evlist__needs_bpf_sb_event(struct evlist *evlist);

+113 −0
Original line number Diff line number Diff line
@@ -3954,3 +3954,116 @@ 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->merged_stat) {
		/* Counter won't be shown. */
		return false;
	}

	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;
	}

	/*
	 * 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->merged_stat || 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;
	char *new_name, *config;
	int 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);
	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;

		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:.. */
			int 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;
	}
}
Loading