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