xref: /openbmc/linux/tools/perf/builtin-list.c (revision 49f3806d89e4cf9e330b6f2e39db1c913a8fd25a)
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/string2.h"
19 #include "util/strlist.h"
20 #include "util/strbuf.h"
21 #include <subcmd/pager.h>
22 #include <subcmd/parse-options.h>
23 #include <linux/zalloc.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 
27 /**
28  * struct print_state - State and configuration passed to the default_print
29  * functions.
30  */
31 struct print_state {
32 	/**
33 	 * @pmu_glob: Optionally restrict PMU and metric matching to PMU or
34 	 * debugfs subsystem name.
35 	 */
36 	char *pmu_glob;
37 	/** @event_glob: Optional pattern matching glob. */
38 	char *event_glob;
39 	/** @name_only: Print event or metric names only. */
40 	bool name_only;
41 	/** @desc: Print the event or metric description. */
42 	bool desc;
43 	/** @long_desc: Print longer event or metric description. */
44 	bool long_desc;
45 	/** @deprecated: Print deprecated events or metrics. */
46 	bool deprecated;
47 	/**
48 	 * @detailed: Print extra information on the perf event such as names
49 	 * and expressions used internally by events.
50 	 */
51 	bool detailed;
52 	/** @metrics: Controls printing of metric and metric groups. */
53 	bool metrics;
54 	/** @metricgroups: Controls printing of metric and metric groups. */
55 	bool metricgroups;
56 	/** @last_topic: The last printed event topic. */
57 	char *last_topic;
58 	/** @last_metricgroups: The last printed metric group. */
59 	char *last_metricgroups;
60 	/** @visited_metrics: Metrics that are printed to avoid duplicates. */
61 	struct strlist *visited_metrics;
62 };
63 
64 static void default_print_start(void *ps)
65 {
66 	struct print_state *print_state = ps;
67 
68 	if (!print_state->name_only && pager_in_use())
69 		printf("\nList of pre-defined events (to be used in -e or -M):\n\n");
70 }
71 
72 static void default_print_end(void *print_state __maybe_unused) {}
73 
74 static void wordwrap(const char *s, int start, int max, int corr)
75 {
76 	int column = start;
77 	int n;
78 	bool saw_newline = false;
79 
80 	while (*s) {
81 		int wlen = strcspn(s, " \t\n");
82 
83 		if ((column + wlen >= max && column > start) || saw_newline) {
84 			printf("\n%*s", start, "");
85 			column = start + corr;
86 		}
87 		n = printf("%s%.*s", column > start ? " " : "", wlen, s);
88 		if (n <= 0)
89 			break;
90 		saw_newline = s[wlen] == '\n';
91 		s += wlen;
92 		column += n;
93 		s = skip_spaces(s);
94 	}
95 }
96 
97 static void default_print_event(void *ps, const char *pmu_name, const char *topic,
98 				const char *event_name, const char *event_alias,
99 				const char *scale_unit __maybe_unused,
100 				bool deprecated, const char *event_type_desc,
101 				const char *desc, const char *long_desc,
102 				const char *encoding_desc)
103 {
104 	struct print_state *print_state = ps;
105 	int pos;
106 
107 	if (deprecated && !print_state->deprecated)
108 		return;
109 
110 	if (print_state->pmu_glob && pmu_name && !strglobmatch(pmu_name, print_state->pmu_glob))
111 		return;
112 
113 	if (print_state->event_glob &&
114 	    (!event_name || !strglobmatch(event_name, print_state->event_glob)) &&
115 	    (!event_alias || !strglobmatch(event_alias, print_state->event_glob)) &&
116 	    (!topic || !strglobmatch_nocase(topic, print_state->event_glob)))
117 		return;
118 
119 	if (print_state->name_only) {
120 		if (event_alias && strlen(event_alias))
121 			printf("%s ", event_alias);
122 		else
123 			printf("%s ", event_name);
124 		return;
125 	}
126 
127 	if (strcmp(print_state->last_topic, topic ?: "")) {
128 		if (topic)
129 			printf("\n%s:\n", topic);
130 		zfree(&print_state->last_topic);
131 		print_state->last_topic = strdup(topic ?: "");
132 	}
133 
134 	if (event_alias && strlen(event_alias))
135 		pos = printf("  %s OR %s", event_name, event_alias);
136 	else
137 		pos = printf("  %s", event_name);
138 
139 	if (!topic && event_type_desc) {
140 		for (; pos < 53; pos++)
141 			putchar(' ');
142 		printf("[%s]\n", event_type_desc);
143 	} else
144 		putchar('\n');
145 
146 	if (desc && print_state->desc) {
147 		printf("%*s", 8, "[");
148 		wordwrap(desc, 8, pager_get_columns(), 0);
149 		printf("]\n");
150 	}
151 	long_desc = long_desc ?: desc;
152 	if (long_desc && print_state->long_desc) {
153 		printf("%*s", 8, "[");
154 		wordwrap(long_desc, 8, pager_get_columns(), 0);
155 		printf("]\n");
156 	}
157 
158 	if (print_state->detailed && encoding_desc) {
159 		printf("%*s", 8, "");
160 		wordwrap(encoding_desc, 8, pager_get_columns(), 0);
161 		putchar('\n');
162 	}
163 }
164 
165 static void default_print_metric(void *ps,
166 				const char *group,
167 				const char *name,
168 				const char *desc,
169 				const char *long_desc,
170 				const char *expr,
171 				const char *threshold,
172 				const char *unit __maybe_unused)
173 {
174 	struct print_state *print_state = ps;
175 
176 	if (print_state->event_glob &&
177 	    (!print_state->metrics || !name || !strglobmatch(name, print_state->event_glob)) &&
178 	    (!print_state->metricgroups || !group || !strglobmatch(group, print_state->event_glob)))
179 		return;
180 
181 	if (!print_state->name_only && !print_state->last_metricgroups) {
182 		if (print_state->metricgroups) {
183 			printf("\nMetric Groups:\n");
184 			if (!print_state->metrics)
185 				putchar('\n');
186 		} else {
187 			printf("\nMetrics:\n\n");
188 		}
189 	}
190 	if (!print_state->last_metricgroups ||
191 	    strcmp(print_state->last_metricgroups, group ?: "")) {
192 		if (group && print_state->metricgroups) {
193 			if (print_state->name_only)
194 				printf("%s ", group);
195 			else if (print_state->metrics) {
196 				const char *gdesc = describe_metricgroup(group);
197 
198 				if (gdesc)
199 					printf("\n%s: [%s]\n", group, gdesc);
200 				else
201 					printf("\n%s:\n", group);
202 			} else
203 				printf("%s\n", group);
204 		}
205 		zfree(&print_state->last_metricgroups);
206 		print_state->last_metricgroups = strdup(group ?: "");
207 	}
208 	if (!print_state->metrics)
209 		return;
210 
211 	if (print_state->name_only) {
212 		if (print_state->metrics &&
213 		    !strlist__has_entry(print_state->visited_metrics, name)) {
214 			printf("%s ", name);
215 			strlist__add(print_state->visited_metrics, name);
216 		}
217 		return;
218 	}
219 	printf("  %s\n", name);
220 
221 	if (desc && print_state->desc) {
222 		printf("%*s", 8, "[");
223 		wordwrap(desc, 8, pager_get_columns(), 0);
224 		printf("]\n");
225 	}
226 	if (long_desc && print_state->long_desc) {
227 		printf("%*s", 8, "[");
228 		wordwrap(long_desc, 8, pager_get_columns(), 0);
229 		printf("]\n");
230 	}
231 	if (expr && print_state->detailed) {
232 		printf("%*s", 8, "[");
233 		wordwrap(expr, 8, pager_get_columns(), 0);
234 		printf("]\n");
235 	}
236 	if (threshold && print_state->detailed) {
237 		printf("%*s", 8, "[");
238 		wordwrap(threshold, 8, pager_get_columns(), 0);
239 		printf("]\n");
240 	}
241 }
242 
243 struct json_print_state {
244 	/** Should a separator be printed prior to the next item? */
245 	bool need_sep;
246 };
247 
248 static void json_print_start(void *print_state __maybe_unused)
249 {
250 	printf("[\n");
251 }
252 
253 static void json_print_end(void *ps)
254 {
255 	struct json_print_state *print_state = ps;
256 
257 	printf("%s]\n", print_state->need_sep ? "\n" : "");
258 }
259 
260 static void fix_escape_printf(struct strbuf *buf, const char *fmt, ...)
261 {
262 	va_list args;
263 
264 	va_start(args, fmt);
265 	strbuf_setlen(buf, 0);
266 	for (size_t fmt_pos = 0; fmt_pos < strlen(fmt); fmt_pos++) {
267 		switch (fmt[fmt_pos]) {
268 		case '%':
269 			fmt_pos++;
270 			switch (fmt[fmt_pos]) {
271 			case 's': {
272 				const char *s = va_arg(args, const char*);
273 
274 				strbuf_addstr(buf, s);
275 				break;
276 			}
277 			case 'S': {
278 				const char *s = va_arg(args, const char*);
279 
280 				for (size_t s_pos = 0; s_pos < strlen(s); s_pos++) {
281 					switch (s[s_pos]) {
282 					case '\n':
283 						strbuf_addstr(buf, "\\n");
284 						break;
285 					case '\\':
286 						fallthrough;
287 					case '\"':
288 						strbuf_addch(buf, '\\');
289 						fallthrough;
290 					default:
291 						strbuf_addch(buf, s[s_pos]);
292 						break;
293 					}
294 				}
295 				break;
296 			}
297 			default:
298 				pr_err("Unexpected format character '%c'\n", fmt[fmt_pos]);
299 				strbuf_addch(buf, '%');
300 				strbuf_addch(buf, fmt[fmt_pos]);
301 			}
302 			break;
303 		default:
304 			strbuf_addch(buf, fmt[fmt_pos]);
305 			break;
306 		}
307 	}
308 	va_end(args);
309 	fputs(buf->buf, stdout);
310 }
311 
312 static void json_print_event(void *ps, const char *pmu_name, const char *topic,
313 			     const char *event_name, const char *event_alias,
314 			     const char *scale_unit,
315 			     bool deprecated, const char *event_type_desc,
316 			     const char *desc, const char *long_desc,
317 			     const char *encoding_desc)
318 {
319 	struct json_print_state *print_state = ps;
320 	bool need_sep = false;
321 	struct strbuf buf;
322 
323 	strbuf_init(&buf, 0);
324 	printf("%s{\n", print_state->need_sep ? ",\n" : "");
325 	print_state->need_sep = true;
326 	if (pmu_name) {
327 		fix_escape_printf(&buf, "\t\"Unit\": \"%S\"", pmu_name);
328 		need_sep = true;
329 	}
330 	if (topic) {
331 		fix_escape_printf(&buf, "%s\t\"Topic\": \"%S\"", need_sep ? ",\n" : "", topic);
332 		need_sep = true;
333 	}
334 	if (event_name) {
335 		fix_escape_printf(&buf, "%s\t\"EventName\": \"%S\"", need_sep ? ",\n" : "",
336 				  event_name);
337 		need_sep = true;
338 	}
339 	if (event_alias && strlen(event_alias)) {
340 		fix_escape_printf(&buf, "%s\t\"EventAlias\": \"%S\"", need_sep ? ",\n" : "",
341 				  event_alias);
342 		need_sep = true;
343 	}
344 	if (scale_unit && strlen(scale_unit)) {
345 		fix_escape_printf(&buf, "%s\t\"ScaleUnit\": \"%S\"", need_sep ? ",\n" : "",
346 				  scale_unit);
347 		need_sep = true;
348 	}
349 	if (event_type_desc) {
350 		fix_escape_printf(&buf, "%s\t\"EventType\": \"%S\"", need_sep ? ",\n" : "",
351 				  event_type_desc);
352 		need_sep = true;
353 	}
354 	if (deprecated) {
355 		fix_escape_printf(&buf, "%s\t\"Deprecated\": \"%S\"", need_sep ? ",\n" : "",
356 				  deprecated ? "1" : "0");
357 		need_sep = true;
358 	}
359 	if (desc) {
360 		fix_escape_printf(&buf, "%s\t\"BriefDescription\": \"%S\"", need_sep ? ",\n" : "",
361 				  desc);
362 		need_sep = true;
363 	}
364 	if (long_desc) {
365 		fix_escape_printf(&buf, "%s\t\"PublicDescription\": \"%S\"", need_sep ? ",\n" : "",
366 				  long_desc);
367 		need_sep = true;
368 	}
369 	if (encoding_desc) {
370 		fix_escape_printf(&buf, "%s\t\"Encoding\": \"%S\"", need_sep ? ",\n" : "",
371 				  encoding_desc);
372 		need_sep = true;
373 	}
374 	printf("%s}", need_sep ? "\n" : "");
375 	strbuf_release(&buf);
376 }
377 
378 static void json_print_metric(void *ps __maybe_unused, const char *group,
379 			      const char *name, const char *desc,
380 			      const char *long_desc, const char *expr,
381 			      const char *threshold, const char *unit)
382 {
383 	struct json_print_state *print_state = ps;
384 	bool need_sep = false;
385 	struct strbuf buf;
386 
387 	strbuf_init(&buf, 0);
388 	printf("%s{\n", print_state->need_sep ? ",\n" : "");
389 	print_state->need_sep = true;
390 	if (group) {
391 		fix_escape_printf(&buf, "\t\"MetricGroup\": \"%S\"", group);
392 		need_sep = true;
393 	}
394 	if (name) {
395 		fix_escape_printf(&buf, "%s\t\"MetricName\": \"%S\"", need_sep ? ",\n" : "", name);
396 		need_sep = true;
397 	}
398 	if (expr) {
399 		fix_escape_printf(&buf, "%s\t\"MetricExpr\": \"%S\"", need_sep ? ",\n" : "", expr);
400 		need_sep = true;
401 	}
402 	if (threshold) {
403 		fix_escape_printf(&buf, "%s\t\"MetricThreshold\": \"%S\"", need_sep ? ",\n" : "",
404 				  threshold);
405 		need_sep = true;
406 	}
407 	if (unit) {
408 		fix_escape_printf(&buf, "%s\t\"ScaleUnit\": \"%S\"", need_sep ? ",\n" : "", unit);
409 		need_sep = true;
410 	}
411 	if (desc) {
412 		fix_escape_printf(&buf, "%s\t\"BriefDescription\": \"%S\"", need_sep ? ",\n" : "",
413 				  desc);
414 		need_sep = true;
415 	}
416 	if (long_desc) {
417 		fix_escape_printf(&buf, "%s\t\"PublicDescription\": \"%S\"", need_sep ? ",\n" : "",
418 				  long_desc);
419 		need_sep = true;
420 	}
421 	printf("%s}", need_sep ? "\n" : "");
422 	strbuf_release(&buf);
423 }
424 
425 int cmd_list(int argc, const char **argv)
426 {
427 	int i, ret = 0;
428 	struct print_state default_ps = {};
429 	struct print_state json_ps = {};
430 	void *ps = &default_ps;
431 	struct print_callbacks print_cb = {
432 		.print_start = default_print_start,
433 		.print_end = default_print_end,
434 		.print_event = default_print_event,
435 		.print_metric = default_print_metric,
436 	};
437 	const char *cputype = NULL;
438 	const char *unit_name = NULL;
439 	bool json = false;
440 	struct option list_options[] = {
441 		OPT_BOOLEAN(0, "raw-dump", &default_ps.name_only, "Dump raw events"),
442 		OPT_BOOLEAN('j', "json", &json, "JSON encode events and metrics"),
443 		OPT_BOOLEAN('d', "desc", &default_ps.desc,
444 			    "Print extra event descriptions. --no-desc to not print."),
445 		OPT_BOOLEAN('v', "long-desc", &default_ps.long_desc,
446 			    "Print longer event descriptions."),
447 		OPT_BOOLEAN(0, "details", &default_ps.detailed,
448 			    "Print information on the perf event names and expressions used internally by events."),
449 		OPT_BOOLEAN(0, "deprecated", &default_ps.deprecated,
450 			    "Print deprecated events."),
451 		OPT_STRING(0, "cputype", &cputype, "cpu type",
452 			   "Limit PMU or metric printing to the given PMU (e.g. cpu, core or atom)."),
453 		OPT_STRING(0, "unit", &unit_name, "PMU name",
454 			   "Limit PMU or metric printing to the specified PMU."),
455 		OPT_INCR(0, "debug", &verbose,
456 			     "Enable debugging output"),
457 		OPT_END()
458 	};
459 	const char * const list_usage[] = {
460 		"perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob]",
461 		NULL
462 	};
463 
464 	set_option_flag(list_options, 0, "raw-dump", PARSE_OPT_HIDDEN);
465 	/* Hide hybrid flag for the more generic 'unit' flag. */
466 	set_option_flag(list_options, 0, "cputype", PARSE_OPT_HIDDEN);
467 
468 	argc = parse_options(argc, argv, list_options, list_usage,
469 			     PARSE_OPT_STOP_AT_NON_OPTION);
470 
471 	setup_pager();
472 
473 	if (!default_ps.name_only)
474 		setup_pager();
475 
476 	if (json) {
477 		print_cb = (struct print_callbacks){
478 			.print_start = json_print_start,
479 			.print_end = json_print_end,
480 			.print_event = json_print_event,
481 			.print_metric = json_print_metric,
482 		};
483 		ps = &json_ps;
484 	} else {
485 		default_ps.desc = !default_ps.long_desc;
486 		default_ps.last_topic = strdup("");
487 		assert(default_ps.last_topic);
488 		default_ps.visited_metrics = strlist__new(NULL, NULL);
489 		assert(default_ps.visited_metrics);
490 		if (unit_name)
491 			default_ps.pmu_glob = strdup(unit_name);
492 		else if (cputype) {
493 			const struct perf_pmu *pmu = perf_pmus__pmu_for_pmu_filter(cputype);
494 
495 			if (!pmu) {
496 				pr_err("ERROR: cputype is not supported!\n");
497 				ret = -1;
498 				goto out;
499 			}
500 			default_ps.pmu_glob = pmu->name;
501 		}
502 	}
503 	print_cb.print_start(ps);
504 
505 	if (argc == 0) {
506 		default_ps.metrics = true;
507 		default_ps.metricgroups = true;
508 		print_events(&print_cb, ps);
509 		goto out;
510 	}
511 
512 	for (i = 0; i < argc; ++i) {
513 		char *sep, *s;
514 
515 		if (strcmp(argv[i], "tracepoint") == 0)
516 			print_tracepoint_events(&print_cb, ps);
517 		else if (strcmp(argv[i], "hw") == 0 ||
518 			 strcmp(argv[i], "hardware") == 0)
519 			print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE,
520 					event_symbols_hw, PERF_COUNT_HW_MAX);
521 		else if (strcmp(argv[i], "sw") == 0 ||
522 			 strcmp(argv[i], "software") == 0) {
523 			print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
524 					event_symbols_sw, PERF_COUNT_SW_MAX);
525 			print_tool_events(&print_cb, ps);
526 		} else if (strcmp(argv[i], "cache") == 0 ||
527 			 strcmp(argv[i], "hwcache") == 0)
528 			print_hwcache_events(&print_cb, ps);
529 		else if (strcmp(argv[i], "pmu") == 0)
530 			perf_pmus__print_pmu_events(&print_cb, ps);
531 		else if (strcmp(argv[i], "sdt") == 0)
532 			print_sdt_events(&print_cb, ps);
533 		else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) {
534 			default_ps.metricgroups = false;
535 			default_ps.metrics = true;
536 			metricgroup__print(&print_cb, ps);
537 		} else if (strcmp(argv[i], "metricgroup") == 0 ||
538 			   strcmp(argv[i], "metricgroups") == 0) {
539 			default_ps.metricgroups = true;
540 			default_ps.metrics = false;
541 			metricgroup__print(&print_cb, ps);
542 		} else if ((sep = strchr(argv[i], ':')) != NULL) {
543 			char *old_pmu_glob = default_ps.pmu_glob;
544 
545 			default_ps.event_glob = strdup(argv[i]);
546 			if (!default_ps.event_glob) {
547 				ret = -1;
548 				goto out;
549 			}
550 
551 			print_tracepoint_events(&print_cb, ps);
552 			print_sdt_events(&print_cb, ps);
553 			default_ps.metrics = true;
554 			default_ps.metricgroups = true;
555 			metricgroup__print(&print_cb, ps);
556 			zfree(&default_ps.event_glob);
557 			default_ps.pmu_glob = old_pmu_glob;
558 		} else {
559 			if (asprintf(&s, "*%s*", argv[i]) < 0) {
560 				printf("Critical: Not enough memory! Trying to continue...\n");
561 				continue;
562 			}
563 			default_ps.event_glob = s;
564 			print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE,
565 					event_symbols_hw, PERF_COUNT_HW_MAX);
566 			print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
567 					event_symbols_sw, PERF_COUNT_SW_MAX);
568 			print_tool_events(&print_cb, ps);
569 			print_hwcache_events(&print_cb, ps);
570 			perf_pmus__print_pmu_events(&print_cb, ps);
571 			print_tracepoint_events(&print_cb, ps);
572 			print_sdt_events(&print_cb, ps);
573 			default_ps.metrics = true;
574 			default_ps.metricgroups = true;
575 			metricgroup__print(&print_cb, ps);
576 			free(s);
577 		}
578 	}
579 
580 out:
581 	print_cb.print_end(ps);
582 	free(default_ps.pmu_glob);
583 	free(default_ps.last_topic);
584 	free(default_ps.last_metricgroups);
585 	strlist__delete(default_ps.visited_metrics);
586 	return ret;
587 }
588