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