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

perf pmu: Abstract alias/event struct



In order to be able to lazily compute aliases/events for a PMU, move
the struct perf_pmu_alias into pmu.c.

Add perf_pmu__find_event and perf_pmu__for_each_event that take a
callback that is called for the found event or for each event.

The layout of struct pmu and the event/alias list is unchanged but the
API is altered so that aliases are no longer directly accessed, allowing
for later changes.

Signed-off-by: default avatarIan Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Gaosheng Cui <cuigaosheng1@huawei.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Rob Herring <robh@kernel.org>
Link: https://lore.kernel.org/r/20230824041330.266337-3-irogers@google.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 50402641
Loading
Loading
Loading
Loading
+2 −6
Original line number Diff line number Diff line
@@ -57,9 +57,7 @@ static int save_result(void)
		r->is_core = pmu->is_core;
		r->nr_caps = pmu->nr_caps;

		r->nr_aliases = 0;
		list_for_each(list, &pmu->aliases)
			r->nr_aliases++;
		r->nr_aliases = perf_pmu__num_events(pmu);

		r->nr_formats = 0;
		list_for_each(list, &pmu->format)
@@ -98,9 +96,7 @@ static int check_result(bool core_only)
			return -1;
		}

		nr = 0;
		list_for_each(list, &pmu->aliases)
			nr++;
		nr = perf_pmu__num_events(pmu);
		if (nr != r->nr_aliases) {
			pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n",
				pmu->name, r->nr_aliases, nr);
+48 −53
Original line number Diff line number Diff line
@@ -341,7 +341,7 @@ static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event
	return 0;
}

static int compare_alias_to_test_event(struct perf_pmu_alias *alias,
static int compare_alias_to_test_event(struct pmu_event_info *alias,
				struct perf_pmu_test_event const *test_event,
				char const *pmu_name)
{
@@ -496,6 +496,23 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused,
	return 0;
}

struct test_core_pmu_event_aliases_cb_args {
	struct perf_pmu_test_event const *test_event;
	int *count;
};

static int test_core_pmu_event_aliases_cb(void *state, struct pmu_event_info *alias)
{
	struct test_core_pmu_event_aliases_cb_args *args = state;

	if (compare_alias_to_test_event(alias, args->test_event, alias->pmu->name))
		return -1;
	(*args->count)++;
	pr_debug2("testing aliases core PMU %s: matched event %s\n",
		alias->pmu_name, alias->name);
	return 0;
}

/* Verify aliases are as expected */
static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
{
@@ -522,25 +539,19 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
	pmu_add_cpu_aliases_table(pmu, table);

	for (; *test_event_table; test_event_table++) {
		struct perf_pmu_test_event const *test_event = *test_event_table;
		struct pmu_event const *event = &test_event->event;
		struct perf_pmu_alias *alias = perf_pmu__find_alias(pmu, event->name);

		if (!alias) {
			pr_debug("testing aliases core PMU %s: no alias, alias_table->name=%s\n",
				  pmu_name, event->name);
			res = -1;
			break;
		}

		if (compare_alias_to_test_event(alias, test_event, pmu_name)) {
			res = -1;
			break;
		}
		struct perf_pmu_test_event test_event = **test_event_table;
		struct pmu_event const *event = &test_event.event;
		struct test_core_pmu_event_aliases_cb_args args = {
			.test_event = &test_event,
			.count = count,
		};
		int err;

		(*count)++;
		pr_debug2("testing aliases core PMU %s: matched event %s\n",
			  pmu_name, alias->name);
		test_event.event.pmu = pmu_name;
		err = perf_pmu__find_event(pmu, event->name, &args,
					   test_core_pmu_event_aliases_cb);
		if (err)
			res = err;
	}
	perf_pmu__delete(pmu);

@@ -553,7 +564,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
	struct perf_pmu_test_event const **table;
	struct perf_pmu *pmu = &test_pmu->pmu;
	const char *pmu_name = pmu->name;
	struct perf_pmu_alias *a, *tmp, *alias;
	const struct pmu_events_table *events_table;
	int res = 0;

@@ -564,8 +574,7 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
	pmu_add_sys_aliases(pmu);

	/* Count how many aliases we generated */
	list_for_each_entry(alias, &pmu->aliases, list)
		alias_count++;
	alias_count = perf_pmu__num_events(pmu);

	/* Count how many aliases we expect from the known table */
	for (table = &test_pmu->aliases[0]; *table; table++)
@@ -574,33 +583,25 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
	if (alias_count != to_match_count) {
		pr_debug("testing aliases uncore PMU %s: mismatch expected aliases (%d) vs found (%d)\n",
			 pmu_name, to_match_count, alias_count);
		res = -1;
		goto out;
		return -1;
	}

	list_for_each_entry(alias, &pmu->aliases, list) {
		bool matched = false;

	for (table = &test_pmu->aliases[0]; *table; table++) {
			struct perf_pmu_test_event const *test_event = *table;
			struct pmu_event const *event = &test_event->event;

			if (!strcmp(event->name, alias->name)) {
				if (compare_alias_to_test_event(alias,
							test_event,
							pmu_name)) {
					continue;
				}
				matched = true;
				matched_count++;
			}
		}
		struct perf_pmu_test_event test_event = **table;
		struct pmu_event const *event = &test_event.event;
		int err;
		struct test_core_pmu_event_aliases_cb_args args = {
			.test_event = &test_event,
			.count = &matched_count,
		};

		if (matched == false) {
		err = perf_pmu__find_event(pmu, event->name, &args,
					   test_core_pmu_event_aliases_cb);
		if (err) {
			res = err;
			pr_debug("testing aliases uncore PMU %s: could not match alias %s\n",
				 pmu_name, alias->name);
			res = -1;
			goto out;
				 pmu_name, event->name);
			return -1;
		}
	}

@@ -609,12 +610,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
			 pmu_name, matched_count, alias_count);
		res = -1;
	}

