xref: /openbmc/linux/tools/perf/builtin-list.c (revision 3f58ff6b)
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/pmu.h"
15 #include "util/pmu-hybrid.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 				const char *metric_name, const char *metric_expr)
104 {
105 	struct print_state *print_state = ps;
106 	int pos;
107 
108 	if (deprecated && !print_state->deprecated)
109 		return;
110 
111 	if (print_state->pmu_glob && pmu_name && !strglobmatch(pmu_name, print_state->pmu_glob))
112 		return;
113 
114 	if (print_state->event_glob &&
115 	    (!event_name || !strglobmatch(event_name, print_state->event_glob)) &&
116 	    (!event_alias || !strglobmatch(event_alias, print_state->event_glob)) &&
117 	    (!topic || !strglobmatch_nocase(topic, print_state->event_glob)))
118 		return;
119 
120 	if (print_state->name_only) {
121 		if (event_alias && strlen(event_alias))
122 			printf("%s ", event_alias);
123 		else
124 			printf("%s ", event_name);
125 		return;
126 	}
127 
128 	if (strcmp(print_state->last_topic, topic ?: "")) {
129 		if (topic)
130 			printf("\n%s:\n", topic);
131 		free(print_state->last_topic);
132 		print_state->last_topic = strdup(topic ?: "");
133 	}
134 
135 	if (event_alias && strlen(event_alias))
136 		pos = printf("  %s OR %s", event_name, event_alias);
137 	else
138 		pos = printf("  %s", event_name);
139 
140 	if (!topic && event_type_desc) {
141 		for (; pos < 53; pos++)
142 			putchar(' ');
143 		printf("[%s]\n", event_type_desc);
144 	} else
145 		putchar('\n');
146 
147 	if (desc && print_state->desc) {
148 		printf("%*s", 8, "[");
149 		wordwrap(desc, 8, pager_get_columns(), 0);
150 		printf("]\n");
151 	}
152 	long_desc = long_desc ?: desc;
153 	if (long_desc && print_state->long_desc) {
154 		printf("%*s", 8, "[");
155 		wordwrap(long_desc, 8, pager_get_columns(), 0);
156 		printf("]\n");
157 	}
158 
159 	if (print_state->detailed && encoding_desc) {
160 		printf("%*s", 8, "");
161 		wordwrap(encoding_desc, 8, pager_get_columns(), 0);
162 		if (metric_name)
163 			printf(" MetricName: %s", metric_name);
164 		if (metric_expr)
165 			printf(" MetricExpr: %s", metric_expr);
166 		putchar('\n');
167 	}
168 }
169 
170 static void default_print_metric(void *ps,
171 				const char *group,
172 				const char *name,
173 				const char *desc,
174 				const char *long_desc,
175 				const char *expr,
176 				const char *unit __maybe_unused)
177 {
178 	struct print_state *print_state = ps;
179 
180 	if (print_state->event_glob &&
181 	    (!print_state->metrics || !name || !strglobmatch(name, print_state->event_glob)) &&
182 	    (!print_state->metricgroups || !group || !strglobmatch(group, print_state->event_glob)))
183 		return;
184 
185 	if (!print_state->name_only && !print_state->last_metricgroups) {
186 		if (print_state->metricgroups) {
187 			printf("\nMetric Groups:\n");
188 			if (!print_state->metrics)
189 				putchar('\n');
190 		} else {
191 			printf("\nMetrics:\n\n");
192 		}
193 	}
194 	if (!print_state->last_metricgroups ||
195 	    strcmp(print_state->last_metricgroups, group ?: "")) {
196 		if (group && print_state->metricgroups) {
197 			if (print_state->name_only)
198 				printf("%s ", group);
199 			else if (print_state->metrics)
200 				printf("\n%s:\n", group);
201 			else
202 				printf("%s\n", group);
203 		}
204 		free(print_state->last_metricgroups);
205 		print_state->last_metricgroups = strdup(group ?: "");
206 	}
207 	if (!print_state->metrics)
208 		return;
209 
210 	if (print_state->name_only) {
211 		if (print_state->metrics &&
212 		    !strlist__has_entry(print_state->visited_metrics, name)) {
213 			printf("%s ", name);
214 			strlist__add(print_state->visited_metrics, name);
215 		}
216 		return;
217 	}
218 	printf("  %s\n", name);
219 
220 	if (desc && print_state->desc) {
221 		printf("%*s", 8, "[");
222 		wordwrap(desc, 8, pager_get_columns(), 0);
223 		printf("]\n");
224 	}
225 	if (long_desc && print_state->long_desc) {
226 		printf("%*s", 8, "[");
227 		wordwrap(long_desc, 8, pager_get_columns(), 0);
228 		printf("]\n");
229 	}
230 	if (expr && print_state->detailed) {
231 		printf("%*s", 8, "[");
232 		wordwrap(expr, 8, pager_get_columns(), 0);
233 		printf("]\n");
234 	}
235 }
236 
237 struct json_print_state {
238 	/** Should a separator be printed prior to the next item? */
239 	bool need_sep;
240 };
241 
242 static void json_print_start(void *print_state __maybe_unused)
243 {
244 	printf("[\n");
245 }
246 
247 static void json_print_end(void *ps)
248 {
249 	struct json_print_state *print_state = ps;
250 
251 	printf("%s]\n", print_state->need_sep ? "\n" : "");
252 }
253 
254 static void fix_escape_printf(struct strbuf *buf, const char *fmt, ...)
255 {
256 	va_list args;
257 
258 	va_start(args, fmt);
259 	strbuf_setlen(buf, 0);
260 	for (size_t fmt_pos = 0; fmt_pos < strlen(fmt); fmt_pos++) {
261 		switch (fmt[fmt_pos]) {
262 		case '%':
263 			fmt_pos++;
264 			switch (fmt[fmt_pos]) {
265 			case 's': {
266 				const char *s = va_arg(args, const char*);
267 
268 				strbuf_addstr(buf, s);
269 				break;
270 			}
271 			case 'S': {
272 				const char *s = va_arg(args, const char*);
273 
274 				for (size_t s_pos = 0; s_pos < strlen(s); s_pos++) {
275 					switch (s[s_pos]) {
276 					case '\n':
277 						strbuf_addstr(buf, "\\n");
278 						break;
279 					case '\\':
280 						__fallthrough;
281 					case '\"':
282 						strbuf_addch(buf, '\\');
283 						__fallthrough;
284 					default:
285 						strbuf_addch(buf, s[s_pos]);
286 						break;
287 					}
288 				}
289 				break;
290 			}
291 			default:
292 				pr_err("Unexpected format character '%c'\n", fmt[fmt_pos]);
293 				strbuf_addch(buf, '%');
294 				strbuf_addch(buf, fmt[fmt_pos]);
295 			}
296 			break;
297 		default:
298 			strbuf_addch(buf, fmt[fmt_pos]);
299 			break;
300 		}
301 	}
302 	va_end(args);
303 	fputs(buf->buf, stdout);
304 }
305 
306 static void json_print_event(void *ps, const char *pmu_name, const char *topic,
307 			     const char *event_name, const char *event_alias,
308 			     const char *scale_unit,
309 			     bool deprecated, const char *event_type_desc,
310 			     const char *desc, const char *long_desc,
311 			     const char *encoding_desc,
312 			     const char *metric_name, const char *metric_expr)
313 {
314 	struct json_print_state *print_state = ps;
315 	bool need_sep = false;
316 	struct strbuf buf;
317 
318 	strbuf_init(&buf, 0);
319 	printf("%s{\n", print_state->need_sep ? ",\n" : "");
320 	print_state->need_sep = true;
321 	if (pmu_name) {
322 		fix_escape_printf(&buf, "\t\"Unit\": \"%S\"", pmu_name);
323 		need_sep = true;
324 	}
325 	if (topic) {
326 		fix_escape_printf(&buf, "%s\t\"Topic\": \"%S\"", need_sep ? ",\n" : "", topic);
327 		need_sep = true;
328 	}
329 	if (event_name) {
330 		fix_escape_printf(&buf, "%s\t\"EventName\": \"%S\"", need_sep ? ",\n" : "",
331 				  event_name);
332 		need_sep = true;
333 	}
334 	if (event_alias && strlen(event_alias)) {
335 		fix_escape_printf(&buf, "%s\t\"EventAlias\": \"%S\"", need_sep ? ",\n" : "",
336 				  event_alias);
337 		need_sep = true;
338 	}
339 	if (scale_unit && strlen(scale_unit)) {
340 		fix_escape_printf(&buf, "%s\t\"ScaleUnit\": \"%S\"", need_sep ? ",\n" : "",
341 				  scale_unit);
342 		need_sep = true;
343 	}
344 	if (event_type_desc) {
345 		fix_escape_printf(&buf, "%s\t\"EventType\": \"%S\"", need_sep ? ",\n" : "",
346 				  event_type_desc);
347 		need_sep = true;
348 	}
349 	if (deprecated) {
350 		fix_escape_printf(&buf, "%s\t\"Deprecated\": \"%S\"", need_sep ? ",\n" : "",
351 				  deprecated ? "1" : "0");
352 		need_sep = true;
353 	}
354 	if (desc) {
355 		fix_escape_printf(&buf, "%s\t\"BriefDescription\": \"%S\"", need_sep ? ",\n" : "",
356 				  desc);
357 		need_sep = true;
358 	}
359 	if (long_desc) {
360 		fix_escape_printf(&buf, "%s\t\"PublicDescription\": \"%S\"", need_sep ? ",\n" : "",
361 				  long_desc);
362 		need_sep = true;
363 	}
364 	if (encoding_desc) {
365 		fix_escape_printf(&buf, "%s\t\"Encoding\": \"%S\"", need_sep ? ",\n" : "",
366 				  encoding_desc);
367 		need_sep = true;
368 	}
369 	if (metric_name) {
370 		fix_escape_printf(&buf, "%s\t\"MetricName\": \"%S\"", need_sep ? ",\n" : "",
371 				  metric_name);
372 		need_sep = true;
373 	}
374 	if (metric_expr) {
375 		fix_escape_printf(&buf, "%s\t\"MetricExpr\": \"%S\"", need_sep ? ",\n" : "",
376 				  metric_expr);
377 		need_sep = true;
378 	}
379 	printf("%s}", need_sep ? "\n" : "");
380 	strbuf_release(&buf);
381 }
382 
383 static void json_print_metric(void *ps __maybe_unused, const char *group,
384 			      const char *name, const char *desc,
385 			      const char *long_desc, const char *expr,
386 			      const char *unit)
387 {
388 	struct json_print_state *print_state = ps;
389 	bool need_sep = false;
390 	struct strbuf buf;
391 
392 	strbuf_init(&buf, 0);
393 	printf("%s{\n", print_state->need_sep ? ",\n" : "");
394 	print_state->need_sep = true;
395 	if (group) {
396 		fix_escape_printf(&buf, "\t\"MetricGroup\": \"%S\"", group);
397 		need_sep = true;
398 	}
399 	if (name) {
400 		fix_escape_printf(&buf, "%s\t\"MetricName\": \"%S\"", need_sep ? ",\n" : "", name);
401 		need_sep = true;
402 	}
403 	if (expr) {
404 		fix_escape_printf(&buf, "%s\t\"MetricExpr\": \"%S\"", need_sep ? ",\n" : "", expr);
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 *hybrid_name = 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", &hybrid_name, "hybrid cpu type",
452 			   "Limit PMU or metric printing to the given hybrid PMU (e.g. 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 (hybrid_name) {
493 			default_ps.pmu_glob = perf_pmu__hybrid_type_to_pmu(hybrid_name);
494 			if (!default_ps.pmu_glob)
495 				pr_warning("WARNING: hybrid cputype is not supported!\n");
496 		}
497 	}
498 	print_cb.print_start(ps);
499 
500 	if (argc == 0) {
501 		default_ps.metrics = true;
502 		default_ps.metricgroups = true;
503 		print_events(&print_cb, ps);
504 		goto out;
505 	}
506 
507 	for (i = 0; i < argc; ++i) {
508 		char *sep, *s;
509 
510 		if (strcmp(argv[i], "tracepoint") == 0)
511 			print_tracepoint_events(&print_cb, ps);
512 		else if (strcmp(argv[i], "hw") == 0 ||
513 			 strcmp(argv[i], "hardware") == 0)
514 			print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE,
515 					event_symbols_hw, PERF_COUNT_HW_MAX);
516 		else if (strcmp(argv[i], "sw") == 0 ||
517 			 strcmp(argv[i], "software") == 0) {
518 			print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
519 					event_symbols_sw, PERF_COUNT_SW_MAX);
520 			print_tool_events(&print_cb, ps);
521 		} else if (strcmp(argv[i], "cache") == 0 ||
522 			 strcmp(argv[i], "hwcache") == 0)
523 			print_hwcache_events(&print_cb, ps);
524 		else if (strcmp(argv[i], "pmu") == 0)
525 			print_pmu_events(&print_cb, ps);
526 		else if (strcmp(argv[i], "sdt") == 0)
527 			print_sdt_events(&print_cb, ps);
528 		else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) {
529 			default_ps.metricgroups = false;
530 			default_ps.metrics = true;
531 			metricgroup__print(&print_cb, ps);
532 		} else if (strcmp(argv[i], "metricgroup") == 0 ||
533 			   strcmp(argv[i], "metricgroups") == 0) {
534 			default_ps.metricgroups = true;
535 			default_ps.metrics = false;
536 			metricgroup__print(&print_cb, ps);
537 		} else if ((sep = strchr(argv[i], ':')) != NULL) {
538 			char *old_pmu_glob = default_ps.pmu_glob;
539 
540 			default_ps.event_glob = strdup(argv[i]);
541 			if (!default_ps.event_glob) {
542 				ret = -1;
543 				goto out;
544 			}
545 
546 			print_tracepoint_events(&print_cb, ps);
547 			print_sdt_events(&print_cb, ps);
548 			default_ps.metrics = true;
549 			default_ps.metricgroups = true;
550 			metricgroup__print(&print_cb, ps);
551 			zfree(&default_ps.event_glob);
552 			default_ps.pmu_glob = old_pmu_glob;
553 		} else {
554 			if (asprintf(&s, "*%s*", argv[i]) < 0) {
555 				printf("Critical: Not enough memory! Trying to continue...\n");
556 				continue;
557 			}
558 			default_ps.event_glob = s;
559 			print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE,
560 					event_symbols_hw, PERF_COUNT_HW_MAX);
561 			print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
562 					event_symbols_sw, PERF_COUNT_SW_MAX);
563 			print_tool_events(&print_cb, ps);
564 			print_hwcache_events(&print_cb, ps);
565 			print_pmu_events(&print_cb, ps);
566 			print_tracepoint_events(&print_cb, ps);
567 			print_sdt_events(&print_cb, ps);
568 			default_ps.metrics = true;
569 			default_ps.metricgroups = true;
570 			metricgroup__print(&print_cb, ps);
571 			free(s);
572 		}
573 	}
574 
575 out:
576 	print_cb.print_end(ps);
577 	free(default_ps.pmu_glob);
578 	free(default_ps.last_topic);
579 	free(default_ps.last_metricgroups);
580 	strlist__delete(default_ps.visited_metrics);
581 	return ret;
582 }
583