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