linux/tools/perf/util/stat-display.c
<<
>>
Prefs
   1#include <stdlib.h>
   2#include <stdio.h>
   3#include <inttypes.h>
   4#include <linux/string.h>
   5#include <linux/time64.h>
   6#include <math.h>
   7#include "color.h"
   8#include "counts.h"
   9#include "evlist.h"
  10#include "evsel.h"
  11#include "stat.h"
  12#include "top.h"
  13#include "thread_map.h"
  14#include "cpumap.h"
  15#include "string2.h"
  16#include <linux/ctype.h>
  17#include "cgroup.h"
  18#include <api/fs/fs.h>
  19#include "util.h"
  20#include "iostat.h"
  21#include "pmu-hybrid.h"
  22#include "evlist-hybrid.h"
  23
  24#define CNTR_NOT_SUPPORTED      "<not supported>"
  25#define CNTR_NOT_COUNTED        "<not counted>"
  26
  27static void print_running(struct perf_stat_config *config,
  28                          u64 run, u64 ena)
  29{
  30        if (config->csv_output) {
  31                fprintf(config->output, "%s%" PRIu64 "%s%.2f",
  32                                        config->csv_sep,
  33                                        run,
  34                                        config->csv_sep,
  35                                        ena ? 100.0 * run / ena : 100.0);
  36        } else if (run != ena) {
  37                fprintf(config->output, "  (%.2f%%)", 100.0 * run / ena);
  38        }
  39}
  40
  41static void print_noise_pct(struct perf_stat_config *config,
  42                            double total, double avg)
  43{
  44        double pct = rel_stddev_stats(total, avg);
  45
  46        if (config->csv_output)
  47                fprintf(config->output, "%s%.2f%%", config->csv_sep, pct);
  48        else if (pct)
  49                fprintf(config->output, "  ( +-%6.2f%% )", pct);
  50}
  51
  52static void print_noise(struct perf_stat_config *config,
  53                        struct evsel *evsel, double avg)
  54{
  55        struct perf_stat_evsel *ps;
  56
  57        if (config->run_count == 1)
  58                return;
  59
  60        ps = evsel->stats;
  61        print_noise_pct(config, stddev_stats(&ps->res_stats[0]), avg);
  62}
  63
  64static void print_cgroup(struct perf_stat_config *config, struct evsel *evsel)
  65{
  66        if (nr_cgroups) {
  67                const char *cgrp_name = evsel->cgrp ? evsel->cgrp->name  : "";
  68                fprintf(config->output, "%s%s", config->csv_sep, cgrp_name);
  69        }
  70}
  71
  72
  73static void aggr_printout(struct perf_stat_config *config,
  74                          struct evsel *evsel, struct aggr_cpu_id id, int nr)
  75{
  76        switch (config->aggr_mode) {
  77        case AGGR_CORE:
  78                fprintf(config->output, "S%d-D%d-C%*d%s%*d%s",
  79                        id.socket,
  80                        id.die,
  81                        config->csv_output ? 0 : -8,
  82                        id.core,
  83                        config->csv_sep,
  84                        config->csv_output ? 0 : 4,
  85                        nr,
  86                        config->csv_sep);
  87                break;
  88        case AGGR_DIE:
  89                fprintf(config->output, "S%d-D%*d%s%*d%s",
  90                        id.socket,
  91                        config->csv_output ? 0 : -8,
  92                        id.die,
  93                        config->csv_sep,
  94                        config->csv_output ? 0 : 4,
  95                        nr,
  96                        config->csv_sep);
  97                break;
  98        case AGGR_SOCKET:
  99                fprintf(config->output, "S%*d%s%*d%s",
 100                        config->csv_output ? 0 : -5,
 101                        id.socket,
 102                        config->csv_sep,
 103                        config->csv_output ? 0 : 4,
 104                        nr,
 105                        config->csv_sep);
 106                        break;
 107        case AGGR_NODE:
 108                fprintf(config->output, "N%*d%s%*d%s",
 109                        config->csv_output ? 0 : -5,
 110                        id.node,
 111                        config->csv_sep,
 112                        config->csv_output ? 0 : 4,
 113                        nr,
 114                        config->csv_sep);
 115                        break;
 116        case AGGR_NONE:
 117                if (evsel->percore && !config->percore_show_thread) {
 118                        fprintf(config->output, "S%d-D%d-C%*d%s",
 119                                id.socket,
 120                                id.die,
 121                                config->csv_output ? 0 : -3,
 122                                id.core, config->csv_sep);
 123                } else if (id.core > -1) {
 124                        fprintf(config->output, "CPU%*d%s",
 125                                config->csv_output ? 0 : -7,
 126                                evsel__cpus(evsel)->map[id.core],
 127                                config->csv_sep);
 128                }
 129                break;
 130        case AGGR_THREAD:
 131                fprintf(config->output, "%*s-%*d%s",
 132                        config->csv_output ? 0 : 16,
 133                        perf_thread_map__comm(evsel->core.threads, id.thread),
 134                        config->csv_output ? 0 : -8,
 135                        perf_thread_map__pid(evsel->core.threads, id.thread),
 136                        config->csv_sep);
 137                break;
 138        case AGGR_GLOBAL:
 139        case AGGR_UNSET:
 140        default:
 141                break;
 142        }
 143}
 144
 145struct outstate {
 146        FILE *fh;
 147        bool newline;
 148        const char *prefix;
 149        int  nfields;
 150        int  nr;
 151        struct aggr_cpu_id id;
 152        struct evsel *evsel;
 153};
 154
 155#define METRIC_LEN  35
 156
 157static void new_line_std(struct perf_stat_config *config __maybe_unused,
 158                         void *ctx)
 159{
 160        struct outstate *os = ctx;
 161
 162        os->newline = true;
 163}
 164
 165static void do_new_line_std(struct perf_stat_config *config,
 166                            struct outstate *os)
 167{
 168        fputc('\n', os->fh);
 169        fputs(os->prefix, os->fh);
 170        aggr_printout(config, os->evsel, os->id, os->nr);
 171        if (config->aggr_mode == AGGR_NONE)
 172                fprintf(os->fh, "        ");
 173        fprintf(os->fh, "                                                 ");
 174}
 175
 176static void print_metric_std(struct perf_stat_config *config,
 177                             void *ctx, const char *color, const char *fmt,
 178                             const char *unit, double val)
 179{
 180        struct outstate *os = ctx;
 181        FILE *out = os->fh;
 182        int n;
 183        bool newline = os->newline;
 184
 185        os->newline = false;
 186
 187        if (unit == NULL || fmt == NULL) {
 188                fprintf(out, "%-*s", METRIC_LEN, "");
 189                return;
 190        }
 191
 192        if (newline)
 193                do_new_line_std(config, os);
 194
 195        n = fprintf(out, " # ");
 196        if (color)
 197                n += color_fprintf(out, color, fmt, val);
 198        else
 199                n += fprintf(out, fmt, val);
 200        fprintf(out, " %-*s", METRIC_LEN - n - 1, unit);
 201}
 202
 203static void new_line_csv(struct perf_stat_config *config, void *ctx)
 204{
 205        struct outstate *os = ctx;
 206        int i;
 207
 208        fputc('\n', os->fh);
 209        if (os->prefix)
 210                fprintf(os->fh, "%s%s", os->prefix, config->csv_sep);
 211        aggr_printout(config, os->evsel, os->id, os->nr);
 212        for (i = 0; i < os->nfields; i++)
 213                fputs(config->csv_sep, os->fh);
 214}
 215
 216static void print_metric_csv(struct perf_stat_config *config __maybe_unused,
 217                             void *ctx,
 218                             const char *color __maybe_unused,
 219                             const char *fmt, const char *unit, double val)
 220{
 221        struct outstate *os = ctx;
 222        FILE *out = os->fh;
 223        char buf[64], *vals, *ends;
 224
 225        if (unit == NULL || fmt == NULL) {
 226                fprintf(out, "%s%s", config->csv_sep, config->csv_sep);
 227                return;
 228        }
 229        snprintf(buf, sizeof(buf), fmt, val);
 230        ends = vals = skip_spaces(buf);
 231        while (isdigit(*ends) || *ends == '.')
 232                ends++;
 233        *ends = 0;
 234        fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, skip_spaces(unit));
 235}
 236
 237/* Filter out some columns that don't work well in metrics only mode */
 238
 239static bool valid_only_metric(const char *unit)
 240{
 241        if (!unit)
 242                return false;
 243        if (strstr(unit, "/sec") ||
 244            strstr(unit, "CPUs utilized"))
 245                return false;
 246        return true;
 247}
 248
 249static const char *fixunit(char *buf, struct evsel *evsel,
 250                           const char *unit)
 251{
 252        if (!strncmp(unit, "of all", 6)) {
 253                snprintf(buf, 1024, "%s %s", evsel__name(evsel),
 254                         unit);
 255                return buf;
 256        }
 257        return unit;
 258}
 259
 260static void print_metric_only(struct perf_stat_config *config,
 261                              void *ctx, const char *color, const char *fmt,
 262                              const char *unit, double val)
 263{
 264        struct outstate *os = ctx;
 265        FILE *out = os->fh;
 266        char buf[1024], str[1024];
 267        unsigned mlen = config->metric_only_len;
 268
 269        if (!valid_only_metric(unit))
 270                return;
 271        unit = fixunit(buf, os->evsel, unit);
 272        if (mlen < strlen(unit))
 273                mlen = strlen(unit) + 1;
 274
 275        if (color)
 276                mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1;
 277
 278        color_snprintf(str, sizeof(str), color ?: "", fmt, val);
 279        fprintf(out, "%*s ", mlen, str);
 280}
 281
 282static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused,
 283                                  void *ctx, const char *color __maybe_unused,
 284                                  const char *fmt,
 285                                  const char *unit, double val)
 286{
 287        struct outstate *os = ctx;
 288        FILE *out = os->fh;
 289        char buf[64], *vals, *ends;
 290        char tbuf[1024];
 291
 292        if (!valid_only_metric(unit))
 293                return;
 294        unit = fixunit(tbuf, os->evsel, unit);
 295        snprintf(buf, sizeof buf, fmt, val);
 296        ends = vals = skip_spaces(buf);
 297        while (isdigit(*ends) || *ends == '.')
 298                ends++;
 299        *ends = 0;
 300        fprintf(out, "%s%s", vals, config->csv_sep);
 301}
 302
 303static void new_line_metric(struct perf_stat_config *config __maybe_unused,
 304                            void *ctx __maybe_unused)
 305{
 306}
 307
 308static void print_metric_header(struct perf_stat_config *config,
 309                                void *ctx, const char *color __maybe_unused,
 310                                const char *fmt __maybe_unused,
 311                                const char *unit, double val __maybe_unused)
 312{
 313        struct outstate *os = ctx;
 314        char tbuf[1024];
 315
 316        /* In case of iostat, print metric header for first root port only */
 317        if (config->iostat_run &&
 318            os->evsel->priv != os->evsel->evlist->selected->priv)
 319                return;
 320
 321        if (!valid_only_metric(unit))
 322                return;
 323        unit = fixunit(tbuf, os->evsel, unit);
 324        if (config->csv_output)
 325                fprintf(os->fh, "%s%s", unit, config->csv_sep);
 326        else
 327                fprintf(os->fh, "%*s ", config->metric_only_len, unit);
 328}
 329
 330static int first_shadow_cpu(struct perf_stat_config *config,
 331                            struct evsel *evsel, struct aggr_cpu_id id)
 332{
 333        struct evlist *evlist = evsel->evlist;
 334        int i;
 335
 336        if (config->aggr_mode == AGGR_NONE)
 337                return id.core;
 338
 339        if (!config->aggr_get_id)
 340                return 0;
 341
 342        for (i = 0; i < evsel__nr_cpus(evsel); i++) {
 343                int cpu2 = evsel__cpus(evsel)->map[i];
 344
 345                if (cpu_map__compare_aggr_cpu_id(
 346                                        config->aggr_get_id(config, evlist->core.cpus, cpu2),
 347                                        id)) {
 348                        return cpu2;
 349                }
 350        }
 351        return 0;
 352}
 353
 354static void abs_printout(struct perf_stat_config *config,
 355                         struct aggr_cpu_id id, int nr, struct evsel *evsel, double avg)
 356{
 357        FILE *output = config->output;
 358        double sc =  evsel->scale;
 359        const char *fmt;
 360
 361        if (config->csv_output) {
 362                fmt = floor(sc) != sc ?  "%.2f%s" : "%.0f%s";
 363        } else {
 364                if (config->big_num)
 365                        fmt = floor(sc) != sc ? "%'18.2f%s" : "%'18.0f%s";
 366                else
 367                        fmt = floor(sc) != sc ? "%18.2f%s" : "%18.0f%s";
 368        }
 369
 370        aggr_printout(config, evsel, id, nr);
 371
 372        fprintf(output, fmt, avg, config->csv_sep);
 373
 374        if (evsel->unit)
 375                fprintf(output, "%-*s%s",
 376                        config->csv_output ? 0 : config->unit_width,
 377                        evsel->unit, config->csv_sep);
 378
 379        fprintf(output, "%-*s", config->csv_output ? 0 : 25, evsel__name(evsel));
 380
 381        print_cgroup(config, evsel);
 382}
 383
 384static bool is_mixed_hw_group(struct evsel *counter)
 385{
 386        struct evlist *evlist = counter->evlist;
 387        u32 pmu_type = counter->core.attr.type;
 388        struct evsel *pos;
 389
 390        if (counter->core.nr_members < 2)
 391                return false;
 392
 393        evlist__for_each_entry(evlist, pos) {
 394                /* software events can be part of any hardware group */
 395                if (pos->core.attr.type == PERF_TYPE_SOFTWARE)
 396                        continue;
 397                if (pmu_type == PERF_TYPE_SOFTWARE) {
 398                        pmu_type = pos->core.attr.type;
 399                        continue;
 400                }
 401                if (pmu_type != pos->core.attr.type)
 402                        return true;
 403        }
 404
 405        return false;
 406}
 407
 408static void printout(struct perf_stat_config *config, struct aggr_cpu_id id, int nr,
 409                     struct evsel *counter, double uval,
 410                     char *prefix, u64 run, u64 ena, double noise,
 411                     struct runtime_stat *st)
 412{
 413        struct perf_stat_output_ctx out;
 414        struct outstate os = {
 415                .fh = config->output,
 416                .prefix = prefix ? prefix : "",
 417                .id = id,
 418                .nr = nr,
 419                .evsel = counter,
 420        };
 421        print_metric_t pm = print_metric_std;
 422        new_line_t nl;
 423
 424        if (config->metric_only) {
 425                nl = new_line_metric;
 426                if (config->csv_output)
 427                        pm = print_metric_only_csv;
 428                else
 429                        pm = print_metric_only;
 430        } else
 431                nl = new_line_std;
 432
 433        if (config->csv_output && !config->metric_only) {
 434                static int aggr_fields[] = {
 435                        [AGGR_GLOBAL] = 0,
 436                        [AGGR_THREAD] = 1,
 437                        [AGGR_NONE] = 1,
 438                        [AGGR_SOCKET] = 2,
 439                        [AGGR_DIE] = 2,
 440                        [AGGR_CORE] = 2,
 441                };
 442
 443                pm = print_metric_csv;
 444                nl = new_line_csv;
 445                os.nfields = 3;
 446                os.nfields += aggr_fields[config->aggr_mode];
 447                if (counter->cgrp)
 448                        os.nfields++;
 449        }
 450
 451        if (!config->no_csv_summary && config->csv_output &&
 452            config->summary && !config->interval) {
 453                fprintf(config->output, "%16s%s", "summary", config->csv_sep);
 454        }
 455
 456        if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
 457                if (config->metric_only) {
 458                        pm(config, &os, NULL, "", "", 0);
 459                        return;
 460                }
 461                aggr_printout(config, counter, id, nr);
 462
 463                fprintf(config->output, "%*s%s",
 464                        config->csv_output ? 0 : 18,
 465                        counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
 466                        config->csv_sep);
 467
 468                if (counter->supported) {
 469                        if (!evlist__has_hybrid(counter->evlist)) {
 470                                config->print_free_counters_hint = 1;
 471                                if (is_mixed_hw_group(counter))
 472                                        config->print_mixed_hw_group_error = 1;
 473                        }
 474                }
 475
 476                fprintf(config->output, "%-*s%s",
 477                        config->csv_output ? 0 : config->unit_width,
 478                        counter->unit, config->csv_sep);
 479
 480                fprintf(config->output, "%*s",
 481                        config->csv_output ? 0 : -25, evsel__name(counter));
 482
 483                print_cgroup(config, counter);
 484
 485                if (!config->csv_output)
 486                        pm(config, &os, NULL, NULL, "", 0);
 487                print_noise(config, counter, noise);
 488                print_running(config, run, ena);
 489                if (config->csv_output)
 490                        pm(config, &os, NULL, NULL, "", 0);
 491                return;
 492        }
 493
 494        if (!config->metric_only)
 495                abs_printout(config, id, nr, counter, uval);
 496
 497        out.print_metric = pm;
 498        out.new_line = nl;
 499        out.ctx = &os;
 500        out.force_header = false;
 501
 502        if (config->csv_output && !config->metric_only) {
 503                print_noise(config, counter, noise);
 504                print_running(config, run, ena);
 505        }
 506
 507        perf_stat__print_shadow_stats(config, counter, uval,
 508                                first_shadow_cpu(config, counter, id),
 509                                &out, &config->metric_events, st);
 510        if (!config->csv_output && !config->metric_only) {
 511                print_noise(config, counter, noise);
 512                print_running(config, run, ena);
 513        }
 514}
 515
 516static void aggr_update_shadow(struct perf_stat_config *config,
 517                               struct evlist *evlist)
 518{
 519        int cpu, s;
 520        struct aggr_cpu_id s2, id;
 521        u64 val;
 522        struct evsel *counter;
 523
 524        for (s = 0; s < config->aggr_map->nr; s++) {
 525                id = config->aggr_map->map[s];
 526                evlist__for_each_entry(evlist, counter) {
 527                        val = 0;
 528                        for (cpu = 0; cpu < evsel__nr_cpus(counter); cpu++) {
 529                                s2 = config->aggr_get_id(config, evlist->core.cpus, cpu);
 530                                if (!cpu_map__compare_aggr_cpu_id(s2, id))
 531                                        continue;
 532                                val += perf_counts(counter->counts, cpu, 0)->val;
 533                        }
 534                        perf_stat__update_shadow_stats(counter, val,
 535                                        first_shadow_cpu(config, counter, id),
 536                                        &rt_stat);
 537                }
 538        }
 539}
 540
 541static void uniquify_event_name(struct evsel *counter)
 542{
 543        char *new_name;
 544        char *config;
 545        int ret = 0;
 546
 547        if (counter->uniquified_name || counter->use_config_name ||
 548            !counter->pmu_name || !strncmp(counter->name, counter->pmu_name,
 549                                           strlen(counter->pmu_name)))
 550                return;
 551
 552        config = strchr(counter->name, '/');
 553        if (config) {
 554                if (asprintf(&new_name,
 555                             "%s%s", counter->pmu_name, config) > 0) {
 556                        free(counter->name);
 557                        counter->name = new_name;
 558                }
 559        } else {
 560                if (perf_pmu__has_hybrid()) {
 561                        ret = asprintf(&new_name, "%s/%s/",
 562                                       counter->pmu_name, counter->name);
 563                } else {
 564                        ret = asprintf(&new_name, "%s [%s]",
 565                                       counter->name, counter->pmu_name);
 566                }
 567
 568                if (ret) {
 569                        free(counter->name);
 570                        counter->name = new_name;
 571                }
 572        }
 573
 574        counter->uniquified_name = true;
 575}
 576
 577static void collect_all_aliases(struct perf_stat_config *config, struct evsel *counter,
 578                            void (*cb)(struct perf_stat_config *config, struct evsel *counter, void *data,
 579                                       bool first),
 580                            void *data)
 581{
 582        struct evlist *evlist = counter->evlist;
 583        struct evsel *alias;
 584
 585        alias = list_prepare_entry(counter, &(evlist->core.entries), core.node);
 586        list_for_each_entry_continue (alias, &evlist->core.entries, core.node) {
 587                if (strcmp(evsel__name(alias), evsel__name(counter)) ||
 588                    alias->scale != counter->scale ||
 589                    alias->cgrp != counter->cgrp ||
 590                    strcmp(alias->unit, counter->unit) ||
 591                    evsel__is_clock(alias) != evsel__is_clock(counter) ||
 592                    !strcmp(alias->pmu_name, counter->pmu_name))
 593                        break;
 594                alias->merged_stat = true;
 595                cb(config, alias, data, false);
 596        }
 597}
 598
 599static bool is_uncore(struct evsel *evsel)
 600{
 601        struct perf_pmu *pmu = evsel__find_pmu(evsel);
 602
 603        return pmu && pmu->is_uncore;
 604}
 605
 606static bool hybrid_uniquify(struct evsel *evsel)
 607{
 608        return perf_pmu__has_hybrid() && !is_uncore(evsel);
 609}
 610
 611static bool collect_data(struct perf_stat_config *config, struct evsel *counter,
 612                            void (*cb)(struct perf_stat_config *config, struct evsel *counter, void *data,
 613                                       bool first),
 614                            void *data)
 615{
 616        if (counter->merged_stat)
 617                return false;
 618        cb(config, counter, data, true);
 619        if (config->no_merge || hybrid_uniquify(counter))
 620                uniquify_event_name(counter);
 621        else if (counter->auto_merge_stats)
 622                collect_all_aliases(config, counter, cb, data);
 623        return true;
 624}
 625
 626struct aggr_data {
 627        u64 ena, run, val;
 628        struct aggr_cpu_id id;
 629        int nr;
 630        int cpu;
 631};
 632
 633static void aggr_cb(struct perf_stat_config *config,
 634                    struct evsel *counter, void *data, bool first)
 635{
 636        struct aggr_data *ad = data;
 637        int cpu;
 638        struct aggr_cpu_id s2;
 639
 640        for (cpu = 0; cpu < evsel__nr_cpus(counter); cpu++) {
 641                struct perf_counts_values *counts;
 642
 643                s2 = config->aggr_get_id(config, evsel__cpus(counter), cpu);
 644                if (!cpu_map__compare_aggr_cpu_id(s2, ad->id))
 645                        continue;
 646                if (first)
 647                        ad->nr++;
 648                counts = perf_counts(counter->counts, cpu, 0);
 649                /*
 650                 * When any result is bad, make them all to give
 651                 * consistent output in interval mode.
 652                 */
 653                if (counts->ena == 0 || counts->run == 0 ||
 654                    counter->counts->scaled == -1) {
 655                        ad->ena = 0;
 656                        ad->run = 0;
 657                        break;
 658                }
 659                ad->val += counts->val;
 660                ad->ena += counts->ena;
 661                ad->run += counts->run;
 662        }
 663}
 664
 665static void print_counter_aggrdata(struct perf_stat_config *config,
 666                                   struct evsel *counter, int s,
 667                                   char *prefix, bool metric_only,
 668                                   bool *first, int cpu)
 669{
 670        struct aggr_data ad;
 671        FILE *output = config->output;
 672        u64 ena, run, val;
 673        int nr;
 674        struct aggr_cpu_id id;
 675        double uval;
 676
 677        ad.id = id = config->aggr_map->map[s];
 678        ad.val = ad.ena = ad.run = 0;
 679        ad.nr = 0;
 680        if (!collect_data(config, counter, aggr_cb, &ad))
 681                return;
 682
 683        if (perf_pmu__has_hybrid() && ad.ena == 0)
 684                return;
 685
 686        nr = ad.nr;
 687        ena = ad.ena;
 688        run = ad.run;
 689        val = ad.val;
 690        if (*first && metric_only) {
 691                *first = false;
 692                aggr_printout(config, counter, id, nr);
 693        }
 694        if (prefix && !metric_only)
 695                fprintf(output, "%s", prefix);
 696
 697        uval = val * counter->scale;
 698        if (cpu != -1) {
 699                id = cpu_map__empty_aggr_cpu_id();
 700                id.core = cpu;
 701        }
 702        printout(config, id, nr, counter, uval,
 703                 prefix, run, ena, 1.0, &rt_stat);
 704        if (!metric_only)
 705                fputc('\n', output);
 706}
 707
 708static void print_aggr(struct perf_stat_config *config,
 709                       struct evlist *evlist,
 710                       char *prefix)
 711{
 712        bool metric_only = config->metric_only;
 713        FILE *output = config->output;
 714        struct evsel *counter;
 715        int s;
 716        bool first;
 717
 718        if (!config->aggr_map || !config->aggr_get_id)
 719                return;
 720
 721        aggr_update_shadow(config, evlist);
 722
 723        /*
 724         * With metric_only everything is on a single line.
 725         * Without each counter has its own line.
 726         */
 727        for (s = 0; s < config->aggr_map->nr; s++) {
 728                if (prefix && metric_only)
 729                        fprintf(output, "%s", prefix);
 730
 731                first = true;
 732                evlist__for_each_entry(evlist, counter) {
 733                        print_counter_aggrdata(config, counter, s,
 734                                               prefix, metric_only,
 735                                               &first, -1);
 736                }
 737                if (metric_only)
 738                        fputc('\n', output);
 739        }
 740}
 741
 742static int cmp_val(const void *a, const void *b)
 743{
 744        return ((struct perf_aggr_thread_value *)b)->val -
 745                ((struct perf_aggr_thread_value *)a)->val;
 746}
 747
 748static struct perf_aggr_thread_value *sort_aggr_thread(
 749                                        struct evsel *counter,
 750                                        int nthreads, int ncpus,
 751                                        int *ret,
 752                                        struct target *_target)
 753{
 754        int cpu, thread, i = 0;
 755        double uval;
 756        struct perf_aggr_thread_value *buf;
 757
 758        buf = calloc(nthreads, sizeof(struct perf_aggr_thread_value));
 759        if (!buf)
 760                return NULL;
 761
 762        for (thread = 0; thread < nthreads; thread++) {
 763                u64 ena = 0, run = 0, val = 0;
 764
 765                for (cpu = 0; cpu < ncpus; cpu++) {
 766                        val += perf_counts(counter->counts, cpu, thread)->val;
 767                        ena += perf_counts(counter->counts, cpu, thread)->ena;
 768                        run += perf_counts(counter->counts, cpu, thread)->run;
 769                }
 770
 771                uval = val * counter->scale;
 772
 773                /*
 774                 * Skip value 0 when enabling --per-thread globally,
 775                 * otherwise too many 0 output.
 776                 */
 777                if (uval == 0.0 && target__has_per_thread(_target))
 778                        continue;
 779
 780                buf[i].counter = counter;
 781                buf[i].id = cpu_map__empty_aggr_cpu_id();
 782                buf[i].id.thread = thread;
 783                buf[i].uval = uval;
 784                buf[i].val = val;
 785                buf[i].run = run;
 786                buf[i].ena = ena;
 787                i++;
 788        }
 789
 790        qsort(buf, i, sizeof(struct perf_aggr_thread_value), cmp_val);
 791
 792        if (ret)
 793                *ret = i;
 794
 795        return buf;
 796}
 797
 798static void print_aggr_thread(struct perf_stat_config *config,
 799                              struct target *_target,
 800                              struct evsel *counter, char *prefix)
 801{
 802        FILE *output = config->output;
 803        int nthreads = perf_thread_map__nr(counter->core.threads);
 804        int ncpus = perf_cpu_map__nr(counter->core.cpus);
 805        int thread, sorted_threads;
 806        struct aggr_cpu_id id;
 807        struct perf_aggr_thread_value *buf;
 808
 809        buf = sort_aggr_thread(counter, nthreads, ncpus, &sorted_threads, _target);
 810        if (!buf) {
 811                perror("cannot sort aggr thread");
 812                return;
 813        }
 814
 815        for (thread = 0; thread < sorted_threads; thread++) {
 816                if (prefix)
 817                        fprintf(output, "%s", prefix);
 818
 819                id = buf[thread].id;
 820                if (config->stats)
 821                        printout(config, id, 0, buf[thread].counter, buf[thread].uval,
 822                                 prefix, buf[thread].run, buf[thread].ena, 1.0,
 823                                 &config->stats[id.thread]);
 824                else
 825                        printout(config, id, 0, buf[thread].counter, buf[thread].uval,
 826                                 prefix, buf[thread].run, buf[thread].ena, 1.0,
 827                                 &rt_stat);
 828                fputc('\n', output);
 829        }
 830
 831        free(buf);
 832}
 833
 834struct caggr_data {
 835        double avg, avg_enabled, avg_running;
 836};
 837
 838static void counter_aggr_cb(struct perf_stat_config *config __maybe_unused,
 839                            struct evsel *counter, void *data,
 840                            bool first __maybe_unused)
 841{
 842        struct caggr_data *cd = data;
 843        struct perf_counts_values *aggr = &counter->counts->aggr;
 844
 845        cd->avg += aggr->val;
 846        cd->avg_enabled += aggr->ena;
 847        cd->avg_running += aggr->run;
 848}
 849
 850/*
 851 * Print out the results of a single counter:
 852 * aggregated counts in system-wide mode
 853 */
 854static void print_counter_aggr(struct perf_stat_config *config,
 855                               struct evsel *counter, char *prefix)
 856{
 857        bool metric_only = config->metric_only;
 858        FILE *output = config->output;
 859        double uval;
 860        struct caggr_data cd = { .avg = 0.0 };
 861
 862        if (!collect_data(config, counter, counter_aggr_cb, &cd))
 863                return;
 864
 865        if (prefix && !metric_only)
 866                fprintf(output, "%s", prefix);
 867
 868        uval = cd.avg * counter->scale;
 869        printout(config, cpu_map__empty_aggr_cpu_id(), 0, counter, uval, prefix, cd.avg_running,
 870                 cd.avg_enabled, cd.avg, &rt_stat);
 871        if (!metric_only)
 872                fprintf(output, "\n");
 873}
 874
 875static void counter_cb(struct perf_stat_config *config __maybe_unused,
 876                       struct evsel *counter, void *data,
 877                       bool first __maybe_unused)
 878{
 879        struct aggr_data *ad = data;
 880
 881        ad->val += perf_counts(counter->counts, ad->cpu, 0)->val;
 882        ad->ena += perf_counts(counter->counts, ad->cpu, 0)->ena;
 883        ad->run += perf_counts(counter->counts, ad->cpu, 0)->run;
 884}
 885
 886/*
 887 * Print out the results of a single counter:
 888 * does not use aggregated count in system-wide
 889 */
 890static void print_counter(struct perf_stat_config *config,
 891                          struct evsel *counter, char *prefix)
 892{
 893        FILE *output = config->output;
 894        u64 ena, run, val;
 895        double uval;
 896        int cpu;
 897        struct aggr_cpu_id id;
 898
 899        for (cpu = 0; cpu < evsel__nr_cpus(counter); cpu++) {
 900                struct aggr_data ad = { .cpu = cpu };
 901
 902                if (!collect_data(config, counter, counter_cb, &ad))
 903                        return;
 904                val = ad.val;
 905                ena = ad.ena;
 906                run = ad.run;
 907
 908                if (prefix)
 909                        fprintf(output, "%s", prefix);
 910
 911                uval = val * counter->scale;
 912                id = cpu_map__empty_aggr_cpu_id();
 913                id.core = cpu;
 914                printout(config, id, 0, counter, uval, prefix,
 915                         run, ena, 1.0, &rt_stat);
 916
 917                fputc('\n', output);
 918        }
 919}
 920
 921static void print_no_aggr_metric(struct perf_stat_config *config,
 922                                 struct evlist *evlist,
 923                                 char *prefix)
 924{
 925        int cpu;
 926        int nrcpus = 0;
 927        struct evsel *counter;
 928        u64 ena, run, val;
 929        double uval;
 930        struct aggr_cpu_id id;
 931
 932        nrcpus = evlist->core.cpus->nr;
 933        for (cpu = 0; cpu < nrcpus; cpu++) {
 934                bool first = true;
 935
 936                if (prefix)
 937                        fputs(prefix, config->output);
 938                evlist__for_each_entry(evlist, counter) {
 939                        id = cpu_map__empty_aggr_cpu_id();
 940                        id.core = cpu;
 941                        if (first) {
 942                                aggr_printout(config, counter, id, 0);
 943                                first = false;
 944                        }
 945                        val = perf_counts(counter->counts, cpu, 0)->val;
 946                        ena = perf_counts(counter->counts, cpu, 0)->ena;
 947                        run = perf_counts(counter->counts, cpu, 0)->run;
 948
 949                        uval = val * counter->scale;
 950                        printout(config, id, 0, counter, uval, prefix,
 951                                 run, ena, 1.0, &rt_stat);
 952                }
 953                fputc('\n', config->output);
 954        }
 955}
 956
 957static int aggr_header_lens[] = {
 958        [AGGR_CORE] = 24,
 959        [AGGR_DIE] = 18,
 960        [AGGR_SOCKET] = 12,
 961        [AGGR_NONE] = 6,
 962        [AGGR_THREAD] = 24,
 963        [AGGR_GLOBAL] = 0,
 964};
 965
 966static const char *aggr_header_csv[] = {
 967        [AGGR_CORE]     =       "core,cpus,",
 968        [AGGR_DIE]      =       "die,cpus",
 969        [AGGR_SOCKET]   =       "socket,cpus",
 970        [AGGR_NONE]     =       "cpu,",
 971        [AGGR_THREAD]   =       "comm-pid,",
 972        [AGGR_GLOBAL]   =       ""
 973};
 974
 975static void print_metric_headers(struct perf_stat_config *config,
 976                                 struct evlist *evlist,
 977                                 const char *prefix, bool no_indent)
 978{
 979        struct perf_stat_output_ctx out;
 980        struct evsel *counter;
 981        struct outstate os = {
 982                .fh = config->output
 983        };
 984
 985        if (prefix)
 986                fprintf(config->output, "%s", prefix);
 987
 988        if (!config->csv_output && !no_indent)
 989                fprintf(config->output, "%*s",
 990                        aggr_header_lens[config->aggr_mode], "");
 991        if (config->csv_output) {
 992                if (config->interval)
 993                        fputs("time,", config->output);
 994                if (!config->iostat_run)
 995                        fputs(aggr_header_csv[config->aggr_mode], config->output);
 996        }
 997        if (config->iostat_run)
 998                iostat_print_header_prefix(config);
 999
1000        /* Print metrics headers only */
1001        evlist__for_each_entry(evlist, counter) {
1002                os.evsel = counter;
1003                out.ctx = &os;
1004                out.print_metric = print_metric_header;
1005                out.new_line = new_line_metric;
1006                out.force_header = true;
1007                perf_stat__print_shadow_stats(config, counter, 0,
1008                                              0,
1009                                              &out,
1010                                              &config->metric_events,
1011                                              &rt_stat);
1012        }
1013        fputc('\n', config->output);
1014}
1015
1016static void print_interval(struct perf_stat_config *config,
1017                           struct evlist *evlist,
1018                           char *prefix, struct timespec *ts)
1019{
1020        bool metric_only = config->metric_only;
1021        unsigned int unit_width = config->unit_width;
1022        FILE *output = config->output;
1023        static int num_print_interval;
1024
1025        if (config->interval_clear)
1026                puts(CONSOLE_CLEAR);
1027
1028        if (!config->iostat_run)
1029                sprintf(prefix, "%6lu.%09lu%s", (unsigned long) ts->tv_sec, ts->tv_nsec, config->csv_sep);
1030
1031        if ((num_print_interval == 0 && !config->csv_output) || config->interval_clear) {
1032                switch (config->aggr_mode) {
1033                case AGGR_NODE:
1034                        fprintf(output, "#           time node   cpus");
1035                        if (!metric_only)
1036                                fprintf(output, "             counts %*s events\n", unit_width, "unit");
1037                        break;
1038                case AGGR_SOCKET:
1039                        fprintf(output, "#           time socket cpus");
1040                        if (!metric_only)
1041                                fprintf(output, "             counts %*s events\n", unit_width, "unit");
1042                        break;
1043                case AGGR_DIE:
1044                        fprintf(output, "#           time die          cpus");
1045                        if (!metric_only)
1046                                fprintf(output, "             counts %*s events\n", unit_width, "unit");
1047                        break;
1048                case AGGR_CORE:
1049                        fprintf(output, "#           time core            cpus");
1050                        if (!metric_only)
1051                                fprintf(output, "             counts %*s events\n", unit_width, "unit");
1052                        break;
1053                case AGGR_NONE:
1054                        fprintf(output, "#           time CPU    ");
1055                        if (!metric_only)
1056                                fprintf(output, "                counts %*s events\n", unit_width, "unit");
1057                        break;
1058                case AGGR_THREAD:
1059                        fprintf(output, "#           time             comm-pid");
1060                        if (!metric_only)
1061                                fprintf(output, "                  counts %*s events\n", unit_width, "unit");
1062                        break;
1063                case AGGR_GLOBAL:
1064                default:
1065                        if (!config->iostat_run) {
1066                                fprintf(output, "#           time");
1067                                if (!metric_only)
1068                                        fprintf(output, "             counts %*s events\n", unit_width, "unit");
1069                        }
1070                case AGGR_UNSET:
1071                        break;
1072                }
1073        }
1074
1075        if ((num_print_interval == 0 || config->interval_clear) && metric_only)
1076                print_metric_headers(config, evlist, " ", true);
1077        if (++num_print_interval == 25)
1078                num_print_interval = 0;
1079}
1080
1081static void print_header(struct perf_stat_config *config,
1082                         struct target *_target,
1083                         int argc, const char **argv)
1084{
1085        FILE *output = config->output;
1086        int i;
1087
1088        fflush(stdout);
1089
1090        if (!config->csv_output) {
1091                fprintf(output, "\n");
1092                fprintf(output, " Performance counter stats for ");
1093                if (_target->bpf_str)
1094                        fprintf(output, "\'BPF program(s) %s", _target->bpf_str);
1095                else if (_target->system_wide)
1096                        fprintf(output, "\'system wide");
1097                else if (_target->cpu_list)
1098                        fprintf(output, "\'CPU(s) %s", _target->cpu_list);
1099                else if (!target__has_task(_target)) {
1100                        fprintf(output, "\'%s", argv ? argv[0] : "pipe");
1101                        for (i = 1; argv && (i < argc); i++)
1102                                fprintf(output, " %s", argv[i]);
1103                } else if (_target->pid)
1104                        fprintf(output, "process id \'%s", _target->pid);
1105                else
1106                        fprintf(output, "thread id \'%s", _target->tid);
1107
1108                fprintf(output, "\'");
1109                if (config->run_count > 1)
1110                        fprintf(output, " (%d runs)", config->run_count);
1111                fprintf(output, ":\n\n");
1112        }
1113}
1114
1115static int get_precision(double num)
1116{
1117        if (num > 1)
1118                return 0;
1119
1120        return lround(ceil(-log10(num)));
1121}
1122
1123static void print_table(struct perf_stat_config *config,
1124                        FILE *output, int precision, double avg)
1125{
1126        char tmp[64];
1127        int idx, indent = 0;
1128
1129        scnprintf(tmp, 64, " %17.*f", precision, avg);
1130        while (tmp[indent] == ' ')
1131                indent++;
1132
1133        fprintf(output, "%*s# Table of individual measurements:\n", indent, "");
1134
1135        for (idx = 0; idx < config->run_count; idx++) {
1136                double run = (double) config->walltime_run[idx] / NSEC_PER_SEC;
1137                int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5);
1138
1139                fprintf(output, " %17.*f (%+.*f) ",
1140                        precision, run, precision, run - avg);
1141
1142                for (h = 0; h < n; h++)
1143                        fprintf(output, "#");
1144
1145                fprintf(output, "\n");
1146        }
1147
1148        fprintf(output, "\n%*s# Final result:\n", indent, "");
1149}
1150
1151static double timeval2double(struct timeval *t)
1152{
1153        return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC;
1154}
1155
1156static void print_footer(struct perf_stat_config *config)
1157{
1158        double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC;
1159        FILE *output = config->output;
1160
1161        if (!config->null_run)
1162                fprintf(output, "\n");
1163
1164        if (config->run_count == 1) {
1165                fprintf(output, " %17.9f seconds time elapsed", avg);
1166
1167                if (config->ru_display) {
1168                        double ru_utime = timeval2double(&config->ru_data.ru_utime);
1169                        double ru_stime = timeval2double(&config->ru_data.ru_stime);
1170
1171                        fprintf(output, "\n\n");
1172                        fprintf(output, " %17.9f seconds user\n", ru_utime);
1173                        fprintf(output, " %17.9f seconds sys\n", ru_stime);
1174                }
1175        } else {
1176                double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC;
1177                /*
1178                 * Display at most 2 more significant
1179                 * digits than the stddev inaccuracy.
1180                 */
1181                int precision = get_precision(sd) + 2;
1182
1183                if (config->walltime_run_table)
1184                        print_table(config, output, precision, avg);
1185
1186                fprintf(output, " %17.*f +- %.*f seconds time elapsed",
1187                        precision, avg, precision, sd);
1188
1189                print_noise_pct(config, sd, avg);
1190        }
1191        fprintf(output, "\n\n");
1192
1193        if (config->print_free_counters_hint && sysctl__nmi_watchdog_enabled())
1194                fprintf(output,
1195"Some events weren't counted. Try disabling the NMI watchdog:\n"
1196"       echo 0 > /proc/sys/kernel/nmi_watchdog\n"
1197"       perf stat ...\n"
1198"       echo 1 > /proc/sys/kernel/nmi_watchdog\n");
1199
1200        if (config->print_mixed_hw_group_error)
1201                fprintf(output,
1202                        "The events in group usually have to be from "
1203                        "the same PMU. Try reorganizing the group.\n");
1204}
1205
1206static void print_percore_thread(struct perf_stat_config *config,
1207                                 struct evsel *counter, char *prefix)
1208{
1209        int s;
1210        struct aggr_cpu_id s2, id;
1211        bool first = true;
1212
1213        for (int i = 0; i < evsel__nr_cpus(counter); i++) {
1214                s2 = config->aggr_get_id(config, evsel__cpus(counter), i);
1215                for (s = 0; s < config->aggr_map->nr; s++) {
1216                        id = config->aggr_map->map[s];
1217                        if (cpu_map__compare_aggr_cpu_id(s2, id))
1218                                break;
1219                }
1220
1221                print_counter_aggrdata(config, counter, s,
1222                                       prefix, false,
1223                                       &first, i);
1224        }
1225}
1226
1227static void print_percore(struct perf_stat_config *config,
1228                          struct evsel *counter, char *prefix)
1229{
1230        bool metric_only = config->metric_only;
1231        FILE *output = config->output;
1232        int s;
1233        bool first = true;
1234
1235        if (!config->aggr_map || !config->aggr_get_id)
1236                return;
1237
1238        if (config->percore_show_thread)
1239                return print_percore_thread(config, counter, prefix);
1240
1241        for (s = 0; s < config->aggr_map->nr; s++) {
1242                if (prefix && metric_only)
1243                        fprintf(output, "%s", prefix);
1244
1245                print_counter_aggrdata(config, counter, s,
1246                                       prefix, metric_only,
1247                                       &first, -1);
1248        }
1249
1250        if (metric_only)
1251                fputc('\n', output);
1252}
1253
1254void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config,
1255                            struct target *_target, struct timespec *ts, int argc, const char **argv)
1256{
1257        bool metric_only = config->metric_only;
1258        int interval = config->interval;
1259        struct evsel *counter;
1260        char buf[64], *prefix = NULL;
1261
1262        if (config->iostat_run)
1263                evlist->selected = evlist__first(evlist);
1264
1265        if (interval)
1266                print_interval(config, evlist, prefix = buf, ts);
1267        else
1268                print_header(config, _target, argc, argv);
1269
1270        if (metric_only) {
1271                static int num_print_iv;
1272
1273                if (num_print_iv == 0 && !interval)
1274                        print_metric_headers(config, evlist, prefix, false);
1275                if (num_print_iv++ == 25)
1276                        num_print_iv = 0;
1277                if (config->aggr_mode == AGGR_GLOBAL && prefix && !config->iostat_run)
1278                        fprintf(config->output, "%s", prefix);
1279        }
1280
1281        switch (config->aggr_mode) {
1282        case AGGR_CORE:
1283        case AGGR_DIE:
1284        case AGGR_SOCKET:
1285        case AGGR_NODE:
1286                print_aggr(config, evlist, prefix);
1287                break;
1288        case AGGR_THREAD:
1289                evlist__for_each_entry(evlist, counter) {
1290                        print_aggr_thread(config, _target, counter, prefix);
1291                }
1292                break;
1293        case AGGR_GLOBAL:
1294                if (config->iostat_run)
1295                        iostat_print_counters(evlist, config, ts, prefix = buf,
1296                                              print_counter_aggr);
1297                else {
1298                        evlist__for_each_entry(evlist, counter) {
1299                                print_counter_aggr(config, counter, prefix);
1300                        }
1301                        if (metric_only)
1302                                fputc('\n', config->output);
1303                }
1304                break;
1305        case AGGR_NONE:
1306                if (metric_only)
1307                        print_no_aggr_metric(config, evlist, prefix);
1308                else {
1309                        evlist__for_each_entry(evlist, counter) {
1310                                if (counter->percore)
1311                                        print_percore(config, counter, prefix);
1312                                else
1313                                        print_counter(config, counter, prefix);
1314                        }
1315                }
1316                break;
1317        case AGGR_UNSET:
1318        default:
1319                break;
1320        }
1321
1322        if (!interval && !config->csv_output)
1323                print_footer(config);
1324
1325        fflush(config->output);
1326}
1327