diff options
Diffstat (limited to 'tools/perf/builtin-stat.c')
-rw-r--r-- | tools/perf/builtin-stat.c | 328 |
1 files changed, 265 insertions, 63 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c247faca7127..99848761f573 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -65,6 +65,11 @@ | |||
65 | #define CNTR_NOT_SUPPORTED "<not supported>" | 65 | #define CNTR_NOT_SUPPORTED "<not supported>" |
66 | #define CNTR_NOT_COUNTED "<not counted>" | 66 | #define CNTR_NOT_COUNTED "<not counted>" |
67 | 67 | ||
68 | static void print_stat(int argc, const char **argv); | ||
69 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix); | ||
70 | static void print_counter(struct perf_evsel *counter, char *prefix); | ||
71 | static void print_aggr_socket(char *prefix); | ||
72 | |||
68 | static struct perf_evlist *evsel_list; | 73 | static struct perf_evlist *evsel_list; |
69 | 74 | ||
70 | static struct perf_target target = { | 75 | static struct perf_target target = { |
@@ -75,6 +80,7 @@ static int run_count = 1; | |||
75 | static bool no_inherit = false; | 80 | static bool no_inherit = false; |
76 | static bool scale = true; | 81 | static bool scale = true; |
77 | static bool no_aggr = false; | 82 | static bool no_aggr = false; |
83 | static bool aggr_socket = false; | ||
78 | static pid_t child_pid = -1; | 84 | static pid_t child_pid = -1; |
79 | static bool null_run = false; | 85 | static bool null_run = false; |
80 | static int detailed_run = 0; | 86 | static int detailed_run = 0; |
@@ -87,6 +93,9 @@ static FILE *output = NULL; | |||
87 | static const char *pre_cmd = NULL; | 93 | static const char *pre_cmd = NULL; |
88 | static const char *post_cmd = NULL; | 94 | static const char *post_cmd = NULL; |
89 | static bool sync_run = false; | 95 | static bool sync_run = false; |
96 | static unsigned int interval = 0; | ||
97 | static struct timespec ref_time; | ||
98 | static struct cpu_map *sock_map; | ||
90 | 99 | ||
91 | static volatile int done = 0; | 100 | static volatile int done = 0; |
92 | 101 | ||
@@ -94,6 +103,28 @@ struct perf_stat { | |||
94 | struct stats res_stats[3]; | 103 | struct stats res_stats[3]; |
95 | }; | 104 | }; |
96 | 105 | ||
106 | static inline void diff_timespec(struct timespec *r, struct timespec *a, | ||
107 | struct timespec *b) | ||
108 | { | ||
109 | r->tv_sec = a->tv_sec - b->tv_sec; | ||
110 | if (a->tv_nsec < b->tv_nsec) { | ||
111 | r->tv_nsec = a->tv_nsec + 1000000000L - b->tv_nsec; | ||
112 | r->tv_sec--; | ||
113 | } else { | ||
114 | r->tv_nsec = a->tv_nsec - b->tv_nsec ; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) | ||
119 | { | ||
120 | return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus; | ||
121 | } | ||
122 | |||
123 | static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) | ||
124 | { | ||
125 | return perf_evsel__cpus(evsel)->nr; | ||
126 | } | ||
127 | |||
97 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | 128 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) |
98 | { | 129 | { |
99 | evsel->priv = zalloc(sizeof(struct perf_stat)); | 130 | evsel->priv = zalloc(sizeof(struct perf_stat)); |
@@ -106,14 +137,27 @@ static void perf_evsel__free_stat_priv(struct perf_evsel *evsel) | |||
106 | evsel->priv = NULL; | 137 | evsel->priv = NULL; |
107 | } | 138 | } |
108 | 139 | ||
109 | static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) | 140 | static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel) |
110 | { | 141 | { |
111 | return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus; | 142 | void *addr; |
143 | size_t sz; | ||
144 | |||
145 | sz = sizeof(*evsel->counts) + | ||
146 | (perf_evsel__nr_cpus(evsel) * sizeof(struct perf_counts_values)); | ||
147 | |||
148 | addr = zalloc(sz); | ||
149 | if (!addr) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | evsel->prev_raw_counts = addr; | ||
153 | |||
154 | return 0; | ||
112 | } | 155 | } |
113 | 156 | ||
114 | static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) | 157 | static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) |
115 | { | 158 | { |
116 | return perf_evsel__cpus(evsel)->nr; | 159 | free(evsel->prev_raw_counts); |
160 | evsel->prev_raw_counts = NULL; | ||
117 | } | 161 | } |
118 | 162 | ||
119 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; | 163 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; |
@@ -132,8 +176,6 @@ static struct stats walltime_nsecs_stats; | |||
132 | static int create_perf_stat_counter(struct perf_evsel *evsel) | 176 | static int create_perf_stat_counter(struct perf_evsel *evsel) |
133 | { | 177 | { |
134 | struct perf_event_attr *attr = &evsel->attr; | 178 | struct perf_event_attr *attr = &evsel->attr; |
135 | bool exclude_guest_missing = false; | ||
136 | int ret; | ||
137 | 179 | ||
138 | if (scale) | 180 | if (scale) |
139 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 181 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
@@ -141,38 +183,16 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
141 | 183 | ||
142 | attr->inherit = !no_inherit; | 184 | attr->inherit = !no_inherit; |
143 | 185 | ||
144 | retry: | 186 | if (perf_target__has_cpu(&target)) |
145 | if (exclude_guest_missing) | 187 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); |
146 | evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; | ||
147 | |||
148 | if (perf_target__has_cpu(&target)) { | ||
149 | ret = perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | ||
150 | if (ret) | ||
151 | goto check_ret; | ||
152 | return 0; | ||
153 | } | ||
154 | 188 | ||
155 | if (!perf_target__has_task(&target) && | 189 | if (!perf_target__has_task(&target) && |
156 | !perf_evsel__is_group_member(evsel)) { | 190 | perf_evsel__is_group_leader(evsel)) { |
157 | attr->disabled = 1; | 191 | attr->disabled = 1; |
158 | attr->enable_on_exec = 1; | 192 | attr->enable_on_exec = 1; |
159 | } | 193 | } |
160 | 194 | ||
161 | ret = perf_evsel__open_per_thread(evsel, evsel_list->threads); | 195 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); |
162 | if (!ret) | ||
163 | return 0; | ||
164 | /* fall through */ | ||
165 | check_ret: | ||
166 | if (ret && errno == EINVAL) { | ||
167 | if (!exclude_guest_missing && | ||
168 | (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { | ||
169 | pr_debug("Old kernel, cannot exclude " | ||
170 | "guest or host samples.\n"); | ||
171 | exclude_guest_missing = true; | ||
172 | goto retry; | ||
173 | } | ||
174 | } | ||
175 | return ret; | ||
176 | } | 196 | } |
177 | 197 | ||
178 | /* | 198 | /* |
@@ -269,15 +289,79 @@ static int read_counter(struct perf_evsel *counter) | |||
269 | return 0; | 289 | return 0; |
270 | } | 290 | } |
271 | 291 | ||
292 | static void print_interval(void) | ||
293 | { | ||
294 | static int num_print_interval; | ||
295 | struct perf_evsel *counter; | ||
296 | struct perf_stat *ps; | ||
297 | struct timespec ts, rs; | ||
298 | char prefix[64]; | ||
299 | |||
300 | if (no_aggr) { | ||
301 | list_for_each_entry(counter, &evsel_list->entries, node) { | ||
302 | ps = counter->priv; | ||
303 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | ||
304 | read_counter(counter); | ||
305 | } | ||
306 | } else { | ||
307 | list_for_each_entry(counter, &evsel_list->entries, node) { | ||
308 | ps = counter->priv; | ||
309 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | ||
310 | read_counter_aggr(counter); | ||
311 | } | ||
312 | } | ||
313 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
314 | diff_timespec(&rs, &ts, &ref_time); | ||
315 | sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep); | ||
316 | |||
317 | if (num_print_interval == 0 && !csv_output) { | ||
318 | if (aggr_socket) | ||
319 | fprintf(output, "# time socket cpus counts events\n"); | ||
320 | else if (no_aggr) | ||
321 | fprintf(output, "# time CPU counts events\n"); | ||
322 | else | ||
323 | fprintf(output, "# time counts events\n"); | ||
324 | } | ||
325 | |||
326 | if (++num_print_interval == 25) | ||
327 | num_print_interval = 0; | ||
328 | |||
329 | if (aggr_socket) | ||
330 | print_aggr_socket(prefix); | ||
331 | else if (no_aggr) { | ||
332 | list_for_each_entry(counter, &evsel_list->entries, node) | ||
333 | print_counter(counter, prefix); | ||
334 | } else { | ||
335 | list_for_each_entry(counter, &evsel_list->entries, node) | ||
336 | print_counter_aggr(counter, prefix); | ||
337 | } | ||
338 | } | ||
339 | |||
272 | static int __run_perf_stat(int argc __maybe_unused, const char **argv) | 340 | static int __run_perf_stat(int argc __maybe_unused, const char **argv) |
273 | { | 341 | { |
342 | char msg[512]; | ||
274 | unsigned long long t0, t1; | 343 | unsigned long long t0, t1; |
275 | struct perf_evsel *counter; | 344 | struct perf_evsel *counter; |
345 | struct timespec ts; | ||
276 | int status = 0; | 346 | int status = 0; |
277 | int child_ready_pipe[2], go_pipe[2]; | 347 | int child_ready_pipe[2], go_pipe[2]; |
278 | const bool forks = (argc > 0); | 348 | const bool forks = (argc > 0); |
279 | char buf; | 349 | char buf; |
280 | 350 | ||
351 | if (interval) { | ||
352 | ts.tv_sec = interval / 1000; | ||
353 | ts.tv_nsec = (interval % 1000) * 1000000; | ||
354 | } else { | ||
355 | ts.tv_sec = 1; | ||
356 | ts.tv_nsec = 0; | ||
357 | } | ||
358 | |||
359 | if (aggr_socket | ||
360 | && cpu_map__build_socket_map(evsel_list->cpus, &sock_map)) { | ||
361 | perror("cannot build socket map"); | ||
362 | return -1; | ||
363 | } | ||
364 | |||
281 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { | 365 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { |
282 | perror("failed to create pipes"); | 366 | perror("failed to create pipes"); |
283 | return -1; | 367 | return -1; |
@@ -348,20 +432,13 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) | |||
348 | continue; | 432 | continue; |
349 | } | 433 | } |
350 | 434 | ||
351 | if (errno == EPERM || errno == EACCES) { | 435 | perf_evsel__open_strerror(counter, &target, |
352 | error("You may not have permission to collect %sstats.\n" | 436 | errno, msg, sizeof(msg)); |
353 | "\t Consider tweaking" | 437 | ui__error("%s\n", msg); |
354 | " /proc/sys/kernel/perf_event_paranoid or running as root.", | 438 | |
355 | target.system_wide ? "system-wide " : ""); | ||
356 | } else { | ||
357 | error("open_counter returned with %d (%s). " | ||
358 | "/bin/dmesg may provide additional information.\n", | ||
359 | errno, strerror(errno)); | ||
360 | } | ||
361 | if (child_pid != -1) | 439 | if (child_pid != -1) |
362 | kill(child_pid, SIGTERM); | 440 | kill(child_pid, SIGTERM); |
363 | 441 | ||
364 | pr_err("Not all events could be opened.\n"); | ||
365 | return -1; | 442 | return -1; |
366 | } | 443 | } |
367 | counter->supported = true; | 444 | counter->supported = true; |
@@ -377,14 +454,25 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) | |||
377 | * Enable counters and exec the command: | 454 | * Enable counters and exec the command: |
378 | */ | 455 | */ |
379 | t0 = rdclock(); | 456 | t0 = rdclock(); |
457 | clock_gettime(CLOCK_MONOTONIC, &ref_time); | ||
380 | 458 | ||
381 | if (forks) { | 459 | if (forks) { |
382 | close(go_pipe[1]); | 460 | close(go_pipe[1]); |
461 | if (interval) { | ||
462 | while (!waitpid(child_pid, &status, WNOHANG)) { | ||
463 | nanosleep(&ts, NULL); | ||
464 | print_interval(); | ||
465 | } | ||
466 | } | ||
383 | wait(&status); | 467 | wait(&status); |
384 | if (WIFSIGNALED(status)) | 468 | if (WIFSIGNALED(status)) |
385 | psignal(WTERMSIG(status), argv[0]); | 469 | psignal(WTERMSIG(status), argv[0]); |
386 | } else { | 470 | } else { |
387 | while(!done) sleep(1); | 471 | while (!done) { |
472 | nanosleep(&ts, NULL); | ||
473 | if (interval) | ||
474 | print_interval(); | ||
475 | } | ||
388 | } | 476 | } |
389 | 477 | ||
390 | t1 = rdclock(); | 478 | t1 = rdclock(); |
@@ -454,13 +542,21 @@ static void print_noise(struct perf_evsel *evsel, double avg) | |||
454 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); | 542 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); |
455 | } | 543 | } |
456 | 544 | ||
457 | static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | 545 | static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) |
458 | { | 546 | { |
459 | double msecs = avg / 1e6; | 547 | double msecs = avg / 1e6; |
460 | char cpustr[16] = { '\0', }; | 548 | char cpustr[16] = { '\0', }; |
461 | const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s"; | 549 | const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s"; |
462 | 550 | ||
463 | if (no_aggr) | 551 | if (aggr_socket) |
552 | sprintf(cpustr, "S%*d%s%*d%s", | ||
553 | csv_output ? 0 : -5, | ||
554 | cpu, | ||
555 | csv_sep, | ||
556 | csv_output ? 0 : 4, | ||
557 | nr, | ||
558 | csv_sep); | ||
559 | else if (no_aggr) | ||
464 | sprintf(cpustr, "CPU%*d%s", | 560 | sprintf(cpustr, "CPU%*d%s", |
465 | csv_output ? 0 : -4, | 561 | csv_output ? 0 : -4, |
466 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); | 562 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); |
@@ -470,7 +566,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
470 | if (evsel->cgrp) | 566 | if (evsel->cgrp) |
471 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 567 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
472 | 568 | ||
473 | if (csv_output) | 569 | if (csv_output || interval) |
474 | return; | 570 | return; |
475 | 571 | ||
476 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) | 572 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) |
@@ -659,7 +755,7 @@ static void print_ll_cache_misses(int cpu, | |||
659 | fprintf(output, " of all LL-cache hits "); | 755 | fprintf(output, " of all LL-cache hits "); |
660 | } | 756 | } |
661 | 757 | ||
662 | static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | 758 | static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) |
663 | { | 759 | { |
664 | double total, ratio = 0.0; | 760 | double total, ratio = 0.0; |
665 | char cpustr[16] = { '\0', }; | 761 | char cpustr[16] = { '\0', }; |
@@ -672,7 +768,15 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
672 | else | 768 | else |
673 | fmt = "%s%18.0f%s%-25s"; | 769 | fmt = "%s%18.0f%s%-25s"; |
674 | 770 | ||
675 | if (no_aggr) | 771 | if (aggr_socket) |
772 | sprintf(cpustr, "S%*d%s%*d%s", | ||
773 | csv_output ? 0 : -5, | ||
774 | cpu, | ||
775 | csv_sep, | ||
776 | csv_output ? 0 : 4, | ||
777 | nr, | ||
778 | csv_sep); | ||
779 | else if (no_aggr) | ||
676 | sprintf(cpustr, "CPU%*d%s", | 780 | sprintf(cpustr, "CPU%*d%s", |
677 | csv_output ? 0 : -4, | 781 | csv_output ? 0 : -4, |
678 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); | 782 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); |
@@ -684,12 +788,11 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
684 | if (evsel->cgrp) | 788 | if (evsel->cgrp) |
685 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 789 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
686 | 790 | ||
687 | if (csv_output) | 791 | if (csv_output || interval) |
688 | return; | 792 | return; |
689 | 793 | ||
690 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { | 794 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { |
691 | total = avg_stats(&runtime_cycles_stats[cpu]); | 795 | total = avg_stats(&runtime_cycles_stats[cpu]); |
692 | |||
693 | if (total) | 796 | if (total) |
694 | ratio = avg / total; | 797 | ratio = avg / total; |
695 | 798 | ||
@@ -779,16 +882,83 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
779 | } | 882 | } |
780 | } | 883 | } |
781 | 884 | ||
885 | static void print_aggr_socket(char *prefix) | ||
886 | { | ||
887 | struct perf_evsel *counter; | ||
888 | u64 ena, run, val; | ||
889 | int cpu, s, s2, sock, nr; | ||
890 | |||
891 | if (!sock_map) | ||
892 | return; | ||
893 | |||
894 | for (s = 0; s < sock_map->nr; s++) { | ||
895 | sock = cpu_map__socket(sock_map, s); | ||
896 | list_for_each_entry(counter, &evsel_list->entries, node) { | ||
897 | val = ena = run = 0; | ||
898 | nr = 0; | ||
899 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
900 | s2 = cpu_map__get_socket(evsel_list->cpus, cpu); | ||
901 | if (s2 != sock) | ||
902 | continue; | ||
903 | val += counter->counts->cpu[cpu].val; | ||
904 | ena += counter->counts->cpu[cpu].ena; | ||
905 | run += counter->counts->cpu[cpu].run; | ||
906 | nr++; | ||
907 | } | ||
908 | if (prefix) | ||
909 | fprintf(output, "%s", prefix); | ||
910 | |||
911 | if (run == 0 || ena == 0) { | ||
912 | fprintf(output, "S%*d%s%*d%s%*s%s%*s", | ||
913 | csv_output ? 0 : -5, | ||
914 | s, | ||
915 | csv_sep, | ||
916 | csv_output ? 0 : 4, | ||
917 | nr, | ||
918 | csv_sep, | ||
919 | csv_output ? 0 : 18, | ||
920 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
921 | csv_sep, | ||
922 | csv_output ? 0 : -24, | ||
923 | perf_evsel__name(counter)); | ||
924 | if (counter->cgrp) | ||
925 | fprintf(output, "%s%s", | ||
926 | csv_sep, counter->cgrp->name); | ||
927 | |||
928 | fputc('\n', output); | ||
929 | continue; | ||
930 | } | ||
931 | |||
932 | if (nsec_counter(counter)) | ||
933 | nsec_printout(sock, nr, counter, val); | ||
934 | else | ||
935 | abs_printout(sock, nr, counter, val); | ||
936 | |||
937 | if (!csv_output) { | ||
938 | print_noise(counter, 1.0); | ||
939 | |||
940 | if (run != ena) | ||
941 | fprintf(output, " (%.2f%%)", | ||
942 | 100.0 * run / ena); | ||
943 | } | ||
944 | fputc('\n', output); | ||
945 | } | ||
946 | } | ||
947 | } | ||
948 | |||
782 | /* | 949 | /* |
783 | * Print out the results of a single counter: | 950 | * Print out the results of a single counter: |
784 | * aggregated counts in system-wide mode | 951 | * aggregated counts in system-wide mode |
785 | */ | 952 | */ |
786 | static void print_counter_aggr(struct perf_evsel *counter) | 953 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix) |
787 | { | 954 | { |
788 | struct perf_stat *ps = counter->priv; | 955 | struct perf_stat *ps = counter->priv; |
789 | double avg = avg_stats(&ps->res_stats[0]); | 956 | double avg = avg_stats(&ps->res_stats[0]); |
790 | int scaled = counter->counts->scaled; | 957 | int scaled = counter->counts->scaled; |
791 | 958 | ||
959 | if (prefix) | ||
960 | fprintf(output, "%s", prefix); | ||
961 | |||
792 | if (scaled == -1) { | 962 | if (scaled == -1) { |
793 | fprintf(output, "%*s%s%*s", | 963 | fprintf(output, "%*s%s%*s", |
794 | csv_output ? 0 : 18, | 964 | csv_output ? 0 : 18, |
@@ -805,9 +975,9 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
805 | } | 975 | } |
806 | 976 | ||
807 | if (nsec_counter(counter)) | 977 | if (nsec_counter(counter)) |
808 | nsec_printout(-1, counter, avg); | 978 | nsec_printout(-1, 0, counter, avg); |
809 | else | 979 | else |
810 | abs_printout(-1, counter, avg); | 980 | abs_printout(-1, 0, counter, avg); |
811 | 981 | ||
812 | print_noise(counter, avg); | 982 | print_noise(counter, avg); |
813 | 983 | ||
@@ -831,7 +1001,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
831 | * Print out the results of a single counter: | 1001 | * Print out the results of a single counter: |
832 | * does not use aggregated count in system-wide | 1002 | * does not use aggregated count in system-wide |
833 | */ | 1003 | */ |
834 | static void print_counter(struct perf_evsel *counter) | 1004 | static void print_counter(struct perf_evsel *counter, char *prefix) |
835 | { | 1005 | { |
836 | u64 ena, run, val; | 1006 | u64 ena, run, val; |
837 | int cpu; | 1007 | int cpu; |
@@ -840,6 +1010,10 @@ static void print_counter(struct perf_evsel *counter) | |||
840 | val = counter->counts->cpu[cpu].val; | 1010 | val = counter->counts->cpu[cpu].val; |
841 | ena = counter->counts->cpu[cpu].ena; | 1011 | ena = counter->counts->cpu[cpu].ena; |
842 | run = counter->counts->cpu[cpu].run; | 1012 | run = counter->counts->cpu[cpu].run; |
1013 | |||
1014 | if (prefix) | ||
1015 | fprintf(output, "%s", prefix); | ||
1016 | |||
843 | if (run == 0 || ena == 0) { | 1017 | if (run == 0 || ena == 0) { |
844 | fprintf(output, "CPU%*d%s%*s%s%*s", | 1018 | fprintf(output, "CPU%*d%s%*s%s%*s", |
845 | csv_output ? 0 : -4, | 1019 | csv_output ? 0 : -4, |
@@ -859,9 +1033,9 @@ static void print_counter(struct perf_evsel *counter) | |||
859 | } | 1033 | } |
860 | 1034 | ||
861 | if (nsec_counter(counter)) | 1035 | if (nsec_counter(counter)) |
862 | nsec_printout(cpu, counter, val); | 1036 | nsec_printout(cpu, 0, counter, val); |
863 | else | 1037 | else |
864 | abs_printout(cpu, counter, val); | 1038 | abs_printout(cpu, 0, counter, val); |
865 | 1039 | ||
866 | if (!csv_output) { | 1040 | if (!csv_output) { |
867 | print_noise(counter, 1.0); | 1041 | print_noise(counter, 1.0); |
@@ -899,12 +1073,14 @@ static void print_stat(int argc, const char **argv) | |||
899 | fprintf(output, ":\n\n"); | 1073 | fprintf(output, ":\n\n"); |
900 | } | 1074 | } |
901 | 1075 | ||
902 | if (no_aggr) { | 1076 | if (aggr_socket) |
1077 | print_aggr_socket(NULL); | ||
1078 | else if (no_aggr) { | ||
903 | list_for_each_entry(counter, &evsel_list->entries, node) | 1079 | list_for_each_entry(counter, &evsel_list->entries, node) |
904 | print_counter(counter); | 1080 | print_counter(counter, NULL); |
905 | } else { | 1081 | } else { |
906 | list_for_each_entry(counter, &evsel_list->entries, node) | 1082 | list_for_each_entry(counter, &evsel_list->entries, node) |
907 | print_counter_aggr(counter); | 1083 | print_counter_aggr(counter, NULL); |
908 | } | 1084 | } |
909 | 1085 | ||
910 | if (!csv_output) { | 1086 | if (!csv_output) { |
@@ -925,7 +1101,7 @@ static volatile int signr = -1; | |||
925 | 1101 | ||
926 | static void skip_signal(int signo) | 1102 | static void skip_signal(int signo) |
927 | { | 1103 | { |
928 | if(child_pid == -1) | 1104 | if ((child_pid == -1) || interval) |
929 | done = 1; | 1105 | done = 1; |
930 | 1106 | ||
931 | signr = signo; | 1107 | signr = signo; |
@@ -1145,6 +1321,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1145 | "command to run prior to the measured command"), | 1321 | "command to run prior to the measured command"), |
1146 | OPT_STRING(0, "post", &post_cmd, "command", | 1322 | OPT_STRING(0, "post", &post_cmd, "command", |
1147 | "command to run after to the measured command"), | 1323 | "command to run after to the measured command"), |
1324 | OPT_UINTEGER('I', "interval-print", &interval, | ||
1325 | "print counts at regular interval in ms (>= 100)"), | ||
1326 | OPT_BOOLEAN(0, "aggr-socket", &aggr_socket, "aggregate counts per processor socket"), | ||
1148 | OPT_END() | 1327 | OPT_END() |
1149 | }; | 1328 | }; |
1150 | const char * const stat_usage[] = { | 1329 | const char * const stat_usage[] = { |
@@ -1231,6 +1410,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1231 | usage_with_options(stat_usage, options); | 1410 | usage_with_options(stat_usage, options); |
1232 | } | 1411 | } |
1233 | 1412 | ||
1413 | if (aggr_socket) { | ||
1414 | if (!perf_target__has_cpu(&target)) { | ||
1415 | fprintf(stderr, "--aggr-socket only available in system-wide mode (-a)\n"); | ||
1416 | usage_with_options(stat_usage, options); | ||
1417 | } | ||
1418 | no_aggr = true; | ||
1419 | } | ||
1420 | |||
1234 | if (add_default_attributes()) | 1421 | if (add_default_attributes()) |
1235 | goto out; | 1422 | goto out; |
1236 | 1423 | ||
@@ -1245,12 +1432,23 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1245 | usage_with_options(stat_usage, options); | 1432 | usage_with_options(stat_usage, options); |
1246 | return -1; | 1433 | return -1; |
1247 | } | 1434 | } |
1435 | if (interval && interval < 100) { | ||
1436 | pr_err("print interval must be >= 100ms\n"); | ||
1437 | usage_with_options(stat_usage, options); | ||
1438 | return -1; | ||
1439 | } | ||
1248 | 1440 | ||
1249 | list_for_each_entry(pos, &evsel_list->entries, node) { | 1441 | list_for_each_entry(pos, &evsel_list->entries, node) { |
1250 | if (perf_evsel__alloc_stat_priv(pos) < 0 || | 1442 | if (perf_evsel__alloc_stat_priv(pos) < 0 || |
1251 | perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0) | 1443 | perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0) |
1252 | goto out_free_fd; | 1444 | goto out_free_fd; |
1253 | } | 1445 | } |
1446 | if (interval) { | ||
1447 | list_for_each_entry(pos, &evsel_list->entries, node) { | ||
1448 | if (perf_evsel__alloc_prev_raw_counts(pos) < 0) | ||
1449 | goto out_free_fd; | ||
1450 | } | ||
1451 | } | ||
1254 | 1452 | ||
1255 | /* | 1453 | /* |
1256 | * We dont want to block the signals - that would cause | 1454 | * We dont want to block the signals - that would cause |
@@ -1260,6 +1458,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1260 | */ | 1458 | */ |
1261 | atexit(sig_atexit); | 1459 | atexit(sig_atexit); |
1262 | signal(SIGINT, skip_signal); | 1460 | signal(SIGINT, skip_signal); |
1461 | signal(SIGCHLD, skip_signal); | ||
1263 | signal(SIGALRM, skip_signal); | 1462 | signal(SIGALRM, skip_signal); |
1264 | signal(SIGABRT, skip_signal); | 1463 | signal(SIGABRT, skip_signal); |
1265 | 1464 | ||
@@ -1272,11 +1471,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1272 | status = run_perf_stat(argc, argv); | 1471 | status = run_perf_stat(argc, argv); |
1273 | } | 1472 | } |
1274 | 1473 | ||
1275 | if (status != -1) | 1474 | if (status != -1 && !interval) |
1276 | print_stat(argc, argv); | 1475 | print_stat(argc, argv); |
1277 | out_free_fd: | 1476 | out_free_fd: |
1278 | list_for_each_entry(pos, &evsel_list->entries, node) | 1477 | list_for_each_entry(pos, &evsel_list->entries, node) { |
1279 | perf_evsel__free_stat_priv(pos); | 1478 | perf_evsel__free_stat_priv(pos); |
1479 | perf_evsel__free_counts(pos); | ||
1480 | perf_evsel__free_prev_raw_counts(pos); | ||
1481 | } | ||
1280 | perf_evlist__delete_maps(evsel_list); | 1482 | perf_evlist__delete_maps(evsel_list); |
1281 | out: | 1483 | out: |
1282 | perf_evlist__delete(evsel_list); | 1484 | perf_evlist__delete(evsel_list); |