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 #include "perf.h" 10 11 #include "util/debug.h" 12 #include "util/event.h" 13 #include "util/hist.h" 14 #include "util/evsel.h" 15 #include "util/evlist.h" 16 #include "util/session.h" 17 #include "util/tool.h" 18 #include "util/sort.h" 19 #include "util/srcline.h" 20 #include "util/symbol.h" 21 #include "util/data.h" 22 #include "util/config.h" 23 #include "util/time-utils.h" 24 #include "util/annotate.h" 25 #include "util/map.h" 26 #include "util/spark.h" 27 #include "util/block-info.h" 28 #include "util/stream.h" 29 #include "util/util.h" 30 #include <linux/err.h> 31 #include <linux/zalloc.h> 32 #include <subcmd/pager.h> 33 #include <subcmd/parse-options.h> 34 35 #include <errno.h> 36 #include <inttypes.h> 37 #include <stdlib.h> 38 #include <math.h> 39 40 struct perf_diff { 41 struct perf_tool tool; 42 const char *time_str; 43 struct perf_time_interval *ptime_range; 44 int range_size; 45 int range_num; 46 bool has_br_stack; 47 bool stream; 48 }; 49 50 /* Diff command specific HPP columns. */ 51 enum { 52 PERF_HPP_DIFF__BASELINE, 53 PERF_HPP_DIFF__PERIOD, 54 PERF_HPP_DIFF__PERIOD_BASELINE, 55 PERF_HPP_DIFF__DELTA, 56 PERF_HPP_DIFF__RATIO, 57 PERF_HPP_DIFF__WEIGHTED_DIFF, 58 PERF_HPP_DIFF__FORMULA, 59 PERF_HPP_DIFF__DELTA_ABS, 60 PERF_HPP_DIFF__CYCLES, 61 PERF_HPP_DIFF__CYCLES_HIST, 62 63 PERF_HPP_DIFF__MAX_INDEX 64 }; 65 66 struct diff_hpp_fmt { 67 struct perf_hpp_fmt fmt; 68 int idx; 69 char *header; 70 int header_width; 71 }; 72 73 struct data__file { 74 struct perf_session *session; 75 struct perf_data data; 76 int idx; 77 struct hists *hists; 78 struct evlist_streams *evlist_streams; 79 struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX]; 80 }; 81 82 static struct data__file *data__files; 83 static int data__files_cnt; 84 85 #define data__for_each_file_start(i, d, s) \ 86 for (i = s, d = &data__files[s]; \ 87 i < data__files_cnt; \ 88 i++, d = &data__files[i]) 89 90 #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0) 91 #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1) 92 93 static bool force; 94 static bool show_period; 95 static bool show_formula; 96 static bool show_baseline_only; 97 static bool cycles_hist; 98 static unsigned int sort_compute = 1; 99 100 static s64 compute_wdiff_w1; 101 static s64 compute_wdiff_w2; 102 103 static const char *cpu_list; 104 static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 105 106 enum { 107 COMPUTE_DELTA, 108 COMPUTE_RATIO, 109 COMPUTE_WEIGHTED_DIFF, 110 COMPUTE_DELTA_ABS, 111 COMPUTE_CYCLES, 112 COMPUTE_MAX, 113 COMPUTE_STREAM, /* After COMPUTE_MAX to avoid use current compute arrays */ 114 }; 115 116 const char *compute_names[COMPUTE_MAX] = { 117 [COMPUTE_DELTA] = "delta", 118 [COMPUTE_DELTA_ABS] = "delta-abs", 119 [COMPUTE_RATIO] = "ratio", 120 [COMPUTE_WEIGHTED_DIFF] = "wdiff", 121 [COMPUTE_CYCLES] = "cycles", 122 }; 123 124 static int compute = COMPUTE_DELTA_ABS; 125 126 static int compute_2_hpp[COMPUTE_MAX] = { 127 [COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA, 128 [COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS, 129 [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO, 130 [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF, 131 [COMPUTE_CYCLES] = PERF_HPP_DIFF__CYCLES, 132 }; 133 134 #define MAX_COL_WIDTH 70 135 136 static struct header_column { 137 const char *name; 138 int width; 139 } columns[PERF_HPP_DIFF__MAX_INDEX] = { 140 [PERF_HPP_DIFF__BASELINE] = { 141 .name = "Baseline", 142 }, 143 [PERF_HPP_DIFF__PERIOD] = { 144 .name = "Period", 145 .width = 14, 146 }, 147 [PERF_HPP_DIFF__PERIOD_BASELINE] = { 148 .name = "Base period", 149 .width = 14, 150 }, 151 [PERF_HPP_DIFF__DELTA] = { 152 .name = "Delta", 153 .width = 7, 154 }, 155 [PERF_HPP_DIFF__DELTA_ABS] = { 156 .name = "Delta Abs", 157 .width = 7, 158 }, 159 [PERF_HPP_DIFF__RATIO] = { 160 .name = "Ratio", 161 .width = 14, 162 }, 163 [PERF_HPP_DIFF__WEIGHTED_DIFF] = { 164 .name = "Weighted diff", 165 .width = 14, 166 }, 167 [PERF_HPP_DIFF__FORMULA] = { 168 .name = "Formula", 169 .width = MAX_COL_WIDTH, 170 }, 171 [PERF_HPP_DIFF__CYCLES] = { 172 .name = "[Program Block Range] Cycles Diff", 173 .width = 70, 174 }, 175 [PERF_HPP_DIFF__CYCLES_HIST] = { 176 .name = "stddev/Hist", 177 .width = NUM_SPARKS + 9, 178 } 179 }; 180 181 static int setup_compute_opt_wdiff(char *opt) 182 { 183 char *w1_str = opt; 184 char *w2_str; 185 186 int ret = -EINVAL; 187 188 if (!opt) 189 goto out; 190 191 w2_str = strchr(opt, ','); 192 if (!w2_str) 193 goto out; 194 195 *w2_str++ = 0x0; 196 if (!*w2_str) 197 goto out; 198 199 compute_wdiff_w1 = strtol(w1_str, NULL, 10); 200 compute_wdiff_w2 = strtol(w2_str, NULL, 10); 201 202 if (!compute_wdiff_w1 || !compute_wdiff_w2) 203 goto out; 204 205 pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n", 206 compute_wdiff_w1, compute_wdiff_w2); 207 208 ret = 0; 209 210 out: 211 if (ret) 212 pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n"); 213 214 return ret; 215 } 216 217 static int setup_compute_opt(char *opt) 218 { 219 if (compute == COMPUTE_WEIGHTED_DIFF) 220 return setup_compute_opt_wdiff(opt); 221 222 if (opt) { 223 pr_err("Failed: extra option specified '%s'", opt); 224 return -EINVAL; 225 } 226 227 return 0; 228 } 229 230 static int setup_compute(const struct option *opt, const char *str, 231 int unset __maybe_unused) 232 { 233 int *cp = (int *) opt->value; 234 char *cstr = (char *) str; 235 char buf[50]; 236 unsigned i; 237 char *option; 238 239 if (!str) { 240 *cp = COMPUTE_DELTA; 241 return 0; 242 } 243 244 option = strchr(str, ':'); 245 if (option) { 246 unsigned len = option++ - str; 247 248 /* 249 * The str data are not writeable, so we need 250 * to use another buffer. 251 */ 252 253 /* No option value is longer. */ 254 if (len >= sizeof(buf)) 255 return -EINVAL; 256 257 strncpy(buf, str, len); 258 buf[len] = 0x0; 259 cstr = buf; 260 } 261 262 for (i = 0; i < COMPUTE_MAX; i++) 263 if (!strcmp(cstr, compute_names[i])) { 264 *cp = i; 265 return setup_compute_opt(option); 266 } 267 268 pr_err("Failed: '%s' is not computation method " 269 "(use 'delta','ratio' or 'wdiff')\n", str); 270 return -EINVAL; 271 } 272 273 static double period_percent(struct hist_entry *he, u64 period) 274 { 275 u64 total = hists__total_period(he->hists); 276 277 return (period * 100.0) / total; 278 } 279 280 static double compute_delta(struct hist_entry *he, struct hist_entry *pair) 281 { 282 double old_percent = period_percent(he, he->stat.period); 283 double new_percent = period_percent(pair, pair->stat.period); 284 285 pair->diff.period_ratio_delta = new_percent - old_percent; 286 pair->diff.computed = true; 287 return pair->diff.period_ratio_delta; 288 } 289 290 static double compute_ratio(struct hist_entry *he, struct hist_entry *pair) 291 { 292 double old_period = he->stat.period ?: 1; 293 double new_period = pair->stat.period; 294 295 pair->diff.computed = true; 296 pair->diff.period_ratio = new_period / old_period; 297 return pair->diff.period_ratio; 298 } 299 300 static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair) 301 { 302 u64 old_period = he->stat.period; 303 u64 new_period = pair->stat.period; 304 305 pair->diff.computed = true; 306 pair->diff.wdiff = new_period * compute_wdiff_w2 - 307 old_period * compute_wdiff_w1; 308 309 return pair->diff.wdiff; 310 } 311 312 static int formula_delta(struct hist_entry *he, struct hist_entry *pair, 313 char *buf, size_t size) 314 { 315 u64 he_total = he->hists->stats.total_period; 316 u64 pair_total = pair->hists->stats.total_period; 317 318 if (symbol_conf.filter_relative) { 319 he_total = he->hists->stats.total_non_filtered_period; 320 pair_total = pair->hists->stats.total_non_filtered_period; 321 } 322 return scnprintf(buf, size, 323 "(%" PRIu64 " * 100 / %" PRIu64 ") - " 324 "(%" PRIu64 " * 100 / %" PRIu64 ")", 325 pair->stat.period, pair_total, 326 he->stat.period, he_total); 327 } 328 329 static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, 330 char *buf, size_t size) 331 { 332 double old_period = he->stat.period; 333 double new_period = pair->stat.period; 334 335 return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); 336 } 337 338 static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair, 339 char *buf, size_t size) 340 { 341 u64 old_period = he->stat.period; 342 u64 new_period = pair->stat.period; 343 344 return scnprintf(buf, size, 345 "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", 346 new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); 347 } 348 349 static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, 350 char *buf, size_t size) 351 { 352 switch (compute) { 353 case COMPUTE_DELTA: 354 case COMPUTE_DELTA_ABS: 355 return formula_delta(he, pair, buf, size); 356 case COMPUTE_RATIO: 357 return formula_ratio(he, pair, buf, size); 358 case COMPUTE_WEIGHTED_DIFF: 359 return formula_wdiff(he, pair, buf, size); 360 default: 361 BUG_ON(1); 362 } 363 364 return -1; 365 } 366 367 static void *block_hist_zalloc(size_t size) 368 { 369 struct block_hist *bh; 370 371 bh = zalloc(size + sizeof(*bh)); 372 if (!bh) 373 return NULL; 374 375 return &bh->he; 376 } 377 378 static void block_hist_free(void *he) 379 { 380 struct block_hist *bh; 381 382 bh = container_of(he, struct block_hist, he); 383 hists__delete_entries(&bh->block_hists); 384 free(bh); 385 } 386 387 struct hist_entry_ops block_hist_ops = { 388 .new = block_hist_zalloc, 389 .free = block_hist_free, 390 }; 391 392 static int diff__process_sample_event(const struct perf_tool *tool, 393 union perf_event *event, 394 struct perf_sample *sample, 395 struct evsel *evsel, 396 struct machine *machine) 397 { 398 struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool); 399 struct addr_location al; 400 struct hists *hists = evsel__hists(evsel); 401 struct hist_entry_iter iter = { 402 .evsel = evsel, 403 .sample = sample, 404 .ops = &hist_iter_normal, 405 }; 406 int ret = -1; 407 408 if (perf_time__ranges_skip_sample(pdiff->ptime_range, pdiff->range_num, 409 sample->time)) { 410 return 0; 411 } 412 413 addr_location__init(&al); 414 if (machine__resolve(machine, &al, sample) < 0) { 415 pr_warning("problem processing %d event, skipping it.\n", 416 event->header.type); 417 ret = -1; 418 goto out; 419 } 420 421 if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) { 422 ret = 0; 423 goto out; 424 } 425 426 switch (compute) { 427 case COMPUTE_CYCLES: 428 if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL, 429 NULL, NULL, NULL, sample, true)) { 430 pr_warning("problem incrementing symbol period, " 431 "skipping event\n"); 432 goto out; 433 } 434 435 hist__account_cycles(sample->branch_stack, &al, sample, 436 false, NULL, evsel); 437 break; 438 439 case COMPUTE_STREAM: 440 if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, 441 NULL)) { 442 pr_debug("problem adding hist entry, skipping event\n"); 443 goto out; 444 } 445 break; 446 447 default: 448 if (!hists__add_entry(hists, &al, NULL, NULL, NULL, NULL, sample, 449 true)) { 450 pr_warning("problem incrementing symbol period, " 451 "skipping event\n"); 452 goto out; 453 } 454 } 455 456 /* 457 * The total_period is updated here before going to the output 458 * tree since normally only the baseline hists will call 459 * hists__output_resort() and precompute needs the total 460 * period in order to sort entries by percentage delta. 461 */ 462 hists->stats.total_period += sample->period; 463 if (!al.filtered) 464 hists->stats.total_non_filtered_period += sample->period; 465 ret = 0; 466 out: 467 addr_location__exit(&al); 468 return ret; 469 } 470 471 static struct perf_diff pdiff; 472 473 static struct evsel *evsel_match(struct evsel *evsel, struct evlist *evlist) 474 { 475 struct evsel *e; 476 477 evlist__for_each_entry(evlist, e) { 478 if ((evsel->core.attr.type == e->core.attr.type) && 479 (evsel->core.attr.config == e->core.attr.config)) 480 return e; 481 } 482 483 return NULL; 484 } 485 486 static void evlist__collapse_resort(struct evlist *evlist) 487 { 488 struct evsel *evsel; 489 490 evlist__for_each_entry(evlist, evsel) { 491 struct hists *hists = evsel__hists(evsel); 492 493 hists__collapse_resort(hists, NULL); 494 } 495 } 496 497 static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt) 498 { 499 struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt); 500 void *ptr = dfmt - dfmt->idx; 501 struct data__file *d = container_of(ptr, struct data__file, fmt); 502 503 return d; 504 } 505 506 static struct hist_entry* 507 get_pair_data(struct hist_entry *he, struct data__file *d) 508 { 509 if (hist_entry__has_pairs(he)) { 510 struct hist_entry *pair; 511 512 list_for_each_entry(pair, &he->pairs.head, pairs.node) 513 if (pair->hists == d->hists) 514 return pair; 515 } 516 517 return NULL; 518 } 519 520 static struct hist_entry* 521 get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt) 522 { 523 struct data__file *d = fmt_to_data_file(&dfmt->fmt); 524 525 return get_pair_data(he, d); 526 } 527 528 static void hists__baseline_only(struct hists *hists) 529 { 530 struct rb_root_cached *root; 531 struct rb_node *next; 532 533 if (hists__has(hists, need_collapse)) 534 root = &hists->entries_collapsed; 535 else 536 root = hists->entries_in; 537 538 next = rb_first_cached(root); 539 while (next != NULL) { 540 struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in); 541 542 next = rb_next(&he->rb_node_in); 543 if (!hist_entry__next_pair(he)) { 544 rb_erase_cached(&he->rb_node_in, root); 545 hist_entry__delete(he); 546 } 547 } 548 } 549 550 static int64_t block_cycles_diff_cmp(struct hist_entry *left, 551 struct hist_entry *right) 552 { 553 bool pairs_left = hist_entry__has_pairs(left); 554 bool pairs_right = hist_entry__has_pairs(right); 555 s64 l, r; 556 557 if (!pairs_left && !pairs_right) 558 return 0; 559 560 l = llabs(left->diff.cycles); 561 r = llabs(right->diff.cycles); 562 return r - l; 563 } 564 565 static int64_t block_sort(struct perf_hpp_fmt *fmt __maybe_unused, 566 struct hist_entry *left, struct hist_entry *right) 567 { 568 return block_cycles_diff_cmp(right, left); 569 } 570 571 static void init_block_hist(struct block_hist *bh) 572 { 573 __hists__init(&bh->block_hists, &bh->block_list); 574 perf_hpp_list__init(&bh->block_list); 575 576 INIT_LIST_HEAD(&bh->block_fmt.list); 577 INIT_LIST_HEAD(&bh->block_fmt.sort_list); 578 bh->block_fmt.cmp = block_info__cmp; 579 bh->block_fmt.sort = block_sort; 580 perf_hpp_list__register_sort_field(&bh->block_list, 581 &bh->block_fmt); 582 bh->valid = true; 583 } 584 585 static struct hist_entry *get_block_pair(struct hist_entry *he, 586 struct hists *hists_pair) 587 { 588 struct rb_root_cached *root = hists_pair->entries_in; 589 struct rb_node *next = rb_first_cached(root); 590 int64_t cmp; 591 592 while (next != NULL) { 593 struct hist_entry *he_pair = rb_entry(next, struct hist_entry, 594 rb_node_in); 595 596 next = rb_next(&he_pair->rb_node_in); 597 598 cmp = __block_info__cmp(he_pair, he); 599 if (!cmp) 600 return he_pair; 601 } 602 603 return NULL; 604 } 605 606 static void init_spark_values(unsigned long *svals, int num) 607 { 608 for (int i = 0; i < num; i++) 609 svals[i] = 0; 610 } 611 612 static void update_spark_value(unsigned long *svals, int num, 613 struct stats *stats, u64 val) 614 { 615 int n = stats->n; 616 617 if (n < num) 618 svals[n] = val; 619 } 620 621 static void compute_cycles_diff(struct hist_entry *he, 622 struct hist_entry *pair) 623 { 624 pair->diff.computed = true; 625 if (pair->block_info->num && he->block_info->num) { 626 pair->diff.cycles = 627 pair->block_info->cycles_aggr / pair->block_info->num_aggr - 628 he->block_info->cycles_aggr / he->block_info->num_aggr; 629 630 if (!cycles_hist) 631 return; 632 633 init_stats(&pair->diff.stats); 634 init_spark_values(pair->diff.svals, NUM_SPARKS); 635 636 for (int i = 0; i < pair->block_info->num; i++) { 637 u64 val; 638 639 if (i >= he->block_info->num || i >= NUM_SPARKS) 640 break; 641 642 val = llabs(pair->block_info->cycles_spark[i] - 643 he->block_info->cycles_spark[i]); 644 645 update_spark_value(pair->diff.svals, NUM_SPARKS, 646 &pair->diff.stats, val); 647 update_stats(&pair->diff.stats, val); 648 } 649 } 650 } 651 652 static void block_hists_match(struct hists *hists_base, 653 struct hists *hists_pair) 654 { 655 struct rb_root_cached *root = hists_base->entries_in; 656 struct rb_node *next = rb_first_cached(root); 657 658 while (next != NULL) { 659 struct hist_entry *he = rb_entry(next, struct hist_entry, 660 rb_node_in); 661 struct hist_entry *pair = get_block_pair(he, hists_pair); 662 663 next = rb_next(&he->rb_node_in); 664 665 if (pair) { 666 hist_entry__add_pair(pair, he); 667 compute_cycles_diff(he, pair); 668 } 669 } 670 } 671 672 static void hists__precompute(struct hists *hists) 673 { 674 struct rb_root_cached *root; 675 struct rb_node *next; 676 677 if (hists__has(hists, need_collapse)) 678 root = &hists->entries_collapsed; 679 else 680 root = hists->entries_in; 681 682 next = rb_first_cached(root); 683 while (next != NULL) { 684 struct block_hist *bh, *pair_bh; 685 struct hist_entry *he, *pair; 686 struct data__file *d; 687 int i; 688 689 he = rb_entry(next, struct hist_entry, rb_node_in); 690 next = rb_next(&he->rb_node_in); 691 692 if (compute == COMPUTE_CYCLES) { 693 bh = container_of(he, struct block_hist, he); 694 init_block_hist(bh); 695 block_info__process_sym(he, bh, NULL, 0, 0); 696 } 697 698 data__for_each_file_new(i, d) { 699 pair = get_pair_data(he, d); 700 if (!pair) 701 continue; 702 703 switch (compute) { 704 case COMPUTE_DELTA: 705 case COMPUTE_DELTA_ABS: 706 compute_delta(he, pair); 707 break; 708 case COMPUTE_RATIO: 709 compute_ratio(he, pair); 710 break; 711 case COMPUTE_WEIGHTED_DIFF: 712 compute_wdiff(he, pair); 713 break; 714 case COMPUTE_CYCLES: 715 pair_bh = container_of(pair, struct block_hist, 716 he); 717 init_block_hist(pair_bh); 718 block_info__process_sym(pair, pair_bh, NULL, 0, 0); 719 720 bh = container_of(he, struct block_hist, he); 721 722 if (bh->valid && pair_bh->valid) { 723 block_hists_match(&bh->block_hists, 724 &pair_bh->block_hists); 725 hists__output_resort(&pair_bh->block_hists, 726 NULL); 727 } 728 break; 729 default: 730 BUG_ON(1); 731 } 732 } 733 } 734 } 735 736 static int64_t cmp_doubles(double l, double r) 737 { 738 if (l > r) 739 return -1; 740 else if (l < r) 741 return 1; 742 else 743 return 0; 744 } 745 746 static int64_t 747 __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, 748 int c) 749 { 750 switch (c) { 751 case COMPUTE_DELTA: 752 { 753 double l = left->diff.period_ratio_delta; 754 double r = right->diff.period_ratio_delta; 755 756 return cmp_doubles(l, r); 757 } 758 case COMPUTE_DELTA_ABS: 759 { 760 double l = fabs(left->diff.period_ratio_delta); 761 double r = fabs(right->diff.period_ratio_delta); 762 763 return cmp_doubles(l, r); 764 } 765 case COMPUTE_RATIO: 766 { 767 double l = left->diff.period_ratio; 768 double r = right->diff.period_ratio; 769 770 return cmp_doubles(l, r); 771 } 772 case COMPUTE_WEIGHTED_DIFF: 773 { 774 s64 l = left->diff.wdiff; 775 s64 r = right->diff.wdiff; 776 777 return r - l; 778 } 779 default: 780 BUG_ON(1); 781 } 782 783 return 0; 784 } 785 786 static int64_t 787 hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, 788 int c, int sort_idx) 789 { 790 bool pairs_left = hist_entry__has_pairs(left); 791 bool pairs_right = hist_entry__has_pairs(right); 792 struct hist_entry *p_right, *p_left; 793 794 if (!pairs_left && !pairs_right) 795 return 0; 796 797 if (!pairs_left || !pairs_right) 798 return pairs_left ? -1 : 1; 799 800 p_left = get_pair_data(left, &data__files[sort_idx]); 801 p_right = get_pair_data(right, &data__files[sort_idx]); 802 803 if (!p_left && !p_right) 804 return 0; 805 806 if (!p_left || !p_right) 807 return p_left ? -1 : 1; 808 809 /* 810 * We have 2 entries of same kind, let's 811 * make the data comparison. 812 */ 813 return __hist_entry__cmp_compute(p_left, p_right, c); 814 } 815 816 static int64_t 817 hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right, 818 int c, int sort_idx) 819 { 820 struct hist_entry *p_right, *p_left; 821 822 p_left = get_pair_data(left, &data__files[sort_idx]); 823 p_right = get_pair_data(right, &data__files[sort_idx]); 824 825 if (!p_left && !p_right) 826 return 0; 827 828 if (!p_left || !p_right) 829 return p_left ? -1 : 1; 830 831 if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) { 832 /* 833 * The delta can be computed without the baseline, but 834 * others are not. Put those entries which have no 835 * values below. 836 */ 837 if (left->dummy && right->dummy) 838 return 0; 839 840 if (left->dummy || right->dummy) 841 return left->dummy ? 1 : -1; 842 } 843 844 return __hist_entry__cmp_compute(p_left, p_right, c); 845 } 846 847 static int64_t 848 hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused, 849 struct hist_entry *left __maybe_unused, 850 struct hist_entry *right __maybe_unused) 851 { 852 return 0; 853 } 854 855 static int64_t 856 hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused, 857 struct hist_entry *left, struct hist_entry *right) 858 { 859 if (left->stat.period == right->stat.period) 860 return 0; 861 return left->stat.period > right->stat.period ? 1 : -1; 862 } 863 864 static int64_t 865 hist_entry__cmp_delta(struct perf_hpp_fmt *fmt, 866 struct hist_entry *left, struct hist_entry *right) 867 { 868 struct data__file *d = fmt_to_data_file(fmt); 869 870 return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx); 871 } 872 873 static int64_t 874 hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt, 875 struct hist_entry *left, struct hist_entry *right) 876 { 877 struct data__file *d = fmt_to_data_file(fmt); 878 879 return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx); 880 } 881 882 static int64_t 883 hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt, 884 struct hist_entry *left, struct hist_entry *right) 885 { 886 struct data__file *d = fmt_to_data_file(fmt); 887 888 return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx); 889 } 890 891 static int64_t 892 hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt, 893 struct hist_entry *left, struct hist_entry *right) 894 { 895 struct data__file *d = fmt_to_data_file(fmt); 896 897 return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx); 898 } 899 900 static int64_t 901 hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused, 902 struct hist_entry *left, struct hist_entry *right) 903 { 904 return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA, 905 sort_compute); 906 } 907 908 static int64_t 909 hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused, 910 struct hist_entry *left, struct hist_entry *right) 911 { 912 return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS, 913 sort_compute); 914 } 915 916 static int64_t 917 hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused, 918 struct hist_entry *left, struct hist_entry *right) 919 { 920 return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO, 921 sort_compute); 922 } 923 924 static int64_t 925 hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused, 926 struct hist_entry *left, struct hist_entry *right) 927 { 928 return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF, 929 sort_compute); 930 } 931 932 static void hists__process(struct hists *hists) 933 { 934 if (show_baseline_only) 935 hists__baseline_only(hists); 936 937 hists__precompute(hists); 938 hists__output_resort(hists, NULL); 939 940 if (compute == COMPUTE_CYCLES) 941 symbol_conf.report_block = true; 942 943 hists__fprintf(hists, !quiet, 0, 0, 0, stdout, 944 !symbol_conf.use_callchain); 945 } 946 947 static void data__fprintf(void) 948 { 949 struct data__file *d; 950 int i; 951 952 fprintf(stdout, "# Data files:\n"); 953 954 data__for_each_file(i, d) 955 fprintf(stdout, "# [%d] %s %s\n", 956 d->idx, d->data.path, 957 !d->idx ? "(Baseline)" : ""); 958 959 fprintf(stdout, "#\n"); 960 } 961 962 static void data_process(void) 963 { 964 struct evlist *evlist_base = data__files[0].session->evlist; 965 struct evsel *evsel_base; 966 bool first = true; 967 968 evlist__for_each_entry(evlist_base, evsel_base) { 969 struct hists *hists_base = evsel__hists(evsel_base); 970 struct data__file *d; 971 int i; 972 973 data__for_each_file_new(i, d) { 974 struct evlist *evlist = d->session->evlist; 975 struct evsel *evsel; 976 struct hists *hists; 977 978 evsel = evsel_match(evsel_base, evlist); 979 if (!evsel) 980 continue; 981 982 hists = evsel__hists(evsel); 983 d->hists = hists; 984 985 hists__match(hists_base, hists); 986 987 if (!show_baseline_only) 988 hists__link(hists_base, hists); 989 } 990 991 if (!quiet) { 992 fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", 993 evsel__name(evsel_base)); 994 } 995 996 first = false; 997 998 if (verbose > 0 || ((data__files_cnt > 2) && !quiet)) 999 data__fprintf(); 1000 1001 /* Don't sort callchain for perf diff */ 1002 evsel__reset_sample_bit(evsel_base, CALLCHAIN); 1003 1004 hists__process(hists_base); 1005 } 1006 } 1007 1008 static int process_base_stream(struct data__file *data_base, 1009 struct data__file *data_pair, 1010 const char *title __maybe_unused) 1011 { 1012 struct evlist *evlist_base = data_base->session->evlist; 1013 struct evlist *evlist_pair = data_pair->session->evlist; 1014 struct evsel *evsel_base, *evsel_pair; 1015 struct evsel_streams *es_base, *es_pair; 1016 1017 evlist__for_each_entry(evlist_base, evsel_base) { 1018 evsel_pair = evsel_match(evsel_base, evlist_pair); 1019 if (!evsel_pair) 1020 continue; 1021 1022 es_base = evsel_streams__entry(data_base->evlist_streams, 1023 evsel_base); 1024 if (!es_base) 1025 return -1; 1026 1027 es_pair = evsel_streams__entry(data_pair->evlist_streams, 1028 evsel_pair); 1029 if (!es_pair) 1030 return -1; 1031 1032 evsel_streams__match(es_base, es_pair); 1033 evsel_streams__report(es_base, es_pair); 1034 } 1035 1036 return 0; 1037 } 1038 1039 static void stream_process(void) 1040 { 1041 /* 1042 * Stream comparison only supports two data files. 1043 * perf.data.old and perf.data. data__files[0] is perf.data.old, 1044 * data__files[1] is perf.data. 1045 */ 1046 process_base_stream(&data__files[0], &data__files[1], 1047 "# Output based on old perf data:\n#\n"); 1048 } 1049 1050 static void data__free(struct data__file *d) 1051 { 1052 int col; 1053 1054 if (d->evlist_streams) 1055 evlist_streams__delete(d->evlist_streams); 1056 1057 for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) { 1058 struct diff_hpp_fmt *fmt = &d->fmt[col]; 1059 1060 zfree(&fmt->header); 1061 } 1062 } 1063 1064 static int abstime_str_dup(char **pstr) 1065 { 1066 char *str = NULL; 1067 1068 if (pdiff.time_str && strchr(pdiff.time_str, ':')) { 1069 str = strdup(pdiff.time_str); 1070 if (!str) 1071 return -ENOMEM; 1072 } 1073 1074 *pstr = str; 1075 return 0; 1076 } 1077 1078 static int parse_absolute_time(struct data__file *d, char **pstr) 1079 { 1080 char *p = *pstr; 1081 int ret; 1082 1083 /* 1084 * Absolute timestamp for one file has the format: a.b,c.d 1085 * For multiple files, the format is: a.b,c.d:a.b,c.d 1086 */ 1087 p = strchr(*pstr, ':'); 1088 if (p) { 1089 if (p == *pstr) { 1090 pr_err("Invalid time string\n"); 1091 return -EINVAL; 1092 } 1093 1094 *p = 0; 1095 p++; 1096 if (*p == 0) { 1097 pr_err("Invalid time string\n"); 1098 return -EINVAL; 1099 } 1100 } 1101 1102 ret = perf_time__parse_for_ranges(*pstr, d->session, 1103 &pdiff.ptime_range, 1104 &pdiff.range_size, 1105 &pdiff.range_num); 1106 if (ret < 0) 1107 return ret; 1108 1109 if (!p || *p == 0) 1110 *pstr = NULL; 1111 else 1112 *pstr = p; 1113 1114 return ret; 1115 } 1116 1117 static int parse_percent_time(struct data__file *d) 1118 { 1119 int ret; 1120 1121 ret = perf_time__parse_for_ranges(pdiff.time_str, d->session, 1122 &pdiff.ptime_range, 1123 &pdiff.range_size, 1124 &pdiff.range_num); 1125 return ret; 1126 } 1127 1128 static int parse_time_str(struct data__file *d, char *abstime_ostr, 1129 char **pabstime_tmp) 1130 { 1131 int ret = 0; 1132 1133 if (abstime_ostr) 1134 ret = parse_absolute_time(d, pabstime_tmp); 1135 else if (pdiff.time_str) 1136 ret = parse_percent_time(d); 1137 1138 return ret; 1139 } 1140 1141 static int check_file_brstack(void) 1142 { 1143 struct data__file *d; 1144 bool has_br_stack; 1145 int i; 1146 1147 data__for_each_file(i, d) { 1148 d->session = perf_session__new(&d->data, &pdiff.tool); 1149 if (IS_ERR(d->session)) { 1150 pr_err("Failed to open %s\n", d->data.path); 1151 return PTR_ERR(d->session); 1152 } 1153 1154 has_br_stack = perf_header__has_feat(&d->session->header, 1155 HEADER_BRANCH_STACK); 1156 perf_session__delete(d->session); 1157 if (!has_br_stack) 1158 return 0; 1159 } 1160 1161 /* Set only all files having branch stacks */ 1162 pdiff.has_br_stack = true; 1163 return 0; 1164 } 1165 1166 static int __cmd_diff(void) 1167 { 1168 struct data__file *d; 1169 int ret, i; 1170 char *abstime_ostr, *abstime_tmp; 1171 1172 ret = abstime_str_dup(&abstime_ostr); 1173 if (ret) 1174 return ret; 1175 1176 abstime_tmp = abstime_ostr; 1177 ret = -EINVAL; 1178 1179 data__for_each_file(i, d) { 1180 d->session = perf_session__new(&d->data, &pdiff.tool); 1181 if (IS_ERR(d->session)) { 1182 ret = PTR_ERR(d->session); 1183 pr_err("Failed to open %s\n", d->data.path); 1184 goto out_delete; 1185 } 1186 1187 if (pdiff.time_str) { 1188 ret = parse_time_str(d, abstime_ostr, &abstime_tmp); 1189 if (ret < 0) 1190 goto out_delete; 1191 } 1192 1193 if (cpu_list) { 1194 ret = perf_session__cpu_bitmap(d->session, cpu_list, 1195 cpu_bitmap); 1196 if (ret < 0) 1197 goto out_delete; 1198 } 1199 1200 ret = perf_session__process_events(d->session); 1201 if (ret) { 1202 pr_err("Failed to process %s\n", d->data.path); 1203 goto out_delete; 1204 } 1205 1206 evlist__collapse_resort(d->session->evlist); 1207 1208 if (pdiff.ptime_range) 1209 zfree(&pdiff.ptime_range); 1210 1211 if (compute == COMPUTE_STREAM) { 1212 d->evlist_streams = evlist__create_streams( 1213 d->session->evlist, 5); 1214 if (!d->evlist_streams) { 1215 ret = -ENOMEM; 1216 goto out_delete; 1217 } 1218 } 1219 } 1220 1221 if (compute == COMPUTE_STREAM) 1222 stream_process(); 1223 else 1224 data_process(); 1225 1226 out_delete: 1227 data__for_each_file(i, d) { 1228 if (!IS_ERR(d->session)) 1229 perf_session__delete(d->session); 1230 data__free(d); 1231 } 1232 1233 free(data__files); 1234 1235 if (pdiff.ptime_range) 1236 zfree(&pdiff.ptime_range); 1237 1238 if (abstime_ostr) 1239 free(abstime_ostr); 1240 1241 return ret; 1242 } 1243 1244 static const char * const diff_usage[] = { 1245 "perf diff [<options>] [old_file] [new_file]", 1246 NULL, 1247 }; 1248 1249 static const struct option options[] = { 1250 OPT_INCR('v', "verbose", &verbose, 1251 "be more verbose (show symbol address, etc)"), 1252 OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any warnings or messages"), 1253 OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, 1254 "Show only items with match in baseline"), 1255 OPT_CALLBACK('c', "compute", &compute, 1256 "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs),cycles", 1257 "Entries differential computation selection", 1258 setup_compute), 1259 OPT_BOOLEAN('p', "period", &show_period, 1260 "Show period values."), 1261 OPT_BOOLEAN('F', "formula", &show_formula, 1262 "Show formula."), 1263 OPT_BOOLEAN(0, "cycles-hist", &cycles_hist, 1264 "Show cycles histogram and standard deviation " 1265 "- WARNING: use only with -c cycles."), 1266 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 1267 "dump raw trace in ASCII"), 1268 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 1269 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, 1270 "file", "kallsyms pathname"), 1271 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, 1272 "load module symbols - WARNING: use only with -k and LIVE kernel"), 1273 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", 1274 "only consider symbols in these dsos"), 1275 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", 1276 "only consider symbols in these comms"), 1277 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", 1278 "only consider these symbols"), 1279 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 1280 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." 1281 " Please refer the man page for the complete list."), 1282 OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator", 1283 "separator for columns, no spaces will be added between " 1284 "columns '.' is reserved."), 1285 OPT_CALLBACK(0, "symfs", NULL, "directory", 1286 "Look for files with symbols relative to this directory", 1287 symbol__config_symfs), 1288 OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), 1289 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", 1290 "How to display percentage of filtered entries", parse_filter_percentage), 1291 OPT_STRING(0, "time", &pdiff.time_str, "str", 1292 "Time span (time percent or absolute timestamp)"), 1293 OPT_STRING(0, "cpu", &cpu_list, "cpu", "list of cpus to profile"), 1294 OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]", 1295 "only consider symbols in these pids"), 1296 OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]", 1297 "only consider symbols in these tids"), 1298 OPT_BOOLEAN(0, "stream", &pdiff.stream, 1299 "Enable hot streams comparison."), 1300 OPT_END() 1301 }; 1302 1303 static double baseline_percent(struct hist_entry *he) 1304 { 1305 u64 total = hists__total_period(he->hists); 1306 1307 return 100.0 * he->stat.period / total; 1308 } 1309 1310 static int hpp__color_baseline(struct perf_hpp_fmt *fmt, 1311 struct perf_hpp *hpp, struct hist_entry *he) 1312 { 1313 struct diff_hpp_fmt *dfmt = 1314 container_of(fmt, struct diff_hpp_fmt, fmt); 1315 double percent = baseline_percent(he); 1316 char pfmt[20] = " "; 1317 1318 if (!he->dummy) { 1319 scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1); 1320 return percent_color_snprintf(hpp->buf, hpp->size, 1321 pfmt, percent); 1322 } else 1323 return scnprintf(hpp->buf, hpp->size, "%*s", 1324 dfmt->header_width, pfmt); 1325 } 1326 1327 static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size) 1328 { 1329 double percent = baseline_percent(he); 1330 const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; 1331 int ret = 0; 1332 1333 if (!he->dummy) 1334 ret = scnprintf(buf, size, fmt, percent); 1335 1336 return ret; 1337 } 1338 1339 static int cycles_printf(struct hist_entry *he, struct hist_entry *pair, 1340 struct perf_hpp *hpp, int width) 1341 { 1342 struct block_hist *bh = container_of(he, struct block_hist, he); 1343 struct block_hist *bh_pair = container_of(pair, struct block_hist, he); 1344 struct hist_entry *block_he; 1345 struct block_info *bi; 1346 char buf[128]; 1347 char *start_line, *end_line; 1348 1349 block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx); 1350 if (!block_he) { 1351 hpp->skip = true; 1352 return 0; 1353 } 1354 1355 /* 1356 * Avoid printing the warning "addr2line_init failed for ..." 1357 */ 1358 symbol_conf.disable_add2line_warn = true; 1359 1360 bi = block_he->block_info; 1361 1362 start_line = map__srcline(he->ms.map, bi->sym->start + bi->start, 1363 he->ms.sym); 1364 1365 end_line = map__srcline(he->ms.map, bi->sym->start + bi->end, 1366 he->ms.sym); 1367 1368 if (start_line != SRCLINE_UNKNOWN && 1369 end_line != SRCLINE_UNKNOWN) { 1370 scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld", 1371 start_line, end_line, block_he->diff.cycles); 1372 } else { 1373 scnprintf(buf, sizeof(buf), "[%7lx -> %7lx] %4ld", 1374 bi->start, bi->end, block_he->diff.cycles); 1375 } 1376 1377 zfree_srcline(&start_line); 1378 zfree_srcline(&end_line); 1379 1380 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf); 1381 } 1382 1383 static int __hpp__color_compare(struct perf_hpp_fmt *fmt, 1384 struct perf_hpp *hpp, struct hist_entry *he, 1385 int comparison_method) 1386 { 1387 struct diff_hpp_fmt *dfmt = 1388 container_of(fmt, struct diff_hpp_fmt, fmt); 1389 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1390 double diff; 1391 s64 wdiff; 1392 char pfmt[20] = " "; 1393 1394 if (!pair) { 1395 if (comparison_method == COMPUTE_CYCLES) { 1396 struct block_hist *bh; 1397 1398 bh = container_of(he, struct block_hist, he); 1399 if (bh->block_idx) 1400 hpp->skip = true; 1401 } 1402 1403 goto no_print; 1404 } 1405 1406 switch (comparison_method) { 1407 case COMPUTE_DELTA: 1408 if (pair->diff.computed) 1409 diff = pair->diff.period_ratio_delta; 1410 else 1411 diff = compute_delta(he, pair); 1412 1413 scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1); 1414 return percent_color_snprintf(hpp->buf, hpp->size, 1415 pfmt, diff); 1416 case COMPUTE_RATIO: 1417 if (he->dummy) 1418 goto dummy_print; 1419 if (pair->diff.computed) 1420 diff = pair->diff.period_ratio; 1421 else 1422 diff = compute_ratio(he, pair); 1423 1424 scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width); 1425 return value_color_snprintf(hpp->buf, hpp->size, 1426 pfmt, diff); 1427 case COMPUTE_WEIGHTED_DIFF: 1428 if (he->dummy) 1429 goto dummy_print; 1430 if (pair->diff.computed) 1431 wdiff = pair->diff.wdiff; 1432 else 1433 wdiff = compute_wdiff(he, pair); 1434 1435 scnprintf(pfmt, 20, "%%14ld", dfmt->header_width); 1436 return color_snprintf(hpp->buf, hpp->size, 1437 get_percent_color(wdiff), 1438 pfmt, wdiff); 1439 case COMPUTE_CYCLES: 1440 return cycles_printf(he, pair, hpp, dfmt->header_width); 1441 default: 1442 BUG_ON(1); 1443 } 1444 dummy_print: 1445 return scnprintf(hpp->buf, hpp->size, "%*s", 1446 dfmt->header_width, "N/A"); 1447 no_print: 1448 return scnprintf(hpp->buf, hpp->size, "%*s", 1449 dfmt->header_width, pfmt); 1450 } 1451 1452 static int hpp__color_delta(struct perf_hpp_fmt *fmt, 1453 struct perf_hpp *hpp, struct hist_entry *he) 1454 { 1455 return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA); 1456 } 1457 1458 static int hpp__color_ratio(struct perf_hpp_fmt *fmt, 1459 struct perf_hpp *hpp, struct hist_entry *he) 1460 { 1461 return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO); 1462 } 1463 1464 static int hpp__color_wdiff(struct perf_hpp_fmt *fmt, 1465 struct perf_hpp *hpp, struct hist_entry *he) 1466 { 1467 return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF); 1468 } 1469 1470 static int hpp__color_cycles(struct perf_hpp_fmt *fmt, 1471 struct perf_hpp *hpp, struct hist_entry *he) 1472 { 1473 return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES); 1474 } 1475 1476 static int all_zero(unsigned long *vals, int len) 1477 { 1478 int i; 1479 1480 for (i = 0; i < len; i++) 1481 if (vals[i] != 0) 1482 return 0; 1483 return 1; 1484 } 1485 1486 static int print_cycles_spark(char *bf, int size, unsigned long *svals, u64 n) 1487 { 1488 int printed; 1489 1490 if (n <= 1) 1491 return 0; 1492 1493 if (n > NUM_SPARKS) 1494 n = NUM_SPARKS; 1495 if (all_zero(svals, n)) 1496 return 0; 1497 1498 printed = print_spark(bf, size, svals, n); 1499 printed += scnprintf(bf + printed, size - printed, " "); 1500 return printed; 1501 } 1502 1503 static int hpp__color_cycles_hist(struct perf_hpp_fmt *fmt, 1504 struct perf_hpp *hpp, struct hist_entry *he) 1505 { 1506 struct diff_hpp_fmt *dfmt = 1507 container_of(fmt, struct diff_hpp_fmt, fmt); 1508 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1509 struct block_hist *bh = container_of(he, struct block_hist, he); 1510 struct block_hist *bh_pair; 1511 struct hist_entry *block_he; 1512 char spark[32], buf[128]; 1513 double r; 1514 int ret, pad; 1515 1516 if (!pair) { 1517 if (bh->block_idx) 1518 hpp->skip = true; 1519 1520 goto no_print; 1521 } 1522 1523 bh_pair = container_of(pair, struct block_hist, he); 1524 1525 block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx); 1526 if (!block_he) { 1527 hpp->skip = true; 1528 goto no_print; 1529 } 1530 1531 ret = print_cycles_spark(spark, sizeof(spark), block_he->diff.svals, 1532 block_he->diff.stats.n); 1533 1534 r = rel_stddev_stats(stddev_stats(&block_he->diff.stats), 1535 avg_stats(&block_he->diff.stats)); 1536 1537 if (ret) { 1538 /* 1539 * Padding spaces if number of sparks less than NUM_SPARKS 1540 * otherwise the output is not aligned. 1541 */ 1542 pad = NUM_SPARKS - ((ret - 1) / 3); 1543 scnprintf(buf, sizeof(buf), "%s%5.1f%% %s", "\u00B1", r, spark); 1544 ret = scnprintf(hpp->buf, hpp->size, "%*s", 1545 dfmt->header_width, buf); 1546 1547 if (pad) { 1548 ret += scnprintf(hpp->buf + ret, hpp->size - ret, 1549 "%-*s", pad, " "); 1550 } 1551 1552 return ret; 1553 } 1554 1555 no_print: 1556 return scnprintf(hpp->buf, hpp->size, "%*s", 1557 dfmt->header_width, " "); 1558 } 1559 1560 static void 1561 hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size) 1562 { 1563 switch (idx) { 1564 case PERF_HPP_DIFF__PERIOD_BASELINE: 1565 scnprintf(buf, size, "%" PRIu64, he->stat.period); 1566 break; 1567 1568 default: 1569 break; 1570 } 1571 } 1572 1573 static void 1574 hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, 1575 int idx, char *buf, size_t size) 1576 { 1577 double diff; 1578 double ratio; 1579 s64 wdiff; 1580 1581 switch (idx) { 1582 case PERF_HPP_DIFF__DELTA: 1583 case PERF_HPP_DIFF__DELTA_ABS: 1584 if (pair->diff.computed) 1585 diff = pair->diff.period_ratio_delta; 1586 else 1587 diff = compute_delta(he, pair); 1588 1589 scnprintf(buf, size, "%+4.2F%%", diff); 1590 break; 1591 1592 case PERF_HPP_DIFF__RATIO: 1593 /* No point for ratio number if we are dummy.. */ 1594 if (he->dummy) { 1595 scnprintf(buf, size, "N/A"); 1596 break; 1597 } 1598 1599 if (pair->diff.computed) 1600 ratio = pair->diff.period_ratio; 1601 else 1602 ratio = compute_ratio(he, pair); 1603 1604 if (ratio > 0.0) 1605 scnprintf(buf, size, "%14.6F", ratio); 1606 break; 1607 1608 case PERF_HPP_DIFF__WEIGHTED_DIFF: 1609 /* No point for wdiff number if we are dummy.. */ 1610 if (he->dummy) { 1611 scnprintf(buf, size, "N/A"); 1612 break; 1613 } 1614 1615 if (pair->diff.computed) 1616 wdiff = pair->diff.wdiff; 1617 else 1618 wdiff = compute_wdiff(he, pair); 1619 1620 if (wdiff != 0) 1621 scnprintf(buf, size, "%14ld", wdiff); 1622 break; 1623 1624 case PERF_HPP_DIFF__FORMULA: 1625 formula_fprintf(he, pair, buf, size); 1626 break; 1627 1628 case PERF_HPP_DIFF__PERIOD: 1629 scnprintf(buf, size, "%" PRIu64, pair->stat.period); 1630 break; 1631 1632 default: 1633 BUG_ON(1); 1634 } 1635 } 1636 1637 static void 1638 __hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt, 1639 char *buf, size_t size) 1640 { 1641 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1642 int idx = dfmt->idx; 1643 1644 /* baseline is special */ 1645 if (idx == PERF_HPP_DIFF__BASELINE) 1646 hpp__entry_baseline(he, buf, size); 1647 else { 1648 if (pair) 1649 hpp__entry_pair(he, pair, idx, buf, size); 1650 else 1651 hpp__entry_unpair(he, idx, buf, size); 1652 } 1653 } 1654 1655 static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp, 1656 struct hist_entry *he) 1657 { 1658 struct diff_hpp_fmt *dfmt = 1659 container_of(_fmt, struct diff_hpp_fmt, fmt); 1660 char buf[MAX_COL_WIDTH] = " "; 1661 1662 __hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH); 1663 1664 if (symbol_conf.field_sep) 1665 return scnprintf(hpp->buf, hpp->size, "%s", buf); 1666 else 1667 return scnprintf(hpp->buf, hpp->size, "%*s", 1668 dfmt->header_width, buf); 1669 } 1670 1671 static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 1672 struct hists *hists __maybe_unused, 1673 int line __maybe_unused, 1674 int *span __maybe_unused) 1675 { 1676 struct diff_hpp_fmt *dfmt = 1677 container_of(fmt, struct diff_hpp_fmt, fmt); 1678 1679 BUG_ON(!dfmt->header); 1680 return scnprintf(hpp->buf, hpp->size, dfmt->header); 1681 } 1682 1683 static int hpp__width(struct perf_hpp_fmt *fmt, 1684 struct perf_hpp *hpp __maybe_unused, 1685 struct hists *hists __maybe_unused) 1686 { 1687 struct diff_hpp_fmt *dfmt = 1688 container_of(fmt, struct diff_hpp_fmt, fmt); 1689 1690 BUG_ON(dfmt->header_width <= 0); 1691 return dfmt->header_width; 1692 } 1693 1694 static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt) 1695 { 1696 #define MAX_HEADER_NAME 100 1697 char buf_indent[MAX_HEADER_NAME]; 1698 char buf[MAX_HEADER_NAME]; 1699 const char *header = NULL; 1700 int width = 0; 1701 1702 BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX); 1703 header = columns[dfmt->idx].name; 1704 width = columns[dfmt->idx].width; 1705 1706 /* Only our defined HPP fmts should appear here. */ 1707 BUG_ON(!header); 1708 1709 if (data__files_cnt > 2) 1710 scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx); 1711 1712 #define NAME (data__files_cnt > 2 ? buf : header) 1713 dfmt->header_width = width; 1714 width = (int) strlen(NAME); 1715 if (dfmt->header_width < width) 1716 dfmt->header_width = width; 1717 1718 scnprintf(buf_indent, MAX_HEADER_NAME, "%*s", 1719 dfmt->header_width, NAME); 1720 1721 dfmt->header = strdup(buf_indent); 1722 #undef MAX_HEADER_NAME 1723 #undef NAME 1724 } 1725 1726 static void data__hpp_register(struct data__file *d, int idx) 1727 { 1728 struct diff_hpp_fmt *dfmt = &d->fmt[idx]; 1729 struct perf_hpp_fmt *fmt = &dfmt->fmt; 1730 1731 dfmt->idx = idx; 1732 1733 fmt->header = hpp__header; 1734 fmt->width = hpp__width; 1735 fmt->entry = hpp__entry_global; 1736 fmt->cmp = hist_entry__cmp_nop; 1737 fmt->collapse = hist_entry__cmp_nop; 1738 1739 /* TODO more colors */ 1740 switch (idx) { 1741 case PERF_HPP_DIFF__BASELINE: 1742 fmt->color = hpp__color_baseline; 1743 fmt->sort = hist_entry__cmp_baseline; 1744 break; 1745 case PERF_HPP_DIFF__DELTA: 1746 fmt->color = hpp__color_delta; 1747 fmt->sort = hist_entry__cmp_delta; 1748 break; 1749 case PERF_HPP_DIFF__RATIO: 1750 fmt->color = hpp__color_ratio; 1751 fmt->sort = hist_entry__cmp_ratio; 1752 break; 1753 case PERF_HPP_DIFF__WEIGHTED_DIFF: 1754 fmt->color = hpp__color_wdiff; 1755 fmt->sort = hist_entry__cmp_wdiff; 1756 break; 1757 case PERF_HPP_DIFF__DELTA_ABS: 1758 fmt->color = hpp__color_delta; 1759 fmt->sort = hist_entry__cmp_delta_abs; 1760 break; 1761 case PERF_HPP_DIFF__CYCLES: 1762 fmt->color = hpp__color_cycles; 1763 fmt->sort = hist_entry__cmp_nop; 1764 break; 1765 case PERF_HPP_DIFF__CYCLES_HIST: 1766 fmt->color = hpp__color_cycles_hist; 1767 fmt->sort = hist_entry__cmp_nop; 1768 break; 1769 default: 1770 fmt->sort = hist_entry__cmp_nop; 1771 break; 1772 } 1773 1774 init_header(d, dfmt); 1775 perf_hpp__column_register(fmt); 1776 perf_hpp__register_sort_field(fmt); 1777 } 1778 1779 static int ui_init(void) 1780 { 1781 struct data__file *d; 1782 struct perf_hpp_fmt *fmt; 1783 int i; 1784 1785 data__for_each_file(i, d) { 1786 1787 /* 1788 * Baseline or compute related columns: 1789 * 1790 * PERF_HPP_DIFF__BASELINE 1791 * PERF_HPP_DIFF__DELTA 1792 * PERF_HPP_DIFF__RATIO 1793 * PERF_HPP_DIFF__WEIGHTED_DIFF 1794 * PERF_HPP_DIFF__CYCLES 1795 */ 1796 data__hpp_register(d, i ? compute_2_hpp[compute] : 1797 PERF_HPP_DIFF__BASELINE); 1798 1799 if (cycles_hist && i) 1800 data__hpp_register(d, PERF_HPP_DIFF__CYCLES_HIST); 1801 1802 /* 1803 * And the rest: 1804 * 1805 * PERF_HPP_DIFF__FORMULA 1806 * PERF_HPP_DIFF__PERIOD 1807 * PERF_HPP_DIFF__PERIOD_BASELINE 1808 */ 1809 if (show_formula && i) 1810 data__hpp_register(d, PERF_HPP_DIFF__FORMULA); 1811 1812 if (show_period) 1813 data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD : 1814 PERF_HPP_DIFF__PERIOD_BASELINE); 1815 } 1816 1817 if (!sort_compute) 1818 return 0; 1819 1820 /* 1821 * Prepend an fmt to sort on columns at 'sort_compute' first. 1822 * This fmt is added only to the sort list but not to the 1823 * output fields list. 1824 * 1825 * Note that this column (data) can be compared twice - one 1826 * for this 'sort_compute' fmt and another for the normal 1827 * diff_hpp_fmt. But it shouldn't a problem as most entries 1828 * will be sorted out by first try or baseline and comparing 1829 * is not a costly operation. 1830 */ 1831 fmt = zalloc(sizeof(*fmt)); 1832 if (fmt == NULL) { 1833 pr_err("Memory allocation failed\n"); 1834 return -1; 1835 } 1836 1837 fmt->cmp = hist_entry__cmp_nop; 1838 fmt->collapse = hist_entry__cmp_nop; 1839 1840 switch (compute) { 1841 case COMPUTE_DELTA: 1842 fmt->sort = hist_entry__cmp_delta_idx; 1843 break; 1844 case COMPUTE_RATIO: 1845 fmt->sort = hist_entry__cmp_ratio_idx; 1846 break; 1847 case COMPUTE_WEIGHTED_DIFF: 1848 fmt->sort = hist_entry__cmp_wdiff_idx; 1849 break; 1850 case COMPUTE_DELTA_ABS: 1851 fmt->sort = hist_entry__cmp_delta_abs_idx; 1852 break; 1853 case COMPUTE_CYCLES: 1854 /* 1855 * Should set since 'fmt->sort' is called without 1856 * checking valid during sorting 1857 */ 1858 fmt->sort = hist_entry__cmp_nop; 1859 break; 1860 default: 1861 BUG_ON(1); 1862 } 1863 1864 perf_hpp__prepend_sort_field(fmt); 1865 return 0; 1866 } 1867 1868 static int data_init(int argc, const char **argv) 1869 { 1870 struct data__file *d; 1871 static const char *defaults[] = { 1872 "perf.data.old", 1873 "perf.data", 1874 }; 1875 bool use_default = true; 1876 int i; 1877 1878 data__files_cnt = 2; 1879 1880 if (argc) { 1881 if (argc == 1) 1882 defaults[1] = argv[0]; 1883 else { 1884 data__files_cnt = argc; 1885 use_default = false; 1886 } 1887 } else if (perf_guest) { 1888 defaults[0] = "perf.data.host"; 1889 defaults[1] = "perf.data.guest"; 1890 } 1891 1892 if (sort_compute >= (unsigned int) data__files_cnt) { 1893 pr_err("Order option out of limit.\n"); 1894 return -EINVAL; 1895 } 1896 1897 data__files = zalloc(sizeof(*data__files) * data__files_cnt); 1898 if (!data__files) 1899 return -ENOMEM; 1900 1901 data__for_each_file(i, d) { 1902 struct perf_data *data = &d->data; 1903 1904 data->path = use_default ? defaults[i] : argv[i]; 1905 data->mode = PERF_DATA_MODE_READ; 1906 data->force = force; 1907 1908 d->idx = i; 1909 } 1910 1911 return 0; 1912 } 1913 1914 static int diff__config(const char *var, const char *value, 1915 void *cb __maybe_unused) 1916 { 1917 if (!strcmp(var, "diff.order")) { 1918 int ret; 1919 if (perf_config_int(&ret, var, value) < 0) 1920 return -1; 1921 sort_compute = ret; 1922 return 0; 1923 } 1924 if (!strcmp(var, "diff.compute")) { 1925 if (!strcmp(value, "delta")) { 1926 compute = COMPUTE_DELTA; 1927 } else if (!strcmp(value, "delta-abs")) { 1928 compute = COMPUTE_DELTA_ABS; 1929 } else if (!strcmp(value, "ratio")) { 1930 compute = COMPUTE_RATIO; 1931 } else if (!strcmp(value, "wdiff")) { 1932 compute = COMPUTE_WEIGHTED_DIFF; 1933 } else { 1934 pr_err("Invalid compute method: %s\n", value); 1935 return -1; 1936 } 1937 } 1938 1939 return 0; 1940 } 1941 1942 int cmd_diff(int argc, const char **argv) 1943 { 1944 int ret = hists__init(); 1945 1946 if (ret < 0) 1947 return ret; 1948 1949 perf_tool__init(&pdiff.tool, /*ordered_events=*/true); 1950 pdiff.tool.sample = diff__process_sample_event; 1951 pdiff.tool.mmap = perf_event__process_mmap; 1952 pdiff.tool.mmap2 = perf_event__process_mmap2; 1953 pdiff.tool.comm = perf_event__process_comm; 1954 pdiff.tool.exit = perf_event__process_exit; 1955 pdiff.tool.fork = perf_event__process_fork; 1956 pdiff.tool.lost = perf_event__process_lost; 1957 pdiff.tool.namespaces = perf_event__process_namespaces; 1958 pdiff.tool.cgroup = perf_event__process_cgroup; 1959 pdiff.tool.ordering_requires_timestamps = true; 1960 1961 perf_config(diff__config, NULL); 1962 1963 argc = parse_options(argc, argv, options, diff_usage, 0); 1964 1965 if (quiet) 1966 perf_quiet_option(); 1967 1968 if (cycles_hist && (compute != COMPUTE_CYCLES)) 1969 usage_with_options(diff_usage, options); 1970 1971 if (pdiff.stream) 1972 compute = COMPUTE_STREAM; 1973 1974 symbol__annotation_init(); 1975 1976 if (symbol__init(NULL) < 0) 1977 return -1; 1978 1979 if (data_init(argc, argv) < 0) 1980 return -1; 1981 1982 if (check_file_brstack() < 0) 1983 return -1; 1984 1985 if ((compute == COMPUTE_CYCLES || compute == COMPUTE_STREAM) 1986 && !pdiff.has_br_stack) { 1987 return -1; 1988 } 1989 1990 if (compute == COMPUTE_STREAM) { 1991 symbol_conf.show_branchflag_count = true; 1992 symbol_conf.disable_add2line_warn = true; 1993 callchain_param.mode = CHAIN_FLAT; 1994 callchain_param.key = CCKEY_SRCLINE; 1995 callchain_param.branch_callstack = 1; 1996 symbol_conf.use_callchain = true; 1997 callchain_register_param(&callchain_param); 1998 sort_order = "srcline,symbol,dso"; 1999 } else { 2000 if (ui_init() < 0) 2001 return -1; 2002 2003 sort__mode = SORT_MODE__DIFF; 2004 } 2005 2006 if (setup_sorting(NULL) < 0) 2007 usage_with_options(diff_usage, options); 2008 2009 setup_pager(); 2010 2011 sort__setup_elide(NULL); 2012 2013 return __cmd_diff(); 2014 } 2015