linux/tools/perf/util/stat-shadow.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <stdio.h>
   3#include "evsel.h"
   4#include "stat.h"
   5#include "color.h"
   6#include "pmu.h"
   7#include "rblist.h"
   8#include "evlist.h"
   9#include "expr.h"
  10#include "metricgroup.h"
  11#include "cgroup.h"
  12#include <linux/zalloc.h>
  13
  14/*
  15 * AGGR_GLOBAL: Use CPU 0
  16 * AGGR_SOCKET: Use first CPU of socket
  17 * AGGR_DIE: Use first CPU of die
  18 * AGGR_CORE: Use first CPU of core
  19 * AGGR_NONE: Use matching CPU
  20 * AGGR_THREAD: Not supported?
  21 */
  22
  23struct runtime_stat rt_stat;
  24struct stats walltime_nsecs_stats;
  25
  26struct saved_value {
  27        struct rb_node rb_node;
  28        struct evsel *evsel;
  29        enum stat_type type;
  30        int ctx;
  31        int cpu;
  32        struct cgroup *cgrp;
  33        struct runtime_stat *stat;
  34        struct stats stats;
  35        u64 metric_total;
  36        int metric_other;
  37};
  38
  39static int saved_value_cmp(struct rb_node *rb_node, const void *entry)
  40{
  41        struct saved_value *a = container_of(rb_node,
  42                                             struct saved_value,
  43                                             rb_node);
  44        const struct saved_value *b = entry;
  45
  46        if (a->cpu != b->cpu)
  47                return a->cpu - b->cpu;
  48
  49        /*
  50         * Previously the rbtree was used to link generic metrics.
  51         * The keys were evsel/cpu. Now the rbtree is extended to support
  52         * per-thread shadow stats. For shadow stats case, the keys
  53         * are cpu/type/ctx/stat (evsel is NULL). For generic metrics
  54         * case, the keys are still evsel/cpu (type/ctx/stat are 0 or NULL).
  55         */
  56        if (a->type != b->type)
  57                return a->type - b->type;
  58
  59        if (a->ctx != b->ctx)
  60                return a->ctx - b->ctx;
  61
  62        if (a->cgrp != b->cgrp)
  63                return (char *)a->cgrp < (char *)b->cgrp ? -1 : +1;
  64
  65        if (a->evsel == NULL && b->evsel == NULL) {
  66                if (a->stat == b->stat)
  67                        return 0;
  68
  69                if ((char *)a->stat < (char *)b->stat)
  70                        return -1;
  71
  72                return 1;
  73        }
  74
  75        if (a->evsel == b->evsel)
  76                return 0;
  77        if ((char *)a->evsel < (char *)b->evsel)
  78                return -1;
  79        return +1;
  80}
  81
  82static struct rb_node *saved_value_new(struct rblist *rblist __maybe_unused,
  83                                     const void *entry)
  84{
  85        struct saved_value *nd = malloc(sizeof(struct saved_value));
  86
  87        if (!nd)
  88                return NULL;
  89        memcpy(nd, entry, sizeof(struct saved_value));
  90        return &nd->rb_node;
  91}
  92
  93static void saved_value_delete(struct rblist *rblist __maybe_unused,
  94                               struct rb_node *rb_node)
  95{
  96        struct saved_value *v;
  97
  98        BUG_ON(!rb_node);
  99        v = container_of(rb_node, struct saved_value, rb_node);
 100        free(v);
 101}
 102
 103static struct saved_value *saved_value_lookup(struct evsel *evsel,
 104                                              int cpu,
 105                                              bool create,
 106                                              enum stat_type type,
 107                                              int ctx,
 108                                              struct runtime_stat *st,
 109                                              struct cgroup *cgrp)
 110{
 111        struct rblist *rblist;
 112        struct rb_node *nd;
 113        struct saved_value dm = {
 114                .cpu = cpu,
 115                .evsel = evsel,
 116                .type = type,
 117                .ctx = ctx,
 118                .stat = st,
 119                .cgrp = cgrp,
 120        };
 121
 122        rblist = &st->value_list;
 123
 124        /* don't use context info for clock events */
 125        if (type == STAT_NSECS)
 126                dm.ctx = 0;
 127
 128        nd = rblist__find(rblist, &dm);
 129        if (nd)
 130                return container_of(nd, struct saved_value, rb_node);
 131        if (create) {
 132                rblist__add_node(rblist, &dm);
 133                nd = rblist__find(rblist, &dm);
 134                if (nd)
 135                        return container_of(nd, struct saved_value, rb_node);
 136        }
 137        return NULL;
 138}
 139
 140void runtime_stat__init(struct runtime_stat *st)
 141{
 142        struct rblist *rblist = &st->value_list;
 143
 144        rblist__init(rblist);
 145        rblist->node_cmp = saved_value_cmp;
 146        rblist->node_new = saved_value_new;
 147        rblist->node_delete = saved_value_delete;
 148}
 149
 150void runtime_stat__exit(struct runtime_stat *st)
 151{
 152        rblist__exit(&st->value_list);
 153}
 154
 155void perf_stat__init_shadow_stats(void)
 156{
 157        runtime_stat__init(&rt_stat);
 158}
 159
 160static int evsel_context(struct evsel *evsel)
 161{
 162        int ctx = 0;
 163
 164        if (evsel->core.attr.exclude_kernel)
 165                ctx |= CTX_BIT_KERNEL;
 166        if (evsel->core.attr.exclude_user)
 167                ctx |= CTX_BIT_USER;
 168        if (evsel->core.attr.exclude_hv)
 169                ctx |= CTX_BIT_HV;
 170        if (evsel->core.attr.exclude_host)
 171                ctx |= CTX_BIT_HOST;
 172        if (evsel->core.attr.exclude_idle)
 173                ctx |= CTX_BIT_IDLE;
 174
 175        return ctx;
 176}
 177
 178static void reset_stat(struct runtime_stat *st)
 179{
 180        struct rblist *rblist;
 181        struct rb_node *pos, *next;
 182
 183        rblist = &st->value_list;
 184        next = rb_first_cached(&rblist->entries);
 185        while (next) {
 186                pos = next;
 187                next = rb_next(pos);
 188                memset(&container_of(pos, struct saved_value, rb_node)->stats,
 189                       0,
 190                       sizeof(struct stats));
 191        }
 192}
 193
 194void perf_stat__reset_shadow_stats(void)
 195{
 196        reset_stat(&rt_stat);
 197        memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats));
 198}
 199
 200void perf_stat__reset_shadow_per_stat(struct runtime_stat *st)
 201{
 202        reset_stat(st);
 203}
 204
 205struct runtime_stat_data {
 206        int ctx;
 207        struct cgroup *cgrp;
 208};
 209
 210static void update_runtime_stat(struct runtime_stat *st,
 211                                enum stat_type type,
 212                                int cpu, u64 count,
 213                                struct runtime_stat_data *rsd)
 214{
 215        struct saved_value *v = saved_value_lookup(NULL, cpu, true, type,
 216                                                   rsd->ctx, st, rsd->cgrp);
 217
 218        if (v)
 219                update_stats(&v->stats, count);
 220}
 221
 222/*
 223 * Update various tracking values we maintain to print
 224 * more semantic information such as miss/hit ratios,
 225 * instruction rates, etc:
 226 */
 227void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
 228                                    int cpu, struct runtime_stat *st)
 229{
 230        u64 count_ns = count;
 231        struct saved_value *v;
 232        struct runtime_stat_data rsd = {
 233                .ctx = evsel_context(counter),
 234                .cgrp = counter->cgrp,
 235        };
 236
 237        count *= counter->scale;
 238
 239        if (evsel__is_clock(counter))
 240                update_runtime_stat(st, STAT_NSECS, cpu, count_ns, &rsd);
 241        else if (evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
 242                update_runtime_stat(st, STAT_CYCLES, cpu, count, &rsd);
 243        else if (perf_stat_evsel__is(counter, CYCLES_IN_TX))
 244                update_runtime_stat(st, STAT_CYCLES_IN_TX, cpu, count, &rsd);
 245        else if (perf_stat_evsel__is(counter, TRANSACTION_START))
 246                update_runtime_stat(st, STAT_TRANSACTION, cpu, count, &rsd);
 247        else if (perf_stat_evsel__is(counter, ELISION_START))
 248                update_runtime_stat(st, STAT_ELISION, cpu, count, &rsd);
 249        else if (perf_stat_evsel__is(counter, TOPDOWN_TOTAL_SLOTS))
 250                update_runtime_stat(st, STAT_TOPDOWN_TOTAL_SLOTS,
 251                                    cpu, count, &rsd);
 252        else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_ISSUED))
 253                update_runtime_stat(st, STAT_TOPDOWN_SLOTS_ISSUED,
 254                                    cpu, count, &rsd);
 255        else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_RETIRED))
 256                update_runtime_stat(st, STAT_TOPDOWN_SLOTS_RETIRED,
 257                                    cpu, count, &rsd);
 258        else if (perf_stat_evsel__is(counter, TOPDOWN_FETCH_BUBBLES))
 259                update_runtime_stat(st, STAT_TOPDOWN_FETCH_BUBBLES,
 260                                    cpu, count, &rsd);
 261        else if (perf_stat_evsel__is(counter, TOPDOWN_RECOVERY_BUBBLES))
 262                update_runtime_stat(st, STAT_TOPDOWN_RECOVERY_BUBBLES,
 263                                    cpu, count, &rsd);
 264        else if (perf_stat_evsel__is(counter, TOPDOWN_RETIRING))
 265                update_runtime_stat(st, STAT_TOPDOWN_RETIRING,
 266                                    cpu, count, &rsd);
 267        else if (perf_stat_evsel__is(counter, TOPDOWN_BAD_SPEC))
 268                update_runtime_stat(st, STAT_TOPDOWN_BAD_SPEC,
 269                                    cpu, count, &rsd);
 270        else if (perf_stat_evsel__is(counter, TOPDOWN_FE_BOUND))
 271                update_runtime_stat(st, STAT_TOPDOWN_FE_BOUND,
 272                                    cpu, count, &rsd);
 273        else if (perf_stat_evsel__is(counter, TOPDOWN_BE_BOUND))
 274                update_runtime_stat(st, STAT_TOPDOWN_BE_BOUND,
 275                                    cpu, count, &rsd);
 276        else if (perf_stat_evsel__is(counter, TOPDOWN_HEAVY_OPS))
 277                update_runtime_stat(st, STAT_TOPDOWN_HEAVY_OPS,
 278                                    cpu, count, &rsd);
 279        else if (perf_stat_evsel__is(counter, TOPDOWN_BR_MISPREDICT))
 280                update_runtime_stat(st, STAT_TOPDOWN_BR_MISPREDICT,
 281                                    cpu, count, &rsd);
 282        else if (perf_stat_evsel__is(counter, TOPDOWN_FETCH_LAT))
 283                update_runtime_stat(st, STAT_TOPDOWN_FETCH_LAT,
 284                                    cpu, count, &rsd);
 285        else if (perf_stat_evsel__is(counter, TOPDOWN_MEM_BOUND))
 286                update_runtime_stat(st, STAT_TOPDOWN_MEM_BOUND,
 287                                    cpu, count, &rsd);
 288        else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
 289                update_runtime_stat(st, STAT_STALLED_CYCLES_FRONT,
 290                                    cpu, count, &rsd);
 291        else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND))
 292                update_runtime_stat(st, STAT_STALLED_CYCLES_BACK,
 293                                    cpu, count, &rsd);
 294        else if (evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
 295                update_runtime_stat(st, STAT_BRANCHES, cpu, count, &rsd);
 296        else if (evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES))
 297                update_runtime_stat(st, STAT_CACHEREFS, cpu, count, &rsd);
 298        else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1D))
 299                update_runtime_stat(st, STAT_L1_DCACHE, cpu, count, &rsd);
 300        else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1I))
 301                update_runtime_stat(st, STAT_L1_ICACHE, cpu, count, &rsd);
 302        else if (evsel__match(counter, HW_CACHE, HW_CACHE_LL))
 303                update_runtime_stat(st, STAT_LL_CACHE, cpu, count, &rsd);
 304        else if (evsel__match(counter, HW_CACHE, HW_CACHE_DTLB))
 305                update_runtime_stat(st, STAT_DTLB_CACHE, cpu, count, &rsd);
 306        else if (evsel__match(counter, HW_CACHE, HW_CACHE_ITLB))
 307                update_runtime_stat(st, STAT_ITLB_CACHE, cpu, count, &rsd);
 308        else if (perf_stat_evsel__is(counter, SMI_NUM))
 309                update_runtime_stat(st, STAT_SMI_NUM, cpu, count, &rsd);
 310        else if (perf_stat_evsel__is(counter, APERF))
 311                update_runtime_stat(st, STAT_APERF, cpu, count, &rsd);
 312
 313        if (counter->collect_stat) {
 314                v = saved_value_lookup(counter, cpu, true, STAT_NONE, 0, st,
 315                                       rsd.cgrp);
 316                update_stats(&v->stats, count);
 317                if (counter->metric_leader)
 318                        v->metric_total += count;
 319        } else if (counter->metric_leader) {
 320                v = saved_value_lookup(counter->metric_leader,
 321                                       cpu, true, STAT_NONE, 0, st, rsd.cgrp);
 322                v->metric_total += count;
 323                v->metric_other++;
 324        }
 325}
 326
 327/* used for get_ratio_color() */
 328enum grc_type {
 329        GRC_STALLED_CYCLES_FE,
 330        GRC_STALLED_CYCLES_BE,
 331        GRC_CACHE_MISSES,
 332        GRC_MAX_NR
 333};
 334
 335static const char *get_ratio_color(enum grc_type type, double ratio)
 336{
 337        static const double grc_table[GRC_MAX_NR][3] = {
 338                [GRC_STALLED_CYCLES_FE] = { 50.0, 30.0, 10.0 },
 339                [GRC_STALLED_CYCLES_BE] = { 75.0, 50.0, 20.0 },
 340                [GRC_CACHE_MISSES]      = { 20.0, 10.0, 5.0 },
 341        };
 342        const char *color = PERF_COLOR_NORMAL;
 343
 344        if (ratio > grc_table[type][0])
 345                color = PERF_COLOR_RED;
 346        else if (ratio > grc_table[type][1])
 347                color = PERF_COLOR_MAGENTA;
 348        else if (ratio > grc_table[type][2])
 349                color = PERF_COLOR_YELLOW;
 350
 351        return color;
 352}
 353
 354static struct evsel *perf_stat__find_event(struct evlist *evsel_list,
 355                                                const char *name)
 356{
 357        struct evsel *c2;
 358
 359        evlist__for_each_entry (evsel_list, c2) {
 360                if (!strcasecmp(c2->name, name) && !c2->collect_stat)
 361                        return c2;
 362        }
 363        return NULL;
 364}
 365
 366/* Mark MetricExpr target events and link events using them to them. */
 367void perf_stat__collect_metric_expr(struct evlist *evsel_list)
 368{
 369        struct evsel *counter, *leader, **metric_events, *oc;
 370        bool found;
 371        struct expr_parse_ctx ctx;
 372        struct hashmap_entry *cur;
 373        size_t bkt;
 374        int i;
 375
 376        expr__ctx_init(&ctx);
 377        evlist__for_each_entry(evsel_list, counter) {
 378                bool invalid = false;
 379
 380                leader = counter->leader;
 381                if (!counter->metric_expr)
 382                        continue;
 383
 384                expr__ctx_clear(&ctx);
 385                metric_events = counter->metric_events;
 386                if (!metric_events) {
 387                        if (expr__find_other(counter->metric_expr,
 388                                             counter->name,
 389                                             &ctx, 1) < 0)
 390                                continue;
 391
 392                        metric_events = calloc(sizeof(struct evsel *),
 393                                               hashmap__size(&ctx.ids) + 1);
 394                        if (!metric_events) {
 395                                expr__ctx_clear(&ctx);
 396                                return;
 397                        }
 398                        counter->metric_events = metric_events;
 399                }
 400
 401                i = 0;
 402                hashmap__for_each_entry((&ctx.ids), cur, bkt) {
 403                        const char *metric_name = (const char *)cur->key;
 404
 405                        found = false;
 406                        if (leader) {
 407                                /* Search in group */
 408                                for_each_group_member (oc, leader) {
 409                                        if (!strcasecmp(oc->name,
 410                                                        metric_name) &&
 411                                                !oc->collect_stat) {
 412                                                found = true;
 413                                                break;
 414                                        }
 415                                }
 416                        }
 417                        if (!found) {
 418                                /* Search ignoring groups */
 419                                oc = perf_stat__find_event(evsel_list,
 420                                                           metric_name);
 421                        }
 422                        if (!oc) {
 423                                /* Deduping one is good enough to handle duplicated PMUs. */
 424                                static char *printed;
 425
 426                                /*
 427                                 * Adding events automatically would be difficult, because
 428                                 * it would risk creating groups that are not schedulable.
 429                                 * perf stat doesn't understand all the scheduling constraints
 430                                 * of events. So we ask the user instead to add the missing
 431                                 * events.
 432                                 */
 433                                if (!printed ||
 434                                    strcasecmp(printed, metric_name)) {
 435                                        fprintf(stderr,
 436                                                "Add %s event to groups to get metric expression for %s\n",
 437                                                metric_name,
 438                                                counter->name);
 439                                        printed = strdup(metric_name);
 440                                }
 441                                invalid = true;
 442                                continue;
 443                        }
 444                        metric_events[i++] = oc;
 445                        oc->collect_stat = true;
 446                }
 447                metric_events[i] = NULL;
 448                if (invalid) {
 449                        free(metric_events);
 450                        counter->metric_events = NULL;
 451                        counter->metric_expr = NULL;
 452                }
 453        }
 454        expr__ctx_clear(&ctx);
 455}
 456
 457static double runtime_stat_avg(struct runtime_stat *st,
 458                               enum stat_type type, int cpu,
 459                               struct runtime_stat_data *rsd)
 460{
 461        struct saved_value *v;
 462
 463        v = saved_value_lookup(NULL, cpu, false, type, rsd->ctx, st, rsd->cgrp);
 464        if (!v)
 465                return 0.0;
 466
 467        return avg_stats(&v->stats);
 468}
 469
 470static double runtime_stat_n(struct runtime_stat *st,
 471                             enum stat_type type, int cpu,
 472                             struct runtime_stat_data *rsd)
 473{
 474        struct saved_value *v;
 475
 476        v = saved_value_lookup(NULL, cpu, false, type, rsd->ctx, st, rsd->cgrp);
 477        if (!v)
 478                return 0.0;
 479
 480        return v->stats.n;
 481}
 482
 483static void print_stalled_cycles_frontend(struct perf_stat_config *config,
 484                                          int cpu, double avg,
 485                                          struct perf_stat_output_ctx *out,
 486                                          struct runtime_stat *st,
 487                                          struct runtime_stat_data *rsd)
 488{
 489        double total, ratio = 0.0;
 490        const char *color;
 491
 492        total = runtime_stat_avg(st, STAT_CYCLES, cpu, rsd);
 493
 494        if (total)
 495                ratio = avg / total * 100.0;
 496
 497        color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio);
 498
 499        if (ratio)
 500                out->print_metric(config, out->ctx, color, "%7.2f%%", "frontend cycles idle",
 501                                  ratio);
 502        else
 503                out->print_metric(config, out->ctx, NULL, NULL, "frontend cycles idle", 0);
 504}
 505
 506static void print_stalled_cycles_backend(struct perf_stat_config *config,
 507                                         int cpu, double avg,
 508                                         struct perf_stat_output_ctx *out,
 509                                         struct runtime_stat *st,
 510                                         struct runtime_stat_data *rsd)
 511{
 512        double total, ratio = 0.0;
 513        const char *color;
 514
 515        total = runtime_stat_avg(st, STAT_CYCLES, cpu, rsd);
 516
 517        if (total)
 518                ratio = avg / total * 100.0;
 519
 520        color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio);
 521
 522        out->print_metric(config, out->ctx, color, "%7.2f%%", "backend cycles idle", ratio);
 523}
 524
 525static void print_branch_misses(struct perf_stat_config *config,
 526                                int cpu, double avg,
 527                                struct perf_stat_output_ctx *out,
 528                                struct runtime_stat *st,
 529                                struct runtime_stat_data *rsd)
 530{
 531        double total, ratio = 0.0;
 532        const char *color;
 533
 534        total = runtime_stat_avg(st, STAT_BRANCHES, cpu, rsd);
 535
 536        if (total)
 537                ratio = avg / total * 100.0;
 538
 539        color = get_ratio_color(GRC_CACHE_MISSES, ratio);
 540
 541        out->print_metric(config, out->ctx, color, "%7.2f%%", "of all branches", ratio);
 542}
 543
 544static void print_l1_dcache_misses(struct perf_stat_config *config,
 545                                   int cpu, double avg,
 546                                   struct perf_stat_output_ctx *out,
 547                                   struct runtime_stat *st,
 548                                   struct runtime_stat_data *rsd)
 549{
 550        double total, ratio = 0.0;
 551        const char *color;
 552
 553        total = runtime_stat_avg(st, STAT_L1_DCACHE, cpu, rsd);
 554
 555        if (total)
 556                ratio = avg / total * 100.0;
 557
 558        color = get_ratio_color(GRC_CACHE_MISSES, ratio);
 559
 560        out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-dcache accesses", ratio);
 561}
 562
 563static void print_l1_icache_misses(struct perf_stat_config *config,
 564                                   int cpu, double avg,
 565                                   struct perf_stat_output_ctx *out,
 566                                   struct runtime_stat *st,
 567                                   struct runtime_stat_data *rsd)
 568{
 569        double total, ratio = 0.0;
 570        const char *color;
 571
 572        total = runtime_stat_avg(st, STAT_L1_ICACHE, cpu, rsd);
 573
 574        if (total)
 575                ratio = avg / total * 100.0;
 576
 577        color = get_ratio_color(GRC_CACHE_MISSES, ratio);
 578        out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-icache accesses", ratio);
 579}
 580
 581static void print_dtlb_cache_misses(struct perf_stat_config *config,
 582                                    int cpu, double avg,
 583                                    struct perf_stat_output_ctx *out,
 584                                    struct runtime_stat *st,
 585                                    struct runtime_stat_data *rsd)
 586{
 587        double total, ratio = 0.0;
 588        const char *color;
 589
 590        total = runtime_stat_avg(st, STAT_DTLB_CACHE, cpu, rsd);
 591
 592        if (total)
 593                ratio = avg / total * 100.0;
 594
 595        color = get_ratio_color(GRC_CACHE_MISSES, ratio);
 596        out->print_metric(config, out->ctx, color, "%7.2f%%", "of all dTLB cache accesses", ratio);
 597}
 598
 599static void print_itlb_cache_misses(struct perf_stat_config *config,
 600                                    int cpu, double avg,
 601                                    struct perf_stat_output_ctx *out,
 602                                    struct runtime_stat *st,
 603                                    struct runtime_stat_data *rsd)
 604{
 605        double total, ratio = 0.0;
 606        const char *color;
 607
 608        total = runtime_stat_avg(st, STAT_ITLB_CACHE, cpu, rsd);
 609
 610        if (total)
 611                ratio = avg / total * 100.0;
 612
 613        color = get_ratio_color(GRC_CACHE_MISSES, ratio);
 614        out->print_metric(config, out->ctx, color, "%7.2f%%", "of all iTLB cache accesses", ratio);
 615}
 616
 617static void print_ll_cache_misses(struct perf_stat_config *config,
 618                                  int cpu, double avg,
 619                                  struct perf_stat_output_ctx *out,
 620                                  struct runtime_stat *st,
 621                                  struct runtime_stat_data *rsd)
 622{
 623        double total, ratio = 0.0;
 624        const char *color;
 625
 626        total = runtime_stat_avg(st, STAT_LL_CACHE, cpu, rsd);
 627
 628        if (total)
 629                ratio = avg / total * 100.0;
 630
 631        color = get_ratio_color(GRC_CACHE_MISSES, ratio);
 632        out->print_metric(config, out->ctx, color, "%7.2f%%", "of all LL-cache accesses", ratio);
 633}
 634
 635/*
 636 * High level "TopDown" CPU core pipe line bottleneck break down.
 637 *
 638 * Basic concept following
 639 * Yasin, A Top Down Method for Performance analysis and Counter architecture
 640 * ISPASS14
 641 *
 642 * The CPU pipeline is divided into 4 areas that can be bottlenecks:
 643 *
 644 * Frontend -> Backend -> Retiring
 645 * BadSpeculation in addition means out of order execution that is thrown away
 646 * (for example branch mispredictions)
 647 * Frontend is instruction decoding.
 648 * Backend is execution, like computation and accessing data in memory
 649 * Retiring is good execution that is not directly bottlenecked
 650 *
 651 * The formulas are computed in slots.
 652 * A slot is an entry in the pipeline each for the pipeline width
 653 * (for example a 4-wide pipeline has 4 slots for each cycle)
 654 *
 655 * Formulas:
 656 * BadSpeculation = ((SlotsIssued - SlotsRetired) + RecoveryBubbles) /
 657 *                      TotalSlots
 658 * Retiring = SlotsRetired / TotalSlots
 659 * FrontendBound = FetchBubbles / TotalSlots
 660 * BackendBound = 1.0 - BadSpeculation - Retiring - FrontendBound
 661 *
 662 * The kernel provides the mapping to the low level CPU events and any scaling
 663 * needed for the CPU pipeline width, for example:
 664 *
 665 * TotalSlots = Cycles * 4
 666 *
 667 * The scaling factor is communicated in the sysfs unit.
 668 *
 669 * In some cases the CPU may not be able to measure all the formulas due to
 670 * missing events. In this case multiple formulas are combined, as possible.
 671 *
 672 * Full TopDown supports more levels to sub-divide each area: for example
 673 * BackendBound into computing bound and memory bound. For now we only
 674 * support Level 1 TopDown.
 675 */
 676
 677static double sanitize_val(double x)
 678{
 679        if (x < 0 && x >= -0.02)
 680                return 0.0;
 681        return x;
 682}
 683
 684static double td_total_slots(int cpu, struct runtime_stat *st,
 685                             struct runtime_stat_data *rsd)
 686{
 687        return runtime_stat_avg(st, STAT_TOPDOWN_TOTAL_SLOTS, cpu, rsd);
 688}
 689
 690static double td_bad_spec(int cpu, struct runtime_stat *st,
 691                          struct runtime_stat_data *rsd)
 692{
 693        double bad_spec = 0;
 694        double total_slots;
 695        double total;
 696
 697        total = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_ISSUED, cpu, rsd) -
 698                runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED, cpu, rsd) +
 699                runtime_stat_avg(st, STAT_TOPDOWN_RECOVERY_BUBBLES, cpu, rsd);
 700
 701        total_slots = td_total_slots(cpu, st, rsd);
 702        if (total_slots)
 703                bad_spec = total / total_slots;
 704        return sanitize_val(bad_spec);
 705}
 706
 707static double td_retiring(int cpu, struct runtime_stat *st,
 708                          struct runtime_stat_data *rsd)
 709{
 710        double retiring = 0;
 711        double total_slots = td_total_slots(cpu, st, rsd);
 712        double ret_slots = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED,
 713                                            cpu, rsd);
 714
 715        if (total_slots)
 716                retiring = ret_slots / total_slots;
 717        return retiring;
 718}
 719
 720static double td_fe_bound(int cpu, struct runtime_stat *st,
 721                          struct runtime_stat_data *rsd)
 722{
 723        double fe_bound = 0;
 724        double total_slots = td_total_slots(cpu, st, rsd);
 725        double fetch_bub = runtime_stat_avg(st, STAT_TOPDOWN_FETCH_BUBBLES,
 726                                            cpu, rsd);
 727
 728        if (total_slots)
 729                fe_bound = fetch_bub / total_slots;
 730        return fe_bound;
 731}
 732
 733static double td_be_bound(int cpu, struct runtime_stat *st,
 734                          struct runtime_stat_data *rsd)
 735{
 736        double sum = (td_fe_bound(cpu, st, rsd) +
 737                      td_bad_spec(cpu, st, rsd) +
 738                      td_retiring(cpu, st, rsd));
 739        if (sum == 0)
 740                return 0;
 741        return sanitize_val(1.0 - sum);
 742}
 743
 744/*
 745 * Kernel reports metrics multiplied with slots. To get back
 746 * the ratios we need to recreate the sum.
 747 */
 748
 749static double td_metric_ratio(int cpu, enum stat_type type,
 750                              struct runtime_stat *stat,
 751                              struct runtime_stat_data *rsd)
 752{
 753        double sum = runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, cpu, rsd) +
 754                runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, cpu, rsd) +
 755                runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, cpu, rsd) +
 756                runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, cpu, rsd);
 757        double d = runtime_stat_avg(stat, type, cpu, rsd);
 758
 759        if (sum)
 760                return d / sum;
 761        return 0;
 762}
 763
 764/*
 765 * ... but only if most of the values are actually available.
 766 * We allow two missing.
 767 */
 768
 769static bool full_td(int cpu, struct runtime_stat *stat,
 770                    struct runtime_stat_data *rsd)
 771{
 772        int c = 0;
 773
 774        if (runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, cpu, rsd) > 0)
 775                c++;
 776        if (runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, cpu, rsd) > 0)
 777                c++;
 778        if (runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, cpu, rsd) > 0)
 779                c++;
 780        if (runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, cpu, rsd) > 0)
 781                c++;
 782        return c >= 2;
 783}
 784
 785static void print_smi_cost(struct perf_stat_config *config, int cpu,
 786                           struct perf_stat_output_ctx *out,
 787                           struct runtime_stat *st,
 788                           struct runtime_stat_data *rsd)
 789{
 790        double smi_num, aperf, cycles, cost = 0.0;
 791        const char *color = NULL;
 792
 793        smi_num = runtime_stat_avg(st, STAT_SMI_NUM, cpu, rsd);
 794        aperf = runtime_stat_avg(st, STAT_APERF, cpu, rsd);
 795        cycles = runtime_stat_avg(st, STAT_CYCLES, cpu, rsd);
 796
 797        if ((cycles == 0) || (aperf == 0))
 798                return;
 799
 800        if (smi_num)
 801                cost = (aperf - cycles) / aperf * 100.00;
 802
 803        if (cost > 10)
 804                color = PERF_COLOR_RED;
 805        out->print_metric(config, out->ctx, color, "%8.1f%%", "SMI cycles%", cost);
 806        out->print_metric(config, out->ctx, NULL, "%4.0f", "SMI#", smi_num);
 807}
 808
 809static int prepare_metric(struct evsel **metric_events,
 810                          struct metric_ref *metric_refs,
 811                          struct expr_parse_ctx *pctx,
 812                          int cpu,
 813                          struct runtime_stat *st)
 814{
 815        double scale;
 816        char *n, *pn;
 817        int i, j, ret;
 818
 819        expr__ctx_init(pctx);
 820        for (i = 0; metric_events[i]; i++) {
 821                struct saved_value *v;
 822                struct stats *stats;
 823                u64 metric_total = 0;
 824
 825                if (!strcmp(metric_events[i]->name, "duration_time")) {
 826                        stats = &walltime_nsecs_stats;
 827                        scale = 1e-9;
 828                } else {
 829                        v = saved_value_lookup(metric_events[i], cpu, false,
 830                                               STAT_NONE, 0, st,
 831                                               metric_events[i]->cgrp);
 832                        if (!v)
 833                                break;
 834                        stats = &v->stats;
 835                        scale = 1.0;
 836
 837                        if (v->metric_other)
 838                                metric_total = v->metric_total;
 839                }
 840
 841                n = strdup(metric_events[i]->name);
 842                if (!n)
 843                        return -ENOMEM;
 844                /*
 845                 * This display code with --no-merge adds [cpu] postfixes.
 846                 * These are not supported by the parser. Remove everything
 847                 * after the space.
 848                 */
 849                pn = strchr(n, ' ');
 850                if (pn)
 851                        *pn = 0;
 852
 853                if (metric_total)
 854                        expr__add_id_val(pctx, n, metric_total);
 855                else
 856                        expr__add_id_val(pctx, n, avg_stats(stats)*scale);
 857        }
 858
 859        for (j = 0; metric_refs && metric_refs[j].metric_name; j++) {
 860                ret = expr__add_ref(pctx, &metric_refs[j]);
 861                if (ret)
 862                        return ret;
 863        }
 864
 865        return i;
 866}
 867
 868static void generic_metric(struct perf_stat_config *config,
 869                           const char *metric_expr,
 870                           struct evsel **metric_events,
 871                           struct metric_ref *metric_refs,
 872                           char *name,
 873                           const char *metric_name,
 874                           const char *metric_unit,
 875                           int runtime,
 876                           int cpu,
 877                           struct perf_stat_output_ctx *out,
 878                           struct runtime_stat *st)
 879{
 880        print_metric_t print_metric = out->print_metric;
 881        struct expr_parse_ctx pctx;
 882        double ratio, scale;
 883        int i;
 884        void *ctxp = out->ctx;
 885
 886        i = prepare_metric(metric_events, metric_refs, &pctx, cpu, st);
 887        if (i < 0)
 888                return;
 889
 890        if (!metric_events[i]) {
 891                if (expr__parse(&ratio, &pctx, metric_expr, runtime) == 0) {
 892                        char *unit;
 893                        char metric_bf[64];
 894
 895                        if (metric_unit && metric_name) {
 896                                if (perf_pmu__convert_scale(metric_unit,
 897                                        &unit, &scale) >= 0) {
 898                                        ratio *= scale;
 899                                }
 900                                if (strstr(metric_expr, "?"))
 901                                        scnprintf(metric_bf, sizeof(metric_bf),
 902                                          "%s  %s_%d", unit, metric_name, runtime);
 903                                else
 904                                        scnprintf(metric_bf, sizeof(metric_bf),
 905                                          "%s  %s", unit, metric_name);
 906
 907                                print_metric(config, ctxp, NULL, "%8.1f",
 908                                             metric_bf, ratio);
 909                        } else {
 910                                print_metric(config, ctxp, NULL, "%8.2f",
 911                                        metric_name ?
 912                                        metric_name :
 913                                        out->force_header ?  name : "",
 914                                        ratio);
 915                        }
 916                } else {
 917                        print_metric(config, ctxp, NULL, NULL,
 918                                     out->force_header ?
 919                                     (metric_name ? metric_name : name) : "", 0);
 920                }
 921        } else {
 922                print_metric(config, ctxp, NULL, NULL,
 923                             out->force_header ?
 924                             (metric_name ? metric_name : name) : "", 0);
 925        }
 926
 927        expr__ctx_clear(&pctx);
 928}
 929
 930double test_generic_metric(struct metric_expr *mexp, int cpu, struct runtime_stat *st)
 931{
 932        struct expr_parse_ctx pctx;
 933        double ratio = 0.0;
 934
 935        if (prepare_metric(mexp->metric_events, mexp->metric_refs, &pctx, cpu, st) < 0)
 936                goto out;
 937
 938        if (expr__parse(&ratio, &pctx, mexp->metric_expr, 1))
 939                ratio = 0.0;
 940
 941out:
 942        expr__ctx_clear(&pctx);
 943        return ratio;
 944}
 945
 946void perf_stat__print_shadow_stats(struct perf_stat_config *config,
 947                                   struct evsel *evsel,
 948                                   double avg, int cpu,
 949                                   struct perf_stat_output_ctx *out,
 950                                   struct rblist *metric_events,
 951                                   struct runtime_stat *st)
 952{
 953        void *ctxp = out->ctx;
 954        print_metric_t print_metric = out->print_metric;
 955        double total, ratio = 0.0, total2;
 956        const char *color = NULL;
 957        struct runtime_stat_data rsd = {
 958                .ctx = evsel_context(evsel),
 959                .cgrp = evsel->cgrp,
 960        };
 961        struct metric_event *me;
 962        int num = 1;
 963
 964        if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
 965                total = runtime_stat_avg(st, STAT_CYCLES, cpu, &rsd);
 966
 967                if (total) {
 968                        ratio = avg / total;
 969                        print_metric(config, ctxp, NULL, "%7.2f ",
 970                                        "insn per cycle", ratio);
 971                } else {
 972                        print_metric(config, ctxp, NULL, NULL, "insn per cycle", 0);
 973                }
 974
 975                total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT, cpu, &rsd);
 976
 977                total = max(total, runtime_stat_avg(st,
 978                                                    STAT_STALLED_CYCLES_BACK,
 979                                                    cpu, &rsd));
 980
 981                if (total && avg) {
 982                        out->new_line(config, ctxp);
 983                        ratio = total / avg;
 984                        print_metric(config, ctxp, NULL, "%7.2f ",
 985                                        "stalled cycles per insn",
 986                                        ratio);
 987                }
 988        } else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) {
 989                if (runtime_stat_n(st, STAT_BRANCHES, cpu, &rsd) != 0)
 990                        print_branch_misses(config, cpu, avg, out, st, &rsd);
 991                else
 992                        print_metric(config, ctxp, NULL, NULL, "of all branches", 0);
 993        } else if (
 994                evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
 995                evsel->core.attr.config ==  ( PERF_COUNT_HW_CACHE_L1D |
 996                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
 997                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
 998
 999                if (runtime_stat_n(st, STAT_L1_DCACHE, cpu, &rsd) != 0)
1000                        print_l1_dcache_misses(config, cpu, avg, out, st, &rsd);
1001                else
1002                        print_metric(config, ctxp, NULL, NULL, "of all L1-dcache accesses", 0);
1003        } else if (
1004                evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
1005                evsel->core.attr.config ==  ( PERF_COUNT_HW_CACHE_L1I |
1006                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
1007                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
1008
1009                if (runtime_stat_n(st, STAT_L1_ICACHE, cpu, &rsd) != 0)
1010                        print_l1_icache_misses(config, cpu, avg, out, st, &rsd);
1011                else
1012                        print_metric(config, ctxp, NULL, NULL, "of all L1-icache accesses", 0);
1013        } else if (
1014                evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
1015                evsel->core.attr.config ==  ( PERF_COUNT_HW_CACHE_DTLB |
1016                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
1017                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
1018
1019                if (runtime_stat_n(st, STAT_DTLB_CACHE, cpu, &rsd) != 0)
1020                        print_dtlb_cache_misses(config, cpu, avg, out, st, &rsd);
1021                else
1022                        print_metric(config, ctxp, NULL, NULL, "of all dTLB cache accesses", 0);
1023        } else if (
1024                evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
1025                evsel->core.attr.config ==  ( PERF_COUNT_HW_CACHE_ITLB |
1026                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
1027                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
1028
1029                if (runtime_stat_n(st, STAT_ITLB_CACHE, cpu, &rsd) != 0)
1030                        print_itlb_cache_misses(config, cpu, avg, out, st, &rsd);
1031                else
1032                        print_metric(config, ctxp, NULL, NULL, "of all iTLB cache accesses", 0);
1033        } else if (
1034                evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
1035                evsel->core.attr.config ==  ( PERF_COUNT_HW_CACHE_LL |
1036                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
1037                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
1038
1039                if (runtime_stat_n(st, STAT_LL_CACHE, cpu, &rsd) != 0)
1040                        print_ll_cache_misses(config, cpu, avg, out, st, &rsd);
1041                else
1042                        print_metric(config, ctxp, NULL, NULL, "of all LL-cache accesses", 0);
1043        } else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) {
1044                total = runtime_stat_avg(st, STAT_CACHEREFS, cpu, &rsd);
1045
1046                if (total)
1047                        ratio = avg * 100 / total;
1048
1049                if (runtime_stat_n(st, STAT_CACHEREFS, cpu, &rsd) != 0)
1050                        print_metric(config, ctxp, NULL, "%8.3f %%",
1051                                     "of all cache refs", ratio);
1052                else
1053                        print_metric(config, ctxp, NULL, NULL, "of all cache refs", 0);
1054        } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) {
1055                print_stalled_cycles_frontend(config, cpu, avg, out, st, &rsd);
1056        } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) {
1057                print_stalled_cycles_backend(config, cpu, avg, out, st, &rsd);
1058        } else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
1059                total = runtime_stat_avg(st, STAT_NSECS, cpu, &rsd);
1060
1061                if (total) {
1062                        ratio = avg / total;
1063                        print_metric(config, ctxp, NULL, "%8.3f", "GHz", ratio);
1064                } else {
1065                        print_metric(config, ctxp, NULL, NULL, "Ghz", 0);
1066                }
1067        } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) {
1068                total = runtime_stat_avg(st, STAT_CYCLES, cpu, &rsd);
1069
1070                if (total)
1071                        print_metric(config, ctxp, NULL,
1072                                        "%7.2f%%", "transactional cycles",
1073                                        100.0 * (avg / total));
1074                else
1075                        print_metric(config, ctxp, NULL, NULL, "transactional cycles",
1076                                     0);
1077        } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) {
1078                total = runtime_stat_avg(st, STAT_CYCLES, cpu, &rsd);
1079                total2 = runtime_stat_avg(st, STAT_CYCLES_IN_TX, cpu, &rsd);
1080
1081                if (total2 < avg)
1082                        total2 = avg;
1083                if (total)
1084                        print_metric(config, ctxp, NULL, "%7.2f%%", "aborted cycles",
1085                                100.0 * ((total2-avg) / total));
1086                else
1087                        print_metric(config, ctxp, NULL, NULL, "aborted cycles", 0);
1088        } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) {
1089                total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, cpu, &rsd);
1090
1091                if (avg)
1092                        ratio = total / avg;
1093
1094                if (runtime_stat_n(st, STAT_CYCLES_IN_TX, cpu, &rsd) != 0)
1095                        print_metric(config, ctxp, NULL, "%8.0f",
1096                                     "cycles / transaction", ratio);
1097                else
1098                        print_metric(config, ctxp, NULL, NULL, "cycles / transaction",
1099                                      0);
1100        } else if (perf_stat_evsel__is(evsel, ELISION_START)) {
1101                total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, cpu, &rsd);
1102
1103                if (avg)
1104                        ratio = total / avg;
1105
1106                print_metric(config, ctxp, NULL, "%8.0f", "cycles / elision", ratio);
1107        } else if (evsel__is_clock(evsel)) {
1108                if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0)
1109                        print_metric(config, ctxp, NULL, "%8.3f", "CPUs utilized",
1110                                     avg / (ratio * evsel->scale));
1111                else
1112                        print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0);
1113        } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) {
1114                double fe_bound = td_fe_bound(cpu, st, &rsd);
1115
1116                if (fe_bound > 0.2)
1117                        color = PERF_COLOR_RED;
1118                print_metric(config, ctxp, color, "%8.1f%%", "frontend bound",
1119                                fe_bound * 100.);
1120        } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) {
1121                double retiring = td_retiring(cpu, st, &rsd);
1122
1123                if (retiring > 0.7)
1124                        color = PERF_COLOR_GREEN;
1125                print_metric(config, ctxp, color, "%8.1f%%", "retiring",
1126                                retiring * 100.);
1127        } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) {
1128                double bad_spec = td_bad_spec(cpu, st, &rsd);
1129
1130                if (bad_spec > 0.1)
1131                        color = PERF_COLOR_RED;
1132                print_metric(config, ctxp, color, "%8.1f%%", "bad speculation",
1133                                bad_spec * 100.);
1134        } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) {
1135                double be_bound = td_be_bound(cpu, st, &rsd);
1136                const char *name = "backend bound";
1137                static int have_recovery_bubbles = -1;
1138
1139                /* In case the CPU does not support topdown-recovery-bubbles */
1140                if (have_recovery_bubbles < 0)
1141                        have_recovery_bubbles = pmu_have_event("cpu",
1142                                        "topdown-recovery-bubbles");
1143                if (!have_recovery_bubbles)
1144                        name = "backend bound/bad spec";
1145
1146                if (be_bound > 0.2)
1147                        color = PERF_COLOR_RED;
1148                if (td_total_slots(cpu, st, &rsd) > 0)
1149                        print_metric(config, ctxp, color, "%8.1f%%", name,
1150                                        be_bound * 100.);
1151                else
1152                        print_metric(config, ctxp, NULL, NULL, name, 0);
1153        } else if (perf_stat_evsel__is(evsel, TOPDOWN_RETIRING) &&
1154                   full_td(cpu, st, &rsd)) {
1155                double retiring = td_metric_ratio(cpu,
1156                                                  STAT_TOPDOWN_RETIRING, st,
1157                                                  &rsd);
1158                if (retiring > 0.7)
1159                        color = PERF_COLOR_GREEN;
1160                print_metric(config, ctxp, color, "%8.1f%%", "retiring",
1161                                retiring * 100.);
1162        } else if (perf_stat_evsel__is(evsel, TOPDOWN_FE_BOUND) &&
1163                   full_td(cpu, st, &rsd)) {
1164                double fe_bound = td_metric_ratio(cpu,
1165                                                  STAT_TOPDOWN_FE_BOUND, st,
1166                                                  &rsd);
1167                if (fe_bound > 0.2)
1168                        color = PERF_COLOR_RED;
1169                print_metric(config, ctxp, color, "%8.1f%%", "frontend bound",
1170                                fe_bound * 100.);
1171        } else if (perf_stat_evsel__is(evsel, TOPDOWN_BE_BOUND) &&
1172                   full_td(cpu, st, &rsd)) {
1173                double be_bound = td_metric_ratio(cpu,
1174                                                  STAT_TOPDOWN_BE_BOUND, st,
1175                                                  &rsd);
1176                if (be_bound > 0.2)
1177                        color = PERF_COLOR_RED;
1178                print_metric(config, ctxp, color, "%8.1f%%", "backend bound",
1179                                be_bound * 100.);
1180        } else if (perf_stat_evsel__is(evsel, TOPDOWN_BAD_SPEC) &&
1181                   full_td(cpu, st, &rsd)) {
1182                double bad_spec = td_metric_ratio(cpu,
1183                                                  STAT_TOPDOWN_BAD_SPEC, st,
1184                                                  &rsd);
1185                if (bad_spec > 0.1)
1186                        color = PERF_COLOR_RED;
1187                print_metric(config, ctxp, color, "%8.1f%%", "bad speculation",
1188                                bad_spec * 100.);
1189        } else if (perf_stat_evsel__is(evsel, TOPDOWN_HEAVY_OPS) &&
1190                        full_td(cpu, st, &rsd) && (config->topdown_level > 1)) {
1191                double retiring = td_metric_ratio(cpu,
1192                                                  STAT_TOPDOWN_RETIRING, st,
1193                                                  &rsd);
1194                double heavy_ops = td_metric_ratio(cpu,
1195                                                   STAT_TOPDOWN_HEAVY_OPS, st,
1196                                                   &rsd);
1197                double light_ops = retiring - heavy_ops;
1198
1199                if (retiring > 0.7 && heavy_ops > 0.1)
1200                        color = PERF_COLOR_GREEN;
1201                print_metric(config, ctxp, color, "%8.1f%%", "heavy operations",
1202                                heavy_ops * 100.);
1203                if (retiring > 0.7 && light_ops > 0.6)
1204                        color = PERF_COLOR_GREEN;
1205                else
1206                        color = NULL;
1207                print_metric(config, ctxp, color, "%8.1f%%", "light operations",
1208                                light_ops * 100.);
1209        } else if (perf_stat_evsel__is(evsel, TOPDOWN_BR_MISPREDICT) &&
1210                        full_td(cpu, st, &rsd) && (config->topdown_level > 1)) {
1211                double bad_spec = td_metric_ratio(cpu,
1212                                                  STAT_TOPDOWN_BAD_SPEC, st,
1213                                                  &rsd);
1214                double br_mis = td_metric_ratio(cpu,
1215                                                STAT_TOPDOWN_BR_MISPREDICT, st,
1216                                                &rsd);
1217                double m_clears = bad_spec - br_mis;
1218
1219                if (bad_spec > 0.1 && br_mis > 0.05)
1220                        color = PERF_COLOR_RED;
1221                print_metric(config, ctxp, color, "%8.1f%%", "branch mispredict",
1222                                br_mis * 100.);
1223                if (bad_spec > 0.1 && m_clears > 0.05)
1224                        color = PERF_COLOR_RED;
1225                else
1226                        color = NULL;
1227                print_metric(config, ctxp, color, "%8.1f%%", "machine clears",
1228                                m_clears * 100.);
1229        } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_LAT) &&
1230                        full_td(cpu, st, &rsd) && (config->topdown_level > 1)) {
1231                double fe_bound = td_metric_ratio(cpu,
1232                                                  STAT_TOPDOWN_FE_BOUND, st,
1233                                                  &rsd);
1234                double fetch_lat = td_metric_ratio(cpu,
1235                                                   STAT_TOPDOWN_FETCH_LAT, st,
1236                                                   &rsd);
1237                double fetch_bw = fe_bound - fetch_lat;
1238
1239                if (fe_bound > 0.2 && fetch_lat > 0.15)
1240                        color = PERF_COLOR_RED;
1241                print_metric(config, ctxp, color, "%8.1f%%", "fetch latency",
1242                                fetch_lat * 100.);
1243                if (fe_bound > 0.2 && fetch_bw > 0.1)
1244                        color = PERF_COLOR_RED;
1245                else
1246                        color = NULL;
1247                print_metric(config, ctxp, color, "%8.1f%%", "fetch bandwidth",
1248                                fetch_bw * 100.);
1249        } else if (perf_stat_evsel__is(evsel, TOPDOWN_MEM_BOUND) &&
1250                        full_td(cpu, st, &rsd) && (config->topdown_level > 1)) {
1251                double be_bound = td_metric_ratio(cpu,
1252                                                  STAT_TOPDOWN_BE_BOUND, st,
1253                                                  &rsd);
1254                double mem_bound = td_metric_ratio(cpu,
1255                                                   STAT_TOPDOWN_MEM_BOUND, st,
1256                                                   &rsd);
1257                double core_bound = be_bound - mem_bound;
1258
1259                if (be_bound > 0.2 && mem_bound > 0.2)
1260                        color = PERF_COLOR_RED;
1261                print_metric(config, ctxp, color, "%8.1f%%", "memory bound",
1262                                mem_bound * 100.);
1263                if (be_bound > 0.2 && core_bound > 0.1)
1264                        color = PERF_COLOR_RED;
1265                else
1266                        color = NULL;
1267                print_metric(config, ctxp, color, "%8.1f%%", "Core bound",
1268                                core_bound * 100.);
1269        } else if (evsel->metric_expr) {
1270                generic_metric(config, evsel->metric_expr, evsel->metric_events, NULL,
1271                                evsel->name, evsel->metric_name, NULL, 1, cpu, out, st);
1272        } else if (runtime_stat_n(st, STAT_NSECS, cpu, &rsd) != 0) {
1273                char unit = 'M';
1274                char unit_buf[10];
1275
1276                total = runtime_stat_avg(st, STAT_NSECS, cpu, &rsd);
1277
1278                if (total)
1279                        ratio = 1000.0 * avg / total;
1280                if (ratio < 0.001) {
1281                        ratio *= 1000;
1282                        unit = 'K';
1283                }
1284                snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit);
1285                print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio);
1286        } else if (perf_stat_evsel__is(evsel, SMI_NUM)) {
1287                print_smi_cost(config, cpu, out, st, &rsd);
1288        } else {
1289                num = 0;
1290        }
1291
1292        if ((me = metricgroup__lookup(metric_events, evsel, false)) != NULL) {
1293                struct metric_expr *mexp;
1294
1295                list_for_each_entry (mexp, &me->head, nd) {
1296                        if (num++ > 0)
1297                                out->new_line(config, ctxp);
1298                        generic_metric(config, mexp->metric_expr, mexp->metric_events,
1299                                        mexp->metric_refs, evsel->name, mexp->metric_name,
1300                                        mexp->metric_unit, mexp->runtime, cpu, out, st);
1301                }
1302        }
1303        if (num == 0)
1304                print_metric(config, ctxp, NULL, NULL, NULL, 0);
1305}
1306