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