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 ((strncmp(start_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0) && 1316 (strncmp(end_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)) { 1317 scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld", 1318 start_line, end_line, block_he->diff.cycles); 1319 } else { 1320 scnprintf(buf, sizeof(buf), "[%7lx -> %7lx] %4ld", 1321 bi->start, bi->end, block_he->diff.cycles); 1322 } 1323 1324 free_srcline(start_line); 1325 free_srcline(end_line); 1326 1327 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf); 1328 } 1329 1330 static int __hpp__color_compare(struct perf_hpp_fmt *fmt, 1331 struct perf_hpp *hpp, struct hist_entry *he, 1332 int comparison_method) 1333 { 1334 struct diff_hpp_fmt *dfmt = 1335 container_of(fmt, struct diff_hpp_fmt, fmt); 1336 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1337 double diff; 1338 s64 wdiff; 1339 char pfmt[20] = " "; 1340 1341 if (!pair) { 1342 if (comparison_method == COMPUTE_CYCLES) { 1343 struct block_hist *bh; 1344 1345 bh = container_of(he, struct block_hist, he); 1346 if (bh->block_idx) 1347 hpp->skip = true; 1348 } 1349 1350 goto no_print; 1351 } 1352 1353 switch (comparison_method) { 1354 case COMPUTE_DELTA: 1355 if (pair->diff.computed) 1356 diff = pair->diff.period_ratio_delta; 1357 else 1358 diff = compute_delta(he, pair); 1359 1360 scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1); 1361 return percent_color_snprintf(hpp->buf, hpp->size, 1362 pfmt, diff); 1363 case COMPUTE_RATIO: 1364 if (he->dummy) 1365 goto dummy_print; 1366 if (pair->diff.computed) 1367 diff = pair->diff.period_ratio; 1368 else 1369 diff = compute_ratio(he, pair); 1370 1371 scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width); 1372 return value_color_snprintf(hpp->buf, hpp->size, 1373 pfmt, diff); 1374 case COMPUTE_WEIGHTED_DIFF: 1375 if (he->dummy) 1376 goto dummy_print; 1377 if (pair->diff.computed) 1378 wdiff = pair->diff.wdiff; 1379 else 1380 wdiff = compute_wdiff(he, pair); 1381 1382 scnprintf(pfmt, 20, "%%14ld", dfmt->header_width); 1383 return color_snprintf(hpp->buf, hpp->size, 1384 get_percent_color(wdiff), 1385 pfmt, wdiff); 1386 case COMPUTE_CYCLES: 1387 return cycles_printf(he, pair, hpp, dfmt->header_width); 1388 default: 1389 BUG_ON(1); 1390 } 1391 dummy_print: 1392 return scnprintf(hpp->buf, hpp->size, "%*s", 1393 dfmt->header_width, "N/A"); 1394 no_print: 1395 return scnprintf(hpp->buf, hpp->size, "%*s", 1396 dfmt->header_width, pfmt); 1397 } 1398 1399 static int hpp__color_delta(struct perf_hpp_fmt *fmt, 1400 struct perf_hpp *hpp, struct hist_entry *he) 1401 { 1402 return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA); 1403 } 1404 1405 static int hpp__color_ratio(struct perf_hpp_fmt *fmt, 1406 struct perf_hpp *hpp, struct hist_entry *he) 1407 { 1408 return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO); 1409 } 1410 1411 static int hpp__color_wdiff(struct perf_hpp_fmt *fmt, 1412 struct perf_hpp *hpp, struct hist_entry *he) 1413 { 1414 return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF); 1415 } 1416 1417 static int hpp__color_cycles(struct perf_hpp_fmt *fmt, 1418 struct perf_hpp *hpp, struct hist_entry *he) 1419 { 1420 return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES); 1421 } 1422 1423 static int all_zero(unsigned long *vals, int len) 1424 { 1425 int i; 1426 1427 for (i = 0; i < len; i++) 1428 if (vals[i] != 0) 1429 return 0; 1430 return 1; 1431 } 1432 1433 static int print_cycles_spark(char *bf, int size, unsigned long *svals, u64 n) 1434 { 1435 int printed; 1436 1437 if (n <= 1) 1438 return 0; 1439 1440 if (n > NUM_SPARKS) 1441 n = NUM_SPARKS; 1442 if (all_zero(svals, n)) 1443 return 0; 1444 1445 printed = print_spark(bf, size, svals, n); 1446 printed += scnprintf(bf + printed, size - printed, " "); 1447 return printed; 1448 } 1449 1450 static int hpp__color_cycles_hist(struct perf_hpp_fmt *fmt, 1451 struct perf_hpp *hpp, struct hist_entry *he) 1452 { 1453 struct diff_hpp_fmt *dfmt = 1454 container_of(fmt, struct diff_hpp_fmt, fmt); 1455 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1456 struct block_hist *bh = container_of(he, struct block_hist, he); 1457 struct block_hist *bh_pair; 1458 struct hist_entry *block_he; 1459 char spark[32], buf[128]; 1460 double r; 1461 int ret, pad; 1462 1463 if (!pair) { 1464 if (bh->block_idx) 1465 hpp->skip = true; 1466 1467 goto no_print; 1468 } 1469 1470 bh_pair = container_of(pair, struct block_hist, he); 1471 1472 block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx); 1473 if (!block_he) { 1474 hpp->skip = true; 1475 goto no_print; 1476 } 1477 1478 ret = print_cycles_spark(spark, sizeof(spark), block_he->diff.svals, 1479 block_he->diff.stats.n); 1480 1481 r = rel_stddev_stats(stddev_stats(&block_he->diff.stats), 1482 avg_stats(&block_he->diff.stats)); 1483 1484 if (ret) { 1485 /* 1486 * Padding spaces if number of sparks less than NUM_SPARKS 1487 * otherwise the output is not aligned. 1488 */ 1489 pad = NUM_SPARKS - ((ret - 1) / 3); 1490 scnprintf(buf, sizeof(buf), "%s%5.1f%% %s", "\u00B1", r, spark); 1491 ret = scnprintf(hpp->buf, hpp->size, "%*s", 1492 dfmt->header_width, buf); 1493 1494 if (pad) { 1495 ret += scnprintf(hpp->buf + ret, hpp->size - ret, 1496 "%-*s", pad, " "); 1497 } 1498 1499 return ret; 1500 } 1501 1502 no_print: 1503 return scnprintf(hpp->buf, hpp->size, "%*s", 1504 dfmt->header_width, " "); 1505 } 1506 1507 static void 1508 hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size) 1509 { 1510 switch (idx) { 1511 case PERF_HPP_DIFF__PERIOD_BASELINE: 1512 scnprintf(buf, size, "%" PRIu64, he->stat.period); 1513 break; 1514 1515 default: 1516 break; 1517 } 1518 } 1519 1520 static void 1521 hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, 1522 int idx, char *buf, size_t size) 1523 { 1524 double diff; 1525 double ratio; 1526 s64 wdiff; 1527 1528 switch (idx) { 1529 case PERF_HPP_DIFF__DELTA: 1530 case PERF_HPP_DIFF__DELTA_ABS: 1531 if (pair->diff.computed) 1532 diff = pair->diff.period_ratio_delta; 1533 else 1534 diff = compute_delta(he, pair); 1535 1536 scnprintf(buf, size, "%+4.2F%%", diff); 1537 break; 1538 1539 case PERF_HPP_DIFF__RATIO: 1540 /* No point for ratio number if we are dummy.. */ 1541 if (he->dummy) { 1542 scnprintf(buf, size, "N/A"); 1543 break; 1544 } 1545 1546 if (pair->diff.computed) 1547 ratio = pair->diff.period_ratio; 1548 else 1549 ratio = compute_ratio(he, pair); 1550 1551 if (ratio > 0.0) 1552 scnprintf(buf, size, "%14.6F", ratio); 1553 break; 1554 1555 case PERF_HPP_DIFF__WEIGHTED_DIFF: 1556 /* No point for wdiff number if we are dummy.. */ 1557 if (he->dummy) { 1558 scnprintf(buf, size, "N/A"); 1559 break; 1560 } 1561 1562 if (pair->diff.computed) 1563 wdiff = pair->diff.wdiff; 1564 else 1565 wdiff = compute_wdiff(he, pair); 1566 1567 if (wdiff != 0) 1568 scnprintf(buf, size, "%14ld", wdiff); 1569 break; 1570 1571 case PERF_HPP_DIFF__FORMULA: 1572 formula_fprintf(he, pair, buf, size); 1573 break; 1574 1575 case PERF_HPP_DIFF__PERIOD: 1576 scnprintf(buf, size, "%" PRIu64, pair->stat.period); 1577 break; 1578 1579 default: 1580 BUG_ON(1); 1581 }; 1582 } 1583 1584 static void 1585 __hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt, 1586 char *buf, size_t size) 1587 { 1588 struct hist_entry *pair = get_pair_fmt(he, dfmt); 1589 int idx = dfmt->idx; 1590 1591 /* baseline is special */ 1592 if (idx == PERF_HPP_DIFF__BASELINE) 1593 hpp__entry_baseline(he, buf, size); 1594 else { 1595 if (pair) 1596 hpp__entry_pair(he, pair, idx, buf, size); 1597 else 1598 hpp__entry_unpair(he, idx, buf, size); 1599 } 1600 } 1601 1602 static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp, 1603 struct hist_entry *he) 1604 { 1605 struct diff_hpp_fmt *dfmt = 1606 container_of(_fmt, struct diff_hpp_fmt, fmt); 1607 char buf[MAX_COL_WIDTH] = " "; 1608 1609 __hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH); 1610 1611 if (symbol_conf.field_sep) 1612 return scnprintf(hpp->buf, hpp->size, "%s", buf); 1613 else 1614 return scnprintf(hpp->buf, hpp->size, "%*s", 1615 dfmt->header_width, buf); 1616 } 1617 1618 static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 1619 struct hists *hists __maybe_unused, 1620 int line __maybe_unused, 1621 int *span __maybe_unused) 1622 { 1623 struct diff_hpp_fmt *dfmt = 1624 container_of(fmt, struct diff_hpp_fmt, fmt); 1625 1626 BUG_ON(!dfmt->header); 1627 return scnprintf(hpp->buf, hpp->size, dfmt->header); 1628 } 1629 1630 static int hpp__width(struct perf_hpp_fmt *fmt, 1631 struct perf_hpp *hpp __maybe_unused, 1632 struct hists *hists __maybe_unused) 1633 { 1634 struct diff_hpp_fmt *dfmt = 1635 container_of(fmt, struct diff_hpp_fmt, fmt); 1636 1637 BUG_ON(dfmt->header_width <= 0); 1638 return dfmt->header_width; 1639 } 1640 1641 static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt) 1642 { 1643 #define MAX_HEADER_NAME 100 1644 char buf_indent[MAX_HEADER_NAME]; 1645 char buf[MAX_HEADER_NAME]; 1646 const char *header = NULL; 1647 int width = 0; 1648 1649 BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX); 1650 header = columns[dfmt->idx].name; 1651 width = columns[dfmt->idx].width; 1652 1653 /* Only our defined HPP fmts should appear here. */ 1654 BUG_ON(!header); 1655 1656 if (data__files_cnt > 2) 1657 scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx); 1658 1659 #define NAME (data__files_cnt > 2 ? buf : header) 1660 dfmt->header_width = width; 1661 width = (int) strlen(NAME); 1662 if (dfmt->header_width < width) 1663 dfmt->header_width = width; 1664 1665 scnprintf(buf_indent, MAX_HEADER_NAME, "%*s", 1666 dfmt->header_width, NAME); 1667 1668 dfmt->header = strdup(buf_indent); 1669 #undef MAX_HEADER_NAME 1670 #undef NAME 1671 } 1672 1673 static void data__hpp_register(struct data__file *d, int idx) 1674 { 1675 struct diff_hpp_fmt *dfmt = &d->fmt[idx]; 1676 struct perf_hpp_fmt *fmt = &dfmt->fmt; 1677 1678 dfmt->idx = idx; 1679 1680 fmt->header = hpp__header; 1681 fmt->width = hpp__width; 1682 fmt->entry = hpp__entry_global; 1683 fmt->cmp = hist_entry__cmp_nop; 1684 fmt->collapse = hist_entry__cmp_nop; 1685 1686 /* TODO more colors */ 1687 switch (idx) { 1688 case PERF_HPP_DIFF__BASELINE: 1689 fmt->color = hpp__color_baseline; 1690 fmt->sort = hist_entry__cmp_baseline; 1691 break; 1692 case PERF_HPP_DIFF__DELTA: 1693 fmt->color = hpp__color_delta; 1694 fmt->sort = hist_entry__cmp_delta; 1695 break; 1696 case PERF_HPP_DIFF__RATIO: 1697 fmt->color = hpp__color_ratio; 1698 fmt->sort = hist_entry__cmp_ratio; 1699 break; 1700 case PERF_HPP_DIFF__WEIGHTED_DIFF: 1701 fmt->color = hpp__color_wdiff; 1702 fmt->sort = hist_entry__cmp_wdiff; 1703 break; 1704 case PERF_HPP_DIFF__DELTA_ABS: 1705 fmt->color = hpp__color_delta; 1706 fmt->sort = hist_entry__cmp_delta_abs; 1707 break; 1708 case PERF_HPP_DIFF__CYCLES: 1709 fmt->color = hpp__color_cycles; 1710 fmt->sort = hist_entry__cmp_nop; 1711 break; 1712 case PERF_HPP_DIFF__CYCLES_HIST: 1713 fmt->color = hpp__color_cycles_hist; 1714 fmt->sort = hist_entry__cmp_nop; 1715 break; 1716 default: 1717 fmt->sort = hist_entry__cmp_nop; 1718 break; 1719 } 1720 1721 init_header(d, dfmt); 1722 perf_hpp__column_register(fmt); 1723 perf_hpp__register_sort_field(fmt); 1724 } 1725 1726 static int ui_init(void) 1727 { 1728 struct data__file *d; 1729 struct perf_hpp_fmt *fmt; 1730 int i; 1731 1732 data__for_each_file(i, d) { 1733 1734 /* 1735 * Baseline or compute realted columns: 1736 * 1737 * PERF_HPP_DIFF__BASELINE 1738 * PERF_HPP_DIFF__DELTA 1739 * PERF_HPP_DIFF__RATIO 1740 * PERF_HPP_DIFF__WEIGHTED_DIFF 1741 * PERF_HPP_DIFF__CYCLES 1742 */ 1743 data__hpp_register(d, i ? compute_2_hpp[compute] : 1744 PERF_HPP_DIFF__BASELINE); 1745 1746 if (cycles_hist && i) 1747 data__hpp_register(d, PERF_HPP_DIFF__CYCLES_HIST); 1748 1749 /* 1750 * And the rest: 1751 * 1752 * PERF_HPP_DIFF__FORMULA 1753 * PERF_HPP_DIFF__PERIOD 1754 * PERF_HPP_DIFF__PERIOD_BASELINE 1755 */ 1756 if (show_formula && i) 1757 data__hpp_register(d, PERF_HPP_DIFF__FORMULA); 1758 1759 if (show_period) 1760 data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD : 1761 PERF_HPP_DIFF__PERIOD_BASELINE); 1762 } 1763 1764 if (!sort_compute) 1765 return 0; 1766 1767 /* 1768 * Prepend an fmt to sort on columns at 'sort_compute' first. 1769 * This fmt is added only to the sort list but not to the 1770 * output fields list. 1771 * 1772 * Note that this column (data) can be compared twice - one 1773 * for this 'sort_compute' fmt and another for the normal 1774 * diff_hpp_fmt. But it shouldn't a problem as most entries 1775 * will be sorted out by first try or baseline and comparing 1776 * is not a costly operation. 1777 */ 1778 fmt = zalloc(sizeof(*fmt)); 1779 if (fmt == NULL) { 1780 pr_err("Memory allocation failed\n"); 1781 return -1; 1782 } 1783 1784 fmt->cmp = hist_entry__cmp_nop; 1785 fmt->collapse = hist_entry__cmp_nop; 1786 1787 switch (compute) { 1788 case COMPUTE_DELTA: 1789 fmt->sort = hist_entry__cmp_delta_idx; 1790 break; 1791 case COMPUTE_RATIO: 1792 fmt->sort = hist_entry__cmp_ratio_idx; 1793 break; 1794 case COMPUTE_WEIGHTED_DIFF: 1795 fmt->sort = hist_entry__cmp_wdiff_idx; 1796 break; 1797 case COMPUTE_DELTA_ABS: 1798 fmt->sort = hist_entry__cmp_delta_abs_idx; 1799 break; 1800 case COMPUTE_CYCLES: 1801 /* 1802 * Should set since 'fmt->sort' is called without 1803 * checking valid during sorting 1804 */ 1805 fmt->sort = hist_entry__cmp_nop; 1806 break; 1807 default: 1808 BUG_ON(1); 1809 } 1810 1811 perf_hpp__prepend_sort_field(fmt); 1812 return 0; 1813 } 1814 1815 static int data_init(int argc, const char **argv) 1816 { 1817 struct data__file *d; 1818 static const char *defaults[] = { 1819 "perf.data.old", 1820 "perf.data", 1821 }; 1822 bool use_default = true; 1823 int i; 1824 1825 data__files_cnt = 2; 1826 1827 if (argc) { 1828 if (argc == 1) 1829 defaults[1] = argv[0]; 1830 else { 1831 data__files_cnt = argc; 1832 use_default = false; 1833 } 1834 } else if (perf_guest) { 1835 defaults[0] = "perf.data.host"; 1836 defaults[1] = "perf.data.guest"; 1837 } 1838 1839 if (sort_compute >= (unsigned int) data__files_cnt) { 1840 pr_err("Order option out of limit.\n"); 1841 return -EINVAL; 1842 } 1843 1844 data__files = zalloc(sizeof(*data__files) * data__files_cnt); 1845 if (!data__files) 1846 return -ENOMEM; 1847 1848 data__for_each_file(i, d) { 1849 struct perf_data *data = &d->data; 1850 1851 data->path = use_default ? defaults[i] : argv[i]; 1852 data->mode = PERF_DATA_MODE_READ, 1853 data->force = force, 1854 1855 d->idx = i; 1856 } 1857 1858 return 0; 1859 } 1860 1861 static int diff__config(const char *var, const char *value, 1862 void *cb __maybe_unused) 1863 { 1864 if (!strcmp(var, "diff.order")) { 1865 int ret; 1866 if (perf_config_int(&ret, var, value) < 0) 1867 return -1; 1868 sort_compute = ret; 1869 return 0; 1870 } 1871 if (!strcmp(var, "diff.compute")) { 1872 if (!strcmp(value, "delta")) { 1873 compute = COMPUTE_DELTA; 1874 } else if (!strcmp(value, "delta-abs")) { 1875 compute = COMPUTE_DELTA_ABS; 1876 } else if (!strcmp(value, "ratio")) { 1877 compute = COMPUTE_RATIO; 1878 } else if (!strcmp(value, "wdiff")) { 1879 compute = COMPUTE_WEIGHTED_DIFF; 1880 } else { 1881 pr_err("Invalid compute method: %s\n", value); 1882 return -1; 1883 } 1884 } 1885 1886 return 0; 1887 } 1888 1889 int cmd_diff(int argc, const char **argv) 1890 { 1891 int ret = hists__init(); 1892 1893 if (ret < 0) 1894 return ret; 1895 1896 perf_config(diff__config, NULL); 1897 1898 argc = parse_options(argc, argv, options, diff_usage, 0); 1899 1900 if (quiet) 1901 perf_quiet_option(); 1902 1903 if (cycles_hist && (compute != COMPUTE_CYCLES)) 1904 usage_with_options(diff_usage, options); 1905 1906 symbol__annotation_init(); 1907 1908 if (symbol__init(NULL) < 0) 1909 return -1; 1910 1911 if (data_init(argc, argv) < 0) 1912 return -1; 1913 1914 if (check_file_brstack() < 0) 1915 return -1; 1916 1917 if (compute == COMPUTE_CYCLES && !pdiff.has_br_stack) 1918 return -1; 1919 1920 if (ui_init() < 0) 1921 return -1; 1922 1923 sort__mode = SORT_MODE__DIFF; 1924 1925 if (setup_sorting(NULL) < 0) 1926 usage_with_options(diff_usage, options); 1927 1928 setup_pager(); 1929 1930 sort__setup_elide(NULL); 1931 1932 return __cmd_diff(); 1933 } 1934