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