[PATCH 10/10] perf, tools, stat: Output generic dividedby metric
From: Andi Kleen Add generic infrastructure to perf stat to output ratios for "DividedBy" entries in the event lists. Many events are more useful as ratios than in raw form, typically some count in relation to total ticks. Transfer the dividedby information from the alias to the evsel. We mark the events that need to be collected for DividedBy, and also link the events using them with a pointer. The code is careful to always prefer the right event in the same group to minimize multiplexing errors. Then add a rblist to the stat shadow code that remembers stats based on the cpu and context. Then finally update and retrieve and print these ratios similarly to the existing hardcoded perf metrics. Normally we just output the ratio as percent without further commentary, but for --metric-only this would lead to empty columns. So for this case use the original event as description. So far there is no attempt to automatically add the DividedBy event, if it is missing, however we suggest it to the user. $ perf stat -a -I 1000 -e '{unc_p_clockticks,unc_p_freq_max_os_cycles}' 1.000228813800,139,950 unc_p_clockticks 1.000228813789,833,783 unc_p_freq_max_os_cycles # 98.7% 2.000654229800,308,990 unc_p_clockticks 2.000654229396,214,238 unc_p_freq_max_os_cycles # 49.5% $ perf stat -a -I 1000 -e '{unc_p_clockticks,unc_p_freq_max_os_cycles}' --metric-only 1.000206740 48.0% 2.000451543 48.1% Signed-off-by: Andi Kleen --- tools/perf/builtin-stat.c | 3 + tools/perf/util/evsel.c| 3 + tools/perf/util/evsel.h| 3 + tools/perf/util/parse-events.c | 1 + tools/perf/util/pmu.c | 2 + tools/perf/util/pmu.h | 1 + tools/perf/util/stat-shadow.c | 139 + tools/perf/util/stat.h | 2 + 8 files changed, 154 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 501d58a4925a..a83a72aebdcb 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1141,6 +1141,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, out.print_metric = pm; out.new_line = nl; out.ctx = &os; + out.force_header = false; if (csv_output && !metric_only) { print_noise(counter, noise); @@ -1465,6 +1466,7 @@ static void print_metric_headers(const char *prefix, bool no_indent) out.ctx = &os; out.print_metric = print_metric_header; out.new_line = new_line_metric; + out.force_header = true; os.evsel = counter; perf_stat__print_shadow_stats(counter, 0, 0, @@ -2447,6 +2449,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, (const char **) stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); + perf_stat__collect_dividedby(evsel_list); perf_stat__init_shadow_stats(); if (csv_sep) { diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e58a2fbf3b16..fabe36c58d99 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -236,6 +236,9 @@ void perf_evsel__init(struct perf_evsel *evsel, evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); perf_evsel__calc_id_pos(evsel); evsel->cmdline_group_boundary = false; + evsel->dividedby = NULL; + evsel->div_event = NULL; + evsel->collect_stat = false; } struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 343776422821..376106e623bd 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -131,6 +131,9 @@ struct perf_evsel { struct list_headconfig_terms; int bpf_fd; boolmerged_stat; + const char *dividedby; + struct perf_evsel *div_event; + boolcollect_stat; }; union u64_swap { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index fba53ba22431..02f10e72cb77 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1252,6 +1252,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data, evsel->scale = info.scale; evsel->per_pkg = info.per_pkg; evsel->snapshot = info.snapshot; + evsel->dividedby = info.dividedby; } return evsel ? 0 : -ENOMEM; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 2e6f803cd56d..40c735036915 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.
[PATCH 10/10] perf, tools, stat: Output generic dividedby metric
From: Andi Kleen Add generic infrastructure to perf stat to output ratios for "DividedBy" entries in the event lists. Many events are more useful as ratios than in raw form, typically some count in relation to total ticks. Transfer the dividedby information from the alias to the evsel. We mark the events that need to be collected for DividedBy, and also link the events using them with a pointer. The code is careful to always prefer the right event in the same group to minimize multiplexing errors. Then add a rblist to the stat shadow code that remembers stats based on the cpu and context. Then finally update and retrieve and print these ratios similarly to the existing hardcoded perf metrics. Normally we just output the ratio as percent without further commentary, but for --metric-only this would lead to empty columns. So for this case use the original event as description. So far there is no attempt to automatically add the DividedBy event, if it is missing, however we suggest it to the user. $ perf stat -a -I 1000 -e '{unc_p_clockticks,unc_p_freq_max_os_cycles}' 1.000228813800,139,950 unc_p_clockticks 1.000228813789,833,783 unc_p_freq_max_os_cycles # 98.7% 2.000654229800,308,990 unc_p_clockticks 2.000654229396,214,238 unc_p_freq_max_os_cycles # 49.5% $ perf stat -a -I 1000 -e '{unc_p_clockticks,unc_p_freq_max_os_cycles}' --metric-only 1.000206740 48.0% 2.000451543 48.1% Signed-off-by: Andi Kleen --- tools/perf/builtin-stat.c | 3 + tools/perf/util/evsel.c| 3 + tools/perf/util/evsel.h| 3 + tools/perf/util/parse-events.c | 1 + tools/perf/util/pmu.c | 2 + tools/perf/util/pmu.h | 1 + tools/perf/util/stat-shadow.c | 139 + tools/perf/util/stat.h | 2 + 8 files changed, 154 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 76304f27c090..b6702a8e0031 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1141,6 +1141,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, out.print_metric = pm; out.new_line = nl; out.ctx = &os; + out.force_header = false; if (csv_output && !metric_only) { print_noise(counter, noise); @@ -1458,6 +1459,7 @@ static void print_metric_headers(const char *prefix, bool no_indent) out.ctx = &os; out.print_metric = print_metric_header; out.new_line = new_line_metric; + out.force_header = true; os.evsel = counter; perf_stat__print_shadow_stats(counter, 0, 0, @@ -2440,6 +2442,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, (const char **) stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); + perf_stat__collect_dividedby(evsel_list); perf_stat__init_shadow_stats(); if (csv_sep) { diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8bc271141d9d..3484c0c67f8d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -235,6 +235,9 @@ void perf_evsel__init(struct perf_evsel *evsel, evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); perf_evsel__calc_id_pos(evsel); evsel->cmdline_group_boundary = false; + evsel->dividedby = NULL; + evsel->div_event = NULL; + evsel->collect_stat = false; } struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4e3158fe79c2..15afaf6a393e 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -129,6 +129,9 @@ struct perf_evsel { struct list_headconfig_terms; int bpf_fd; boolalias; + const char *dividedby; + struct perf_evsel *div_event; + boolcollect_stat; }; union u64_swap { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 8b2333278988..59234666b743 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1245,6 +1245,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data, evsel->scale = info.scale; evsel->per_pkg = info.per_pkg; evsel->snapshot = info.snapshot; + evsel->dividedby = info.dividedby; } return evsel ? 0 : -ENOMEM; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index d298e7413a80..5c30a6ceee0c 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -