diff options
Diffstat (limited to 'tools/perf/util/hist.c')
-rw-r--r-- | tools/perf/util/hist.c | 157 |
1 files changed, 93 insertions, 64 deletions
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c61235f81260..2ec4b60aff6c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -607,7 +607,7 @@ static void init_rem_hits(void) | |||
607 | rem_hits.ms.sym = rem_sq_bracket; | 607 | rem_hits.ms.sym = rem_sq_bracket; |
608 | } | 608 | } |
609 | 609 | ||
610 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 610 | static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
611 | u64 total_samples, int depth, | 611 | u64 total_samples, int depth, |
612 | int depth_mask, int left_margin) | 612 | int depth_mask, int left_margin) |
613 | { | 613 | { |
@@ -615,21 +615,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
615 | struct callchain_node *child; | 615 | struct callchain_node *child; |
616 | struct callchain_list *chain; | 616 | struct callchain_list *chain; |
617 | int new_depth_mask = depth_mask; | 617 | int new_depth_mask = depth_mask; |
618 | u64 new_total; | ||
619 | u64 remaining; | 618 | u64 remaining; |
620 | size_t ret = 0; | 619 | size_t ret = 0; |
621 | int i; | 620 | int i; |
622 | uint entries_printed = 0; | 621 | uint entries_printed = 0; |
623 | 622 | ||
624 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 623 | remaining = total_samples; |
625 | new_total = self->children_hit; | ||
626 | else | ||
627 | new_total = total_samples; | ||
628 | |||
629 | remaining = new_total; | ||
630 | 624 | ||
631 | node = rb_first(&self->rb_root); | 625 | node = rb_first(root); |
632 | while (node) { | 626 | while (node) { |
627 | u64 new_total; | ||
633 | u64 cumul; | 628 | u64 cumul; |
634 | 629 | ||
635 | child = rb_entry(node, struct callchain_node, rb_node); | 630 | child = rb_entry(node, struct callchain_node, rb_node); |
@@ -657,11 +652,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
657 | list_for_each_entry(chain, &child->val, list) { | 652 | list_for_each_entry(chain, &child->val, list) { |
658 | ret += ipchain__fprintf_graph(fp, chain, depth, | 653 | ret += ipchain__fprintf_graph(fp, chain, depth, |
659 | new_depth_mask, i++, | 654 | new_depth_mask, i++, |
660 | new_total, | 655 | total_samples, |
661 | cumul, | 656 | cumul, |
662 | left_margin); | 657 | left_margin); |
663 | } | 658 | } |
664 | ret += __callchain__fprintf_graph(fp, child, new_total, | 659 | |
660 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
661 | new_total = child->children_hit; | ||
662 | else | ||
663 | new_total = total_samples; | ||
664 | |||
665 | ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total, | ||
665 | depth + 1, | 666 | depth + 1, |
666 | new_depth_mask | (1 << depth), | 667 | new_depth_mask | (1 << depth), |
667 | left_margin); | 668 | left_margin); |
@@ -671,61 +672,75 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
671 | } | 672 | } |
672 | 673 | ||
673 | if (callchain_param.mode == CHAIN_GRAPH_REL && | 674 | if (callchain_param.mode == CHAIN_GRAPH_REL && |
674 | remaining && remaining != new_total) { | 675 | remaining && remaining != total_samples) { |
675 | 676 | ||
676 | if (!rem_sq_bracket) | 677 | if (!rem_sq_bracket) |
677 | return ret; | 678 | return ret; |
678 | 679 | ||
679 | new_depth_mask &= ~(1 << (depth - 1)); | 680 | new_depth_mask &= ~(1 << (depth - 1)); |
680 | |||
681 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | 681 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, |
682 | new_depth_mask, 0, new_total, | 682 | new_depth_mask, 0, total_samples, |
683 | remaining, left_margin); | 683 | remaining, left_margin); |
684 | } | 684 | } |
685 | 685 | ||
686 | return ret; | 686 | return ret; |
687 | } | 687 | } |
688 | 688 | ||
689 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 689 | static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
690 | u64 total_samples, int left_margin) | 690 | u64 total_samples, int left_margin) |
691 | { | 691 | { |
692 | struct callchain_node *cnode; | ||
692 | struct callchain_list *chain; | 693 | struct callchain_list *chain; |
694 | u32 entries_printed = 0; | ||
693 | bool printed = false; | 695 | bool printed = false; |
696 | struct rb_node *node; | ||
694 | int i = 0; | 697 | int i = 0; |
695 | int ret = 0; | 698 | int ret; |
696 | u32 entries_printed = 0; | ||
697 | |||
698 | list_for_each_entry(chain, &self->val, list) { | ||
699 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
700 | continue; | ||
701 | |||
702 | if (!printed) { | ||
703 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
704 | ret += fprintf(fp, "|\n"); | ||
705 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
706 | ret += fprintf(fp, "---"); | ||
707 | |||
708 | left_margin += 3; | ||
709 | printed = true; | ||
710 | } else | ||
711 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
712 | 699 | ||
713 | if (chain->ms.sym) | 700 | /* |
714 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | 701 | * If have one single callchain root, don't bother printing |
715 | else | 702 | * its percentage (100 % in fractal mode and the same percentage |
716 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | 703 | * than the hist in graph mode). This also avoid one level of column. |
704 | */ | ||
705 | node = rb_first(root); | ||
706 | if (node && !rb_next(node)) { | ||
707 | cnode = rb_entry(node, struct callchain_node, rb_node); | ||
708 | list_for_each_entry(chain, &cnode->val, list) { | ||
709 | /* | ||
710 | * If we sort by symbol, the first entry is the same than | ||
711 | * the symbol. No need to print it otherwise it appears as | ||
712 | * displayed twice. | ||
713 | */ | ||
714 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
715 | continue; | ||
716 | if (!printed) { | ||
717 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
718 | ret += fprintf(fp, "|\n"); | ||
719 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
720 | ret += fprintf(fp, "---"); | ||
721 | left_margin += 3; | ||
722 | printed = true; | ||
723 | } else | ||
724 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
725 | |||
726 | if (chain->ms.sym) | ||
727 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | ||
728 | else | ||
729 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
717 | 730 | ||
718 | if (++entries_printed == callchain_param.print_limit) | 731 | if (++entries_printed == callchain_param.print_limit) |
719 | break; | 732 | break; |
733 | } | ||
734 | root = &cnode->rb_root; | ||
720 | } | 735 | } |
721 | 736 | ||
722 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | 737 | return __callchain__fprintf_graph(fp, root, total_samples, |
723 | 738 | 1, 1, left_margin); | |
724 | return ret; | ||
725 | } | 739 | } |
726 | 740 | ||
727 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | 741 | static size_t __callchain__fprintf_flat(FILE *fp, |
728 | u64 total_samples) | 742 | struct callchain_node *self, |
743 | u64 total_samples) | ||
729 | { | 744 | { |
730 | struct callchain_list *chain; | 745 | struct callchain_list *chain; |
731 | size_t ret = 0; | 746 | size_t ret = 0; |
@@ -733,7 +748,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
733 | if (!self) | 748 | if (!self) |
734 | return 0; | 749 | return 0; |
735 | 750 | ||
736 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | 751 | ret += __callchain__fprintf_flat(fp, self->parent, total_samples); |
737 | 752 | ||
738 | 753 | ||
739 | list_for_each_entry(chain, &self->val, list) { | 754 | list_for_each_entry(chain, &self->val, list) { |
@@ -749,44 +764,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
749 | return ret; | 764 | return ret; |
750 | } | 765 | } |
751 | 766 | ||
752 | static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | 767 | static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self, |
753 | u64 total_samples, int left_margin, | 768 | u64 total_samples) |
754 | FILE *fp) | ||
755 | { | 769 | { |
756 | struct rb_node *rb_node; | ||
757 | struct callchain_node *chain; | ||
758 | size_t ret = 0; | 770 | size_t ret = 0; |
759 | u32 entries_printed = 0; | 771 | u32 entries_printed = 0; |
772 | struct rb_node *rb_node; | ||
773 | struct callchain_node *chain; | ||
760 | 774 | ||
761 | rb_node = rb_first(&he->sorted_chain); | 775 | rb_node = rb_first(self); |
762 | while (rb_node) { | 776 | while (rb_node) { |
763 | double percent; | 777 | double percent; |
764 | 778 | ||
765 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | 779 | chain = rb_entry(rb_node, struct callchain_node, rb_node); |
766 | percent = chain->hit * 100.0 / total_samples; | 780 | percent = chain->hit * 100.0 / total_samples; |
767 | switch (callchain_param.mode) { | 781 | |
768 | case CHAIN_FLAT: | 782 | ret = percent_color_fprintf(fp, " %6.2f%%\n", percent); |
769 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | 783 | ret += __callchain__fprintf_flat(fp, chain, total_samples); |
770 | percent); | ||
771 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
772 | break; | ||
773 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
774 | case CHAIN_GRAPH_REL: | ||
775 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
776 | left_margin); | ||
777 | case CHAIN_NONE: | ||
778 | default: | ||
779 | break; | ||
780 | } | ||
781 | ret += fprintf(fp, "\n"); | 784 | ret += fprintf(fp, "\n"); |
782 | if (++entries_printed == callchain_param.print_limit) | 785 | if (++entries_printed == callchain_param.print_limit) |
783 | break; | 786 | break; |
787 | |||
784 | rb_node = rb_next(rb_node); | 788 | rb_node = rb_next(rb_node); |
785 | } | 789 | } |
786 | 790 | ||
787 | return ret; | 791 | return ret; |
788 | } | 792 | } |
789 | 793 | ||
794 | static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | ||
795 | u64 total_samples, int left_margin, | ||
796 | FILE *fp) | ||
797 | { | ||
798 | switch (callchain_param.mode) { | ||
799 | case CHAIN_GRAPH_REL: | ||
800 | return callchain__fprintf_graph(fp, &he->sorted_chain, he->period, | ||
801 | left_margin); | ||
802 | break; | ||
803 | case CHAIN_GRAPH_ABS: | ||
804 | return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, | ||
805 | left_margin); | ||
806 | break; | ||
807 | case CHAIN_FLAT: | ||
808 | return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); | ||
809 | break; | ||
810 | case CHAIN_NONE: | ||
811 | break; | ||
812 | default: | ||
813 | pr_err("Bad callchain mode\n"); | ||
814 | } | ||
815 | |||
816 | return 0; | ||
817 | } | ||
818 | |||
790 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 819 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
791 | { | 820 | { |
792 | struct rb_node *next = rb_first(&hists->entries); | 821 | struct rb_node *next = rb_first(&hists->entries); |