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