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
default_print_start(void * ps)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
default_print_end(void * print_state __maybe_unused)72 static void default_print_end(void *print_state __maybe_unused) {}
73
wordwrap(const char * s,int start,int max,int corr)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
default_print_event(void * ps,const char * pmu_name,const char * topic,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)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
default_print_metric(void * ps,const char * group,const char * name,const char * desc,const char * long_desc,const char * expr,const char * unit __maybe_unused)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
json_print_start(void * print_state __maybe_unused)237 static void json_print_start(void *print_state __maybe_unused)
238 {
239 printf("[\n");
240 }
241
json_print_end(void * ps)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
fix_escape_printf(struct strbuf * buf,const char * fmt,...)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
json_print_event(void * ps,const char * pmu_name,const char * topic,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)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
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 * unit)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
cmd_list(int argc,const char ** argv)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