out:
	list_for_each_entry_safe(a, tmp, &pmu->aliases, list) {
		list_del(&a->list);
		perf_pmu_free_alias(a);
	}
	return res;
}

+27 −40
Original line number Diff line number Diff line
@@ -193,26 +193,20 @@ static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu)
	struct parse_events_term *term;

	list_for_each_entry(term, config_terms, list) {
		struct perf_pmu_alias *alias;
		bool matched = false;
		u64 num;

		if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW)
			continue;

		list_for_each_entry(alias, &pmu->aliases, list) {
			if (!strcmp(alias->name, term->val.str)) {
		if (perf_pmu__have_event(pmu, term->val.str)) {
			free(term->config);
			term->config = term->val.str;
			term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
			term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
			term->val.num = 1;
			term->no_value = true;
				matched = true;
				break;
			}
			continue;
		}
		if (!matched) {
			u64 num;

		free(term->config);
		term->config = strdup("config");
@@ -226,7 +220,6 @@ static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu)
		term->no_value = false;
	}
}
}

static struct evsel *
__add_event(struct list_head *list, int *idx,
@@ -1458,29 +1451,23 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
	INIT_LIST_HEAD(list);

	while ((pmu = perf_pmus__scan(pmu)) != NULL) {
		struct perf_pmu_alias *alias;
		bool auto_merge_stats;

		if (parse_events__filter_pmu(parse_state, pmu))
			continue;

		auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
		if (!perf_pmu__have_event(pmu, str))
			continue;

		list_for_each_entry(alias, &pmu->aliases, list) {
			if (!strcasecmp(alias->name, str)) {
		auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
		parse_events_copy_term_list(head, &orig_head);
				if (!parse_events_add_pmu(parse_state, list,
							  pmu->name, orig_head,
							  auto_merge_stats, loc)) {
					pr_debug("%s -> %s/%s/\n", str,
						 pmu->name, alias->str);
					parse_state->wild_card_pmus = true;
		if (!parse_events_add_pmu(parse_state, list, pmu->name,
					  orig_head, auto_merge_stats, loc)) {
			pr_debug("%s -> %s/%s/\n", str, pmu->name, str);
			ok++;
		}
		parse_events_terms__delete(orig_head);
	}
		}
	}

	if (parse_state->fake_pmu) {
		if (!parse_events_add_pmu(parse_state, list, str, head,
+194 −18
Original line number Diff line number Diff line
@@ -31,6 +31,61 @@

struct perf_pmu perf_pmu__fake;

#define UNIT_MAX_LEN	31 /* max length for event unit name */

/**
 * struct perf_pmu_alias - An event either read from sysfs or builtin in
 * pmu-events.c, created by parsing the pmu-events json files.
 */
struct perf_pmu_alias {
	/** @name: Name of the event like "mem-loads". */
	char *name;
	/** @desc: Optional short description of the event. */
	char *desc;
	/** @long_desc: Optional long description. */
	char *long_desc;
	/**
	 * @topic: Optional topic such as cache or pipeline, particularly for
	 * json events.
	 */
	char *topic;
	/**
	 * @str: Comma separated parameter list like
	 * "event=0xcd,umask=0x1,ldlat=0x3".
	 */
	char *str;
	/** @terms: Owned list of the original parsed parameters. */
	struct list_head terms;
	/** @list: List element of struct perf_pmu aliases. */
	struct list_head list;
	/** @unit: Units for the event, such as bytes or cache lines. */
	char unit[UNIT_MAX_LEN+1];
	/** @scale: Value to scale read counter values by. */
	double scale;
	/**
	 * @per_pkg: Does the file
	 * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or
	 * equivalent json value exist and have the value 1.
	 */
	bool per_pkg;
	/**
	 * @snapshot: Does the file
	 * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot
	 * exist and have the value 1.
	 */
	bool snapshot;
	/**
	 * @deprecated: Is the event hidden and so not shown in perf list by
	 * default.
	 */
	bool deprecated;
	/**
	 * @pmu_name: The name copied from the json struct pmu_event. This can
	 * differ from the PMU name as it won't have suffixes.
	 */
	char *pmu_name;
};

/**
 * struct perf_pmu_format - Values from a format file read from
 * <sysfs>/devices/cpu/format/ held in struct perf_pmu.
@@ -355,7 +410,7 @@ static void perf_pmu_update_alias(struct perf_pmu_alias *old,
}

/* Delete an alias entry. */
void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
static void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
{
	zfree(&newalias->name);
	zfree(&newalias->desc);
@@ -1349,10 +1404,20 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
	return perf_pmu__config_terms(pmu, attr, head_terms, zero, err);
}

static struct perf_pmu_alias *perf_pmu__find_alias(const struct perf_pmu *pmu, const char *str)
{
	struct perf_pmu_alias *alias;

	list_for_each_entry(alias, &pmu->aliases, list) {
		if (!strcasecmp(alias->name, str))
			return alias;
	}
	return NULL;
}

static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
					     struct parse_events_term *term)
{
	struct perf_pmu_alias *alias;
	char *name;

	if (parse_events__is_hardcoded_term(term))
@@ -1364,6 +1429,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
		if (pmu_find_format(&pmu->format, term->config))
			return NULL;
		name = term->config;

	} else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
		if (strcasecmp(term->config, "event"))
			return NULL;
@@ -1372,11 +1438,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
		return NULL;
	}

	list_for_each_entry(alias, &pmu->aliases, list) {
		if (!strcasecmp(alias->name, name))
			return alias;
	}
	return NULL;
	return perf_pmu__find_alias(pmu, name);
}


@@ -1459,16 +1521,33 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
	return 0;
}

struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu, const char *event)
struct find_event_args {
	const char *event;
	void *state;
	pmu_event_callback cb;
};

