at v5.7-rc2 45 kB view raw
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 38struct 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. */ 48enum { 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 63struct diff_hpp_fmt { 64 struct perf_hpp_fmt fmt; 65 int idx; 66 char *header; 67 int header_width; 68}; 69 70struct 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 78static struct data__file *data__files; 79static 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 89static bool force; 90static bool show_period; 91static bool show_formula; 92static bool show_baseline_only; 93static bool cycles_hist; 94static unsigned int sort_compute = 1; 95 96static s64 compute_wdiff_w1; 97static s64 compute_wdiff_w2; 98 99static const char *cpu_list; 100static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 101 102enum { 103 COMPUTE_DELTA, 104 COMPUTE_RATIO, 105 COMPUTE_WEIGHTED_DIFF, 106 COMPUTE_DELTA_ABS, 107 COMPUTE_CYCLES, 108 COMPUTE_MAX, 109}; 110 111const 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 119static int compute = COMPUTE_DELTA_ABS; 120 121static 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 131static 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 176static 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 212static 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 225static 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 268static 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 275static 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 285static 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 295static 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 307static 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 324static 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 333static 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 344static 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 362static 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 373static 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 382struct hist_entry_ops block_hist_ops = { 383 .new = block_hist_zalloc, 384 .free = block_hist_free, 385}; 386 387static 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; 443out_put: 444 addr_location__put(&al); 445 return ret; 446} 447 448static 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 464static 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 (perf_evsel__match2(evsel, e)) 471 return e; 472 } 473 474 return NULL; 475} 476 477static 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 488static 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 497static struct hist_entry* 498get_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 511static struct hist_entry* 512get_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 519static 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 541static 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 556static 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 562static 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 576static 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 597static void init_spark_values(unsigned long *svals, int num) 598{ 599 for (int i = 0; i < num; i++) 600 svals[i] = 0; 601} 602 603static 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 612static 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 643static 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 663static 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 727static 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 737static 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 777static int64_t 778hist_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 807static int64_t 808hist_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 838static int64_t 839hist_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 846static int64_t 847hist_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 855static int64_t 856hist_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 864static int64_t 865hist_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 873static int64_t 874hist_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 882static int64_t 883hist_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 891static int64_t 892hist_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 899static int64_t 900hist_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 907static int64_t 908hist_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 915static int64_t 916hist_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 923static 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 938static 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 953static 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 perf_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 perf_evsel__reset_sample_bit(evsel_base, CALLCHAIN); 994 995 hists__process(hists_base); 996 } 997} 998 999static 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 1010static 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 1024static 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 1063static 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 1074static 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 1087static 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 1112static 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 1177static const char * const diff_usage[] = { 1178 "perf diff [<options>] [old_file] [new_file]", 1179 NULL, 1180}; 1181 1182static 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 1234static 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 1241static 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 1258static 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 1270static 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 1314static 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 } 1375dummy_print: 1376 return scnprintf(hpp->buf, hpp->size, "%*s", 1377 dfmt->header_width, "N/A"); 1378no_print: 1379 return scnprintf(hpp->buf, hpp->size, "%*s", 1380 dfmt->header_width, pfmt); 1381} 1382 1383static 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 1389static 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 1395static 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 1401static 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 1407static 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 1417static 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 1434static 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 1486no_print: 1487 return scnprintf(hpp->buf, hpp->size, "%*s", 1488 dfmt->header_width, " "); 1489} 1490 1491static void 1492hpp__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 1504static void 1505hpp__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 1568static 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 1586static 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 1602static 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 1614static 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 1625static 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 1657static 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 1710static 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 1799static 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 1845static 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 1873int 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}