linux/tools/perf/builtin-diff.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * builtin-diff.c
   4 *
   5 * Builtin diff command: Analyze two perf.data input files, look up and read
   6 * DSOs and symbol information, sort them and produce a diff.
   7 */
   8#include "builtin.h"
   9
  10#include "util/debug.h"
  11#include "util/event.h"
  12#include "util/hist.h"
  13#include "util/evsel.h"
  14#include "util/evlist.h"
  15#include "util/session.h"
  16#include "util/tool.h"
  17#include "util/sort.h"
  18#include "util/symbol.h"
  19#include "util/util.h"
  20#include "util/data.h"
  21#include "util/config.h"
  22
  23#include <errno.h>
  24#include <inttypes.h>
  25#include <stdlib.h>
  26#include <math.h>
  27
  28/* Diff command specific HPP columns. */
  29enum {
  30        PERF_HPP_DIFF__BASELINE,
  31        PERF_HPP_DIFF__PERIOD,
  32        PERF_HPP_DIFF__PERIOD_BASELINE,
  33        PERF_HPP_DIFF__DELTA,
  34        PERF_HPP_DIFF__RATIO,
  35        PERF_HPP_DIFF__WEIGHTED_DIFF,
  36        PERF_HPP_DIFF__FORMULA,
  37        PERF_HPP_DIFF__DELTA_ABS,
  38
  39        PERF_HPP_DIFF__MAX_INDEX
  40};
  41
  42struct diff_hpp_fmt {
  43        struct perf_hpp_fmt      fmt;
  44        int                      idx;
  45        char                    *header;
  46        int                      header_width;
  47};
  48
  49struct data__file {
  50        struct perf_session     *session;
  51        struct perf_data_file   file;
  52        int                      idx;
  53        struct hists            *hists;
  54        struct diff_hpp_fmt      fmt[PERF_HPP_DIFF__MAX_INDEX];
  55};
  56
  57static struct data__file *data__files;
  58static int data__files_cnt;
  59
  60#define data__for_each_file_start(i, d, s)      \
  61        for (i = s, d = &data__files[s];        \
  62             i < data__files_cnt;               \
  63             i++, d = &data__files[i])
  64
  65#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
  66#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
  67
  68static bool force;
  69static bool show_period;
  70static bool show_formula;
  71static bool show_baseline_only;
  72static unsigned int sort_compute = 1;
  73
  74static s64 compute_wdiff_w1;
  75static s64 compute_wdiff_w2;
  76
  77enum {
  78        COMPUTE_DELTA,
  79        COMPUTE_RATIO,
  80        COMPUTE_WEIGHTED_DIFF,
  81        COMPUTE_DELTA_ABS,
  82        COMPUTE_MAX,
  83};
  84
  85const char *compute_names[COMPUTE_MAX] = {
  86        [COMPUTE_DELTA] = "delta",
  87        [COMPUTE_DELTA_ABS] = "delta-abs",
  88        [COMPUTE_RATIO] = "ratio",
  89        [COMPUTE_WEIGHTED_DIFF] = "wdiff",
  90};
  91
  92static int compute = COMPUTE_DELTA_ABS;
  93
  94static int compute_2_hpp[COMPUTE_MAX] = {
  95        [COMPUTE_DELTA]         = PERF_HPP_DIFF__DELTA,
  96        [COMPUTE_DELTA_ABS]     = PERF_HPP_DIFF__DELTA_ABS,
  97        [COMPUTE_RATIO]         = PERF_HPP_DIFF__RATIO,
  98        [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
  99};
 100
 101#define MAX_COL_WIDTH 70
 102
 103static struct header_column {
 104        const char *name;
 105        int width;
 106} columns[PERF_HPP_DIFF__MAX_INDEX] = {
 107        [PERF_HPP_DIFF__BASELINE] = {
 108                .name  = "Baseline",
 109        },
 110        [PERF_HPP_DIFF__PERIOD] = {
 111                .name  = "Period",
 112                .width = 14,
 113        },
 114        [PERF_HPP_DIFF__PERIOD_BASELINE] = {
 115                .name  = "Base period",
 116                .width = 14,
 117        },
 118        [PERF_HPP_DIFF__DELTA] = {
 119                .name  = "Delta",
 120                .width = 7,
 121        },
 122        [PERF_HPP_DIFF__DELTA_ABS] = {
 123                .name  = "Delta Abs",
 124                .width = 7,
 125        },
 126        [PERF_HPP_DIFF__RATIO] = {
 127                .name  = "Ratio",
 128                .width = 14,
 129        },
 130        [PERF_HPP_DIFF__WEIGHTED_DIFF] = {
 131                .name  = "Weighted diff",
 132                .width = 14,
 133        },
 134        [PERF_HPP_DIFF__FORMULA] = {
 135                .name  = "Formula",
 136                .width = MAX_COL_WIDTH,
 137        }
 138};
 139
 140static int setup_compute_opt_wdiff(char *opt)
 141{
 142        char *w1_str = opt;
 143        char *w2_str;
 144
 145        int ret = -EINVAL;
 146
 147        if (!opt)
 148                goto out;
 149
 150        w2_str = strchr(opt, ',');
 151        if (!w2_str)
 152                goto out;
 153
 154        *w2_str++ = 0x0;
 155        if (!*w2_str)
 156                goto out;
 157
 158        compute_wdiff_w1 = strtol(w1_str, NULL, 10);
 159        compute_wdiff_w2 = strtol(w2_str, NULL, 10);
 160
 161        if (!compute_wdiff_w1 || !compute_wdiff_w2)
 162                goto out;
 163
 164        pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
 165                  compute_wdiff_w1, compute_wdiff_w2);
 166
 167        ret = 0;
 168
 169 out:
 170        if (ret)
 171                pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
 172
 173        return ret;
 174}
 175
 176static int setup_compute_opt(char *opt)
 177{
 178        if (compute == COMPUTE_WEIGHTED_DIFF)
 179                return setup_compute_opt_wdiff(opt);
 180
 181        if (opt) {
 182                pr_err("Failed: extra option specified '%s'", opt);
 183                return -EINVAL;
 184        }
 185
 186        return 0;
 187}
 188
 189static int setup_compute(const struct option *opt, const char *str,
 190                         int unset __maybe_unused)
 191{
 192        int *cp = (int *) opt->value;
 193        char *cstr = (char *) str;
 194        char buf[50];
 195        unsigned i;
 196        char *option;
 197
 198        if (!str) {
 199                *cp = COMPUTE_DELTA;
 200                return 0;
 201        }
 202
 203        option = strchr(str, ':');
 204        if (option) {
 205                unsigned len = option++ - str;
 206
 207                /*
 208                 * The str data are not writeable, so we need
 209                 * to use another buffer.
 210                 */
 211
 212                /* No option value is longer. */
 213                if (len >= sizeof(buf))
 214                        return -EINVAL;
 215
 216                strncpy(buf, str, len);
 217                buf[len] = 0x0;
 218                cstr = buf;
 219        }
 220
 221        for (i = 0; i < COMPUTE_MAX; i++)
 222                if (!strcmp(cstr, compute_names[i])) {
 223                        *cp = i;
 224                        return setup_compute_opt(option);
 225                }
 226
 227        pr_err("Failed: '%s' is not computation method "
 228               "(use 'delta','ratio' or 'wdiff')\n", str);
 229        return -EINVAL;
 230}
 231
 232static double period_percent(struct hist_entry *he, u64 period)
 233{
 234        u64 total = hists__total_period(he->hists);
 235
 236        return (period * 100.0) / total;
 237}
 238
 239static double compute_delta(struct hist_entry *he, struct hist_entry *pair)
 240{
 241        double old_percent = period_percent(he, he->stat.period);
 242        double new_percent = period_percent(pair, pair->stat.period);
 243
 244        pair->diff.period_ratio_delta = new_percent - old_percent;
 245        pair->diff.computed = true;
 246        return pair->diff.period_ratio_delta;
 247}
 248
 249static double compute_ratio(struct hist_entry *he, struct hist_entry *pair)
 250{
 251        double old_period = he->stat.period ?: 1;
 252        double new_period = pair->stat.period;
 253
 254        pair->diff.computed = true;
 255        pair->diff.period_ratio = new_period / old_period;
 256        return pair->diff.period_ratio;
 257}
 258
 259static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
 260{
 261        u64 old_period = he->stat.period;
 262        u64 new_period = pair->stat.period;
 263
 264        pair->diff.computed = true;
 265        pair->diff.wdiff = new_period * compute_wdiff_w2 -
 266                           old_period * compute_wdiff_w1;
 267
 268        return pair->diff.wdiff;
 269}
 270
 271static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
 272                         char *buf, size_t size)
 273{
 274        u64 he_total = he->hists->stats.total_period;
 275        u64 pair_total = pair->hists->stats.total_period;
 276
 277        if (symbol_conf.filter_relative) {
 278                he_total = he->hists->stats.total_non_filtered_period;
 279                pair_total = pair->hists->stats.total_non_filtered_period;
 280        }
 281        return scnprintf(buf, size,
 282                         "(%" PRIu64 " * 100 / %" PRIu64 ") - "
 283                         "(%" PRIu64 " * 100 / %" PRIu64 ")",
 284                         pair->stat.period, pair_total,
 285                         he->stat.period, he_total);
 286}
 287
 288static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
 289                         char *buf, size_t size)
 290{
 291        double old_period = he->stat.period;
 292        double new_period = pair->stat.period;
 293
 294        return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
 295}
 296
 297static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
 298                         char *buf, size_t size)
 299{
 300        u64 old_period = he->stat.period;
 301        u64 new_period = pair->stat.period;
 302
 303        return scnprintf(buf, size,
 304                  "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
 305                  new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
 306}
 307
 308static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
 309                           char *buf, size_t size)
 310{
 311        switch (compute) {
 312        case COMPUTE_DELTA:
 313        case COMPUTE_DELTA_ABS:
 314                return formula_delta(he, pair, buf, size);
 315        case COMPUTE_RATIO:
 316                return formula_ratio(he, pair, buf, size);
 317        case COMPUTE_WEIGHTED_DIFF:
 318                return formula_wdiff(he, pair, buf, size);
 319        default:
 320                BUG_ON(1);
 321        }
 322
 323        return -1;
 324}
 325
 326static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
 327                                      union perf_event *event,
 328                                      struct perf_sample *sample,
 329                                      struct perf_evsel *evsel,
 330                                      struct machine *machine)
 331{
 332        struct addr_location al;
 333        struct hists *hists = evsel__hists(evsel);
 334        int ret = -1;
 335
 336        if (machine__resolve(machine, &al, sample) < 0) {
 337                pr_warning("problem processing %d event, skipping it.\n",
 338                           event->header.type);
 339                return -1;
 340        }
 341
 342        if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
 343                pr_warning("problem incrementing symbol period, skipping event\n");
 344                goto out_put;
 345        }
 346
 347        /*
 348         * The total_period is updated here before going to the output
 349         * tree since normally only the baseline hists will call
 350         * hists__output_resort() and precompute needs the total
 351         * period in order to sort entries by percentage delta.
 352         */
 353        hists->stats.total_period += sample->period;
 354        if (!al.filtered)
 355                hists->stats.total_non_filtered_period += sample->period;
 356        ret = 0;
 357out_put:
 358        addr_location__put(&al);
 359        return ret;
 360}
 361
 362static struct perf_tool tool = {
 363        .sample = diff__process_sample_event,
 364        .mmap   = perf_event__process_mmap,
 365        .mmap2  = perf_event__process_mmap2,
 366        .comm   = perf_event__process_comm,
 367        .exit   = perf_event__process_exit,
 368        .fork   = perf_event__process_fork,
 369        .lost   = perf_event__process_lost,
 370        .namespaces = perf_event__process_namespaces,
 371        .ordered_events = true,
 372        .ordering_requires_timestamps = true,
 373};
 374
 375static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
 376                                      struct perf_evlist *evlist)
 377{
 378        struct perf_evsel *e;
 379
 380        evlist__for_each_entry(evlist, e) {
 381                if (perf_evsel__match2(evsel, e))
 382                        return e;
 383        }
 384
 385        return NULL;
 386}
 387
 388static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
 389{
 390        struct perf_evsel *evsel;
 391
 392        evlist__for_each_entry(evlist, evsel) {
 393                struct hists *hists = evsel__hists(evsel);
 394
 395                hists__collapse_resort(hists, NULL);
 396        }
 397}
 398
 399static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt)
 400{
 401        struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt);
 402        void *ptr = dfmt - dfmt->idx;
 403        struct data__file *d = container_of(ptr, struct data__file, fmt);
 404
 405        return d;
 406}
 407
 408static struct hist_entry*
 409get_pair_data(struct hist_entry *he, struct data__file *d)
 410{
 411        if (hist_entry__has_pairs(he)) {
 412                struct hist_entry *pair;
 413
 414                list_for_each_entry(pair, &he->pairs.head, pairs.node)
 415                        if (pair->hists == d->hists)
 416                                return pair;
 417        }
 418
 419        return NULL;
 420}
 421
 422static struct hist_entry*
 423get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
 424{
 425        struct data__file *d = fmt_to_data_file(&dfmt->fmt);
 426
 427        return get_pair_data(he, d);
 428}
 429
 430static void hists__baseline_only(struct hists *hists)
 431{
 432        struct rb_root *root;
 433        struct rb_node *next;
 434
 435        if (hists__has(hists, need_collapse))
 436                root = &hists->entries_collapsed;
 437        else
 438                root = hists->entries_in;
 439
 440        next = rb_first(root);
 441        while (next != NULL) {
 442                struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
 443
 444                next = rb_next(&he->rb_node_in);
 445                if (!hist_entry__next_pair(he)) {
 446                        rb_erase(&he->rb_node_in, root);
 447                        hist_entry__delete(he);
 448                }
 449        }
 450}
 451
 452static void hists__precompute(struct hists *hists)
 453{
 454        struct rb_root *root;
 455        struct rb_node *next;
 456
 457        if (hists__has(hists, need_collapse))
 458                root = &hists->entries_collapsed;
 459        else
 460                root = hists->entries_in;
 461
 462        next = rb_first(root);
 463        while (next != NULL) {
 464                struct hist_entry *he, *pair;
 465                struct data__file *d;
 466                int i;
 467
 468                he   = rb_entry(next, struct hist_entry, rb_node_in);
 469                next = rb_next(&he->rb_node_in);
 470
 471                data__for_each_file_new(i, d) {
 472                        pair = get_pair_data(he, d);
 473                        if (!pair)
 474                                continue;
 475
 476                        switch (compute) {
 477                        case COMPUTE_DELTA:
 478                        case COMPUTE_DELTA_ABS:
 479                                compute_delta(he, pair);
 480                                break;
 481                        case COMPUTE_RATIO:
 482                                compute_ratio(he, pair);
 483                                break;
 484                        case COMPUTE_WEIGHTED_DIFF:
 485                                compute_wdiff(he, pair);
 486                                break;
 487                        default:
 488                                BUG_ON(1);
 489                        }
 490                }
 491        }
 492}
 493
 494static int64_t cmp_doubles(double l, double r)
 495{
 496        if (l > r)
 497                return -1;
 498        else if (l < r)
 499                return 1;
 500        else
 501                return 0;
 502}
 503
 504static int64_t
 505__hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
 506                        int c)
 507{
 508        switch (c) {
 509        case COMPUTE_DELTA:
 510        {
 511                double l = left->diff.period_ratio_delta;
 512                double r = right->diff.period_ratio_delta;
 513
 514                return cmp_doubles(l, r);
 515        }
 516        case COMPUTE_DELTA_ABS:
 517        {
 518                double l = fabs(left->diff.period_ratio_delta);
 519                double r = fabs(right->diff.period_ratio_delta);
 520
 521                return cmp_doubles(l, r);
 522        }
 523        case COMPUTE_RATIO:
 524        {
 525                double l = left->diff.period_ratio;
 526                double r = right->diff.period_ratio;
 527
 528                return cmp_doubles(l, r);
 529        }
 530        case COMPUTE_WEIGHTED_DIFF:
 531        {
 532                s64 l = left->diff.wdiff;
 533                s64 r = right->diff.wdiff;
 534
 535                return r - l;
 536        }
 537        default:
 538                BUG_ON(1);
 539        }
 540
 541        return 0;
 542}
 543
 544static int64_t
 545hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
 546                        int c, int sort_idx)
 547{
 548        bool pairs_left  = hist_entry__has_pairs(left);
 549        bool pairs_right = hist_entry__has_pairs(right);
 550        struct hist_entry *p_right, *p_left;
 551
 552        if (!pairs_left && !pairs_right)
 553                return 0;
 554
 555        if (!pairs_left || !pairs_right)
 556                return pairs_left ? -1 : 1;
 557
 558        p_left  = get_pair_data(left,  &data__files[sort_idx]);
 559        p_right = get_pair_data(right, &data__files[sort_idx]);
 560
 561        if (!p_left && !p_right)
 562                return 0;
 563
 564        if (!p_left || !p_right)
 565                return p_left ? -1 : 1;
 566
 567        /*
 568         * We have 2 entries of same kind, let's
 569         * make the data comparison.
 570         */
 571        return __hist_entry__cmp_compute(p_left, p_right, c);
 572}
 573
 574static int64_t
 575hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right,
 576                            int c, int sort_idx)
 577{
 578        struct hist_entry *p_right, *p_left;
 579
 580        p_left  = get_pair_data(left,  &data__files[sort_idx]);
 581        p_right = get_pair_data(right, &data__files[sort_idx]);
 582
 583        if (!p_left && !p_right)
 584                return 0;
 585
 586        if (!p_left || !p_right)
 587                return p_left ? -1 : 1;
 588
 589        if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) {
 590                /*
 591                 * The delta can be computed without the baseline, but
 592                 * others are not.  Put those entries which have no
 593                 * values below.
 594                 */
 595                if (left->dummy && right->dummy)
 596                        return 0;
 597
 598                if (left->dummy || right->dummy)
 599                        return left->dummy ? 1 : -1;
 600        }
 601
 602        return __hist_entry__cmp_compute(p_left, p_right, c);
 603}
 604
 605static int64_t
 606hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused,
 607                    struct hist_entry *left __maybe_unused,
 608                    struct hist_entry *right __maybe_unused)
 609{
 610        return 0;
 611}
 612
 613static int64_t
 614hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused,
 615                         struct hist_entry *left, struct hist_entry *right)
 616{
 617        if (left->stat.period == right->stat.period)
 618                return 0;
 619        return left->stat.period > right->stat.period ? 1 : -1;
 620}
 621
 622static int64_t
 623hist_entry__cmp_delta(struct perf_hpp_fmt *fmt,
 624                      struct hist_entry *left, struct hist_entry *right)
 625{
 626        struct data__file *d = fmt_to_data_file(fmt);
 627
 628        return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx);
 629}
 630
 631static int64_t
 632hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt,
 633                      struct hist_entry *left, struct hist_entry *right)
 634{
 635        struct data__file *d = fmt_to_data_file(fmt);
 636
 637        return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx);
 638}
 639
 640static int64_t
 641hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt,
 642                      struct hist_entry *left, struct hist_entry *right)
 643{
 644        struct data__file *d = fmt_to_data_file(fmt);
 645
 646        return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx);
 647}
 648
 649static int64_t
 650hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt,
 651                      struct hist_entry *left, struct hist_entry *right)
 652{
 653        struct data__file *d = fmt_to_data_file(fmt);
 654
 655        return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx);
 656}
 657
 658static int64_t
 659hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused,
 660                          struct hist_entry *left, struct hist_entry *right)
 661{
 662        return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA,
 663                                           sort_compute);
 664}
 665
 666static int64_t
 667hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused,
 668                              struct hist_entry *left, struct hist_entry *right)
 669{
 670        return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS,
 671                                           sort_compute);
 672}
 673
 674static int64_t
 675hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused,
 676                          struct hist_entry *left, struct hist_entry *right)
 677{
 678        return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO,
 679                                           sort_compute);
 680}
 681
 682static int64_t
 683hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused,
 684                          struct hist_entry *left, struct hist_entry *right)
 685{
 686        return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF,
 687                                           sort_compute);
 688}
 689
 690static void hists__process(struct hists *hists)
 691{
 692        if (show_baseline_only)
 693                hists__baseline_only(hists);
 694
 695        hists__precompute(hists);
 696        hists__output_resort(hists, NULL);
 697
 698        hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
 699                       symbol_conf.use_callchain);
 700}
 701
 702static void data__fprintf(void)
 703{
 704        struct data__file *d;
 705        int i;
 706
 707        fprintf(stdout, "# Data files:\n");
 708
 709        data__for_each_file(i, d)
 710                fprintf(stdout, "#  [%d] %s %s\n",
 711                        d->idx, d->file.path,
 712                        !d->idx ? "(Baseline)" : "");
 713
 714        fprintf(stdout, "#\n");
 715}
 716
 717static void data_process(void)
 718{
 719        struct perf_evlist *evlist_base = data__files[0].session->evlist;
 720        struct perf_evsel *evsel_base;
 721        bool first = true;
 722
 723        evlist__for_each_entry(evlist_base, evsel_base) {
 724                struct hists *hists_base = evsel__hists(evsel_base);
 725                struct data__file *d;
 726                int i;
 727
 728                data__for_each_file_new(i, d) {
 729                        struct perf_evlist *evlist = d->session->evlist;
 730                        struct perf_evsel *evsel;
 731                        struct hists *hists;
 732
 733                        evsel = evsel_match(evsel_base, evlist);
 734                        if (!evsel)
 735                                continue;
 736
 737                        hists = evsel__hists(evsel);
 738                        d->hists = hists;
 739
 740                        hists__match(hists_base, hists);
 741
 742                        if (!show_baseline_only)
 743                                hists__link(hists_base, hists);
 744                }
 745
 746                if (!quiet) {
 747                        fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
 748                                perf_evsel__name(evsel_base));
 749                }
 750
 751                first = false;
 752
 753                if (verbose > 0 || ((data__files_cnt > 2) && !quiet))
 754                        data__fprintf();
 755
 756                /* Don't sort callchain for perf diff */
 757                perf_evsel__reset_sample_bit(evsel_base, CALLCHAIN);
 758
 759                hists__process(hists_base);
 760        }
 761}
 762
 763static void data__free(struct data__file *d)
 764{
 765        int col;
 766
 767        for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
 768                struct diff_hpp_fmt *fmt = &d->fmt[col];
 769
 770                zfree(&fmt->header);
 771        }
 772}
 773
 774static int __cmd_diff(void)
 775{
 776        struct data__file *d;
 777        int ret = -EINVAL, i;
 778
 779        data__for_each_file(i, d) {
 780                d->session = perf_session__new(&d->file, false, &tool);
 781                if (!d->session) {
 782                        pr_err("Failed to open %s\n", d->file.path);
 783                        ret = -1;
 784                        goto out_delete;
 785                }
 786
 787                ret = perf_session__process_events(d->session);
 788                if (ret) {
 789                        pr_err("Failed to process %s\n", d->file.path);
 790                        goto out_delete;
 791                }
 792
 793                perf_evlist__collapse_resort(d->session->evlist);
 794        }
 795
 796        data_process();
 797
 798 out_delete:
 799        data__for_each_file(i, d) {
 800                perf_session__delete(d->session);
 801                data__free(d);
 802        }
 803
 804        free(data__files);
 805        return ret;
 806}
 807
 808static const char * const diff_usage[] = {
 809        "perf diff [<options>] [old_file] [new_file]",
 810        NULL,
 811};
 812
 813static const struct option options[] = {
 814        OPT_INCR('v', "verbose", &verbose,
 815                    "be more verbose (show symbol address, etc)"),
 816        OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"),
 817        OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
 818                    "Show only items with match in baseline"),
 819        OPT_CALLBACK('c', "compute", &compute,
 820                     "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)",
 821                     "Entries differential computation selection",
 822                     setup_compute),
 823        OPT_BOOLEAN('p', "period", &show_period,
 824                    "Show period values."),
 825        OPT_BOOLEAN('F', "formula", &show_formula,
 826                    "Show formula."),
 827        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
 828                    "dump raw trace in ASCII"),
 829        OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
 830        OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
 831                   "file", "kallsyms pathname"),
 832        OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
 833                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
 834        OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
 835                   "only consider symbols in these dsos"),
 836        OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
 837                   "only consider symbols in these comms"),
 838        OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
 839                   "only consider these symbols"),
 840        OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
 841                   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
 842                   " Please refer the man page for the complete list."),
 843        OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
 844                   "separator for columns, no spaces will be added between "
 845                   "columns '.' is reserved."),
 846        OPT_CALLBACK(0, "symfs", NULL, "directory",
 847                     "Look for files with symbols relative to this directory",
 848                     symbol__config_symfs),
 849        OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
 850        OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
 851                     "How to display percentage of filtered entries", parse_filter_percentage),
 852        OPT_END()
 853};
 854
 855static double baseline_percent(struct hist_entry *he)
 856{
 857        u64 total = hists__total_period(he->hists);
 858
 859        return 100.0 * he->stat.period / total;
 860}
 861
 862static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
 863                               struct perf_hpp *hpp, struct hist_entry *he)
 864{
 865        struct diff_hpp_fmt *dfmt =
 866                container_of(fmt, struct diff_hpp_fmt, fmt);
 867        double percent = baseline_percent(he);
 868        char pfmt[20] = " ";
 869
 870        if (!he->dummy) {
 871                scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1);
 872                return percent_color_snprintf(hpp->buf, hpp->size,
 873                                              pfmt, percent);
 874        } else
 875                return scnprintf(hpp->buf, hpp->size, "%*s",
 876                                 dfmt->header_width, pfmt);
 877}
 878
 879static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
 880{
 881        double percent = baseline_percent(he);
 882        const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
 883        int ret = 0;
 884
 885        if (!he->dummy)
 886                ret = scnprintf(buf, size, fmt, percent);
 887
 888        return ret;
 889}
 890
 891static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
 892                                struct perf_hpp *hpp, struct hist_entry *he,
 893                                int comparison_method)
 894{
 895        struct diff_hpp_fmt *dfmt =
 896                container_of(fmt, struct diff_hpp_fmt, fmt);
 897        struct hist_entry *pair = get_pair_fmt(he, dfmt);
 898        double diff;
 899        s64 wdiff;
 900        char pfmt[20] = " ";
 901
 902        if (!pair)
 903                goto no_print;
 904
 905        switch (comparison_method) {
 906        case COMPUTE_DELTA:
 907                if (pair->diff.computed)
 908                        diff = pair->diff.period_ratio_delta;
 909                else
 910                        diff = compute_delta(he, pair);
 911
 912                scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1);
 913                return percent_color_snprintf(hpp->buf, hpp->size,
 914                                        pfmt, diff);
 915        case COMPUTE_RATIO:
 916                if (he->dummy)
 917                        goto dummy_print;
 918                if (pair->diff.computed)
 919                        diff = pair->diff.period_ratio;
 920                else
 921                        diff = compute_ratio(he, pair);
 922
 923                scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width);
 924                return value_color_snprintf(hpp->buf, hpp->size,
 925                                        pfmt, diff);
 926        case COMPUTE_WEIGHTED_DIFF:
 927                if (he->dummy)
 928                        goto dummy_print;
 929                if (pair->diff.computed)
 930                        wdiff = pair->diff.wdiff;
 931                else
 932                        wdiff = compute_wdiff(he, pair);
 933
 934                scnprintf(pfmt, 20, "%%14ld", dfmt->header_width);
 935                return color_snprintf(hpp->buf, hpp->size,
 936                                get_percent_color(wdiff),
 937                                pfmt, wdiff);
 938        default:
 939                BUG_ON(1);
 940        }
 941dummy_print:
 942        return scnprintf(hpp->buf, hpp->size, "%*s",
 943                        dfmt->header_width, "N/A");
 944no_print:
 945        return scnprintf(hpp->buf, hpp->size, "%*s",
 946                        dfmt->header_width, pfmt);
 947}
 948
 949static int hpp__color_delta(struct perf_hpp_fmt *fmt,
 950                        struct perf_hpp *hpp, struct hist_entry *he)
 951{
 952        return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA);
 953}
 954
 955static int hpp__color_ratio(struct perf_hpp_fmt *fmt,
 956                        struct perf_hpp *hpp, struct hist_entry *he)
 957{
 958        return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO);
 959}
 960
 961static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
 962                        struct perf_hpp *hpp, struct hist_entry *he)
 963{
 964        return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
 965}
 966
 967static void
 968hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
 969{
 970        switch (idx) {
 971        case PERF_HPP_DIFF__PERIOD_BASELINE:
 972                scnprintf(buf, size, "%" PRIu64, he->stat.period);
 973                break;
 974
 975        default:
 976                break;
 977        }
 978}
 979
 980static void
 981hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
 982                int idx, char *buf, size_t size)
 983{
 984        double diff;
 985        double ratio;
 986        s64 wdiff;
 987
 988        switch (idx) {
 989        case PERF_HPP_DIFF__DELTA:
 990        case PERF_HPP_DIFF__DELTA_ABS:
 991                if (pair->diff.computed)
 992                        diff = pair->diff.period_ratio_delta;
 993                else
 994                        diff = compute_delta(he, pair);
 995
 996                scnprintf(buf, size, "%+4.2F%%", diff);
 997                break;
 998
 999        case PERF_HPP_DIFF__RATIO:
1000                /* No point for ratio number if we are dummy.. */
1001                if (he->dummy) {
1002                        scnprintf(buf, size, "N/A");
1003                        break;
1004                }
1005
1006                if (pair->diff.computed)
1007                        ratio = pair->diff.period_ratio;
1008                else
1009                        ratio = compute_ratio(he, pair);
1010
1011                if (ratio > 0.0)
1012                        scnprintf(buf, size, "%14.6F", ratio);
1013                break;
1014
1015        case PERF_HPP_DIFF__WEIGHTED_DIFF:
1016                /* No point for wdiff number if we are dummy.. */
1017                if (he->dummy) {
1018                        scnprintf(buf, size, "N/A");
1019                        break;
1020                }
1021
1022                if (pair->diff.computed)
1023                        wdiff = pair->diff.wdiff;
1024                else
1025                        wdiff = compute_wdiff(he, pair);
1026
1027                if (wdiff != 0)
1028                        scnprintf(buf, size, "%14ld", wdiff);
1029                break;
1030
1031        case PERF_HPP_DIFF__FORMULA:
1032                formula_fprintf(he, pair, buf, size);
1033                break;
1034
1035        case PERF_HPP_DIFF__PERIOD:
1036                scnprintf(buf, size, "%" PRIu64, pair->stat.period);
1037                break;
1038
1039        default:
1040                BUG_ON(1);
1041        };
1042}
1043
1044static void
1045__hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt,
1046                    char *buf, size_t size)
1047{
1048        struct hist_entry *pair = get_pair_fmt(he, dfmt);
1049        int idx = dfmt->idx;
1050
1051        /* baseline is special */
1052        if (idx == PERF_HPP_DIFF__BASELINE)
1053                hpp__entry_baseline(he, buf, size);
1054        else {
1055                if (pair)
1056                        hpp__entry_pair(he, pair, idx, buf, size);
1057                else
1058                        hpp__entry_unpair(he, idx, buf, size);
1059        }
1060}
1061
1062static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
1063                             struct hist_entry *he)
1064{
1065        struct diff_hpp_fmt *dfmt =
1066                container_of(_fmt, struct diff_hpp_fmt, fmt);
1067        char buf[MAX_COL_WIDTH] = " ";
1068
1069        __hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH);
1070
1071        if (symbol_conf.field_sep)
1072                return scnprintf(hpp->buf, hpp->size, "%s", buf);
1073        else
1074                return scnprintf(hpp->buf, hpp->size, "%*s",
1075                                 dfmt->header_width, buf);
1076}
1077
1078static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1079                       struct hists *hists __maybe_unused,
1080                       int line __maybe_unused,
1081                       int *span __maybe_unused)
1082{
1083        struct diff_hpp_fmt *dfmt =
1084                container_of(fmt, struct diff_hpp_fmt, fmt);
1085
1086        BUG_ON(!dfmt->header);
1087        return scnprintf(hpp->buf, hpp->size, dfmt->header);
1088}
1089
1090static int hpp__width(struct perf_hpp_fmt *fmt,
1091                      struct perf_hpp *hpp __maybe_unused,
1092                      struct hists *hists __maybe_unused)
1093{
1094        struct diff_hpp_fmt *dfmt =
1095                container_of(fmt, struct diff_hpp_fmt, fmt);
1096
1097        BUG_ON(dfmt->header_width <= 0);
1098        return dfmt->header_width;
1099}
1100
1101static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt)
1102{
1103#define MAX_HEADER_NAME 100
1104        char buf_indent[MAX_HEADER_NAME];
1105        char buf[MAX_HEADER_NAME];
1106        const char *header = NULL;
1107        int width = 0;
1108
1109        BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX);
1110        header = columns[dfmt->idx].name;
1111        width  = columns[dfmt->idx].width;
1112
1113        /* Only our defined HPP fmts should appear here. */
1114        BUG_ON(!header);
1115
1116        if (data__files_cnt > 2)
1117                scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx);
1118
1119#define NAME (data__files_cnt > 2 ? buf : header)
1120        dfmt->header_width = width;
1121        width = (int) strlen(NAME);
1122        if (dfmt->header_width < width)
1123                dfmt->header_width = width;
1124
1125        scnprintf(buf_indent, MAX_HEADER_NAME, "%*s",
1126                  dfmt->header_width, NAME);
1127
1128        dfmt->header = strdup(buf_indent);
1129#undef MAX_HEADER_NAME
1130#undef NAME
1131}
1132
1133static void data__hpp_register(struct data__file *d, int idx)
1134{
1135        struct diff_hpp_fmt *dfmt = &d->fmt[idx];
1136        struct perf_hpp_fmt *fmt = &dfmt->fmt;
1137
1138        dfmt->idx = idx;
1139
1140        fmt->header = hpp__header;
1141        fmt->width  = hpp__width;
1142        fmt->entry  = hpp__entry_global;
1143        fmt->cmp    = hist_entry__cmp_nop;
1144        fmt->collapse = hist_entry__cmp_nop;
1145
1146        /* TODO more colors */
1147        switch (idx) {
1148        case PERF_HPP_DIFF__BASELINE:
1149                fmt->color = hpp__color_baseline;
1150                fmt->sort  = hist_entry__cmp_baseline;
1151                break;
1152        case PERF_HPP_DIFF__DELTA:
1153                fmt->color = hpp__color_delta;
1154                fmt->sort  = hist_entry__cmp_delta;
1155                break;
1156        case PERF_HPP_DIFF__RATIO:
1157                fmt->color = hpp__color_ratio;
1158                fmt->sort  = hist_entry__cmp_ratio;
1159                break;
1160        case PERF_HPP_DIFF__WEIGHTED_DIFF:
1161                fmt->color = hpp__color_wdiff;
1162                fmt->sort  = hist_entry__cmp_wdiff;
1163                break;
1164        case PERF_HPP_DIFF__DELTA_ABS:
1165                fmt->color = hpp__color_delta;
1166                fmt->sort  = hist_entry__cmp_delta_abs;
1167                break;
1168        default:
1169                fmt->sort  = hist_entry__cmp_nop;
1170                break;
1171        }
1172
1173        init_header(d, dfmt);
1174        perf_hpp__column_register(fmt);
1175        perf_hpp__register_sort_field(fmt);
1176}
1177
1178static int ui_init(void)
1179{
1180        struct data__file *d;
1181        struct perf_hpp_fmt *fmt;
1182        int i;
1183
1184        data__for_each_file(i, d) {
1185
1186                /*
1187                 * Baseline or compute realted columns:
1188                 *
1189                 *   PERF_HPP_DIFF__BASELINE
1190                 *   PERF_HPP_DIFF__DELTA
1191                 *   PERF_HPP_DIFF__RATIO
1192                 *   PERF_HPP_DIFF__WEIGHTED_DIFF
1193                 */
1194                data__hpp_register(d, i ? compute_2_hpp[compute] :
1195                                          PERF_HPP_DIFF__BASELINE);
1196
1197                /*
1198                 * And the rest:
1199                 *
1200                 * PERF_HPP_DIFF__FORMULA
1201                 * PERF_HPP_DIFF__PERIOD
1202                 * PERF_HPP_DIFF__PERIOD_BASELINE
1203                 */
1204                if (show_formula && i)
1205                        data__hpp_register(d, PERF_HPP_DIFF__FORMULA);
1206
1207                if (show_period)
1208                        data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
1209                                                  PERF_HPP_DIFF__PERIOD_BASELINE);
1210        }
1211
1212        if (!sort_compute)
1213                return 0;
1214
1215        /*
1216         * Prepend an fmt to sort on columns at 'sort_compute' first.
1217         * This fmt is added only to the sort list but not to the
1218         * output fields list.
1219         *
1220         * Note that this column (data) can be compared twice - one
1221         * for this 'sort_compute' fmt and another for the normal
1222         * diff_hpp_fmt.  But it shouldn't a problem as most entries
1223         * will be sorted out by first try or baseline and comparing
1224         * is not a costly operation.
1225         */
1226        fmt = zalloc(sizeof(*fmt));
1227        if (fmt == NULL) {
1228                pr_err("Memory allocation failed\n");
1229                return -1;
1230        }
1231
1232        fmt->cmp      = hist_entry__cmp_nop;
1233        fmt->collapse = hist_entry__cmp_nop;
1234
1235        switch (compute) {
1236        case COMPUTE_DELTA:
1237                fmt->sort = hist_entry__cmp_delta_idx;
1238                break;
1239        case COMPUTE_RATIO:
1240                fmt->sort = hist_entry__cmp_ratio_idx;
1241                break;
1242        case COMPUTE_WEIGHTED_DIFF:
1243                fmt->sort = hist_entry__cmp_wdiff_idx;
1244                break;
1245        case COMPUTE_DELTA_ABS:
1246                fmt->sort = hist_entry__cmp_delta_abs_idx;
1247                break;
1248        default:
1249                BUG_ON(1);
1250        }
1251
1252        perf_hpp__prepend_sort_field(fmt);
1253        return 0;
1254}
1255
1256static int data_init(int argc, const char **argv)
1257{
1258        struct data__file *d;
1259        static const char *defaults[] = {
1260                "perf.data.old",
1261                "perf.data",
1262        };
1263        bool use_default = true;
1264        int i;
1265
1266        data__files_cnt = 2;
1267
1268        if (argc) {
1269                if (argc == 1)
1270                        defaults[1] = argv[0];
1271                else {
1272                        data__files_cnt = argc;
1273                        use_default = false;
1274                }
1275        } else if (perf_guest) {
1276                defaults[0] = "perf.data.host";
1277                defaults[1] = "perf.data.guest";
1278        }
1279
1280        if (sort_compute >= (unsigned int) data__files_cnt) {
1281                pr_err("Order option out of limit.\n");
1282                return -EINVAL;
1283        }
1284
1285        data__files = zalloc(sizeof(*data__files) * data__files_cnt);
1286        if (!data__files)
1287                return -ENOMEM;
1288
1289        data__for_each_file(i, d) {
1290                struct perf_data_file *file = &d->file;
1291
1292                file->path  = use_default ? defaults[i] : argv[i];
1293                file->mode  = PERF_DATA_MODE_READ,
1294                file->force = force,
1295
1296                d->idx  = i;
1297        }
1298
1299        return 0;
1300}
1301
1302static int diff__config(const char *var, const char *value,
1303                        void *cb __maybe_unused)
1304{
1305        if (!strcmp(var, "diff.order")) {
1306                int ret;
1307                if (perf_config_int(&ret, var, value) < 0)
1308                        return -1;
1309                sort_compute = ret;
1310                return 0;
1311        }
1312        if (!strcmp(var, "diff.compute")) {
1313                if (!strcmp(value, "delta")) {
1314                        compute = COMPUTE_DELTA;
1315                } else if (!strcmp(value, "delta-abs")) {
1316                        compute = COMPUTE_DELTA_ABS;
1317                } else if (!strcmp(value, "ratio")) {
1318                        compute = COMPUTE_RATIO;
1319                } else if (!strcmp(value, "wdiff")) {
1320                        compute = COMPUTE_WEIGHTED_DIFF;
1321                } else {
1322                        pr_err("Invalid compute method: %s\n", value);
1323                        return -1;
1324                }
1325        }
1326
1327        return 0;
1328}
1329
1330int cmd_diff(int argc, const char **argv)
1331{
1332        int ret = hists__init();
1333
1334        if (ret < 0)
1335                return ret;
1336
1337        perf_config(diff__config, NULL);
1338
1339        argc = parse_options(argc, argv, options, diff_usage, 0);
1340
1341        if (quiet)
1342                perf_quiet_option();
1343
1344        if (symbol__init(NULL) < 0)
1345                return -1;
1346
1347        if (data_init(argc, argv) < 0)
1348                return -1;
1349
1350        if (ui_init() < 0)
1351                return -1;
1352
1353        sort__mode = SORT_MODE__DIFF;
1354
1355        if (setup_sorting(NULL) < 0)
1356                usage_with_options(diff_usage, options);
1357
1358        setup_pager();
1359
1360        sort__setup_elide(NULL);
1361
1362        return __cmd_diff();
1363}
1364