static int find_event_callback(void *state, struct pmu_event_info *info)
{
	struct perf_pmu_alias *alias;
	struct find_event_args *args = state;

	list_for_each_entry(alias, &pmu->aliases, list)
		if (!strcmp(event, alias->name))
			return alias;
	if (!strcmp(args->event, info->name))
		return args->cb(args->state, info);

	return NULL;
	return 0;
}

int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb)
{
	struct find_event_args args = {
		.event = event,
		.state = state,
		.cb = cb,
	};

	return perf_pmu__for_each_event(pmu, &args, find_event_callback);
}

static void perf_pmu__del_formats(struct list_head *formats)
{
	struct perf_pmu_format *fmt, *tmp;
@@ -1508,13 +1587,110 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)

bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
{
	struct perf_pmu_alias *alias;
	return perf_pmu__find_alias(pmu, name) != NULL;
}

	list_for_each_entry(alias, &pmu->aliases, list) {
		if (!strcmp(alias->name, name))
			return true;
size_t perf_pmu__num_events(const struct perf_pmu *pmu)
{
	struct list_head *list;
	size_t nr = 0;

	list_for_each(list, &pmu->aliases)
		nr++;

	return pmu->selectable ? nr + 1 : nr;
}
	return false;

static int sub_non_neg(int a, int b)
{
	if (b > a)
		return 0;
	return a - b;
}

static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
			  const struct perf_pmu_alias *alias)
{
	struct parse_events_term *term;
	int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name);

	list_for_each_entry(term, &alias->terms, list) {
		if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
			used += snprintf(buf + used, sub_non_neg(len, used),
					",%s=%s", term->config,
					term->val.str);
	}

	if (sub_non_neg(len, used) > 0) {
		buf[used] = '/';
		used++;
	}
	if (sub_non_neg(len, used) > 0) {
		buf[used] = '\0';
		used++;
	} else
		buf[len - 1] = '\0';

	return buf;
}

