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