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