diff options
Diffstat (limited to 'tools/perf/util/hist.c')
-rw-r--r-- | tools/perf/util/hist.c | 187 |
1 files changed, 102 insertions, 85 deletions
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f38590d7561b..b262b44b7a65 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -225,14 +225,18 @@ static void he_stat__decay(struct he_stat *he_stat) | |||
225 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | 225 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) |
226 | { | 226 | { |
227 | u64 prev_period = he->stat.period; | 227 | u64 prev_period = he->stat.period; |
228 | u64 diff; | ||
228 | 229 | ||
229 | if (prev_period == 0) | 230 | if (prev_period == 0) |
230 | return true; | 231 | return true; |
231 | 232 | ||
232 | he_stat__decay(&he->stat); | 233 | he_stat__decay(&he->stat); |
233 | 234 | ||
235 | diff = prev_period - he->stat.period; | ||
236 | |||
237 | hists->stats.total_period -= diff; | ||
234 | if (!he->filtered) | 238 | if (!he->filtered) |
235 | hists->stats.total_period -= prev_period - he->stat.period; | 239 | hists->stats.total_non_filtered_period -= diff; |
236 | 240 | ||
237 | return he->stat.period == 0; | 241 | return he->stat.period == 0; |
238 | } | 242 | } |
@@ -259,8 +263,11 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | |||
259 | if (sort__need_collapse) | 263 | if (sort__need_collapse) |
260 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | 264 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); |
261 | 265 | ||
262 | hist_entry__free(n); | ||
263 | --hists->nr_entries; | 266 | --hists->nr_entries; |
267 | if (!n->filtered) | ||
268 | --hists->nr_non_filtered_entries; | ||
269 | |||
270 | hist_entry__free(n); | ||
264 | } | 271 | } |
265 | } | 272 | } |
266 | } | 273 | } |
@@ -317,15 +324,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
317 | return he; | 324 | return he; |
318 | } | 325 | } |
319 | 326 | ||
320 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) | ||
321 | { | ||
322 | if (!h->filtered) { | ||
323 | hists__calc_col_len(hists, h); | ||
324 | ++hists->nr_entries; | ||
325 | hists->stats.total_period += h->stat.period; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | static u8 symbol__parent_filter(const struct symbol *parent) | 327 | static u8 symbol__parent_filter(const struct symbol *parent) |
330 | { | 328 | { |
331 | if (symbol_conf.exclude_other && parent == NULL) | 329 | if (symbol_conf.exclude_other && parent == NULL) |
@@ -391,7 +389,6 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
391 | if (!he) | 389 | if (!he) |
392 | return NULL; | 390 | return NULL; |
393 | 391 | ||
394 | hists->nr_entries++; | ||
395 | rb_link_node(&he->rb_node_in, parent, p); | 392 | rb_link_node(&he->rb_node_in, parent, p); |
396 | rb_insert_color(&he->rb_node_in, hists->entries_in); | 393 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
397 | out: | 394 | out: |
@@ -435,11 +432,14 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
435 | int64_t | 432 | int64_t |
436 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | 433 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) |
437 | { | 434 | { |
438 | struct sort_entry *se; | 435 | struct perf_hpp_fmt *fmt; |
439 | int64_t cmp = 0; | 436 | int64_t cmp = 0; |
440 | 437 | ||
441 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 438 | perf_hpp__for_each_sort_list(fmt) { |
442 | cmp = se->se_cmp(left, right); | 439 | if (perf_hpp__should_skip(fmt)) |
440 | continue; | ||
441 | |||
442 | cmp = fmt->cmp(left, right); | ||
443 | if (cmp) | 443 | if (cmp) |
444 | break; | 444 | break; |
445 | } | 445 | } |
@@ -450,15 +450,14 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |||
450 | int64_t | 450 | int64_t |
451 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | 451 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) |
452 | { | 452 | { |
453 | struct sort_entry *se; | 453 | struct perf_hpp_fmt *fmt; |
454 | int64_t cmp = 0; | 454 | int64_t cmp = 0; |
455 | 455 | ||
456 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 456 | perf_hpp__for_each_sort_list(fmt) { |
457 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | 457 | if (perf_hpp__should_skip(fmt)) |
458 | 458 | continue; | |
459 | f = se->se_collapse ?: se->se_cmp; | ||
460 | 459 | ||
461 | cmp = f(left, right); | 460 | cmp = fmt->collapse(left, right); |
462 | if (cmp) | 461 | if (cmp) |
463 | break; | 462 | break; |
464 | } | 463 | } |
@@ -571,64 +570,50 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | |||
571 | } | 570 | } |
572 | } | 571 | } |
573 | 572 | ||
574 | /* | 573 | static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) |
575 | * reverse the map, sort on period. | ||
576 | */ | ||
577 | |||
578 | static int period_cmp(u64 period_a, u64 period_b) | ||
579 | { | 574 | { |
580 | if (period_a > period_b) | 575 | struct perf_hpp_fmt *fmt; |
581 | return 1; | 576 | int64_t cmp = 0; |
582 | if (period_a < period_b) | ||
583 | return -1; | ||
584 | return 0; | ||
585 | } | ||
586 | |||
587 | static int hist_entry__sort_on_period(struct hist_entry *a, | ||
588 | struct hist_entry *b) | ||
589 | { | ||
590 | int ret; | ||
591 | int i, nr_members; | ||
592 | struct perf_evsel *evsel; | ||
593 | struct hist_entry *pair; | ||
594 | u64 *periods_a, *periods_b; | ||
595 | 577 | ||
596 | ret = period_cmp(a->stat.period, b->stat.period); | 578 | perf_hpp__for_each_sort_list(fmt) { |
597 | if (ret || !symbol_conf.event_group) | 579 | if (perf_hpp__should_skip(fmt)) |
598 | return ret; | 580 | continue; |
599 | 581 | ||
600 | evsel = hists_to_evsel(a->hists); | 582 | cmp = fmt->sort(a, b); |
601 | nr_members = evsel->nr_members; | 583 | if (cmp) |
602 | if (nr_members <= 1) | 584 | break; |
603 | return ret; | 585 | } |
604 | 586 | ||
605 | periods_a = zalloc(sizeof(periods_a) * nr_members); | 587 | return cmp; |
606 | periods_b = zalloc(sizeof(periods_b) * nr_members); | 588 | } |
607 | 589 | ||
608 | if (!periods_a || !periods_b) | 590 | static void hists__reset_filter_stats(struct hists *hists) |
609 | goto out; | 591 | { |
592 | hists->nr_non_filtered_entries = 0; | ||
593 | hists->stats.total_non_filtered_period = 0; | ||
594 | } | ||
610 | 595 | ||
611 | list_for_each_entry(pair, &a->pairs.head, pairs.node) { | 596 | void hists__reset_stats(struct hists *hists) |
612 | evsel = hists_to_evsel(pair->hists); | 597 | { |
613 | periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; | 598 | hists->nr_entries = 0; |
614 | } | 599 | hists->stats.total_period = 0; |
615 | 600 | ||
616 | list_for_each_entry(pair, &b->pairs.head, pairs.node) { | 601 | hists__reset_filter_stats(hists); |
617 | evsel = hists_to_evsel(pair->hists); | 602 | } |
618 | periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period; | ||
619 | } | ||
620 | 603 | ||
621 | for (i = 1; i < nr_members; i++) { | 604 | static void hists__inc_filter_stats(struct hists *hists, struct hist_entry *h) |
622 | ret = period_cmp(periods_a[i], periods_b[i]); | 605 | { |
623 | if (ret) | 606 | hists->nr_non_filtered_entries++; |
624 | break; | 607 | hists->stats.total_non_filtered_period += h->stat.period; |
625 | } | 608 | } |
626 | 609 | ||
627 | out: | 610 | void hists__inc_stats(struct hists *hists, struct hist_entry *h) |
628 | free(periods_a); | 611 | { |
629 | free(periods_b); | 612 | if (!h->filtered) |
613 | hists__inc_filter_stats(hists, h); | ||
630 | 614 | ||
631 | return ret; | 615 | hists->nr_entries++; |
616 | hists->stats.total_period += h->stat.period; | ||
632 | } | 617 | } |
633 | 618 | ||
634 | static void __hists__insert_output_entry(struct rb_root *entries, | 619 | static void __hists__insert_output_entry(struct rb_root *entries, |
@@ -647,7 +632,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
647 | parent = *p; | 632 | parent = *p; |
648 | iter = rb_entry(parent, struct hist_entry, rb_node); | 633 | iter = rb_entry(parent, struct hist_entry, rb_node); |
649 | 634 | ||
650 | if (hist_entry__sort_on_period(he, iter) > 0) | 635 | if (hist_entry__sort(he, iter) > 0) |
651 | p = &(*p)->rb_left; | 636 | p = &(*p)->rb_left; |
652 | else | 637 | else |
653 | p = &(*p)->rb_right; | 638 | p = &(*p)->rb_right; |
@@ -674,8 +659,7 @@ void hists__output_resort(struct hists *hists) | |||
674 | next = rb_first(root); | 659 | next = rb_first(root); |
675 | hists->entries = RB_ROOT; | 660 | hists->entries = RB_ROOT; |
676 | 661 | ||
677 | hists->nr_entries = 0; | 662 | hists__reset_stats(hists); |
678 | hists->stats.total_period = 0; | ||
679 | hists__reset_col_len(hists); | 663 | hists__reset_col_len(hists); |
680 | 664 | ||
681 | while (next) { | 665 | while (next) { |
@@ -683,7 +667,10 @@ void hists__output_resort(struct hists *hists) | |||
683 | next = rb_next(&n->rb_node_in); | 667 | next = rb_next(&n->rb_node_in); |
684 | 668 | ||
685 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); | 669 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); |
686 | hists__inc_nr_entries(hists, n); | 670 | hists__inc_stats(hists, n); |
671 | |||
672 | if (!n->filtered) | ||
673 | hists__calc_col_len(hists, n); | ||
687 | } | 674 | } |
688 | } | 675 | } |
689 | 676 | ||
@@ -694,13 +681,13 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h | |||
694 | if (h->filtered) | 681 | if (h->filtered) |
695 | return; | 682 | return; |
696 | 683 | ||
697 | ++hists->nr_entries; | 684 | /* force fold unfiltered entry for simplicity */ |
698 | if (h->ms.unfolded) | 685 | h->ms.unfolded = false; |
699 | hists->nr_entries += h->nr_rows; | ||
700 | h->row_offset = 0; | 686 | h->row_offset = 0; |
701 | hists->stats.total_period += h->stat.period; | ||
702 | hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; | ||
703 | 687 | ||
688 | hists->stats.nr_non_filtered_samples += h->stat.nr_events; | ||
689 | |||
690 | hists__inc_filter_stats(hists, h); | ||
704 | hists__calc_col_len(hists, h); | 691 | hists__calc_col_len(hists, h); |
705 | } | 692 | } |
706 | 693 | ||
@@ -721,8 +708,9 @@ void hists__filter_by_dso(struct hists *hists) | |||
721 | { | 708 | { |
722 | struct rb_node *nd; | 709 | struct rb_node *nd; |
723 | 710 | ||
724 | hists->nr_entries = hists->stats.total_period = 0; | 711 | hists->stats.nr_non_filtered_samples = 0; |
725 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 712 | |
713 | hists__reset_filter_stats(hists); | ||
726 | hists__reset_col_len(hists); | 714 | hists__reset_col_len(hists); |
727 | 715 | ||
728 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 716 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
@@ -754,8 +742,9 @@ void hists__filter_by_thread(struct hists *hists) | |||
754 | { | 742 | { |
755 | struct rb_node *nd; | 743 | struct rb_node *nd; |
756 | 744 | ||
757 | hists->nr_entries = hists->stats.total_period = 0; | 745 | hists->stats.nr_non_filtered_samples = 0; |
758 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 746 | |
747 | hists__reset_filter_stats(hists); | ||
759 | hists__reset_col_len(hists); | 748 | hists__reset_col_len(hists); |
760 | 749 | ||
761 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 750 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
@@ -785,8 +774,9 @@ void hists__filter_by_symbol(struct hists *hists) | |||
785 | { | 774 | { |
786 | struct rb_node *nd; | 775 | struct rb_node *nd; |
787 | 776 | ||
788 | hists->nr_entries = hists->stats.total_period = 0; | 777 | hists->stats.nr_non_filtered_samples = 0; |
789 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 778 | |
779 | hists__reset_filter_stats(hists); | ||
790 | hists__reset_col_len(hists); | 780 | hists__reset_col_len(hists); |
791 | 781 | ||
792 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 782 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
@@ -847,7 +837,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, | |||
847 | he->hists = hists; | 837 | he->hists = hists; |
848 | rb_link_node(&he->rb_node_in, parent, p); | 838 | rb_link_node(&he->rb_node_in, parent, p); |
849 | rb_insert_color(&he->rb_node_in, root); | 839 | rb_insert_color(&he->rb_node_in, root); |
850 | hists__inc_nr_entries(hists, he); | 840 | hists__inc_stats(hists, he); |
851 | he->dummy = true; | 841 | he->dummy = true; |
852 | } | 842 | } |
853 | out: | 843 | out: |
@@ -931,3 +921,30 @@ int hists__link(struct hists *leader, struct hists *other) | |||
931 | 921 | ||
932 | return 0; | 922 | return 0; |
933 | } | 923 | } |
924 | |||
925 | u64 hists__total_period(struct hists *hists) | ||
926 | { | ||
927 | return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : | ||
928 | hists->stats.total_period; | ||
929 | } | ||
930 | |||
931 | int parse_filter_percentage(const struct option *opt __maybe_unused, | ||
932 | const char *arg, int unset __maybe_unused) | ||
933 | { | ||
934 | if (!strcmp(arg, "relative")) | ||
935 | symbol_conf.filter_relative = true; | ||
936 | else if (!strcmp(arg, "absolute")) | ||
937 | symbol_conf.filter_relative = false; | ||
938 | else | ||
939 | return -1; | ||
940 | |||
941 | return 0; | ||
942 | } | ||
943 | |||
944 | int perf_hist_config(const char *var, const char *value) | ||
945 | { | ||
946 | if (!strcmp(var, "hist.percentage")) | ||
947 | return parse_filter_percentage(NULL, value, 0); | ||
948 | |||
949 | return 0; | ||
950 | } | ||