NULL,
 };
 
+static const char *topdown_metric_attrs[] = {
+       "slots",
+       "topdown-retiring",
+       "topdown-bad-spec",
+       "topdown-fe-bound",
+       "topdown-be-bound",
+       NULL,
+};
+
 static const char *smi_cost_attrs = {
        "{"
        "msr/aperf/,"
                char *str = NULL;
                bool warn = false;
 
+               if (!force_metric_only)
+                       stat_config.metric_only = true;
+
+               if (topdown_filter_events(topdown_metric_attrs, &str, 1) < 0) {
+                       pr_err("Out of memory\n");
+                       return -1;
+               }
+               if (topdown_metric_attrs[0] && str) {
+                       if (!stat_config.interval && !stat_config.metric_only) {
+                               fprintf(stat_config.output,
+                                       "Topdown accuracy may decrease when measuring long periods.\n"
+                                       "Please print the result regularly, e.g. -I1000\n");
+                       }
+                       goto setup_metrics;
+               }
+
+               zfree(&str);
+
                if (stat_config.aggr_mode != AGGR_GLOBAL &&
                    stat_config.aggr_mode != AGGR_CORE) {
                        pr_err("top down event configuration requires --per-core mode\n");
                        return -1;
                }
 
-               if (!force_metric_only)
-                       stat_config.metric_only = true;
                if (topdown_filter_events(topdown_attrs, &str,
                                arch_topdown_check_group(&warn)) < 0) {
                        pr_err("Out of memory\n");
                if (topdown_attrs[0] && str) {
                        if (warn)
                                arch_topdown_group_warn();
+setup_metrics:
                        err = parse_events(evsel_list, str, &errinfo);
                        if (err) {
                                fprintf(stderr,
 
        else if (perf_stat_evsel__is(counter, TOPDOWN_RECOVERY_BUBBLES))
                update_runtime_stat(st, STAT_TOPDOWN_RECOVERY_BUBBLES,
                                    ctx, cpu, count);
+       else if (perf_stat_evsel__is(counter, TOPDOWN_RETIRING))
+               update_runtime_stat(st, STAT_TOPDOWN_RETIRING,
+                                   ctx, cpu, count);
+       else if (perf_stat_evsel__is(counter, TOPDOWN_BAD_SPEC))
+               update_runtime_stat(st, STAT_TOPDOWN_BAD_SPEC,
+                                   ctx, cpu, count);
+       else if (perf_stat_evsel__is(counter, TOPDOWN_FE_BOUND))
+               update_runtime_stat(st, STAT_TOPDOWN_FE_BOUND,
+                                   ctx, cpu, count);
+       else if (perf_stat_evsel__is(counter, TOPDOWN_BE_BOUND))
+               update_runtime_stat(st, STAT_TOPDOWN_BE_BOUND,
+                                   ctx, cpu, count);
        else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
                update_runtime_stat(st, STAT_STALLED_CYCLES_FRONT,
                                    ctx, cpu, count);
        return sanitize_val(1.0 - sum);
 }
 
+/*
+ * Kernel reports metrics multiplied with slots. To get back
+ * the ratios we need to recreate the sum.
+ */
+
+static double td_metric_ratio(int ctx, int cpu,
+                             enum stat_type type,
+                             struct runtime_stat *stat)
+{
+       double sum = runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, ctx, cpu) +
+               runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, ctx, cpu) +
+               runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, ctx, cpu) +
+               runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, ctx, cpu);
+       double d = runtime_stat_avg(stat, type, ctx, cpu);
+
+       if (sum)
+               return d / sum;
+       return 0;
+}
+
+/*
+ * ... but only if most of the values are actually available.
+ * We allow two missing.
+ */
+
+static bool full_td(int ctx, int cpu,
+                   struct runtime_stat *stat)
+{
+       int c = 0;
+
+       if (runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, ctx, cpu) > 0)
+               c++;
+       if (runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, ctx, cpu) > 0)
+               c++;
+       if (runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, ctx, cpu) > 0)
+               c++;
+       if (runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, ctx, cpu) > 0)
+               c++;
+       return c >= 2;
+}
+
 static void print_smi_cost(struct perf_stat_config *config,
                           int cpu, struct evsel *evsel,
                           struct perf_stat_output_ctx *out,
                                        be_bound * 100.);
                else
                        print_metric(config, ctxp, NULL, NULL, name, 0);
+       } else if (perf_stat_evsel__is(evsel, TOPDOWN_RETIRING) &&
+                       full_td(ctx, cpu, st)) {
+               double retiring = td_metric_ratio(ctx, cpu,
+                                                 STAT_TOPDOWN_RETIRING, st);
+
+               if (retiring > 0.7)
+                       color = PERF_COLOR_GREEN;
+               print_metric(config, ctxp, color, "%8.1f%%", "retiring",
+                               retiring * 100.);
+       } else if (perf_stat_evsel__is(evsel, TOPDOWN_FE_BOUND) &&
+                       full_td(ctx, cpu, st)) {
+               double fe_bound = td_metric_ratio(ctx, cpu,
+                                                 STAT_TOPDOWN_FE_BOUND, st);
+
+               if (fe_bound > 0.2)
+                       color = PERF_COLOR_RED;
+               print_metric(config, ctxp, color, "%8.1f%%", "frontend bound",
+                               fe_bound * 100.);
+       } else if (perf_stat_evsel__is(evsel, TOPDOWN_BE_BOUND) &&
+                       full_td(ctx, cpu, st)) {
+               double be_bound = td_metric_ratio(ctx, cpu,
+                                                 STAT_TOPDOWN_BE_BOUND, st);
+
+               if (be_bound > 0.2)
+                       color = PERF_COLOR_RED;
+               print_metric(config, ctxp, color, "%8.1f%%", "backend bound",
+                               be_bound * 100.);
+       } else if (perf_stat_evsel__is(evsel, TOPDOWN_BAD_SPEC) &&
+                       full_td(ctx, cpu, st)) {
+               double bad_spec = td_metric_ratio(ctx, cpu,
+                                                 STAT_TOPDOWN_BAD_SPEC, st);
+
+               if (bad_spec > 0.1)
+                       color = PERF_COLOR_RED;
+               print_metric(config, ctxp, color, "%8.1f%%", "bad speculation",
+                               bad_spec * 100.);
        } else if (evsel->metric_expr) {
                generic_metric(config, evsel->metric_expr, evsel->metric_events, NULL,
                                evsel->name, evsel->metric_name, NULL, 1, cpu, out, st);