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