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