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