aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2010-05-11 22:18:06 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2010-05-11 22:23:20 -0400
commitef7b93a11904c6ba10604233d318d9e8ec88cddc (patch)
tree7ae6fa9cbe19be8fbbc18c8fdeb7edfdb7bdab60 /tools/perf
parent3798ed7bc7ade26d3f59506cd06288615dfc7585 (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/Makefile5
-rw-r--r--tools/perf/builtin-annotate.c198
-rw-r--r--tools/perf/builtin-report.c19
-rw-r--r--tools/perf/util/hist.c184
-rw-r--r--tools/perf/util/hist.h29
-rw-r--r--tools/perf/util/newt.c352
-rw-r--r--tools/perf/util/sort.h6
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
562else 562else
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
565endif 567endif
@@ -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
35static bool print_line; 35static bool print_line;
36 36
37struct sym_hist {
38 u64 sum;
39 u64 ip[0];
40};
41
42struct sym_ext {
43 struct rb_node node;
44 double percent;
45 char *path;
46};
47
48struct sym_priv {
49 struct sym_hist *hist;
50 struct sym_ext *ext;
51};
52
53static const char *sym_hist_filter; 37static const char *sym_hist_filter;
54 38
55static 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 */
68static 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
99static int hists__add_entry(struct hists *self, struct addr_location *al) 39static 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
121static int process_sample_event(event_t *event, struct perf_session *session) 61static 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
143struct objdump_line {
144 struct list_head node;
145 s64 offset;
146 char *line;
147};
148
149static 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
161static void objdump_line__free(struct objdump_line *self)
162{
163 free(self->line);
164 free(self);
165}
166
167static void objdump__add_line(struct list_head *head, struct objdump_line *line)
168{
169 list_add_tail(&line->node, head);
170}
171
172static 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
182static 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
238static int objdump_line__print(struct objdump_line *self, 83static 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);
111out_free_syms: 121out_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
847static 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
857int 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
888static 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
900void objdump_line__free(struct objdump_line *self)
901{
902 free(self->line);
903 free(self);
904}
905
906static void objdump__add_line(struct list_head *head, struct objdump_line *line)
907{
908 list_add_tail(&line->node, head);
909}
910
911struct 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
921static 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
979int 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;
11struct symbol; 11struct symbol;
12struct rb_root; 12struct rb_root;
13 13
14struct objdump_line {
15 struct list_head node;
16 s64 offset;
17 char *line;
18};
19
20void objdump_line__free(struct objdump_line *self);
21struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
22 struct objdump_line *pos);
23
24struct sym_hist {
25 u64 sum;
26 u64 ip[0];
27};
28
29struct sym_ext {
30 struct rb_node node;
31 double percent;
32 char *path;
33};
34
35struct sym_priv {
36 struct sym_hist *hist;
37 struct sym_ext *ext;
38};
39
14struct events_stats { 40struct 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);
45size_t hists__fprintf(struct hists *self, struct hists *pair, 71size_t hists__fprintf(struct hists *self, struct hists *pair,
46 bool show_displacement, FILE *fp); 72 bool show_displacement, FILE *fp);
47 73
74int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
75int hist_entry__annotate(struct hist_entry *self, struct list_head *head);
76
48void hists__filter_by_dso(struct hists *self, const struct dso *dso); 77void hists__filter_by_dso(struct hists *self, const struct dso *dso);
49void hists__filter_by_thread(struct hists *self, const struct thread *thread); 78void 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
181static 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
192struct 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
201static 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
215static 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
221static 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
270static 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
299static 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
356static void map_symbol__annotate_browser(const struct map_symbol *self, 605static 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();
410out_free_str:
411 free(str);
412} 639}
413 640
414static const void *newt__symbol_tree_get_current(newtComponent self) 641static 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
530static struct thread *hist_browser__selected_thread(struct hist_browser *self) 757static 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;
545out: 772out:
546 return *(struct thread **)(self->selection + 1); 773 return container_of(self->selection, struct hist_entry, ms);
774}
775
776static 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
549static int hist_browser__title(char *bf, size_t size, const char *input_name, 782static 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;
638do_annotate: 871do_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
924static 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
684void setup_browser(void) 938void 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
695void exit_browser(bool wait_for_ok) 955void 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;