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