diff options
Diffstat (limited to 'tools/perf/builtin-stat.c')
-rw-r--r-- | tools/perf/builtin-stat.c | 230 |
1 files changed, 147 insertions, 83 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 59af5a8419e2..98bf9d32f222 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -63,7 +63,6 @@ | |||
63 | #include "util/group.h" | 63 | #include "util/group.h" |
64 | #include "util/session.h" | 64 | #include "util/session.h" |
65 | #include "util/tool.h" | 65 | #include "util/tool.h" |
66 | #include "util/group.h" | ||
67 | #include "util/string2.h" | 66 | #include "util/string2.h" |
68 | #include "util/metricgroup.h" | 67 | #include "util/metricgroup.h" |
69 | #include "asm/bug.h" | 68 | #include "asm/bug.h" |
@@ -214,8 +213,13 @@ static inline void diff_timespec(struct timespec *r, struct timespec *a, | |||
214 | 213 | ||
215 | static void perf_stat__reset_stats(void) | 214 | static void perf_stat__reset_stats(void) |
216 | { | 215 | { |
216 | int i; | ||
217 | |||
217 | perf_evlist__reset_stats(evsel_list); | 218 | perf_evlist__reset_stats(evsel_list); |
218 | perf_stat__reset_shadow_stats(); | 219 | perf_stat__reset_shadow_stats(); |
220 | |||
221 | for (i = 0; i < stat_config.stats_num; i++) | ||
222 | perf_stat__reset_shadow_per_stat(&stat_config.stats[i]); | ||
219 | } | 223 | } |
220 | 224 | ||
221 | static int create_perf_stat_counter(struct perf_evsel *evsel) | 225 | static int create_perf_stat_counter(struct perf_evsel *evsel) |
@@ -272,7 +276,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
272 | attr->enable_on_exec = 1; | 276 | attr->enable_on_exec = 1; |
273 | } | 277 | } |
274 | 278 | ||
275 | if (target__has_cpu(&target)) | 279 | if (target__has_cpu(&target) && !target__has_per_thread(&target)) |
276 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | 280 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); |
277 | 281 | ||
278 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); | 282 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); |
@@ -335,7 +339,7 @@ static int read_counter(struct perf_evsel *counter) | |||
335 | int nthreads = thread_map__nr(evsel_list->threads); | 339 | int nthreads = thread_map__nr(evsel_list->threads); |
336 | int ncpus, cpu, thread; | 340 | int ncpus, cpu, thread; |
337 | 341 | ||
338 | if (target__has_cpu(&target)) | 342 | if (target__has_cpu(&target) && !target__has_per_thread(&target)) |
339 | ncpus = perf_evsel__nr_cpus(counter); | 343 | ncpus = perf_evsel__nr_cpus(counter); |
340 | else | 344 | else |
341 | ncpus = 1; | 345 | ncpus = 1; |
@@ -458,19 +462,8 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf | |||
458 | workload_exec_errno = info->si_value.sival_int; | 462 | workload_exec_errno = info->si_value.sival_int; |
459 | } | 463 | } |
460 | 464 | ||
461 | static bool has_unit(struct perf_evsel *counter) | ||
462 | { | ||
463 | return counter->unit && *counter->unit; | ||
464 | } | ||
465 | |||
466 | static bool has_scale(struct perf_evsel *counter) | ||
467 | { | ||
468 | return counter->scale != 1; | ||
469 | } | ||
470 | |||
471 | static int perf_stat_synthesize_config(bool is_pipe) | 465 | static int perf_stat_synthesize_config(bool is_pipe) |
472 | { | 466 | { |
473 | struct perf_evsel *counter; | ||
474 | int err; | 467 | int err; |
475 | 468 | ||
476 | if (is_pipe) { | 469 | if (is_pipe) { |
@@ -482,53 +475,10 @@ static int perf_stat_synthesize_config(bool is_pipe) | |||
482 | } | 475 | } |
483 | } | 476 | } |
484 | 477 | ||
485 | /* | 478 | err = perf_event__synthesize_extra_attr(NULL, |
486 | * Synthesize other events stuff not carried within | 479 | evsel_list, |
487 | * attr event - unit, scale, name | 480 | process_synthesized_event, |
488 | */ | 481 | is_pipe); |
489 | evlist__for_each_entry(evsel_list, counter) { | ||
490 | if (!counter->supported) | ||
491 | continue; | ||
492 | |||
493 | /* | ||
494 | * Synthesize unit and scale only if it's defined. | ||
495 | */ | ||
496 | if (has_unit(counter)) { | ||
497 | err = perf_event__synthesize_event_update_unit(NULL, counter, process_synthesized_event); | ||
498 | if (err < 0) { | ||
499 | pr_err("Couldn't synthesize evsel unit.\n"); | ||
500 | return err; | ||
501 | } | ||
502 | } | ||
503 | |||
504 | if (has_scale(counter)) { | ||
505 | err = perf_event__synthesize_event_update_scale(NULL, counter, process_synthesized_event); | ||
506 | if (err < 0) { | ||
507 | pr_err("Couldn't synthesize evsel scale.\n"); | ||
508 | return err; | ||
509 | } | ||
510 | } | ||
511 | |||
512 | if (counter->own_cpus) { | ||
513 | err = perf_event__synthesize_event_update_cpus(NULL, counter, process_synthesized_event); | ||
514 | if (err < 0) { | ||
515 | pr_err("Couldn't synthesize evsel scale.\n"); | ||
516 | return err; | ||
517 | } | ||
518 | } | ||
519 | |||
520 | /* | ||
521 | * Name is needed only for pipe output, | ||
522 | * perf.data carries event names. | ||
523 | */ | ||
524 | if (is_pipe) { | ||
525 | err = perf_event__synthesize_event_update_name(NULL, counter, process_synthesized_event); | ||
526 | if (err < 0) { | ||
527 | pr_err("Couldn't synthesize evsel name.\n"); | ||
528 | return err; | ||
529 | } | ||
530 | } | ||
531 | } | ||
532 | 482 | ||
533 | err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, | 483 | err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, |
534 | process_synthesized_event, | 484 | process_synthesized_event, |
@@ -1151,7 +1101,8 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
1151 | } | 1101 | } |
1152 | 1102 | ||
1153 | static void printout(int id, int nr, struct perf_evsel *counter, double uval, | 1103 | static void printout(int id, int nr, struct perf_evsel *counter, double uval, |
1154 | char *prefix, u64 run, u64 ena, double noise) | 1104 | char *prefix, u64 run, u64 ena, double noise, |
1105 | struct runtime_stat *st) | ||
1155 | { | 1106 | { |
1156 | struct perf_stat_output_ctx out; | 1107 | struct perf_stat_output_ctx out; |
1157 | struct outstate os = { | 1108 | struct outstate os = { |
@@ -1244,7 +1195,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, | |||
1244 | 1195 | ||
1245 | perf_stat__print_shadow_stats(counter, uval, | 1196 | perf_stat__print_shadow_stats(counter, uval, |
1246 | first_shadow_cpu(counter, id), | 1197 | first_shadow_cpu(counter, id), |
1247 | &out, &metric_events); | 1198 | &out, &metric_events, st); |
1248 | if (!csv_output && !metric_only) { | 1199 | if (!csv_output && !metric_only) { |
1249 | print_noise(counter, noise); | 1200 | print_noise(counter, noise); |
1250 | print_running(run, ena); | 1201 | print_running(run, ena); |
@@ -1268,7 +1219,8 @@ static void aggr_update_shadow(void) | |||
1268 | val += perf_counts(counter->counts, cpu, 0)->val; | 1219 | val += perf_counts(counter->counts, cpu, 0)->val; |
1269 | } | 1220 | } |
1270 | perf_stat__update_shadow_stats(counter, val, | 1221 | perf_stat__update_shadow_stats(counter, val, |
1271 | first_shadow_cpu(counter, id)); | 1222 | first_shadow_cpu(counter, id), |
1223 | &rt_stat); | ||
1272 | } | 1224 | } |
1273 | } | 1225 | } |
1274 | } | 1226 | } |
@@ -1388,7 +1340,8 @@ static void print_aggr(char *prefix) | |||
1388 | fprintf(output, "%s", prefix); | 1340 | fprintf(output, "%s", prefix); |
1389 | 1341 | ||
1390 | uval = val * counter->scale; | 1342 | uval = val * counter->scale; |
1391 | printout(id, nr, counter, uval, prefix, run, ena, 1.0); | 1343 | printout(id, nr, counter, uval, prefix, run, ena, 1.0, |
1344 | &rt_stat); | ||
1392 | if (!metric_only) | 1345 | if (!metric_only) |
1393 | fputc('\n', output); | 1346 | fputc('\n', output); |
1394 | } | 1347 | } |
@@ -1397,13 +1350,24 @@ static void print_aggr(char *prefix) | |||
1397 | } | 1350 | } |
1398 | } | 1351 | } |
1399 | 1352 | ||
1400 | static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | 1353 | static int cmp_val(const void *a, const void *b) |
1401 | { | 1354 | { |
1402 | FILE *output = stat_config.output; | 1355 | return ((struct perf_aggr_thread_value *)b)->val - |
1403 | int nthreads = thread_map__nr(counter->threads); | 1356 | ((struct perf_aggr_thread_value *)a)->val; |
1404 | int ncpus = cpu_map__nr(counter->cpus); | 1357 | } |
1405 | int cpu, thread; | 1358 | |
1359 | static struct perf_aggr_thread_value *sort_aggr_thread( | ||
1360 | struct perf_evsel *counter, | ||
1361 | int nthreads, int ncpus, | ||
1362 | int *ret) | ||
1363 | { | ||
1364 | int cpu, thread, i = 0; | ||
1406 | double uval; | 1365 | double uval; |
1366 | struct perf_aggr_thread_value *buf; | ||
1367 | |||
1368 | buf = calloc(nthreads, sizeof(struct perf_aggr_thread_value)); | ||
1369 | if (!buf) | ||
1370 | return NULL; | ||
1407 | 1371 | ||
1408 | for (thread = 0; thread < nthreads; thread++) { | 1372 | for (thread = 0; thread < nthreads; thread++) { |
1409 | u64 ena = 0, run = 0, val = 0; | 1373 | u64 ena = 0, run = 0, val = 0; |
@@ -1414,13 +1378,63 @@ static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | |||
1414 | run += perf_counts(counter->counts, cpu, thread)->run; | 1378 | run += perf_counts(counter->counts, cpu, thread)->run; |
1415 | } | 1379 | } |
1416 | 1380 | ||
1381 | uval = val * counter->scale; | ||
1382 | |||
1383 | /* | ||
1384 | * Skip value 0 when enabling --per-thread globally, | ||
1385 | * otherwise too many 0 output. | ||
1386 | */ | ||
1387 | if (uval == 0.0 && target__has_per_thread(&target)) | ||
1388 | continue; | ||
1389 | |||
1390 | buf[i].counter = counter; | ||
1391 | buf[i].id = thread; | ||
1392 | buf[i].uval = uval; | ||
1393 | buf[i].val = val; | ||
1394 | buf[i].run = run; | ||
1395 | buf[i].ena = ena; | ||
1396 | i++; | ||
1397 | } | ||
1398 | |||
1399 | qsort(buf, i, sizeof(struct perf_aggr_thread_value), cmp_val); | ||
1400 | |||
1401 | if (ret) | ||
1402 | *ret = i; | ||
1403 | |||
1404 | return buf; | ||
1405 | } | ||
1406 | |||
1407 | static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | ||
1408 | { | ||
1409 | FILE *output = stat_config.output; | ||
1410 | int nthreads = thread_map__nr(counter->threads); | ||
1411 | int ncpus = cpu_map__nr(counter->cpus); | ||
1412 | int thread, sorted_threads, id; | ||
1413 | struct perf_aggr_thread_value *buf; | ||
1414 | |||
1415 | buf = sort_aggr_thread(counter, nthreads, ncpus, &sorted_threads); | ||
1416 | if (!buf) { | ||
1417 | perror("cannot sort aggr thread"); | ||
1418 | return; | ||
1419 | } | ||
1420 | |||
1421 | for (thread = 0; thread < sorted_threads; thread++) { | ||
1417 | if (prefix) | 1422 | if (prefix) |
1418 | fprintf(output, "%s", prefix); | 1423 | fprintf(output, "%s", prefix); |
1419 | 1424 | ||
1420 | uval = val * counter->scale; | 1425 | id = buf[thread].id; |
1421 | printout(thread, 0, counter, uval, prefix, run, ena, 1.0); | 1426 | if (stat_config.stats) |
1427 | printout(id, 0, buf[thread].counter, buf[thread].uval, | ||
1428 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
1429 | &stat_config.stats[id]); | ||
1430 | else | ||
1431 | printout(id, 0, buf[thread].counter, buf[thread].uval, | ||
1432 | prefix, buf[thread].run, buf[thread].ena, 1.0, | ||
1433 | &rt_stat); | ||
1422 | fputc('\n', output); | 1434 | fputc('\n', output); |
1423 | } | 1435 | } |
1436 | |||
1437 | free(buf); | ||
1424 | } | 1438 | } |
1425 | 1439 | ||
1426 | struct caggr_data { | 1440 | struct caggr_data { |
@@ -1455,7 +1469,8 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix) | |||
1455 | fprintf(output, "%s", prefix); | 1469 | fprintf(output, "%s", prefix); |
1456 | 1470 | ||
1457 | uval = cd.avg * counter->scale; | 1471 | uval = cd.avg * counter->scale; |
1458 | printout(-1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled, cd.avg); | 1472 | printout(-1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled, |
1473 | cd.avg, &rt_stat); | ||
1459 | if (!metric_only) | 1474 | if (!metric_only) |
1460 | fprintf(output, "\n"); | 1475 | fprintf(output, "\n"); |
1461 | } | 1476 | } |
@@ -1494,7 +1509,8 @@ static void print_counter(struct perf_evsel *counter, char *prefix) | |||
1494 | fprintf(output, "%s", prefix); | 1509 | fprintf(output, "%s", prefix); |
1495 | 1510 | ||
1496 | uval = val * counter->scale; | 1511 | uval = val * counter->scale; |
1497 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0); | 1512 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0, |
1513 | &rt_stat); | ||
1498 | 1514 | ||
1499 | fputc('\n', output); | 1515 | fputc('\n', output); |
1500 | } | 1516 | } |
@@ -1526,7 +1542,8 @@ static void print_no_aggr_metric(char *prefix) | |||
1526 | run = perf_counts(counter->counts, cpu, 0)->run; | 1542 | run = perf_counts(counter->counts, cpu, 0)->run; |
1527 | 1543 | ||
1528 | uval = val * counter->scale; | 1544 | uval = val * counter->scale; |
1529 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0); | 1545 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0, |
1546 | &rt_stat); | ||
1530 | } | 1547 | } |
1531 | fputc('\n', stat_config.output); | 1548 | fputc('\n', stat_config.output); |
1532 | } | 1549 | } |
@@ -1582,7 +1599,8 @@ static void print_metric_headers(const char *prefix, bool no_indent) | |||
1582 | perf_stat__print_shadow_stats(counter, 0, | 1599 | perf_stat__print_shadow_stats(counter, 0, |
1583 | 0, | 1600 | 0, |
1584 | &out, | 1601 | &out, |
1585 | &metric_events); | 1602 | &metric_events, |
1603 | &rt_stat); | ||
1586 | } | 1604 | } |
1587 | fputc('\n', stat_config.output); | 1605 | fputc('\n', stat_config.output); |
1588 | } | 1606 | } |
@@ -2541,6 +2559,35 @@ int process_cpu_map_event(struct perf_tool *tool, | |||
2541 | return set_maps(st); | 2559 | return set_maps(st); |
2542 | } | 2560 | } |
2543 | 2561 | ||
2562 | static int runtime_stat_new(struct perf_stat_config *config, int nthreads) | ||
2563 | { | ||
2564 | int i; | ||
2565 | |||
2566 | config->stats = calloc(nthreads, sizeof(struct runtime_stat)); | ||
2567 | if (!config->stats) | ||
2568 | return -1; | ||
2569 | |||
2570 | config->stats_num = nthreads; | ||
2571 | |||
2572 | for (i = 0; i < nthreads; i++) | ||
2573 | runtime_stat__init(&config->stats[i]); | ||
2574 | |||
2575 | return 0; | ||
2576 | } | ||
2577 | |||
2578 | static void runtime_stat_delete(struct perf_stat_config *config) | ||
2579 | { | ||
2580 | int i; | ||
2581 | |||
2582 | if (!config->stats) | ||
2583 | return; | ||
2584 | |||
2585 | for (i = 0; i < config->stats_num; i++) | ||
2586 | runtime_stat__exit(&config->stats[i]); | ||
2587 | |||
2588 | free(config->stats); | ||
2589 | } | ||
2590 | |||
2544 | static const char * const stat_report_usage[] = { | 2591 | static const char * const stat_report_usage[] = { |
2545 | "perf stat report [<options>]", | 2592 | "perf stat report [<options>]", |
2546 | NULL, | 2593 | NULL, |
@@ -2750,12 +2797,16 @@ int cmd_stat(int argc, const char **argv) | |||
2750 | run_count = 1; | 2797 | run_count = 1; |
2751 | } | 2798 | } |
2752 | 2799 | ||
2753 | if ((stat_config.aggr_mode == AGGR_THREAD) && !target__has_task(&target)) { | 2800 | if ((stat_config.aggr_mode == AGGR_THREAD) && |
2754 | fprintf(stderr, "The --per-thread option is only available " | 2801 | !target__has_task(&target)) { |
2755 | "when monitoring via -p -t options.\n"); | 2802 | if (!target.system_wide || target.cpu_list) { |
2756 | parse_options_usage(NULL, stat_options, "p", 1); | 2803 | fprintf(stderr, "The --per-thread option is only " |
2757 | parse_options_usage(NULL, stat_options, "t", 1); | 2804 | "available when monitoring via -p -t -a " |
2758 | goto out; | 2805 | "options or only --per-thread.\n"); |
2806 | parse_options_usage(NULL, stat_options, "p", 1); | ||
2807 | parse_options_usage(NULL, stat_options, "t", 1); | ||
2808 | goto out; | ||
2809 | } | ||
2759 | } | 2810 | } |
2760 | 2811 | ||
2761 | /* | 2812 | /* |
@@ -2779,6 +2830,9 @@ int cmd_stat(int argc, const char **argv) | |||
2779 | 2830 | ||
2780 | target__validate(&target); | 2831 | target__validate(&target); |
2781 | 2832 | ||
2833 | if ((stat_config.aggr_mode == AGGR_THREAD) && (target.system_wide)) | ||
2834 | target.per_thread = true; | ||
2835 | |||
2782 | if (perf_evlist__create_maps(evsel_list, &target) < 0) { | 2836 | if (perf_evlist__create_maps(evsel_list, &target) < 0) { |
2783 | if (target__has_task(&target)) { | 2837 | if (target__has_task(&target)) { |
2784 | pr_err("Problems finding threads of monitor\n"); | 2838 | pr_err("Problems finding threads of monitor\n"); |
@@ -2796,8 +2850,15 @@ int cmd_stat(int argc, const char **argv) | |||
2796 | * Initialize thread_map with comm names, | 2850 | * Initialize thread_map with comm names, |
2797 | * so we could print it out on output. | 2851 | * so we could print it out on output. |
2798 | */ | 2852 | */ |
2799 | if (stat_config.aggr_mode == AGGR_THREAD) | 2853 | if (stat_config.aggr_mode == AGGR_THREAD) { |
2800 | thread_map__read_comms(evsel_list->threads); | 2854 | thread_map__read_comms(evsel_list->threads); |
2855 | if (target.system_wide) { | ||
2856 | if (runtime_stat_new(&stat_config, | ||
2857 | thread_map__nr(evsel_list->threads))) { | ||
2858 | goto out; | ||
2859 | } | ||
2860 | } | ||
2861 | } | ||
2801 | 2862 | ||
2802 | if (interval && interval < 100) { | 2863 | if (interval && interval < 100) { |
2803 | if (interval < 10) { | 2864 | if (interval < 10) { |
@@ -2887,5 +2948,8 @@ out: | |||
2887 | sysfs__write_int(FREEZE_ON_SMI_PATH, 0); | 2948 | sysfs__write_int(FREEZE_ON_SMI_PATH, 0); |
2888 | 2949 | ||
2889 | perf_evlist__delete(evsel_list); | 2950 | perf_evlist__delete(evsel_list); |
2951 | |||
2952 | runtime_stat_delete(&stat_config); | ||
2953 | |||
2890 | return status; | 2954 | return status; |
2891 | } | 2955 | } |