diff options
author | Stephane Eranian <eranian@google.com> | 2010-12-01 11:49:05 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-12-01 16:47:41 -0500 |
commit | d7470b6afca85ed4388fff57fc9d89f5a3be02ff (patch) | |
tree | f170c6e53e53448649c6658ec0208f051b248a1c /tools | |
parent | 201e0b06efee80ce090579aa165c65c3d0836d95 (diff) |
perf stat: Add csv-style output
This patch adds an option (-x/--field-separator) to print counts using a
CSV-style output. The user can pass a custom separator. This makes it very easy
to import counts directly into your favorite spreadsheet without having to
write scripts.
Example:
$ perf stat --field-separator=, -a -- sleep 1
4009.961740,task-clock-msecs
13,context-switches
2,CPU-migrations
189,page-faults
9596385684,cycles
3493659441,instructions
872897069,branches
41562,branch-misses
22424,cache-references
1289,cache-misses
Works also in non-aggregated mode:
$ perf stat -x , -a -A -- sleep 1
CPU0,1002.526168,task-clock-msecs
CPU1,1002.528365,task-clock-msecs
CPU2,1002.523360,task-clock-msecs
CPU3,1002.519878,task-clock-msecs
CPU0,1,context-switches
CPU1,5,context-switches
CPU2,5,context-switches
CPU3,6,context-switches
CPU0,0,CPU-migrations
CPU1,1,CPU-migrations
CPU2,0,CPU-migrations
CPU3,1,CPU-migrations
CPU0,2,page-faults
CPU1,6,page-faults
CPU2,9,page-faults
CPU3,174,page-faults
CPU0,2399439771,cycles
CPU1,2380369063,cycles
CPU2,2399142710,cycles
CPU3,2373161192,cycles
CPU0,872900618,instructions
CPU1,873030960,instructions
CPU2,872714525,instructions
CPU3,874460580,instructions
CPU0,221556839,branches
CPU1,218134342,branches
CPU2,218161730,branches
CPU3,218284093,branches
CPU0,18556,branch-misses
CPU1,1449,branch-misses
CPU2,3447,branch-misses
CPU3,12714,branch-misses
CPU0,8330,cache-references
CPU1,313844,cache-references
CPU2,47993728,cache-references
CPU3,826481,cache-references
CPU0,272,cache-misses
CPU1,5360,cache-misses
CPU2,1342193,cache-misses
CPU3,13992,cache-misses
This second version adds the ability to name a separator and uses
field-separator as the long option to be consistent with perf report.
Commiter note: Since we enabled --big-num by default in 201e0b0 and -x can't be
used with it, we need to notice if the user explicitely enabled or disabled -B,
add code to disable big_num if the user didn't explicitely set --big_num when
-x is used.
Cc: David S. Miller <davem@davemloft.net>
Cc: Frederik Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: paulus@samba.org
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Robert Richter <robert.richter@amd.com>
LKML-Reference: <4cf68aa7.0fedd80a.5294.1203@mx.google.com>
Signed-off-by: Stephane Eranian <eranian@google.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/Documentation/perf-stat.txt | 5 | ||||
-rw-r--r-- | tools/perf/builtin-stat.c | 144 |
2 files changed, 109 insertions, 40 deletions
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index c469ba56c07..b6da7affbbe 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -78,6 +78,11 @@ This option is only valid in system-wide mode. | |||
78 | --verbose:: | 78 | --verbose:: |
79 | be more verbose (show counter open errors, etc) | 79 | be more verbose (show counter open errors, etc) |
80 | 80 | ||
81 | -x SEP:: | ||
82 | --field-separator SEP:: | ||
83 | print counts using a CSV-style output to make it easy to import directly into | ||
84 | spreadsheets. Columns are separated by the string specified in SEP. | ||
85 | |||
81 | EXAMPLES | 86 | EXAMPLES |
82 | -------- | 87 | -------- |
83 | 88 | ||
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index acbf7ccd9d0..7ff746da7e6 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -52,6 +52,8 @@ | |||
52 | #include <math.h> | 52 | #include <math.h> |
53 | #include <locale.h> | 53 | #include <locale.h> |
54 | 54 | ||
55 | #define DEFAULT_SEPARATOR " " | ||
56 | |||
55 | static struct perf_event_attr default_attrs[] = { | 57 | static struct perf_event_attr default_attrs[] = { |
56 | 58 | ||
57 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, | 59 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, |
@@ -83,7 +85,10 @@ static int thread_num = 0; | |||
83 | static pid_t child_pid = -1; | 85 | static pid_t child_pid = -1; |
84 | static bool null_run = false; | 86 | static bool null_run = false; |
85 | static bool big_num = true; | 87 | static bool big_num = true; |
88 | static int big_num_opt = -1; | ||
86 | static const char *cpu_list; | 89 | static const char *cpu_list; |
90 | static const char *csv_sep = NULL; | ||
91 | static bool csv_output = false; | ||
87 | 92 | ||
88 | 93 | ||
89 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; | 94 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; |
@@ -449,12 +454,18 @@ static void print_noise(int counter, double avg) | |||
449 | static void nsec_printout(int cpu, int counter, double avg) | 454 | static void nsec_printout(int cpu, int counter, double avg) |
450 | { | 455 | { |
451 | double msecs = avg / 1e6; | 456 | double msecs = avg / 1e6; |
457 | char cpustr[16] = { '\0', }; | ||
458 | const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-24s"; | ||
452 | 459 | ||
453 | if (no_aggr) | 460 | if (no_aggr) |
454 | fprintf(stderr, "CPU%-4d %18.6f %-24s", | 461 | sprintf(cpustr, "CPU%*d%s", |
455 | cpumap[cpu], msecs, event_name(counter)); | 462 | csv_output ? 0 : -4, |
456 | else | 463 | cpumap[cpu], csv_sep); |
457 | fprintf(stderr, " %18.6f %-24s", msecs, event_name(counter)); | 464 | |
465 | fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(counter)); | ||
466 | |||
467 | if (csv_output) | ||
468 | return; | ||
458 | 469 | ||
459 | if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) { | 470 | if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) { |
460 | fprintf(stderr, " # %10.3f CPUs ", | 471 | fprintf(stderr, " # %10.3f CPUs ", |
@@ -466,18 +477,26 @@ static void abs_printout(int cpu, int counter, double avg) | |||
466 | { | 477 | { |
467 | double total, ratio = 0.0; | 478 | double total, ratio = 0.0; |
468 | char cpustr[16] = { '\0', }; | 479 | char cpustr[16] = { '\0', }; |
480 | const char *fmt; | ||
481 | |||
482 | if (csv_output) | ||
483 | fmt = "%s%.0f%s%s"; | ||
484 | else if (big_num) | ||
485 | fmt = "%s%'18.0f%s%-24s"; | ||
486 | else | ||
487 | fmt = "%s%18.0f%s%-24s"; | ||
469 | 488 | ||
470 | if (no_aggr) | 489 | if (no_aggr) |
471 | sprintf(cpustr, "CPU%-4d", cpumap[cpu]); | 490 | sprintf(cpustr, "CPU%*d%s", |
491 | csv_output ? 0 : -4, | ||
492 | cpumap[cpu], csv_sep); | ||
472 | else | 493 | else |
473 | cpu = 0; | 494 | cpu = 0; |
474 | 495 | ||
475 | if (big_num) | 496 | fprintf(stderr, fmt, cpustr, avg, csv_sep, event_name(counter)); |
476 | fprintf(stderr, "%s %'18.0f %-24s", | 497 | |
477 | cpustr, avg, event_name(counter)); | 498 | if (csv_output) |
478 | else | 499 | return; |
479 | fprintf(stderr, "%s %18.0f %-24s", | ||
480 | cpustr, avg, event_name(counter)); | ||
481 | 500 | ||
482 | if (MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) { | 501 | if (MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) { |
483 | total = avg_stats(&runtime_cycles_stats[cpu]); | 502 | total = avg_stats(&runtime_cycles_stats[cpu]); |
@@ -515,8 +534,9 @@ static void print_counter_aggr(int counter) | |||
515 | int scaled = event_scaled[counter]; | 534 | int scaled = event_scaled[counter]; |
516 | 535 | ||
517 | if (scaled == -1) { | 536 | if (scaled == -1) { |
518 | fprintf(stderr, " %18s %-24s\n", | 537 | fprintf(stderr, "%*s%s%-24s\n", |
519 | "<not counted>", event_name(counter)); | 538 | csv_output ? 0 : 18, |
539 | "<not counted>", csv_sep, event_name(counter)); | ||
520 | return; | 540 | return; |
521 | } | 541 | } |
522 | 542 | ||
@@ -525,6 +545,11 @@ static void print_counter_aggr(int counter) | |||
525 | else | 545 | else |
526 | abs_printout(-1, counter, avg); | 546 | abs_printout(-1, counter, avg); |
527 | 547 | ||
548 | if (csv_output) { | ||
549 | fputc('\n', stderr); | ||
550 | return; | ||
551 | } | ||
552 | |||
528 | print_noise(counter, avg); | 553 | print_noise(counter, avg); |
529 | 554 | ||
530 | if (scaled) { | 555 | if (scaled) { |
@@ -554,8 +579,12 @@ static void print_counter(int counter) | |||
554 | ena = cpu_counts[cpu][counter].ena; | 579 | ena = cpu_counts[cpu][counter].ena; |
555 | run = cpu_counts[cpu][counter].run; | 580 | run = cpu_counts[cpu][counter].run; |
556 | if (run == 0 || ena == 0) { | 581 | if (run == 0 || ena == 0) { |
557 | fprintf(stderr, "CPU%-4d %18s %-24s", cpumap[cpu], | 582 | fprintf(stderr, "CPU%*d%s%*s%s%-24s", |
558 | "<not counted>", event_name(counter)); | 583 | csv_output ? 0 : -4, |
584 | cpumap[cpu], csv_sep, | ||
585 | csv_output ? 0 : 18, | ||
586 | "<not counted>", csv_sep, | ||
587 | event_name(counter)); | ||
559 | 588 | ||
560 | fprintf(stderr, "\n"); | 589 | fprintf(stderr, "\n"); |
561 | continue; | 590 | continue; |
@@ -566,11 +595,13 @@ static void print_counter(int counter) | |||
566 | else | 595 | else |
567 | abs_printout(cpu, counter, val); | 596 | abs_printout(cpu, counter, val); |
568 | 597 | ||
569 | print_noise(counter, 1.0); | 598 | if (!csv_output) { |
599 | print_noise(counter, 1.0); | ||
570 | 600 | ||
571 | if (run != ena) { | 601 | if (run != ena) { |
572 | fprintf(stderr, " (scaled from %.2f%%)", | 602 | fprintf(stderr, " (scaled from %.2f%%)", |
573 | 100.0 * run / ena); | 603 | 100.0 * run / ena); |
604 | } | ||
574 | } | 605 | } |
575 | fprintf(stderr, "\n"); | 606 | fprintf(stderr, "\n"); |
576 | } | 607 | } |
@@ -582,21 +613,23 @@ static void print_stat(int argc, const char **argv) | |||
582 | 613 | ||
583 | fflush(stdout); | 614 | fflush(stdout); |
584 | 615 | ||
585 | fprintf(stderr, "\n"); | 616 | if (!csv_output) { |
586 | fprintf(stderr, " Performance counter stats for "); | 617 | fprintf(stderr, "\n"); |
587 | if(target_pid == -1 && target_tid == -1) { | 618 | fprintf(stderr, " Performance counter stats for "); |
588 | fprintf(stderr, "\'%s", argv[0]); | 619 | if(target_pid == -1 && target_tid == -1) { |
589 | for (i = 1; i < argc; i++) | 620 | fprintf(stderr, "\'%s", argv[0]); |
590 | fprintf(stderr, " %s", argv[i]); | 621 | for (i = 1; i < argc; i++) |
591 | } else if (target_pid != -1) | 622 | fprintf(stderr, " %s", argv[i]); |
592 | fprintf(stderr, "process id \'%d", target_pid); | 623 | } else if (target_pid != -1) |
593 | else | 624 | fprintf(stderr, "process id \'%d", target_pid); |
594 | fprintf(stderr, "thread id \'%d", target_tid); | 625 | else |
626 | fprintf(stderr, "thread id \'%d", target_tid); | ||
595 | 627 | ||
596 | fprintf(stderr, "\'"); | 628 | fprintf(stderr, "\'"); |
597 | if (run_count > 1) | 629 | if (run_count > 1) |
598 | fprintf(stderr, " (%d runs)", run_count); | 630 | fprintf(stderr, " (%d runs)", run_count); |
599 | fprintf(stderr, ":\n\n"); | 631 | fprintf(stderr, ":\n\n"); |
632 | } | ||
600 | 633 | ||
601 | if (no_aggr) { | 634 | if (no_aggr) { |
602 | for (counter = 0; counter < nr_counters; counter++) | 635 | for (counter = 0; counter < nr_counters; counter++) |
@@ -606,15 +639,17 @@ static void print_stat(int argc, const char **argv) | |||
606 | print_counter_aggr(counter); | 639 | print_counter_aggr(counter); |
607 | } | 640 | } |
608 | 641 | ||
609 | fprintf(stderr, "\n"); | 642 | if (!csv_output) { |
610 | fprintf(stderr, " %18.9f seconds time elapsed", | 643 | fprintf(stderr, "\n"); |
611 | avg_stats(&walltime_nsecs_stats)/1e9); | 644 | fprintf(stderr, " %18.9f seconds time elapsed", |
612 | if (run_count > 1) { | 645 | avg_stats(&walltime_nsecs_stats)/1e9); |
613 | fprintf(stderr, " ( +- %7.3f%% )", | 646 | if (run_count > 1) { |
647 | fprintf(stderr, " ( +- %7.3f%% )", | ||
614 | 100*stddev_stats(&walltime_nsecs_stats) / | 648 | 100*stddev_stats(&walltime_nsecs_stats) / |
615 | avg_stats(&walltime_nsecs_stats)); | 649 | avg_stats(&walltime_nsecs_stats)); |
650 | } | ||
651 | fprintf(stderr, "\n\n"); | ||
616 | } | 652 | } |
617 | fprintf(stderr, "\n\n"); | ||
618 | } | 653 | } |
619 | 654 | ||
620 | static volatile int signr = -1; | 655 | static volatile int signr = -1; |
@@ -644,6 +679,13 @@ static const char * const stat_usage[] = { | |||
644 | NULL | 679 | NULL |
645 | }; | 680 | }; |
646 | 681 | ||
682 | static int stat__set_big_num(const struct option *opt __used, | ||
683 | const char *s __used, int unset) | ||
684 | { | ||
685 | big_num_opt = unset ? 0 : 1; | ||
686 | return 0; | ||
687 | } | ||
688 | |||
647 | static const struct option options[] = { | 689 | static const struct option options[] = { |
648 | OPT_CALLBACK('e', "event", NULL, "event", | 690 | OPT_CALLBACK('e', "event", NULL, "event", |
649 | "event selector. use 'perf list' to list available events", | 691 | "event selector. use 'perf list' to list available events", |
@@ -664,12 +706,15 @@ static const struct option options[] = { | |||
664 | "repeat command and print average + stddev (max: 100)"), | 706 | "repeat command and print average + stddev (max: 100)"), |
665 | OPT_BOOLEAN('n', "null", &null_run, | 707 | OPT_BOOLEAN('n', "null", &null_run, |
666 | "null run - dont start any counters"), | 708 | "null run - dont start any counters"), |
667 | OPT_BOOLEAN('B', "big-num", &big_num, | 709 | OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, |
668 | "print large numbers with thousands\' separators"), | 710 | "print large numbers with thousands\' separators", |
711 | stat__set_big_num), | ||
669 | OPT_STRING('C', "cpu", &cpu_list, "cpu", | 712 | OPT_STRING('C', "cpu", &cpu_list, "cpu", |
670 | "list of cpus to monitor in system-wide"), | 713 | "list of cpus to monitor in system-wide"), |
671 | OPT_BOOLEAN('A', "no-aggr", &no_aggr, | 714 | OPT_BOOLEAN('A', "no-aggr", &no_aggr, |
672 | "disable CPU count aggregation"), | 715 | "disable CPU count aggregation"), |
716 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | ||
717 | "print counts with custom separator"), | ||
673 | OPT_END() | 718 | OPT_END() |
674 | }; | 719 | }; |
675 | 720 | ||
@@ -682,6 +727,25 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
682 | 727 | ||
683 | argc = parse_options(argc, argv, options, stat_usage, | 728 | argc = parse_options(argc, argv, options, stat_usage, |
684 | PARSE_OPT_STOP_AT_NON_OPTION); | 729 | PARSE_OPT_STOP_AT_NON_OPTION); |
730 | |||
731 | if (csv_sep) | ||
732 | csv_output = true; | ||
733 | else | ||
734 | csv_sep = DEFAULT_SEPARATOR; | ||
735 | |||
736 | /* | ||
737 | * let the spreadsheet do the pretty-printing | ||
738 | */ | ||
739 | if (csv_output) { | ||
740 | /* User explicitely passed -B? */ | ||
741 | if (big_num_opt == 1) { | ||
742 | fprintf(stderr, "-B option not supported with -x\n"); | ||
743 | usage_with_options(stat_usage, options); | ||
744 | } else /* Nope, so disable big number formatting */ | ||
745 | big_num = false; | ||
746 | } else if (big_num_opt == 0) /* User passed --no-big-num */ | ||
747 | big_num = false; | ||
748 | |||
685 | if (!argc && target_pid == -1 && target_tid == -1) | 749 | if (!argc && target_pid == -1 && target_tid == -1) |
686 | usage_with_options(stat_usage, options); | 750 | usage_with_options(stat_usage, options); |
687 | if (run_count <= 0) | 751 | if (run_count <= 0) |