When perf stat --topdown is enabled, the internal event list is expanded to: "{topdown-total-slots,topdown-slots-retired,topdown-recovery-bubbles,topdown-fetch-bubbles,topdown-slots-issued}".
With this patch, 1. When --all-user is enabled, it's expanded to: "{topdown-total-slots:u,topdown-slots-retired:u,topdown-recovery-bubbles:u,topdown-fetch-bubbles:u,topdown-slots-issued:u}" 2. When --all-kernel is enabled, it's expanded to: "{topdown-total-slots:k,topdown-slots-retired:k,topdown-recovery-bubbles:k,topdown-fetch-bubbles:k,topdown-slots-issued:k}" 3. Both are enabled, it's expanded to: "{topdown-total-slots:k,topdown-slots-retired:k,topdown-recovery-bubbles:k,topdown-fetch-bubbles:k,topdown-slots-issued:k},{topdown-total-slots:u,topdown-slots-retired:u,topdown-recovery-bubbles:u,topdown-fetch-bubbles:u,topdown-slots-issued:u}" This patch creates new topdown stat type (STAT_TOPDOWN_XXX_K / STAT_TOPDOWN_XXX_U), and save the event counting value to type related entry in runtime_stat rblist. For example, root@kbl:~# perf stat -a --topdown --all-kernel -- sleep 1 Performance counter stats for 'system wide': retiring:k bad speculation:k frontend bound:k backend bound:k S0-D0-C0 2 7.6% 1.8% 40.5% 50.0% S0-D0-C1 2 15.4% 3.4% 14.4% 66.8% S0-D0-C2 2 15.8% 5.1% 26.9% 52.2% S0-D0-C3 2 5.7% 5.7% 46.2% 42.4% 1.000771709 seconds time elapsed root@kbl:~# perf stat -a --topdown --all-user -- sleep 1 Performance counter stats for 'system wide': retiring:u bad speculation:u frontend bound:u backend bound:u S0-D0-C0 2 0.5% 0.0% 0.0% 99.4% S0-D0-C1 2 5.7% 5.8% 77.7% 10.7% S0-D0-C2 2 15.5% 20.5% 35.8% 28.2% S0-D0-C3 2 14.1% 0.5% 1.5% 83.9% 1.000773028 seconds time elapsed Signed-off-by: Jin Yao <yao....@linux.intel.com> --- tools/perf/builtin-stat.c | 37 +++++++- tools/perf/util/stat-shadow.c | 167 +++++++++++++++++++++++++--------- tools/perf/util/stat.h | 12 +++ 3 files changed, 171 insertions(+), 45 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 7f4d22b00d04..b766293b9a15 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1436,7 +1436,8 @@ static int add_default_attributes(void) if (topdown_run) { char *str = NULL; - bool warn = false; + bool warn = false, append_uk = false; + struct strbuf new_str; if (stat_config.aggr_mode != AGGR_GLOBAL && stat_config.aggr_mode != AGGR_CORE) { @@ -1457,6 +1458,21 @@ static int add_default_attributes(void) return -1; } if (topdown_attrs[0] && str) { + int ret; + + if (stat_config.all_kernel || stat_config.all_user) { + ret = append_modifier(&new_str, str, + stat_config.all_kernel, + stat_config.all_user); + if (ret) + return ret; + + free(str); + str = strbuf_detach(&new_str, NULL); + strbuf_release(&new_str); + append_uk = true; + } + if (warn) arch_topdown_group_warn(); err = parse_events(evsel_list, str, &errinfo); @@ -1468,6 +1484,25 @@ static int add_default_attributes(void) free(str); return -1; } + + if (append_uk) { + struct evsel *evsel; + char *p; + + evlist__for_each_entry(evsel_list, evsel) { + /* + * We appended the modifiers ":u"/":k" + * to evsel->name. Since the events have + * been parsed, remove the appended + * modifiers from event name here. + */ + if (evsel->name) { + p = strchr(evsel->name, ':'); + if (p) + *p = 0; + } + } + } } else { fprintf(stderr, "System does not support topdown\n"); return -1; diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 70c87fdb2a43..013e0f772658 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -204,6 +204,24 @@ static void update_runtime_stat(struct runtime_stat *st, update_stats(&v->stats, count); } +static void update_runtime_stat_uk(struct runtime_stat *st, + enum stat_type type, + int ctx, int cpu, u64 count, + struct evsel *counter, int type_off) +{ + struct perf_event_attr *attr = &counter->core.attr; + + if (!attr->exclude_user && !attr->exclude_kernel) + update_runtime_stat(st, type, ctx, cpu, count); + else if (attr->exclude_user) { + update_runtime_stat(st, type + type_off, + ctx, cpu, count); + } else { + update_runtime_stat(st, type + type_off * 2, + ctx, cpu, count); + } +} + /* * Update various tracking values we maintain to print * more semantic information such as miss/hit ratios, @@ -229,20 +247,25 @@ void perf_stat__update_shadow_stats(struct evsel *counter, u64 count, else if (perf_stat_evsel__is(counter, ELISION_START)) update_runtime_stat(st, STAT_ELISION, ctx, cpu, count); else if (perf_stat_evsel__is(counter, TOPDOWN_TOTAL_SLOTS)) - update_runtime_stat(st, STAT_TOPDOWN_TOTAL_SLOTS, - ctx, cpu, count); + update_runtime_stat_uk(st, STAT_TOPDOWN_TOTAL_SLOTS, + ctx, cpu, count, counter, + STAT_TOPDOWN_NUM); else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_ISSUED)) - update_runtime_stat(st, STAT_TOPDOWN_SLOTS_ISSUED, - ctx, cpu, count); + update_runtime_stat_uk(st, STAT_TOPDOWN_SLOTS_ISSUED, + ctx, cpu, count, counter, + STAT_TOPDOWN_NUM); else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_RETIRED)) - update_runtime_stat(st, STAT_TOPDOWN_SLOTS_RETIRED, - ctx, cpu, count); + update_runtime_stat_uk(st, STAT_TOPDOWN_SLOTS_RETIRED, + ctx, cpu, count, counter, + STAT_TOPDOWN_NUM); else if (perf_stat_evsel__is(counter, TOPDOWN_FETCH_BUBBLES)) - update_runtime_stat(st, STAT_TOPDOWN_FETCH_BUBBLES, - ctx, cpu, count); + update_runtime_stat_uk(st, STAT_TOPDOWN_FETCH_BUBBLES, + ctx, cpu, count, counter, + STAT_TOPDOWN_NUM); else if (perf_stat_evsel__is(counter, TOPDOWN_RECOVERY_BUBBLES)) - update_runtime_stat(st, STAT_TOPDOWN_RECOVERY_BUBBLES, - ctx, cpu, count); + update_runtime_stat_uk(st, STAT_TOPDOWN_RECOVERY_BUBBLES, + ctx, cpu, count, counter, + STAT_TOPDOWN_NUM); else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) update_runtime_stat(st, STAT_STALLED_CYCLES_FRONT, ctx, cpu, count); @@ -410,6 +433,20 @@ static double runtime_stat_avg(struct runtime_stat *st, return avg_stats(&v->stats); } +static double runtime_stat_avg_uk(struct runtime_stat *st, + enum stat_type type, int ctx, int cpu, + struct evsel *counter, int type_off) +{ + struct perf_event_attr *attr = &counter->core.attr; + + if (!attr->exclude_user && !attr->exclude_kernel) + return runtime_stat_avg(st, type, ctx, cpu); + else if (attr->exclude_user) + return runtime_stat_avg(st, type + type_off, ctx, cpu); + + return runtime_stat_avg(st, type + type_off * 2, ctx, cpu); +} + static double runtime_stat_n(struct runtime_stat *st, enum stat_type type, int ctx, int cpu) { @@ -639,56 +676,67 @@ static double sanitize_val(double x) return x; } -static double td_total_slots(int ctx, int cpu, struct runtime_stat *st) +static double td_total_slots(int ctx, int cpu, struct runtime_stat *st, + struct evsel *evsel) { - return runtime_stat_avg(st, STAT_TOPDOWN_TOTAL_SLOTS, ctx, cpu); + return runtime_stat_avg_uk(st, STAT_TOPDOWN_TOTAL_SLOTS, ctx, cpu, + evsel, STAT_TOPDOWN_NUM); } -static double td_bad_spec(int ctx, int cpu, struct runtime_stat *st) +static double td_bad_spec(int ctx, int cpu, struct runtime_stat *st, + struct evsel *evsel) { double bad_spec = 0; double total_slots; double total; - total = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_ISSUED, ctx, cpu) - - runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED, ctx, cpu) + - runtime_stat_avg(st, STAT_TOPDOWN_RECOVERY_BUBBLES, ctx, cpu); + total = runtime_stat_avg_uk(st, STAT_TOPDOWN_SLOTS_ISSUED, ctx, cpu, + evsel, STAT_TOPDOWN_NUM) - + runtime_stat_avg_uk(st, STAT_TOPDOWN_SLOTS_RETIRED, ctx, cpu, + evsel, STAT_TOPDOWN_NUM) + + runtime_stat_avg_uk(st, STAT_TOPDOWN_RECOVERY_BUBBLES, ctx, cpu, + evsel, STAT_TOPDOWN_NUM); - total_slots = td_total_slots(ctx, cpu, st); + total_slots = td_total_slots(ctx, cpu, st, evsel); if (total_slots) bad_spec = total / total_slots; return sanitize_val(bad_spec); } -static double td_retiring(int ctx, int cpu, struct runtime_stat *st) +static double td_retiring(int ctx, int cpu, struct runtime_stat *st, + struct evsel *evsel) { double retiring = 0; - double total_slots = td_total_slots(ctx, cpu, st); - double ret_slots = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED, - ctx, cpu); + double total_slots = td_total_slots(ctx, cpu, st, evsel); + double ret_slots = runtime_stat_avg_uk(st, STAT_TOPDOWN_SLOTS_RETIRED, + ctx, cpu, evsel, + STAT_TOPDOWN_NUM); if (total_slots) retiring = ret_slots / total_slots; return retiring; } -static double td_fe_bound(int ctx, int cpu, struct runtime_stat *st) +static double td_fe_bound(int ctx, int cpu, struct runtime_stat *st, + struct evsel *evsel) { double fe_bound = 0; - double total_slots = td_total_slots(ctx, cpu, st); - double fetch_bub = runtime_stat_avg(st, STAT_TOPDOWN_FETCH_BUBBLES, - ctx, cpu); + double total_slots = td_total_slots(ctx, cpu, st, evsel); + double fetch_bub = runtime_stat_avg_uk(st, STAT_TOPDOWN_FETCH_BUBBLES, + ctx, cpu, evsel, + STAT_TOPDOWN_NUM); if (total_slots) fe_bound = fetch_bub / total_slots; return fe_bound; } -static double td_be_bound(int ctx, int cpu, struct runtime_stat *st) +static double td_be_bound(int ctx, int cpu, struct runtime_stat *st, + struct evsel *evsel) { - double sum = (td_fe_bound(ctx, cpu, st) + - td_bad_spec(ctx, cpu, st) + - td_retiring(ctx, cpu, st)); + double sum = (td_fe_bound(ctx, cpu, st, evsel) + + td_bad_spec(ctx, cpu, st, evsel) + + td_retiring(ctx, cpu, st, evsel)); if (sum == 0) return 0; return sanitize_val(1.0 - sum); @@ -814,6 +862,33 @@ static void generic_metric(struct perf_stat_config *config, zfree(&pctx.ids[i].name); } +static void print_metric_uk(struct perf_stat_config *config, + void *ctx, const char *color, + const char *fmt, const char *unit, + double val, struct evsel *evsel, + print_metric_t print_metric) +{ + struct perf_event_attr *attr = &evsel->core.attr; + char *new_unit; + + if (!attr->exclude_user && !attr->exclude_kernel) { + print_metric(config, ctx, color, fmt, unit, val); + return; + } + + new_unit = calloc(1, strlen(unit) + 3); + if (!new_unit) + return; + + if (attr->exclude_user) + sprintf(new_unit, "%s:k", unit); + else + sprintf(new_unit, "%s:u", unit); + + print_metric(config, ctx, color, fmt, new_unit, val); + free(new_unit); +} + void perf_stat__print_shadow_stats(struct perf_stat_config *config, struct evsel *evsel, double avg, int cpu, @@ -986,28 +1061,30 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config, else print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0); } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) { - double fe_bound = td_fe_bound(ctx, cpu, st); + double fe_bound = td_fe_bound(ctx, cpu, st, evsel); if (fe_bound > 0.2) color = PERF_COLOR_RED; - print_metric(config, ctxp, color, "%8.1f%%", "frontend bound", - fe_bound * 100.); + print_metric_uk(config, ctxp, color, "%8.1f%%", + "frontend bound", + fe_bound * 100., evsel, print_metric); } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) { - double retiring = td_retiring(ctx, cpu, st); + double retiring = td_retiring(ctx, cpu, st, evsel); if (retiring > 0.7) color = PERF_COLOR_GREEN; - print_metric(config, ctxp, color, "%8.1f%%", "retiring", - retiring * 100.); + print_metric_uk(config, ctxp, color, "%8.1f%%", "retiring", + retiring * 100., evsel, print_metric); } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) { - double bad_spec = td_bad_spec(ctx, cpu, st); + double bad_spec = td_bad_spec(ctx, cpu, st, evsel); if (bad_spec > 0.1) color = PERF_COLOR_RED; - print_metric(config, ctxp, color, "%8.1f%%", "bad speculation", - bad_spec * 100.); + print_metric_uk(config, ctxp, color, "%8.1f%%", + "bad speculation", + bad_spec * 100., evsel, print_metric); } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) { - double be_bound = td_be_bound(ctx, cpu, st); + double be_bound = td_be_bound(ctx, cpu, st, evsel); const char *name = "backend bound"; static int have_recovery_bubbles = -1; @@ -1020,11 +1097,13 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config, if (be_bound > 0.2) color = PERF_COLOR_RED; - if (td_total_slots(ctx, cpu, st) > 0) - print_metric(config, ctxp, color, "%8.1f%%", name, - be_bound * 100.); - else - print_metric(config, ctxp, NULL, NULL, name, 0); + if (td_total_slots(ctx, cpu, st, evsel) > 0) + print_metric_uk(config, ctxp, color, "%8.1f%%", name, + be_bound * 100., evsel, print_metric); + else { + print_metric_uk(config, ctxp, NULL, NULL, name, 0, + evsel, print_metric); + } } else if (evsel->metric_expr) { generic_metric(config, evsel->metric_expr, evsel->metric_events, evsel->name, evsel->metric_name, NULL, avg, cpu, out, st); diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 8154e07ced64..1bae80ed5543 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -60,6 +60,8 @@ enum { #define NUM_CTX CTX_BIT_MAX +#define STAT_TOPDOWN_NUM 5 + enum stat_type { STAT_NONE = 0, STAT_NSECS, @@ -81,6 +83,16 @@ enum stat_type { STAT_TOPDOWN_SLOTS_RETIRED, STAT_TOPDOWN_FETCH_BUBBLES, STAT_TOPDOWN_RECOVERY_BUBBLES, + STAT_TOPDOWN_TOTAL_SLOTS_K, + STAT_TOPDOWN_SLOTS_ISSUED_K, + STAT_TOPDOWN_SLOTS_RETIRED_K, + STAT_TOPDOWN_FETCH_BUBBLES_K, + STAT_TOPDOWN_RECOVERY_BUBBLES_K, + STAT_TOPDOWN_TOTAL_SLOTS_U, + STAT_TOPDOWN_SLOTS_ISSUED_U, + STAT_TOPDOWN_SLOTS_RETIRED_U, + STAT_TOPDOWN_FETCH_BUBBLES_U, + STAT_TOPDOWN_RECOVERY_BUBBLES_U, STAT_SMI_NUM, STAT_APERF, STAT_MAX -- 2.17.1