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