diff options
-rw-r--r-- | tools/perf/Makefile | 5 | ||||
-rw-r--r-- | tools/perf/builtin-annotate.c | 198 | ||||
-rw-r--r-- | tools/perf/builtin-report.c | 19 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 184 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 29 | ||||
-rw-r--r-- | tools/perf/util/newt.c | 352 | ||||
-rw-r--r-- | tools/perf/util/sort.h | 6 |
7 files changed, 544 insertions, 249 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0797786aa72a..9c4dc30cdc13 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -560,6 +560,8 @@ ifneq ($(shell sh -c "(echo '\#include <newt.h>'; echo 'int main(void) { newtIni | |||
560 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); | 560 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); |
561 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT | 561 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT |
562 | else | 562 | else |
563 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | ||
564 | BASIC_CFLAGS += -I/usr/include/slang | ||
563 | EXTLIBS += -lnewt | 565 | EXTLIBS += -lnewt |
564 | LIB_OBJS += $(OUTPUT)util/newt.o | 566 | LIB_OBJS += $(OUTPUT)util/newt.o |
565 | endif | 567 | endif |
@@ -948,6 +950,9 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS | |||
948 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS | 950 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS |
949 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 951 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
950 | 952 | ||
953 | $(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS | ||
954 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | ||
955 | |||
951 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS | 956 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS |
952 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 957 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
953 | 958 | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 3940964161b3..fd1b786c8f35 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -34,68 +34,8 @@ static bool full_paths; | |||
34 | 34 | ||
35 | static bool print_line; | 35 | static bool print_line; |
36 | 36 | ||
37 | struct sym_hist { | ||
38 | u64 sum; | ||
39 | u64 ip[0]; | ||
40 | }; | ||
41 | |||
42 | struct sym_ext { | ||
43 | struct rb_node node; | ||
44 | double percent; | ||
45 | char *path; | ||
46 | }; | ||
47 | |||
48 | struct sym_priv { | ||
49 | struct sym_hist *hist; | ||
50 | struct sym_ext *ext; | ||
51 | }; | ||
52 | |||
53 | static const char *sym_hist_filter; | 37 | static const char *sym_hist_filter; |
54 | 38 | ||
55 | static int sym__alloc_hist(struct symbol *self) | ||
56 | { | ||
57 | struct sym_priv *priv = symbol__priv(self); | ||
58 | const int size = (sizeof(*priv->hist) + | ||
59 | (self->end - self->start) * sizeof(u64)); | ||
60 | |||
61 | priv->hist = zalloc(size); | ||
62 | return priv->hist == NULL ? -1 : 0; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * collect histogram counts | ||
67 | */ | ||
68 | static int annotate__hist_hit(struct hist_entry *he, u64 ip) | ||
69 | { | ||
70 | unsigned int sym_size, offset; | ||
71 | struct symbol *sym = he->ms.sym; | ||
72 | struct sym_priv *priv; | ||
73 | struct sym_hist *h; | ||
74 | |||
75 | if (!sym || !he->ms.map) | ||
76 | return 0; | ||
77 | |||
78 | priv = symbol__priv(sym); | ||
79 | if (priv->hist == NULL && sym__alloc_hist(sym) < 0) | ||
80 | return -ENOMEM; | ||
81 | |||
82 | sym_size = sym->end - sym->start; | ||
83 | offset = ip - sym->start; | ||
84 | |||
85 | pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); | ||
86 | |||
87 | if (offset >= sym_size) | ||
88 | return 0; | ||
89 | |||
90 | h = priv->hist; | ||
91 | h->sum++; | ||
92 | h->ip[offset]++; | ||
93 | |||
94 | pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, | ||
95 | he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int hists__add_entry(struct hists *self, struct addr_location *al) | 39 | static int hists__add_entry(struct hists *self, struct addr_location *al) |
100 | { | 40 | { |
101 | struct hist_entry *he; | 41 | struct hist_entry *he; |
@@ -115,7 +55,7 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) | |||
115 | if (he == NULL) | 55 | if (he == NULL) |
116 | return -ENOMEM; | 56 | return -ENOMEM; |
117 | 57 | ||
118 | return annotate__hist_hit(he, al->addr); | 58 | return hist_entry__inc_addr_samples(he, al->addr); |
119 | } | 59 | } |
120 | 60 | ||
121 | static int process_sample_event(event_t *event, struct perf_session *session) | 61 | static int process_sample_event(event_t *event, struct perf_session *session) |
@@ -140,101 +80,6 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
140 | return 0; | 80 | return 0; |
141 | } | 81 | } |
142 | 82 | ||
143 | struct objdump_line { | ||
144 | struct list_head node; | ||
145 | s64 offset; | ||
146 | char *line; | ||
147 | }; | ||
148 | |||
149 | static struct objdump_line *objdump_line__new(s64 offset, char *line) | ||
150 | { | ||
151 | struct objdump_line *self = malloc(sizeof(*self)); | ||
152 | |||
153 | if (self != NULL) { | ||
154 | self->offset = offset; | ||
155 | self->line = line; | ||
156 | } | ||
157 | |||
158 | return self; | ||
159 | } | ||
160 | |||
161 | static void objdump_line__free(struct objdump_line *self) | ||
162 | { | ||
163 | free(self->line); | ||
164 | free(self); | ||
165 | } | ||
166 | |||
167 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
168 | { | ||
169 | list_add_tail(&line->node, head); | ||
170 | } | ||
171 | |||
172 | static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
173 | struct objdump_line *pos) | ||
174 | { | ||
175 | list_for_each_entry_continue(pos, head, node) | ||
176 | if (pos->offset >= 0) | ||
177 | return pos; | ||
178 | |||
179 | return NULL; | ||
180 | } | ||
181 | |||
182 | static int parse_line(FILE *file, struct hist_entry *he, | ||
183 | struct list_head *head) | ||
184 | { | ||
185 | struct symbol *sym = he->ms.sym; | ||
186 | struct objdump_line *objdump_line; | ||
187 | char *line = NULL, *tmp, *tmp2; | ||
188 | size_t line_len; | ||
189 | s64 line_ip, offset = -1; | ||
190 | char *c; | ||
191 | |||
192 | if (getline(&line, &line_len, file) < 0) | ||
193 | return -1; | ||
194 | |||
195 | if (!line) | ||
196 | return -1; | ||
197 | |||
198 | c = strchr(line, '\n'); | ||
199 | if (c) | ||
200 | *c = 0; | ||
201 | |||
202 | line_ip = -1; | ||
203 | |||
204 | /* | ||
205 | * Strip leading spaces: | ||
206 | */ | ||
207 | tmp = line; | ||
208 | while (*tmp) { | ||
209 | if (*tmp != ' ') | ||
210 | break; | ||
211 | tmp++; | ||
212 | } | ||
213 | |||
214 | if (*tmp) { | ||
215 | /* | ||
216 | * Parse hexa addresses followed by ':' | ||
217 | */ | ||
218 | line_ip = strtoull(tmp, &tmp2, 16); | ||
219 | if (*tmp2 != ':') | ||
220 | line_ip = -1; | ||
221 | } | ||
222 | |||
223 | if (line_ip != -1) { | ||
224 | u64 start = map__rip_2objdump(he->ms.map, sym->start); | ||
225 | offset = line_ip - start; | ||
226 | } | ||
227 | |||
228 | objdump_line = objdump_line__new(offset, line); | ||
229 | if (objdump_line == NULL) { | ||
230 | free(line); | ||
231 | return -1; | ||
232 | } | ||
233 | objdump__add_line(head, objdump_line); | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int objdump_line__print(struct objdump_line *self, | 83 | static int objdump_line__print(struct objdump_line *self, |
239 | struct list_head *head, | 84 | struct list_head *head, |
240 | struct hist_entry *he, u64 len) | 85 | struct hist_entry *he, u64 len) |
@@ -439,27 +284,11 @@ static void annotate_sym(struct hist_entry *he) | |||
439 | struct symbol *sym = he->ms.sym; | 284 | struct symbol *sym = he->ms.sym; |
440 | const char *filename = dso->long_name, *d_filename; | 285 | const char *filename = dso->long_name, *d_filename; |
441 | u64 len; | 286 | u64 len; |
442 | char command[PATH_MAX*2]; | ||
443 | FILE *file; | ||
444 | LIST_HEAD(head); | 287 | LIST_HEAD(head); |
445 | struct objdump_line *pos, *n; | 288 | struct objdump_line *pos, *n; |
446 | 289 | ||
447 | if (!filename) | 290 | if (hist_entry__annotate(he, &head) < 0) |
448 | return; | ||
449 | |||
450 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
451 | if (dso->annotate_warned) | ||
452 | return; | ||
453 | dso->annotate_warned = 1; | ||
454 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
455 | "path:\n", sym->name); | ||
456 | vmlinux_path__fprintf(stderr); | ||
457 | return; | 291 | return; |
458 | } | ||
459 | |||
460 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | ||
461 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
462 | map->unmap_ip(map, sym->end)); | ||
463 | 292 | ||
464 | if (full_paths) | 293 | if (full_paths) |
465 | d_filename = filename; | 294 | d_filename = filename; |
@@ -477,29 +306,6 @@ static void annotate_sym(struct hist_entry *he) | |||
477 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); | 306 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); |
478 | printf("------------------------------------------------\n"); | 307 | printf("------------------------------------------------\n"); |
479 | 308 | ||
480 | if (verbose >= 2) | ||
481 | printf("annotating [%p] %30s : [%p] %30s\n", | ||
482 | dso, dso->long_name, sym, sym->name); | ||
483 | |||
484 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", | ||
485 | map__rip_2objdump(map, sym->start), | ||
486 | map__rip_2objdump(map, sym->end), | ||
487 | filename, filename); | ||
488 | |||
489 | if (verbose >= 3) | ||
490 | printf("doing: %s\n", command); | ||
491 | |||
492 | file = popen(command, "r"); | ||
493 | if (!file) | ||
494 | return; | ||
495 | |||
496 | while (!feof(file)) { | ||
497 | if (parse_line(file, he, &head) < 0) | ||
498 | break; | ||
499 | } | ||
500 | |||
501 | pclose(file); | ||
502 | |||
503 | if (verbose) | 309 | if (verbose) |
504 | hist_entry__print_hits(he); | 310 | hist_entry__print_hits(he); |
505 | 311 | ||
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3d67d6bf22cf..04de3387de3f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -106,8 +106,18 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
106 | if (he == NULL) | 106 | if (he == NULL) |
107 | goto out_free_syms; | 107 | goto out_free_syms; |
108 | err = 0; | 108 | err = 0; |
109 | if (symbol_conf.use_callchain) | 109 | if (symbol_conf.use_callchain) { |
110 | err = append_chain(he->callchain, data->callchain, syms); | 110 | err = append_chain(he->callchain, data->callchain, syms); |
111 | if (err) | ||
112 | goto out_free_syms; | ||
113 | } | ||
114 | /* | ||
115 | * Only in the newt browser we are doing integrated annotation, | ||
116 | * so we don't allocated the extra space needed because the stdio | ||
117 | * code will not use it. | ||
118 | */ | ||
119 | if (use_browser) | ||
120 | err = hist_entry__inc_addr_samples(he, al->addr); | ||
111 | out_free_syms: | 121 | out_free_syms: |
112 | free(syms); | 122 | free(syms); |
113 | return err; | 123 | return err; |
@@ -458,6 +468,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
458 | 468 | ||
459 | if (strcmp(input_name, "-") != 0) | 469 | if (strcmp(input_name, "-") != 0) |
460 | setup_browser(); | 470 | setup_browser(); |
471 | /* | ||
472 | * Only in the newt browser we are doing integrated annotation, | ||
473 | * so don't allocate extra space that won't be used in the stdio | ||
474 | * implementation. | ||
475 | */ | ||
476 | if (use_browser) | ||
477 | symbol_conf.priv_size = sizeof(struct sym_priv); | ||
461 | 478 | ||
462 | if (symbol__init() < 0) | 479 | if (symbol__init() < 0) |
463 | return -1; | 480 | return -1; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index baa55be64d9e..451d2e45d843 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -843,3 +843,187 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
843 | } | 843 | } |
844 | } | 844 | } |
845 | } | 845 | } |
846 | |||
847 | static int symbol__alloc_hist(struct symbol *self) | ||
848 | { | ||
849 | struct sym_priv *priv = symbol__priv(self); | ||
850 | const int size = (sizeof(*priv->hist) + | ||
851 | (self->end - self->start) * sizeof(u64)); | ||
852 | |||
853 | priv->hist = zalloc(size); | ||
854 | return priv->hist == NULL ? -1 : 0; | ||
855 | } | ||
856 | |||
857 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | ||
858 | { | ||
859 | unsigned int sym_size, offset; | ||
860 | struct symbol *sym = self->ms.sym; | ||
861 | struct sym_priv *priv; | ||
862 | struct sym_hist *h; | ||
863 | |||
864 | if (!sym || !self->ms.map) | ||
865 | return 0; | ||
866 | |||
867 | priv = symbol__priv(sym); | ||
868 | if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | ||
869 | return -ENOMEM; | ||
870 | |||
871 | sym_size = sym->end - sym->start; | ||
872 | offset = ip - sym->start; | ||
873 | |||
874 | pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | ||
875 | |||
876 | if (offset >= sym_size) | ||
877 | return 0; | ||
878 | |||
879 | h = priv->hist; | ||
880 | h->sum++; | ||
881 | h->ip[offset]++; | ||
882 | |||
883 | pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, | ||
884 | self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); | ||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static struct objdump_line *objdump_line__new(s64 offset, char *line) | ||
889 | { | ||
890 | struct objdump_line *self = malloc(sizeof(*self)); | ||
891 | |||
892 | if (self != NULL) { | ||
893 | self->offset = offset; | ||
894 | self->line = line; | ||
895 | } | ||
896 | |||
897 | return self; | ||
898 | } | ||
899 | |||
900 | void objdump_line__free(struct objdump_line *self) | ||
901 | { | ||
902 | free(self->line); | ||
903 | free(self); | ||
904 | } | ||
905 | |||
906 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
907 | { | ||
908 | list_add_tail(&line->node, head); | ||
909 | } | ||
910 | |||
911 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
912 | struct objdump_line *pos) | ||
913 | { | ||
914 | list_for_each_entry_continue(pos, head, node) | ||
915 | if (pos->offset >= 0) | ||
916 | return pos; | ||
917 | |||
918 | return NULL; | ||
919 | } | ||
920 | |||
921 | static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | ||
922 | struct list_head *head) | ||
923 | { | ||
924 | struct symbol *sym = self->ms.sym; | ||
925 | struct objdump_line *objdump_line; | ||
926 | char *line = NULL, *tmp, *tmp2, *c; | ||
927 | size_t line_len; | ||
928 | s64 line_ip, offset = -1; | ||
929 | |||
930 | if (getline(&line, &line_len, file) < 0) | ||
931 | return -1; | ||
932 | |||
933 | if (!line) | ||
934 | return -1; | ||
935 | |||
936 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
937 | line[--line_len] = '\0'; | ||
938 | |||
939 | c = strchr(line, '\n'); | ||
940 | if (c) | ||
941 | *c = 0; | ||
942 | |||
943 | line_ip = -1; | ||
944 | |||
945 | /* | ||
946 | * Strip leading spaces: | ||
947 | */ | ||
948 | tmp = line; | ||
949 | while (*tmp) { | ||
950 | if (*tmp != ' ') | ||
951 | break; | ||
952 | tmp++; | ||
953 | } | ||
954 | |||
955 | if (*tmp) { | ||
956 | /* | ||
957 | * Parse hexa addresses followed by ':' | ||
958 | */ | ||
959 | line_ip = strtoull(tmp, &tmp2, 16); | ||
960 | if (*tmp2 != ':') | ||
961 | line_ip = -1; | ||
962 | } | ||
963 | |||
964 | if (line_ip != -1) { | ||
965 | u64 start = map__rip_2objdump(self->ms.map, sym->start); | ||
966 | offset = line_ip - start; | ||
967 | } | ||
968 | |||
969 | objdump_line = objdump_line__new(offset, line); | ||
970 | if (objdump_line == NULL) { | ||
971 | free(line); | ||
972 | return -1; | ||
973 | } | ||
974 | objdump__add_line(head, objdump_line); | ||
975 | |||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head) | ||
980 | { | ||
981 | struct symbol *sym = self->ms.sym; | ||
982 | struct map *map = self->ms.map; | ||
983 | struct dso *dso = map->dso; | ||
984 | const char *filename = dso->long_name; | ||
985 | char command[PATH_MAX * 2]; | ||
986 | FILE *file; | ||
987 | u64 len; | ||
988 | |||
989 | if (!filename) | ||
990 | return -1; | ||
991 | |||
992 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
993 | if (dso->annotate_warned) | ||
994 | return 0; | ||
995 | dso->annotate_warned = 1; | ||
996 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
997 | "path:\n", sym->name); | ||
998 | vmlinux_path__fprintf(stderr); | ||
999 | return -1; | ||
1000 | } | ||
1001 | |||
1002 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | ||
1003 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
1004 | map->unmap_ip(map, sym->end)); | ||
1005 | |||
1006 | len = sym->end - sym->start; | ||
1007 | |||
1008 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
1009 | dso, dso->long_name, sym, sym->name); | ||
1010 | |||
1011 | snprintf(command, sizeof(command), | ||
1012 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", | ||
1013 | map__rip_2objdump(map, sym->start), | ||
1014 | map__rip_2objdump(map, sym->end), | ||
1015 | filename, filename); | ||
1016 | |||
1017 | pr_debug("Executing: %s\n", command); | ||
1018 | |||
1019 | file = popen(command, "r"); | ||
1020 | if (!file) | ||
1021 | return -1; | ||
1022 | |||
1023 | while (!feof(file)) | ||
1024 | if (hist_entry__parse_objdump_line(self, file, head) < 0) | ||
1025 | break; | ||
1026 | |||
1027 | pclose(file); | ||
1028 | return 0; | ||
1029 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1c5f93ac5ab7..ed9c06734965 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -11,6 +11,32 @@ struct addr_location; | |||
11 | struct symbol; | 11 | struct symbol; |
12 | struct rb_root; | 12 | struct rb_root; |
13 | 13 | ||
14 | struct objdump_line { | ||
15 | struct list_head node; | ||
16 | s64 offset; | ||
17 | char *line; | ||
18 | }; | ||
19 | |||
20 | void objdump_line__free(struct objdump_line *self); | ||
21 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
22 | struct objdump_line *pos); | ||
23 | |||
24 | struct sym_hist { | ||
25 | u64 sum; | ||
26 | u64 ip[0]; | ||
27 | }; | ||
28 | |||
29 | struct sym_ext { | ||
30 | struct rb_node node; | ||
31 | double percent; | ||
32 | char *path; | ||
33 | }; | ||
34 | |||
35 | struct sym_priv { | ||
36 | struct sym_hist *hist; | ||
37 | struct sym_ext *ext; | ||
38 | }; | ||
39 | |||
14 | struct events_stats { | 40 | struct events_stats { |
15 | u64 total; | 41 | u64 total; |
16 | u64 lost; | 42 | u64 lost; |
@@ -45,6 +71,9 @@ void hists__collapse_resort(struct hists *self); | |||
45 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 71 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
46 | bool show_displacement, FILE *fp); | 72 | bool show_displacement, FILE *fp); |
47 | 73 | ||
74 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); | ||
75 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head); | ||
76 | |||
48 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | 77 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); |
49 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | 78 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); |
50 | 79 | ||
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index daa86efffce6..ba6acd04c082 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <stdio.h> | 2 | #include <stdio.h> |
3 | #undef _GNU_SOURCE | 3 | #undef _GNU_SOURCE |
4 | 4 | ||
5 | #include <slang.h> | ||
5 | #include <stdlib.h> | 6 | #include <stdlib.h> |
6 | #include <newt.h> | 7 | #include <newt.h> |
7 | #include <sys/ttydefaults.h> | 8 | #include <sys/ttydefaults.h> |
@@ -171,6 +172,254 @@ static bool dialog_yesno(const char *msg) | |||
171 | return newtWinChoice(NULL, yes, no, (char *)msg) == 1; | 172 | return newtWinChoice(NULL, yes, no, (char *)msg) == 1; |
172 | } | 173 | } |
173 | 174 | ||
175 | #define HE_COLORSET_TOP 50 | ||
176 | #define HE_COLORSET_MEDIUM 51 | ||
177 | #define HE_COLORSET_NORMAL 52 | ||
178 | #define HE_COLORSET_SELECTED 53 | ||
179 | #define HE_COLORSET_CODE 54 | ||
180 | |||
181 | static int ui_browser__percent_color(double percent, bool current) | ||
182 | { | ||
183 | if (current) | ||
184 | return HE_COLORSET_SELECTED; | ||
185 | if (percent >= MIN_RED) | ||
186 | return HE_COLORSET_TOP; | ||
187 | if (percent >= MIN_GREEN) | ||
188 | return HE_COLORSET_MEDIUM; | ||
189 | return HE_COLORSET_NORMAL; | ||
190 | } | ||
191 | |||
192 | struct ui_browser { | ||
193 | newtComponent form, sb; | ||
194 | u64 index, first_visible_entry_idx; | ||
195 | void *first_visible_entry, *entries; | ||
196 | u16 top, left, width, height; | ||
197 | void *priv; | ||
198 | u32 nr_entries; | ||
199 | }; | ||
200 | |||
201 | static void ui_browser__refresh_dimensions(struct ui_browser *self) | ||
202 | { | ||
203 | int cols, rows; | ||
204 | newtGetScreenSize(&cols, &rows); | ||
205 | |||
206 | if (self->width > cols - 4) | ||
207 | self->width = cols - 4; | ||
208 | self->height = rows - 5; | ||
209 | if (self->height > self->nr_entries) | ||
210 | self->height = self->nr_entries; | ||
211 | self->top = (rows - self->height) / 2; | ||
212 | self->left = (cols - self->width) / 2; | ||
213 | } | ||
214 | |||
215 | static void ui_browser__reset_index(struct ui_browser *self) | ||
216 | { | ||
217 | self->index = self->first_visible_entry_idx = 0; | ||
218 | self->first_visible_entry = NULL; | ||
219 | } | ||
220 | |||
221 | static int objdump_line__show(struct objdump_line *self, struct list_head *head, | ||
222 | int width, struct hist_entry *he, int len, | ||
223 | bool current_entry) | ||
224 | { | ||
225 | if (self->offset != -1) { | ||
226 | struct symbol *sym = he->ms.sym; | ||
227 | unsigned int hits = 0; | ||
228 | double percent = 0.0; | ||
229 | int color; | ||
230 | struct sym_priv *priv = symbol__priv(sym); | ||
231 | struct sym_ext *sym_ext = priv->ext; | ||
232 | struct sym_hist *h = priv->hist; | ||
233 | s64 offset = self->offset; | ||
234 | struct objdump_line *next = objdump__get_next_ip_line(head, self); | ||
235 | |||
236 | while (offset < (s64)len && | ||
237 | (next == NULL || offset < next->offset)) { | ||
238 | if (sym_ext) { | ||
239 | percent += sym_ext[offset].percent; | ||
240 | } else | ||
241 | hits += h->ip[offset]; | ||
242 | |||
243 | ++offset; | ||
244 | } | ||
245 | |||
246 | if (sym_ext == NULL && h->sum) | ||
247 | percent = 100.0 * hits / h->sum; | ||
248 | |||
249 | color = ui_browser__percent_color(percent, current_entry); | ||
250 | SLsmg_set_color(color); | ||
251 | SLsmg_printf(" %7.2f ", percent); | ||
252 | if (!current_entry) | ||
253 | SLsmg_set_color(HE_COLORSET_CODE); | ||
254 | } else { | ||
255 | int color = ui_browser__percent_color(0, current_entry); | ||
256 | SLsmg_set_color(color); | ||
257 | SLsmg_write_nstring(" ", 9); | ||
258 | } | ||
259 | |||
260 | SLsmg_write_char(':'); | ||
261 | SLsmg_write_nstring(" ", 8); | ||
262 | if (!*self->line) | ||
263 | SLsmg_write_nstring(" ", width - 18); | ||
264 | else | ||
265 | SLsmg_write_nstring(self->line, width - 18); | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int ui_browser__refresh_entries(struct ui_browser *self) | ||
271 | { | ||
272 | struct objdump_line *pos; | ||
273 | struct list_head *head = self->entries; | ||
274 | struct hist_entry *he = self->priv; | ||
275 | int row = 0; | ||
276 | int len = he->ms.sym->end - he->ms.sym->start; | ||
277 | |||
278 | if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) | ||
279 | self->first_visible_entry = head->next; | ||
280 | |||
281 | pos = list_entry(self->first_visible_entry, struct objdump_line, node); | ||
282 | |||
283 | list_for_each_entry_from(pos, head, node) { | ||
284 | bool current_entry = (self->first_visible_entry_idx + row) == self->index; | ||
285 | SLsmg_gotorc(self->top + row, self->left); | ||
286 | objdump_line__show(pos, head, self->width, | ||
287 | he, len, current_entry); | ||
288 | if (++row == self->height) | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | SLsmg_set_color(HE_COLORSET_NORMAL); | ||
293 | SLsmg_fill_region(self->top + row, self->left, | ||
294 | self->height - row, self->width, ' '); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int ui_browser__run(struct ui_browser *self, const char *title, | ||
300 | struct newtExitStruct *es) | ||
301 | { | ||
302 | if (self->form) { | ||
303 | newtFormDestroy(self->form); | ||
304 | newtPopWindow(); | ||
305 | } | ||
306 | |||
307 | ui_browser__refresh_dimensions(self); | ||
308 | newtCenteredWindow(self->width + 2, self->height, title); | ||
309 | self->form = newt_form__new(); | ||
310 | if (self->form == NULL) | ||
311 | return -1; | ||
312 | |||
313 | self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, | ||
314 | HE_COLORSET_NORMAL, | ||
315 | HE_COLORSET_SELECTED); | ||
316 | if (self->sb == NULL) | ||
317 | return -1; | ||
318 | |||
319 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | ||
320 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | ||
321 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | ||
322 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | ||
323 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | ||
324 | newtFormAddHotKey(self->form, NEWT_KEY_END); | ||
325 | |||
326 | if (ui_browser__refresh_entries(self) < 0) | ||
327 | return -1; | ||
328 | newtFormAddComponent(self->form, self->sb); | ||
329 | |||
330 | while (1) { | ||
331 | unsigned int offset; | ||
332 | |||
333 | newtFormRun(self->form, es); | ||
334 | |||
335 | if (es->reason != NEWT_EXIT_HOTKEY) | ||
336 | break; | ||
337 | switch (es->u.key) { | ||
338 | case NEWT_KEY_DOWN: | ||
339 | if (self->index == self->nr_entries - 1) | ||
340 | break; | ||
341 | ++self->index; | ||
342 | if (self->index == self->first_visible_entry_idx + self->height) { | ||
343 | struct list_head *pos = self->first_visible_entry; | ||
344 | ++self->first_visible_entry_idx; | ||
345 | self->first_visible_entry = pos->next; | ||
346 | } | ||
347 | break; | ||
348 | case NEWT_KEY_UP: | ||
349 | if (self->index == 0) | ||
350 | break; | ||
351 | --self->index; | ||
352 | if (self->index < self->first_visible_entry_idx) { | ||
353 | struct list_head *pos = self->first_visible_entry; | ||
354 | --self->first_visible_entry_idx; | ||
355 | self->first_visible_entry = pos->prev; | ||
356 | } | ||
357 | break; | ||
358 | case NEWT_KEY_PGDN: | ||
359 | if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) | ||
360 | break; | ||
361 | |||
362 | offset = self->height; | ||
363 | if (self->index + offset > self->nr_entries - 1) | ||
364 | offset = self->nr_entries - 1 - self->index; | ||
365 | self->index += offset; | ||
366 | self->first_visible_entry_idx += offset; | ||
367 | |||
368 | while (offset--) { | ||
369 | struct list_head *pos = self->first_visible_entry; | ||
370 | self->first_visible_entry = pos->next; | ||
371 | } | ||
372 | |||
373 | break; | ||
374 | case NEWT_KEY_PGUP: | ||
375 | if (self->first_visible_entry_idx == 0) | ||
376 | break; | ||
377 | |||
378 | if (self->first_visible_entry_idx < self->height) | ||
379 | offset = self->first_visible_entry_idx; | ||
380 | else | ||
381 | offset = self->height; | ||
382 | |||
383 | self->index -= offset; | ||
384 | self->first_visible_entry_idx -= offset; | ||
385 | |||
386 | while (offset--) { | ||
387 | struct list_head *pos = self->first_visible_entry; | ||
388 | self->first_visible_entry = pos->prev; | ||
389 | } | ||
390 | break; | ||
391 | case NEWT_KEY_HOME: | ||
392 | ui_browser__reset_index(self); | ||
393 | break; | ||
394 | case NEWT_KEY_END: { | ||
395 | struct list_head *head = self->entries; | ||
396 | offset = self->height - 1; | ||
397 | |||
398 | if (offset > self->nr_entries) | ||
399 | offset = self->nr_entries; | ||
400 | |||
401 | self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; | ||
402 | self->first_visible_entry = head->prev; | ||
403 | while (offset-- != 0) { | ||
404 | struct list_head *pos = self->first_visible_entry; | ||
405 | self->first_visible_entry = pos->prev; | ||
406 | } | ||
407 | } | ||
408 | break; | ||
409 | case NEWT_KEY_ESCAPE: | ||
410 | case CTRL('c'): | ||
411 | case 'Q': | ||
412 | case 'q': | ||
413 | return 0; | ||
414 | default: | ||
415 | continue; | ||
416 | } | ||
417 | if (ui_browser__refresh_entries(self) < 0) | ||
418 | return -1; | ||
419 | } | ||
420 | return 0; | ||
421 | } | ||
422 | |||
174 | /* | 423 | /* |
175 | * When debugging newt problems it was useful to be able to "unroll" | 424 | * When debugging newt problems it was useful to be able to "unroll" |
176 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate | 425 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate |
@@ -353,62 +602,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self, | |||
353 | return ret; | 602 | return ret; |
354 | } | 603 | } |
355 | 604 | ||
356 | static void map_symbol__annotate_browser(const struct map_symbol *self, | 605 | static void hist_entry__annotate_browser(struct hist_entry *self) |
357 | const char *input_name) | ||
358 | { | 606 | { |
359 | FILE *fp; | 607 | struct ui_browser browser; |
360 | int cols, rows; | ||
361 | newtComponent form, tree; | ||
362 | struct newtExitStruct es; | 608 | struct newtExitStruct es; |
363 | char *str; | 609 | struct objdump_line *pos, *n; |
364 | size_t line_len, max_line_len = 0; | 610 | LIST_HEAD(head); |
365 | size_t max_usable_width; | ||
366 | char *line = NULL; | ||
367 | 611 | ||
368 | if (self->sym == NULL) | 612 | if (self->ms.sym == NULL) |
369 | return; | 613 | return; |
370 | 614 | ||
371 | if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", | 615 | if (hist_entry__annotate(self, &head) < 0) |
372 | input_name, self->map->dso->name, self->sym->name) < 0) | ||
373 | return; | 616 | return; |
374 | 617 | ||
375 | fp = popen(str, "r"); | ||
376 | if (fp == NULL) | ||
377 | goto out_free_str; | ||
378 | |||
379 | ui_helpline__push("Press ESC to exit"); | 618 | ui_helpline__push("Press ESC to exit"); |
380 | newtGetScreenSize(&cols, &rows); | ||
381 | tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); | ||
382 | |||
383 | while (!feof(fp)) { | ||
384 | if (getline(&line, &line_len, fp) < 0 || !line_len) | ||
385 | break; | ||
386 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
387 | line[--line_len] = '\0'; | ||
388 | 619 | ||
389 | if (line_len > max_line_len) | 620 | memset(&browser, 0, sizeof(browser)); |
390 | max_line_len = line_len; | 621 | browser.entries = &head; |
391 | newtListboxAppendEntry(tree, line, NULL); | 622 | browser.priv = self; |
623 | list_for_each_entry(pos, &head, node) { | ||
624 | size_t line_len = strlen(pos->line); | ||
625 | if (browser.width < line_len) | ||
626 | browser.width = line_len; | ||
627 | ++browser.nr_entries; | ||
392 | } | 628 | } |
393 | fclose(fp); | ||
394 | free(line); | ||
395 | |||
396 | max_usable_width = cols - 22; | ||
397 | if (max_line_len > max_usable_width) | ||
398 | max_line_len = max_usable_width; | ||
399 | |||
400 | newtListboxSetWidth(tree, max_line_len); | ||
401 | 629 | ||
402 | newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); | 630 | browser.width += 18; /* Percentage */ |
403 | form = newt_form__new(); | 631 | ui_browser__run(&browser, self->ms.sym->name, &es); |
404 | newtFormAddComponent(form, tree); | 632 | newtFormDestroy(browser.form); |
405 | |||
406 | newtFormRun(form, &es); | ||
407 | newtFormDestroy(form); | ||
408 | newtPopWindow(); | 633 | newtPopWindow(); |
634 | list_for_each_entry_safe(pos, n, &head, node) { | ||
635 | list_del(&pos->node); | ||
636 | objdump_line__free(pos); | ||
637 | } | ||
409 | ui_helpline__pop(); | 638 | ui_helpline__pop(); |
410 | out_free_str: | ||
411 | free(str); | ||
412 | } | 639 | } |
413 | 640 | ||
414 | static const void *newt__symbol_tree_get_current(newtComponent self) | 641 | static const void *newt__symbol_tree_get_current(newtComponent self) |
@@ -527,7 +754,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists | |||
527 | return 0; | 754 | return 0; |
528 | } | 755 | } |
529 | 756 | ||
530 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | 757 | static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) |
531 | { | 758 | { |
532 | int *indexes; | 759 | int *indexes; |
533 | 760 | ||
@@ -543,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) | |||
543 | } | 770 | } |
544 | return NULL; | 771 | return NULL; |
545 | out: | 772 | out: |
546 | return *(struct thread **)(self->selection + 1); | 773 | return container_of(self->selection, struct hist_entry, ms); |
774 | } | ||
775 | |||
776 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | ||
777 | { | ||
778 | struct hist_entry *he = hist_browser__selected_entry(self); | ||
779 | return he ? he->thread : NULL; | ||
547 | } | 780 | } |
548 | 781 | ||
549 | static int hist_browser__title(char *bf, size_t size, const char *input_name, | 782 | static int hist_browser__title(char *bf, size_t size, const char *input_name, |
@@ -637,13 +870,20 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na | |||
637 | continue; | 870 | continue; |
638 | do_annotate: | 871 | do_annotate: |
639 | if (choice == annotate) { | 872 | if (choice == annotate) { |
873 | struct hist_entry *he; | ||
874 | |||
640 | if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { | 875 | if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { |
641 | ui_helpline__puts("No vmlinux file found, can't " | 876 | ui_helpline__puts("No vmlinux file found, can't " |
642 | "annotate with just a " | 877 | "annotate with just a " |
643 | "kallsyms file"); | 878 | "kallsyms file"); |
644 | continue; | 879 | continue; |
645 | } | 880 | } |
646 | map_symbol__annotate_browser(browser->selection, input_name); | 881 | |
882 | he = hist_browser__selected_entry(browser); | ||
883 | if (he == NULL) | ||
884 | continue; | ||
885 | |||
886 | hist_entry__annotate_browser(he); | ||
647 | } else if (choice == zoom_dso) { | 887 | } else if (choice == zoom_dso) { |
648 | if (dso_filter) { | 888 | if (dso_filter) { |
649 | ui_helpline__pop(); | 889 | ui_helpline__pop(); |
@@ -681,8 +921,23 @@ out: | |||
681 | return err; | 921 | return err; |
682 | } | 922 | } |
683 | 923 | ||
924 | static struct newtPercentTreeColors { | ||
925 | const char *topColorFg, *topColorBg; | ||
926 | const char *mediumColorFg, *mediumColorBg; | ||
927 | const char *normalColorFg, *normalColorBg; | ||
928 | const char *selColorFg, *selColorBg; | ||
929 | const char *codeColorFg, *codeColorBg; | ||
930 | } defaultPercentTreeColors = { | ||
931 | "red", "lightgray", | ||
932 | "green", "lightgray", | ||
933 | "black", "lightgray", | ||
934 | "lightgray", "magenta", | ||
935 | "blue", "lightgray", | ||
936 | }; | ||
937 | |||
684 | void setup_browser(void) | 938 | void setup_browser(void) |
685 | { | 939 | { |
940 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | ||
686 | if (!isatty(1)) | 941 | if (!isatty(1)) |
687 | return; | 942 | return; |
688 | 943 | ||
@@ -690,6 +945,11 @@ void setup_browser(void) | |||
690 | newtInit(); | 945 | newtInit(); |
691 | newtCls(); | 946 | newtCls(); |
692 | ui_helpline__puts(" "); | 947 | ui_helpline__puts(" "); |
948 | SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | ||
949 | SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | ||
950 | SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); | ||
951 | SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); | ||
952 | SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); | ||
693 | } | 953 | } |
694 | 954 | ||
695 | void exit_browser(bool wait_for_ok) | 955 | void exit_browser(bool wait_for_ok) |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b7c54eeed9c9..af301acc461c 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -48,12 +48,6 @@ struct hist_entry { | |||
48 | u64 count_us; | 48 | u64 count_us; |
49 | u64 count_guest_sys; | 49 | u64 count_guest_sys; |
50 | u64 count_guest_us; | 50 | u64 count_guest_us; |
51 | |||
52 | /* | ||
53 | * XXX WARNING! | ||
54 | * thread _has_ to come after ms, see | ||
55 | * hist_browser__selected_thread in util/newt.c | ||
56 | */ | ||
57 | struct map_symbol ms; | 51 | struct map_symbol ms; |
58 | struct thread *thread; | 52 | struct thread *thread; |
59 | u64 ip; | 53 | u64 ip; |