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