162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2017, Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/* Manage metrics and groups of metrics from JSON files */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "metricgroup.h"
962306a36Sopenharmony_ci#include "debug.h"
1062306a36Sopenharmony_ci#include "evlist.h"
1162306a36Sopenharmony_ci#include "evsel.h"
1262306a36Sopenharmony_ci#include "strbuf.h"
1362306a36Sopenharmony_ci#include "pmu.h"
1462306a36Sopenharmony_ci#include "pmus.h"
1562306a36Sopenharmony_ci#include "print-events.h"
1662306a36Sopenharmony_ci#include "smt.h"
1762306a36Sopenharmony_ci#include "expr.h"
1862306a36Sopenharmony_ci#include "rblist.h"
1962306a36Sopenharmony_ci#include <string.h>
2062306a36Sopenharmony_ci#include <errno.h>
2162306a36Sopenharmony_ci#include "strlist.h"
2262306a36Sopenharmony_ci#include <assert.h>
2362306a36Sopenharmony_ci#include <linux/ctype.h>
2462306a36Sopenharmony_ci#include <linux/list_sort.h>
2562306a36Sopenharmony_ci#include <linux/string.h>
2662306a36Sopenharmony_ci#include <linux/zalloc.h>
2762306a36Sopenharmony_ci#include <perf/cpumap.h>
2862306a36Sopenharmony_ci#include <subcmd/parse-options.h>
2962306a36Sopenharmony_ci#include <api/fs/fs.h>
3062306a36Sopenharmony_ci#include "util.h"
3162306a36Sopenharmony_ci#include <asm/bug.h>
3262306a36Sopenharmony_ci#include "cgroup.h"
3362306a36Sopenharmony_ci#include "util/hashmap.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct metric_event *metricgroup__lookup(struct rblist *metric_events,
3662306a36Sopenharmony_ci					 struct evsel *evsel,
3762306a36Sopenharmony_ci					 bool create)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct rb_node *nd;
4062306a36Sopenharmony_ci	struct metric_event me = {
4162306a36Sopenharmony_ci		.evsel = evsel
4262306a36Sopenharmony_ci	};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (!metric_events)
4562306a36Sopenharmony_ci		return NULL;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	nd = rblist__find(metric_events, &me);
4862306a36Sopenharmony_ci	if (nd)
4962306a36Sopenharmony_ci		return container_of(nd, struct metric_event, nd);
5062306a36Sopenharmony_ci	if (create) {
5162306a36Sopenharmony_ci		rblist__add_node(metric_events, &me);
5262306a36Sopenharmony_ci		nd = rblist__find(metric_events, &me);
5362306a36Sopenharmony_ci		if (nd)
5462306a36Sopenharmony_ci			return container_of(nd, struct metric_event, nd);
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	return NULL;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int metric_event_cmp(struct rb_node *rb_node, const void *entry)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct metric_event *a = container_of(rb_node,
6262306a36Sopenharmony_ci					      struct metric_event,
6362306a36Sopenharmony_ci					      nd);
6462306a36Sopenharmony_ci	const struct metric_event *b = entry;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (a->evsel == b->evsel)
6762306a36Sopenharmony_ci		return 0;
6862306a36Sopenharmony_ci	if ((char *)a->evsel < (char *)b->evsel)
6962306a36Sopenharmony_ci		return -1;
7062306a36Sopenharmony_ci	return +1;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
7462306a36Sopenharmony_ci					const void *entry)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct metric_event *me = malloc(sizeof(struct metric_event));
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!me)
7962306a36Sopenharmony_ci		return NULL;
8062306a36Sopenharmony_ci	memcpy(me, entry, sizeof(struct metric_event));
8162306a36Sopenharmony_ci	me->evsel = ((struct metric_event *)entry)->evsel;
8262306a36Sopenharmony_ci	me->is_default = false;
8362306a36Sopenharmony_ci	INIT_LIST_HEAD(&me->head);
8462306a36Sopenharmony_ci	return &me->nd;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void metric_event_delete(struct rblist *rblist __maybe_unused,
8862306a36Sopenharmony_ci				struct rb_node *rb_node)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct metric_event *me = container_of(rb_node, struct metric_event, nd);
9162306a36Sopenharmony_ci	struct metric_expr *expr, *tmp;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	list_for_each_entry_safe(expr, tmp, &me->head, nd) {
9462306a36Sopenharmony_ci		zfree(&expr->metric_name);
9562306a36Sopenharmony_ci		zfree(&expr->metric_refs);
9662306a36Sopenharmony_ci		zfree(&expr->metric_events);
9762306a36Sopenharmony_ci		free(expr);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	free(me);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void metricgroup__rblist_init(struct rblist *metric_events)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	rblist__init(metric_events);
10662306a36Sopenharmony_ci	metric_events->node_cmp = metric_event_cmp;
10762306a36Sopenharmony_ci	metric_events->node_new = metric_event_new;
10862306a36Sopenharmony_ci	metric_events->node_delete = metric_event_delete;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_civoid metricgroup__rblist_exit(struct rblist *metric_events)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	rblist__exit(metric_events);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/**
11762306a36Sopenharmony_ci * The metric under construction. The data held here will be placed in a
11862306a36Sopenharmony_ci * metric_expr.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistruct metric {
12162306a36Sopenharmony_ci	struct list_head nd;
12262306a36Sopenharmony_ci	/**
12362306a36Sopenharmony_ci	 * The expression parse context importantly holding the IDs contained
12462306a36Sopenharmony_ci	 * within the expression.
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci	struct expr_parse_ctx *pctx;
12762306a36Sopenharmony_ci	const char *pmu;
12862306a36Sopenharmony_ci	/** The name of the metric such as "IPC". */
12962306a36Sopenharmony_ci	const char *metric_name;
13062306a36Sopenharmony_ci	/** Modifier on the metric such as "u" or NULL for none. */
13162306a36Sopenharmony_ci	const char *modifier;
13262306a36Sopenharmony_ci	/** The expression to parse, for example, "instructions/cycles". */
13362306a36Sopenharmony_ci	const char *metric_expr;
13462306a36Sopenharmony_ci	/** Optional threshold expression where zero value is green, otherwise red. */
13562306a36Sopenharmony_ci	const char *metric_threshold;
13662306a36Sopenharmony_ci	/**
13762306a36Sopenharmony_ci	 * The "ScaleUnit" that scales and adds a unit to the metric during
13862306a36Sopenharmony_ci	 * output.
13962306a36Sopenharmony_ci	 */
14062306a36Sopenharmony_ci	const char *metric_unit;
14162306a36Sopenharmony_ci	/**
14262306a36Sopenharmony_ci	 * Optional name of the metric group reported
14362306a36Sopenharmony_ci	 * if the Default metric group is being processed.
14462306a36Sopenharmony_ci	 */
14562306a36Sopenharmony_ci	const char *default_metricgroup_name;
14662306a36Sopenharmony_ci	/** Optional null terminated array of referenced metrics. */
14762306a36Sopenharmony_ci	struct metric_ref *metric_refs;
14862306a36Sopenharmony_ci	/**
14962306a36Sopenharmony_ci	 * Should events of the metric be grouped?
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	bool group_events;
15262306a36Sopenharmony_ci	/**
15362306a36Sopenharmony_ci	 * Parsed events for the metric. Optional as events may be taken from a
15462306a36Sopenharmony_ci	 * different metric whose group contains all the IDs necessary for this
15562306a36Sopenharmony_ci	 * one.
15662306a36Sopenharmony_ci	 */
15762306a36Sopenharmony_ci	struct evlist *evlist;
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic void metric__watchdog_constraint_hint(const char *name, bool foot)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	static bool violate_nmi_constraint;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!foot) {
16562306a36Sopenharmony_ci		pr_warning("Not grouping metric %s's events.\n", name);
16662306a36Sopenharmony_ci		violate_nmi_constraint = true;
16762306a36Sopenharmony_ci		return;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!violate_nmi_constraint)
17162306a36Sopenharmony_ci		return;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
17462306a36Sopenharmony_ci		   "    echo 0 > /proc/sys/kernel/nmi_watchdog\n"
17562306a36Sopenharmony_ci		   "    perf stat ...\n"
17662306a36Sopenharmony_ci		   "    echo 1 > /proc/sys/kernel/nmi_watchdog\n");
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic bool metric__group_events(const struct pmu_metric *pm)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	switch (pm->event_grouping) {
18262306a36Sopenharmony_ci	case MetricNoGroupEvents:
18362306a36Sopenharmony_ci		return false;
18462306a36Sopenharmony_ci	case MetricNoGroupEventsNmi:
18562306a36Sopenharmony_ci		if (!sysctl__nmi_watchdog_enabled())
18662306a36Sopenharmony_ci			return true;
18762306a36Sopenharmony_ci		metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false);
18862306a36Sopenharmony_ci		return false;
18962306a36Sopenharmony_ci	case MetricNoGroupEventsSmt:
19062306a36Sopenharmony_ci		return !smt_on();
19162306a36Sopenharmony_ci	case MetricGroupEvents:
19262306a36Sopenharmony_ci	default:
19362306a36Sopenharmony_ci		return true;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void metric__free(struct metric *m)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	if (!m)
20062306a36Sopenharmony_ci		return;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	zfree(&m->metric_refs);
20362306a36Sopenharmony_ci	expr__ctx_free(m->pctx);
20462306a36Sopenharmony_ci	zfree(&m->modifier);
20562306a36Sopenharmony_ci	evlist__delete(m->evlist);
20662306a36Sopenharmony_ci	free(m);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic struct metric *metric__new(const struct pmu_metric *pm,
21062306a36Sopenharmony_ci				  const char *modifier,
21162306a36Sopenharmony_ci				  bool metric_no_group,
21262306a36Sopenharmony_ci				  int runtime,
21362306a36Sopenharmony_ci				  const char *user_requested_cpu_list,
21462306a36Sopenharmony_ci				  bool system_wide)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct metric *m;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	m = zalloc(sizeof(*m));
21962306a36Sopenharmony_ci	if (!m)
22062306a36Sopenharmony_ci		return NULL;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	m->pctx = expr__ctx_new();
22362306a36Sopenharmony_ci	if (!m->pctx)
22462306a36Sopenharmony_ci		goto out_err;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	m->pmu = pm->pmu ?: "cpu";
22762306a36Sopenharmony_ci	m->metric_name = pm->metric_name;
22862306a36Sopenharmony_ci	m->default_metricgroup_name = pm->default_metricgroup_name ?: "";
22962306a36Sopenharmony_ci	m->modifier = NULL;
23062306a36Sopenharmony_ci	if (modifier) {
23162306a36Sopenharmony_ci		m->modifier = strdup(modifier);
23262306a36Sopenharmony_ci		if (!m->modifier)
23362306a36Sopenharmony_ci			goto out_err;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci	m->metric_expr = pm->metric_expr;
23662306a36Sopenharmony_ci	m->metric_threshold = pm->metric_threshold;
23762306a36Sopenharmony_ci	m->metric_unit = pm->unit;
23862306a36Sopenharmony_ci	m->pctx->sctx.user_requested_cpu_list = NULL;
23962306a36Sopenharmony_ci	if (user_requested_cpu_list) {
24062306a36Sopenharmony_ci		m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list);
24162306a36Sopenharmony_ci		if (!m->pctx->sctx.user_requested_cpu_list)
24262306a36Sopenharmony_ci			goto out_err;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	m->pctx->sctx.runtime = runtime;
24562306a36Sopenharmony_ci	m->pctx->sctx.system_wide = system_wide;
24662306a36Sopenharmony_ci	m->group_events = !metric_no_group && metric__group_events(pm);
24762306a36Sopenharmony_ci	m->metric_refs = NULL;
24862306a36Sopenharmony_ci	m->evlist = NULL;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return m;
25162306a36Sopenharmony_ciout_err:
25262306a36Sopenharmony_ci	metric__free(m);
25362306a36Sopenharmony_ci	return NULL;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic bool contains_metric_id(struct evsel **metric_events, int num_events,
25762306a36Sopenharmony_ci			       const char *metric_id)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	int i;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	for (i = 0; i < num_events; i++) {
26262306a36Sopenharmony_ci		if (!strcmp(evsel__metric_id(metric_events[i]), metric_id))
26362306a36Sopenharmony_ci			return true;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci	return false;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/**
26962306a36Sopenharmony_ci * setup_metric_events - Find a group of events in metric_evlist that correspond
27062306a36Sopenharmony_ci *                       to the IDs from a parsed metric expression.
27162306a36Sopenharmony_ci * @pmu: The PMU for the IDs.
27262306a36Sopenharmony_ci * @ids: the metric IDs to match.
27362306a36Sopenharmony_ci * @metric_evlist: the list of perf events.
27462306a36Sopenharmony_ci * @out_metric_events: holds the created metric events array.
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_cistatic int setup_metric_events(const char *pmu, struct hashmap *ids,
27762306a36Sopenharmony_ci			       struct evlist *metric_evlist,
27862306a36Sopenharmony_ci			       struct evsel ***out_metric_events)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct evsel **metric_events;
28162306a36Sopenharmony_ci	const char *metric_id;
28262306a36Sopenharmony_ci	struct evsel *ev;
28362306a36Sopenharmony_ci	size_t ids_size, matched_events, i;
28462306a36Sopenharmony_ci	bool all_pmus = !strcmp(pmu, "all") || perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	*out_metric_events = NULL;
28762306a36Sopenharmony_ci	ids_size = hashmap__size(ids);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	metric_events = calloc(sizeof(void *), ids_size + 1);
29062306a36Sopenharmony_ci	if (!metric_events)
29162306a36Sopenharmony_ci		return -ENOMEM;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	matched_events = 0;
29462306a36Sopenharmony_ci	evlist__for_each_entry(metric_evlist, ev) {
29562306a36Sopenharmony_ci		struct expr_id_data *val_ptr;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		/* Don't match events for the wrong hybrid PMU. */
29862306a36Sopenharmony_ci		if (!all_pmus && ev->pmu_name && evsel__is_hybrid(ev) &&
29962306a36Sopenharmony_ci		    strcmp(ev->pmu_name, pmu))
30062306a36Sopenharmony_ci			continue;
30162306a36Sopenharmony_ci		/*
30262306a36Sopenharmony_ci		 * Check for duplicate events with the same name. For
30362306a36Sopenharmony_ci		 * example, uncore_imc/cas_count_read/ will turn into 6
30462306a36Sopenharmony_ci		 * events per socket on skylakex. Only the first such
30562306a36Sopenharmony_ci		 * event is placed in metric_events.
30662306a36Sopenharmony_ci		 */
30762306a36Sopenharmony_ci		metric_id = evsel__metric_id(ev);
30862306a36Sopenharmony_ci		if (contains_metric_id(metric_events, matched_events, metric_id))
30962306a36Sopenharmony_ci			continue;
31062306a36Sopenharmony_ci		/*
31162306a36Sopenharmony_ci		 * Does this event belong to the parse context? For
31262306a36Sopenharmony_ci		 * combined or shared groups, this metric may not care
31362306a36Sopenharmony_ci		 * about this event.
31462306a36Sopenharmony_ci		 */
31562306a36Sopenharmony_ci		if (hashmap__find(ids, metric_id, &val_ptr)) {
31662306a36Sopenharmony_ci			pr_debug("Matched metric-id %s to %s\n", metric_id, evsel__name(ev));
31762306a36Sopenharmony_ci			metric_events[matched_events++] = ev;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci			if (matched_events >= ids_size)
32062306a36Sopenharmony_ci				break;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	if (matched_events < ids_size) {
32462306a36Sopenharmony_ci		free(metric_events);
32562306a36Sopenharmony_ci		return -EINVAL;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci	for (i = 0; i < ids_size; i++) {
32862306a36Sopenharmony_ci		ev = metric_events[i];
32962306a36Sopenharmony_ci		ev->collect_stat = true;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		/*
33262306a36Sopenharmony_ci		 * The metric leader points to the identically named
33362306a36Sopenharmony_ci		 * event in metric_events.
33462306a36Sopenharmony_ci		 */
33562306a36Sopenharmony_ci		ev->metric_leader = ev;
33662306a36Sopenharmony_ci		/*
33762306a36Sopenharmony_ci		 * Mark two events with identical names in the same
33862306a36Sopenharmony_ci		 * group (or globally) as being in use as uncore events
33962306a36Sopenharmony_ci		 * may be duplicated for each pmu. Set the metric leader
34062306a36Sopenharmony_ci		 * of such events to be the event that appears in
34162306a36Sopenharmony_ci		 * metric_events.
34262306a36Sopenharmony_ci		 */
34362306a36Sopenharmony_ci		metric_id = evsel__metric_id(ev);
34462306a36Sopenharmony_ci		evlist__for_each_entry_continue(metric_evlist, ev) {
34562306a36Sopenharmony_ci			if (!strcmp(evsel__metric_id(ev), metric_id))
34662306a36Sopenharmony_ci				ev->metric_leader = metric_events[i];
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	*out_metric_events = metric_events;
35062306a36Sopenharmony_ci	return 0;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic bool match_metric(const char *n, const char *list)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	int len;
35662306a36Sopenharmony_ci	char *m;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (!list)
35962306a36Sopenharmony_ci		return false;
36062306a36Sopenharmony_ci	if (!strcmp(list, "all"))
36162306a36Sopenharmony_ci		return true;
36262306a36Sopenharmony_ci	if (!n)
36362306a36Sopenharmony_ci		return !strcasecmp(list, "No_group");
36462306a36Sopenharmony_ci	len = strlen(list);
36562306a36Sopenharmony_ci	m = strcasestr(n, list);
36662306a36Sopenharmony_ci	if (!m)
36762306a36Sopenharmony_ci		return false;
36862306a36Sopenharmony_ci	if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
36962306a36Sopenharmony_ci	    (m[len] == 0 || m[len] == ';'))
37062306a36Sopenharmony_ci		return true;
37162306a36Sopenharmony_ci	return false;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic bool match_pm_metric(const struct pmu_metric *pm, const char *pmu, const char *metric)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	const char *pm_pmu = pm->pmu ?: "cpu";
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (strcmp(pmu, "all") && strcmp(pm_pmu, pmu))
37962306a36Sopenharmony_ci		return false;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return match_metric(pm->metric_group, metric) ||
38262306a36Sopenharmony_ci	       match_metric(pm->metric_name, metric);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci/** struct mep - RB-tree node for building printing information. */
38662306a36Sopenharmony_cistruct mep {
38762306a36Sopenharmony_ci	/** nd - RB-tree element. */
38862306a36Sopenharmony_ci	struct rb_node nd;
38962306a36Sopenharmony_ci	/** @metric_group: Owned metric group name, separated others with ';'. */
39062306a36Sopenharmony_ci	char *metric_group;
39162306a36Sopenharmony_ci	const char *metric_name;
39262306a36Sopenharmony_ci	const char *metric_desc;
39362306a36Sopenharmony_ci	const char *metric_long_desc;
39462306a36Sopenharmony_ci	const char *metric_expr;
39562306a36Sopenharmony_ci	const char *metric_threshold;
39662306a36Sopenharmony_ci	const char *metric_unit;
39762306a36Sopenharmony_ci};
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int mep_cmp(struct rb_node *rb_node, const void *entry)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct mep *a = container_of(rb_node, struct mep, nd);
40262306a36Sopenharmony_ci	struct mep *b = (struct mep *)entry;
40362306a36Sopenharmony_ci	int ret;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	ret = strcmp(a->metric_group, b->metric_group);
40662306a36Sopenharmony_ci	if (ret)
40762306a36Sopenharmony_ci		return ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return strcmp(a->metric_name, b->metric_name);
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct mep *me = malloc(sizeof(struct mep));
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (!me)
41762306a36Sopenharmony_ci		return NULL;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	memcpy(me, entry, sizeof(struct mep));
42062306a36Sopenharmony_ci	return &me->nd;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic void mep_delete(struct rblist *rl __maybe_unused,
42462306a36Sopenharmony_ci		       struct rb_node *nd)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct mep *me = container_of(nd, struct mep, nd);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	zfree(&me->metric_group);
42962306a36Sopenharmony_ci	free(me);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
43362306a36Sopenharmony_ci			      const char *metric_name)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct rb_node *nd;
43662306a36Sopenharmony_ci	struct mep me = {
43762306a36Sopenharmony_ci		.metric_group = strdup(metric_group),
43862306a36Sopenharmony_ci		.metric_name = metric_name,
43962306a36Sopenharmony_ci	};
44062306a36Sopenharmony_ci	nd = rblist__find(groups, &me);
44162306a36Sopenharmony_ci	if (nd) {
44262306a36Sopenharmony_ci		free(me.metric_group);
44362306a36Sopenharmony_ci		return container_of(nd, struct mep, nd);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci	rblist__add_node(groups, &me);
44662306a36Sopenharmony_ci	nd = rblist__find(groups, &me);
44762306a36Sopenharmony_ci	if (nd)
44862306a36Sopenharmony_ci		return container_of(nd, struct mep, nd);
44962306a36Sopenharmony_ci	return NULL;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int metricgroup__add_to_mep_groups(const struct pmu_metric *pm,
45362306a36Sopenharmony_ci					struct rblist *groups)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	const char *g;
45662306a36Sopenharmony_ci	char *omg, *mg;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	mg = strdup(pm->metric_group ?: "No_group");
45962306a36Sopenharmony_ci	if (!mg)
46062306a36Sopenharmony_ci		return -ENOMEM;
46162306a36Sopenharmony_ci	omg = mg;
46262306a36Sopenharmony_ci	while ((g = strsep(&mg, ";")) != NULL) {
46362306a36Sopenharmony_ci		struct mep *me;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		g = skip_spaces(g);
46662306a36Sopenharmony_ci		if (strlen(g))
46762306a36Sopenharmony_ci			me = mep_lookup(groups, g, pm->metric_name);
46862306a36Sopenharmony_ci		else
46962306a36Sopenharmony_ci			me = mep_lookup(groups, "No_group", pm->metric_name);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		if (me) {
47262306a36Sopenharmony_ci			me->metric_desc = pm->desc;
47362306a36Sopenharmony_ci			me->metric_long_desc = pm->long_desc;
47462306a36Sopenharmony_ci			me->metric_expr = pm->metric_expr;
47562306a36Sopenharmony_ci			me->metric_threshold = pm->metric_threshold;
47662306a36Sopenharmony_ci			me->metric_unit = pm->unit;
47762306a36Sopenharmony_ci		}
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	free(omg);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistruct metricgroup_iter_data {
48562306a36Sopenharmony_ci	pmu_metric_iter_fn fn;
48662306a36Sopenharmony_ci	void *data;
48762306a36Sopenharmony_ci};
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int metricgroup__sys_event_iter(const struct pmu_metric *pm,
49062306a36Sopenharmony_ci				       const struct pmu_metrics_table *table,
49162306a36Sopenharmony_ci				       void *data)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct metricgroup_iter_data *d = data;
49462306a36Sopenharmony_ci	struct perf_pmu *pmu = NULL;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (!pm->metric_expr || !pm->compat)
49762306a36Sopenharmony_ci		return 0;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	while ((pmu = perf_pmus__scan(pmu))) {
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		if (!pmu->id || strcmp(pmu->id, pm->compat))
50262306a36Sopenharmony_ci			continue;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		return d->fn(pm, table, d->data);
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci	return 0;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm,
51062306a36Sopenharmony_ci					const struct pmu_metrics_table *table __maybe_unused,
51162306a36Sopenharmony_ci					void *vdata)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct rblist *groups = vdata;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return metricgroup__add_to_mep_groups(pm, groups);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_civoid metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct rblist groups;
52162306a36Sopenharmony_ci	const struct pmu_metrics_table *table;
52262306a36Sopenharmony_ci	struct rb_node *node, *next;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	rblist__init(&groups);
52562306a36Sopenharmony_ci	groups.node_new = mep_new;
52662306a36Sopenharmony_ci	groups.node_cmp = mep_cmp;
52762306a36Sopenharmony_ci	groups.node_delete = mep_delete;
52862306a36Sopenharmony_ci	table = pmu_metrics_table__find();
52962306a36Sopenharmony_ci	if (table) {
53062306a36Sopenharmony_ci		pmu_metrics_table__for_each_metric(table,
53162306a36Sopenharmony_ci						 metricgroup__add_to_mep_groups_callback,
53262306a36Sopenharmony_ci						 &groups);
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci	{
53562306a36Sopenharmony_ci		struct metricgroup_iter_data data = {
53662306a36Sopenharmony_ci			.fn = metricgroup__add_to_mep_groups_callback,
53762306a36Sopenharmony_ci			.data = &groups,
53862306a36Sopenharmony_ci		};
53962306a36Sopenharmony_ci		pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	for (node = rb_first_cached(&groups.entries); node; node = next) {
54362306a36Sopenharmony_ci		struct mep *me = container_of(node, struct mep, nd);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		print_cb->print_metric(print_state,
54662306a36Sopenharmony_ci				me->metric_group,
54762306a36Sopenharmony_ci				me->metric_name,
54862306a36Sopenharmony_ci				me->metric_desc,
54962306a36Sopenharmony_ci				me->metric_long_desc,
55062306a36Sopenharmony_ci				me->metric_expr,
55162306a36Sopenharmony_ci				me->metric_threshold,
55262306a36Sopenharmony_ci				me->metric_unit);
55362306a36Sopenharmony_ci		next = rb_next(node);
55462306a36Sopenharmony_ci		rblist__remove_node(&groups, node);
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic const char *code_characters = ",-=@";
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic int encode_metric_id(struct strbuf *sb, const char *x)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	char *c;
56362306a36Sopenharmony_ci	int ret = 0;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	for (; *x; x++) {
56662306a36Sopenharmony_ci		c = strchr(code_characters, *x);
56762306a36Sopenharmony_ci		if (c) {
56862306a36Sopenharmony_ci			ret = strbuf_addch(sb, '!');
56962306a36Sopenharmony_ci			if (ret)
57062306a36Sopenharmony_ci				break;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci			ret = strbuf_addch(sb, '0' + (c - code_characters));
57362306a36Sopenharmony_ci			if (ret)
57462306a36Sopenharmony_ci				break;
57562306a36Sopenharmony_ci		} else {
57662306a36Sopenharmony_ci			ret = strbuf_addch(sb, *x);
57762306a36Sopenharmony_ci			if (ret)
57862306a36Sopenharmony_ci				break;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	return ret;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int decode_metric_id(struct strbuf *sb, const char *x)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	const char *orig = x;
58762306a36Sopenharmony_ci	size_t i;
58862306a36Sopenharmony_ci	char c;
58962306a36Sopenharmony_ci	int ret;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	for (; *x; x++) {
59262306a36Sopenharmony_ci		c = *x;
59362306a36Sopenharmony_ci		if (*x == '!') {
59462306a36Sopenharmony_ci			x++;
59562306a36Sopenharmony_ci			i = *x - '0';
59662306a36Sopenharmony_ci			if (i > strlen(code_characters)) {
59762306a36Sopenharmony_ci				pr_err("Bad metric-id encoding in: '%s'", orig);
59862306a36Sopenharmony_ci				return -1;
59962306a36Sopenharmony_ci			}
60062306a36Sopenharmony_ci			c = code_characters[i];
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci		ret = strbuf_addch(sb, c);
60362306a36Sopenharmony_ci		if (ret)
60462306a36Sopenharmony_ci			return ret;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct evsel *ev;
61262306a36Sopenharmony_ci	struct strbuf sb = STRBUF_INIT;
61362306a36Sopenharmony_ci	char *cur;
61462306a36Sopenharmony_ci	int ret = 0;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	evlist__for_each_entry(perf_evlist, ev) {
61762306a36Sopenharmony_ci		if (!ev->metric_id)
61862306a36Sopenharmony_ci			continue;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		ret = strbuf_setlen(&sb, 0);
62162306a36Sopenharmony_ci		if (ret)
62262306a36Sopenharmony_ci			break;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		ret = decode_metric_id(&sb, ev->metric_id);
62562306a36Sopenharmony_ci		if (ret)
62662306a36Sopenharmony_ci			break;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		free((char *)ev->metric_id);
62962306a36Sopenharmony_ci		ev->metric_id = strdup(sb.buf);
63062306a36Sopenharmony_ci		if (!ev->metric_id) {
63162306a36Sopenharmony_ci			ret = -ENOMEM;
63262306a36Sopenharmony_ci			break;
63362306a36Sopenharmony_ci		}
63462306a36Sopenharmony_ci		/*
63562306a36Sopenharmony_ci		 * If the name is just the parsed event, use the metric-id to
63662306a36Sopenharmony_ci		 * give a more friendly display version.
63762306a36Sopenharmony_ci		 */
63862306a36Sopenharmony_ci		if (strstr(ev->name, "metric-id=")) {
63962306a36Sopenharmony_ci			bool has_slash = false;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci			zfree(&ev->name);
64262306a36Sopenharmony_ci			for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) {
64362306a36Sopenharmony_ci				*cur = '/';
64462306a36Sopenharmony_ci				has_slash = true;
64562306a36Sopenharmony_ci			}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci			if (modifier) {
64862306a36Sopenharmony_ci				if (!has_slash && !strchr(sb.buf, ':')) {
64962306a36Sopenharmony_ci					ret = strbuf_addch(&sb, ':');
65062306a36Sopenharmony_ci					if (ret)
65162306a36Sopenharmony_ci						break;
65262306a36Sopenharmony_ci				}
65362306a36Sopenharmony_ci				ret = strbuf_addstr(&sb, modifier);
65462306a36Sopenharmony_ci				if (ret)
65562306a36Sopenharmony_ci					break;
65662306a36Sopenharmony_ci			}
65762306a36Sopenharmony_ci			ev->name = strdup(sb.buf);
65862306a36Sopenharmony_ci			if (!ev->name) {
65962306a36Sopenharmony_ci				ret = -ENOMEM;
66062306a36Sopenharmony_ci				break;
66162306a36Sopenharmony_ci			}
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci	strbuf_release(&sb);
66562306a36Sopenharmony_ci	return ret;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic int metricgroup__build_event_string(struct strbuf *events,
66962306a36Sopenharmony_ci					   const struct expr_parse_ctx *ctx,
67062306a36Sopenharmony_ci					   const char *modifier,
67162306a36Sopenharmony_ci					   bool group_events)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	struct hashmap_entry *cur;
67462306a36Sopenharmony_ci	size_t bkt;
67562306a36Sopenharmony_ci	bool no_group = true, has_tool_events = false;
67662306a36Sopenharmony_ci	bool tool_events[PERF_TOOL_MAX] = {false};
67762306a36Sopenharmony_ci	int ret = 0;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci#define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	hashmap__for_each_entry(ctx->ids, cur, bkt) {
68262306a36Sopenharmony_ci		const char *sep, *rsep, *id = cur->pkey;
68362306a36Sopenharmony_ci		enum perf_tool_event ev;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		pr_debug("found event %s\n", id);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci		/* Always move tool events outside of the group. */
68862306a36Sopenharmony_ci		ev = perf_tool_event__from_str(id);
68962306a36Sopenharmony_ci		if (ev != PERF_TOOL_NONE) {
69062306a36Sopenharmony_ci			has_tool_events = true;
69162306a36Sopenharmony_ci			tool_events[ev] = true;
69262306a36Sopenharmony_ci			continue;
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci		/* Separate events with commas and open the group if necessary. */
69562306a36Sopenharmony_ci		if (no_group) {
69662306a36Sopenharmony_ci			if (group_events) {
69762306a36Sopenharmony_ci				ret = strbuf_addch(events, '{');
69862306a36Sopenharmony_ci				RETURN_IF_NON_ZERO(ret);
69962306a36Sopenharmony_ci			}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci			no_group = false;
70262306a36Sopenharmony_ci		} else {
70362306a36Sopenharmony_ci			ret = strbuf_addch(events, ',');
70462306a36Sopenharmony_ci			RETURN_IF_NON_ZERO(ret);
70562306a36Sopenharmony_ci		}
70662306a36Sopenharmony_ci		/*
70762306a36Sopenharmony_ci		 * Encode the ID as an event string. Add a qualifier for
70862306a36Sopenharmony_ci		 * metric_id that is the original name except with characters
70962306a36Sopenharmony_ci		 * that parse-events can't parse replaced. For example,
71062306a36Sopenharmony_ci		 * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/
71162306a36Sopenharmony_ci		 */
71262306a36Sopenharmony_ci		sep = strchr(id, '@');
71362306a36Sopenharmony_ci		if (sep != NULL) {
71462306a36Sopenharmony_ci			ret = strbuf_add(events, id, sep - id);
71562306a36Sopenharmony_ci			RETURN_IF_NON_ZERO(ret);
71662306a36Sopenharmony_ci			ret = strbuf_addch(events, '/');
71762306a36Sopenharmony_ci			RETURN_IF_NON_ZERO(ret);
71862306a36Sopenharmony_ci			rsep = strrchr(sep, '@');
71962306a36Sopenharmony_ci			ret = strbuf_add(events, sep + 1, rsep - sep - 1);
72062306a36Sopenharmony_ci			RETURN_IF_NON_ZERO(ret);
72162306a36Sopenharmony_ci			ret = strbuf_addstr(events, ",metric-id=");
72262306a36Sopenharmony_ci			RETURN_IF_NON_ZERO(ret);
72362306a36Sopenharmony_ci			sep = rsep;
72462306a36Sopenharmony_ci		} else {
72562306a36Sopenharmony_ci			sep = strchr(id, ':');
72662306a36Sopenharmony_ci			if (sep != NULL) {
72762306a36Sopenharmony_ci				ret = strbuf_add(events, id, sep - id);
72862306a36Sopenharmony_ci				RETURN_IF_NON_ZERO(ret);
72962306a36Sopenharmony_ci			} else {
73062306a36Sopenharmony_ci				ret = strbuf_addstr(events, id);
73162306a36Sopenharmony_ci				RETURN_IF_NON_ZERO(ret);
73262306a36Sopenharmony_ci			}
73362306a36Sopenharmony_ci			ret = strbuf_addstr(events, "/metric-id=");
73462306a36Sopenharmony_ci			RETURN_IF_NON_ZERO(ret);
73562306a36Sopenharmony_ci		}
73662306a36Sopenharmony_ci		ret = encode_metric_id(events, id);
73762306a36Sopenharmony_ci		RETURN_IF_NON_ZERO(ret);
73862306a36Sopenharmony_ci		ret = strbuf_addstr(events, "/");
73962306a36Sopenharmony_ci		RETURN_IF_NON_ZERO(ret);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		if (sep != NULL) {
74262306a36Sopenharmony_ci			ret = strbuf_addstr(events, sep + 1);
74362306a36Sopenharmony_ci			RETURN_IF_NON_ZERO(ret);
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci		if (modifier) {
74662306a36Sopenharmony_ci			ret = strbuf_addstr(events, modifier);
74762306a36Sopenharmony_ci			RETURN_IF_NON_ZERO(ret);
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci	if (!no_group && group_events) {
75162306a36Sopenharmony_ci		ret = strbuf_addf(events, "}:W");
75262306a36Sopenharmony_ci		RETURN_IF_NON_ZERO(ret);
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci	if (has_tool_events) {
75562306a36Sopenharmony_ci		int i;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		perf_tool_event__for_each_event(i) {
75862306a36Sopenharmony_ci			if (tool_events[i]) {
75962306a36Sopenharmony_ci				if (!no_group) {
76062306a36Sopenharmony_ci					ret = strbuf_addch(events, ',');
76162306a36Sopenharmony_ci					RETURN_IF_NON_ZERO(ret);
76262306a36Sopenharmony_ci				}
76362306a36Sopenharmony_ci				no_group = false;
76462306a36Sopenharmony_ci				ret = strbuf_addstr(events, perf_tool_event__to_str(i));
76562306a36Sopenharmony_ci				RETURN_IF_NON_ZERO(ret);
76662306a36Sopenharmony_ci			}
76762306a36Sopenharmony_ci		}
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	return ret;
77162306a36Sopenharmony_ci#undef RETURN_IF_NON_ZERO
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ciint __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	return 1;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci/*
78062306a36Sopenharmony_ci * A singly linked list on the stack of the names of metrics being
78162306a36Sopenharmony_ci * processed. Used to identify recursion.
78262306a36Sopenharmony_ci */
78362306a36Sopenharmony_cistruct visited_metric {
78462306a36Sopenharmony_ci	const char *name;
78562306a36Sopenharmony_ci	const struct visited_metric *parent;
78662306a36Sopenharmony_ci};
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistruct metricgroup_add_iter_data {
78962306a36Sopenharmony_ci	struct list_head *metric_list;
79062306a36Sopenharmony_ci	const char *pmu;
79162306a36Sopenharmony_ci	const char *metric_name;
79262306a36Sopenharmony_ci	const char *modifier;
79362306a36Sopenharmony_ci	int *ret;
79462306a36Sopenharmony_ci	bool *has_match;
79562306a36Sopenharmony_ci	bool metric_no_group;
79662306a36Sopenharmony_ci	bool metric_no_threshold;
79762306a36Sopenharmony_ci	const char *user_requested_cpu_list;
79862306a36Sopenharmony_ci	bool system_wide;
79962306a36Sopenharmony_ci	struct metric *root_metric;
80062306a36Sopenharmony_ci	const struct visited_metric *visited;
80162306a36Sopenharmony_ci	const struct pmu_metrics_table *table;
80262306a36Sopenharmony_ci};
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic bool metricgroup__find_metric(const char *pmu,
80562306a36Sopenharmony_ci				     const char *metric,
80662306a36Sopenharmony_ci				     const struct pmu_metrics_table *table,
80762306a36Sopenharmony_ci				     struct pmu_metric *pm);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic int add_metric(struct list_head *metric_list,
81062306a36Sopenharmony_ci		      const struct pmu_metric *pm,
81162306a36Sopenharmony_ci		      const char *modifier,
81262306a36Sopenharmony_ci		      bool metric_no_group,
81362306a36Sopenharmony_ci		      bool metric_no_threshold,
81462306a36Sopenharmony_ci		      const char *user_requested_cpu_list,
81562306a36Sopenharmony_ci		      bool system_wide,
81662306a36Sopenharmony_ci		      struct metric *root_metric,
81762306a36Sopenharmony_ci		      const struct visited_metric *visited,
81862306a36Sopenharmony_ci		      const struct pmu_metrics_table *table);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci/**
82162306a36Sopenharmony_ci * resolve_metric - Locate metrics within the root metric and recursively add
82262306a36Sopenharmony_ci *                    references to them.
82362306a36Sopenharmony_ci * @metric_list: The list the metric is added to.
82462306a36Sopenharmony_ci * @pmu: The PMU name to resolve metrics on, or "all" for all PMUs.
82562306a36Sopenharmony_ci * @modifier: if non-null event modifiers like "u".
82662306a36Sopenharmony_ci * @metric_no_group: Should events written to events be grouped "{}" or
82762306a36Sopenharmony_ci *                   global. Grouping is the default but due to multiplexing the
82862306a36Sopenharmony_ci *                   user may override.
82962306a36Sopenharmony_ci * @user_requested_cpu_list: Command line specified CPUs to record on.
83062306a36Sopenharmony_ci * @system_wide: Are events for all processes recorded.
83162306a36Sopenharmony_ci * @root_metric: Metrics may reference other metrics to form a tree. In this
83262306a36Sopenharmony_ci *               case the root_metric holds all the IDs and a list of referenced
83362306a36Sopenharmony_ci *               metrics. When adding a root this argument is NULL.
83462306a36Sopenharmony_ci * @visited: A singly linked list of metric names being added that is used to
83562306a36Sopenharmony_ci *           detect recursion.
83662306a36Sopenharmony_ci * @table: The table that is searched for metrics, most commonly the table for the
83762306a36Sopenharmony_ci *       architecture perf is running upon.
83862306a36Sopenharmony_ci */
83962306a36Sopenharmony_cistatic int resolve_metric(struct list_head *metric_list,
84062306a36Sopenharmony_ci			  const char *pmu,
84162306a36Sopenharmony_ci			  const char *modifier,
84262306a36Sopenharmony_ci			  bool metric_no_group,
84362306a36Sopenharmony_ci			  bool metric_no_threshold,
84462306a36Sopenharmony_ci			  const char *user_requested_cpu_list,
84562306a36Sopenharmony_ci			  bool system_wide,
84662306a36Sopenharmony_ci			  struct metric *root_metric,
84762306a36Sopenharmony_ci			  const struct visited_metric *visited,
84862306a36Sopenharmony_ci			  const struct pmu_metrics_table *table)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	struct hashmap_entry *cur;
85162306a36Sopenharmony_ci	size_t bkt;
85262306a36Sopenharmony_ci	struct to_resolve {
85362306a36Sopenharmony_ci		/* The metric to resolve. */
85462306a36Sopenharmony_ci		struct pmu_metric pm;
85562306a36Sopenharmony_ci		/*
85662306a36Sopenharmony_ci		 * The key in the IDs map, this may differ from in case,
85762306a36Sopenharmony_ci		 * etc. from pm->metric_name.
85862306a36Sopenharmony_ci		 */
85962306a36Sopenharmony_ci		const char *key;
86062306a36Sopenharmony_ci	} *pending = NULL;
86162306a36Sopenharmony_ci	int i, ret = 0, pending_cnt = 0;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/*
86462306a36Sopenharmony_ci	 * Iterate all the parsed IDs and if there's a matching metric and it to
86562306a36Sopenharmony_ci	 * the pending array.
86662306a36Sopenharmony_ci	 */
86762306a36Sopenharmony_ci	hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
86862306a36Sopenharmony_ci		struct pmu_metric pm;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		if (metricgroup__find_metric(pmu, cur->pkey, table, &pm)) {
87162306a36Sopenharmony_ci			pending = realloc(pending,
87262306a36Sopenharmony_ci					(pending_cnt + 1) * sizeof(struct to_resolve));
87362306a36Sopenharmony_ci			if (!pending)
87462306a36Sopenharmony_ci				return -ENOMEM;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci			memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm));
87762306a36Sopenharmony_ci			pending[pending_cnt].key = cur->pkey;
87862306a36Sopenharmony_ci			pending_cnt++;
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* Remove the metric IDs from the context. */
88362306a36Sopenharmony_ci	for (i = 0; i < pending_cnt; i++)
88462306a36Sopenharmony_ci		expr__del_id(root_metric->pctx, pending[i].key);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	/*
88762306a36Sopenharmony_ci	 * Recursively add all the metrics, IDs are added to the root metric's
88862306a36Sopenharmony_ci	 * context.
88962306a36Sopenharmony_ci	 */
89062306a36Sopenharmony_ci	for (i = 0; i < pending_cnt; i++) {
89162306a36Sopenharmony_ci		ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group,
89262306a36Sopenharmony_ci				 metric_no_threshold, user_requested_cpu_list, system_wide,
89362306a36Sopenharmony_ci				 root_metric, visited, table);
89462306a36Sopenharmony_ci		if (ret)
89562306a36Sopenharmony_ci			break;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	free(pending);
89962306a36Sopenharmony_ci	return ret;
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci/**
90362306a36Sopenharmony_ci * __add_metric - Add a metric to metric_list.
90462306a36Sopenharmony_ci * @metric_list: The list the metric is added to.
90562306a36Sopenharmony_ci * @pm: The pmu_metric containing the metric to be added.
90662306a36Sopenharmony_ci * @modifier: if non-null event modifiers like "u".
90762306a36Sopenharmony_ci * @metric_no_group: Should events written to events be grouped "{}" or
90862306a36Sopenharmony_ci *                   global. Grouping is the default but due to multiplexing the
90962306a36Sopenharmony_ci *                   user may override.
91062306a36Sopenharmony_ci * @metric_no_threshold: Should threshold expressions be ignored?
91162306a36Sopenharmony_ci * @runtime: A special argument for the parser only known at runtime.
91262306a36Sopenharmony_ci * @user_requested_cpu_list: Command line specified CPUs to record on.
91362306a36Sopenharmony_ci * @system_wide: Are events for all processes recorded.
91462306a36Sopenharmony_ci * @root_metric: Metrics may reference other metrics to form a tree. In this
91562306a36Sopenharmony_ci *               case the root_metric holds all the IDs and a list of referenced
91662306a36Sopenharmony_ci *               metrics. When adding a root this argument is NULL.
91762306a36Sopenharmony_ci * @visited: A singly linked list of metric names being added that is used to
91862306a36Sopenharmony_ci *           detect recursion.
91962306a36Sopenharmony_ci * @table: The table that is searched for metrics, most commonly the table for the
92062306a36Sopenharmony_ci *       architecture perf is running upon.
92162306a36Sopenharmony_ci */
92262306a36Sopenharmony_cistatic int __add_metric(struct list_head *metric_list,
92362306a36Sopenharmony_ci			const struct pmu_metric *pm,
92462306a36Sopenharmony_ci			const char *modifier,
92562306a36Sopenharmony_ci			bool metric_no_group,
92662306a36Sopenharmony_ci			bool metric_no_threshold,
92762306a36Sopenharmony_ci			int runtime,
92862306a36Sopenharmony_ci			const char *user_requested_cpu_list,
92962306a36Sopenharmony_ci			bool system_wide,
93062306a36Sopenharmony_ci			struct metric *root_metric,
93162306a36Sopenharmony_ci			const struct visited_metric *visited,
93262306a36Sopenharmony_ci			const struct pmu_metrics_table *table)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	const struct visited_metric *vm;
93562306a36Sopenharmony_ci	int ret;
93662306a36Sopenharmony_ci	bool is_root = !root_metric;
93762306a36Sopenharmony_ci	const char *expr;
93862306a36Sopenharmony_ci	struct visited_metric visited_node = {
93962306a36Sopenharmony_ci		.name = pm->metric_name,
94062306a36Sopenharmony_ci		.parent = visited,
94162306a36Sopenharmony_ci	};
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	for (vm = visited; vm; vm = vm->parent) {
94462306a36Sopenharmony_ci		if (!strcmp(pm->metric_name, vm->name)) {
94562306a36Sopenharmony_ci			pr_err("failed: recursion detected for %s\n", pm->metric_name);
94662306a36Sopenharmony_ci			return -1;
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (is_root) {
95162306a36Sopenharmony_ci		/*
95262306a36Sopenharmony_ci		 * This metric is the root of a tree and may reference other
95362306a36Sopenharmony_ci		 * metrics that are added recursively.
95462306a36Sopenharmony_ci		 */
95562306a36Sopenharmony_ci		root_metric = metric__new(pm, modifier, metric_no_group, runtime,
95662306a36Sopenharmony_ci					  user_requested_cpu_list, system_wide);
95762306a36Sopenharmony_ci		if (!root_metric)
95862306a36Sopenharmony_ci			return -ENOMEM;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	} else {
96162306a36Sopenharmony_ci		int cnt = 0;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci		/*
96462306a36Sopenharmony_ci		 * This metric was referenced in a metric higher in the
96562306a36Sopenharmony_ci		 * tree. Check if the same metric is already resolved in the
96662306a36Sopenharmony_ci		 * metric_refs list.
96762306a36Sopenharmony_ci		 */
96862306a36Sopenharmony_ci		if (root_metric->metric_refs) {
96962306a36Sopenharmony_ci			for (; root_metric->metric_refs[cnt].metric_name; cnt++) {
97062306a36Sopenharmony_ci				if (!strcmp(pm->metric_name,
97162306a36Sopenharmony_ci					    root_metric->metric_refs[cnt].metric_name))
97262306a36Sopenharmony_ci					return 0;
97362306a36Sopenharmony_ci			}
97462306a36Sopenharmony_ci		}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci		/* Create reference. Need space for the entry and the terminator. */
97762306a36Sopenharmony_ci		root_metric->metric_refs = realloc(root_metric->metric_refs,
97862306a36Sopenharmony_ci						(cnt + 2) * sizeof(struct metric_ref));
97962306a36Sopenharmony_ci		if (!root_metric->metric_refs)
98062306a36Sopenharmony_ci			return -ENOMEM;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		/*
98362306a36Sopenharmony_ci		 * Intentionally passing just const char pointers,
98462306a36Sopenharmony_ci		 * from 'pe' object, so they never go away. We don't
98562306a36Sopenharmony_ci		 * need to change them, so there's no need to create
98662306a36Sopenharmony_ci		 * our own copy.
98762306a36Sopenharmony_ci		 */
98862306a36Sopenharmony_ci		root_metric->metric_refs[cnt].metric_name = pm->metric_name;
98962306a36Sopenharmony_ci		root_metric->metric_refs[cnt].metric_expr = pm->metric_expr;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		/* Null terminate array. */
99262306a36Sopenharmony_ci		root_metric->metric_refs[cnt+1].metric_name = NULL;
99362306a36Sopenharmony_ci		root_metric->metric_refs[cnt+1].metric_expr = NULL;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/*
99762306a36Sopenharmony_ci	 * For both the parent and referenced metrics, we parse
99862306a36Sopenharmony_ci	 * all the metric's IDs and add it to the root context.
99962306a36Sopenharmony_ci	 */
100062306a36Sopenharmony_ci	ret = 0;
100162306a36Sopenharmony_ci	expr = pm->metric_expr;
100262306a36Sopenharmony_ci	if (is_root && pm->metric_threshold) {
100362306a36Sopenharmony_ci		/*
100462306a36Sopenharmony_ci		 * Threshold expressions are built off the actual metric. Switch
100562306a36Sopenharmony_ci		 * to use that in case of additional necessary events. Change
100662306a36Sopenharmony_ci		 * the visited node name to avoid this being flagged as
100762306a36Sopenharmony_ci		 * recursion. If the threshold events are disabled, just use the
100862306a36Sopenharmony_ci		 * metric's name as a reference. This allows metric threshold
100962306a36Sopenharmony_ci		 * computation if there are sufficient events.
101062306a36Sopenharmony_ci		 */
101162306a36Sopenharmony_ci		assert(strstr(pm->metric_threshold, pm->metric_name));
101262306a36Sopenharmony_ci		expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold;
101362306a36Sopenharmony_ci		visited_node.name = "__threshold__";
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci	if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) {
101662306a36Sopenharmony_ci		/* Broken metric. */
101762306a36Sopenharmony_ci		ret = -EINVAL;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci	if (!ret) {
102062306a36Sopenharmony_ci		/* Resolve referenced metrics. */
102162306a36Sopenharmony_ci		const char *pmu = pm->pmu ?: "cpu";
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		ret = resolve_metric(metric_list, pmu, modifier, metric_no_group,
102462306a36Sopenharmony_ci				     metric_no_threshold, user_requested_cpu_list,
102562306a36Sopenharmony_ci				     system_wide, root_metric, &visited_node,
102662306a36Sopenharmony_ci				     table);
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci	if (ret) {
102962306a36Sopenharmony_ci		if (is_root)
103062306a36Sopenharmony_ci			metric__free(root_metric);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	} else if (is_root)
103362306a36Sopenharmony_ci		list_add(&root_metric->nd, metric_list);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	return ret;
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistruct metricgroup__find_metric_data {
103962306a36Sopenharmony_ci	const char *pmu;
104062306a36Sopenharmony_ci	const char *metric;
104162306a36Sopenharmony_ci	struct pmu_metric *pm;
104262306a36Sopenharmony_ci};
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cistatic int metricgroup__find_metric_callback(const struct pmu_metric *pm,
104562306a36Sopenharmony_ci					     const struct pmu_metrics_table *table  __maybe_unused,
104662306a36Sopenharmony_ci					     void *vdata)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct metricgroup__find_metric_data *data = vdata;
104962306a36Sopenharmony_ci	const char *pm_pmu = pm->pmu ?: "cpu";
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	if (strcmp(data->pmu, "all") && strcmp(pm_pmu, data->pmu))
105262306a36Sopenharmony_ci		return 0;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (!match_metric(pm->metric_name, data->metric))
105562306a36Sopenharmony_ci		return 0;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	memcpy(data->pm, pm, sizeof(*pm));
105862306a36Sopenharmony_ci	return 1;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic bool metricgroup__find_metric(const char *pmu,
106262306a36Sopenharmony_ci				     const char *metric,
106362306a36Sopenharmony_ci				     const struct pmu_metrics_table *table,
106462306a36Sopenharmony_ci				     struct pmu_metric *pm)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	struct metricgroup__find_metric_data data = {
106762306a36Sopenharmony_ci		.pmu = pmu,
106862306a36Sopenharmony_ci		.metric = metric,
106962306a36Sopenharmony_ci		.pm = pm,
107062306a36Sopenharmony_ci	};
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	return pmu_metrics_table__for_each_metric(table, metricgroup__find_metric_callback, &data)
107362306a36Sopenharmony_ci		? true : false;
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_cistatic int add_metric(struct list_head *metric_list,
107762306a36Sopenharmony_ci		      const struct pmu_metric *pm,
107862306a36Sopenharmony_ci		      const char *modifier,
107962306a36Sopenharmony_ci		      bool metric_no_group,
108062306a36Sopenharmony_ci		      bool metric_no_threshold,
108162306a36Sopenharmony_ci		      const char *user_requested_cpu_list,
108262306a36Sopenharmony_ci		      bool system_wide,
108362306a36Sopenharmony_ci		      struct metric *root_metric,
108462306a36Sopenharmony_ci		      const struct visited_metric *visited,
108562306a36Sopenharmony_ci		      const struct pmu_metrics_table *table)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	int ret = 0;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (!strstr(pm->metric_expr, "?")) {
109262306a36Sopenharmony_ci		ret = __add_metric(metric_list, pm, modifier, metric_no_group,
109362306a36Sopenharmony_ci				   metric_no_threshold, 0, user_requested_cpu_list,
109462306a36Sopenharmony_ci				   system_wide, root_metric, visited, table);
109562306a36Sopenharmony_ci	} else {
109662306a36Sopenharmony_ci		int j, count;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		count = arch_get_runtimeparam(pm);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		/* This loop is added to create multiple
110162306a36Sopenharmony_ci		 * events depend on count value and add
110262306a36Sopenharmony_ci		 * those events to metric_list.
110362306a36Sopenharmony_ci		 */
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		for (j = 0; j < count && !ret; j++)
110662306a36Sopenharmony_ci			ret = __add_metric(metric_list, pm, modifier, metric_no_group,
110762306a36Sopenharmony_ci					   metric_no_threshold, j, user_requested_cpu_list,
110862306a36Sopenharmony_ci					   system_wide, root_metric, visited, table);
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	return ret;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm,
111562306a36Sopenharmony_ci					const struct pmu_metrics_table *table __maybe_unused,
111662306a36Sopenharmony_ci					void *data)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	struct metricgroup_add_iter_data *d = data;
111962306a36Sopenharmony_ci	int ret;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (!match_pm_metric(pm, d->pmu, d->metric_name))
112262306a36Sopenharmony_ci		return 0;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group,
112562306a36Sopenharmony_ci			 d->metric_no_threshold, d->user_requested_cpu_list,
112662306a36Sopenharmony_ci			 d->system_wide, d->root_metric, d->visited, d->table);
112762306a36Sopenharmony_ci	if (ret)
112862306a36Sopenharmony_ci		goto out;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	*(d->has_match) = true;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ciout:
113362306a36Sopenharmony_ci	*(d->ret) = ret;
113462306a36Sopenharmony_ci	return ret;
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci/**
113862306a36Sopenharmony_ci * metric_list_cmp - list_sort comparator that sorts metrics with more events to
113962306a36Sopenharmony_ci *                   the front. tool events are excluded from the count.
114062306a36Sopenharmony_ci */
114162306a36Sopenharmony_cistatic int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
114262306a36Sopenharmony_ci			   const struct list_head *r)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	const struct metric *left = container_of(l, struct metric, nd);
114562306a36Sopenharmony_ci	const struct metric *right = container_of(r, struct metric, nd);
114662306a36Sopenharmony_ci	struct expr_id_data *data;
114762306a36Sopenharmony_ci	int i, left_count, right_count;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	left_count = hashmap__size(left->pctx->ids);
115062306a36Sopenharmony_ci	perf_tool_event__for_each_event(i) {
115162306a36Sopenharmony_ci		if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data))
115262306a36Sopenharmony_ci			left_count--;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	right_count = hashmap__size(right->pctx->ids);
115662306a36Sopenharmony_ci	perf_tool_event__for_each_event(i) {
115762306a36Sopenharmony_ci		if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data))
115862306a36Sopenharmony_ci			right_count--;
115962306a36Sopenharmony_ci	}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	return right_count - left_count;
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci/**
116562306a36Sopenharmony_ci * default_metricgroup_cmp - Implements complex key for the Default metricgroup
116662306a36Sopenharmony_ci *			     that first sorts by default_metricgroup_name, then
116762306a36Sopenharmony_ci *			     metric_name.
116862306a36Sopenharmony_ci */
116962306a36Sopenharmony_cistatic int default_metricgroup_cmp(void *priv __maybe_unused,
117062306a36Sopenharmony_ci				   const struct list_head *l,
117162306a36Sopenharmony_ci				   const struct list_head *r)
117262306a36Sopenharmony_ci{
117362306a36Sopenharmony_ci	const struct metric *left = container_of(l, struct metric, nd);
117462306a36Sopenharmony_ci	const struct metric *right = container_of(r, struct metric, nd);
117562306a36Sopenharmony_ci	int diff = strcmp(right->default_metricgroup_name, left->default_metricgroup_name);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (diff)
117862306a36Sopenharmony_ci		return diff;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	return strcmp(right->metric_name, left->metric_name);
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistruct metricgroup__add_metric_data {
118462306a36Sopenharmony_ci	struct list_head *list;
118562306a36Sopenharmony_ci	const char *pmu;
118662306a36Sopenharmony_ci	const char *metric_name;
118762306a36Sopenharmony_ci	const char *modifier;
118862306a36Sopenharmony_ci	const char *user_requested_cpu_list;
118962306a36Sopenharmony_ci	bool metric_no_group;
119062306a36Sopenharmony_ci	bool metric_no_threshold;
119162306a36Sopenharmony_ci	bool system_wide;
119262306a36Sopenharmony_ci	bool has_match;
119362306a36Sopenharmony_ci};
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cistatic int metricgroup__add_metric_callback(const struct pmu_metric *pm,
119662306a36Sopenharmony_ci					    const struct pmu_metrics_table *table,
119762306a36Sopenharmony_ci					    void *vdata)
119862306a36Sopenharmony_ci{
119962306a36Sopenharmony_ci	struct metricgroup__add_metric_data *data = vdata;
120062306a36Sopenharmony_ci	int ret = 0;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	if (pm->metric_expr && match_pm_metric(pm, data->pmu, data->metric_name)) {
120362306a36Sopenharmony_ci		bool metric_no_group = data->metric_no_group ||
120462306a36Sopenharmony_ci			match_metric(pm->metricgroup_no_group, data->metric_name);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		data->has_match = true;
120762306a36Sopenharmony_ci		ret = add_metric(data->list, pm, data->modifier, metric_no_group,
120862306a36Sopenharmony_ci				 data->metric_no_threshold, data->user_requested_cpu_list,
120962306a36Sopenharmony_ci				 data->system_wide, /*root_metric=*/NULL,
121062306a36Sopenharmony_ci				 /*visited_metrics=*/NULL, table);
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci	return ret;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci/**
121662306a36Sopenharmony_ci * metricgroup__add_metric - Find and add a metric, or a metric group.
121762306a36Sopenharmony_ci * @pmu: The PMU name to search for metrics on, or "all" for all PMUs.
121862306a36Sopenharmony_ci * @metric_name: The name of the metric or metric group. For example, "IPC"
121962306a36Sopenharmony_ci *               could be the name of a metric and "TopDownL1" the name of a
122062306a36Sopenharmony_ci *               metric group.
122162306a36Sopenharmony_ci * @modifier: if non-null event modifiers like "u".
122262306a36Sopenharmony_ci * @metric_no_group: Should events written to events be grouped "{}" or
122362306a36Sopenharmony_ci *                   global. Grouping is the default but due to multiplexing the
122462306a36Sopenharmony_ci *                   user may override.
122562306a36Sopenharmony_ci * @user_requested_cpu_list: Command line specified CPUs to record on.
122662306a36Sopenharmony_ci * @system_wide: Are events for all processes recorded.
122762306a36Sopenharmony_ci * @metric_list: The list that the metric or metric group are added to.
122862306a36Sopenharmony_ci * @table: The table that is searched for metrics, most commonly the table for the
122962306a36Sopenharmony_ci *       architecture perf is running upon.
123062306a36Sopenharmony_ci */
123162306a36Sopenharmony_cistatic int metricgroup__add_metric(const char *pmu, const char *metric_name, const char *modifier,
123262306a36Sopenharmony_ci				   bool metric_no_group, bool metric_no_threshold,
123362306a36Sopenharmony_ci				   const char *user_requested_cpu_list,
123462306a36Sopenharmony_ci				   bool system_wide,
123562306a36Sopenharmony_ci				   struct list_head *metric_list,
123662306a36Sopenharmony_ci				   const struct pmu_metrics_table *table)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	LIST_HEAD(list);
123962306a36Sopenharmony_ci	int ret;
124062306a36Sopenharmony_ci	bool has_match = false;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	{
124362306a36Sopenharmony_ci		struct metricgroup__add_metric_data data = {
124462306a36Sopenharmony_ci			.list = &list,
124562306a36Sopenharmony_ci			.pmu = pmu,
124662306a36Sopenharmony_ci			.metric_name = metric_name,
124762306a36Sopenharmony_ci			.modifier = modifier,
124862306a36Sopenharmony_ci			.metric_no_group = metric_no_group,
124962306a36Sopenharmony_ci			.metric_no_threshold = metric_no_threshold,
125062306a36Sopenharmony_ci			.user_requested_cpu_list = user_requested_cpu_list,
125162306a36Sopenharmony_ci			.system_wide = system_wide,
125262306a36Sopenharmony_ci			.has_match = false,
125362306a36Sopenharmony_ci		};
125462306a36Sopenharmony_ci		/*
125562306a36Sopenharmony_ci		 * Iterate over all metrics seeing if metric matches either the
125662306a36Sopenharmony_ci		 * name or group. When it does add the metric to the list.
125762306a36Sopenharmony_ci		 */
125862306a36Sopenharmony_ci		ret = pmu_metrics_table__for_each_metric(table, metricgroup__add_metric_callback,
125962306a36Sopenharmony_ci						       &data);
126062306a36Sopenharmony_ci		if (ret)
126162306a36Sopenharmony_ci			goto out;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci		has_match = data.has_match;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci	{
126662306a36Sopenharmony_ci		struct metricgroup_iter_data data = {
126762306a36Sopenharmony_ci			.fn = metricgroup__add_metric_sys_event_iter,
126862306a36Sopenharmony_ci			.data = (void *) &(struct metricgroup_add_iter_data) {
126962306a36Sopenharmony_ci				.metric_list = &list,
127062306a36Sopenharmony_ci				.pmu = pmu,
127162306a36Sopenharmony_ci				.metric_name = metric_name,
127262306a36Sopenharmony_ci				.modifier = modifier,
127362306a36Sopenharmony_ci				.metric_no_group = metric_no_group,
127462306a36Sopenharmony_ci				.user_requested_cpu_list = user_requested_cpu_list,
127562306a36Sopenharmony_ci				.system_wide = system_wide,
127662306a36Sopenharmony_ci				.has_match = &has_match,
127762306a36Sopenharmony_ci				.ret = &ret,
127862306a36Sopenharmony_ci				.table = table,
127962306a36Sopenharmony_ci			},
128062306a36Sopenharmony_ci		};
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci		pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci	/* End of pmu events. */
128562306a36Sopenharmony_ci	if (!has_match)
128662306a36Sopenharmony_ci		ret = -EINVAL;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ciout:
128962306a36Sopenharmony_ci	/*
129062306a36Sopenharmony_ci	 * add to metric_list so that they can be released
129162306a36Sopenharmony_ci	 * even if it's failed
129262306a36Sopenharmony_ci	 */
129362306a36Sopenharmony_ci	list_splice(&list, metric_list);
129462306a36Sopenharmony_ci	return ret;
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci/**
129862306a36Sopenharmony_ci * metricgroup__add_metric_list - Find and add metrics, or metric groups,
129962306a36Sopenharmony_ci *                                specified in a list.
130062306a36Sopenharmony_ci * @pmu: A pmu to restrict the metrics to, or "all" for all PMUS.
130162306a36Sopenharmony_ci * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1"
130262306a36Sopenharmony_ci *        would match the IPC and CPI metrics, and TopDownL1 would match all
130362306a36Sopenharmony_ci *        the metrics in the TopDownL1 group.
130462306a36Sopenharmony_ci * @metric_no_group: Should events written to events be grouped "{}" or
130562306a36Sopenharmony_ci *                   global. Grouping is the default but due to multiplexing the
130662306a36Sopenharmony_ci *                   user may override.
130762306a36Sopenharmony_ci * @user_requested_cpu_list: Command line specified CPUs to record on.
130862306a36Sopenharmony_ci * @system_wide: Are events for all processes recorded.
130962306a36Sopenharmony_ci * @metric_list: The list that metrics are added to.
131062306a36Sopenharmony_ci * @table: The table that is searched for metrics, most commonly the table for the
131162306a36Sopenharmony_ci *       architecture perf is running upon.
131262306a36Sopenharmony_ci */
131362306a36Sopenharmony_cistatic int metricgroup__add_metric_list(const char *pmu, const char *list,
131462306a36Sopenharmony_ci					bool metric_no_group,
131562306a36Sopenharmony_ci					bool metric_no_threshold,
131662306a36Sopenharmony_ci					const char *user_requested_cpu_list,
131762306a36Sopenharmony_ci					bool system_wide, struct list_head *metric_list,
131862306a36Sopenharmony_ci					const struct pmu_metrics_table *table)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	char *list_itr, *list_copy, *metric_name, *modifier;
132162306a36Sopenharmony_ci	int ret, count = 0;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	list_copy = strdup(list);
132462306a36Sopenharmony_ci	if (!list_copy)
132562306a36Sopenharmony_ci		return -ENOMEM;
132662306a36Sopenharmony_ci	list_itr = list_copy;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	while ((metric_name = strsep(&list_itr, ",")) != NULL) {
132962306a36Sopenharmony_ci		modifier = strchr(metric_name, ':');
133062306a36Sopenharmony_ci		if (modifier)
133162306a36Sopenharmony_ci			*modifier++ = '\0';
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci		ret = metricgroup__add_metric(pmu, metric_name, modifier,
133462306a36Sopenharmony_ci					      metric_no_group, metric_no_threshold,
133562306a36Sopenharmony_ci					      user_requested_cpu_list,
133662306a36Sopenharmony_ci					      system_wide, metric_list, table);
133762306a36Sopenharmony_ci		if (ret == -EINVAL)
133862306a36Sopenharmony_ci			pr_err("Cannot find metric or group `%s'\n", metric_name);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci		if (ret)
134162306a36Sopenharmony_ci			break;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci		count++;
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci	free(list_copy);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	if (!ret) {
134862306a36Sopenharmony_ci		/*
134962306a36Sopenharmony_ci		 * Warn about nmi_watchdog if any parsed metrics had the
135062306a36Sopenharmony_ci		 * NO_NMI_WATCHDOG constraint.
135162306a36Sopenharmony_ci		 */
135262306a36Sopenharmony_ci		metric__watchdog_constraint_hint(NULL, /*foot=*/true);
135362306a36Sopenharmony_ci		/* No metrics. */
135462306a36Sopenharmony_ci		if (count == 0)
135562306a36Sopenharmony_ci			return -EINVAL;
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci	return ret;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic void metricgroup__free_metrics(struct list_head *metric_list)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	struct metric *m, *tmp;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	list_for_each_entry_safe (m, tmp, metric_list, nd) {
136562306a36Sopenharmony_ci		list_del_init(&m->nd);
136662306a36Sopenharmony_ci		metric__free(m);
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci/**
137162306a36Sopenharmony_ci * find_tool_events - Search for the pressence of tool events in metric_list.
137262306a36Sopenharmony_ci * @metric_list: List to take metrics from.
137362306a36Sopenharmony_ci * @tool_events: Array of false values, indices corresponding to tool events set
137462306a36Sopenharmony_ci *               to true if tool event is found.
137562306a36Sopenharmony_ci */
137662306a36Sopenharmony_cistatic void find_tool_events(const struct list_head *metric_list,
137762306a36Sopenharmony_ci			     bool tool_events[PERF_TOOL_MAX])
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	struct metric *m;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	list_for_each_entry(m, metric_list, nd) {
138262306a36Sopenharmony_ci		int i;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci		perf_tool_event__for_each_event(i) {
138562306a36Sopenharmony_ci			struct expr_id_data *data;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci			if (!tool_events[i] &&
138862306a36Sopenharmony_ci			    !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data))
138962306a36Sopenharmony_ci				tool_events[i] = true;
139062306a36Sopenharmony_ci		}
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci/**
139562306a36Sopenharmony_ci * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events
139662306a36Sopenharmony_ci *                           metric IDs, as the IDs are held in a set,
139762306a36Sopenharmony_ci *                           duplicates will be removed.
139862306a36Sopenharmony_ci * @metric_list: List to take metrics from.
139962306a36Sopenharmony_ci * @combined: Out argument for result.
140062306a36Sopenharmony_ci */
140162306a36Sopenharmony_cistatic int build_combined_expr_ctx(const struct list_head *metric_list,
140262306a36Sopenharmony_ci				   struct expr_parse_ctx **combined)
140362306a36Sopenharmony_ci{
140462306a36Sopenharmony_ci	struct hashmap_entry *cur;
140562306a36Sopenharmony_ci	size_t bkt;
140662306a36Sopenharmony_ci	struct metric *m;
140762306a36Sopenharmony_ci	char *dup;
140862306a36Sopenharmony_ci	int ret;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	*combined = expr__ctx_new();
141162306a36Sopenharmony_ci	if (!*combined)
141262306a36Sopenharmony_ci		return -ENOMEM;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	list_for_each_entry(m, metric_list, nd) {
141562306a36Sopenharmony_ci		if (!m->group_events && !m->modifier) {
141662306a36Sopenharmony_ci			hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
141762306a36Sopenharmony_ci				dup = strdup(cur->pkey);
141862306a36Sopenharmony_ci				if (!dup) {
141962306a36Sopenharmony_ci					ret = -ENOMEM;
142062306a36Sopenharmony_ci					goto err_out;
142162306a36Sopenharmony_ci				}
142262306a36Sopenharmony_ci				ret = expr__add_id(*combined, dup);
142362306a36Sopenharmony_ci				if (ret)
142462306a36Sopenharmony_ci					goto err_out;
142562306a36Sopenharmony_ci			}
142662306a36Sopenharmony_ci		}
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci	return 0;
142962306a36Sopenharmony_cierr_out:
143062306a36Sopenharmony_ci	expr__ctx_free(*combined);
143162306a36Sopenharmony_ci	*combined = NULL;
143262306a36Sopenharmony_ci	return ret;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci/**
143662306a36Sopenharmony_ci * parse_ids - Build the event string for the ids and parse them creating an
143762306a36Sopenharmony_ci *             evlist. The encoded metric_ids are decoded.
143862306a36Sopenharmony_ci * @metric_no_merge: is metric sharing explicitly disabled.
143962306a36Sopenharmony_ci * @fake_pmu: used when testing metrics not supported by the current CPU.
144062306a36Sopenharmony_ci * @ids: the event identifiers parsed from a metric.
144162306a36Sopenharmony_ci * @modifier: any modifiers added to the events.
144262306a36Sopenharmony_ci * @group_events: should events be placed in a weak group.
144362306a36Sopenharmony_ci * @tool_events: entries set true if the tool event of index could be present in
144462306a36Sopenharmony_ci *               the overall list of metrics.
144562306a36Sopenharmony_ci * @out_evlist: the created list of events.
144662306a36Sopenharmony_ci */
144762306a36Sopenharmony_cistatic int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
144862306a36Sopenharmony_ci		     struct expr_parse_ctx *ids, const char *modifier,
144962306a36Sopenharmony_ci		     bool group_events, const bool tool_events[PERF_TOOL_MAX],
145062306a36Sopenharmony_ci		     struct evlist **out_evlist)
145162306a36Sopenharmony_ci{
145262306a36Sopenharmony_ci	struct parse_events_error parse_error;
145362306a36Sopenharmony_ci	struct evlist *parsed_evlist;
145462306a36Sopenharmony_ci	struct strbuf events = STRBUF_INIT;
145562306a36Sopenharmony_ci	int ret;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	*out_evlist = NULL;
145862306a36Sopenharmony_ci	if (!metric_no_merge || hashmap__size(ids->ids) == 0) {
145962306a36Sopenharmony_ci		bool added_event = false;
146062306a36Sopenharmony_ci		int i;
146162306a36Sopenharmony_ci		/*
146262306a36Sopenharmony_ci		 * We may fail to share events between metrics because a tool
146362306a36Sopenharmony_ci		 * event isn't present in one metric. For example, a ratio of
146462306a36Sopenharmony_ci		 * cache misses doesn't need duration_time but the same events
146562306a36Sopenharmony_ci		 * may be used for a misses per second. Events without sharing
146662306a36Sopenharmony_ci		 * implies multiplexing, that is best avoided, so place
146762306a36Sopenharmony_ci		 * all tool events in every group.
146862306a36Sopenharmony_ci		 *
146962306a36Sopenharmony_ci		 * Also, there may be no ids/events in the expression parsing
147062306a36Sopenharmony_ci		 * context because of constant evaluation, e.g.:
147162306a36Sopenharmony_ci		 *    event1 if #smt_on else 0
147262306a36Sopenharmony_ci		 * Add a tool event to avoid a parse error on an empty string.
147362306a36Sopenharmony_ci		 */
147462306a36Sopenharmony_ci		perf_tool_event__for_each_event(i) {
147562306a36Sopenharmony_ci			if (tool_events[i]) {
147662306a36Sopenharmony_ci				char *tmp = strdup(perf_tool_event__to_str(i));
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci				if (!tmp)
147962306a36Sopenharmony_ci					return -ENOMEM;
148062306a36Sopenharmony_ci				ids__insert(ids->ids, tmp);
148162306a36Sopenharmony_ci				added_event = true;
148262306a36Sopenharmony_ci			}
148362306a36Sopenharmony_ci		}
148462306a36Sopenharmony_ci		if (!added_event && hashmap__size(ids->ids) == 0) {
148562306a36Sopenharmony_ci			char *tmp = strdup("duration_time");
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci			if (!tmp)
148862306a36Sopenharmony_ci				return -ENOMEM;
148962306a36Sopenharmony_ci			ids__insert(ids->ids, tmp);
149062306a36Sopenharmony_ci		}
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci	ret = metricgroup__build_event_string(&events, ids, modifier,
149362306a36Sopenharmony_ci					      group_events);
149462306a36Sopenharmony_ci	if (ret)
149562306a36Sopenharmony_ci		return ret;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	parsed_evlist = evlist__new();
149862306a36Sopenharmony_ci	if (!parsed_evlist) {
149962306a36Sopenharmony_ci		ret = -ENOMEM;
150062306a36Sopenharmony_ci		goto err_out;
150162306a36Sopenharmony_ci	}
150262306a36Sopenharmony_ci	pr_debug("Parsing metric events '%s'\n", events.buf);
150362306a36Sopenharmony_ci	parse_events_error__init(&parse_error);
150462306a36Sopenharmony_ci	ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL,
150562306a36Sopenharmony_ci			     &parse_error, fake_pmu, /*warn_if_reordered=*/false);
150662306a36Sopenharmony_ci	if (ret) {
150762306a36Sopenharmony_ci		parse_events_error__print(&parse_error, events.buf);
150862306a36Sopenharmony_ci		goto err_out;
150962306a36Sopenharmony_ci	}
151062306a36Sopenharmony_ci	ret = decode_all_metric_ids(parsed_evlist, modifier);
151162306a36Sopenharmony_ci	if (ret)
151262306a36Sopenharmony_ci		goto err_out;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	*out_evlist = parsed_evlist;
151562306a36Sopenharmony_ci	parsed_evlist = NULL;
151662306a36Sopenharmony_cierr_out:
151762306a36Sopenharmony_ci	parse_events_error__exit(&parse_error);
151862306a36Sopenharmony_ci	evlist__delete(parsed_evlist);
151962306a36Sopenharmony_ci	strbuf_release(&events);
152062306a36Sopenharmony_ci	return ret;
152162306a36Sopenharmony_ci}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_cistatic int parse_groups(struct evlist *perf_evlist,
152462306a36Sopenharmony_ci			const char *pmu, const char *str,
152562306a36Sopenharmony_ci			bool metric_no_group,
152662306a36Sopenharmony_ci			bool metric_no_merge,
152762306a36Sopenharmony_ci			bool metric_no_threshold,
152862306a36Sopenharmony_ci			const char *user_requested_cpu_list,
152962306a36Sopenharmony_ci			bool system_wide,
153062306a36Sopenharmony_ci			struct perf_pmu *fake_pmu,
153162306a36Sopenharmony_ci			struct rblist *metric_events_list,
153262306a36Sopenharmony_ci			const struct pmu_metrics_table *table)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	struct evlist *combined_evlist = NULL;
153562306a36Sopenharmony_ci	LIST_HEAD(metric_list);
153662306a36Sopenharmony_ci	struct metric *m;
153762306a36Sopenharmony_ci	bool tool_events[PERF_TOOL_MAX] = {false};
153862306a36Sopenharmony_ci	bool is_default = !strcmp(str, "Default");
153962306a36Sopenharmony_ci	int ret;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (metric_events_list->nr_entries == 0)
154262306a36Sopenharmony_ci		metricgroup__rblist_init(metric_events_list);
154362306a36Sopenharmony_ci	ret = metricgroup__add_metric_list(pmu, str, metric_no_group, metric_no_threshold,
154462306a36Sopenharmony_ci					   user_requested_cpu_list,
154562306a36Sopenharmony_ci					   system_wide, &metric_list, table);
154662306a36Sopenharmony_ci	if (ret)
154762306a36Sopenharmony_ci		goto out;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	/* Sort metrics from largest to smallest. */
155062306a36Sopenharmony_ci	list_sort(NULL, &metric_list, metric_list_cmp);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	if (!metric_no_merge) {
155362306a36Sopenharmony_ci		struct expr_parse_ctx *combined = NULL;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci		find_tool_events(&metric_list, tool_events);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci		ret = build_combined_expr_ctx(&metric_list, &combined);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci		if (!ret && combined && hashmap__size(combined->ids)) {
156062306a36Sopenharmony_ci			ret = parse_ids(metric_no_merge, fake_pmu, combined,
156162306a36Sopenharmony_ci					/*modifier=*/NULL,
156262306a36Sopenharmony_ci					/*group_events=*/false,
156362306a36Sopenharmony_ci					tool_events,
156462306a36Sopenharmony_ci					&combined_evlist);
156562306a36Sopenharmony_ci		}
156662306a36Sopenharmony_ci		if (combined)
156762306a36Sopenharmony_ci			expr__ctx_free(combined);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci		if (ret)
157062306a36Sopenharmony_ci			goto out;
157162306a36Sopenharmony_ci	}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	if (is_default)
157462306a36Sopenharmony_ci		list_sort(NULL, &metric_list, default_metricgroup_cmp);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	list_for_each_entry(m, &metric_list, nd) {
157762306a36Sopenharmony_ci		struct metric_event *me;
157862306a36Sopenharmony_ci		struct evsel **metric_events;
157962306a36Sopenharmony_ci		struct evlist *metric_evlist = NULL;
158062306a36Sopenharmony_ci		struct metric *n;
158162306a36Sopenharmony_ci		struct metric_expr *expr;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci		if (combined_evlist && !m->group_events) {
158462306a36Sopenharmony_ci			metric_evlist = combined_evlist;
158562306a36Sopenharmony_ci		} else if (!metric_no_merge) {
158662306a36Sopenharmony_ci			/*
158762306a36Sopenharmony_ci			 * See if the IDs for this metric are a subset of an
158862306a36Sopenharmony_ci			 * earlier metric.
158962306a36Sopenharmony_ci			 */
159062306a36Sopenharmony_ci			list_for_each_entry(n, &metric_list, nd) {
159162306a36Sopenharmony_ci				if (m == n)
159262306a36Sopenharmony_ci					break;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci				if (n->evlist == NULL)
159562306a36Sopenharmony_ci					continue;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci				if ((!m->modifier && n->modifier) ||
159862306a36Sopenharmony_ci				    (m->modifier && !n->modifier) ||
159962306a36Sopenharmony_ci				    (m->modifier && n->modifier &&
160062306a36Sopenharmony_ci					    strcmp(m->modifier, n->modifier)))
160162306a36Sopenharmony_ci					continue;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci				if ((!m->pmu && n->pmu) ||
160462306a36Sopenharmony_ci				    (m->pmu && !n->pmu) ||
160562306a36Sopenharmony_ci				    (m->pmu && n->pmu && strcmp(m->pmu, n->pmu)))
160662306a36Sopenharmony_ci					continue;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci				if (expr__subset_of_ids(n->pctx, m->pctx)) {
160962306a36Sopenharmony_ci					pr_debug("Events in '%s' fully contained within '%s'\n",
161062306a36Sopenharmony_ci						 m->metric_name, n->metric_name);
161162306a36Sopenharmony_ci					metric_evlist = n->evlist;
161262306a36Sopenharmony_ci					break;
161362306a36Sopenharmony_ci				}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci			}
161662306a36Sopenharmony_ci		}
161762306a36Sopenharmony_ci		if (!metric_evlist) {
161862306a36Sopenharmony_ci			ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier,
161962306a36Sopenharmony_ci					m->group_events, tool_events, &m->evlist);
162062306a36Sopenharmony_ci			if (ret)
162162306a36Sopenharmony_ci				goto out;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci			metric_evlist = m->evlist;
162462306a36Sopenharmony_ci		}
162562306a36Sopenharmony_ci		ret = setup_metric_events(fake_pmu ? "all" : m->pmu, m->pctx->ids,
162662306a36Sopenharmony_ci					  metric_evlist, &metric_events);
162762306a36Sopenharmony_ci		if (ret) {
162862306a36Sopenharmony_ci			pr_err("Cannot resolve IDs for %s: %s\n",
162962306a36Sopenharmony_ci				m->metric_name, m->metric_expr);
163062306a36Sopenharmony_ci			goto out;
163162306a36Sopenharmony_ci		}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci		me = metricgroup__lookup(metric_events_list, metric_events[0], true);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci		expr = malloc(sizeof(struct metric_expr));
163662306a36Sopenharmony_ci		if (!expr) {
163762306a36Sopenharmony_ci			ret = -ENOMEM;
163862306a36Sopenharmony_ci			free(metric_events);
163962306a36Sopenharmony_ci			goto out;
164062306a36Sopenharmony_ci		}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci		expr->metric_refs = m->metric_refs;
164362306a36Sopenharmony_ci		m->metric_refs = NULL;
164462306a36Sopenharmony_ci		expr->metric_expr = m->metric_expr;
164562306a36Sopenharmony_ci		if (m->modifier) {
164662306a36Sopenharmony_ci			char *tmp;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci			if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0)
164962306a36Sopenharmony_ci				expr->metric_name = NULL;
165062306a36Sopenharmony_ci			else
165162306a36Sopenharmony_ci				expr->metric_name = tmp;
165262306a36Sopenharmony_ci		} else
165362306a36Sopenharmony_ci			expr->metric_name = strdup(m->metric_name);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci		if (!expr->metric_name) {
165662306a36Sopenharmony_ci			ret = -ENOMEM;
165762306a36Sopenharmony_ci			free(metric_events);
165862306a36Sopenharmony_ci			goto out;
165962306a36Sopenharmony_ci		}
166062306a36Sopenharmony_ci		expr->metric_threshold = m->metric_threshold;
166162306a36Sopenharmony_ci		expr->metric_unit = m->metric_unit;
166262306a36Sopenharmony_ci		expr->metric_events = metric_events;
166362306a36Sopenharmony_ci		expr->runtime = m->pctx->sctx.runtime;
166462306a36Sopenharmony_ci		expr->default_metricgroup_name = m->default_metricgroup_name;
166562306a36Sopenharmony_ci		me->is_default = is_default;
166662306a36Sopenharmony_ci		list_add(&expr->nd, &me->head);
166762306a36Sopenharmony_ci	}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	if (combined_evlist) {
167162306a36Sopenharmony_ci		evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries);
167262306a36Sopenharmony_ci		evlist__delete(combined_evlist);
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	list_for_each_entry(m, &metric_list, nd) {
167662306a36Sopenharmony_ci		if (m->evlist)
167762306a36Sopenharmony_ci			evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries);
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ciout:
168162306a36Sopenharmony_ci	metricgroup__free_metrics(&metric_list);
168262306a36Sopenharmony_ci	return ret;
168362306a36Sopenharmony_ci}
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ciint metricgroup__parse_groups(struct evlist *perf_evlist,
168662306a36Sopenharmony_ci			      const char *pmu,
168762306a36Sopenharmony_ci			      const char *str,
168862306a36Sopenharmony_ci			      bool metric_no_group,
168962306a36Sopenharmony_ci			      bool metric_no_merge,
169062306a36Sopenharmony_ci			      bool metric_no_threshold,
169162306a36Sopenharmony_ci			      const char *user_requested_cpu_list,
169262306a36Sopenharmony_ci			      bool system_wide,
169362306a36Sopenharmony_ci			      struct rblist *metric_events)
169462306a36Sopenharmony_ci{
169562306a36Sopenharmony_ci	const struct pmu_metrics_table *table = pmu_metrics_table__find();
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	if (!table)
169862306a36Sopenharmony_ci		return -EINVAL;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	return parse_groups(perf_evlist, pmu, str, metric_no_group, metric_no_merge,
170162306a36Sopenharmony_ci			    metric_no_threshold, user_requested_cpu_list, system_wide,
170262306a36Sopenharmony_ci			    /*fake_pmu=*/NULL, metric_events, table);
170362306a36Sopenharmony_ci}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ciint metricgroup__parse_groups_test(struct evlist *evlist,
170662306a36Sopenharmony_ci				   const struct pmu_metrics_table *table,
170762306a36Sopenharmony_ci				   const char *str,
170862306a36Sopenharmony_ci				   struct rblist *metric_events)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	return parse_groups(evlist, "all", str,
171162306a36Sopenharmony_ci			    /*metric_no_group=*/false,
171262306a36Sopenharmony_ci			    /*metric_no_merge=*/false,
171362306a36Sopenharmony_ci			    /*metric_no_threshold=*/false,
171462306a36Sopenharmony_ci			    /*user_requested_cpu_list=*/NULL,
171562306a36Sopenharmony_ci			    /*system_wide=*/false,
171662306a36Sopenharmony_ci			    &perf_pmu__fake, metric_events, table);
171762306a36Sopenharmony_ci}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_cistruct metricgroup__has_metric_data {
172062306a36Sopenharmony_ci	const char *pmu;
172162306a36Sopenharmony_ci	const char *metric;
172262306a36Sopenharmony_ci};
172362306a36Sopenharmony_cistatic int metricgroup__has_metric_callback(const struct pmu_metric *pm,
172462306a36Sopenharmony_ci					    const struct pmu_metrics_table *table __maybe_unused,
172562306a36Sopenharmony_ci					    void *vdata)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	struct metricgroup__has_metric_data *data = vdata;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	return match_pm_metric(pm, data->pmu, data->metric) ? 1 : 0;
173062306a36Sopenharmony_ci}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cibool metricgroup__has_metric(const char *pmu, const char *metric)
173362306a36Sopenharmony_ci{
173462306a36Sopenharmony_ci	const struct pmu_metrics_table *table = pmu_metrics_table__find();
173562306a36Sopenharmony_ci	struct metricgroup__has_metric_data data = {
173662306a36Sopenharmony_ci		.pmu = pmu,
173762306a36Sopenharmony_ci		.metric = metric,
173862306a36Sopenharmony_ci	};
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	if (!table)
174162306a36Sopenharmony_ci		return false;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	return pmu_metrics_table__for_each_metric(table, metricgroup__has_metric_callback, &data)
174462306a36Sopenharmony_ci		? true : false;
174562306a36Sopenharmony_ci}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_cistatic int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm,
174862306a36Sopenharmony_ci					    const struct pmu_metrics_table *table __maybe_unused,
174962306a36Sopenharmony_ci					    void *data)
175062306a36Sopenharmony_ci{
175162306a36Sopenharmony_ci	unsigned int *max_level = data;
175262306a36Sopenharmony_ci	unsigned int level;
175362306a36Sopenharmony_ci	const char *p = strstr(pm->metric_group ?: "", "TopdownL");
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (!p || p[8] == '\0')
175662306a36Sopenharmony_ci		return 0;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	level = p[8] - '0';
175962306a36Sopenharmony_ci	if (level > *max_level)
176062306a36Sopenharmony_ci		*max_level = level;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	return 0;
176362306a36Sopenharmony_ci}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ciunsigned int metricgroups__topdown_max_level(void)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	unsigned int max_level = 0;
176862306a36Sopenharmony_ci	const struct pmu_metrics_table *table = pmu_metrics_table__find();
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	if (!table)
177162306a36Sopenharmony_ci		return false;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	pmu_metrics_table__for_each_metric(table, metricgroup__topdown_max_level_callback,
177462306a36Sopenharmony_ci					  &max_level);
177562306a36Sopenharmony_ci	return max_level;
177662306a36Sopenharmony_ci}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ciint metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
177962306a36Sopenharmony_ci				    struct rblist *new_metric_events,
178062306a36Sopenharmony_ci				    struct rblist *old_metric_events)
178162306a36Sopenharmony_ci{
178262306a36Sopenharmony_ci	unsigned int i;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	for (i = 0; i < rblist__nr_entries(old_metric_events); i++) {
178562306a36Sopenharmony_ci		struct rb_node *nd;
178662306a36Sopenharmony_ci		struct metric_event *old_me, *new_me;
178762306a36Sopenharmony_ci		struct metric_expr *old_expr, *new_expr;
178862306a36Sopenharmony_ci		struct evsel *evsel;
178962306a36Sopenharmony_ci		size_t alloc_size;
179062306a36Sopenharmony_ci		int idx, nr;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci		nd = rblist__entry(old_metric_events, i);
179362306a36Sopenharmony_ci		old_me = container_of(nd, struct metric_event, nd);
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci		evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx);
179662306a36Sopenharmony_ci		if (!evsel)
179762306a36Sopenharmony_ci			return -EINVAL;
179862306a36Sopenharmony_ci		new_me = metricgroup__lookup(new_metric_events, evsel, true);
179962306a36Sopenharmony_ci		if (!new_me)
180062306a36Sopenharmony_ci			return -ENOMEM;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci		pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n",
180362306a36Sopenharmony_ci			 cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx);
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci		list_for_each_entry(old_expr, &old_me->head, nd) {
180662306a36Sopenharmony_ci			new_expr = malloc(sizeof(*new_expr));
180762306a36Sopenharmony_ci			if (!new_expr)
180862306a36Sopenharmony_ci				return -ENOMEM;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci			new_expr->metric_expr = old_expr->metric_expr;
181162306a36Sopenharmony_ci			new_expr->metric_threshold = old_expr->metric_threshold;
181262306a36Sopenharmony_ci			new_expr->metric_name = strdup(old_expr->metric_name);
181362306a36Sopenharmony_ci			if (!new_expr->metric_name)
181462306a36Sopenharmony_ci				return -ENOMEM;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci			new_expr->metric_unit = old_expr->metric_unit;
181762306a36Sopenharmony_ci			new_expr->runtime = old_expr->runtime;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci			if (old_expr->metric_refs) {
182062306a36Sopenharmony_ci				/* calculate number of metric_events */
182162306a36Sopenharmony_ci				for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++)
182262306a36Sopenharmony_ci					continue;
182362306a36Sopenharmony_ci				alloc_size = sizeof(*new_expr->metric_refs);
182462306a36Sopenharmony_ci				new_expr->metric_refs = calloc(nr + 1, alloc_size);
182562306a36Sopenharmony_ci				if (!new_expr->metric_refs) {
182662306a36Sopenharmony_ci					free(new_expr);
182762306a36Sopenharmony_ci					return -ENOMEM;
182862306a36Sopenharmony_ci				}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci				memcpy(new_expr->metric_refs, old_expr->metric_refs,
183162306a36Sopenharmony_ci				       nr * alloc_size);
183262306a36Sopenharmony_ci			} else {
183362306a36Sopenharmony_ci				new_expr->metric_refs = NULL;
183462306a36Sopenharmony_ci			}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci			/* calculate number of metric_events */
183762306a36Sopenharmony_ci			for (nr = 0; old_expr->metric_events[nr]; nr++)
183862306a36Sopenharmony_ci				continue;
183962306a36Sopenharmony_ci			alloc_size = sizeof(*new_expr->metric_events);
184062306a36Sopenharmony_ci			new_expr->metric_events = calloc(nr + 1, alloc_size);
184162306a36Sopenharmony_ci			if (!new_expr->metric_events) {
184262306a36Sopenharmony_ci				zfree(&new_expr->metric_refs);
184362306a36Sopenharmony_ci				free(new_expr);
184462306a36Sopenharmony_ci				return -ENOMEM;
184562306a36Sopenharmony_ci			}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci			/* copy evsel in the same position */
184862306a36Sopenharmony_ci			for (idx = 0; idx < nr; idx++) {
184962306a36Sopenharmony_ci				evsel = old_expr->metric_events[idx];
185062306a36Sopenharmony_ci				evsel = evlist__find_evsel(evlist, evsel->core.idx);
185162306a36Sopenharmony_ci				if (evsel == NULL) {
185262306a36Sopenharmony_ci					zfree(&new_expr->metric_events);
185362306a36Sopenharmony_ci					zfree(&new_expr->metric_refs);
185462306a36Sopenharmony_ci					free(new_expr);
185562306a36Sopenharmony_ci					return -EINVAL;
185662306a36Sopenharmony_ci				}
185762306a36Sopenharmony_ci				new_expr->metric_events[idx] = evsel;
185862306a36Sopenharmony_ci			}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci			list_add(&new_expr->nd, &new_me->head);
186162306a36Sopenharmony_ci		}
186262306a36Sopenharmony_ci	}
186362306a36Sopenharmony_ci	return 0;
186462306a36Sopenharmony_ci}
1865