diff options
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 53f6697d014e..f556e5f6388b 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -23,6 +23,7 @@ struct hist_browser { | |||
23 | struct hists *hists; | 23 | struct hists *hists; |
24 | struct hist_entry *he_selection; | 24 | struct hist_entry *he_selection; |
25 | struct map_symbol *selection; | 25 | struct map_symbol *selection; |
26 | int print_seq; | ||
26 | bool has_symbols; | 27 | bool has_symbols; |
27 | }; | 28 | }; |
28 | 29 | ||
@@ -800,6 +801,196 @@ do_offset: | |||
800 | } | 801 | } |
801 | } | 802 | } |
802 | 803 | ||
804 | static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser, | ||
805 | struct callchain_node *chain_node, | ||
806 | u64 total, int level, | ||
807 | FILE *fp) | ||
808 | { | ||
809 | struct rb_node *node; | ||
810 | int offset = level * LEVEL_OFFSET_STEP; | ||
811 | u64 new_total, remaining; | ||
812 | int printed = 0; | ||
813 | |||
814 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
815 | new_total = chain_node->children_hit; | ||
816 | else | ||
817 | new_total = total; | ||
818 | |||
819 | remaining = new_total; | ||
820 | node = rb_first(&chain_node->rb_root); | ||
821 | while (node) { | ||
822 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
823 | struct rb_node *next = rb_next(node); | ||
824 | u64 cumul = callchain_cumul_hits(child); | ||
825 | struct callchain_list *chain; | ||
826 | char folded_sign = ' '; | ||
827 | int first = true; | ||
828 | int extra_offset = 0; | ||
829 | |||
830 | remaining -= cumul; | ||
831 | |||
832 | list_for_each_entry(chain, &child->val, list) { | ||
833 | char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; | ||
834 | const char *str; | ||
835 | bool was_first = first; | ||
836 | |||
837 | if (first) | ||
838 | first = false; | ||
839 | else | ||
840 | extra_offset = LEVEL_OFFSET_STEP; | ||
841 | |||
842 | folded_sign = callchain_list__folded(chain); | ||
843 | |||
844 | alloc_str = NULL; | ||
845 | str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
846 | if (was_first) { | ||
847 | double percent = cumul * 100.0 / new_total; | ||
848 | |||
849 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
850 | str = "Not enough memory!"; | ||
851 | else | ||
852 | str = alloc_str; | ||
853 | } | ||
854 | |||
855 | printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str); | ||
856 | free(alloc_str); | ||
857 | if (folded_sign == '+') | ||
858 | break; | ||
859 | } | ||
860 | |||
861 | if (folded_sign == '-') { | ||
862 | const int new_level = level + (extra_offset ? 2 : 1); | ||
863 | printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total, | ||
864 | new_level, fp); | ||
865 | } | ||
866 | |||
867 | node = next; | ||
868 | } | ||
869 | |||
870 | return printed; | ||
871 | } | ||
872 | |||
873 | static int hist_browser__fprintf_callchain_node(struct hist_browser *browser, | ||
874 | struct callchain_node *node, | ||
875 | int level, FILE *fp) | ||
876 | { | ||
877 | struct callchain_list *chain; | ||
878 | int offset = level * LEVEL_OFFSET_STEP; | ||
879 | char folded_sign = ' '; | ||
880 | int printed = 0; | ||
881 | |||
882 | list_for_each_entry(chain, &node->val, list) { | ||
883 | char ipstr[BITS_PER_LONG / 4 + 1], *s; | ||
884 | |||
885 | folded_sign = callchain_list__folded(chain); | ||
886 | s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
887 | printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s); | ||
888 | } | ||
889 | |||
890 | if (folded_sign == '-') | ||
891 | printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node, | ||
892 | browser->hists->stats.total_period, | ||
893 | level + 1, fp); | ||
894 | return printed; | ||
895 | } | ||
896 | |||
897 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, | ||
898 | struct rb_root *chain, int level, FILE *fp) | ||
899 | { | ||
900 | struct rb_node *nd; | ||
901 | int printed = 0; | ||
902 | |||
903 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | ||
904 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
905 | |||
906 | printed += hist_browser__fprintf_callchain_node(browser, node, level, fp); | ||
907 | } | ||
908 | |||
909 | return printed; | ||
910 | } | ||
911 | |||
912 | static int hist_browser__fprintf_entry(struct hist_browser *browser, | ||
913 | struct hist_entry *he, FILE *fp) | ||
914 | { | ||
915 | char s[8192]; | ||
916 | double percent; | ||
917 | int printed = 0; | ||
918 | char folded_sign = ' '; | ||
919 | |||
920 | if (symbol_conf.use_callchain) | ||
921 | folded_sign = hist_entry__folded(he); | ||
922 | |||
923 | hist_entry__snprintf(he, s, sizeof(s), browser->hists); | ||
924 | percent = (he->period * 100.0) / browser->hists->stats.total_period; | ||
925 | |||
926 | if (symbol_conf.use_callchain) | ||
927 | printed += fprintf(fp, "%c ", folded_sign); | ||
928 | |||
929 | printed += fprintf(fp, " %5.2f%%", percent); | ||
930 | |||
931 | if (symbol_conf.show_nr_samples) | ||
932 | printed += fprintf(fp, " %11u", he->nr_events); | ||
933 | |||
934 | if (symbol_conf.show_total_period) | ||
935 | printed += fprintf(fp, " %12" PRIu64, he->period); | ||
936 | |||
937 | printed += fprintf(fp, "%s\n", rtrim(s)); | ||
938 | |||
939 | if (folded_sign == '-') | ||
940 | printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp); | ||
941 | |||
942 | return printed; | ||
943 | } | ||
944 | |||
945 | static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) | ||
946 | { | ||
947 | struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries)); | ||
948 | int printed = 0; | ||
949 | |||
950 | while (nd) { | ||
951 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
952 | |||
953 | printed += hist_browser__fprintf_entry(browser, h, fp); | ||
954 | nd = hists__filter_entries(rb_next(nd)); | ||
955 | } | ||
956 | |||
957 | return printed; | ||
958 | } | ||
959 | |||
960 | static int hist_browser__dump(struct hist_browser *browser) | ||
961 | { | ||
962 | char filename[64]; | ||
963 | FILE *fp; | ||
964 | |||
965 | while (1) { | ||
966 | scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq); | ||
967 | if (access(filename, F_OK)) | ||
968 | break; | ||
969 | /* | ||
970 | * XXX: Just an arbitrary lazy upper limit | ||
971 | */ | ||
972 | if (++browser->print_seq == 8192) { | ||
973 | ui_helpline__fpush("Too many perf.hist.N files, nothing written!"); | ||
974 | return -1; | ||
975 | } | ||
976 | } | ||
977 | |||
978 | fp = fopen(filename, "w"); | ||
979 | if (fp == NULL) { | ||
980 | char bf[64]; | ||
981 | strerror_r(errno, bf, sizeof(bf)); | ||
982 | ui_helpline__fpush("Couldn't write to %s: %s", filename, bf); | ||
983 | return -1; | ||
984 | } | ||
985 | |||
986 | ++browser->print_seq; | ||
987 | hist_browser__fprintf(browser, fp); | ||
988 | fclose(fp); | ||
989 | ui_helpline__fpush("%s written!", filename); | ||
990 | |||
991 | return 0; | ||
992 | } | ||
993 | |||
803 | static struct hist_browser *hist_browser__new(struct hists *hists) | 994 | static struct hist_browser *hist_browser__new(struct hists *hists) |
804 | { | 995 | { |
805 | struct hist_browser *browser = zalloc(sizeof(*browser)); | 996 | struct hist_browser *browser = zalloc(sizeof(*browser)); |
@@ -937,6 +1128,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
937 | browser->selection->map->dso->annotate_warned) | 1128 | browser->selection->map->dso->annotate_warned) |
938 | continue; | 1129 | continue; |
939 | goto do_annotate; | 1130 | goto do_annotate; |
1131 | case 'P': | ||
1132 | hist_browser__dump(browser); | ||
1133 | continue; | ||
940 | case 'd': | 1134 | case 'd': |
941 | goto zoom_dso; | 1135 | goto zoom_dso; |
942 | case 't': | 1136 | case 't': |
@@ -969,6 +1163,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
969 | "E Expand all callchains\n" | 1163 | "E Expand all callchains\n" |
970 | "d Zoom into current DSO\n" | 1164 | "d Zoom into current DSO\n" |
971 | "t Zoom into current Thread\n" | 1165 | "t Zoom into current Thread\n" |
1166 | "P Print histograms to perf.hist.N\n" | ||
972 | "/ Filter symbol by name"); | 1167 | "/ Filter symbol by name"); |
973 | continue; | 1168 | continue; |
974 | case K_ENTER: | 1169 | case K_ENTER: |