int perf_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb)
{
	char buf[1024];
	struct perf_pmu_alias *event;
	struct pmu_event_info info = {
		.pmu = pmu,
	};
	int ret = 0;

	list_for_each_entry(event, &pmu->aliases, list) {
		size_t buf_used;

		info.pmu_name = event->pmu_name ?: pmu->name;
		info.alias = NULL;
		if (event->desc) {
			info.name = event->name;
			buf_used = 0;
		} else {
			info.name = format_alias(buf, sizeof(buf), pmu, event);
			if (pmu->is_core) {
				info.alias = info.name;
				info.name = event->name;
			}
			buf_used = strlen(buf) + 1;
		}
		info.scale_unit = NULL;
		if (strlen(event->unit) || event->scale != 1.0) {
			info.scale_unit = buf + buf_used;
			buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
					"%G%s", event->scale, event->unit) + 1;
		}
		info.desc = event->desc;
		info.long_desc = event->long_desc;
		info.encoding_desc = buf + buf_used;
		buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
				"%s/%s/", info.pmu_name, event->str) + 1;
		info.topic = event->topic;
		info.str = event->str;
		info.deprecated = event->deprecated;
		ret = cb(state, &info);
		if (ret)
			return ret;
	}
	if (pmu->selectable) {
		info.name = buf;
		snprintf(buf, sizeof(buf), "%s//", pmu->name);
		info.alias = NULL;
		info.scale_unit = NULL;
		info.desc = NULL;
		info.long_desc = NULL;
		info.encoding_desc = NULL;
		info.topic = NULL;
		info.pmu_name = pmu->name;
		info.deprecated = false;
		ret = cb(state, &info);
	}
	return ret;
}

bool perf_pmu__is_software(const struct perf_pmu *pmu)
+17 −54
Original line number Diff line number Diff line
@@ -158,61 +158,22 @@ struct perf_pmu_info {
	bool snapshot;
};

#define UNIT_MAX_LEN	31 /* max length for event unit name */

/**
 * struct perf_pmu_alias - An event either read from sysfs or builtin in
 * pmu-events.c, created by parsing the pmu-events json files.
 */
struct perf_pmu_alias {
	/** @name: Name of the event like "mem-loads". */
	char *name;
	/** @desc: Optional short description of the event. */
	char *desc;
	/** @long_desc: Optional long description. */
	char *long_desc;
	/**
	 * @topic: Optional topic such as cache or pipeline, particularly for
	 * json events.
	 */
	char *topic;
	/**
	 * @str: Comma separated parameter list like
	 * "event=0xcd,umask=0x1,ldlat=0x3".
	 */
	char *str;
	/** @terms: Owned list of the original parsed parameters. */
	struct list_head terms;
	/** @list: List element of struct perf_pmu aliases. */
	struct list_head list;
	/** @unit: Units for the event, such as bytes or cache lines. */
	char unit[UNIT_MAX_LEN+1];
	/** @scale: Value to scale read counter values by. */
	double scale;
	/**
	 * @per_pkg: Does the file
	 * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or
	 * equivalent json value exist and have the value 1.
	 */
	bool per_pkg;
	/**
	 * @snapshot: Does the file
	 * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot
	 * exist and have the value 1.
	 */
	bool snapshot;
	/**
	 * @deprecated: Is the event hidden and so not shown in perf list by
	 * default.
	 */
struct pmu_event_info {
	const struct perf_pmu *pmu;
	const char *name;
	const char* alias;
	const char *scale_unit;
	const char *desc;
	const char *long_desc;
	const char *encoding_desc;
	const char *topic;
	const char *pmu_name;
	const char *str;
	bool deprecated;
	/**
	 * @pmu_name: The name copied from the json struct pmu_event. This can
	 * differ from the PMU name as it won't have suffixes.
	 */
	char *pmu_name;
};

typedef int (*pmu_event_callback)(void *state, struct pmu_event_info *info);

void pmu_add_sys_aliases(struct perf_pmu *pmu);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
		     struct list_head *head_terms,
@@ -225,7 +186,7 @@ __u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name);
int perf_pmu__format_type(struct perf_pmu *pmu, const char *name);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
			  struct perf_pmu_info *info);
struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu, const char *event);
int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb);

int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load);
void perf_pmu_format__set_value(void *format, int config, unsigned long *bits);
@@ -235,6 +196,9 @@ bool is_pmu_core(const char *name);
bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu);
bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu);
bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name);
size_t perf_pmu__num_events(const struct perf_pmu *pmu);
int perf_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb);

/**
 * perf_pmu_is_software - is the PMU a software PMU as in it uses the
 *                        perf_sw_context in the kernel?
@@ -259,7 +223,6 @@ void pmu_add_cpu_aliases_table(struct perf_pmu *pmu,
char *perf_pmu__getcpuid(struct perf_pmu *pmu);
const struct pmu_events_table *pmu_events_table__find(void);
const struct pmu_metrics_table *pmu_metrics_table__find(void);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);

int perf_pmu__convert_scale(const char *scale, char **end, double *sval);

Loading