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

perf list: Reorganize to use callbacks to allow honouring command line options



Rather than controlling the list output with passed flags, add
callbacks that are called when an event or metric are
encountered. State is passed to the callback so that command line
options can be respected, alternatively the callbacks can be changed.

Fix a few bugs:
 - wordwrap to columns metric descriptions and expressions;
 - remove unnecessary whitespace after PMU event names;
 - the metric filter is a glob but matched using strstr which will
   always fail, switch to using a proper globmatch,
 - the detail flag gives details for extra kernel PMU events like
   branch-instructions.

In metricgroup.c switch from struct mep being a rbtree of metricgroups
containing a list of metrics, to the tree directly containing all the
metrics. In general the alias for a name is passed to the print
routine rather than being contained in the name with OR.

Committer notes:

Check the asprint() return to address this on fedora 36:

  util/print-events.c: In function ‘print_sdt_events’:
  util/print-events.c:183:33: error: ignoring return value of ‘asprintf’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
    183 |                                 asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid);
        |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  cc1: all warnings being treated as errors

  $ gcc --version | head -1
  gcc (GCC) 12.2.1 20220819 (Red Hat 12.2.1-2)
  $

Fix ps.pmu_glob setting when dealing with *:* events, it was being left
with a freed pointer that then at the end of cmd_list() would be double
freed.

Check if pmu_name is NULL in default_print_event() before calling
strglobmatch(pmu_name, ...) to avoid a segfault.

Signed-off-by: default avatarIan Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Caleb Biggers <caleb.biggers@intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Perry Taylor <perry.taylor@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Sandipan Das <sandipan.das@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Weilin Wang <weilin.wang@intel.com>
Cc: Xin Gao <gaoxin@cdjrlc.com>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Link: http://lore.kernel.org/lkml/20221114210723.2749751-10-irogers@google.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent a3720e96
Loading
Loading
Loading
Loading
+281 −52
Original line number Diff line number Diff line
@@ -15,31 +15,240 @@
#include "util/pmu-hybrid.h"
#include "util/debug.h"
#include "util/metricgroup.h"
#include "util/string2.h"
#include "util/strlist.h"
#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include <stdio.h>

static bool desc_flag = true;
static bool details_flag;
/**
 * struct print_state - State and configuration passed to the default_print
 * functions.
 */
struct print_state {
	/**
	 * @pmu_glob: Optionally restrict PMU and metric matching to PMU or
	 * debugfs subsystem name.
	 */
	char *pmu_glob;
	/** @event_glob: Optional pattern matching glob. */
	char *event_glob;
	/** @name_only: Print event or metric names only. */
	bool name_only;
	/** @desc: Print the event or metric description. */
	bool desc;
	/** @long_desc: Print longer event or metric description. */
	bool long_desc;
	/** @deprecated: Print deprecated events or metrics. */
	bool deprecated;
	/**
	 * @detailed: Print extra information on the perf event such as names
	 * and expressions used internally by events.
	 */
	bool detailed;
	/** @metrics: Controls printing of metric and metric groups. */
	bool metrics;
	/** @metricgroups: Controls printing of metric and metric groups. */
	bool metricgroups;
	/** @last_topic: The last printed event topic. */
	char *last_topic;
	/** @last_metricgroups: The last printed metric group. */
	char *last_metricgroups;
	/** @visited_metrics: Metrics that are printed to avoid duplicates. */
	struct strlist *visited_metrics;
};

static void default_print_start(void *ps)
{
	struct print_state *print_state = ps;

	if (!print_state->name_only && pager_in_use())
		printf("\nList of pre-defined events (to be used in -e or -M):\n\n");
}

static void default_print_end(void *print_state __maybe_unused) {}

