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 int block_pair_cmp(struct hist_entry *a, struct hist_entry *b) 576 { 577 struct block_info *bi_a = a->block_info; 578 struct block_info *bi_b = b->block_info; 579 int cmp; 580 581 if (!bi_a->sym || !bi_b->sym) 582 return -1; 583 584 cmp = strcmp(bi_a->sym->name, bi_b->sym->name); 585 586 if ((!cmp) && (bi_a->start == bi_b->start) && (bi_a->end == bi_b->end)) 587 return 0; 588 589 return -1; 590 } 591 592 static struct hist_entry *get_block_pair(struct hist_entry *he, 593 struct hists *hists_pair) 594 { 595 struct rb_root_cached *root = hists_pair->entries_in; 596 struct rb_node *next = rb_first_cached(root); 597 int cmp; 598 599 while (next != NULL) { 600 struct hist_entry *he_pair = rb_entry(next, struct hist_entry, 601 rb_node_in); 602 603 next = rb_next(&he_pair->rb_node_in); 604 605 cmp = block_pair_cmp(he_pair, he); 606 if (!cmp) 607 return he_pair; 608 } 609 610 return NULL; 611 } 612 613 static void init_spark_values(unsigned long *svals, int num) 614 { 615 for (int i = 0; i < num; i++) 616 svals[i] = 0; 617 } 618 619 static void update_spark_value(unsigned long *svals, int num, 620 struct stats *stats, u64 val) 621 { 622 int n = stats->n; 623 624 if (n < num) 625 svals[n] = val; 626 } 627 628 static void compute_cycles_diff(struct hist_entry *he, 629 struct hist_entry *pair) 630 { 631 pair->diff.computed = true; 632 if (pair->block_info->num && he->block_info->num) { 633 pair->diff.cycles = 634 pair->block_info->cycles_aggr / pair->block_info->num_aggr - 635 he->block_info->cycles_aggr / he->block_info->num_aggr; 636 637 if (!cycles_hist) 638 return; 639 640 init_stats(&pair->diff.stats); 641 init_spark_values(pair->diff.svals, NUM_SPARKS); 642 643 for (int i = 0; i < pair->block_info->num; i++) { 644 u64 val; 645 646 if (i >= he->block_info->num || i >= NUM_SPARKS) 647 break; 648 649 val = llabs(pair->block_info->cycles_spark[i] - 650 he->block_info->cycles_spark[i]); 651 652 update_spark_value(pair->diff.svals, NUM_SPARKS, 653 &pair->diff.stats, val); 654 update_stats(&pair->diff.stats, val); 655 } 656 } 657 } 658 659 static void block_hists_match(struct hists *hists_base, 660 struct hists *hists_pair) 661 { 662 struct rb_root_cached *root = hists_base->entries_in; 663 struct rb_node *next = rb_first_cached(root); 664 665 while (next != NULL) { 666 struct hist_entry *he = rb_entry(next, struct hist_entry, 667 rb_node_in); 668 struct hist_entry *pair = get_block_pair(he, hists_pair); 669 670 next = rb_next(&he->rb_node_in); 671 672 if (pair) { 673 hist_entry__add_pair(pair, he); 674 compute_cycles_diff(he, pair); 675 } 676 } 677 } 678 679 static void hists__precompute(struct hists *hists) 680 { 681 struct rb_root_cached *root; 682 struct rb_node *next; 683 684 if (hists__has(hists, need_collapse)) 685 root = &hists->entries_collapsed; 686 else 687 root = hists->entries_in; 688 689 next = rb_first_cached(root); 690 while (next != NULL) { 691 struct block_hist *bh, *pair_bh; 692 struct hist_entry *he, *pair; 693 struct data__file *d; 694 int i; 695 696 he = rb_entry(next, struct hist_entry, rb_node_in); 697 next = rb_next(&he->rb_node_in); 698 699 if (compute == COMPUTE_CYCLES) { 700 bh = container_of(he, struct block_hist, he); 701 init_block_hist(bh); 702 block_info__process_sym(he, bh, NULL, 0); 703 } 704 705 data__for_each_file_new(i, d) { 706 pair = get_pair_data(he, d); 707 if (!pair) 708 continue; 709 710 switch (compute) { 711 case COMPUTE_DELTA: 712 case COMPUTE_DELTA_ABS: 713 compute_delta(he, pair); 714 break; 715 case COMPUTE_RATIO: 716 compute_ratio(he, pair); 717 break; 718 case COMPUTE_WEIGHTED_DIFF: 719 compute_wdiff(he, pair); 720 break; 721 case COMPUTE_CYCLES: 722 pair_bh = container_of(pair, struct block_hist, 723 he); 724 init_block_hist(pair_bh); 725 block_info__process_sym(pair, pair_bh, NULL, 0); 726 727 bh = container_of(he, struct block_hist, he); 728 729 if (bh->valid && pair_bh->valid) { 730 block_hists_match(&bh->block_hists, 731 &pair_bh->block_hists); 732 hists__output_resort(&pair_bh->block_hists, 733 NULL); 734 } 735 break; 736 default: 737 BUG_ON(1); 738 } 739 } 740 } 741 } 742 743 static int64_t cmp_doubles(double l, double r) 744 { 745 if (l > r) 746 return -1; 747 else if (l < r) 748 return 1; 749 else 750 return 0; 751 } 752 753 static int64_t 754 __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, 755 int c) 756 { 757 switch (c) { 758 case COMPUTE_DELTA: 759 { 760 double l = left->diff.period_ratio_delta; 761 double r = right->diff.period_ratio_delta; 762 763 return cmp_doubles(l, r); 764 } 765 case COMPUTE_DELTA_ABS: 766 { 767 double l = fabs(left->diff.period_ratio_delta); 768 double r = fabs(right->diff.period_ratio_delta); 769 770 return cmp_doubles(l, r); 771 } 772 case COMPUTE_RATIO: 773 { 774 double l = left->diff.period_ratio; 775 double r = right->diff.period_ratio; 776 777 return cmp_doubles(l, r); 778 } 779 case COMPUTE_WEIGHTED_DIFF: 780 { 781 s64 l = left->diff.wdiff; 782 s64 r = right->diff.wdiff; 783 784 return r - l; 785 } 786 default: 787 BUG_ON(1); 788 } 789 790 return 0; 791 } 792 793 static int64_t 794 hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, 795 int c, int sort_idx) 796 { 797 bool pairs_left = hist_entry__has_pairs(left); 798 bool pairs_right = hist_entry__has_pairs(right); 799 struct hist_entry *p_right, *p_left; 800 801 if (!pairs_left && !pairs_right) 802 return 0; 803 804 if (!pairs_left || !pairs_right) 805 return pairs_left ? -1 : 1; 806 807 p_left = get_pair_data(left, &data__files[sort_idx]); 808 p_right = get_pair_data(right, &data__files[sort_idx]); 809 810 if (!p_left && !p_right) 811 return 0; 812 813 if (!p_left || !p_right) 814 return p_left ? -1 : 1; 815 816 /* 817 * We have 2 entries of same kind, let's 818 * make the data comparison. 819 */ 820 return __hist_entry__cmp_compute(p_left, p_right, c); 821 } 822 823 static int64_t 824 hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right, 825 int c, int sort_idx) 826 { 827 struct hist_entry *p_right, *p_left; 828 829 p_left = get_pair_data(left, &data__files[sort_idx]); 830 p_right = get_pair_data(right, &data__files[sort_idx]); 831 832 if (!p_left && !p_right) 833 return 0; 834 835 if (!p_left || !p_right) 836 return p_left ? -1 : 1; 837 838 if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) { 839 /* 840 * The delta can be computed without the baseline, but 841 * others are not. Put those entries which have no 842 * values below. 843 */ 844 if (left->dummy && right->dummy) 845 return 0; 846 847 if (left->dummy || right->dummy) 848 return left->dummy ? 1 : -1; 849 } 850 851 return __hist_entry__cmp_compute(p_left, p_right, c); 852 } 853 854 static int64_t 855 hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused, 856 struct hist_entry *left __maybe_unused, 857 struct hist_entry *right __maybe_unused) 858 { 859 return 0; 860 } 861 862 static int64_t 863 hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused, 864 struct hist_entry *left, struct hist_entry *right) 865 { 866 if (left->stat.period == right->stat.period) 867 return 0; 868 return left->stat.period > right->stat.period ? 1 : -1; 869 } 870 871 static int64_t 872 hist_entry__cmp_delta(struct perf_hpp_fmt *fmt, 873 struct hist_entry *left, struct hist_entry *right) 874 { 875 struct data__file *d = fmt_to_data_file(fmt); 876 877 return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx); 878 } 879 880 static int64_t 881 hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt, 882 struct hist_entry *left, struct hist_entry *right) 883 { 884 struct data__file *d = fmt_to_data_file(fmt); 885 886 return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx); 887 } 888 889 static int64_t 890 hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt, 891 struct hist_entry *left, struct hist_entry *right) 892 { 893 struct data__file *d = fmt_to_data_file(fmt); 894 895 return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx); 896 } 897 898 static int64_t 899 hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt, 900 struct hist_entry *left, struct hist_entry *right) 901 { 902 struct data__file *d = fmt_to_data_file(fmt); 903 904 return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx); 905 } 906 907 static int64_t 908 hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused, 909 struct hist_entry *left, struct hist_entry *right) 910 { 911 return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA, 912 sort_compute); 913 } 914 915 static int64_t 916 hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused, 917 struct hist_entry *left, struct hist_entry *right) 918 { 919 return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS, 920 sort_compute); 921 } 922 923 static int64_t 924 hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused, 925 struct hist_entry *left, struct hist_entry *right) 926 { 927 return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO, 928 sort_compute); 929 } 930 931 static int64_t 932 hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused, 933 struct hist_entry *left, struct hist_entry *right) 934 { 935 return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF, 936 sort_compute); 937 } 938 939 static void hists__process(struct hists *hists) 940 { 941 if (show_baseline_only) 942 hists__baseline_only(hists); 943 944 hists__precompute(hists); 945 hists__output_resort(hists, NULL); 946 947 if (compute == COMPUTE_CYCLES) 948 symbol_conf.report_block = true; 949 950 hists__fprintf(hists, !quiet, 0, 0, 0, stdout, 951 !symbol_conf.use_callchain); 952 } 953 954 static void data__fprintf(void) 955 { 956 struct data__file *d; 957 int i; 958 959 fprintf(stdout, "# Data files:\n"); 960 961 data__for_each_file(i, d) 962 fprintf(stdout, "# [%d] %s %s\n", 963 d->idx, d->data.path, 964 !d->idx ? "(Baseline)" : ""); 965 966 fprintf(stdout, "#\n"); 967 } 968 969 static void data_process(void) 970 { 971 struct evlist *evlist_base = data__files[0].session->evlist; 972 struct evsel *evsel_base; 973 bool first = true; 974 975 evlist__for_each_entry(evlist_base, evsel_base) { 976 struct hists *hists_base = evsel__hists(evsel_base); 977 struct data__file *d; 978 int i; 979 980 data__for_each_file_new(i, d) { 981 struct evlist *evlist = d->session->evlist; 982 struct evsel *evsel; 983 struct hists *hists; 984 985 evsel = evsel_match(evsel_base, evlist); 986 if (!evsel) 987 continue; 988 989 hists = evsel__hists(evsel); 990 d->hists = hists; 991 992 hists__match(hists_base, hists); 993 994 if (!show_baseline_only) 995 hists__link(hists_base, hists); 996 } 997 998 if (!quiet) { 999 fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", 1000 perf_evsel__name(evsel_base)); 1001 } 1002 1003 first = false; 1004 1005 if (verbose > 0 || ((data__files_cnt > 2) && !quiet)) 1006 data__fprintf(); 1007 1008 /* Don't sort callchain for perf diff */ 1009 perf_evsel__reset_sample_bit(evsel_base, CALLCHAIN); 1010 1011 hists__process(hists_base); 1012 } 1013 } 1014 1015 static void data__free(struct data__file *d) 1016 { 1017 int col; 1018 1019 for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) { 1020 struct diff_hpp_fmt *fmt = &d->fmt[col]; 1021 1022 zfree(&fmt->header); 1023 } 1024 } 1025 1026 static int abstime_str_dup(char **pstr) 1027 { 1028 char *str = NULL; 1029 1030 if (pdiff.time_str && strchr(pdiff.time_str, ':')) { 1031 str = strdup(pdiff.time_str); 1032 if (!str) 1033 return -ENOMEM; 1034 } 1035 1036 *pstr = str; 1037 return 0; 1038 } 1039 1040 static int parse_absolute_time(struct data__file *d, char **pstr) 1041 { 1042 char *p = *pstr; 1043 int ret; 1044 1045 /* 1046 * Absolute timestamp for one file has the format: a.b,c.d 1047 * For multiple files, the format is: a.b,c.d:a.b,c.d 1048 */ 1049 p = strchr(*pstr, ':'); 1050 if (p) { 1051 if (p == *pstr) { 1052 pr_err("Invalid time string\n"); 1053 return -EINVAL; 1054 } 1055 1056 *p = 0; 1057 p++; 1058 if (*p == 0) { 1059 pr_err("Invalid time string\n"); 1060 return -EINVAL; 1061 } 1062 } 1063 1064 ret = perf_time__parse_for_ranges(*pstr, d->session, 1065 &pdiff.ptime_range, 1066 &pdiff.range_size, 1067 &pdiff.range_num); 1068 if (ret < 0) 1069 return ret; 1070 1071 if (!p || *p == 0) 1072 *pstr = NULL; 1073 else 1074 *pstr = p; 1075 1076 return ret; 1077 } 1078 1079 static int parse_percent_time(struct data__file *d) 1080 { 1081 int ret; 1082 1083 ret = perf_time__parse_for_ranges(pdiff.time_str, d->session, 1084 &pdiff.ptime_range, 1085 &pdiff.range_size, 1086 &pdiff.range_num); 1087 return ret; 1088 } 1089 1090 static int parse_time_str(struct data__file *d, char *abstime_ostr, 1091 char **pabstime_tmp) 1092 { 1093 int ret = 0; 1094 1095 if (abstime_ostr) 1096 ret = parse_absolute_time(d, pabstime_tmp); 1097 else if (pdiff.time_str) 1098 ret = parse_percent_time(d); 1099 1100 return ret; 1101 } 1102 1103 static int check_file_brstack(void) 1104 { 1105 struct data__file *d; 1106 bool has_br_stack; 1107 int i; 1108 1109 data__for_each_file(i, d) { 1110 d->session = perf_session__new(&d->data, false, &pdiff.tool); 1111 if (IS_ERR(d->session)) { 1112 pr_err("Failed to open %s\n", d->data.path); 1113 return PTR_ERR(d->session); 1114 } 1115 1116 has_br_stack = perf_header__has_feat(&d->session->header, 1117 HEADER_BRANCH_STACK); 1118 perf_session__delete(d->session); 1119 if (!has_br_stack) 1120 return 0; 1121 } 1122 1123 /* Set only all files having branch stacks */ 1124 pdiff.has_br_stack = true; 1125 return 0; 1126 } 1127 1128 static int __cmd_diff(void) 1129 { 1130 struct data__file *d; 1131 int ret, i; 1132 char *abstime_ostr, *abstime_tmp; 1133 1134 ret = abstime_str_dup(&abstime_ostr); 1135 if (ret) 1136 return ret; 1137 1138 abstime_tmp = abstime_ostr; 1139 ret = -EINVAL; 1140 1141 data__for_each_file(i, d) { 1142 d->session = perf_session__new(&d->data, false, &pdiff.tool); 1143 if (IS_ERR(d->session)) { 1144 ret = PTR_ERR(d->session); 1145 pr_err("Failed to open %s\n", d->data.path); 1146 goto out_delete; 1147 } 1148 1149 if (pdiff.time_str) { 1150 ret = parse_time_str(d, abstime_ostr, &abstime_tmp); 1151 if (ret < 0) 1152 goto out_delete; 1153 } 1154 1155 if (cpu_list) { 1156 ret = perf_session__cpu_bitmap(d->session, cpu_list, 1157 cpu_bitmap); 1158 if (ret < 0) 1159 goto out_delete; 1160 } 1161 1162 ret = perf_session__process_events(d->session); 1163 if (ret) { 1164 pr_err("Failed to process %s\n", d->data.path); 1165 goto out_delete; 1166 } 1167 1168 perf_evlist__collapse_resort(d->session->evlist); 1169 1170 if (pdiff.ptime_range) 1171 zfree(&pdiff.ptime_range); 1172 } 1173 1174 data_process(); 1175 1176 out_delete: 1177 data__for_each_file(i, d) { 1178 perf_session__delete(d->session); 1179 data__free(d); 1180 } 1181 1182 free(data__files); 1183 1184 if (pdiff.ptime_range) 1185 zfree(&pdiff.ptime_range); 1186 1187 if (abstime_ostr) 1188 free(abstime_ostr); 1189 1190 return ret; 1191 } 1192 1193 static const char * const diff_usage[] = { 1194 "perf diff [<options>] [old_file] [new_file]", 1195 NULL, 1196 }; 1197 1198 static const struct option options[] = { 1199 OPT_INCR('v', "verbose", &verbose, 1200 "be more verbose (show symbol address, etc)"), 1201 OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"), 1202 OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, 1203 "Show only items with match in baseline"), 1204 OPT_CALLBACK('c', "compute", &compute, 1205 "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs),cycles", 1206 "Entries differential computation selection", 1207 setup_compute), 1208 OPT_BOOLEAN('p', "period", &show_period, 1209 "Show period values."), 1210 OPT_BOOLEAN('F', "formula", &show_formula, 1211 "Show formula."), 1212 OPT_BOOLEAN(0, "cycles-hist", &cycles_hist, 1213 "Show cycles histogram and standard deviation " 1214 "- WARNING: use only with -c cycles."), 1215 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 1216 "dump raw trace in ASCII"), 1217 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 1218 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, 1219 "file", "kallsyms pathname"), 1220 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, 1221 "load module symbols - WARNING: use only with -k and LIVE kernel"), 1222 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", 1223 "only consider symbols in these dsos"), 1224 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", 1225 "only consider symbols in these comms"), 1226 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", 1227 "only consider these symbols"), 1228 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 1229 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." 1230 " Please refer the man page for the complete list."), 1231 OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator", 1232 "separator for columns, no spaces will be added between " 1233 "columns '.' is reserved."), 1234 OPT_CALLBACK(0, "symfs", NULL, "directory", 1235 "Look for files with symbols relative to this directory", 1236 symbol__config_symfs), 1237 OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), 1238 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", 1239 "How to display percentage of filtered entries", parse_filter_percentage), 1240 OPT_STRING(0, "time", &pdiff.time_str, "str", 1241 "Time span (time percent or absolute timestamp)"), 1242 OPT_STRING(0, "cpu", &cpu_list, "cpu", "list of cpus to profile"), 1243 OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]", 1244 "only consider symbols in these pids"), 1245 OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]", 1246 "only consider symbols in these tids"), 1247 OPT_END() 1248 }; 1249 1250 static double baseline_percent(struct hist_entry *he) 1251 { 1252 u64 total = hists__total_period(he->hists); 1253 1254 return 100.0 * he->stat.period / total; 1255 } 1256 1257 static int hpp__color_baseline(struct perf_hpp_fmt *fmt, 1258 struct perf_hpp *hpp, struct hist_entry *he) 1259 { 1260 struct diff_hpp_fmt *dfmt = 1261 container_of(fmt, struct diff_hpp_fmt, fmt); 1262 double percent = baseline_percent(he); 1263 char pfmt[20] = " "; 1264 1265 if (!he->dummy) { 1266 scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1); 1267 return percent_color_snprintf(hpp->buf, hpp->size, 1268 pfmt, percent); 1269 } else 1270 return scnprintf(hpp->buf, hpp->size, "%*s", 1271 dfmt->header_width, pfmt); 1272 } 1273 1274 static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size) 1275 { 1276 double percent = baseline_percent(he); 1277 const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; 1278 int ret = 0; 1279 1280 if (!he->dummy) 1281 ret = scnprintf(buf, size, fmt, percent); 1282 1283 return ret; 1284 } 1285 1286 static int cycles_printf(struct hist_entry *he, struct hist_entry *pair, 1287 struct perf_hpp *hpp, int width) 1288 { 1289 struct block_hist *bh = container_of(he, struct block_hist, he); 1290 struct block_hist *bh_pair = container_of(pair, struct block_hist, he); 1291 struct hist_entry *block_he; 1292 struct block_info *bi; 1293 char buf[128]; 1294 char *start_line, *end_line; 1295 1296 block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx); 1297 if (!block_he) { 1298 hpp->skip = true; 1299 return 0; 1300 } 1301 1302 /* 1303 * Avoid printing the warning "addr2line_init failed for ..." 1304 */ 1305 symbol_conf.disable_add2line_warn = true; 1306 1307 bi = block_he->block_info; 1308 1309 start_line = map__srcline(he->ms.map, bi->sym->start + bi->start, 1310 he->ms.sym); 1311 1312 end_line = map__srcline(he->ms.map, bi->sym->start + bi->end, 1313 he->ms.sym); 1314 1315 if ((start_line != SRCLINE_UNKNOWN) && (end_line != SRCLINE_UNKNOWN)) { 1316 scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld", 1317 start_line, end_line, block_he->diff.cycles); 1318 } else { 1319 scnprintf(buf, sizeof(buf), "[%7lx -> %7lx] %4ld", 1320 bi->start, bi->end, block_he->diff.cycles); 1321 } 1322 1323 free_srcline(start_line); 1324 free_srcline(end_line); 1325 1326 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf); 1327 } 1328 1329 static int __hpp__color_compare(struct perf_hpp_fmt *fmt, 1330 struct perf_hpp *hpp, struct hist_entry *he, 1331 int comparison_method) 1332 { 1333 struct diff_hpp_fmt *dfmt = 1334 container_of(fmt, struct diff_hpp_fmt, fmt); 1335 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1336 double diff; 1337 s64 wdiff; 1338 char pfmt[20] = " "; 1339 1340 if (!pair) { 1341 if (comparison_method == COMPUTE_CYCLES) { 1342 struct block_hist *bh; 1343 1344 bh = container_of(he, struct block_hist, he); 1345 if (bh->block_idx) 1346 hpp->skip = true; 1347 } 1348 1349 goto no_print; 1350 } 1351 1352 switch (comparison_method) { 1353 case COMPUTE_DELTA: 1354 if (pair->diff.computed) 1355 diff = pair->diff.period_ratio_delta; 1356 else 1357 diff = compute_delta(he, pair); 1358 1359 scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1); 1360 return percent_color_snprintf(hpp->buf, hpp->size, 1361 pfmt, diff); 1362 case COMPUTE_RATIO: 1363 if (he->dummy) 1364 goto dummy_print; 1365 if (pair->diff.computed) 1366 diff = pair->diff.period_ratio; 1367 else 1368 diff = compute_ratio(he, pair); 1369 1370 scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width); 1371 return value_color_snprintf(hpp->buf, hpp->size, 1372 pfmt, diff); 1373 case COMPUTE_WEIGHTED_DIFF: 1374 if (he->dummy) 1375 goto dummy_print; 1376 if (pair->diff.computed) 1377 wdiff = pair->diff.wdiff; 1378 else 1379 wdiff = compute_wdiff(he, pair); 1380 1381 scnprintf(pfmt, 20, "%%14ld", dfmt->header_width); 1382 return color_snprintf(hpp->buf, hpp->size, 1383 get_percent_color(wdiff), 1384 pfmt, wdiff); 1385 case COMPUTE_CYCLES: 1386 return cycles_printf(he, pair, hpp, dfmt->header_width); 1387 default: 1388 BUG_ON(1); 1389 } 1390 dummy_print: 1391 return scnprintf(hpp->buf, hpp->size, "%*s", 1392 dfmt->header_width, "N/A"); 1393 no_print: 1394 return scnprintf(hpp->buf, hpp->size, "%*s", 1395 dfmt->header_width, pfmt); 1396 } 1397 1398 static int hpp__color_delta(struct perf_hpp_fmt *fmt, 1399 struct perf_hpp *hpp, struct hist_entry *he) 1400 { 1401 return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA); 1402 } 1403 1404 static int hpp__color_ratio(struct perf_hpp_fmt *fmt, 1405 struct perf_hpp *hpp, struct hist_entry *he) 1406 { 1407 return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO); 1408 } 1409 1410 static int hpp__color_wdiff(struct perf_hpp_fmt *fmt, 1411 struct perf_hpp *hpp, struct hist_entry *he) 1412 { 1413 return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF); 1414 } 1415 1416 static int hpp__color_cycles(struct perf_hpp_fmt *fmt, 1417 struct perf_hpp *hpp, struct hist_entry *he) 1418 { 1419 return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES); 1420 } 1421 1422 static int all_zero(unsigned long *vals, int len) 1423 { 1424 int i; 1425 1426 for (i = 0; i < len; i++) 1427 if (vals[i] != 0) 1428 return 0; 1429 return 1; 1430 } 1431 1432 static int print_cycles_spark(char *bf, int size, unsigned long *svals, u64 n) 1433 { 1434 int printed; 1435 1436 if (n <= 1) 1437 return 0; 1438 1439 if (n > NUM_SPARKS) 1440 n = NUM_SPARKS; 1441 if (all_zero(svals, n)) 1442 return 0; 1443 1444 printed = print_spark(bf, size, svals, n); 1445 printed += scnprintf(bf + printed, size - printed, " "); 1446 return printed; 1447 } 1448 1449 static int hpp__color_cycles_hist(struct perf_hpp_fmt *fmt, 1450 struct perf_hpp *hpp, struct hist_entry *he) 1451 { 1452 struct diff_hpp_fmt *dfmt = 1453 container_of(fmt, struct diff_hpp_fmt, fmt); 1454 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1455 struct block_hist *bh = container_of(he, struct block_hist, he); 1456 struct block_hist *bh_pair; 1457 struct hist_entry *block_he; 1458 char spark[32], buf[128]; 1459 double r; 1460 int ret, pad; 1461 1462 if (!pair) { 1463 if (bh->block_idx) 1464 hpp->skip = true; 1465 1466 goto no_print; 1467 } 1468 1469 bh_pair = container_of(pair, struct block_hist, he); 1470 1471 block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx); 1472 if (!block_he) { 1473 hpp->skip = true; 1474 goto no_print; 1475 } 1476 1477 ret = print_cycles_spark(spark, sizeof(spark), block_he->diff.svals, 1478 block_he->diff.stats.n); 1479 1480 r = rel_stddev_stats(stddev_stats(&block_he->diff.stats), 1481 avg_stats(&block_he->diff.stats)); 1482 1483 if (ret) { 1484 /* 1485 * Padding spaces if number of sparks less than NUM_SPARKS 1486 * otherwise the output is not aligned. 1487 */ 1488 pad = NUM_SPARKS - ((ret - 1) / 3); 1489 scnprintf(buf, sizeof(buf), "%s%5.1f%% %s", "\u00B1", r, spark); 1490 ret = scnprintf(hpp->buf, hpp->size, "%*s", 1491 dfmt->header_width, buf); 1492 1493 if (pad) { 1494 ret += scnprintf(hpp->buf + ret, hpp->size - ret, 1495 "%-*s", pad, " "); 1496 } 1497 1498 return ret; 1499 } 1500 1501 no_print: 1502 return scnprintf(hpp->buf, hpp->size, "%*s", 1503 dfmt->header_width, " "); 1504 } 1505 1506 static void 1507 hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size) 1508 { 1509 switch (idx) { 1510 case PERF_HPP_DIFF__PERIOD_BASELINE: 1511 scnprintf(buf, size, "%" PRIu64, he->stat.period); 1512 break; 1513 1514 default: 1515 break; 1516 } 1517 } 1518 1519 static void 1520 hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, 1521 int idx, char *buf, size_t size) 1522 { 1523 double diff; 1524 double ratio; 1525 s64 wdiff; 1526 1527 switch (idx) { 1528 case PERF_HPP_DIFF__DELTA: 1529 case PERF_HPP_DIFF__DELTA_ABS: 1530 if (pair->diff.computed) 1531 diff = pair->diff.period_ratio_delta; 1532 else 1533 diff = compute_delta(he, pair); 1534 1535 scnprintf(buf, size, "%+4.2F%%", diff); 1536 break; 1537 1538 case PERF_HPP_DIFF__RATIO: 1539 /* No point for ratio 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 ratio = pair->diff.period_ratio; 1547 else 1548 ratio = compute_ratio(he, pair); 1549 1550 if (ratio > 0.0) 1551 scnprintf(buf, size, "%14.6F", ratio); 1552 break; 1553 1554 case PERF_HPP_DIFF__WEIGHTED_DIFF: 1555 /* No point for wdiff number if we are dummy.. */ 1556 if (he->dummy) { 1557 scnprintf(buf, size, "N/A"); 1558 break; 1559 } 1560 1561 if (pair->diff.computed) 1562 wdiff = pair->diff.wdiff; 1563 else 1564 wdiff = compute_wdiff(he, pair); 1565 1566 if (wdiff != 0) 1567 scnprintf(buf, size, "%14ld", wdiff); 1568 break; 1569 1570 case PERF_HPP_DIFF__FORMULA: 1571 formula_fprintf(he, pair, buf, size); 1572 break; 1573 1574 case PERF_HPP_DIFF__PERIOD: 1575 scnprintf(buf, size, "%" PRIu64, pair->stat.period); 1576 break; 1577 1578 default: 1579 BUG_ON(1); 1580 }; 1581 } 1582 1583 static void 1584 __hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt, 1585 char *buf, size_t size) 1586 { 1587 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1588 int idx = dfmt->idx; 1589 1590 /* baseline is special */ 1591 if (idx == PERF_HPP_DIFF__BASELINE) 1592 hpp__entry_baseline(he, buf, size); 1593 else { 1594 if (pair) 1595 hpp__entry_pair(he, pair, idx, buf, size); 1596 else 1597 hpp__entry_unpair(he, idx, buf, size); 1598 } 1599 } 1600 1601 static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp, 1602 struct hist_entry *he) 1603 { 1604 struct diff_hpp_fmt *dfmt = 1605 container_of(_fmt, struct diff_hpp_fmt, fmt); 1606 char buf[MAX_COL_WIDTH] = " "; 1607 1608 __hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH); 1609 1610 if (symbol_conf.field_sep) 1611 return scnprintf(hpp->buf, hpp->size, "%s", buf); 1612 else 1613 return scnprintf(hpp->buf, hpp->size, "%*s", 1614 dfmt->header_width, buf); 1615 } 1616 1617 static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 1618 struct hists *hists __maybe_unused, 1619 int line __maybe_unused, 1620 int *span __maybe_unused) 1621 { 1622 struct diff_hpp_fmt *dfmt = 1623 container_of(fmt, struct diff_hpp_fmt, fmt); 1624 1625 BUG_ON(!dfmt->header); 1626 return scnprintf(hpp->buf, hpp->size, dfmt->header); 1627 } 1628 1629 static int hpp__width(struct perf_hpp_fmt *fmt, 1630 struct perf_hpp *hpp __maybe_unused, 1631 struct hists *hists __maybe_unused) 1632 { 1633 struct diff_hpp_fmt *dfmt = 1634 container_of(fmt, struct diff_hpp_fmt, fmt); 1635 1636 BUG_ON(dfmt->header_width <= 0); 1637 return dfmt->header_width; 1638 } 1639 1640 static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt) 1641 { 1642 #define MAX_HEADER_NAME 100 1643 char buf_indent[MAX_HEADER_NAME]; 1644 char buf[MAX_HEADER_NAME]; 1645 const char *header = NULL; 1646 int width = 0; 1647 1648 BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX); 1649 header = columns[dfmt->idx].name; 1650 width = columns[dfmt->idx].width; 1651 1652 /* Only our defined HPP fmts should appear here. */ 1653 BUG_ON(!header); 1654 1655 if (data__files_cnt > 2) 1656 scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx); 1657 1658 #define NAME (data__files_cnt > 2 ? buf : header) 1659 dfmt->header_width = width; 1660 width = (int) strlen(NAME); 1661 if (dfmt->header_width < width) 1662 dfmt->header_width = width; 1663 1664 scnprintf(buf_indent, MAX_HEADER_NAME, "%*s", 1665 dfmt->header_width, NAME); 1666 1667 dfmt->header = strdup(buf_indent); 1668 #undef MAX_HEADER_NAME 1669 #undef NAME 1670 } 1671 1672 static void data__hpp_register(struct data__file *d, int idx) 1673 { 1674 struct diff_hpp_fmt *dfmt = &d->fmt[idx]; 1675 struct perf_hpp_fmt *fmt = &dfmt->fmt; 1676 1677 dfmt->idx = idx; 1678 1679 fmt->header = hpp__header; 1680 fmt->width = hpp__width; 1681 fmt->entry = hpp__entry_global; 1682 fmt->cmp = hist_entry__cmp_nop; 1683 fmt->collapse = hist_entry__cmp_nop; 1684 1685 /* TODO more colors */ 1686 switch (idx) { 1687 case PERF_HPP_DIFF__BASELINE: 1688 fmt->color = hpp__color_baseline; 1689 fmt->sort = hist_entry__cmp_baseline; 1690 break; 1691 case PERF_HPP_DIFF__DELTA: 1692 fmt->color = hpp__color_delta; 1693 fmt->sort = hist_entry__cmp_delta; 1694 break; 1695 case PERF_HPP_DIFF__RATIO: 1696 fmt->color = hpp__color_ratio; 1697 fmt->sort = hist_entry__cmp_ratio; 1698 break; 1699 case PERF_HPP_DIFF__WEIGHTED_DIFF: 1700 fmt->color = hpp__color_wdiff; 1701 fmt->sort = hist_entry__cmp_wdiff; 1702 break; 1703 case PERF_HPP_DIFF__DELTA_ABS: 1704 fmt->color = hpp__color_delta; 1705 fmt->sort = hist_entry__cmp_delta_abs; 1706 break; 1707 case PERF_HPP_DIFF__CYCLES: 1708 fmt->color = hpp__color_cycles; 1709 fmt->sort = hist_entry__cmp_nop; 1710 break; 1711 case PERF_HPP_DIFF__CYCLES_HIST: 1712 fmt->color = hpp__color_cycles_hist; 1713 fmt->sort = hist_entry__cmp_nop; 1714 break; 1715 default: 1716 fmt->sort = hist_entry__cmp_nop; 1717 break; 1718 } 1719 1720 init_header(d, dfmt); 1721 perf_hpp__column_register(fmt); 1722 perf_hpp__register_sort_field(fmt); 1723 } 1724 1725 static int ui_init(void) 1726 { 1727 struct data__file *d; 1728 struct perf_hpp_fmt *fmt; 1729 int i; 1730 1731 data__for_each_file(i, d) { 1732 1733 /* 1734 * Baseline or compute realted columns: 1735 * 1736 * PERF_HPP_DIFF__BASELINE 1737 * PERF_HPP_DIFF__DELTA 1738 * PERF_HPP_DIFF__RATIO 1739 * PERF_HPP_DIFF__WEIGHTED_DIFF 1740 * PERF_HPP_DIFF__CYCLES 1741 */ 1742 data__hpp_register(d, i ? compute_2_hpp[compute] : 1743 PERF_HPP_DIFF__BASELINE); 1744 1745 if (cycles_hist && i) 1746 data__hpp_register(d, PERF_HPP_DIFF__CYCLES_HIST); 1747 1748 /* 1749 * And the rest: 1750 * 1751 * PERF_HPP_DIFF__FORMULA 1752 * PERF_HPP_DIFF__PERIOD 1753 * PERF_HPP_DIFF__PERIOD_BASELINE 1754 */ 1755 if (show_formula && i) 1756 data__hpp_register(d, PERF_HPP_DIFF__FORMULA); 1757 1758 if (show_period) 1759 data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD : 1760 PERF_HPP_DIFF__PERIOD_BASELINE); 1761 } 1762 1763 if (!sort_compute) 1764 return 0; 1765 1766 /* 1767 * Prepend an fmt to sort on columns at 'sort_compute' first. 1768 * This fmt is added only to the sort list but not to the 1769 * output fields list. 1770 * 1771 * Note that this column (data) can be compared twice - one 1772 * for this 'sort_compute' fmt and another for the normal 1773 * diff_hpp_fmt. But it shouldn't a problem as most entries 1774 * will be sorted out by first try or baseline and comparing 1775 * is not a costly operation. 1776 */ 1777 fmt = zalloc(sizeof(*fmt)); 1778 if (fmt == NULL) { 1779 pr_err("Memory allocation failed\n"); 1780 return -1; 1781 } 1782 1783 fmt->cmp = hist_entry__cmp_nop; 1784 fmt->collapse = hist_entry__cmp_nop; 1785 1786 switch (compute) { 1787 case COMPUTE_DELTA: 1788 fmt->sort = hist_entry__cmp_delta_idx; 1789 break; 1790 case COMPUTE_RATIO: 1791 fmt->sort = hist_entry__cmp_ratio_idx; 1792 break; 1793 case COMPUTE_WEIGHTED_DIFF: 1794 fmt->sort = hist_entry__cmp_wdiff_idx; 1795 break; 1796 case COMPUTE_DELTA_ABS: 1797 fmt->sort = hist_entry__cmp_delta_abs_idx; 1798 break; 1799 case COMPUTE_CYCLES: 1800 /* 1801 * Should set since 'fmt->sort' is called without 1802 * checking valid during sorting 1803 */ 1804 fmt->sort = hist_entry__cmp_nop; 1805 break; 1806 default: 1807 BUG_ON(1); 1808 } 1809 1810 perf_hpp__prepend_sort_field(fmt); 1811 return 0; 1812 } 1813 1814 static int data_init(int argc, const char **argv) 1815 { 1816 struct data__file *d; 1817 static const char *defaults[] = { 1818 "perf.data.old", 1819 "perf.data", 1820 }; 1821 bool use_default = true; 1822 int i; 1823 1824 data__files_cnt = 2; 1825 1826 if (argc) { 1827 if (argc == 1) 1828 defaults[1] = argv[0]; 1829 else { 1830 data__files_cnt = argc; 1831 use_default = false; 1832 } 1833 } else if (perf_guest) { 1834 defaults[0] = "perf.data.host"; 1835 defaults[1] = "perf.data.guest"; 1836 } 1837 1838 if (sort_compute >= (unsigned int) data__files_cnt) { 1839 pr_err("Order option out of limit.\n"); 1840 return -EINVAL; 1841 } 1842 1843 data__files = zalloc(sizeof(*data__files) * data__files_cnt); 1844 if (!data__files) 1845 return -ENOMEM; 1846 1847 data__for_each_file(i, d) { 1848 struct perf_data *data = &d->data; 1849 1850 data->path = use_default ? defaults[i] : argv[i]; 1851 data->mode = PERF_DATA_MODE_READ, 1852 data->force = force, 1853 1854 d->idx = i; 1855 } 1856 1857 return 0; 1858 } 1859 1860 static int diff__config(const char *var, const char *value, 1861 void *cb __maybe_unused) 1862 { 1863 if (!strcmp(var, "diff.order")) { 1864 int ret; 1865 if (perf_config_int(&ret, var, value) < 0) 1866 return -1; 1867 sort_compute = ret; 1868 return 0; 1869 } 1870 if (!strcmp(var, "diff.compute")) { 1871 if (!strcmp(value, "delta")) { 1872 compute = COMPUTE_DELTA; 1873 } else if (!strcmp(value, "delta-abs")) { 1874 compute = COMPUTE_DELTA_ABS; 1875 } else if (!strcmp(value, "ratio")) { 1876 compute = COMPUTE_RATIO; 1877 } else if (!strcmp(value, "wdiff")) { 1878 compute = COMPUTE_WEIGHTED_DIFF; 1879 } else { 1880 pr_err("Invalid compute method: %s\n", value); 1881 return -1; 1882 } 1883 } 1884 1885 return 0; 1886 } 1887 1888 int cmd_diff(int argc, const char **argv) 1889 { 1890 int ret = hists__init(); 1891 1892 if (ret < 0) 1893 return ret; 1894 1895 perf_config(diff__config, NULL); 1896 1897 argc = parse_options(argc, argv, options, diff_usage, 0); 1898 1899 if (quiet) 1900 perf_quiet_option(); 1901 1902 if (cycles_hist && (compute != COMPUTE_CYCLES)) 1903 usage_with_options(diff_usage, options); 1904 1905 symbol__annotation_init(); 1906 1907 if (symbol__init(NULL) < 0) 1908 return -1; 1909 1910 if (data_init(argc, argv) < 0) 1911 return -1; 1912 1913 if (check_file_brstack() < 0) 1914 return -1; 1915 1916 if (compute == COMPUTE_CYCLES && !pdiff.has_br_stack) 1917 return -1; 1918 1919 if (ui_init() < 0) 1920 return -1; 1921 1922 sort__mode = SORT_MODE__DIFF; 1923 1924 if (setup_sorting(NULL) < 0) 1925 usage_with_options(diff_usage, options); 1926 1927 setup_pager(); 1928 1929 sort__setup_elide(NULL); 1930 1931 return __cmd_diff(); 1932 } 1933