Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf list: Add output file option

Add an option to write the 'perf list' output to a specific file. This
can avoid issues with debug output being written into the output stream.

Signed-off-by: Ian Rogers <irogers@google.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Ross Zwisler <zwisler@chromium.org>
Cc: Shirisha G <shirisha@linux.ibm.com>
Link: https://lore.kernel.org/r/20240124043015.1388867-3-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
79bacb6a 9d95c6be

+133 -82
+4
tools/perf/Documentation/perf-list.txt
··· 47 47 --json:: 48 48 Output in JSON format. 49 49 50 + -o:: 51 + --output=:: 52 + Output file name. By default output is written to stdout. 53 + 50 54 [[EVENT_MODIFIERS]] 51 55 EVENT MODIFIERS 52 56 ---------------
+129 -82
tools/perf/builtin-list.c
··· 30 30 * functions. 31 31 */ 32 32 struct print_state { 33 + /** @fp: File to write output to. */ 34 + FILE *fp; 33 35 /** 34 36 * @pmu_glob: Optionally restrict PMU and metric matching to PMU or 35 37 * debugfs subsystem name. ··· 68 66 { 69 67 struct print_state *print_state = ps; 70 68 71 - if (!print_state->name_only && pager_in_use()) 72 - printf("\nList of pre-defined events (to be used in -e or -M):\n\n"); 69 + if (!print_state->name_only && pager_in_use()) { 70 + fprintf(print_state->fp, 71 + "\nList of pre-defined events (to be used in -e or -M):\n\n"); 72 + } 73 73 } 74 74 75 75 static void default_print_end(void *print_state __maybe_unused) {} 76 76 77 - static void wordwrap(const char *s, int start, int max, int corr) 77 + static void wordwrap(FILE *fp, const char *s, int start, int max, int corr) 78 78 { 79 79 int column = start; 80 80 int n; ··· 86 82 int wlen = strcspn(s, " \t\n"); 87 83 88 84 if ((column + wlen >= max && column > start) || saw_newline) { 89 - printf("\n%*s", start, ""); 85 + fprintf(fp, "\n%*s", start, ""); 90 86 column = start + corr; 91 87 } 92 - n = printf("%s%.*s", column > start ? " " : "", wlen, s); 88 + n = fprintf(fp, "%s%.*s", column > start ? " " : "", wlen, s); 93 89 if (n <= 0) 94 90 break; 95 91 saw_newline = s[wlen] == '\n'; ··· 108 104 { 109 105 struct print_state *print_state = ps; 110 106 int pos; 107 + FILE *fp = print_state->fp; 111 108 112 109 if (deprecated && !print_state->deprecated) 113 110 return; ··· 124 119 125 120 if (print_state->name_only) { 126 121 if (event_alias && strlen(event_alias)) 127 - printf("%s ", event_alias); 122 + fprintf(fp, "%s ", event_alias); 128 123 else 129 - printf("%s ", event_name); 124 + fprintf(fp, "%s ", event_name); 130 125 return; 131 126 } 132 127 133 128 if (strcmp(print_state->last_topic, topic ?: "")) { 134 129 if (topic) 135 - printf("\n%s:\n", topic); 130 + fprintf(fp, "\n%s:\n", topic); 136 131 zfree(&print_state->last_topic); 137 132 print_state->last_topic = strdup(topic ?: ""); 138 133 } 139 134 140 135 if (event_alias && strlen(event_alias)) 141 - pos = printf(" %s OR %s", event_name, event_alias); 136 + pos = fprintf(fp, " %s OR %s", event_name, event_alias); 142 137 else 143 - pos = printf(" %s", event_name); 138 + pos = fprintf(fp, " %s", event_name); 144 139 145 140 if (!topic && event_type_desc) { 146 141 for (; pos < 53; pos++) 147 - putchar(' '); 148 - printf("[%s]\n", event_type_desc); 142 + fputc(' ', fp); 143 + fprintf(fp, "[%s]\n", event_type_desc); 149 144 } else 150 - putchar('\n'); 145 + fputc('\n', fp); 151 146 152 147 if (desc && print_state->desc) { 153 148 char *desc_with_unit = NULL; ··· 160 155 ? "%s. Unit: %s" : "%s Unit: %s", 161 156 desc, pmu_name); 162 157 } 163 - printf("%*s", 8, "["); 164 - wordwrap(desc_len > 0 ? desc_with_unit : desc, 8, pager_get_columns(), 0); 165 - printf("]\n"); 158 + fprintf(fp, "%*s", 8, "["); 159 + wordwrap(fp, desc_len > 0 ? desc_with_unit : desc, 8, pager_get_columns(), 0); 160 + fprintf(fp, "]\n"); 166 161 free(desc_with_unit); 167 162 } 168 163 long_desc = long_desc ?: desc; 169 164 if (long_desc && print_state->long_desc) { 170 - printf("%*s", 8, "["); 171 - wordwrap(long_desc, 8, pager_get_columns(), 0); 172 - printf("]\n"); 165 + fprintf(fp, "%*s", 8, "["); 166 + wordwrap(fp, long_desc, 8, pager_get_columns(), 0); 167 + fprintf(fp, "]\n"); 173 168 } 174 169 175 170 if (print_state->detailed && encoding_desc) { 176 - printf("%*s", 8, ""); 177 - wordwrap(encoding_desc, 8, pager_get_columns(), 0); 178 - putchar('\n'); 171 + fprintf(fp, "%*s", 8, ""); 172 + wordwrap(fp, encoding_desc, 8, pager_get_columns(), 0); 173 + fputc('\n', fp); 179 174 } 180 175 } 181 176 ··· 189 184 const char *unit __maybe_unused) 190 185 { 191 186 struct print_state *print_state = ps; 187 + FILE *fp = print_state->fp; 192 188 193 189 if (print_state->event_glob && 194 190 (!print_state->metrics || !name || !strglobmatch(name, print_state->event_glob)) && ··· 198 192 199 193 if (!print_state->name_only && !print_state->last_metricgroups) { 200 194 if (print_state->metricgroups) { 201 - printf("\nMetric Groups:\n"); 195 + fprintf(fp, "\nMetric Groups:\n"); 202 196 if (!print_state->metrics) 203 - putchar('\n'); 197 + fputc('\n', fp); 204 198 } else { 205 - printf("\nMetrics:\n\n"); 199 + fprintf(fp, "\nMetrics:\n\n"); 206 200 } 207 201 } 208 202 if (!print_state->last_metricgroups || 209 203 strcmp(print_state->last_metricgroups, group ?: "")) { 210 204 if (group && print_state->metricgroups) { 211 205 if (print_state->name_only) 212 - printf("%s ", group); 206 + fprintf(fp, "%s ", group); 213 207 else if (print_state->metrics) { 214 208 const char *gdesc = describe_metricgroup(group); 215 209 216 210 if (gdesc) 217 - printf("\n%s: [%s]\n", group, gdesc); 211 + fprintf(fp, "\n%s: [%s]\n", group, gdesc); 218 212 else 219 - printf("\n%s:\n", group); 213 + fprintf(fp, "\n%s:\n", group); 220 214 } else 221 - printf("%s\n", group); 215 + fprintf(fp, "%s\n", group); 222 216 } 223 217 zfree(&print_state->last_metricgroups); 224 218 print_state->last_metricgroups = strdup(group ?: ""); ··· 229 223 if (print_state->name_only) { 230 224 if (print_state->metrics && 231 225 !strlist__has_entry(print_state->visited_metrics, name)) { 232 - printf("%s ", name); 226 + fprintf(fp, "%s ", name); 233 227 strlist__add(print_state->visited_metrics, name); 234 228 } 235 229 return; 236 230 } 237 - printf(" %s\n", name); 231 + fprintf(fp, " %s\n", name); 238 232 239 233 if (desc && print_state->desc) { 240 - printf("%*s", 8, "["); 241 - wordwrap(desc, 8, pager_get_columns(), 0); 242 - printf("]\n"); 234 + fprintf(fp, "%*s", 8, "["); 235 + wordwrap(fp, desc, 8, pager_get_columns(), 0); 236 + fprintf(fp, "]\n"); 243 237 } 244 238 if (long_desc && print_state->long_desc) { 245 - printf("%*s", 8, "["); 246 - wordwrap(long_desc, 8, pager_get_columns(), 0); 247 - printf("]\n"); 239 + fprintf(fp, "%*s", 8, "["); 240 + wordwrap(fp, long_desc, 8, pager_get_columns(), 0); 241 + fprintf(fp, "]\n"); 248 242 } 249 243 if (expr && print_state->detailed) { 250 - printf("%*s", 8, "["); 251 - wordwrap(expr, 8, pager_get_columns(), 0); 252 - printf("]\n"); 244 + fprintf(fp, "%*s", 8, "["); 245 + wordwrap(fp, expr, 8, pager_get_columns(), 0); 246 + fprintf(fp, "]\n"); 253 247 } 254 248 if (threshold && print_state->detailed) { 255 - printf("%*s", 8, "["); 256 - wordwrap(threshold, 8, pager_get_columns(), 0); 257 - printf("]\n"); 249 + fprintf(fp, "%*s", 8, "["); 250 + wordwrap(fp, threshold, 8, pager_get_columns(), 0); 251 + fprintf(fp, "]\n"); 258 252 } 259 253 } 260 254 261 255 struct json_print_state { 256 + /** @fp: File to write output to. */ 257 + FILE *fp; 262 258 /** Should a separator be printed prior to the next item? */ 263 259 bool need_sep; 264 260 }; 265 261 266 - static void json_print_start(void *print_state __maybe_unused) 262 + static void json_print_start(void *ps) 267 263 { 268 - printf("[\n"); 264 + struct json_print_state *print_state = ps; 265 + FILE *fp = print_state->fp; 266 + 267 + fprintf(fp, "[\n"); 269 268 } 270 269 271 270 static void json_print_end(void *ps) 272 271 { 273 272 struct json_print_state *print_state = ps; 273 + FILE *fp = print_state->fp; 274 274 275 - printf("%s]\n", print_state->need_sep ? "\n" : ""); 275 + fprintf(fp, "%s]\n", print_state->need_sep ? "\n" : ""); 276 276 } 277 277 278 - static void fix_escape_printf(struct strbuf *buf, const char *fmt, ...) 278 + static void fix_escape_fprintf(FILE *fp, struct strbuf *buf, const char *fmt, ...) 279 279 { 280 280 va_list args; 281 281 ··· 330 318 } 331 319 } 332 320 va_end(args); 333 - fputs(buf->buf, stdout); 321 + fputs(buf->buf, fp); 334 322 } 335 323 336 324 static void json_print_event(void *ps, const char *pmu_name, const char *topic, ··· 342 330 { 343 331 struct json_print_state *print_state = ps; 344 332 bool need_sep = false; 333 + FILE *fp = print_state->fp; 345 334 struct strbuf buf; 346 335 347 336 strbuf_init(&buf, 0); 348 - printf("%s{\n", print_state->need_sep ? ",\n" : ""); 337 + fprintf(fp, "%s{\n", print_state->need_sep ? ",\n" : ""); 349 338 print_state->need_sep = true; 350 339 if (pmu_name) { 351 - fix_escape_printf(&buf, "\t\"Unit\": \"%S\"", pmu_name); 340 + fix_escape_fprintf(fp, &buf, "\t\"Unit\": \"%S\"", pmu_name); 352 341 need_sep = true; 353 342 } 354 343 if (topic) { 355 - fix_escape_printf(&buf, "%s\t\"Topic\": \"%S\"", need_sep ? ",\n" : "", topic); 344 + fix_escape_fprintf(fp, &buf, "%s\t\"Topic\": \"%S\"", 345 + need_sep ? ",\n" : "", 346 + topic); 356 347 need_sep = true; 357 348 } 358 349 if (event_name) { 359 - fix_escape_printf(&buf, "%s\t\"EventName\": \"%S\"", need_sep ? ",\n" : "", 360 - event_name); 350 + fix_escape_fprintf(fp, &buf, "%s\t\"EventName\": \"%S\"", 351 + need_sep ? ",\n" : "", 352 + event_name); 361 353 need_sep = true; 362 354 } 363 355 if (event_alias && strlen(event_alias)) { 364 - fix_escape_printf(&buf, "%s\t\"EventAlias\": \"%S\"", need_sep ? ",\n" : "", 365 - event_alias); 356 + fix_escape_fprintf(fp, &buf, "%s\t\"EventAlias\": \"%S\"", 357 + need_sep ? ",\n" : "", 358 + event_alias); 366 359 need_sep = true; 367 360 } 368 361 if (scale_unit && strlen(scale_unit)) { 369 - fix_escape_printf(&buf, "%s\t\"ScaleUnit\": \"%S\"", need_sep ? ",\n" : "", 370 - scale_unit); 362 + fix_escape_fprintf(fp, &buf, "%s\t\"ScaleUnit\": \"%S\"", 363 + need_sep ? ",\n" : "", 364 + scale_unit); 371 365 need_sep = true; 372 366 } 373 367 if (event_type_desc) { 374 - fix_escape_printf(&buf, "%s\t\"EventType\": \"%S\"", need_sep ? ",\n" : "", 375 - event_type_desc); 368 + fix_escape_fprintf(fp, &buf, "%s\t\"EventType\": \"%S\"", 369 + need_sep ? ",\n" : "", 370 + event_type_desc); 376 371 need_sep = true; 377 372 } 378 373 if (deprecated) { 379 - fix_escape_printf(&buf, "%s\t\"Deprecated\": \"%S\"", need_sep ? ",\n" : "", 380 - deprecated ? "1" : "0"); 374 + fix_escape_fprintf(fp, &buf, "%s\t\"Deprecated\": \"%S\"", 375 + need_sep ? ",\n" : "", 376 + deprecated ? "1" : "0"); 381 377 need_sep = true; 382 378 } 383 379 if (desc) { 384 - fix_escape_printf(&buf, "%s\t\"BriefDescription\": \"%S\"", need_sep ? ",\n" : "", 385 - desc); 380 + fix_escape_fprintf(fp, &buf, "%s\t\"BriefDescription\": \"%S\"", 381 + need_sep ? ",\n" : "", 382 + desc); 386 383 need_sep = true; 387 384 } 388 385 if (long_desc) { 389 - fix_escape_printf(&buf, "%s\t\"PublicDescription\": \"%S\"", need_sep ? ",\n" : "", 390 - long_desc); 386 + fix_escape_fprintf(fp, &buf, "%s\t\"PublicDescription\": \"%S\"", 387 + need_sep ? ",\n" : "", 388 + long_desc); 391 389 need_sep = true; 392 390 } 393 391 if (encoding_desc) { 394 - fix_escape_printf(&buf, "%s\t\"Encoding\": \"%S\"", need_sep ? ",\n" : "", 395 - encoding_desc); 392 + fix_escape_fprintf(fp, &buf, "%s\t\"Encoding\": \"%S\"", 393 + need_sep ? ",\n" : "", 394 + encoding_desc); 396 395 need_sep = true; 397 396 } 398 - printf("%s}", need_sep ? "\n" : ""); 397 + fprintf(fp, "%s}", need_sep ? "\n" : ""); 399 398 strbuf_release(&buf); 400 399 } 401 400 ··· 417 394 { 418 395 struct json_print_state *print_state = ps; 419 396 bool need_sep = false; 397 + FILE *fp = print_state->fp; 420 398 struct strbuf buf; 421 399 422 400 strbuf_init(&buf, 0); 423 - printf("%s{\n", print_state->need_sep ? ",\n" : ""); 401 + fprintf(fp, "%s{\n", print_state->need_sep ? ",\n" : ""); 424 402 print_state->need_sep = true; 425 403 if (group) { 426 - fix_escape_printf(&buf, "\t\"MetricGroup\": \"%S\"", group); 404 + fix_escape_fprintf(fp, &buf, "\t\"MetricGroup\": \"%S\"", group); 427 405 need_sep = true; 428 406 } 429 407 if (name) { 430 - fix_escape_printf(&buf, "%s\t\"MetricName\": \"%S\"", need_sep ? ",\n" : "", name); 408 + fix_escape_fprintf(fp, &buf, "%s\t\"MetricName\": \"%S\"", 409 + need_sep ? ",\n" : "", 410 + name); 431 411 need_sep = true; 432 412 } 433 413 if (expr) { 434 - fix_escape_printf(&buf, "%s\t\"MetricExpr\": \"%S\"", need_sep ? ",\n" : "", expr); 414 + fix_escape_fprintf(fp, &buf, "%s\t\"MetricExpr\": \"%S\"", 415 + need_sep ? ",\n" : "", 416 + expr); 435 417 need_sep = true; 436 418 } 437 419 if (threshold) { 438 - fix_escape_printf(&buf, "%s\t\"MetricThreshold\": \"%S\"", need_sep ? ",\n" : "", 439 - threshold); 420 + fix_escape_fprintf(fp, &buf, "%s\t\"MetricThreshold\": \"%S\"", 421 + need_sep ? ",\n" : "", 422 + threshold); 440 423 need_sep = true; 441 424 } 442 425 if (unit) { 443 - fix_escape_printf(&buf, "%s\t\"ScaleUnit\": \"%S\"", need_sep ? ",\n" : "", unit); 426 + fix_escape_fprintf(fp, &buf, "%s\t\"ScaleUnit\": \"%S\"", 427 + need_sep ? ",\n" : "", 428 + unit); 444 429 need_sep = true; 445 430 } 446 431 if (desc) { 447 - fix_escape_printf(&buf, "%s\t\"BriefDescription\": \"%S\"", need_sep ? ",\n" : "", 448 - desc); 432 + fix_escape_fprintf(fp, &buf, "%s\t\"BriefDescription\": \"%S\"", 433 + need_sep ? ",\n" : "", 434 + desc); 449 435 need_sep = true; 450 436 } 451 437 if (long_desc) { 452 - fix_escape_printf(&buf, "%s\t\"PublicDescription\": \"%S\"", need_sep ? ",\n" : "", 453 - long_desc); 438 + fix_escape_fprintf(fp, &buf, "%s\t\"PublicDescription\": \"%S\"", 439 + need_sep ? ",\n" : "", 440 + long_desc); 454 441 need_sep = true; 455 442 } 456 - printf("%s}", need_sep ? "\n" : ""); 443 + fprintf(fp, "%s}", need_sep ? "\n" : ""); 457 444 strbuf_release(&buf); 458 445 } 459 446 ··· 482 449 int cmd_list(int argc, const char **argv) 483 450 { 484 451 int i, ret = 0; 485 - struct print_state default_ps = {}; 486 - struct print_state json_ps = {}; 452 + struct print_state default_ps = { 453 + .fp = stdout, 454 + }; 455 + struct print_state json_ps = { 456 + .fp = stdout, 457 + }; 487 458 void *ps = &default_ps; 488 459 struct print_callbacks print_cb = { 489 460 .print_start = default_print_start, ··· 498 461 }; 499 462 const char *cputype = NULL; 500 463 const char *unit_name = NULL; 464 + const char *output_path = NULL; 501 465 bool json = false; 502 466 struct option list_options[] = { 503 467 OPT_BOOLEAN(0, "raw-dump", &default_ps.name_only, "Dump raw events"), ··· 509 471 "Print longer event descriptions."), 510 472 OPT_BOOLEAN(0, "details", &default_ps.detailed, 511 473 "Print information on the perf event names and expressions used internally by events."), 474 + OPT_STRING('o', "output", &output_path, "file", "output file name"), 512 475 OPT_BOOLEAN(0, "deprecated", &default_ps.deprecated, 513 476 "Print deprecated events."), 514 477 OPT_STRING(0, "cputype", &cputype, "cpu type", ··· 535 496 536 497 argc = parse_options(argc, argv, list_options, list_usage, 537 498 PARSE_OPT_STOP_AT_NON_OPTION); 499 + 500 + if (output_path) { 501 + default_ps.fp = fopen(output_path, "w"); 502 + json_ps.fp = default_ps.fp; 503 + } 538 504 539 505 setup_pager(); 540 506 ··· 662 618 free(default_ps.last_topic); 663 619 free(default_ps.last_metricgroups); 664 620 strlist__delete(default_ps.visited_metrics); 621 + if (output_path) 622 + fclose(default_ps.fp); 623 + 665 624 return ret; 666 625 }