xref: /linux/tools/perf/builtin-list.c (revision 9e906a9dead17d81d6c2687f65e159231d0e3286)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * builtin-list.c
4  *
5  * Builtin list command: list all event types
6  *
7  * Copyright (C) 2009, Thomas Gleixner <tglx@linutronix.de>
8  * Copyright (C) 2008-2009, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
9  * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
10  */
11 #include "builtin.h"
12 
13 #include "util/print-events.h"
14 #include "util/pmus.h"
15 #include "util/pmu.h"
16 #include "util/debug.h"
17 #include "util/metricgroup.h"
18 #include "util/pfm.h"
19 #include "util/string2.h"
20 #include "util/strlist.h"
21 #include "util/strbuf.h"
22 #include "util/tool_pmu.h"
23 #include <subcmd/pager.h>
24 #include <subcmd/parse-options.h>
25 #include <linux/zalloc.h>
26 #include <ctype.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 
30 /**
31  * struct print_state - State and configuration passed to the default_print
32  * functions.
33  */
34 struct print_state {
35 	/** @fp: File to write output to. */
36 	FILE *fp;
37 	/**
38 	 * @pmu_glob: Optionally restrict PMU and metric matching to PMU or
39 	 * debugfs subsystem name.
40 	 */
41 	char *pmu_glob;
42 	/** @event_glob: Optional pattern matching glob. */
43 	char *event_glob;
44 	/** @name_only: Print event or metric names only. */
45 	bool name_only;
46 	/** @desc: Print the event or metric description. */
47 	bool desc;
48 	/** @long_desc: Print longer event or metric description. */
49 	bool long_desc;
50 	/** @deprecated: Print deprecated events or metrics. */
51 	bool deprecated;
52 	/**
53 	 * @detailed: Print extra information on the perf event such as names
54 	 * and expressions used internally by events.
55 	 */
56 	bool detailed;
57 	/** @metrics: Controls printing of metric and metric groups. */
58 	bool metrics;
59 	/** @metricgroups: Controls printing of metric and metric groups. */
60 	bool metricgroups;
61 	/** @exclude_abi: Exclude PMUs with types less than PERF_TYPE_MAX except PERF_TYPE_RAW. */
62 	bool exclude_abi;
63 	/** @last_topic: The last printed event topic. */
64 	char *last_topic;
65 	/** @last_metricgroups: The last printed metric group. */
66 	char *last_metricgroups;
67 	/** @visited_metrics: Metrics that are printed to avoid duplicates. */
68 	struct strlist *visited_metrics;
69 };
70 
default_print_start(void * ps)71 static void default_print_start(void *ps)
72 {
73 	struct print_state *print_state = ps;
74 
75 	if (!print_state->name_only && pager_in_use()) {
76 		fprintf(print_state->fp,
77 			"\nList of pre-defined events (to be used in -e or -M):\n\n");
78 	}
79 }
80 
default_print_end(void * print_state __maybe_unused)81 static void default_print_end(void *print_state __maybe_unused) {}
82 
skip_spaces_or_commas(const char * str)83 static const char *skip_spaces_or_commas(const char *str)
84 {
85 	while (isspace(*str) || *str == ',')
86 		++str;
87 	return str;
88 }
89 
wordwrap(FILE * fp,const char * s,int start,int max,int corr)90 static void wordwrap(FILE *fp, const char *s, int start, int max, int corr)
91 {
92 	int column = start;
93 	int n;
94 	bool saw_newline = false;
95 	bool comma = false;
96 
97 	while (*s) {
98 		int wlen = strcspn(s, " ,\t\n");
99 		const char *sep = comma ? "," : " ";
100 
101 		if ((column + wlen >= max && column > start) || saw_newline) {
102 			fprintf(fp, comma ? ",\n%*s" : "\n%*s", start, "");
103 			column = start + corr;
104 		}
105 		if (column <= start)
106 			sep = "";
107 		n = fprintf(fp, "%s%.*s", sep, wlen, s);
108 		if (n <= 0)
109 			break;
110 		saw_newline = s[wlen] == '\n';
111 		s += wlen;
112 		comma = s[0] == ',';
113 		column += n;
114 		s = skip_spaces_or_commas(s);
115 	}
116 }
117 
default_print_event(void * ps,const char * topic,const char * pmu_name,u32 pmu_type,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)118 static void default_print_event(void *ps, const char *topic,
119 				const char *pmu_name, u32 pmu_type,
120 				const char *event_name, const char *event_alias,
121 				const char *scale_unit __maybe_unused,
122 				bool deprecated, const char *event_type_desc,
123 				const char *desc, const char *long_desc,
124 				const char *encoding_desc)
125 {
126 	struct print_state *print_state = ps;
127 	int pos;
128 	FILE *fp = print_state->fp;
129 
130 	if (deprecated && !print_state->deprecated)
131 		return;
132 
133 	if (print_state->pmu_glob && (!pmu_name || !strglobmatch(pmu_name, print_state->pmu_glob)))
134 		return;
135 
136 	if (print_state->exclude_abi && pmu_type < PERF_TYPE_MAX && pmu_type != PERF_TYPE_RAW)
137 		return;
138 
139 	if (print_state->event_glob &&
140 	    (!event_name || !strglobmatch(event_name, print_state->event_glob)) &&
141 	    (!event_alias || !strglobmatch(event_alias, print_state->event_glob)) &&
142 	    (!topic || !strglobmatch_nocase(topic, print_state->event_glob)))
143 		return;
144 
145 	if (print_state->name_only) {
146 		if (event_alias && strlen(event_alias))
147 			fprintf(fp, "%s ", event_alias);
148 		else
149 			fprintf(fp, "%s ", event_name);
150 		return;
151 	}
152 
153 	if (strcmp(print_state->last_topic, topic ?: "")) {
154 		if (topic)
155 			fprintf(fp, "\n%s:\n", topic);
156 		zfree(&print_state->last_topic);
157 		print_state->last_topic = strdup(topic ?: "");
158 	}
159 
160 	if (event_alias && strlen(event_alias))
161 		pos = fprintf(fp, "  %s OR %s", event_name, event_alias);
162 	else
163 		pos = fprintf(fp, "  %s", event_name);
164 
165 	if (!topic && event_type_desc) {
166 		for (; pos < 53; pos++)
167 			fputc(' ', fp);
168 		fprintf(fp, "[%s]\n", event_type_desc);
169 	} else
170 		fputc('\n', fp);
171 
172 	if (long_desc && print_state->long_desc)
173 		desc = long_desc;
174 
175 	if (desc && (print_state->desc || print_state->long_desc)) {
176 		char *desc_with_unit = NULL;
177 		int desc_len = -1;
178 
179 		if (pmu_name && strcmp(pmu_name, "default_core")) {
180 			desc_len = strlen(desc);
181 			desc_len = asprintf(&desc_with_unit,
182 					    desc_len > 0 && desc[desc_len - 1] != '.'
183 					      ? "%s. Unit: %s" : "%s Unit: %s",
184 					    desc, pmu_name);
185 		}
186 		fprintf(fp, "%*s", 8, "[");
187 		wordwrap(fp, desc_len > 0 ? desc_with_unit : desc, 8, pager_get_columns(), 0);
188 		fprintf(fp, "]\n");
189 		free(desc_with_unit);
190 	}
191 
192 	if (print_state->detailed && encoding_desc) {
193 		fprintf(fp, "%*s", 8, "");
194 		wordwrap(fp, encoding_desc, 8, pager_get_columns(), 0);
195 		fputc('\n', fp);
196 	}
197 }
198 
default_print_metric(void * ps,const char * group,const char * name,const char * desc,const char * long_desc,const char * expr,const char * threshold,const char * unit __maybe_unused,const char * pmu_name __maybe_unused)199 static void default_print_metric(void *ps,
200 				const char *group,
201 				const char *name,
202 				const char *desc,
203 				const char *long_desc,
204 				const char *expr,
205 				const char *threshold,
206 				const char *unit __maybe_unused,
207 				const char *pmu_name __maybe_unused)
208 {
209 	struct print_state *print_state = ps;
210 	FILE *fp = print_state->fp;
211 
212 	if (print_state->event_glob &&
213 	    (!print_state->metrics || !name || !strglobmatch(name, print_state->event_glob)) &&
214 	    (!print_state->metricgroups || !group || !strglobmatch(group, print_state->event_glob)))
215 		return;
216 
217 	if (!print_state->name_only && !print_state->last_metricgroups) {
218 		if (print_state->metricgroups) {
219 			fprintf(fp, "\nMetric Groups:\n");
220 			if (!print_state->metrics)
221 				fputc('\n', fp);
222 		} else {
223 			fprintf(fp, "\nMetrics:\n\n");
224 		}
225 	}
226 	if (!print_state->last_metricgroups ||
227 	    strcmp(print_state->last_metricgroups, group ?: "")) {
228 		if (group && print_state->metricgroups) {
229 			if (print_state->name_only) {
230 				fprintf(fp, "%s ", group);
231 			} else {
232 				const char *gdesc = print_state->desc
233 					? describe_metricgroup(group)
234 					: NULL;
235 				const char *print_colon = "";
236 
237 				if (print_state->metrics) {
238 					print_colon = ":";
239 					fputc('\n', fp);
240 				}
241 
242 				if (gdesc)
243 					fprintf(fp, "%s%s [%s]\n", group, print_colon, gdesc);
244 				else
245 					fprintf(fp, "%s%s\n", group, print_colon);
246 			}
247 		}
248 		zfree(&print_state->last_metricgroups);
249 		print_state->last_metricgroups = strdup(group ?: "");
250 	}
251 	if (!print_state->metrics)
252 		return;
253 
254 	if (print_state->name_only) {
255 		if (print_state->metrics &&
256 		    !strlist__has_entry(print_state->visited_metrics, name)) {
257 			fprintf(fp, "%s ", name);
258 			strlist__add(print_state->visited_metrics, name);
259 		}
260 		return;
261 	}
262 	fprintf(fp, "  %s\n", name);
263 
264 	if (long_desc && print_state->long_desc) {
265 		fprintf(fp, "%*s", 8, "[");
266 		wordwrap(fp, long_desc, 8, pager_get_columns(), 0);
267 		fprintf(fp, "]\n");
268 	} else if (desc && print_state->desc) {
269 		fprintf(fp, "%*s", 8, "[");
270 		wordwrap(fp, desc, 8, pager_get_columns(), 0);
271 		fprintf(fp, "]\n");
272 	}
273 	if (expr && print_state->detailed) {
274 		fprintf(fp, "%*s", 8, "[");
275 		wordwrap(fp, expr, 8, pager_get_columns(), 0);
276 		fprintf(fp, "]\n");
277 	}
278 	if (threshold && print_state->detailed) {
279 		fprintf(fp, "%*s", 8, "[");
280 		wordwrap(fp, threshold, 8, pager_get_columns(), 0);
281 		fprintf(fp, "]\n");
282 	}
283 }
284 
285 struct json_print_state {
286 	/** The shared print_state */
287 	struct print_state common;
288 	/** Should a separator be printed prior to the next item? */
289 	bool need_sep;
290 };
291 
json_print_start(void * ps)292 static void json_print_start(void *ps)
293 {
294 	struct json_print_state *print_state = ps;
295 	FILE *fp = print_state->common.fp;
296 
297 	fprintf(fp, "[\n");
298 }
299 
json_print_end(void * ps)300 static void json_print_end(void *ps)
301 {
302 	struct json_print_state *print_state = ps;
303 	FILE *fp = print_state->common.fp;
304 
305 	fprintf(fp, "%s]\n", print_state->need_sep ? "\n" : "");
306 }
307 
fix_escape_fprintf(FILE * fp,struct strbuf * buf,const char * fmt,...)308 static void fix_escape_fprintf(FILE *fp, struct strbuf *buf, const char *fmt, ...)
309 {
310 	va_list args;
311 
312 	va_start(args, fmt);
313 	strbuf_setlen(buf, 0);
314 	for (size_t fmt_pos = 0; fmt_pos < strlen(fmt); fmt_pos++) {
315 		switch (fmt[fmt_pos]) {
316 		case '%':
317 			fmt_pos++;
318 			switch (fmt[fmt_pos]) {
319 			case 's': {
320 				const char *s = va_arg(args, const char*);
321 
322 				strbuf_addstr(buf, s);
323 				break;
324 			}
325 			case 'S': {
326 				const char *s = va_arg(args, const char*);
327 
328 				for (size_t s_pos = 0; s_pos < strlen(s); s_pos++) {
329 					switch (s[s_pos]) {
330 					case '\n':
331 						strbuf_addstr(buf, "\\n");
332 						break;
333 					case '\r':
334 						strbuf_addstr(buf, "\\r");
335 						break;
336 					case '\\':
337 						fallthrough;
338 					case '\"':
339 						strbuf_addch(buf, '\\');
340 						fallthrough;
341 					default:
342 						strbuf_addch(buf, s[s_pos]);
343 						break;
344 					}
345 				}
346 				break;
347 			}
348 			default:
349 				pr_err("Unexpected format character '%c'\n", fmt[fmt_pos]);
350 				strbuf_addch(buf, '%');
351 				strbuf_addch(buf, fmt[fmt_pos]);
352 			}
353 			break;
354 		default:
355 			strbuf_addch(buf, fmt[fmt_pos]);
356 			break;
357 		}
358 	}
359 	va_end(args);
360 	fputs(buf->buf, fp);
361 }
362 
json_print_event(void * ps,const char * topic,const char * pmu_name,u32 pmu_type __maybe_unused,const char * event_name,const char * event_alias,const char * scale_unit,bool deprecated,const char * event_type_desc,const char * desc,const char * long_desc,const char * encoding_desc)363 static void json_print_event(void *ps, const char *topic,
364 			     const char *pmu_name, u32 pmu_type __maybe_unused,
365 			     const char *event_name, const char *event_alias,
366 			     const char *scale_unit,
367 			     bool deprecated, const char *event_type_desc,
368 			     const char *desc, const char *long_desc,
369 			     const char *encoding_desc)
370 {
371 	struct json_print_state *print_state = ps;
372 	bool need_sep = false;
373 	FILE *fp = print_state->common.fp;
374 	struct strbuf buf;
375 
376 	if (deprecated && !print_state->common.deprecated)
377 		return;
378 
379 	if (print_state->common.pmu_glob &&
380 	    (!pmu_name || !strglobmatch(pmu_name, print_state->common.pmu_glob)))
381 		return;
382 
383 	if (print_state->common.exclude_abi && pmu_type < PERF_TYPE_MAX &&
384 	    pmu_type != PERF_TYPE_RAW)
385 		return;
386 
387 	if (print_state->common.event_glob &&
388 	    (!event_name || !strglobmatch(event_name, print_state->common.event_glob)) &&
389 	    (!event_alias || !strglobmatch(event_alias, print_state->common.event_glob)) &&
390 	    (!topic || !strglobmatch_nocase(topic, print_state->common.event_glob)))
391 		return;
392 
393 	strbuf_init(&buf, 0);
394 	fprintf(fp, "%s{\n", print_state->need_sep ? ",\n" : "");
395 	print_state->need_sep = true;
396 	if (pmu_name) {
397 		fix_escape_fprintf(fp, &buf, "\t\"Unit\": \"%S\"", pmu_name);
398 		need_sep = true;
399 	}
400 	if (topic) {
401 		fix_escape_fprintf(fp, &buf, "%s\t\"Topic\": \"%S\"",
402 				   need_sep ? ",\n" : "",
403 				   topic);
404 		need_sep = true;
405 	}
406 	if (event_name) {
407 		fix_escape_fprintf(fp, &buf, "%s\t\"EventName\": \"%S\"",
408 				   need_sep ? ",\n" : "",
409 				   event_name);
410 		need_sep = true;
411 	}
412 	if (event_alias && strlen(event_alias)) {
413 		fix_escape_fprintf(fp, &buf, "%s\t\"EventAlias\": \"%S\"",
414 				   need_sep ? ",\n" : "",
415 				   event_alias);
416 		need_sep = true;
417 	}
418 	if (scale_unit && strlen(scale_unit)) {
419 		fix_escape_fprintf(fp, &buf, "%s\t\"ScaleUnit\": \"%S\"",
420 				   need_sep ? ",\n" : "",
421 				   scale_unit);
422 		need_sep = true;
423 	}
424 	if (event_type_desc) {
425 		fix_escape_fprintf(fp, &buf, "%s\t\"EventType\": \"%S\"",
426 				   need_sep ? ",\n" : "",
427 				   event_type_desc);
428 		need_sep = true;
429 	}
430 	if (deprecated) {
431 		fix_escape_fprintf(fp, &buf, "%s\t\"Deprecated\": \"%S\"",
432 				   need_sep ? ",\n" : "",
433 				   deprecated ? "1" : "0");
434 		need_sep = true;
435 	}
436 	if (desc) {
437 		fix_escape_fprintf(fp, &buf, "%s\t\"BriefDescription\": \"%S\"",
438 				   need_sep ? ",\n" : "",
439 				   desc);
440 		need_sep = true;
441 	}
442 	if (long_desc) {
443 		fix_escape_fprintf(fp, &buf, "%s\t\"PublicDescription\": \"%S\"",
444 				   need_sep ? ",\n" : "",
445 				   long_desc);
446 		need_sep = true;
447 	}
448 	if (encoding_desc) {
449 		fix_escape_fprintf(fp, &buf, "%s\t\"Encoding\": \"%S\"",
450 				   need_sep ? ",\n" : "",
451 				   encoding_desc);
452 		need_sep = true;
453 	}
454 	fprintf(fp, "%s}", need_sep ? "\n" : "");
455 	strbuf_release(&buf);
456 }
457 
json_print_metric(void * ps __maybe_unused,const char * group,const char * name,const char * desc,const char * long_desc,const char * expr,const char * threshold,const char * unit,const char * pmu_name)458 static void json_print_metric(void *ps __maybe_unused, const char *group,
459 			      const char *name, const char *desc,
460 			      const char *long_desc, const char *expr,
461 			      const char *threshold, const char *unit,
462 			      const char *pmu_name)
463 {
464 	struct json_print_state *print_state = ps;
465 	bool need_sep = false;
466 	FILE *fp = print_state->common.fp;
467 	struct strbuf buf;
468 
469 	if (print_state->common.event_glob &&
470 	    (!print_state->common.metrics || !name ||
471 	     !strglobmatch(name, print_state->common.event_glob)) &&
472 	    (!print_state->common.metricgroups || !group ||
473 	     !strglobmatch(group, print_state->common.event_glob)))
474 		return;
475 
476 	strbuf_init(&buf, 0);
477 	fprintf(fp, "%s{\n", print_state->need_sep ? ",\n" : "");
478 	print_state->need_sep = true;
479 	if (group) {
480 		fix_escape_fprintf(fp, &buf, "\t\"MetricGroup\": \"%S\"", group);
481 		need_sep = true;
482 	}
483 	if (name) {
484 		fix_escape_fprintf(fp, &buf, "%s\t\"MetricName\": \"%S\"",
485 				   need_sep ? ",\n" : "",
486 				   name);
487 		need_sep = true;
488 	}
489 	if (expr) {
490 		fix_escape_fprintf(fp, &buf, "%s\t\"MetricExpr\": \"%S\"",
491 				   need_sep ? ",\n" : "",
492 				   expr);
493 		need_sep = true;
494 	}
495 	if (threshold) {
496 		fix_escape_fprintf(fp, &buf, "%s\t\"MetricThreshold\": \"%S\"",
497 				   need_sep ? ",\n" : "",
498 				   threshold);
499 		need_sep = true;
500 	}
501 	if (unit) {
502 		fix_escape_fprintf(fp, &buf, "%s\t\"ScaleUnit\": \"%S\"",
503 				   need_sep ? ",\n" : "",
504 				   unit);
505 		need_sep = true;
506 	}
507 	if (desc) {
508 		fix_escape_fprintf(fp, &buf, "%s\t\"BriefDescription\": \"%S\"",
509 				   need_sep ? ",\n" : "",
510 				   desc);
511 		need_sep = true;
512 	}
513 	if (long_desc) {
514 		fix_escape_fprintf(fp, &buf, "%s\t\"PublicDescription\": \"%S\"",
515 				   need_sep ? ",\n" : "",
516 				   long_desc);
517 		need_sep = true;
518 	}
519 	if (pmu_name) {
520 		fix_escape_fprintf(fp, &buf, "%s\t\"Unit\": \"%S\"",
521 				   need_sep ? ",\n" : "",
522 				   pmu_name);
523 		need_sep = true;
524 	}
525 	fprintf(fp, "%s}", need_sep ? "\n" : "");
526 	strbuf_release(&buf);
527 }
528 
json_skip_duplicate_pmus(void * ps __maybe_unused)529 static bool json_skip_duplicate_pmus(void *ps __maybe_unused)
530 {
531 	return false;
532 }
533 
default_skip_duplicate_pmus(void * ps)534 static bool default_skip_duplicate_pmus(void *ps)
535 {
536 	struct print_state *print_state = ps;
537 
538 	return !print_state->long_desc;
539 }
540 
cmd_list(int argc,const char ** argv)541 int cmd_list(int argc, const char **argv)
542 {
543 	int i, ret = 0;
544 	struct print_state default_ps = {
545 		.fp = stdout,
546 		.desc = true,
547 	};
548 	struct json_print_state json_ps = {
549 		.common = {
550 			.fp = stdout,
551 		},
552 	};
553 	struct print_state *ps = &default_ps;
554 	struct print_callbacks print_cb = {
555 		.print_start = default_print_start,
556 		.print_end = default_print_end,
557 		.print_event = default_print_event,
558 		.print_metric = default_print_metric,
559 		.skip_duplicate_pmus = default_skip_duplicate_pmus,
560 	};
561 	const char *cputype = NULL;
562 	const char *unit_name = NULL;
563 	const char *output_path = NULL;
564 	bool json = false;
565 	struct option list_options[] = {
566 		OPT_BOOLEAN(0, "raw-dump", &default_ps.name_only, "Dump raw events"),
567 		OPT_BOOLEAN('j', "json", &json, "JSON encode events and metrics"),
568 		OPT_BOOLEAN('d', "desc", &default_ps.desc,
569 			    "Print extra event descriptions. --no-desc to not print."),
570 		OPT_BOOLEAN('v', "long-desc", &default_ps.long_desc,
571 			    "Print longer event descriptions and all similar PMUs with alphanumeric suffixes."),
572 		OPT_BOOLEAN(0, "details", &default_ps.detailed,
573 			    "Print information on the perf event names and expressions used internally by events."),
574 		OPT_STRING('o', "output", &output_path, "file", "output file name"),
575 		OPT_BOOLEAN(0, "deprecated", &default_ps.deprecated,
576 			    "Print deprecated events."),
577 		OPT_STRING(0, "cputype", &cputype, "cpu type",
578 			   "Limit PMU or metric printing to the given PMU (e.g. cpu, core or atom)."),
579 		OPT_STRING(0, "unit", &unit_name, "PMU name",
580 			   "Limit PMU or metric printing to the specified PMU."),
581 		OPT_INCR(0, "debug", &verbose,
582 			     "Enable debugging output"),
583 		OPT_END()
584 	};
585 	const char * const list_usage[] = {
586 #ifdef HAVE_LIBPFM
587 		"perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob|pfm]",
588 #else
589 		"perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob]",
590 #endif
591 		NULL
592 	};
593 
594 	set_option_flag(list_options, 0, "raw-dump", PARSE_OPT_HIDDEN);
595 	/* Hide hybrid flag for the more generic 'unit' flag. */
596 	set_option_flag(list_options, 0, "cputype", PARSE_OPT_HIDDEN);
597 
598 	argc = parse_options(argc, argv, list_options, list_usage,
599 			     PARSE_OPT_STOP_AT_NON_OPTION);
600 
601 	if (json)
602 		ps = &json_ps.common;
603 
604 	if (output_path) {
605 		ps->fp = fopen(output_path, "w");
606 	}
607 
608 	setup_pager();
609 
610 	if (!default_ps.name_only)
611 		setup_pager();
612 
613 	if (json) {
614 		print_cb = (struct print_callbacks){
615 			.print_start = json_print_start,
616 			.print_end = json_print_end,
617 			.print_event = json_print_event,
618 			.print_metric = json_print_metric,
619 			.skip_duplicate_pmus = json_skip_duplicate_pmus,
620 		};
621 	} else {
622 		ps->last_topic = strdup("");
623 		assert(ps->last_topic);
624 		ps->visited_metrics = strlist__new(NULL, NULL);
625 		assert(ps->visited_metrics);
626 		if (unit_name)
627 			ps->pmu_glob = strdup(unit_name);
628 		else if (cputype) {
629 			const struct perf_pmu *pmu = perf_pmus__pmu_for_pmu_filter(cputype);
630 
631 			if (!pmu) {
632 				pr_err("ERROR: cputype is not supported!\n");
633 				ret = -1;
634 				goto out;
635 			}
636 			ps->pmu_glob = strdup(pmu->name);
637 		}
638 	}
639 	print_cb.print_start(ps);
640 
641 	if (argc == 0) {
642 		if (!unit_name) {
643 			ps->metrics = true;
644 			ps->metricgroups = true;
645 		}
646 		print_events(&print_cb, ps);
647 		goto out;
648 	}
649 
650 	for (i = 0; i < argc; ++i) {
651 		char *sep, *s;
652 
653 		if (strcmp(argv[i], "tracepoint") == 0) {
654 			char *old_pmu_glob = default_ps.pmu_glob;
655 
656 			default_ps.pmu_glob = strdup("tracepoint");
657 			if (!default_ps.pmu_glob) {
658 				ret = -1;
659 				goto out;
660 			}
661 			perf_pmus__print_pmu_events(&print_cb, ps);
662 			zfree(&default_ps.pmu_glob);
663 			default_ps.pmu_glob = old_pmu_glob;
664 		} else if (strcmp(argv[i], "hw") == 0 ||
665 			   strcmp(argv[i], "hardware") == 0) {
666 			char *old_event_glob = ps->event_glob;
667 
668 			ps->event_glob = strdup("legacy hardware");
669 			if (!ps->event_glob) {
670 				ret = -1;
671 				goto out;
672 			}
673 			perf_pmus__print_pmu_events(&print_cb, ps);
674 			zfree(&ps->event_glob);
675 			ps->event_glob = old_event_glob;
676 		} else if (strcmp(argv[i], "sw") == 0 ||
677 			 strcmp(argv[i], "software") == 0) {
678 			char *old_pmu_glob = ps->pmu_glob;
679 			static const char * const sw_globs[] = { "software", "tool" };
680 
681 			for (size_t j = 0; j < ARRAY_SIZE(sw_globs); j++) {
682 				ps->pmu_glob = strdup(sw_globs[j]);
683 				if (!ps->pmu_glob) {
684 					ret = -1;
685 					goto out;
686 				}
687 				perf_pmus__print_pmu_events(&print_cb, ps);
688 				zfree(&ps->pmu_glob);
689 			}
690 			ps->pmu_glob = old_pmu_glob;
691 		} else if (strcmp(argv[i], "cache") == 0 ||
692 			   strcmp(argv[i], "hwcache") == 0) {
693 			char *old_event_glob = ps->event_glob;
694 
695 			ps->event_glob = strdup("legacy cache");
696 			if (!ps->event_glob) {
697 				ret = -1;
698 				goto out;
699 			}
700 			perf_pmus__print_pmu_events(&print_cb, ps);
701 			zfree(&ps->event_glob);
702 			ps->event_glob = old_event_glob;
703 		} else if (strcmp(argv[i], "pmu") == 0) {
704 			ps->exclude_abi = true;
705 			perf_pmus__print_pmu_events(&print_cb, ps);
706 			ps->exclude_abi = false;
707 		} else if (strcmp(argv[i], "sdt") == 0)
708 			print_sdt_events(&print_cb, ps);
709 		else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) {
710 			ps->metricgroups = false;
711 			ps->metrics = true;
712 			metricgroup__print(&print_cb, ps);
713 		} else if (strcmp(argv[i], "metricgroup") == 0 ||
714 			   strcmp(argv[i], "metricgroups") == 0) {
715 			ps->metricgroups = true;
716 			ps->metrics = false;
717 			metricgroup__print(&print_cb, ps);
718 		}
719 #ifdef HAVE_LIBPFM
720 		else if (strcmp(argv[i], "pfm") == 0)
721 			print_libpfm_events(&print_cb, ps);
722 #endif
723 		else if ((sep = strchr(argv[i], ':')) != NULL) {
724 			char *old_pmu_glob = ps->pmu_glob;
725 			char *old_event_glob = ps->event_glob;
726 
727 			ps->event_glob = strdup(argv[i]);
728 			if (!ps->event_glob) {
729 				ret = -1;
730 				goto out;
731 			}
732 
733 			ps->pmu_glob = strdup("tracepoint");
734 			if (!ps->pmu_glob) {
735 				zfree(&ps->event_glob);
736 				ret = -1;
737 				goto out;
738 			}
739 			perf_pmus__print_pmu_events(&print_cb, ps);
740 			zfree(&ps->pmu_glob);
741 			ps->pmu_glob = old_pmu_glob;
742 			print_sdt_events(&print_cb, ps);
743 			ps->metrics = true;
744 			ps->metricgroups = true;
745 			metricgroup__print(&print_cb, ps);
746 			zfree(&ps->event_glob);
747 			ps->event_glob = old_event_glob;
748 		} else {
749 			if (asprintf(&s, "*%s*", argv[i]) < 0) {
750 				printf("Critical: Not enough memory! Trying to continue...\n");
751 				continue;
752 			}
753 			ps->event_glob = s;
754 			perf_pmus__print_pmu_events(&print_cb, ps);
755 			print_sdt_events(&print_cb, ps);
756 			ps->metrics = true;
757 			ps->metricgroups = true;
758 			metricgroup__print(&print_cb, ps);
759 			free(s);
760 		}
761 	}
762 
763 out:
764 	print_cb.print_end(ps);
765 	free(ps->pmu_glob);
766 	free(ps->last_topic);
767 	free(ps->last_metricgroups);
768 	strlist__delete(ps->visited_metrics);
769 	if (output_path)
770 		fclose(ps->fp);
771 
772 	return ret;
773 }
774