diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-05-11 22:18:06 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-05-11 22:23:20 -0400 |
commit | ef7b93a11904c6ba10604233d318d9e8ec88cddc (patch) | |
tree | 7ae6fa9cbe19be8fbbc18c8fdeb7edfdb7bdab60 /tools/perf | |
parent | 3798ed7bc7ade26d3f59506cd06288615dfc7585 (diff) |
perf report: Librarize the annotation code and use it in the newt browser
Now we don't anymore use popen to run 'perf annotate' for the selected
symbol, instead we collect per address samplings when processing samples
in 'perf report' if we're using the newt browser, then we use this data
directly to do annotation.
Done this way we can actually traverse the objdump_line objects
directly, matching the addresses to the collected samples and colouring
them appropriately using lower level slang routines.
The new ui_browser class will be reused for the main, callchain aware,
histogram browser, when it will be made generic and don't assume that
the objects are always instances of the objdump_line class maintained
using list_heads.
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-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; |