xref: /openbmc/linux/tools/perf/builtin-list.c (revision 7effbd18)
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 {
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 		free(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 				printf("\n%s:\n", group);
197 			else
198 				printf("%s\n", group);
199 		}
200 		free(print_state->last_metricgroups);
201 		print_state->last_metricgroups = strdup(group ?: "");
202 	}
203 	if (!print_state->metrics)
204 		return;
205 
206 	if (print_state->name_only) {
207 		if (print_state->metrics &&
208 		    !strlist__has_entry(print_state->visited_metrics, name)) {
209 			printf("%s ", name);
210 			strlist__add(print_state->visited_metrics, name);
211 		}
212 		return;
213 	}
214 	printf("  %s\n", name);
215 
216 	if (desc && print_state->desc) {
217 		printf("%*s", 8, "[");
218 		wordwrap(desc, 8, pager_get_columns(), 0);
219 		printf("]\n");
220 	}
221 	if (long_desc && print_state->long_desc) {
222 		printf("%*s", 8, "[");
223 		wordwrap(long_desc, 8, pager_get_columns(), 0);
224 		printf("]\n");
225 	}
226 	if (expr && print_state->detailed) {
227 		printf("%*s", 8, "[");
228 		wordwrap(expr, 8, pager_get_columns(), 0);
229 		printf("]\n");
230 	}
231 	if (threshold && print_state->detailed) {
232 		printf("%*s", 8, "[");
233 		wordwrap(threshold, 8, pager_get_columns(), 0);
234 		printf("]\n");
235 	}
236 }
237 
238 struct json_print_state {
239 	/** Should a separator be printed prior to the next item? */
240 	bool need_sep;
241 };
242 
243 static void json_print_start(void *print_state __maybe_unused)
244 {
245 	printf("[\n");
246 }
247 
248 static void json_print_end(void *ps)
249 {
250 	struct json_print_state *print_state = ps;
251 
252 	printf("%s]\n", print_state->need_sep ? "\n" : "");
253 }
254 
255 static void fix_escape_printf(struct strbuf *buf, const char *fmt, ...)
256 {
257 	va_list args;
258 
259 	va_start(args, fmt);
260 	strbuf_setlen(buf, 0);
261 	for (size_t fmt_pos = 0; fmt_pos < strlen(fmt); fmt_pos++) {
262 		switch (fmt[fmt_pos]) {
263 		case '%':
264 			fmt_pos++;
265 			switch (fmt[fmt_pos]) {
266 			case 's': {
267 				const char *s = va_arg(args, const char*);
268 
269 				strbuf_addstr(buf, s);
270 				break;
271 			}
272 			case 'S': {
273 				const char *s = va_arg(args, const char*);
274 
275 				for (size_t s_pos = 0; s_pos < strlen(s); s_pos++) {
276 					switch (s[s_pos]) {
277 					case '\n':
278 						strbuf_addstr(buf, "\\n");
279 						break;
280 					case '\\':
281 						__fallthrough;
282 					case '\"':
283 						strbuf_addch(buf, '\\');
284 						__fallthrough;
285 					default:
286 						strbuf_addch(buf, s[s_pos]);
287 						break;
288 					}
289 				}
290 				break;
291 			}
292 			default:
293 				pr_err("Unexpected format character '%c'\n", fmt[fmt_pos]);
294 				strbuf_addch(buf, '%');
295 				strbuf_addch(buf, fmt[fmt_pos]);
296 			}
297 			break;
298 		default:
299 			strbuf_addch(buf, fmt[fmt_pos]);
300 			break;
301 		}
302 	}
303 	va_end(args);
304 	fputs(buf->buf, stdout);
305 }
306 
307 static void json_print_event(void *ps, const char *pmu_name, const char *topic,
308 			     const char *event_name, const char *event_alias,
309 			     const char *scale_unit,
310 			     bool deprecated, const char *event_type_desc,
311 			     const char *desc, const char *long_desc,
312 			     const char *encoding_desc)
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 	printf("%s}", need_sep ? "\n" : "");
370 	strbuf_release(&buf);
371 }
372 
373 static void json_print_metric(void *ps __maybe_unused, const char *group,
374 			      const char *name, const char *desc,
375 			      const char *long_desc, const char *expr,
376 			      const char *threshold, const char *unit)
377 {
378 	struct json_print_state *print_state = ps;
379 	bool need_sep = false;
380 	struct strbuf buf;
381 
382 	strbuf_init(&buf, 0);
383 	printf("%s{\n", print_state->need_sep ? ",\n" : "");
384 	print_state->need_sep = true;
385 	if (group) {
386 		fix_escape_printf(&buf, "\t\"MetricGroup\": \"%S\"", group);
387 		need_sep = true;
388 	}
389 	if (name) {
390 		fix_escape_printf(&buf, "%s\t\"MetricName\": \"%S\"", need_sep ? ",\n" : "", name);
391 		need_sep = true;
392 	}
393 	if (expr) {
394 		fix_escape_printf(&buf, "%s\t\"MetricExpr\": \"%S\"", need_sep ? ",\n" : "", expr);
395 		need_sep = true;
396 	}
397 	if (threshold) {
398 		fix_escape_printf(&buf, "%s\t\"MetricThreshold\": \"%S\"", need_sep ? ",\n" : "",
399 				  threshold);
400 		need_sep = true;
401 	}
402 	if (unit) {
403 		fix_escape_printf(&buf, "%s\t\"ScaleUnit\": \"%S\"", need_sep ? ",\n" : "", unit);
404 		need_sep = true;
405 	}
406 	if (desc) {
407 		fix_escape_printf(&buf, "%s\t\"BriefDescription\": \"%S\"", need_sep ? ",\n" : "",
408 				  desc);
409 		need_sep = true;
410 	}
411 	if (long_desc) {
412 		fix_escape_printf(&buf, "%s\t\"PublicDescription\": \"%S\"", need_sep ? ",\n" : "",
413 				  long_desc);
414 		need_sep = true;
415 	}
416 	printf("%s}", need_sep ? "\n" : "");
417 	strbuf_release(&buf);
418 }
419 
420 int cmd_list(int argc, const char **argv)
421 {
422 	int i, ret = 0;
423 	struct print_state default_ps = {};
424 	struct print_state json_ps = {};
425 	void *ps = &default_ps;
426 	struct print_callbacks print_cb = {
427 		.print_start = default_print_start,
428 		.print_end = default_print_end,
429 		.print_event = default_print_event,
430 		.print_metric = default_print_metric,
431 	};
432 	const char *hybrid_name = NULL;
433 	const char *unit_name = NULL;
434 	bool json = false;
435 	struct option list_options[] = {
436 		OPT_BOOLEAN(0, "raw-dump", &default_ps.name_only, "Dump raw events"),
437 		OPT_BOOLEAN('j', "json", &json, "JSON encode events and metrics"),
438 		OPT_BOOLEAN('d', "desc", &default_ps.desc,
439 			    "Print extra event descriptions. --no-desc to not print."),
440 		OPT_BOOLEAN('v', "long-desc", &default_ps.long_desc,
441 			    "Print longer event descriptions."),
442 		OPT_BOOLEAN(0, "details", &default_ps.detailed,
443 			    "Print information on the perf event names and expressions used internally by events."),
444 		OPT_BOOLEAN(0, "deprecated", &default_ps.deprecated,
445 			    "Print deprecated events."),
446 		OPT_STRING(0, "cputype", &hybrid_name, "hybrid cpu type",
447 			   "Limit PMU or metric printing to the given hybrid PMU (e.g. core or atom)."),
448 		OPT_STRING(0, "unit", &unit_name, "PMU name",
449 			   "Limit PMU or metric printing to the specified PMU."),
450 		OPT_INCR(0, "debug", &verbose,
451 			     "Enable debugging output"),
452 		OPT_END()
453 	};
454 	const char * const list_usage[] = {
455 		"perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob]",
456 		NULL
457 	};
458 
459 	set_option_flag(list_options, 0, "raw-dump", PARSE_OPT_HIDDEN);
460 	/* Hide hybrid flag for the more generic 'unit' flag. */
461 	set_option_flag(list_options, 0, "cputype", PARSE_OPT_HIDDEN);
462 
463 	argc = parse_options(argc, argv, list_options, list_usage,
464 			     PARSE_OPT_STOP_AT_NON_OPTION);
465 
466 	setup_pager();
467 
468 	if (!default_ps.name_only)
469 		setup_pager();
470 
471 	if (json) {
472 		print_cb = (struct print_callbacks){
473 			.print_start = json_print_start,
474 			.print_end = json_print_end,
475 			.print_event = json_print_event,
476 			.print_metric = json_print_metric,
477 		};
478 		ps = &json_ps;
479 	} else {
480 		default_ps.desc = !default_ps.long_desc;
481 		default_ps.last_topic = strdup("");
482 		assert(default_ps.last_topic);
483 		default_ps.visited_metrics = strlist__new(NULL, NULL);
484 		assert(default_ps.visited_metrics);
485 		if (unit_name)
486 			default_ps.pmu_glob = strdup(unit_name);
487 		else if (hybrid_name) {
488 			default_ps.pmu_glob = perf_pmu__hybrid_type_to_pmu(hybrid_name);
489 			if (!default_ps.pmu_glob)
490 				pr_warning("WARNING: hybrid cputype is not supported!\n");
491 		}
492 	}
493 	print_cb.print_start(ps);
494 
495 	if (argc == 0) {
496 		default_ps.metrics = true;
497 		default_ps.metricgroups = true;
498 		print_events(&print_cb, ps);
499 		goto out;
500 	}
501 
502 	for (i = 0; i < argc; ++i) {
503 		char *sep, *s;
504 
505 		if (strcmp(argv[i], "tracepoint") == 0)
506 			print_tracepoint_events(&print_cb, ps);
507 		else if (strcmp(argv[i], "hw") == 0 ||
508 			 strcmp(argv[i], "hardware") == 0)
509 			print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE,
510 					event_symbols_hw, PERF_COUNT_HW_MAX);
511 		else if (strcmp(argv[i], "sw") == 0 ||
512 			 strcmp(argv[i], "software") == 0) {
513 			print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
514 					event_symbols_sw, PERF_COUNT_SW_MAX);
515 			print_tool_events(&print_cb, ps);
516 		} else if (strcmp(argv[i], "cache") == 0 ||
517 			 strcmp(argv[i], "hwcache") == 0)
518 			print_hwcache_events(&print_cb, ps);
519 		else if (strcmp(argv[i], "pmu") == 0)
520 			print_pmu_events(&print_cb, ps);
521 		else if (strcmp(argv[i], "sdt") == 0)
522 			print_sdt_events(&print_cb, ps);
523 		else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) {
524 			default_ps.metricgroups = false;
525 			default_ps.metrics = true;
526 			metricgroup__print(&print_cb, ps);
527 		} else if (strcmp(argv[i], "metricgroup") == 0 ||
528 			   strcmp(argv[i], "metricgroups") == 0) {
529 			default_ps.metricgroups = true;
530 			default_ps.metrics = false;
531 			metricgroup__print(&print_cb, ps);
532 		} else if ((sep = strchr(argv[i], ':')) != NULL) {
533 			char *old_pmu_glob = default_ps.pmu_glob;
534 
535 			default_ps.event_glob = strdup(argv[i]);
536 			if (!default_ps.event_glob) {
537 				ret = -1;
538 				goto out;
539 			}
540 
541 			print_tracepoint_events(&print_cb, ps);
542 			print_sdt_events(&print_cb, ps);
543 			default_ps.metrics = true;
544 			default_ps.metricgroups = true;
545 			metricgroup__print(&print_cb, ps);
546 			zfree(&default_ps.event_glob);
547 			default_ps.pmu_glob = old_pmu_glob;
548 		} else {
549 			if (asprintf(&s, "*%s*", argv[i]) < 0) {
550 				printf("Critical: Not enough memory! Trying to continue...\n");
551 				continue;
552 			}
553 			default_ps.event_glob = s;
554 			print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE,
555 					event_symbols_hw, PERF_COUNT_HW_MAX);
556 			print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
557 					event_symbols_sw, PERF_COUNT_SW_MAX);
558 			print_tool_events(&print_cb, ps);
559 			print_hwcache_events(&print_cb, ps);
560 			print_pmu_events(&print_cb, ps);
561 			print_tracepoint_events(&print_cb, ps);
562 			print_sdt_events(&print_cb, ps);
563 			default_ps.metrics = true;
564 			default_ps.metricgroups = true;
565 			metricgroup__print(&print_cb, ps);
566 			free(s);
567 		}
568 	}
569 
570 out:
571 	print_cb.print_end(ps);
572 	free(default_ps.pmu_glob);
573 	free(default_ps.last_topic);
574 	free(default_ps.last_metricgroups);
575 	strlist__delete(default_ps.visited_metrics);
576 	return ret;
577 }
578