static void wordwrap(const char *s, int start, int max, int corr)
{
	int column = start;
	int n;

	while (*s) {
		int wlen = strcspn(s, " \t");

		if (column + wlen >= max && column > start) {
			printf("\n%*s", start, "");
			column = start + corr;
		}
		n = printf("%s%.*s", column > start ? " " : "", wlen, s);
		if (n <= 0)
			break;
		s += wlen;
		column += n;
		s = skip_spaces(s);
	}
}

static void default_print_event(void *ps, const char *pmu_name, const char *topic,
				const char *event_name, const char *event_alias,
				const char *scale_unit __maybe_unused,
				bool deprecated, const char *event_type_desc,
				const char *desc, const char *long_desc,
				const char *encoding_desc,
				const char *metric_name, const char *metric_expr)
{
	struct print_state *print_state = ps;
	int pos;

	if (deprecated && !print_state->deprecated)
		return;

	if (print_state->pmu_glob && pmu_name && !strglobmatch(pmu_name, print_state->pmu_glob))
		return;

	if (print_state->event_glob &&
	    (!event_name || !strglobmatch(event_name, print_state->event_glob)) &&
	    (!event_alias || !strglobmatch(event_alias, print_state->event_glob)) &&
	    (!topic || !strglobmatch_nocase(topic, print_state->event_glob)))
		return;

	if (print_state->name_only) {
		if (event_alias && strlen(event_alias))
			printf("%s ", event_alias);
		else
			printf("%s ", event_name);
		return;
	}

	if (strcmp(print_state->last_topic, topic ?: "")) {
		if (topic)
			printf("\n%s:\n", topic);
		free(print_state->last_topic);
		print_state->last_topic = strdup(topic ?: "");
	}

	if (event_alias && strlen(event_alias))
		pos = printf("  %s OR %s", event_name, event_alias);
	else
		pos = printf("  %s", event_name);

	if (!topic && event_type_desc) {
		for (; pos < 53; pos++)
			putchar(' ');
		printf("[%s]\n", event_type_desc);
	} else
		putchar('\n');

	if (desc && print_state->desc) {
		printf("%*s", 8, "[");
		wordwrap(desc, 8, pager_get_columns(), 0);
		printf("]\n");
	}

	if (long_desc && print_state->long_desc) {
		printf("%*s", 8, "[");
		wordwrap(long_desc, 8, pager_get_columns(), 0);
		printf("]\n");
	}

	if (print_state->detailed && encoding_desc) {
		printf("%*s%s", 8, "", encoding_desc);
		if (metric_name)
			printf(" MetricName: %s", metric_name);
		if (metric_expr)
			printf(" MetricExpr: %s", metric_expr);
		putchar('\n');
	}
}

static void default_print_metric(void *ps,
				const char *group,
				const char *name,
				const char *desc,
				const char *long_desc,
				const char *expr,
				const char *unit __maybe_unused)
{
	struct print_state *print_state = ps;

	if (print_state->event_glob &&
	    (!print_state->metrics || !name || !strglobmatch(name, print_state->event_glob)) &&
	    (!print_state->metricgroups || !group || !strglobmatch(group, print_state->event_glob)))
		return;

	if (!print_state->name_only && !print_state->last_metricgroups) {
		if (print_state->metricgroups) {
			printf("\nMetric Groups:\n");
			if (!print_state->metrics)
				putchar('\n');
		} else {
			printf("\nMetrics:\n\n");
		}
	}
	if (!print_state->last_metricgroups ||
	    strcmp(print_state->last_metricgroups, group ?: "")) {
		if (group && print_state->metricgroups) {
			if (print_state->name_only)
				printf("%s ", group);
			else if (print_state->metrics)
				printf("\n%s:\n", group);
			else
				printf("%s\n", group);
		}
		free(print_state->last_metricgroups);
		print_state->last_metricgroups = strdup(group ?: "");
	}
	if (!print_state->metrics)
		return;

	if (print_state->name_only) {
		if (print_state->metrics &&
		    !strlist__has_entry(print_state->visited_metrics, name)) {
			printf("%s ", name);
			strlist__add(print_state->visited_metrics, name);
		}
		return;
	}
	printf("  %s\n", name);

	if (desc && print_state->desc) {
		printf("%*s", 8, "[");
		wordwrap(desc, 8, pager_get_columns(), 0);
		printf("]\n");
	}
	if (long_desc && print_state->long_desc) {
		printf("%*s", 8, "[");
		wordwrap(long_desc, 8, pager_get_columns(), 0);
		printf("]\n");
	}
	if (expr && print_state->detailed) {
		printf("%*s", 8, "[");
		wordwrap(expr, 8, pager_get_columns(), 0);
		printf("]\n");
	}
}

