aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2010-08-11 03:39:10 -0400
committerIngo Molnar <mingo@elte.hu>2010-08-11 03:39:10 -0400
commitb3e84ffa21f916e3354a12a7f19169c9febe96d0 (patch)
tree1248316ef0cf5bee08309d492cf925fd87662ea8
parent1c250d709fdc8aa5bf42d90be99428a01a256a55 (diff)
parent4694153c252a6ae19704b5bb66466050256395a4 (diff)
Merge branch 'perf/core' into perf/urgent
-rw-r--r--tools/perf/Makefile25
-rw-r--r--tools/perf/builtin-annotate.c2
-rw-r--r--tools/perf/util/debug.c2
-rw-r--r--tools/perf/util/debug.h9
-rw-r--r--tools/perf/util/hist.c13
-rw-r--r--tools/perf/util/hist.h3
-rw-r--r--tools/perf/util/pstack.h2
-rw-r--r--tools/perf/util/symbol.c10
-rw-r--r--tools/perf/util/ui/browser.c76
-rw-r--r--tools/perf/util/ui/browser.h11
-rw-r--r--tools/perf/util/ui/browsers/annotate.c240
-rw-r--r--tools/perf/util/ui/browsers/hists.c (renamed from tools/perf/util/newt.c)1375
-rw-r--r--tools/perf/util/ui/browsers/map.c161
-rw-r--r--tools/perf/util/ui/browsers/map.h6
-rw-r--r--tools/perf/util/ui/helpline.c69
-rw-r--r--tools/perf/util/ui/helpline.h11
-rw-r--r--tools/perf/util/ui/libslang.h27
-rw-r--r--tools/perf/util/ui/progress.c60
-rw-r--r--tools/perf/util/ui/progress.h11
-rw-r--r--tools/perf/util/ui/setup.c42
-rw-r--r--tools/perf/util/ui/util.c114
-rw-r--r--tools/perf/util/ui/util.h10
22 files changed, 1270 insertions, 1009 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index d5bce768b4bf..41abb90df50d 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -158,7 +158,7 @@ all::
158# Define NO_DWARF if you do not want debug-info analysis feature at all. 158# Define NO_DWARF if you do not want debug-info analysis feature at all.
159 159
160$(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null) 160$(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null)
161$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui,scripting-engines}/' 2> /dev/null) 161$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui/browsers,scripting-engines}/' 2> /dev/null)
162$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) 162$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null)
163 163
164$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE 164$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@@ -567,9 +567,20 @@ else
567 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h 567 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
568 BASIC_CFLAGS += -I/usr/include/slang 568 BASIC_CFLAGS += -I/usr/include/slang
569 EXTLIBS += -lnewt -lslang 569 EXTLIBS += -lnewt -lslang
570 LIB_OBJS += $(OUTPUT)util/newt.o 570 LIB_OBJS += $(OUTPUT)util/ui/setup.o
571 LIB_OBJS += $(OUTPUT)util/ui/browser.o 571 LIB_OBJS += $(OUTPUT)util/ui/browser.o
572 LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
573 LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
574 LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
575 LIB_OBJS += $(OUTPUT)util/ui/helpline.o
576 LIB_OBJS += $(OUTPUT)util/ui/progress.o
577 LIB_OBJS += $(OUTPUT)util/ui/util.o
572 LIB_H += util/ui/browser.h 578 LIB_H += util/ui/browser.h
579 LIB_H += util/ui/browsers/map.h
580 LIB_H += util/ui/helpline.h
581 LIB_H += util/ui/libslang.h
582 LIB_H += util/ui/progress.h
583 LIB_H += util/ui/util.h
573 endif 584 endif
574endif 585endif
575 586
@@ -967,10 +978,16 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
967$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS 978$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
968 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< 979 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
969 980
970$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS 981$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
971 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< 982 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
972 983
973$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS 984$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
985 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
986
987$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
988 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
989
990$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
974 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< 991 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
975 992
976$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS 993$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index fd20670ce986..1478dc64bf15 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -285,7 +285,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
285 LIST_HEAD(head); 285 LIST_HEAD(head);
286 struct objdump_line *pos, *n; 286 struct objdump_line *pos, *n;
287 287
288 if (hist_entry__annotate(he, &head) < 0) 288 if (hist_entry__annotate(he, &head, 0) < 0)
289 return -1; 289 return -1;
290 290
291 if (full_paths) 291 if (full_paths)
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 318dab15d177..f9c7e3ad1aa7 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...)
23 if (verbose >= level) { 23 if (verbose >= level) {
24 va_start(args, fmt); 24 va_start(args, fmt);
25 if (use_browser > 0) 25 if (use_browser > 0)
26 ret = browser__show_help(fmt, args); 26 ret = ui_helpline__show_help(fmt, args);
27 else 27 else
28 ret = vfprintf(stderr, fmt, args); 28 ret = vfprintf(stderr, fmt, args);
29 va_end(args); 29 va_end(args);
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 047ac3324ebe..7a17ee061bcb 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -14,7 +14,7 @@ void trace_event(event_t *event);
14struct ui_progress; 14struct ui_progress;
15 15
16#ifdef NO_NEWT_SUPPORT 16#ifdef NO_NEWT_SUPPORT
17static inline int browser__show_help(const char *format __used, va_list ap __used) 17static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
18{ 18{
19 return 0; 19 return 0;
20} 20}
@@ -30,10 +30,9 @@ static inline void ui_progress__update(struct ui_progress *self __used,
30 30
31static inline void ui_progress__delete(struct ui_progress *self __used) {} 31static inline void ui_progress__delete(struct ui_progress *self __used) {}
32#else 32#else
33int browser__show_help(const char *format, va_list ap); 33extern char ui_helpline__last_msg[];
34struct ui_progress *ui_progress__new(const char *title, u64 total); 34int ui_helpline__show_help(const char *format, va_list ap);
35void ui_progress__update(struct ui_progress *self, u64 curr); 35#include "ui/progress.h"
36void ui_progress__delete(struct ui_progress *self);
37#endif 36#endif
38 37
39#endif /* __PERF_DEBUG_H */ 38#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 62ec9b0e4b9a..be22ae6ef055 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -983,9 +983,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
983 return 0; 983 return 0;
984} 984}
985 985
986static struct objdump_line *objdump_line__new(s64 offset, char *line) 986static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
987{ 987{
988 struct objdump_line *self = malloc(sizeof(*self)); 988 struct objdump_line *self = malloc(sizeof(*self) + privsize);
989 989
990 if (self != NULL) { 990 if (self != NULL) {
991 self->offset = offset; 991 self->offset = offset;
@@ -1017,7 +1017,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
1017} 1017}
1018 1018
1019static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, 1019static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1020 struct list_head *head) 1020 struct list_head *head, size_t privsize)
1021{ 1021{
1022 struct symbol *sym = self->ms.sym; 1022 struct symbol *sym = self->ms.sym;
1023 struct objdump_line *objdump_line; 1023 struct objdump_line *objdump_line;
@@ -1068,7 +1068,7 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1068 offset = -1; 1068 offset = -1;
1069 } 1069 }
1070 1070
1071 objdump_line = objdump_line__new(offset, line); 1071 objdump_line = objdump_line__new(offset, line, privsize);
1072 if (objdump_line == NULL) { 1072 if (objdump_line == NULL) {
1073 free(line); 1073 free(line);
1074 return -1; 1074 return -1;
@@ -1078,7 +1078,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1078 return 0; 1078 return 0;
1079} 1079}
1080 1080
1081int hist_entry__annotate(struct hist_entry *self, struct list_head *head) 1081int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
1082 size_t privsize)
1082{ 1083{
1083 struct symbol *sym = self->ms.sym; 1084 struct symbol *sym = self->ms.sym;
1084 struct map *map = self->ms.map; 1085 struct map *map = self->ms.map;
@@ -1143,7 +1144,7 @@ fallback:
1143 goto out_free_filename; 1144 goto out_free_filename;
1144 1145
1145 while (!feof(file)) 1146 while (!feof(file))
1146 if (hist_entry__parse_objdump_line(self, file, head) < 0) 1147 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1147 break; 1148 break;
1148 1149
1149 pclose(file); 1150 pclose(file);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 65a48db46a29..587d375d3430 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -101,7 +101,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
101 bool show_displacement, FILE *fp); 101 bool show_displacement, FILE *fp);
102 102
103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); 103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
104int hist_entry__annotate(struct hist_entry *self, struct list_head *head); 104int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
105 size_t privsize);
105 106
106void hists__filter_by_dso(struct hists *self, const struct dso *dso); 107void hists__filter_by_dso(struct hists *self, const struct dso *dso);
107void hists__filter_by_thread(struct hists *self, const struct thread *thread); 108void hists__filter_by_thread(struct hists *self, const struct thread *thread);
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
index 5ad07023504b..4cedea59f518 100644
--- a/tools/perf/util/pstack.h
+++ b/tools/perf/util/pstack.h
@@ -1,6 +1,8 @@
1#ifndef _PERF_PSTACK_ 1#ifndef _PERF_PSTACK_
2#define _PERF_PSTACK_ 2#define _PERF_PSTACK_
3 3
4#include <stdbool.h>
5
4struct pstack; 6struct pstack;
5struct pstack *pstack__new(unsigned short max_nr_entries); 7struct pstack *pstack__new(unsigned short max_nr_entries);
6void pstack__delete(struct pstack *self); 8void pstack__delete(struct pstack *self);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b6f5970f9106..1a367734e016 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1079,6 +1079,16 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1079 if (!is_label && !elf_sym__is_a(&sym, map->type)) 1079 if (!is_label && !elf_sym__is_a(&sym, map->type))
1080 continue; 1080 continue;
1081 1081
1082 /* Reject ARM ELF "mapping symbols": these aren't unique and
1083 * don't identify functions, so will confuse the profile
1084 * output: */
1085 if (ehdr.e_machine == EM_ARM) {
1086 if (!strcmp(elf_name, "$a") ||
1087 !strcmp(elf_name, "$d") ||
1088 !strcmp(elf_name, "$t"))
1089 continue;
1090 }
1091
1082 if (opdsec && sym.st_shndx == opdidx) { 1092 if (opdsec && sym.st_shndx == opdidx) {
1083 u32 offset = sym.st_value - opdshdr.sh_addr; 1093 u32 offset = sym.st_value - opdshdr.sh_addr;
1084 u64 *opd = opddata->d_buf + offset; 1094 u64 *opd = opddata->d_buf + offset;
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
index 0b2b9306312d..66f2d583d8c4 100644
--- a/tools/perf/util/ui/browser.c
+++ b/tools/perf/util/ui/browser.c
@@ -16,6 +16,7 @@
16#include <stdlib.h> 16#include <stdlib.h>
17#include <sys/ttydefaults.h> 17#include <sys/ttydefaults.h>
18#include "browser.h" 18#include "browser.h"
19#include "helpline.h"
19#include "../color.h" 20#include "../color.h"
20#include "../util.h" 21#include "../util.h"
21 22
@@ -49,7 +50,7 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc
49 pos = head->next; 50 pos = head->next;
50 break; 51 break;
51 case SEEK_CUR: 52 case SEEK_CUR:
52 pos = self->first_visible_entry; 53 pos = self->top;
53 break; 54 break;
54 case SEEK_END: 55 case SEEK_END:
55 pos = head->prev; 56 pos = head->prev;
@@ -66,7 +67,7 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc
66 pos = pos->prev; 67 pos = pos->prev;
67 } 68 }
68 69
69 self->first_visible_entry = pos; 70 self->top = pos;
70} 71}
71 72
72void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) 73void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
@@ -79,7 +80,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
79 nd = rb_first(root); 80 nd = rb_first(root);
80 break; 81 break;
81 case SEEK_CUR: 82 case SEEK_CUR:
82 nd = self->first_visible_entry; 83 nd = self->top;
83 break; 84 break;
84 case SEEK_END: 85 case SEEK_END:
85 nd = rb_last(root); 86 nd = rb_last(root);
@@ -96,7 +97,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
96 nd = rb_prev(nd); 97 nd = rb_prev(nd);
97 } 98 }
98 99
99 self->first_visible_entry = nd; 100 self->top = nd;
100} 101}
101 102
102unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) 103unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
@@ -104,13 +105,13 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
104 struct rb_node *nd; 105 struct rb_node *nd;
105 int row = 0; 106 int row = 0;
106 107
107 if (self->first_visible_entry == NULL) 108 if (self->top == NULL)
108 self->first_visible_entry = rb_first(self->entries); 109 self->top = rb_first(self->entries);
109 110
110 nd = self->first_visible_entry; 111 nd = self->top;
111 112
112 while (nd != NULL) { 113 while (nd != NULL) {
113 SLsmg_gotorc(self->top + row, self->left); 114 SLsmg_gotorc(self->y + row, self->x);
114 self->write(self, nd, row); 115 self->write(self, nd, row);
115 if (++row == self->height) 116 if (++row == self->height)
116 break; 117 break;
@@ -122,7 +123,7 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
122 123
123bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) 124bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
124{ 125{
125 return (self->first_visible_entry_idx + row) == self->index; 126 return self->top_idx + row == self->index;
126} 127}
127 128
128void ui_browser__refresh_dimensions(struct ui_browser *self) 129void ui_browser__refresh_dimensions(struct ui_browser *self)
@@ -135,18 +136,21 @@ void ui_browser__refresh_dimensions(struct ui_browser *self)
135 self->height = rows - 5; 136 self->height = rows - 5;
136 if (self->height > self->nr_entries) 137 if (self->height > self->nr_entries)
137 self->height = self->nr_entries; 138 self->height = self->nr_entries;
138 self->top = (rows - self->height) / 2; 139 self->y = (rows - self->height) / 2;
139 self->left = (cols - self->width) / 2; 140 self->x = (cols - self->width) / 2;
140} 141}
141 142
142void ui_browser__reset_index(struct ui_browser *self) 143void ui_browser__reset_index(struct ui_browser *self)
143{ 144{
144 self->index = self->first_visible_entry_idx = 0; 145 self->index = self->top_idx = 0;
145 self->seek(self, 0, SEEK_SET); 146 self->seek(self, 0, SEEK_SET);
146} 147}
147 148
148int ui_browser__show(struct ui_browser *self, const char *title) 149int ui_browser__show(struct ui_browser *self, const char *title,
150 const char *helpline, ...)
149{ 151{
152 va_list ap;
153
150 if (self->form != NULL) { 154 if (self->form != NULL) {
151 newtFormDestroy(self->form); 155 newtFormDestroy(self->form);
152 newtPopWindow(); 156 newtPopWindow();
@@ -169,10 +173,23 @@ int ui_browser__show(struct ui_browser *self, const char *title)
169 newtFormAddHotKey(self->form, NEWT_KEY_PGDN); 173 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
170 newtFormAddHotKey(self->form, NEWT_KEY_HOME); 174 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
171 newtFormAddHotKey(self->form, NEWT_KEY_END); 175 newtFormAddHotKey(self->form, NEWT_KEY_END);
176 newtFormAddHotKey(self->form, ' ');
172 newtFormAddComponent(self->form, self->sb); 177 newtFormAddComponent(self->form, self->sb);
178
179 va_start(ap, helpline);
180 ui_helpline__vpush(helpline, ap);
181 va_end(ap);
173 return 0; 182 return 0;
174} 183}
175 184
185void ui_browser__hide(struct ui_browser *self)
186{
187 newtFormDestroy(self->form);
188 newtPopWindow();
189 self->form = NULL;
190 ui_helpline__pop();
191}
192
176int ui_browser__refresh(struct ui_browser *self) 193int ui_browser__refresh(struct ui_browser *self)
177{ 194{
178 int row; 195 int row;
@@ -180,7 +197,7 @@ int ui_browser__refresh(struct ui_browser *self)
180 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); 197 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
181 row = self->refresh(self); 198 row = self->refresh(self);
182 SLsmg_set_color(HE_COLORSET_NORMAL); 199 SLsmg_set_color(HE_COLORSET_NORMAL);
183 SLsmg_fill_region(self->top + row, self->left, 200 SLsmg_fill_region(self->y + row, self->x,
184 self->height - row, self->width, ' '); 201 self->height - row, self->width, ' ');
185 202
186 return 0; 203 return 0;
@@ -205,8 +222,8 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
205 if (self->index == self->nr_entries - 1) 222 if (self->index == self->nr_entries - 1)
206 break; 223 break;
207 ++self->index; 224 ++self->index;
208 if (self->index == self->first_visible_entry_idx + self->height) { 225 if (self->index == self->top_idx + self->height) {
209 ++self->first_visible_entry_idx; 226 ++self->top_idx;
210 self->seek(self, +1, SEEK_CUR); 227 self->seek(self, +1, SEEK_CUR);
211 } 228 }
212 break; 229 break;
@@ -214,34 +231,34 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
214 if (self->index == 0) 231 if (self->index == 0)
215 break; 232 break;
216 --self->index; 233 --self->index;
217 if (self->index < self->first_visible_entry_idx) { 234 if (self->index < self->top_idx) {
218 --self->first_visible_entry_idx; 235 --self->top_idx;
219 self->seek(self, -1, SEEK_CUR); 236 self->seek(self, -1, SEEK_CUR);
220 } 237 }
221 break; 238 break;
222 case NEWT_KEY_PGDN: 239 case NEWT_KEY_PGDN:
223 case ' ': 240 case ' ':
224 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) 241 if (self->top_idx + self->height > self->nr_entries - 1)
225 break; 242 break;
226 243
227 offset = self->height; 244 offset = self->height;
228 if (self->index + offset > self->nr_entries - 1) 245 if (self->index + offset > self->nr_entries - 1)
229 offset = self->nr_entries - 1 - self->index; 246 offset = self->nr_entries - 1 - self->index;
230 self->index += offset; 247 self->index += offset;
231 self->first_visible_entry_idx += offset; 248 self->top_idx += offset;
232 self->seek(self, +offset, SEEK_CUR); 249 self->seek(self, +offset, SEEK_CUR);
233 break; 250 break;
234 case NEWT_KEY_PGUP: 251 case NEWT_KEY_PGUP:
235 if (self->first_visible_entry_idx == 0) 252 if (self->top_idx == 0)
236 break; 253 break;
237 254
238 if (self->first_visible_entry_idx < self->height) 255 if (self->top_idx < self->height)
239 offset = self->first_visible_entry_idx; 256 offset = self->top_idx;
240 else 257 else
241 offset = self->height; 258 offset = self->height;
242 259
243 self->index -= offset; 260 self->index -= offset;
244 self->first_visible_entry_idx -= offset; 261 self->top_idx -= offset;
245 self->seek(self, -offset, SEEK_CUR); 262 self->seek(self, -offset, SEEK_CUR);
246 break; 263 break;
247 case NEWT_KEY_HOME: 264 case NEWT_KEY_HOME:
@@ -253,7 +270,7 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
253 offset = self->nr_entries - 1; 270 offset = self->nr_entries - 1;
254 271
255 self->index = self->nr_entries - 1; 272 self->index = self->nr_entries - 1;
256 self->first_visible_entry_idx = self->index - offset; 273 self->top_idx = self->index - offset;
257 self->seek(self, -offset, SEEK_END); 274 self->seek(self, -offset, SEEK_END);
258 break; 275 break;
259 default: 276 default:
@@ -271,14 +288,13 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
271 struct list_head *head = self->entries; 288 struct list_head *head = self->entries;
272 int row = 0; 289 int row = 0;
273 290
274 if (self->first_visible_entry == NULL || 291 if (self->top == NULL || self->top == self->entries)
275 self->first_visible_entry == self->entries) 292 self->top = head->next;
276 self->first_visible_entry = head->next;
277 293
278 pos = self->first_visible_entry; 294 pos = self->top;
279 295
280 list_for_each_from(pos, head) { 296 list_for_each_from(pos, head) {
281 SLsmg_gotorc(self->top + row, self->left); 297 SLsmg_gotorc(self->y + row, self->x);
282 self->write(self, pos, row); 298 self->write(self, pos, row);
283 if (++row == self->height) 299 if (++row == self->height)
284 break; 300 break;
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
index bcc4391405bd..0b9f829214f7 100644
--- a/tools/perf/util/ui/browser.h
+++ b/tools/perf/util/ui/browser.h
@@ -3,6 +3,7 @@
3 3
4#include <stdbool.h> 4#include <stdbool.h>
5#include <newt.h> 5#include <newt.h>
6#include <sys/types.h>
6#include "../types.h" 7#include "../types.h"
7 8
8#define HE_COLORSET_TOP 50 9#define HE_COLORSET_TOP 50
@@ -13,9 +14,9 @@
13 14
14struct ui_browser { 15struct ui_browser {
15 newtComponent form, sb; 16 newtComponent form, sb;
16 u64 index, first_visible_entry_idx; 17 u64 index, top_idx;
17 void *first_visible_entry, *entries; 18 void *top, *entries;
18 u16 top, left, width, height; 19 u16 y, x, width, height;
19 void *priv; 20 void *priv;
20 unsigned int (*refresh)(struct ui_browser *self); 21 unsigned int (*refresh)(struct ui_browser *self);
21 void (*write)(struct ui_browser *self, void *entry, int row); 22 void (*write)(struct ui_browser *self, void *entry, int row);
@@ -29,7 +30,9 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
29void ui_browser__refresh_dimensions(struct ui_browser *self); 30void ui_browser__refresh_dimensions(struct ui_browser *self);
30void ui_browser__reset_index(struct ui_browser *self); 31void ui_browser__reset_index(struct ui_browser *self);
31 32
32int ui_browser__show(struct ui_browser *self, const char *title); 33int ui_browser__show(struct ui_browser *self, const char *title,
34 const char *helpline, ...);
35void ui_browser__hide(struct ui_browser *self);
33int ui_browser__refresh(struct ui_browser *self); 36int ui_browser__refresh(struct ui_browser *self);
34int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es); 37int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es);
35 38
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
new file mode 100644
index 000000000000..55ff792459ac
--- /dev/null
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -0,0 +1,240 @@
1#include "../browser.h"
2#include "../helpline.h"
3#include "../libslang.h"
4#include "../../hist.h"
5#include "../../sort.h"
6#include "../../symbol.h"
7
8static void ui__error_window(const char *fmt, ...)
9{
10 va_list ap;
11
12 va_start(ap, fmt);
13 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
14 va_end(ap);
15}
16
17struct annotate_browser {
18 struct ui_browser b;
19 struct rb_root entries;
20 struct rb_node *curr_hot;
21};
22
23struct objdump_line_rb_node {
24 struct rb_node rb_node;
25 double percent;
26 u32 idx;
27};
28
29static inline
30struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
31{
32 return (struct objdump_line_rb_node *)(self + 1);
33}
34
35static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
36{
37 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
38 bool current_entry = ui_browser__is_current_entry(self, row);
39 int width = self->width;
40
41 if (ol->offset != -1) {
42 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
43 int color = ui_browser__percent_color(olrb->percent, current_entry);
44 SLsmg_set_color(color);
45 slsmg_printf(" %7.2f ", olrb->percent);
46 if (!current_entry)
47 SLsmg_set_color(HE_COLORSET_CODE);
48 } else {
49 int color = ui_browser__percent_color(0, current_entry);
50 SLsmg_set_color(color);
51 slsmg_write_nstring(" ", 9);
52 }
53
54 SLsmg_write_char(':');
55 slsmg_write_nstring(" ", 8);
56 if (!*ol->line)
57 slsmg_write_nstring(" ", width - 18);
58 else
59 slsmg_write_nstring(ol->line, width - 18);
60}
61
62static double objdump_line__calc_percent(struct objdump_line *self,
63 struct list_head *head,
64 struct symbol *sym)
65{
66 double percent = 0.0;
67
68 if (self->offset != -1) {
69 int len = sym->end - sym->start;
70 unsigned int hits = 0;
71 struct sym_priv *priv = symbol__priv(sym);
72 struct sym_ext *sym_ext = priv->ext;
73 struct sym_hist *h = priv->hist;
74 s64 offset = self->offset;
75 struct objdump_line *next = objdump__get_next_ip_line(head, self);
76
77
78 while (offset < (s64)len &&
79 (next == NULL || offset < next->offset)) {
80 if (sym_ext) {
81 percent += sym_ext[offset].percent;
82 } else
83 hits += h->ip[offset];
84
85 ++offset;
86 }
87
88 if (sym_ext == NULL && h->sum)
89 percent = 100.0 * hits / h->sum;
90 }
91
92 return percent;
93}
94
95static void objdump__insert_line(struct rb_root *self,
96 struct objdump_line_rb_node *line)
97{
98 struct rb_node **p = &self->rb_node;
99 struct rb_node *parent = NULL;
100 struct objdump_line_rb_node *l;
101
102 while (*p != NULL) {
103 parent = *p;
104 l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
105 if (line->percent < l->percent)
106 p = &(*p)->rb_left;
107 else
108 p = &(*p)->rb_right;
109 }
110 rb_link_node(&line->rb_node, parent, p);
111 rb_insert_color(&line->rb_node, self);
112}
113
114static void annotate_browser__set_top(struct annotate_browser *self,
115 struct rb_node *nd)
116{
117 struct objdump_line_rb_node *rbpos;
118 struct objdump_line *pos;
119 unsigned back;
120
121 ui_browser__refresh_dimensions(&self->b);
122 back = self->b.height / 2;
123 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
124 pos = ((struct objdump_line *)rbpos) - 1;
125 self->b.top_idx = self->b.index = rbpos->idx;
126
127 while (self->b.top_idx != 0 && back != 0) {
128 pos = list_entry(pos->node.prev, struct objdump_line, node);
129
130 --self->b.top_idx;
131 --back;
132 }
133
134 self->b.top = pos;
135 self->curr_hot = nd;
136}
137
138static int annotate_browser__run(struct annotate_browser *self,
139 struct newtExitStruct *es)
140{
141 struct rb_node *nd;
142 struct hist_entry *he = self->b.priv;
143
144 if (ui_browser__show(&self->b, he->ms.sym->name,
145 "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
146 return -1;
147
148 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
149
150 nd = self->curr_hot;
151 if (nd) {
152 newtFormAddHotKey(self->b.form, NEWT_KEY_TAB);
153 newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB);
154 }
155
156 while (1) {
157 ui_browser__run(&self->b, es);
158
159 if (es->reason != NEWT_EXIT_HOTKEY)
160 break;
161
162 switch (es->u.key) {
163 case NEWT_KEY_TAB:
164 nd = rb_prev(nd);
165 if (nd == NULL)
166 nd = rb_last(&self->entries);
167 annotate_browser__set_top(self, nd);
168 break;
169 case NEWT_KEY_UNTAB:
170 nd = rb_next(nd);
171 if (nd == NULL)
172 nd = rb_first(&self->entries);
173 annotate_browser__set_top(self, nd);
174 break;
175 default:
176 goto out;
177 }
178 }
179out:
180 ui_browser__hide(&self->b);
181 return 0;
182}
183
184int hist_entry__tui_annotate(struct hist_entry *self)
185{
186 struct newtExitStruct es;
187 struct objdump_line *pos, *n;
188 struct objdump_line_rb_node *rbpos;
189 LIST_HEAD(head);
190 struct annotate_browser browser = {
191 .b = {
192 .entries = &head,
193 .refresh = ui_browser__list_head_refresh,
194 .seek = ui_browser__list_head_seek,
195 .write = annotate_browser__write,
196 .priv = self,
197 },
198 };
199 int ret;
200
201 if (self->ms.sym == NULL)
202 return -1;
203
204 if (self->ms.map->dso->annotate_warned)
205 return -1;
206
207 if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) {
208 ui__error_window(ui_helpline__last_msg);
209 return -1;
210 }
211
212 ui_helpline__push("Press <- or ESC to exit");
213
214 list_for_each_entry(pos, &head, node) {
215 size_t line_len = strlen(pos->line);
216 if (browser.b.width < line_len)
217 browser.b.width = line_len;
218 rbpos = objdump_line__rb(pos);
219 rbpos->idx = browser.b.nr_entries++;
220 rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym);
221 if (rbpos->percent < 0.01)
222 continue;
223 objdump__insert_line(&browser.entries, rbpos);
224 }
225
226 /*
227 * Position the browser at the hottest line.
228 */
229 browser.curr_hot = rb_last(&browser.entries);
230 if (browser.curr_hot)
231 annotate_browser__set_top(&browser, browser.curr_hot);
232
233 browser.b.width += 18; /* Percentage */
234 ret = annotate_browser__run(&browser, &es);
235 list_for_each_entry_safe(pos, n, &head, node) {
236 list_del(&pos->node);
237 objdump_line__free(pos);
238 }
239 return ret;
240}
diff --git a/tools/perf/util/newt.c b/tools/perf/util/ui/browsers/hists.c
index 266a9e06525b..dafdf6775d77 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -1,897 +1,272 @@
1#define _GNU_SOURCE 1#define _GNU_SOURCE
2#include <stdio.h> 2#include <stdio.h>
3#undef _GNU_SOURCE 3#undef _GNU_SOURCE
4/* 4#include "../libslang.h"
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <signal.h>
15#include <stdlib.h> 5#include <stdlib.h>
16#include <elf.h> 6#include <string.h>
17#include <newt.h> 7#include <newt.h>
18#include <sys/ttydefaults.h> 8#include <linux/rbtree.h>
19
20#include "cache.h"
21#include "hist.h"
22#include "pstack.h"
23#include "session.h"
24#include "sort.h"
25#include "symbol.h"
26#include "ui/browser.h"
27
28#if SLANG_VERSION < 20104
29#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
30#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
31#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
32 (char *)fg, (char *)bg)
33#else
34#define slsmg_printf SLsmg_printf
35#define slsmg_write_nstring SLsmg_write_nstring
36#define sltt_set_color SLtt_set_color
37#endif
38
39newtComponent newt_form__new(void);
40
41struct ui_progress {
42 newtComponent form, scale;
43};
44
45struct ui_progress *ui_progress__new(const char *title, u64 total)
46{
47 struct ui_progress *self = malloc(sizeof(*self));
48
49 if (self != NULL) {
50 int cols;
51
52 if (use_browser <= 0)
53 return self;
54 newtGetScreenSize(&cols, NULL);
55 cols -= 4;
56 newtCenteredWindow(cols, 1, title);
57 self->form = newtForm(NULL, NULL, 0);
58 if (self->form == NULL)
59 goto out_free_self;
60 self->scale = newtScale(0, 0, cols, total);
61 if (self->scale == NULL)
62 goto out_free_form;
63 newtFormAddComponent(self->form, self->scale);
64 newtRefresh();
65 }
66 9
67 return self; 10#include "../../hist.h"
11#include "../../pstack.h"
12#include "../../sort.h"
13#include "../../util.h"
68 14
69out_free_form: 15#include "../browser.h"
70 newtFormDestroy(self->form); 16#include "../helpline.h"
71out_free_self: 17#include "../util.h"
72 free(self); 18#include "map.h"
73 return NULL;
74}
75 19
76void ui_progress__update(struct ui_progress *self, u64 curr) 20struct hist_browser {
77{ 21 struct ui_browser b;
78 /* 22 struct hists *hists;
79 * FIXME: We should have a per UI backend way of showing progress, 23 struct hist_entry *he_selection;
80 * stdio will just show a percentage as NN%, etc. 24 struct map_symbol *selection;
81 */ 25};
82 if (use_browser <= 0)
83 return;
84 newtScaleSet(self->scale, curr);
85 newtRefresh();
86}
87 26
88void ui_progress__delete(struct ui_progress *self) 27static void hist_browser__refresh_dimensions(struct hist_browser *self)
89{ 28{
90 if (use_browser > 0) { 29 /* 3 == +/- toggle symbol before actual hist_entry rendering */
91 newtFormDestroy(self->form); 30 self->b.width = 3 + (hists__sort_list_width(self->hists) +
92 newtPopWindow(); 31 sizeof("[k]"));
93 }
94 free(self);
95} 32}
96 33
97static void ui_helpline__pop(void) 34static void hist_browser__reset(struct hist_browser *self)
98{ 35{
99 newtPopHelpLine(); 36 self->b.nr_entries = self->hists->nr_entries;
37 hist_browser__refresh_dimensions(self);
38 ui_browser__reset_index(&self->b);
100} 39}
101 40
102static void ui_helpline__push(const char *msg) 41static char tree__folded_sign(bool unfolded)
103{ 42{
104 newtPushHelpLine(msg); 43 return unfolded ? '-' : '+';
105} 44}
106 45
107static void ui_helpline__vpush(const char *fmt, va_list ap) 46static char map_symbol__folded(const struct map_symbol *self)
108{ 47{
109 char *s; 48 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
110
111 if (vasprintf(&s, fmt, ap) < 0)
112 vfprintf(stderr, fmt, ap);
113 else {
114 ui_helpline__push(s);
115 free(s);
116 }
117} 49}
118 50
119static void ui_helpline__fpush(const char *fmt, ...) 51static char hist_entry__folded(const struct hist_entry *self)
120{ 52{
121 va_list ap; 53 return map_symbol__folded(&self->ms);
122
123 va_start(ap, fmt);
124 ui_helpline__vpush(fmt, ap);
125 va_end(ap);
126} 54}
127 55
128static void ui_helpline__puts(const char *msg) 56static char callchain_list__folded(const struct callchain_list *self)
129{ 57{
130 ui_helpline__pop(); 58 return map_symbol__folded(&self->ms);
131 ui_helpline__push(msg);
132} 59}
133 60
134static int ui_entry__read(const char *title, char *bf, size_t size, int width) 61static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
135{ 62{
136 struct newtExitStruct es; 63 int n = 0;
137 newtComponent form, entry; 64 struct rb_node *nd;
138 const char *result;
139 int err = -1;
140
141 newtCenteredWindow(width, 1, title);
142 form = newtForm(NULL, NULL, 0);
143 if (form == NULL)
144 return -1;
145 65
146 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); 66 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
147 if (entry == NULL) 67 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
148 goto out_free_form; 68 struct callchain_list *chain;
69 char folded_sign = ' '; /* No children */
149 70
150 newtFormAddComponent(form, entry); 71 list_for_each_entry(chain, &child->val, list) {
151 newtFormAddHotKey(form, NEWT_KEY_ENTER); 72 ++n;
152 newtFormAddHotKey(form, NEWT_KEY_ESCAPE); 73 /* We need this because we may not have children */
153 newtFormAddHotKey(form, NEWT_KEY_LEFT); 74 folded_sign = callchain_list__folded(chain);
154 newtFormAddHotKey(form, CTRL('c')); 75 if (folded_sign == '+')
155 newtFormRun(form, &es); 76 break;
77 }
156 78
157 if (result != NULL) { 79 if (folded_sign == '-') /* Have children and they're unfolded */
158 strncpy(bf, result, size); 80 n += callchain_node__count_rows_rb_tree(child);
159 err = 0;
160 } 81 }
161out_free_form:
162 newtPopWindow();
163 newtFormDestroy(form);
164 return 0;
165}
166 82
167static char browser__last_msg[1024]; 83 return n;
84}
168 85
169int browser__show_help(const char *format, va_list ap) 86static int callchain_node__count_rows(struct callchain_node *node)
170{ 87{
171 int ret; 88 struct callchain_list *chain;
172 static int backlog; 89 bool unfolded = false;
173 90 int n = 0;
174 ret = vsnprintf(browser__last_msg + backlog,
175 sizeof(browser__last_msg) - backlog, format, ap);
176 backlog += ret;
177 91
178 if (browser__last_msg[backlog - 1] == '\n') { 92 list_for_each_entry(chain, &node->val, list) {
179 ui_helpline__puts(browser__last_msg); 93 ++n;
180 newtRefresh(); 94 unfolded = chain->ms.unfolded;
181 backlog = 0;
182 } 95 }
183 96
184 return ret; 97 if (unfolded)
185} 98 n += callchain_node__count_rows_rb_tree(node);
186
187static void newt_form__set_exit_keys(newtComponent self)
188{
189 newtFormAddHotKey(self, NEWT_KEY_LEFT);
190 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
191 newtFormAddHotKey(self, 'Q');
192 newtFormAddHotKey(self, 'q');
193 newtFormAddHotKey(self, CTRL('c'));
194}
195 99
196newtComponent newt_form__new(void) 100 return n;
197{
198 newtComponent self = newtForm(NULL, NULL, 0);
199 if (self)
200 newt_form__set_exit_keys(self);
201 return self;
202} 101}
203 102
204static int popup_menu(int argc, char * const argv[]) 103static int callchain__count_rows(struct rb_root *chain)
205{ 104{
206 struct newtExitStruct es; 105 struct rb_node *nd;
207 int i, rc = -1, max_len = 5; 106 int n = 0;
208 newtComponent listbox, form = newt_form__new();
209
210 if (form == NULL)
211 return -1;
212
213 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
214 if (listbox == NULL)
215 goto out_destroy_form;
216
217 newtFormAddComponent(form, listbox);
218 107
219 for (i = 0; i < argc; ++i) { 108 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
220 int len = strlen(argv[i]); 109 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
221 if (len > max_len) 110 n += callchain_node__count_rows(node);
222 max_len = len;
223 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
224 goto out_destroy_form;
225 } 111 }
226 112
227 newtCenteredWindow(max_len, argc, NULL); 113 return n;
228 newtFormRun(form, &es);
229 rc = newtListboxGetCurrent(listbox) - NULL;
230 if (es.reason == NEWT_EXIT_HOTKEY)
231 rc = -1;
232 newtPopWindow();
233out_destroy_form:
234 newtFormDestroy(form);
235 return rc;
236} 114}
237 115
238static int ui__help_window(const char *text) 116static bool map_symbol__toggle_fold(struct map_symbol *self)
239{ 117{
240 struct newtExitStruct es; 118 if (!self->has_children)
241 newtComponent tb, form = newt_form__new(); 119 return false;
242 int rc = -1;
243 int max_len = 0, nr_lines = 0;
244 const char *t;
245
246 if (form == NULL)
247 return -1;
248
249 t = text;
250 while (1) {
251 const char *sep = strchr(t, '\n');
252 int len;
253
254 if (sep == NULL)
255 sep = strchr(t, '\0');
256 len = sep - t;
257 if (max_len < len)
258 max_len = len;
259 ++nr_lines;
260 if (*sep == '\0')
261 break;
262 t = sep + 1;
263 }
264
265 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
266 if (tb == NULL)
267 goto out_destroy_form;
268
269 newtTextboxSetText(tb, text);
270 newtFormAddComponent(form, tb);
271 newtCenteredWindow(max_len, nr_lines, NULL);
272 newtFormRun(form, &es);
273 newtPopWindow();
274 rc = 0;
275out_destroy_form:
276 newtFormDestroy(form);
277 return rc;
278}
279 120
280static bool dialog_yesno(const char *msg) 121 self->unfolded = !self->unfolded;
281{ 122 return true;
282 /* newtWinChoice should really be accepting const char pointers... */
283 char yes[] = "Yes", no[] = "No";
284 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
285} 123}
286 124
287static void ui__error_window(const char *fmt, ...) 125static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
288{ 126{
289 va_list ap; 127 struct rb_node *nd = rb_first(&self->rb_root);
290 128
291 va_start(ap, fmt); 129 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
292 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); 130 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
293 va_end(ap); 131 struct callchain_list *chain;
294} 132 int first = true;
295 133
296static void annotate_browser__write(struct ui_browser *self, void *entry, int row) 134 list_for_each_entry(chain, &child->val, list) {
297{ 135 if (first) {
298 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); 136 first = false;
299 bool current_entry = ui_browser__is_current_entry(self, row); 137 chain->ms.has_children = chain->list.next != &child->val ||
300 int width = self->width; 138 rb_first(&child->rb_root) != NULL;
301
302 if (ol->offset != -1) {
303 struct hist_entry *he = self->priv;
304 struct symbol *sym = he->ms.sym;
305 int len = he->ms.sym->end - he->ms.sym->start;
306 unsigned int hits = 0;
307 double percent = 0.0;
308 int color;
309 struct sym_priv *priv = symbol__priv(sym);
310 struct sym_ext *sym_ext = priv->ext;
311 struct sym_hist *h = priv->hist;
312 s64 offset = ol->offset;
313 struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol);
314
315 while (offset < (s64)len &&
316 (next == NULL || offset < next->offset)) {
317 if (sym_ext) {
318 percent += sym_ext[offset].percent;
319 } else 139 } else
320 hits += h->ip[offset]; 140 chain->ms.has_children = chain->list.next == &child->val &&
321 141 rb_first(&child->rb_root) != NULL;
322 ++offset;
323 } 142 }
324 143
325 if (sym_ext == NULL && h->sum) 144 callchain_node__init_have_children_rb_tree(child);
326 percent = 100.0 * hits / h->sum;
327
328 color = ui_browser__percent_color(percent, current_entry);
329 SLsmg_set_color(color);
330 slsmg_printf(" %7.2f ", percent);
331 if (!current_entry)
332 SLsmg_set_color(HE_COLORSET_CODE);
333 } else {
334 int color = ui_browser__percent_color(0, current_entry);
335 SLsmg_set_color(color);
336 slsmg_write_nstring(" ", 9);
337 } 145 }
338
339 SLsmg_write_char(':');
340 slsmg_write_nstring(" ", 8);
341 if (!*ol->line)
342 slsmg_write_nstring(" ", width - 18);
343 else
344 slsmg_write_nstring(ol->line, width - 18);
345} 146}
346 147
347static char *callchain_list__sym_name(struct callchain_list *self, 148static void callchain_node__init_have_children(struct callchain_node *self)
348 char *bf, size_t bfsize)
349{
350 if (self->ms.sym)
351 return self->ms.sym->name;
352
353 snprintf(bf, bfsize, "%#Lx", self->ip);
354 return bf;
355}
356
357int hist_entry__tui_annotate(struct hist_entry *self)
358{ 149{
359 struct newtExitStruct es; 150 struct callchain_list *chain;
360 struct objdump_line *pos, *n;
361 LIST_HEAD(head);
362 struct ui_browser browser = {
363 .entries = &head,
364 .refresh = ui_browser__list_head_refresh,
365 .seek = ui_browser__list_head_seek,
366 .write = annotate_browser__write,
367 .priv = self,
368 };
369 int ret;
370
371 if (self->ms.sym == NULL)
372 return -1;
373
374 if (self->ms.map->dso->annotate_warned)
375 return -1;
376
377 if (hist_entry__annotate(self, &head) < 0) {
378 ui__error_window(browser__last_msg);
379 return -1;
380 }
381
382 ui_helpline__push("Press <- or ESC to exit");
383 151
384 list_for_each_entry(pos, &head, node) { 152 list_for_each_entry(chain, &self->val, list)
385 size_t line_len = strlen(pos->line); 153 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
386 if (browser.width < line_len)
387 browser.width = line_len;
388 ++browser.nr_entries;
389 }
390 154
391 browser.width += 18; /* Percentage */ 155 callchain_node__init_have_children_rb_tree(self);
392 ui_browser__show(&browser, self->ms.sym->name);
393 newtFormAddHotKey(browser.form, ' ');
394 ret = ui_browser__run(&browser, &es);
395 newtFormDestroy(browser.form);
396 newtPopWindow();
397 list_for_each_entry_safe(pos, n, &head, node) {
398 list_del(&pos->node);
399 objdump_line__free(pos);
400 }
401 ui_helpline__pop();
402 return ret;
403} 156}
404 157
405/* -------------------------------------------------------------------- */ 158static void callchain__init_have_children(struct rb_root *self)
406
407struct map_browser {
408 struct ui_browser b;
409 struct map *map;
410 u16 namelen;
411 u8 addrlen;
412};
413
414static void map_browser__write(struct ui_browser *self, void *nd, int row)
415{ 159{
416 struct symbol *sym = rb_entry(nd, struct symbol, rb_node); 160 struct rb_node *nd;
417 struct map_browser *mb = container_of(self, struct map_browser, b);
418 bool current_entry = ui_browser__is_current_entry(self, row);
419 int color = ui_browser__percent_color(0, current_entry);
420
421 SLsmg_set_color(color);
422 slsmg_printf("%*llx %*llx %c ",
423 mb->addrlen, sym->start, mb->addrlen, sym->end,
424 sym->binding == STB_GLOBAL ? 'g' :
425 sym->binding == STB_LOCAL ? 'l' : 'w');
426 slsmg_write_nstring(sym->name, mb->namelen);
427}
428 161
429/* FIXME uber-kludgy, see comment on cmd_report... */ 162 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
430static u32 *symbol__browser_index(struct symbol *self) 163 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
431{ 164 callchain_node__init_have_children(node);
432 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); 165 }
433} 166}
434 167
435static int map_browser__search(struct map_browser *self) 168static void hist_entry__init_have_children(struct hist_entry *self)
436{ 169{
437 char target[512]; 170 if (!self->init_have_children) {
438 struct symbol *sym; 171 callchain__init_have_children(&self->sorted_chain);
439 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); 172 self->init_have_children = true;
440 173 }
441 if (err)
442 return err;
443
444 if (target[0] == '0' && tolower(target[1]) == 'x') {
445 u64 addr = strtoull(target, NULL, 16);
446 sym = map__find_symbol(self->map, addr, NULL);
447 } else
448 sym = map__find_symbol_by_name(self->map, target, NULL);
449
450 if (sym != NULL) {
451 u32 *idx = symbol__browser_index(sym);
452
453 self->b.first_visible_entry = &sym->rb_node;
454 self->b.index = self->b.first_visible_entry_idx = *idx;
455 } else
456 ui_helpline__fpush("%s not found!", target);
457
458 return 0;
459} 174}
460 175
461static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) 176static bool hist_browser__toggle_fold(struct hist_browser *self)
462{ 177{
463 if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) 178 if (map_symbol__toggle_fold(self->selection)) {
464 return -1; 179 struct hist_entry *he = self->he_selection;
465
466 ui_helpline__fpush("Press <- or ESC to exit, %s / to search",
467 verbose ? "" : "restart with -v to use");
468 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
469 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
470 if (verbose)
471 newtFormAddHotKey(self->b.form, '/');
472 180
473 while (1) { 181 hist_entry__init_have_children(he);
474 ui_browser__run(&self->b, es); 182 self->hists->nr_entries -= he->nr_rows;
475 183
476 if (es->reason != NEWT_EXIT_HOTKEY) 184 if (he->ms.unfolded)
477 break; 185 he->nr_rows = callchain__count_rows(&he->sorted_chain);
478 if (verbose && es->u.key == '/')
479 map_browser__search(self);
480 else 186 else
481 break; 187 he->nr_rows = 0;
482 } 188 self->hists->nr_entries += he->nr_rows;
483 189 self->b.nr_entries = self->hists->nr_entries;
484 newtFormDestroy(self->b.form);
485 newtPopWindow();
486 ui_helpline__pop();
487 return 0;
488}
489 190
490static int map__browse(struct map *self) 191 return true;
491{
492 struct map_browser mb = {
493 .b = {
494 .entries = &self->dso->symbols[self->type],
495 .refresh = ui_browser__rb_tree_refresh,
496 .seek = ui_browser__rb_tree_seek,
497 .write = map_browser__write,
498 },
499 .map = self,
500 };
501 struct newtExitStruct es;
502 struct rb_node *nd;
503 char tmp[BITS_PER_LONG / 4];
504 u64 maxaddr = 0;
505
506 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
507 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
508
509 if (mb.namelen < pos->namelen)
510 mb.namelen = pos->namelen;
511 if (maxaddr < pos->end)
512 maxaddr = pos->end;
513 if (verbose) {
514 u32 *idx = symbol__browser_index(pos);
515 *idx = mb.b.nr_entries;
516 }
517 ++mb.b.nr_entries;
518 } 192 }
519 193
520 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); 194 /* If it doesn't have children, no toggling performed */
521 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; 195 return false;
522 return map_browser__run(&mb, &es);
523} 196}
524 197
525/* -------------------------------------------------------------------- */
526
527struct hist_browser {
528 struct ui_browser b;
529 struct hists *hists;
530 struct hist_entry *he_selection;
531 struct map_symbol *selection;
532};
533
534static void hist_browser__reset(struct hist_browser *self);
535static int hist_browser__run(struct hist_browser *self, const char *title, 198static int hist_browser__run(struct hist_browser *self, const char *title,
536 struct newtExitStruct *es); 199 struct newtExitStruct *es)
537static unsigned int hist_browser__refresh(struct ui_browser *self);
538static void ui_browser__hists_seek(struct ui_browser *self,
539 off_t offset, int whence);
540
541static struct hist_browser *hist_browser__new(struct hists *hists)
542{
543 struct hist_browser *self = zalloc(sizeof(*self));
544
545 if (self) {
546 self->hists = hists;
547 self->b.refresh = hist_browser__refresh;
548 self->b.seek = ui_browser__hists_seek;
549 }
550
551 return self;
552}
553
554static void hist_browser__delete(struct hist_browser *self)
555{
556 newtFormDestroy(self->b.form);
557 newtPopWindow();
558 free(self);
559}
560
561static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
562{
563 return self->he_selection;
564}
565
566static struct thread *hist_browser__selected_thread(struct hist_browser *self)
567{ 200{
568 return self->he_selection->thread; 201 char str[256], unit;
569} 202 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
570 203
571static int hist_browser__title(char *bf, size_t size, const char *ev_name, 204 self->b.entries = &self->hists->entries;
572 const struct dso *dso, const struct thread *thread) 205 self->b.nr_entries = self->hists->nr_entries;
573{
574 int printed = 0;
575 206
576 if (thread) 207 hist_browser__refresh_dimensions(self);
577 printed += snprintf(bf + printed, size - printed,
578 "Thread: %s(%d)",
579 (thread->comm_set ? thread->comm : ""),
580 thread->pid);
581 if (dso)
582 printed += snprintf(bf + printed, size - printed,
583 "%sDSO: %s", thread ? " " : "",
584 dso->short_name);
585 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
586}
587 208
588int hists__browse(struct hists *self, const char *helpline, const char *ev_name) 209 nr_events = convert_unit(nr_events, &unit);
589{ 210 snprintf(str, sizeof(str), "Events: %lu%c ",
590 struct hist_browser *browser = hist_browser__new(self); 211 nr_events, unit);
591 struct pstack *fstack; 212 newtDrawRootText(0, 0, str);
592 const struct thread *thread_filter = NULL;
593 const struct dso *dso_filter = NULL;
594 struct newtExitStruct es;
595 char msg[160];
596 int key = -1;
597 213
598 if (browser == NULL) 214 if (ui_browser__show(&self->b, title,
215 "Press '?' for help on key bindings") < 0)
599 return -1; 216 return -1;
600 217
601 fstack = pstack__new(2); 218 newtFormAddHotKey(self->b.form, 'a');
602 if (fstack == NULL) 219 newtFormAddHotKey(self->b.form, '?');
603 goto out; 220 newtFormAddHotKey(self->b.form, 'h');
604 221 newtFormAddHotKey(self->b.form, 'd');
605 ui_helpline__push(helpline); 222 newtFormAddHotKey(self->b.form, 'D');
223 newtFormAddHotKey(self->b.form, 't');
606 224
607 hist_browser__title(msg, sizeof(msg), ev_name, 225 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
608 dso_filter, thread_filter); 226 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
227 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
609 228
610 while (1) { 229 while (1) {
611 const struct thread *thread; 230 ui_browser__run(&self->b, es);
612 const struct dso *dso;
613 char *options[16];
614 int nr_options = 0, choice = 0, i,
615 annotate = -2, zoom_dso = -2, zoom_thread = -2,
616 browse_map = -2;
617 231
618 if (hist_browser__run(browser, msg, &es)) 232 if (es->reason != NEWT_EXIT_HOTKEY)
619 break; 233 break;
620 234 switch (es->u.key) {
621 thread = hist_browser__selected_thread(browser); 235 case 'D': { /* Debug */
622 dso = browser->selection->map ? browser->selection->map->dso : NULL; 236 static int seq;
623 237 struct hist_entry *h = rb_entry(self->b.top,
624 if (es.reason == NEWT_EXIT_HOTKEY) { 238 struct hist_entry, rb_node);
625 key = es.u.key; 239 ui_helpline__pop();
626 240 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
627 switch (key) { 241 seq++, self->b.nr_entries,
628 case NEWT_KEY_F1: 242 self->hists->nr_entries,
629 goto do_help; 243 self->b.height,
630 case NEWT_KEY_TAB: 244 self->b.index,
631 case NEWT_KEY_UNTAB: 245 self->b.top_idx,
632 /* 246 h->row_offset, h->nr_rows);
633 * Exit the browser, let hists__browser_tree
634 * go to the next or previous
635 */
636 goto out_free_stack;
637 default:;
638 }
639
640 key = toupper(key);
641 switch (key) {
642 case 'A':
643 if (browser->selection->map == NULL &&
644 browser->selection->map->dso->annotate_warned)
645 continue;
646 goto do_annotate;
647 case 'D':
648 goto zoom_dso;
649 case 'T':
650 goto zoom_thread;
651 case 'H':
652 case '?':
653do_help:
654 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
655 "<- Zoom out\n"
656 "a Annotate current symbol\n"
657 "h/?/F1 Show this window\n"
658 "d Zoom into current DSO\n"
659 "t Zoom into current Thread\n"
660 "q/CTRL+C Exit browser");
661 continue;
662 default:;
663 }
664 if (is_exit_key(key)) {
665 if (key == NEWT_KEY_ESCAPE) {
666 if (dialog_yesno("Do you really want to exit?"))
667 break;
668 else
669 continue;
670 } else
671 break;
672 }
673
674 if (es.u.key == NEWT_KEY_LEFT) {
675 const void *top;
676
677 if (pstack__empty(fstack))
678 continue;
679 top = pstack__pop(fstack);
680 if (top == &dso_filter)
681 goto zoom_out_dso;
682 if (top == &thread_filter)
683 goto zoom_out_thread;
684 continue;
685 }
686 } 247 }
687
688 if (browser->selection->sym != NULL &&
689 !browser->selection->map->dso->annotate_warned &&
690 asprintf(&options[nr_options], "Annotate %s",
691 browser->selection->sym->name) > 0)
692 annotate = nr_options++;
693
694 if (thread != NULL &&
695 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
696 (thread_filter ? "out of" : "into"),
697 (thread->comm_set ? thread->comm : ""),
698 thread->pid) > 0)
699 zoom_thread = nr_options++;
700
701 if (dso != NULL &&
702 asprintf(&options[nr_options], "Zoom %s %s DSO",
703 (dso_filter ? "out of" : "into"),
704 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
705 zoom_dso = nr_options++;
706
707 if (browser->selection->map != NULL &&
708 asprintf(&options[nr_options], "Browse map details") > 0)
709 browse_map = nr_options++;
710
711 options[nr_options++] = (char *)"Exit";
712
713 choice = popup_menu(nr_options, options);
714
715 for (i = 0; i < nr_options - 1; ++i)
716 free(options[i]);
717
718 if (choice == nr_options - 1)
719 break;
720
721 if (choice == -1)
722 continue; 248 continue;
723 249 case NEWT_KEY_ENTER:
724 if (choice == annotate) { 250 if (hist_browser__toggle_fold(self))
725 struct hist_entry *he; 251 break;
726do_annotate: 252 /* fall thru */
727 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
728 browser->selection->map->dso->annotate_warned = 1;
729 ui_helpline__puts("No vmlinux file found, can't "
730 "annotate with just a "
731 "kallsyms file");
732 continue;
733 }
734
735 he = hist_browser__selected_entry(browser);
736 if (he == NULL)
737 continue;
738
739 hist_entry__tui_annotate(he);
740 } else if (choice == browse_map)
741 map__browse(browser->selection->map);
742 else if (choice == zoom_dso) {
743zoom_dso:
744 if (dso_filter) {
745 pstack__remove(fstack, &dso_filter);
746zoom_out_dso:
747 ui_helpline__pop();
748 dso_filter = NULL;
749 } else {
750 if (dso == NULL)
751 continue;
752 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
753 dso->kernel ? "the Kernel" : dso->short_name);
754 dso_filter = dso;
755 pstack__push(fstack, &dso_filter);
756 }
757 hists__filter_by_dso(self, dso_filter);
758 hist_browser__title(msg, sizeof(msg), ev_name,
759 dso_filter, thread_filter);
760 hist_browser__reset(browser);
761 } else if (choice == zoom_thread) {
762zoom_thread:
763 if (thread_filter) {
764 pstack__remove(fstack, &thread_filter);
765zoom_out_thread:
766 ui_helpline__pop();
767 thread_filter = NULL;
768 } else {
769 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
770 thread->comm_set ? thread->comm : "",
771 thread->pid);
772 thread_filter = thread;
773 pstack__push(fstack, &thread_filter);
774 }
775 hists__filter_by_thread(self, thread_filter);
776 hist_browser__title(msg, sizeof(msg), ev_name,
777 dso_filter, thread_filter);
778 hist_browser__reset(browser);
779 }
780 }
781out_free_stack:
782 pstack__delete(fstack);
783out:
784 hist_browser__delete(browser);
785 return key;
786}
787
788int hists__tui_browse_tree(struct rb_root *self, const char *help)
789{
790 struct rb_node *first = rb_first(self), *nd = first, *next;
791 int key = 0;
792
793 while (nd) {
794 struct hists *hists = rb_entry(nd, struct hists, rb_node);
795 const char *ev_name = __event_name(hists->type, hists->config);
796
797 key = hists__browse(hists, help, ev_name);
798
799 if (is_exit_key(key))
800 break;
801
802 switch (key) {
803 case NEWT_KEY_TAB:
804 next = rb_next(nd);
805 if (next)
806 nd = next;
807 break;
808 case NEWT_KEY_UNTAB:
809 if (nd == first)
810 continue;
811 nd = rb_prev(nd);
812 default: 253 default:
813 break; 254 return 0;
814 }
815 }
816
817 return key;
818}
819
820static void newt_suspend(void *d __used)
821{
822 newtSuspend();
823 raise(SIGTSTP);
824 newtResume();
825}
826
827void setup_browser(void)
828{
829 if (!isatty(1) || !use_browser || dump_trace) {
830 use_browser = 0;
831 setup_pager();
832 return;
833 }
834
835 use_browser = 1;
836 newtInit();
837 newtCls();
838 newtSetSuspendCallback(newt_suspend, NULL);
839 ui_helpline__puts(" ");
840 ui_browser__init();
841}
842
843void exit_browser(bool wait_for_ok)
844{
845 if (use_browser > 0) {
846 if (wait_for_ok) {
847 char title[] = "Fatal Error", ok[] = "Ok";
848 newtWinMessage(title, ok, browser__last_msg);
849 } 255 }
850 newtFinished();
851 } 256 }
852}
853 257
854static void hist_browser__refresh_dimensions(struct hist_browser *self) 258 ui_browser__hide(&self->b);
855{ 259 return 0;
856 /* 3 == +/- toggle symbol before actual hist_entry rendering */
857 self->b.width = 3 + (hists__sort_list_width(self->hists) +
858 sizeof("[k]"));
859}
860
861static void hist_browser__reset(struct hist_browser *self)
862{
863 self->b.nr_entries = self->hists->nr_entries;
864 hist_browser__refresh_dimensions(self);
865 ui_browser__reset_index(&self->b);
866}
867
868static char tree__folded_sign(bool unfolded)
869{
870 return unfolded ? '-' : '+';
871}
872
873static char map_symbol__folded(const struct map_symbol *self)
874{
875 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
876}
877
878static char hist_entry__folded(const struct hist_entry *self)
879{
880 return map_symbol__folded(&self->ms);
881}
882
883static char callchain_list__folded(const struct callchain_list *self)
884{
885 return map_symbol__folded(&self->ms);
886} 260}
887 261
888static bool map_symbol__toggle_fold(struct map_symbol *self) 262static char *callchain_list__sym_name(struct callchain_list *self,
263 char *bf, size_t bfsize)
889{ 264{
890 if (!self->has_children) 265 if (self->ms.sym)
891 return false; 266 return self->ms.sym->name;
892 267
893 self->unfolded = !self->unfolded; 268 snprintf(bf, bfsize, "%#Lx", self->ip);
894 return true; 269 return bf;
895} 270}
896 271
897#define LEVEL_OFFSET_STEP 3 272#define LEVEL_OFFSET_STEP 3
@@ -967,7 +342,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
967 } 342 }
968 343
969 SLsmg_set_color(color); 344 SLsmg_set_color(color);
970 SLsmg_gotorc(self->b.top + row, self->b.left); 345 SLsmg_gotorc(self->b.y + row, self->b.x);
971 slsmg_write_nstring(" ", offset + extra_offset); 346 slsmg_write_nstring(" ", offset + extra_offset);
972 slsmg_printf("%c ", folded_sign); 347 slsmg_printf("%c ", folded_sign);
973 slsmg_write_nstring(str, width); 348 slsmg_write_nstring(str, width);
@@ -1030,7 +405,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
1030 } 405 }
1031 406
1032 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); 407 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1033 SLsmg_gotorc(self->b.top + row, self->b.left); 408 SLsmg_gotorc(self->b.y + row, self->b.x);
1034 SLsmg_set_color(color); 409 SLsmg_set_color(color);
1035 slsmg_write_nstring(" ", offset); 410 slsmg_write_nstring(" ", offset);
1036 slsmg_printf("%c ", folded_sign); 411 slsmg_printf("%c ", folded_sign);
@@ -1110,7 +485,7 @@ static int hist_browser__show_entry(struct hist_browser *self,
1110 } 485 }
1111 486
1112 SLsmg_set_color(color); 487 SLsmg_set_color(color);
1113 SLsmg_gotorc(self->b.top + row, self->b.left); 488 SLsmg_gotorc(self->b.y + row, self->b.x);
1114 if (symbol_conf.use_callchain) { 489 if (symbol_conf.use_callchain) {
1115 slsmg_printf("%c ", folded_sign); 490 slsmg_printf("%c ", folded_sign);
1116 width -= 2; 491 width -= 2;
@@ -1138,10 +513,10 @@ static unsigned int hist_browser__refresh(struct ui_browser *self)
1138 struct rb_node *nd; 513 struct rb_node *nd;
1139 struct hist_browser *hb = container_of(self, struct hist_browser, b); 514 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1140 515
1141 if (self->first_visible_entry == NULL) 516 if (self->top == NULL)
1142 self->first_visible_entry = rb_first(&hb->hists->entries); 517 self->top = rb_first(&hb->hists->entries);
1143 518
1144 for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { 519 for (nd = self->top; nd; nd = rb_next(nd)) {
1145 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 520 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1146 521
1147 if (h->filtered) 522 if (h->filtered)
@@ -1155,57 +530,6 @@ static unsigned int hist_browser__refresh(struct ui_browser *self)
1155 return row; 530 return row;
1156} 531}
1157 532
1158static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
1159{
1160 struct rb_node *nd = rb_first(&self->rb_root);
1161
1162 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1163 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1164 struct callchain_list *chain;
1165 int first = true;
1166
1167 list_for_each_entry(chain, &child->val, list) {
1168 if (first) {
1169 first = false;
1170 chain->ms.has_children = chain->list.next != &child->val ||
1171 rb_first(&child->rb_root) != NULL;
1172 } else
1173 chain->ms.has_children = chain->list.next == &child->val &&
1174 rb_first(&child->rb_root) != NULL;
1175 }
1176
1177 callchain_node__init_have_children_rb_tree(child);
1178 }
1179}
1180
1181static void callchain_node__init_have_children(struct callchain_node *self)
1182{
1183 struct callchain_list *chain;
1184
1185 list_for_each_entry(chain, &self->val, list)
1186 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
1187
1188 callchain_node__init_have_children_rb_tree(self);
1189}
1190
1191static void callchain__init_have_children(struct rb_root *self)
1192{
1193 struct rb_node *nd;
1194
1195 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
1196 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1197 callchain_node__init_have_children(node);
1198 }
1199}
1200
1201static void hist_entry__init_have_children(struct hist_entry *self)
1202{
1203 if (!self->init_have_children) {
1204 callchain__init_have_children(&self->sorted_chain);
1205 self->init_have_children = true;
1206 }
1207}
1208
1209static struct rb_node *hists__filter_entries(struct rb_node *nd) 533static struct rb_node *hists__filter_entries(struct rb_node *nd)
1210{ 534{
1211 while (nd != NULL) { 535 while (nd != NULL) {
@@ -1244,7 +568,7 @@ static void ui_browser__hists_seek(struct ui_browser *self,
1244 nd = hists__filter_entries(rb_first(self->entries)); 568 nd = hists__filter_entries(rb_first(self->entries));
1245 break; 569 break;
1246 case SEEK_CUR: 570 case SEEK_CUR:
1247 nd = self->first_visible_entry; 571 nd = self->top;
1248 goto do_offset; 572 goto do_offset;
1249 case SEEK_END: 573 case SEEK_END:
1250 nd = hists__filter_prev_entries(rb_last(self->entries)); 574 nd = hists__filter_prev_entries(rb_last(self->entries));
@@ -1258,7 +582,7 @@ static void ui_browser__hists_seek(struct ui_browser *self,
1258 * Moves not relative to the first visible entry invalidates its 582 * Moves not relative to the first visible entry invalidates its
1259 * row_offset: 583 * row_offset:
1260 */ 584 */
1261 h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); 585 h = rb_entry(self->top, struct hist_entry, rb_node);
1262 h->row_offset = 0; 586 h->row_offset = 0;
1263 587
1264 /* 588 /*
@@ -1286,7 +610,7 @@ do_offset:
1286 } else { 610 } else {
1287 h->row_offset += offset; 611 h->row_offset += offset;
1288 offset = 0; 612 offset = 0;
1289 self->first_visible_entry = nd; 613 self->top = nd;
1290 break; 614 break;
1291 } 615 }
1292 } 616 }
@@ -1294,7 +618,7 @@ do_offset:
1294 if (nd == NULL) 618 if (nd == NULL)
1295 break; 619 break;
1296 --offset; 620 --offset;
1297 self->first_visible_entry = nd; 621 self->top = nd;
1298 } while (offset != 0); 622 } while (offset != 0);
1299 } else if (offset < 0) { 623 } else if (offset < 0) {
1300 while (1) { 624 while (1) {
@@ -1307,7 +631,7 @@ do_offset:
1307 } else { 631 } else {
1308 h->row_offset += offset; 632 h->row_offset += offset;
1309 offset = 0; 633 offset = 0;
1310 self->first_visible_entry = nd; 634 self->top = nd;
1311 break; 635 break;
1312 } 636 }
1313 } else { 637 } else {
@@ -1317,7 +641,7 @@ do_offset:
1317 } else { 641 } else {
1318 h->row_offset = h->nr_rows + offset; 642 h->row_offset = h->nr_rows + offset;
1319 offset = 0; 643 offset = 0;
1320 self->first_visible_entry = nd; 644 self->top = nd;
1321 break; 645 break;
1322 } 646 }
1323 } 647 }
@@ -1327,7 +651,7 @@ do_offset:
1327 if (nd == NULL) 651 if (nd == NULL)
1328 break; 652 break;
1329 ++offset; 653 ++offset;
1330 self->first_visible_entry = nd; 654 self->top = nd;
1331 if (offset == 0) { 655 if (offset == 0) {
1332 /* 656 /*
1333 * Last unfiltered hist_entry, check if it is 657 * Last unfiltered hist_entry, check if it is
@@ -1342,146 +666,283 @@ do_offset:
1342 first = false; 666 first = false;
1343 } 667 }
1344 } else { 668 } else {
1345 self->first_visible_entry = nd; 669 self->top = nd;
1346 h = rb_entry(nd, struct hist_entry, rb_node); 670 h = rb_entry(nd, struct hist_entry, rb_node);
1347 h->row_offset = 0; 671 h->row_offset = 0;
1348 } 672 }
1349} 673}
1350 674
1351static int callchain_node__count_rows_rb_tree(struct callchain_node *self) 675static struct hist_browser *hist_browser__new(struct hists *hists)
1352{ 676{
1353 int n = 0; 677 struct hist_browser *self = zalloc(sizeof(*self));
1354 struct rb_node *nd;
1355
1356 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1357 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1358 struct callchain_list *chain;
1359 char folded_sign = ' '; /* No children */
1360
1361 list_for_each_entry(chain, &child->val, list) {
1362 ++n;
1363 /* We need this because we may not have children */
1364 folded_sign = callchain_list__folded(chain);
1365 if (folded_sign == '+')
1366 break;
1367 }
1368 678
1369 if (folded_sign == '-') /* Have children and they're unfolded */ 679 if (self) {
1370 n += callchain_node__count_rows_rb_tree(child); 680 self->hists = hists;
681 self->b.refresh = hist_browser__refresh;
682 self->b.seek = ui_browser__hists_seek;
1371 } 683 }
1372 684
1373 return n; 685 return self;
1374} 686}
1375 687
1376static int callchain_node__count_rows(struct callchain_node *node) 688static void hist_browser__delete(struct hist_browser *self)
1377{ 689{
1378 struct callchain_list *chain; 690 newtFormDestroy(self->b.form);
1379 bool unfolded = false; 691 newtPopWindow();
1380 int n = 0; 692 free(self);
1381 693}
1382 list_for_each_entry(chain, &node->val, list) {
1383 ++n;
1384 unfolded = chain->ms.unfolded;
1385 }
1386
1387 if (unfolded)
1388 n += callchain_node__count_rows_rb_tree(node);
1389 694
1390 return n; 695static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
696{
697 return self->he_selection;
1391} 698}
1392 699
1393static int callchain__count_rows(struct rb_root *chain) 700static struct thread *hist_browser__selected_thread(struct hist_browser *self)
1394{ 701{
1395 struct rb_node *nd; 702 return self->he_selection->thread;
1396 int n = 0; 703}
1397 704
1398 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 705static int hist_browser__title(char *bf, size_t size, const char *ev_name,
1399 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 706 const struct dso *dso, const struct thread *thread)
1400 n += callchain_node__count_rows(node); 707{
1401 } 708 int printed = 0;
1402 709
1403 return n; 710 if (thread)
711 printed += snprintf(bf + printed, size - printed,
712 "Thread: %s(%d)",
713 (thread->comm_set ? thread->comm : ""),
714 thread->pid);
715 if (dso)
716 printed += snprintf(bf + printed, size - printed,
717 "%sDSO: %s", thread ? " " : "",
718 dso->short_name);
719 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
1404} 720}
1405 721
1406static bool hist_browser__toggle_fold(struct hist_browser *self) 722int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
1407{ 723{
1408 if (map_symbol__toggle_fold(self->selection)) { 724 struct hist_browser *browser = hist_browser__new(self);
1409 struct hist_entry *he = self->he_selection; 725 struct pstack *fstack;
726 const struct thread *thread_filter = NULL;
727 const struct dso *dso_filter = NULL;
728 struct newtExitStruct es;
729 char msg[160];
730 int key = -1;
1410 731
1411 hist_entry__init_have_children(he); 732 if (browser == NULL)
1412 self->hists->nr_entries -= he->nr_rows; 733 return -1;
1413 734
1414 if (he->ms.unfolded) 735 fstack = pstack__new(2);
1415 he->nr_rows = callchain__count_rows(&he->sorted_chain); 736 if (fstack == NULL)
1416 else 737 goto out;
1417 he->nr_rows = 0;
1418 self->hists->nr_entries += he->nr_rows;
1419 self->b.nr_entries = self->hists->nr_entries;
1420 738
1421 return true; 739 ui_helpline__push(helpline);
1422 }
1423 740
1424 /* If it doesn't have children, no toggling performed */ 741 hist_browser__title(msg, sizeof(msg), ev_name,
1425 return false; 742 dso_filter, thread_filter);
1426}
1427 743
1428static int hist_browser__run(struct hist_browser *self, const char *title, 744 while (1) {
1429 struct newtExitStruct *es) 745 const struct thread *thread;
1430{ 746 const struct dso *dso;
1431 char str[256], unit; 747 char *options[16];
1432 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; 748 int nr_options = 0, choice = 0, i,
749 annotate = -2, zoom_dso = -2, zoom_thread = -2,
750 browse_map = -2;
1433 751
1434 self->b.entries = &self->hists->entries; 752 if (hist_browser__run(browser, msg, &es))
1435 self->b.nr_entries = self->hists->nr_entries; 753 break;
1436 754
1437 hist_browser__refresh_dimensions(self); 755 thread = hist_browser__selected_thread(browser);
756 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1438 757
1439 nr_events = convert_unit(nr_events, &unit); 758 if (es.reason == NEWT_EXIT_HOTKEY) {
1440 snprintf(str, sizeof(str), "Events: %lu%c ", 759 key = es.u.key;
1441 nr_events, unit);
1442 newtDrawRootText(0, 0, str);
1443 760
1444 if (ui_browser__show(&self->b, title) < 0) 761 switch (key) {
1445 return -1; 762 case NEWT_KEY_F1:
763 goto do_help;
764 case NEWT_KEY_TAB:
765 case NEWT_KEY_UNTAB:
766 /*
767 * Exit the browser, let hists__browser_tree
768 * go to the next or previous
769 */
770 goto out_free_stack;
771 default:;
772 }
1446 773
1447 newtFormAddHotKey(self->b.form, 'A'); 774 switch (key) {
1448 newtFormAddHotKey(self->b.form, 'a'); 775 case 'a':
1449 newtFormAddHotKey(self->b.form, '?'); 776 if (browser->selection->map == NULL &&
1450 newtFormAddHotKey(self->b.form, 'h'); 777 browser->selection->map->dso->annotate_warned)
1451 newtFormAddHotKey(self->b.form, 'H'); 778 continue;
1452 newtFormAddHotKey(self->b.form, 'd'); 779 goto do_annotate;
780 case 'd':
781 goto zoom_dso;
782 case 't':
783 goto zoom_thread;
784 case 'h':
785 case '?':
786do_help:
787 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
788 "<- Zoom out\n"
789 "a Annotate current symbol\n"
790 "h/?/F1 Show this window\n"
791 "d Zoom into current DSO\n"
792 "t Zoom into current Thread\n"
793 "q/CTRL+C Exit browser");
794 continue;
795 default:;
796 }
797 if (is_exit_key(key)) {
798 if (key == NEWT_KEY_ESCAPE &&
799 !ui__dialog_yesno("Do you really want to exit?"))
800 continue;
801 break;
802 }
1453 803
1454 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); 804 if (es.u.key == NEWT_KEY_LEFT) {
1455 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); 805 const void *top;
1456 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1457 806
1458 while (1) { 807 if (pstack__empty(fstack))
1459 ui_browser__run(&self->b, es); 808 continue;
809 top = pstack__pop(fstack);
810 if (top == &dso_filter)
811 goto zoom_out_dso;
812 if (top == &thread_filter)
813 goto zoom_out_thread;
814 continue;
815 }
816 }
1460 817
1461 if (es->reason != NEWT_EXIT_HOTKEY) 818 if (browser->selection->sym != NULL &&
819 !browser->selection->map->dso->annotate_warned &&
820 asprintf(&options[nr_options], "Annotate %s",
821 browser->selection->sym->name) > 0)
822 annotate = nr_options++;
823
824 if (thread != NULL &&
825 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
826 (thread_filter ? "out of" : "into"),
827 (thread->comm_set ? thread->comm : ""),
828 thread->pid) > 0)
829 zoom_thread = nr_options++;
830
831 if (dso != NULL &&
832 asprintf(&options[nr_options], "Zoom %s %s DSO",
833 (dso_filter ? "out of" : "into"),
834 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
835 zoom_dso = nr_options++;
836
837 if (browser->selection->map != NULL &&
838 asprintf(&options[nr_options], "Browse map details") > 0)
839 browse_map = nr_options++;
840
841 options[nr_options++] = (char *)"Exit";
842
843 choice = ui__popup_menu(nr_options, options);
844
845 for (i = 0; i < nr_options - 1; ++i)
846 free(options[i]);
847
848 if (choice == nr_options - 1)
1462 break; 849 break;
1463 switch (es->u.key) { 850
1464 case 'd': { /* Debug */ 851 if (choice == -1)
1465 static int seq;
1466 struct hist_entry *h = rb_entry(self->b.first_visible_entry,
1467 struct hist_entry, rb_node);
1468 ui_helpline__pop();
1469 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1470 seq++, self->b.nr_entries,
1471 self->hists->nr_entries,
1472 self->b.height,
1473 self->b.index,
1474 self->b.first_visible_entry_idx,
1475 h->row_offset, h->nr_rows);
1476 }
1477 continue; 852 continue;
1478 case NEWT_KEY_ENTER: 853
1479 if (hist_browser__toggle_fold(self)) 854 if (choice == annotate) {
1480 break; 855 struct hist_entry *he;
1481 /* fall thru */ 856do_annotate:
857 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
858 browser->selection->map->dso->annotate_warned = 1;
859 ui_helpline__puts("No vmlinux file found, can't "
860 "annotate with just a "
861 "kallsyms file");
862 continue;
863 }
864
865 he = hist_browser__selected_entry(browser);
866 if (he == NULL)
867 continue;
868
869 hist_entry__tui_annotate(he);
870 } else if (choice == browse_map)
871 map__browse(browser->selection->map);
872 else if (choice == zoom_dso) {
873zoom_dso:
874 if (dso_filter) {
875 pstack__remove(fstack, &dso_filter);
876zoom_out_dso:
877 ui_helpline__pop();
878 dso_filter = NULL;
879 } else {
880 if (dso == NULL)
881 continue;
882 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
883 dso->kernel ? "the Kernel" : dso->short_name);
884 dso_filter = dso;
885 pstack__push(fstack, &dso_filter);
886 }
887 hists__filter_by_dso(self, dso_filter);
888 hist_browser__title(msg, sizeof(msg), ev_name,
889 dso_filter, thread_filter);
890 hist_browser__reset(browser);
891 } else if (choice == zoom_thread) {
892zoom_thread:
893 if (thread_filter) {
894 pstack__remove(fstack, &thread_filter);
895zoom_out_thread:
896 ui_helpline__pop();
897 thread_filter = NULL;
898 } else {
899 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
900 thread->comm_set ? thread->comm : "",
901 thread->pid);
902 thread_filter = thread;
903 pstack__push(fstack, &thread_filter);
904 }
905 hists__filter_by_thread(self, thread_filter);
906 hist_browser__title(msg, sizeof(msg), ev_name,
907 dso_filter, thread_filter);
908 hist_browser__reset(browser);
909 }
910 }
911out_free_stack:
912 pstack__delete(fstack);
913out:
914 hist_browser__delete(browser);
915 return key;
916}
917
918int hists__tui_browse_tree(struct rb_root *self, const char *help)
919{
920 struct rb_node *first = rb_first(self), *nd = first, *next;
921 int key = 0;
922
923 while (nd) {
924 struct hists *hists = rb_entry(nd, struct hists, rb_node);
925 const char *ev_name = __event_name(hists->type, hists->config);
926
927 key = hists__browse(hists, help, ev_name);
928
929 if (is_exit_key(key))
930 break;
931
932 switch (key) {
933 case NEWT_KEY_TAB:
934 next = rb_next(nd);
935 if (next)
936 nd = next;
937 break;
938 case NEWT_KEY_UNTAB:
939 if (nd == first)
940 continue;
941 nd = rb_prev(nd);
1482 default: 942 default:
1483 return 0; 943 break;
1484 } 944 }
1485 } 945 }
1486 return 0; 946
947 return key;
1487} 948}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
new file mode 100644
index 000000000000..142b825b42bf
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.c
@@ -0,0 +1,161 @@
1#include "../libslang.h"
2#include <elf.h>
3#include <newt.h>
4#include <sys/ttydefaults.h>
5#include <ctype.h>
6#include <string.h>
7#include <linux/bitops.h>
8#include "../../debug.h"
9#include "../../symbol.h"
10#include "../browser.h"
11#include "../helpline.h"
12#include "map.h"
13
14static int ui_entry__read(const char *title, char *bf, size_t size, int width)
15{
16 struct newtExitStruct es;
17 newtComponent form, entry;
18 const char *result;
19 int err = -1;
20
21 newtCenteredWindow(width, 1, title);
22 form = newtForm(NULL, NULL, 0);
23 if (form == NULL)
24 return -1;
25
26 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
27 if (entry == NULL)
28 goto out_free_form;
29
30 newtFormAddComponent(form, entry);
31 newtFormAddHotKey(form, NEWT_KEY_ENTER);
32 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
33 newtFormAddHotKey(form, NEWT_KEY_LEFT);
34 newtFormAddHotKey(form, CTRL('c'));
35 newtFormRun(form, &es);
36
37 if (result != NULL) {
38 strncpy(bf, result, size);
39 err = 0;
40 }
41out_free_form:
42 newtPopWindow();
43 newtFormDestroy(form);
44 return 0;
45}
46
47struct map_browser {
48 struct ui_browser b;
49 struct map *map;
50 u16 namelen;
51 u8 addrlen;
52};
53
54static void map_browser__write(struct ui_browser *self, void *nd, int row)
55{
56 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
57 struct map_browser *mb = container_of(self, struct map_browser, b);
58 bool current_entry = ui_browser__is_current_entry(self, row);
59 int color = ui_browser__percent_color(0, current_entry);
60
61 SLsmg_set_color(color);
62 slsmg_printf("%*llx %*llx %c ",
63 mb->addrlen, sym->start, mb->addrlen, sym->end,
64 sym->binding == STB_GLOBAL ? 'g' :
65 sym->binding == STB_LOCAL ? 'l' : 'w');
66 slsmg_write_nstring(sym->name, mb->namelen);
67}
68
69/* FIXME uber-kludgy, see comment on cmd_report... */
70static u32 *symbol__browser_index(struct symbol *self)
71{
72 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
73}
74
75static int map_browser__search(struct map_browser *self)
76{
77 char target[512];
78 struct symbol *sym;
79 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
80
81 if (err)
82 return err;
83
84 if (target[0] == '0' && tolower(target[1]) == 'x') {
85 u64 addr = strtoull(target, NULL, 16);
86 sym = map__find_symbol(self->map, addr, NULL);
87 } else
88 sym = map__find_symbol_by_name(self->map, target, NULL);
89
90 if (sym != NULL) {
91 u32 *idx = symbol__browser_index(sym);
92
93 self->b.top = &sym->rb_node;
94 self->b.index = self->b.top_idx = *idx;
95 } else
96 ui_helpline__fpush("%s not found!", target);
97
98 return 0;
99}
100
101static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
102{
103 if (ui_browser__show(&self->b, self->map->dso->long_name,
104 "Press <- or ESC to exit, %s / to search",
105 verbose ? "" : "restart with -v to use") < 0)
106 return -1;
107
108 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
109 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
110 if (verbose)
111 newtFormAddHotKey(self->b.form, '/');
112
113 while (1) {
114 ui_browser__run(&self->b, es);
115
116 if (es->reason != NEWT_EXIT_HOTKEY)
117 break;
118 if (verbose && es->u.key == '/')
119 map_browser__search(self);
120 else
121 break;
122 }
123
124 ui_browser__hide(&self->b);
125 return 0;
126}
127
128int map__browse(struct map *self)
129{
130 struct map_browser mb = {
131 .b = {
132 .entries = &self->dso->symbols[self->type],
133 .refresh = ui_browser__rb_tree_refresh,
134 .seek = ui_browser__rb_tree_seek,
135 .write = map_browser__write,
136 },
137 .map = self,
138 };
139 struct newtExitStruct es;
140 struct rb_node *nd;
141 char tmp[BITS_PER_LONG / 4];
142 u64 maxaddr = 0;
143
144 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
145 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
146
147 if (mb.namelen < pos->namelen)
148 mb.namelen = pos->namelen;
149 if (maxaddr < pos->end)
150 maxaddr = pos->end;
151 if (verbose) {
152 u32 *idx = symbol__browser_index(pos);
153 *idx = mb.b.nr_entries;
154 }
155 ++mb.b.nr_entries;
156 }
157
158 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
159 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
160 return map_browser__run(&mb, &es);
161}
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h
new file mode 100644
index 000000000000..df8581a43e17
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.h
@@ -0,0 +1,6 @@
1#ifndef _PERF_UI_MAP_BROWSER_H_
2#define _PERF_UI_MAP_BROWSER_H_ 1
3struct map;
4
5int map__browse(struct map *self);
6#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c
new file mode 100644
index 000000000000..8d79daa4458a
--- /dev/null
+++ b/tools/perf/util/ui/helpline.c
@@ -0,0 +1,69 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#include <stdlib.h>
4#include <newt.h>
5
6#include "../debug.h"
7#include "helpline.h"
8
9void ui_helpline__pop(void)
10{
11 newtPopHelpLine();
12}
13
14void ui_helpline__push(const char *msg)
15{
16 newtPushHelpLine(msg);
17}
18
19void ui_helpline__vpush(const char *fmt, va_list ap)
20{
21 char *s;
22
23 if (vasprintf(&s, fmt, ap) < 0)
24 vfprintf(stderr, fmt, ap);
25 else {
26 ui_helpline__push(s);
27 free(s);
28 }
29}
30
31void ui_helpline__fpush(const char *fmt, ...)
32{
33 va_list ap;
34
35 va_start(ap, fmt);
36 ui_helpline__vpush(fmt, ap);
37 va_end(ap);
38}
39
40void ui_helpline__puts(const char *msg)
41{
42 ui_helpline__pop();
43 ui_helpline__push(msg);
44}
45
46void ui_helpline__init(void)
47{
48 ui_helpline__puts(" ");
49}
50
51char ui_helpline__last_msg[1024];
52
53int ui_helpline__show_help(const char *format, va_list ap)
54{
55 int ret;
56 static int backlog;
57
58 ret = vsnprintf(ui_helpline__last_msg + backlog,
59 sizeof(ui_helpline__last_msg) - backlog, format, ap);
60 backlog += ret;
61
62 if (ui_helpline__last_msg[backlog - 1] == '\n') {
63 ui_helpline__puts(ui_helpline__last_msg);
64 newtRefresh();
65 backlog = 0;
66 }
67
68 return ret;
69}
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h
new file mode 100644
index 000000000000..ab6028d0c401
--- /dev/null
+++ b/tools/perf/util/ui/helpline.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1
3
4void ui_helpline__init(void);
5void ui_helpline__pop(void);
6void ui_helpline__push(const char *msg);
7void ui_helpline__vpush(const char *fmt, va_list ap);
8void ui_helpline__fpush(const char *fmt, ...);
9void ui_helpline__puts(const char *msg);
10
11#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
new file mode 100644
index 000000000000..5623da8e8080
--- /dev/null
+++ b/tools/perf/util/ui/libslang.h
@@ -0,0 +1,27 @@
1#ifndef _PERF_UI_SLANG_H_
2#define _PERF_UI_SLANG_H_ 1
3/*
4 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
5 * the build if it isn't defined. Use the equivalent one that glibc
6 * has on features.h.
7 */
8#include <features.h>
9#ifndef HAVE_LONG_LONG
10#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
11#endif
12#include <slang.h>
13
14#if SLANG_VERSION < 20104
15#define slsmg_printf(msg, args...) \
16 SLsmg_printf((char *)msg, ##args)
17#define slsmg_write_nstring(msg, len) \
18 SLsmg_write_nstring((char *)msg, len)
19#define sltt_set_color(obj, name, fg, bg) \
20 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
21#else
22#define slsmg_printf SLsmg_printf
23#define slsmg_write_nstring SLsmg_write_nstring
24#define sltt_set_color SLtt_set_color
25#endif
26
27#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c
new file mode 100644
index 000000000000..d7fc399d36b3
--- /dev/null
+++ b/tools/perf/util/ui/progress.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <newt.h>
3#include "../cache.h"
4#include "progress.h"
5
6struct ui_progress {
7 newtComponent form, scale;
8};
9
10struct ui_progress *ui_progress__new(const char *title, u64 total)
11{
12 struct ui_progress *self = malloc(sizeof(*self));
13
14 if (self != NULL) {
15 int cols;
16
17 if (use_browser <= 0)
18 return self;
19 newtGetScreenSize(&cols, NULL);
20 cols -= 4;
21 newtCenteredWindow(cols, 1, title);
22 self->form = newtForm(NULL, NULL, 0);
23 if (self->form == NULL)
24 goto out_free_self;
25 self->scale = newtScale(0, 0, cols, total);
26 if (self->scale == NULL)
27 goto out_free_form;
28 newtFormAddComponent(self->form, self->scale);
29 newtRefresh();
30 }
31
32 return self;
33
34out_free_form:
35 newtFormDestroy(self->form);
36out_free_self:
37 free(self);
38 return NULL;
39}
40
41void ui_progress__update(struct ui_progress *self, u64 curr)
42{
43 /*
44 * FIXME: We should have a per UI backend way of showing progress,
45 * stdio will just show a percentage as NN%, etc.
46 */
47 if (use_browser <= 0)
48 return;
49 newtScaleSet(self->scale, curr);
50 newtRefresh();
51}
52
53void ui_progress__delete(struct ui_progress *self)
54{
55 if (use_browser > 0) {
56 newtFormDestroy(self->form);
57 newtPopWindow();
58 }
59 free(self);
60}
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h
new file mode 100644
index 000000000000..a3820a0beb5b
--- /dev/null
+++ b/tools/perf/util/ui/progress.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1
3
4struct ui_progress;
5
6struct ui_progress *ui_progress__new(const char *title, u64 total);
7void ui_progress__delete(struct ui_progress *self);
8
9void ui_progress__update(struct ui_progress *self, u64 curr);
10
11#endif
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
new file mode 100644
index 000000000000..662085032eb7
--- /dev/null
+++ b/tools/perf/util/ui/setup.c
@@ -0,0 +1,42 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdbool.h>
4
5#include "../cache.h"
6#include "../debug.h"
7#include "browser.h"
8#include "helpline.h"
9
10static void newt_suspend(void *d __used)
11{
12 newtSuspend();
13 raise(SIGTSTP);
14 newtResume();
15}
16
17void setup_browser(void)
18{
19 if (!isatty(1) || !use_browser || dump_trace) {
20 use_browser = 0;
21 setup_pager();
22 return;
23 }
24
25 use_browser = 1;
26 newtInit();
27 newtCls();
28 newtSetSuspendCallback(newt_suspend, NULL);
29 ui_helpline__init();
30 ui_browser__init();
31}
32
33void exit_browser(bool wait_for_ok)
34{
35 if (use_browser > 0) {
36 if (wait_for_ok) {
37 char title[] = "Fatal Error", ok[] = "Ok";
38 newtWinMessage(title, ok, ui_helpline__last_msg);
39 }
40 newtFinished();
41 }
42}
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
new file mode 100644
index 000000000000..04600e26ceea
--- /dev/null
+++ b/tools/perf/util/ui/util.c
@@ -0,0 +1,114 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include <string.h>
6#include <sys/ttydefaults.h>
7
8#include "../cache.h"
9#include "../debug.h"
10#include "browser.h"
11#include "helpline.h"
12#include "util.h"
13
14newtComponent newt_form__new(void);
15
16static void newt_form__set_exit_keys(newtComponent self)
17{
18 newtFormAddHotKey(self, NEWT_KEY_LEFT);
19 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
20 newtFormAddHotKey(self, 'Q');
21 newtFormAddHotKey(self, 'q');
22 newtFormAddHotKey(self, CTRL('c'));
23}
24
25newtComponent newt_form__new(void)
26{
27 newtComponent self = newtForm(NULL, NULL, 0);
28 if (self)
29 newt_form__set_exit_keys(self);
30 return self;
31}
32
33int ui__popup_menu(int argc, char * const argv[])
34{
35 struct newtExitStruct es;
36 int i, rc = -1, max_len = 5;
37 newtComponent listbox, form = newt_form__new();
38
39 if (form == NULL)
40 return -1;
41
42 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
43 if (listbox == NULL)
44 goto out_destroy_form;
45
46 newtFormAddComponent(form, listbox);
47
48 for (i = 0; i < argc; ++i) {
49 int len = strlen(argv[i]);
50 if (len > max_len)
51 max_len = len;
52 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
53 goto out_destroy_form;
54 }
55
56 newtCenteredWindow(max_len, argc, NULL);
57 newtFormRun(form, &es);
58 rc = newtListboxGetCurrent(listbox) - NULL;
59 if (es.reason == NEWT_EXIT_HOTKEY)
60 rc = -1;
61 newtPopWindow();
62out_destroy_form:
63 newtFormDestroy(form);
64 return rc;
65}
66
67int ui__help_window(const char *text)
68{
69 struct newtExitStruct es;
70 newtComponent tb, form = newt_form__new();
71 int rc = -1;
72 int max_len = 0, nr_lines = 0;
73 const char *t;
74
75 if (form == NULL)
76 return -1;
77
78 t = text;
79 while (1) {
80 const char *sep = strchr(t, '\n');
81 int len;
82
83 if (sep == NULL)
84 sep = strchr(t, '\0');
85 len = sep - t;
86 if (max_len < len)
87 max_len = len;
88 ++nr_lines;
89 if (*sep == '\0')
90 break;
91 t = sep + 1;
92 }
93
94 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
95 if (tb == NULL)
96 goto out_destroy_form;
97
98 newtTextboxSetText(tb, text);
99 newtFormAddComponent(form, tb);
100 newtCenteredWindow(max_len, nr_lines, NULL);
101 newtFormRun(form, &es);
102 newtPopWindow();
103 rc = 0;
104out_destroy_form:
105 newtFormDestroy(form);
106 return rc;
107}
108
109bool ui__dialog_yesno(const char *msg)
110{
111 /* newtWinChoice should really be accepting const char pointers... */
112 char yes[] = "Yes", no[] = "No";
113 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
114}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644
index 000000000000..afcbc1d99531
--- /dev/null
+++ b/tools/perf/util/ui/util.h
@@ -0,0 +1,10 @@
1#ifndef _PERF_UI_UTIL_H_
2#define _PERF_UI_UTIL_H_ 1
3
4#include <stdbool.h>
5
6int ui__popup_menu(int argc, char * const argv[]);
7int ui__help_window(const char *text);
8bool ui__dialog_yesno(const char *msg);
9
10#endif /* _PERF_UI_UTIL_H_ */