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(pmu_name, print_state->pmu_glob)) 117 return; 118 119 if (print_state->event_glob && 120 (!event_name || !strglobmatch(event_name, print_state->event_glob)) && 121 (!event_alias || !strglobmatch(event_alias, print_state->event_glob)) && 122 (!topic || !strglobmatch_nocase(topic, 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(name, print_state->event_glob)) && 196 (!print_state->metricgroups || !group || !strglobmatch(group, 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(print_state->visited_metrics, name)) { 239 fprintf(fp, "%s ", name); 240 strlist__add(print_state->visited_metrics, 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(buf, 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(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(buf, "\\n"); 315 break; 316 case '\\': 317 fallthrough; 318 case '\"': 319 strbuf_addch(buf, '\\'); 320 fallthrough; 321 default: 322 strbuf_addch(buf, 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(buf, '%'); 331 strbuf_addch(buf, fmt[fmt_pos]); 332 } 333 break; 334 default: 335 strbuf_addch(buf, 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, 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); 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, 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); 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(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, 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, ps); 598 else if (strcmp(argv[i], "hw") == 0 || 599 strcmp(argv[i], "hardware") == 0) 600 print_symbol_events(&print_cb, ps, PERF_TYPE_HARDWARE, 601 event_symbols_hw, PERF_COUNT_HW_MAX); 602 else if (strcmp(argv[i], "sw") == 0 || 603 strcmp(argv[i], "software") == 0) { 604 print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE, 605 event_symbols_sw, PERF_COUNT_SW_MAX); 606 print_tool_events(&print_cb, ps); 607 } else if (strcmp(argv[i], "cache") == 0 || 608 strcmp(argv[i], "hwcache") == 0) 609 print_hwcache_events(&print_cb, ps); 610 else if (strcmp(argv[i], "pmu") == 0) 611 perf_pmus__print_pmu_events(&print_cb, ps); 612 else if (strcmp(argv[i], "sdt") == 0) 613 print_sdt_events(&print_cb, 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, 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, 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, ps); 638 print_sdt_events(&print_cb, ps); 639 default_ps.metrics = true; 640 default_ps.metricgroups = true; 641 metricgroup__print(&print_cb, 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, ps, PERF_TYPE_HARDWARE, 651 event_symbols_hw, PERF_COUNT_HW_MAX); 652 print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE, 653 event_symbols_sw, PERF_COUNT_SW_MAX); 654 print_tool_events(&print_cb, ps); 655 print_hwcache_events(&print_cb, ps); 656 perf_pmus__print_pmu_events(&print_cb, ps); 657 print_tracepoint_events(&print_cb, ps); 658 print_sdt_events(&print_cb, ps); 659 default_ps.metrics = true; 660 default_ps.metricgroups = true; 661 metricgroup__print(&print_cb, 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(default_ps.visited_metrics); 672 if (output_path) 673 fclose(default_ps.fp); 674 675 return ret; 676 } 677