int cmd_list(int argc, const char **argv)
{
	int i, ret = 0;
	bool raw_dump = false;
	bool long_desc_flag = false;
	bool deprecated = false;
	char *pmu_name = NULL;
	struct print_state ps = {};
	struct print_callbacks print_cb = {
		.print_start = default_print_start,
		.print_end = default_print_end,
		.print_event = default_print_event,
		.print_metric = default_print_metric,
	};
	const char *hybrid_name = NULL;
	const char *unit_name = NULL;
	struct option list_options[] = {
		OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"),
		OPT_BOOLEAN('d', "desc", &desc_flag,
		OPT_BOOLEAN(0, "raw-dump", &ps.name_only, "Dump raw events"),
		OPT_BOOLEAN('d', "desc", &ps.desc,
			    "Print extra event descriptions. --no-desc to not print."),
		OPT_BOOLEAN('v', "long-desc", &long_desc_flag,
		OPT_BOOLEAN('v', "long-desc", &ps.long_desc,
			    "Print longer event descriptions."),
		OPT_BOOLEAN(0, "details", &details_flag,
		OPT_BOOLEAN(0, "details", &ps.detailed,
			    "Print information on the perf event names and expressions used internally by events."),
		OPT_BOOLEAN(0, "deprecated", &deprecated,
		OPT_BOOLEAN(0, "deprecated", &ps.deprecated,
			    "Print deprecated events."),
		OPT_STRING(0, "cputype", &hybrid_name, "hybrid cpu type",
			   "Limit PMU or metric printing to the given hybrid PMU (e.g. core or atom)."),
@@ -63,20 +272,28 @@ int cmd_list(int argc, const char **argv)

	setup_pager();

	if (!raw_dump && pager_in_use())
		printf("\nList of pre-defined events (to be used in -e or -M):\n\n");
	if (!ps.name_only)
		setup_pager();

	ps.desc = !ps.long_desc;
	ps.last_topic = strdup("");
	assert(ps.last_topic);
	ps.visited_metrics = strlist__new(NULL, NULL);
	assert(ps.visited_metrics);
	if (unit_name)
		pmu_name = strdup(unit_name);
		ps.pmu_glob = strdup(unit_name);
	else if (hybrid_name) {
		pmu_name = perf_pmu__hybrid_type_to_pmu(hybrid_name);
		if (!pmu_name)
		ps.pmu_glob = perf_pmu__hybrid_type_to_pmu(hybrid_name);
		if (!ps.pmu_glob)
			pr_warning("WARNING: hybrid cputype is not supported!\n");
	}

	print_cb.print_start(&ps);

	if (argc == 0) {
		print_events(NULL, raw_dump, !desc_flag, long_desc_flag,
				details_flag, deprecated, pmu_name);
		ps.metrics = true;
		ps.metricgroups = true;
		print_events(&print_cb, &ps);
		goto out;
	}

@@ -84,31 +301,35 @@ int cmd_list(int argc, const char **argv)
		char *sep, *s;

		if (strcmp(argv[i], "tracepoint") == 0)
			print_tracepoint_events(NULL, NULL, raw_dump);
			print_tracepoint_events(&print_cb, &ps);
		else if (strcmp(argv[i], "hw") == 0 ||
			 strcmp(argv[i], "hardware") == 0)
			print_symbol_events(NULL, PERF_TYPE_HARDWARE,
					event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
			print_symbol_events(&print_cb, &ps, PERF_TYPE_HARDWARE,
					event_symbols_hw, PERF_COUNT_HW_MAX);
		else if (strcmp(argv[i], "sw") == 0 ||
			 strcmp(argv[i], "software") == 0) {
			print_symbol_events(NULL, PERF_TYPE_SOFTWARE,
					event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
			print_tool_events(NULL, raw_dump);
			print_symbol_events(&print_cb, &ps, PERF_TYPE_SOFTWARE,
					event_symbols_sw, PERF_COUNT_SW_MAX);
			print_tool_events(&print_cb, &ps);
		} else if (strcmp(argv[i], "cache") == 0 ||
			 strcmp(argv[i], "hwcache") == 0)
			print_hwcache_events(NULL, raw_dump);
			print_hwcache_events(&print_cb, &ps);
		else if (strcmp(argv[i], "pmu") == 0)
			print_pmu_events(NULL, raw_dump, !desc_flag,
						long_desc_flag, details_flag,
						deprecated, pmu_name);
			print_pmu_events(&print_cb, &ps);
		else if (strcmp(argv[i], "sdt") == 0)
			print_sdt_events(NULL, NULL, raw_dump);
		else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0)
			metricgroup__print(true, false, NULL, raw_dump, details_flag, pmu_name);
		else if (strcmp(argv[i], "metricgroup") == 0 || strcmp(argv[i], "metricgroups") == 0)
			metricgroup__print(false, true, NULL, raw_dump, details_flag, pmu_name);
		else if ((sep = strchr(argv[i], ':')) != NULL) {
			print_sdt_events(&print_cb, &ps);
		else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) {
			ps.metricgroups = false;
			ps.metrics = true;
			metricgroup__print(&print_cb, &ps);
		} else if (strcmp(argv[i], "metricgroup") == 0 ||
			   strcmp(argv[i], "metricgroups") == 0) {
			ps.metricgroups = true;
			ps.metrics = false;
			metricgroup__print(&print_cb, &ps);
		} else if ((sep = strchr(argv[i], ':')) != NULL) {
			int sep_idx;
			char *old_pmu_glob = ps.pmu_glob;

			sep_idx = sep - argv[i];
			s = strdup(argv[i]);
@@ -118,34 +339,42 @@ int cmd_list(int argc, const char **argv)
			}

			s[sep_idx] = '\0';
			print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
			print_sdt_events(s, s + sep_idx + 1, raw_dump);
			metricgroup__print(true, true, s, raw_dump, details_flag, pmu_name);
			ps.pmu_glob = s;
			ps.event_glob = s + sep_idx + 1;
			print_tracepoint_events(&print_cb, &ps);
			print_sdt_events(&print_cb, &ps);
			ps.metrics = true;
			ps.metricgroups = true;
			metricgroup__print(&print_cb, &ps);
			free(s);
			ps.pmu_glob = old_pmu_glob;
		} else {
			if (asprintf(&s, "*%s*", argv[i]) < 0) {
				printf("Critical: Not enough memory! Trying to continue...\n");
				continue;
			}
			print_symbol_events(s, PERF_TYPE_HARDWARE,
					    event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
			print_symbol_events(s, PERF_TYPE_SOFTWARE,
					    event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
			print_tool_events(s, raw_dump);
			print_hwcache_events(s, raw_dump);
			print_pmu_events(s, raw_dump, !desc_flag,
						long_desc_flag,
						details_flag,
						deprecated,
						pmu_name);
			print_tracepoint_events(NULL, s, raw_dump);
			print_sdt_events(NULL, s, raw_dump);
			metricgroup__print(true, true, s, raw_dump, details_flag, pmu_name);
			ps.event_glob = s;
			print_symbol_events(&print_cb, &ps, PERF_TYPE_HARDWARE,
					event_symbols_hw, PERF_COUNT_HW_MAX);
			print_symbol_events(&print_cb, &ps, PERF_TYPE_SOFTWARE,
					event_symbols_sw, PERF_COUNT_SW_MAX);
			print_tool_events(&print_cb, &ps);
			print_hwcache_events(&print_cb, &ps);
			print_pmu_events(&print_cb, &ps);
			print_tracepoint_events(&print_cb, &ps);
			print_sdt_events(&print_cb, &ps);
			ps.metrics = true;
			ps.metricgroups = true;
			metricgroup__print(&print_cb, &ps);
			free(s);
		}
	}

out:
	free(pmu_name);
	print_cb.print_end(&ps);
	free(ps.pmu_glob);
	free(ps.last_topic);
	free(ps.last_metricgroups);
	strlist__delete(ps.visited_metrics);
	return ret;
}
+64 −179
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include "strbuf.h"
#include "pmu.h"
#include "pmu-hybrid.h"
#include "print-events.h"
#include "expr.h"
#include "rblist.h"
#include <string.h>
@@ -353,51 +354,65 @@ static bool match_pe_metric(const struct pmu_event *pe, const char *metric)
	       match_metric(pe->metric_name, metric);
}

/** struct mep - RB-tree node for building printing information. */
struct mep {
	/** nd - RB-tree element. */
	struct rb_node nd;
	const char *name;
	struct strlist *metrics;
	/** @metric_group: Owned metric group name, separated others with ';'. */
	char *metric_group;
	const char *metric_name;
	const char *metric_desc;
	const char *metric_long_desc;
	const char *metric_expr;
	const char *metric_unit;
};

static int mep_cmp(struct rb_node *rb_node, const void *entry)
{
	struct mep *a = container_of(rb_node, struct mep, nd);
	struct mep *b = (struct mep *)entry;
	int ret;

	return strcmp(a->name, b->name);
	ret = strcmp(a->metric_group, b->metric_group);
	if (ret)
		return ret;

	return strcmp(a->metric_name, b->metric_name);
}

static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
					const void *entry)
static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry)
{
	struct mep *me = malloc(sizeof(struct mep));

	if (!me)
		return NULL;

	memcpy(me, entry, sizeof(struct mep));
	me->name = strdup(me->name);
	if (!me->name)
		goto out_me;
	me->metrics = strlist__new(NULL, NULL);
	if (!me->metrics)
		goto out_name;
	return &me->nd;
out_name:
	zfree(&me->name);
out_me:
}

static void mep_delete(struct rblist *rl __maybe_unused,
		       struct rb_node *nd)
{
	struct mep *me = container_of(nd, struct mep, nd);

	zfree(&me->metric_group);
	free(me);
	return NULL;
}

static struct mep *mep_lookup(struct rblist *groups, const char *name)
static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
			      const char *metric_name)
{
	struct rb_node *nd;
	struct mep me = {
		.name = name
		.metric_group = strdup(metric_group),
		.metric_name = metric_name,
	};
	nd = rblist__find(groups, &me);
	if (nd)
	if (nd) {
		free(me.metric_group);
		return container_of(nd, struct mep, nd);
	}
	rblist__add_node(groups, &me);
	nd = rblist__find(groups, &me);
	if (nd)
@@ -405,107 +420,37 @@ static struct mep *mep_lookup(struct rblist *groups, const char *name)
	return NULL;
}

static void mep_delete(struct rblist *rl __maybe_unused,
		       struct rb_node *nd)
{
	struct mep *me = container_of(nd, struct mep, nd);

	strlist__delete(me->metrics);
	zfree(&me->name);
	free(me);
}

static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
{
	struct str_node *sn;
	int n = 0;

	strlist__for_each_entry (sn, metrics) {
		if (raw)
			printf("%s%s", n > 0 ? " " : "", sn->s);
		else
			printf("  %s\n", sn->s);
		n++;
	}
	if (raw)
		putchar('\n');
}

static int metricgroup__print_pmu_event(const struct pmu_event *pe,
					bool metricgroups, char *filter,
					bool raw, bool details,
					struct rblist *groups,
					struct strlist *metriclist)
static int metricgroup__add_to_mep_groups(const struct pmu_event *pe,
					struct rblist *groups)
{
	const char *g;
	char *omg, *mg;

	g = pe->metric_group;
	if (!g && pe->metric_name) {
		if (pe->name)
			return 0;
		g = "No_group";
	}

	if (!g)
		return 0;

	mg = strdup(g);

	mg = strdup(pe->metric_group ?: "No_group");
	if (!mg)
		return -ENOMEM;
	omg = mg;
	while ((g = strsep(&mg, ";")) != NULL) {
		struct mep *me;
		char *s;

		g = skip_spaces(g);
		if (*g == 0)
			g = "No_group";
		if (filter && !strstr(g, filter))
			continue;
		if (raw)
			s = (char *)pe->metric_name;
		else {
			if (asprintf(&s, "%s\n%*s%s]",
				     pe->metric_name, 8, "[", pe->desc) < 0)
				return -1;
			if (details) {
				if (asprintf(&s, "%s\n%*s%s]",
					     s, 8, "[", pe->metric_expr) < 0)
					return -1;
			}
		}

		if (!s)
			continue;
		if (strlen(g))
			me = mep_lookup(groups, g, pe->metric_name);
		else
			me = mep_lookup(groups, "No_group", pe->metric_name);

		if (!metricgroups) {
			strlist__add(metriclist, s);
		} else {
			me = mep_lookup(groups, g);
			if (!me)
				continue;
			strlist__add(me->metrics, s);
		if (me) {
			me->metric_desc = pe->desc;
			me->metric_long_desc = pe->long_desc;
			me->metric_expr = pe->metric_expr;
			me->metric_unit = pe->unit;
		}

		if (!raw)
			free(s);
	}
	free(omg);

	return 0;
}

struct metricgroup_print_sys_idata {
	struct strlist *metriclist;
	char *filter;
	struct rblist *groups;
	bool metricgroups;
	bool raw;
	bool details;
};

struct metricgroup_iter_data {
	pmu_event_iter_fn fn;
	void *data;
@@ -528,61 +473,26 @@ static int metricgroup__sys_event_iter(const struct pmu_event *pe,

		return d->fn(pe, table, d->data);
	}

	return 0;
}

static int metricgroup__print_sys_event_iter(const struct pmu_event *pe,
					     const struct pmu_events_table *table __maybe_unused,
					     void *data)
{
	struct metricgroup_print_sys_idata *d = data;

	return metricgroup__print_pmu_event(pe, d->metricgroups, d->filter, d->raw,
				     d->details, d->groups, d->metriclist);
}

struct metricgroup_print_data {
	const char *pmu_name;
	struct strlist *metriclist;
	char *filter;
	struct rblist *groups;
	bool metricgroups;
	bool raw;
	bool details;
};

static int metricgroup__print_callback(const struct pmu_event *pe,
static int metricgroup__add_to_mep_groups_callback(const struct pmu_event *pe,
						const struct pmu_events_table *table __maybe_unused,
						void *vdata)
{
	struct metricgroup_print_data *data = vdata;
	const char *pmu = pe->pmu ?: "cpu";

	if (!pe->metric_expr)
		return 0;
	struct rblist *groups = vdata;

	if (data->pmu_name && strcmp(data->pmu_name, pmu))
	if (!pe->metric_name)
		return 0;

	return metricgroup__print_pmu_event(pe, data->metricgroups, data->filter,
					    data->raw, data->details, data->groups,
					    data->metriclist);
	return metricgroup__add_to_mep_groups(pe, groups);
}

void metricgroup__print(bool metrics, bool metricgroups, char *filter,
			bool raw, bool details, const char *pmu_name)
void metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
{
	struct rblist groups;
	struct rb_node *node, *next;
	struct strlist *metriclist = NULL;
	const struct pmu_events_table *table;

	if (!metricgroups) {
		metriclist = strlist__new(NULL, NULL);
		if (!metriclist)
			return;
	}
	struct rb_node *node, *next;

	rblist__init(&groups);
	groups.node_new = mep_new;
@@ -590,56 +500,31 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
	groups.node_delete = mep_delete;
	table = pmu_events_table__find();
	if (table) {
		struct metricgroup_print_data data = {
			.pmu_name = pmu_name,
			.metriclist = metriclist,
			.metricgroups = metricgroups,
			.filter = filter,
			.raw = raw,
			.details = details,
			.groups = &groups,
		};

		pmu_events_table_for_each_event(table,
						metricgroup__print_callback,
						&data);
						metricgroup__add_to_mep_groups_callback,
						&groups);
	}
	{
		struct metricgroup_iter_data data = {
			.fn = metricgroup__print_sys_event_iter,
			.data = (void *) &(struct metricgroup_print_sys_idata){
				.metriclist = metriclist,
				.metricgroups = metricgroups,
				.filter = filter,
				.raw = raw,
				.details = details,
				.groups = &groups,
			},
			.fn = metricgroup__add_to_mep_groups_callback,
			.data = &groups,
		};

		pmu_for_each_sys_event(metricgroup__sys_event_iter, &data);
	}

	if (!filter || !rblist__empty(&groups)) {
		if (metricgroups && !raw)
			printf("\nMetric Groups:\n\n");
		else if (metrics && !raw)
			printf("\nMetrics:\n\n");
	}

	for (node = rb_first_cached(&groups.entries); node; node = next) {
		struct mep *me = container_of(node, struct mep, nd);

		if (metricgroups)
			printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
		if (metrics)
			metricgroup__print_strlist(me->metrics, raw);
		print_cb->print_metric(print_state,
				me->metric_group,
				me->metric_name,
				me->metric_desc,
				me->metric_long_desc,
				me->metric_expr,
				me->metric_unit);
		next = rb_next(node);
		rblist__remove_node(&groups, node);
	}
	if (!metricgroups)
		metricgroup__print_strlist(metriclist, raw);
	strlist__delete(metriclist);
}

static const char *code_characters = ",-=@";
+2 −2
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
struct evlist;
struct evsel;
struct option;
struct print_callbacks;
struct rblist;
struct cgroup;

@@ -78,8 +79,7 @@ int metricgroup__parse_groups_test(struct evlist *evlist,
				   bool metric_no_merge,
				   struct rblist *metric_events);

void metricgroup__print(bool metrics, bool groups, char *filter,
			bool raw, bool details, const char *pmu_name);
void metricgroup__print(const struct print_callbacks *print_cb, void *print_state);
bool metricgroup__has_metric(const char *metric);
int arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused);
void metricgroup__rblist_exit(struct rblist *metric_events);
+52 −93

File changed.

Preview size limit exceeded, changes collapsed.

+2 −3
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@

struct evsel_config_term;
struct perf_cpu_map;
struct print_callbacks;

enum {
	PERF_PMU_FORMAT_VALUE_CONFIG,
@@ -225,9 +226,7 @@ void perf_pmu__del_formats(struct list_head *formats);
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);

bool is_pmu_core(const char *name);
void print_pmu_events(const char *event_glob, bool name_only, bool quiet,
		      bool long_desc, bool details_flag,
		      bool deprecated, const char *pmu_name);
void print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
bool pmu_have_event(const char *pname, const char *name);

int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4);
Loading