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