diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /tools/perf/util/ui | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'tools/perf/util/ui')
-rw-r--r-- | tools/perf/util/ui/browser.c | 137 | ||||
-rw-r--r-- | tools/perf/util/ui/browser.h | 12 | ||||
-rw-r--r-- | tools/perf/util/ui/browsers/annotate.c | 195 | ||||
-rw-r--r-- | tools/perf/util/ui/browsers/hists.c | 502 | ||||
-rw-r--r-- | tools/perf/util/ui/browsers/map.c | 39 | ||||
-rw-r--r-- | tools/perf/util/ui/browsers/top.c | 213 | ||||
-rw-r--r-- | tools/perf/util/ui/helpline.c | 5 | ||||
-rw-r--r-- | tools/perf/util/ui/libslang.h | 6 | ||||
-rw-r--r-- | tools/perf/util/ui/setup.c | 8 | ||||
-rw-r--r-- | tools/perf/util/ui/ui.h | 8 | ||||
-rw-r--r-- | tools/perf/util/ui/util.c | 26 |
11 files changed, 837 insertions, 314 deletions
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 66f2d583d8c4..611219f80680 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c | |||
@@ -1,16 +1,6 @@ | |||
1 | #define _GNU_SOURCE | 1 | #include "libslang.h" |
2 | #include <stdio.h> | 2 | #include "ui.h" |
3 | #undef _GNU_SOURCE | 3 | #include <linux/compiler.h> |
4 | /* | ||
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 <linux/list.h> | 4 | #include <linux/list.h> |
15 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
16 | #include <stdlib.h> | 6 | #include <stdlib.h> |
@@ -19,17 +9,9 @@ | |||
19 | #include "helpline.h" | 9 | #include "helpline.h" |
20 | #include "../color.h" | 10 | #include "../color.h" |
21 | #include "../util.h" | 11 | #include "../util.h" |
12 | #include <stdio.h> | ||
22 | 13 | ||
23 | #if SLANG_VERSION < 20104 | 14 | static int ui_browser__percent_color(double percent, bool current) |
24 | #define sltt_set_color(obj, name, fg, bg) \ | ||
25 | SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) | ||
26 | #else | ||
27 | #define sltt_set_color SLtt_set_color | ||
28 | #endif | ||
29 | |||
30 | newtComponent newt_form__new(void); | ||
31 | |||
32 | int ui_browser__percent_color(double percent, bool current) | ||
33 | { | 15 | { |
34 | if (current) | 16 | if (current) |
35 | return HE_COLORSET_SELECTED; | 17 | return HE_COLORSET_SELECTED; |
@@ -40,6 +22,23 @@ int ui_browser__percent_color(double percent, bool current) | |||
40 | return HE_COLORSET_NORMAL; | 22 | return HE_COLORSET_NORMAL; |
41 | } | 23 | } |
42 | 24 | ||
25 | void ui_browser__set_color(struct ui_browser *self __used, int color) | ||
26 | { | ||
27 | SLsmg_set_color(color); | ||
28 | } | ||
29 | |||
30 | void ui_browser__set_percent_color(struct ui_browser *self, | ||
31 | double percent, bool current) | ||
32 | { | ||
33 | int color = ui_browser__percent_color(percent, current); | ||
34 | ui_browser__set_color(self, color); | ||
35 | } | ||
36 | |||
37 | void ui_browser__gotorc(struct ui_browser *self, int y, int x) | ||
38 | { | ||
39 | SLsmg_gotorc(self->y + y, self->x + x); | ||
40 | } | ||
41 | |||
43 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) | 42 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) |
44 | { | 43 | { |
45 | struct list_head *head = self->entries; | 44 | struct list_head *head = self->entries; |
@@ -111,7 +110,7 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) | |||
111 | nd = self->top; | 110 | nd = self->top; |
112 | 111 | ||
113 | while (nd != NULL) { | 112 | while (nd != NULL) { |
114 | SLsmg_gotorc(self->y + row, self->x); | 113 | ui_browser__gotorc(self, row, 0); |
115 | self->write(self, nd, row); | 114 | self->write(self, nd, row); |
116 | if (++row == self->height) | 115 | if (++row == self->height) |
117 | break; | 116 | break; |
@@ -131,13 +130,10 @@ void ui_browser__refresh_dimensions(struct ui_browser *self) | |||
131 | int cols, rows; | 130 | int cols, rows; |
132 | newtGetScreenSize(&cols, &rows); | 131 | newtGetScreenSize(&cols, &rows); |
133 | 132 | ||
134 | if (self->width > cols - 4) | 133 | self->width = cols - 1; |
135 | self->width = cols - 4; | 134 | self->height = rows - 2; |
136 | self->height = rows - 5; | 135 | self->y = 1; |
137 | if (self->height > self->nr_entries) | 136 | self->x = 0; |
138 | self->height = self->nr_entries; | ||
139 | self->y = (rows - self->height) / 2; | ||
140 | self->x = (cols - self->width) / 2; | ||
141 | } | 137 | } |
142 | 138 | ||
143 | void ui_browser__reset_index(struct ui_browser *self) | 139 | void ui_browser__reset_index(struct ui_browser *self) |
@@ -146,78 +142,109 @@ void ui_browser__reset_index(struct ui_browser *self) | |||
146 | self->seek(self, 0, SEEK_SET); | 142 | self->seek(self, 0, SEEK_SET); |
147 | } | 143 | } |
148 | 144 | ||
145 | void ui_browser__add_exit_key(struct ui_browser *self, int key) | ||
146 | { | ||
147 | newtFormAddHotKey(self->form, key); | ||
148 | } | ||
149 | |||
150 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]) | ||
151 | { | ||
152 | int i = 0; | ||
153 | |||
154 | while (keys[i] && i < 64) { | ||
155 | ui_browser__add_exit_key(self, keys[i]); | ||
156 | ++i; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) | ||
161 | { | ||
162 | SLsmg_gotorc(0, 0); | ||
163 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | ||
164 | slsmg_write_nstring(title, browser->width); | ||
165 | } | ||
166 | |||
167 | void ui_browser__show_title(struct ui_browser *browser, const char *title) | ||
168 | { | ||
169 | pthread_mutex_lock(&ui__lock); | ||
170 | __ui_browser__show_title(browser, title); | ||
171 | pthread_mutex_unlock(&ui__lock); | ||
172 | } | ||
173 | |||
149 | int ui_browser__show(struct ui_browser *self, const char *title, | 174 | int ui_browser__show(struct ui_browser *self, const char *title, |
150 | const char *helpline, ...) | 175 | const char *helpline, ...) |
151 | { | 176 | { |
152 | va_list ap; | 177 | va_list ap; |
178 | int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP, | ||
179 | NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ', | ||
180 | NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 }; | ||
153 | 181 | ||
154 | if (self->form != NULL) { | 182 | if (self->form != NULL) |
155 | newtFormDestroy(self->form); | 183 | newtFormDestroy(self->form); |
156 | newtPopWindow(); | 184 | |
157 | } | ||
158 | ui_browser__refresh_dimensions(self); | 185 | ui_browser__refresh_dimensions(self); |
159 | newtCenteredWindow(self->width, self->height, title); | 186 | self->form = newtForm(NULL, NULL, 0); |
160 | self->form = newt_form__new(); | ||
161 | if (self->form == NULL) | 187 | if (self->form == NULL) |
162 | return -1; | 188 | return -1; |
163 | 189 | ||
164 | self->sb = newtVerticalScrollbar(self->width, 0, self->height, | 190 | self->sb = newtVerticalScrollbar(self->width, 1, self->height, |
165 | HE_COLORSET_NORMAL, | 191 | HE_COLORSET_NORMAL, |
166 | HE_COLORSET_SELECTED); | 192 | HE_COLORSET_SELECTED); |
167 | if (self->sb == NULL) | 193 | if (self->sb == NULL) |
168 | return -1; | 194 | return -1; |
169 | 195 | ||
170 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | 196 | pthread_mutex_lock(&ui__lock); |
171 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | 197 | __ui_browser__show_title(self, title); |
172 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | 198 | |
173 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | 199 | ui_browser__add_exit_keys(self, keys); |
174 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | ||
175 | newtFormAddHotKey(self->form, NEWT_KEY_END); | ||
176 | newtFormAddHotKey(self->form, ' '); | ||
177 | newtFormAddComponent(self->form, self->sb); | 200 | newtFormAddComponent(self->form, self->sb); |
178 | 201 | ||
179 | va_start(ap, helpline); | 202 | va_start(ap, helpline); |
180 | ui_helpline__vpush(helpline, ap); | 203 | ui_helpline__vpush(helpline, ap); |
181 | va_end(ap); | 204 | va_end(ap); |
205 | pthread_mutex_unlock(&ui__lock); | ||
182 | return 0; | 206 | return 0; |
183 | } | 207 | } |
184 | 208 | ||
185 | void ui_browser__hide(struct ui_browser *self) | 209 | void ui_browser__hide(struct ui_browser *self) |
186 | { | 210 | { |
211 | pthread_mutex_lock(&ui__lock); | ||
187 | newtFormDestroy(self->form); | 212 | newtFormDestroy(self->form); |
188 | newtPopWindow(); | ||
189 | self->form = NULL; | 213 | self->form = NULL; |
190 | ui_helpline__pop(); | 214 | ui_helpline__pop(); |
215 | pthread_mutex_unlock(&ui__lock); | ||
191 | } | 216 | } |
192 | 217 | ||
193 | int ui_browser__refresh(struct ui_browser *self) | 218 | int ui_browser__refresh(struct ui_browser *self) |
194 | { | 219 | { |
195 | int row; | 220 | int row; |
196 | 221 | ||
222 | pthread_mutex_lock(&ui__lock); | ||
197 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | 223 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); |
198 | row = self->refresh(self); | 224 | row = self->refresh(self); |
199 | SLsmg_set_color(HE_COLORSET_NORMAL); | 225 | ui_browser__set_color(self, HE_COLORSET_NORMAL); |
200 | SLsmg_fill_region(self->y + row, self->x, | 226 | SLsmg_fill_region(self->y + row, self->x, |
201 | self->height - row, self->width, ' '); | 227 | self->height - row, self->width, ' '); |
228 | pthread_mutex_unlock(&ui__lock); | ||
202 | 229 | ||
203 | return 0; | 230 | return 0; |
204 | } | 231 | } |
205 | 232 | ||
206 | int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) | 233 | int ui_browser__run(struct ui_browser *self) |
207 | { | 234 | { |
235 | struct newtExitStruct es; | ||
236 | |||
208 | if (ui_browser__refresh(self) < 0) | 237 | if (ui_browser__refresh(self) < 0) |
209 | return -1; | 238 | return -1; |
210 | 239 | ||
211 | while (1) { | 240 | while (1) { |
212 | off_t offset; | 241 | off_t offset; |
213 | 242 | ||
214 | newtFormRun(self->form, es); | 243 | newtFormRun(self->form, &es); |
215 | 244 | ||
216 | if (es->reason != NEWT_EXIT_HOTKEY) | 245 | if (es.reason != NEWT_EXIT_HOTKEY) |
217 | break; | 246 | break; |
218 | if (is_exit_key(es->u.key)) | 247 | switch (es.u.key) { |
219 | return es->u.key; | ||
220 | switch (es->u.key) { | ||
221 | case NEWT_KEY_DOWN: | 248 | case NEWT_KEY_DOWN: |
222 | if (self->index == self->nr_entries - 1) | 249 | if (self->index == self->nr_entries - 1) |
223 | break; | 250 | break; |
@@ -274,12 +301,12 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) | |||
274 | self->seek(self, -offset, SEEK_END); | 301 | self->seek(self, -offset, SEEK_END); |
275 | break; | 302 | break; |
276 | default: | 303 | default: |
277 | return es->u.key; | 304 | return es.u.key; |
278 | } | 305 | } |
279 | if (ui_browser__refresh(self) < 0) | 306 | if (ui_browser__refresh(self) < 0) |
280 | return -1; | 307 | return -1; |
281 | } | 308 | } |
282 | return 0; | 309 | return -1; |
283 | } | 310 | } |
284 | 311 | ||
285 | unsigned int ui_browser__list_head_refresh(struct ui_browser *self) | 312 | unsigned int ui_browser__list_head_refresh(struct ui_browser *self) |
@@ -294,7 +321,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self) | |||
294 | pos = self->top; | 321 | pos = self->top; |
295 | 322 | ||
296 | list_for_each_from(pos, head) { | 323 | list_for_each_from(pos, head) { |
297 | SLsmg_gotorc(self->y + row, self->x); | 324 | ui_browser__gotorc(self, row, 0); |
298 | self->write(self, pos, row); | 325 | self->write(self, pos, row); |
299 | if (++row == self->height) | 326 | if (++row == self->height) |
300 | break; | 327 | break; |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 0b9f829214f7..fc63dda10910 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
@@ -24,17 +24,23 @@ struct ui_browser { | |||
24 | u32 nr_entries; | 24 | u32 nr_entries; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | 27 | void ui_browser__set_color(struct ui_browser *self, int color); | |
28 | int ui_browser__percent_color(double percent, bool current); | 28 | void ui_browser__set_percent_color(struct ui_browser *self, |
29 | double percent, bool current); | ||
29 | bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); | 30 | bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); |
30 | void ui_browser__refresh_dimensions(struct ui_browser *self); | 31 | void ui_browser__refresh_dimensions(struct ui_browser *self); |
31 | void ui_browser__reset_index(struct ui_browser *self); | 32 | void ui_browser__reset_index(struct ui_browser *self); |
32 | 33 | ||
34 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); | ||
35 | void ui_browser__add_exit_key(struct ui_browser *self, int key); | ||
36 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); | ||
37 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); | ||
38 | void ui_browser__show_title(struct ui_browser *browser, const char *title); | ||
33 | int ui_browser__show(struct ui_browser *self, const char *title, | 39 | int ui_browser__show(struct ui_browser *self, const char *title, |
34 | const char *helpline, ...); | 40 | const char *helpline, ...); |
35 | void ui_browser__hide(struct ui_browser *self); | 41 | void ui_browser__hide(struct ui_browser *self); |
36 | int ui_browser__refresh(struct ui_browser *self); | 42 | int ui_browser__refresh(struct ui_browser *self); |
37 | int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es); | 43 | int ui_browser__run(struct ui_browser *self); |
38 | 44 | ||
39 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); | 45 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); |
40 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); | 46 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index a90273e63f4f..0229723aceb3 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -1,9 +1,11 @@ | |||
1 | #include "../browser.h" | 1 | #include "../browser.h" |
2 | #include "../helpline.h" | 2 | #include "../helpline.h" |
3 | #include "../libslang.h" | 3 | #include "../libslang.h" |
4 | #include "../../annotate.h" | ||
4 | #include "../../hist.h" | 5 | #include "../../hist.h" |
5 | #include "../../sort.h" | 6 | #include "../../sort.h" |
6 | #include "../../symbol.h" | 7 | #include "../../symbol.h" |
8 | #include <pthread.h> | ||
7 | 9 | ||
8 | static void ui__error_window(const char *fmt, ...) | 10 | static void ui__error_window(const char *fmt, ...) |
9 | { | 11 | { |
@@ -40,14 +42,10 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
40 | 42 | ||
41 | if (ol->offset != -1) { | 43 | if (ol->offset != -1) { |
42 | struct objdump_line_rb_node *olrb = objdump_line__rb(ol); | 44 | struct objdump_line_rb_node *olrb = objdump_line__rb(ol); |
43 | int color = ui_browser__percent_color(olrb->percent, current_entry); | 45 | ui_browser__set_percent_color(self, olrb->percent, current_entry); |
44 | SLsmg_set_color(color); | ||
45 | slsmg_printf(" %7.2f ", olrb->percent); | 46 | slsmg_printf(" %7.2f ", olrb->percent); |
46 | if (!current_entry) | ||
47 | SLsmg_set_color(HE_COLORSET_CODE); | ||
48 | } else { | 47 | } else { |
49 | int color = ui_browser__percent_color(0, current_entry); | 48 | ui_browser__set_percent_color(self, 0, current_entry); |
50 | SLsmg_set_color(color); | ||
51 | slsmg_write_nstring(" ", 9); | 49 | slsmg_write_nstring(" ", 9); |
52 | } | 50 | } |
53 | 51 | ||
@@ -57,35 +55,40 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
57 | slsmg_write_nstring(" ", width - 18); | 55 | slsmg_write_nstring(" ", width - 18); |
58 | else | 56 | else |
59 | slsmg_write_nstring(ol->line, width - 18); | 57 | slsmg_write_nstring(ol->line, width - 18); |
58 | |||
59 | if (!current_entry) | ||
60 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
60 | } | 61 | } |
61 | 62 | ||
62 | static double objdump_line__calc_percent(struct objdump_line *self, | 63 | static double objdump_line__calc_percent(struct objdump_line *self, |
63 | struct list_head *head, | 64 | struct symbol *sym, int evidx) |
64 | struct symbol *sym) | ||
65 | { | 65 | { |
66 | double percent = 0.0; | 66 | double percent = 0.0; |
67 | 67 | ||
68 | if (self->offset != -1) { | 68 | if (self->offset != -1) { |
69 | int len = sym->end - sym->start; | 69 | int len = sym->end - sym->start; |
70 | unsigned int hits = 0; | 70 | unsigned int hits = 0; |
71 | struct sym_priv *priv = symbol__priv(sym); | 71 | struct annotation *notes = symbol__annotation(sym); |
72 | struct sym_ext *sym_ext = priv->ext; | 72 | struct source_line *src_line = notes->src->lines; |
73 | struct sym_hist *h = priv->hist; | 73 | struct sym_hist *h = annotation__histogram(notes, evidx); |
74 | s64 offset = self->offset; | 74 | s64 offset = self->offset; |
75 | struct objdump_line *next = objdump__get_next_ip_line(head, self); | 75 | struct objdump_line *next; |
76 | |||
77 | 76 | ||
77 | next = objdump__get_next_ip_line(¬es->src->source, self); | ||
78 | while (offset < (s64)len && | 78 | while (offset < (s64)len && |
79 | (next == NULL || offset < next->offset)) { | 79 | (next == NULL || offset < next->offset)) { |
80 | if (sym_ext) { | 80 | if (src_line) { |
81 | percent += sym_ext[offset].percent; | 81 | percent += src_line[offset].percent; |
82 | } else | 82 | } else |
83 | hits += h->ip[offset]; | 83 | hits += h->addr[offset]; |
84 | 84 | ||
85 | ++offset; | 85 | ++offset; |
86 | } | 86 | } |
87 | 87 | /* | |
88 | if (sym_ext == NULL && h->sum) | 88 | * If the percentage wasn't already calculated in |
89 | * symbol__get_source_line, do it now: | ||
90 | */ | ||
91 | if (src_line == NULL && h->sum) | ||
89 | percent = 100.0 * hits / h->sum; | 92 | percent = 100.0 * hits / h->sum; |
90 | } | 93 | } |
91 | 94 | ||
@@ -135,105 +138,163 @@ static void annotate_browser__set_top(struct annotate_browser *self, | |||
135 | self->curr_hot = nd; | 138 | self->curr_hot = nd; |
136 | } | 139 | } |
137 | 140 | ||
138 | static int annotate_browser__run(struct annotate_browser *self, | 141 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
139 | struct newtExitStruct *es) | 142 | int evidx) |
143 | { | ||
144 | struct symbol *sym = browser->b.priv; | ||
145 | struct annotation *notes = symbol__annotation(sym); | ||
146 | struct objdump_line *pos; | ||
147 | |||
148 | browser->entries = RB_ROOT; | ||
149 | |||
150 | pthread_mutex_lock(¬es->lock); | ||
151 | |||
152 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
153 | struct objdump_line_rb_node *rbpos = objdump_line__rb(pos); | ||
154 | rbpos->percent = objdump_line__calc_percent(pos, sym, evidx); | ||
155 | if (rbpos->percent < 0.01) { | ||
156 | RB_CLEAR_NODE(&rbpos->rb_node); | ||
157 | continue; | ||
158 | } | ||
159 | objdump__insert_line(&browser->entries, rbpos); | ||
160 | } | ||
161 | pthread_mutex_unlock(¬es->lock); | ||
162 | |||
163 | browser->curr_hot = rb_last(&browser->entries); | ||
164 | } | ||
165 | |||
166 | static int annotate_browser__run(struct annotate_browser *self, int evidx, | ||
167 | int refresh) | ||
140 | { | 168 | { |
141 | struct rb_node *nd; | 169 | struct rb_node *nd = NULL; |
142 | struct hist_entry *he = self->b.priv; | 170 | struct symbol *sym = self->b.priv; |
171 | /* | ||
172 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by | ||
173 | * examining the exit key for this function. | ||
174 | */ | ||
175 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, | ||
176 | NEWT_KEY_RIGHT, 0 }; | ||
177 | int key; | ||
143 | 178 | ||
144 | if (ui_browser__show(&self->b, he->ms.sym->name, | 179 | if (ui_browser__show(&self->b, sym->name, |
145 | "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) | 180 | "<-, -> or ESC: exit, TAB/shift+TAB: " |
181 | "cycle hottest lines, H: Hottest") < 0) | ||
146 | return -1; | 182 | return -1; |
147 | 183 | ||
148 | newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); | 184 | ui_browser__add_exit_keys(&self->b, exit_keys); |
149 | newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); | 185 | annotate_browser__calc_percent(self, evidx); |
186 | |||
187 | if (self->curr_hot) | ||
188 | annotate_browser__set_top(self, self->curr_hot); | ||
150 | 189 | ||
151 | nd = self->curr_hot; | 190 | nd = self->curr_hot; |
152 | if (nd) { | ||
153 | newtFormAddHotKey(self->b.form, NEWT_KEY_TAB); | ||
154 | newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB); | ||
155 | } | ||
156 | 191 | ||
157 | while (1) { | 192 | if (refresh != 0) |
158 | ui_browser__run(&self->b, es); | 193 | newtFormSetTimer(self->b.form, refresh); |
159 | 194 | ||
160 | if (es->reason != NEWT_EXIT_HOTKEY) | 195 | while (1) { |
161 | break; | 196 | key = ui_browser__run(&self->b); |
197 | |||
198 | if (refresh != 0) { | ||
199 | annotate_browser__calc_percent(self, evidx); | ||
200 | /* | ||
201 | * Current line focus got out of the list of most active | ||
202 | * lines, NULL it so that if TAB|UNTAB is pressed, we | ||
203 | * move to curr_hot (current hottest line). | ||
204 | */ | ||
205 | if (nd != NULL && RB_EMPTY_NODE(nd)) | ||
206 | nd = NULL; | ||
207 | } | ||
162 | 208 | ||
163 | switch (es->u.key) { | 209 | switch (key) { |
210 | case -1: | ||
211 | /* | ||
212 | * FIXME we need to check if it was | ||
213 | * es.reason == NEWT_EXIT_TIMER | ||
214 | */ | ||
215 | if (refresh != 0) | ||
216 | symbol__annotate_decay_histogram(sym, evidx); | ||
217 | continue; | ||
164 | case NEWT_KEY_TAB: | 218 | case NEWT_KEY_TAB: |
165 | nd = rb_prev(nd); | 219 | if (nd != NULL) { |
166 | if (nd == NULL) | 220 | nd = rb_prev(nd); |
167 | nd = rb_last(&self->entries); | 221 | if (nd == NULL) |
168 | annotate_browser__set_top(self, nd); | 222 | nd = rb_last(&self->entries); |
223 | } else | ||
224 | nd = self->curr_hot; | ||
169 | break; | 225 | break; |
170 | case NEWT_KEY_UNTAB: | 226 | case NEWT_KEY_UNTAB: |
171 | nd = rb_next(nd); | 227 | if (nd != NULL) |
172 | if (nd == NULL) | 228 | nd = rb_next(nd); |
173 | nd = rb_first(&self->entries); | 229 | if (nd == NULL) |
174 | annotate_browser__set_top(self, nd); | 230 | nd = rb_first(&self->entries); |
231 | else | ||
232 | nd = self->curr_hot; | ||
233 | break; | ||
234 | case 'H': | ||
235 | nd = self->curr_hot; | ||
175 | break; | 236 | break; |
176 | default: | 237 | default: |
177 | goto out; | 238 | goto out; |
178 | } | 239 | } |
240 | |||
241 | if (nd != NULL) | ||
242 | annotate_browser__set_top(self, nd); | ||
179 | } | 243 | } |
180 | out: | 244 | out: |
181 | ui_browser__hide(&self->b); | 245 | ui_browser__hide(&self->b); |
182 | return es->u.key; | 246 | return key; |
183 | } | 247 | } |
184 | 248 | ||
185 | int hist_entry__tui_annotate(struct hist_entry *self) | 249 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx) |
250 | { | ||
251 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); | ||
252 | } | ||
253 | |||
254 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | ||
255 | int refresh) | ||
186 | { | 256 | { |
187 | struct newtExitStruct es; | ||
188 | struct objdump_line *pos, *n; | 257 | struct objdump_line *pos, *n; |
189 | struct objdump_line_rb_node *rbpos; | 258 | struct annotation *notes; |
190 | LIST_HEAD(head); | ||
191 | struct annotate_browser browser = { | 259 | struct annotate_browser browser = { |
192 | .b = { | 260 | .b = { |
193 | .entries = &head, | ||
194 | .refresh = ui_browser__list_head_refresh, | 261 | .refresh = ui_browser__list_head_refresh, |
195 | .seek = ui_browser__list_head_seek, | 262 | .seek = ui_browser__list_head_seek, |
196 | .write = annotate_browser__write, | 263 | .write = annotate_browser__write, |
197 | .priv = self, | 264 | .priv = sym, |
198 | }, | 265 | }, |
199 | }; | 266 | }; |
200 | int ret; | 267 | int ret; |
201 | 268 | ||
202 | if (self->ms.sym == NULL) | 269 | if (sym == NULL) |
203 | return -1; | 270 | return -1; |
204 | 271 | ||
205 | if (self->ms.map->dso->annotate_warned) | 272 | if (map->dso->annotate_warned) |
206 | return -1; | 273 | return -1; |
207 | 274 | ||
208 | if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) { | 275 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { |
209 | ui__error_window(ui_helpline__last_msg); | 276 | ui__error_window(ui_helpline__last_msg); |
210 | return -1; | 277 | return -1; |
211 | } | 278 | } |
212 | 279 | ||
213 | ui_helpline__push("Press <- or ESC to exit"); | 280 | ui_helpline__push("Press <- or ESC to exit"); |
214 | 281 | ||
215 | list_for_each_entry(pos, &head, node) { | 282 | notes = symbol__annotation(sym); |
283 | |||
284 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
285 | struct objdump_line_rb_node *rbpos; | ||
216 | size_t line_len = strlen(pos->line); | 286 | size_t line_len = strlen(pos->line); |
287 | |||
217 | if (browser.b.width < line_len) | 288 | if (browser.b.width < line_len) |
218 | browser.b.width = line_len; | 289 | browser.b.width = line_len; |
219 | rbpos = objdump_line__rb(pos); | 290 | rbpos = objdump_line__rb(pos); |
220 | rbpos->idx = browser.b.nr_entries++; | 291 | rbpos->idx = browser.b.nr_entries++; |
221 | rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym); | ||
222 | if (rbpos->percent < 0.01) | ||
223 | continue; | ||
224 | objdump__insert_line(&browser.entries, rbpos); | ||
225 | } | 292 | } |
226 | 293 | ||
227 | /* | 294 | browser.b.entries = ¬es->src->source, |
228 | * Position the browser at the hottest line. | ||
229 | */ | ||
230 | browser.curr_hot = rb_last(&browser.entries); | ||
231 | if (browser.curr_hot) | ||
232 | annotate_browser__set_top(&browser, browser.curr_hot); | ||
233 | |||
234 | browser.b.width += 18; /* Percentage */ | 295 | browser.b.width += 18; /* Percentage */ |
235 | ret = annotate_browser__run(&browser, &es); | 296 | ret = annotate_browser__run(&browser, evidx, refresh); |
236 | list_for_each_entry_safe(pos, n, &head, node) { | 297 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
237 | list_del(&pos->node); | 298 | list_del(&pos->node); |
238 | objdump_line__free(pos); | 299 | objdump_line__free(pos); |
239 | } | 300 | } |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 6866aa4c41e0..5d767c622dfc 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <newt.h> | 7 | #include <newt.h> |
8 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
9 | 9 | ||
10 | #include "../../evsel.h" | ||
11 | #include "../../evlist.h" | ||
10 | #include "../../hist.h" | 12 | #include "../../hist.h" |
11 | #include "../../pstack.h" | 13 | #include "../../pstack.h" |
12 | #include "../../sort.h" | 14 | #include "../../sort.h" |
@@ -58,6 +60,11 @@ static char callchain_list__folded(const struct callchain_list *self) | |||
58 | return map_symbol__folded(&self->ms); | 60 | return map_symbol__folded(&self->ms); |
59 | } | 61 | } |
60 | 62 | ||
63 | static void map_symbol__set_folding(struct map_symbol *self, bool unfold) | ||
64 | { | ||
65 | self->unfolded = unfold ? self->has_children : false; | ||
66 | } | ||
67 | |||
61 | static int callchain_node__count_rows_rb_tree(struct callchain_node *self) | 68 | static int callchain_node__count_rows_rb_tree(struct callchain_node *self) |
62 | { | 69 | { |
63 | int n = 0; | 70 | int n = 0; |
@@ -129,16 +136,16 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *se | |||
129 | for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { | 136 | for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { |
130 | struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); | 137 | struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); |
131 | struct callchain_list *chain; | 138 | struct callchain_list *chain; |
132 | int first = true; | 139 | bool first = true; |
133 | 140 | ||
134 | list_for_each_entry(chain, &child->val, list) { | 141 | list_for_each_entry(chain, &child->val, list) { |
135 | if (first) { | 142 | if (first) { |
136 | first = false; | 143 | first = false; |
137 | chain->ms.has_children = chain->list.next != &child->val || | 144 | chain->ms.has_children = chain->list.next != &child->val || |
138 | rb_first(&child->rb_root) != NULL; | 145 | !RB_EMPTY_ROOT(&child->rb_root); |
139 | } else | 146 | } else |
140 | chain->ms.has_children = chain->list.next == &child->val && | 147 | chain->ms.has_children = chain->list.next == &child->val && |
141 | rb_first(&child->rb_root) != NULL; | 148 | !RB_EMPTY_ROOT(&child->rb_root); |
142 | } | 149 | } |
143 | 150 | ||
144 | callchain_node__init_have_children_rb_tree(child); | 151 | callchain_node__init_have_children_rb_tree(child); |
@@ -150,7 +157,7 @@ static void callchain_node__init_have_children(struct callchain_node *self) | |||
150 | struct callchain_list *chain; | 157 | struct callchain_list *chain; |
151 | 158 | ||
152 | list_for_each_entry(chain, &self->val, list) | 159 | list_for_each_entry(chain, &self->val, list) |
153 | chain->ms.has_children = rb_first(&self->rb_root) != NULL; | 160 | chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root); |
154 | 161 | ||
155 | callchain_node__init_have_children_rb_tree(self); | 162 | callchain_node__init_have_children_rb_tree(self); |
156 | } | 163 | } |
@@ -168,6 +175,7 @@ static void callchain__init_have_children(struct rb_root *self) | |||
168 | static void hist_entry__init_have_children(struct hist_entry *self) | 175 | static void hist_entry__init_have_children(struct hist_entry *self) |
169 | { | 176 | { |
170 | if (!self->init_have_children) { | 177 | if (!self->init_have_children) { |
178 | self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain); | ||
171 | callchain__init_have_children(&self->sorted_chain); | 179 | callchain__init_have_children(&self->sorted_chain); |
172 | self->init_have_children = true; | 180 | self->init_have_children = true; |
173 | } | 181 | } |
@@ -195,43 +203,115 @@ static bool hist_browser__toggle_fold(struct hist_browser *self) | |||
195 | return false; | 203 | return false; |
196 | } | 204 | } |
197 | 205 | ||
198 | static int hist_browser__run(struct hist_browser *self, const char *title, | 206 | static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold) |
199 | struct newtExitStruct *es) | ||
200 | { | 207 | { |
201 | char str[256], unit; | 208 | int n = 0; |
202 | unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 209 | struct rb_node *nd; |
210 | |||
211 | for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { | ||
212 | struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); | ||
213 | struct callchain_list *chain; | ||
214 | bool has_children = false; | ||
215 | |||
216 | list_for_each_entry(chain, &child->val, list) { | ||
217 | ++n; | ||
218 | map_symbol__set_folding(&chain->ms, unfold); | ||
219 | has_children = chain->ms.has_children; | ||
220 | } | ||
221 | |||
222 | if (has_children) | ||
223 | n += callchain_node__set_folding_rb_tree(child, unfold); | ||
224 | } | ||
225 | |||
226 | return n; | ||
227 | } | ||
228 | |||
229 | static int callchain_node__set_folding(struct callchain_node *node, bool unfold) | ||
230 | { | ||
231 | struct callchain_list *chain; | ||
232 | bool has_children = false; | ||
233 | int n = 0; | ||
234 | |||
235 | list_for_each_entry(chain, &node->val, list) { | ||
236 | ++n; | ||
237 | map_symbol__set_folding(&chain->ms, unfold); | ||
238 | has_children = chain->ms.has_children; | ||
239 | } | ||
240 | |||
241 | if (has_children) | ||
242 | n += callchain_node__set_folding_rb_tree(node, unfold); | ||
243 | |||
244 | return n; | ||
245 | } | ||
246 | |||
247 | static int callchain__set_folding(struct rb_root *chain, bool unfold) | ||
248 | { | ||
249 | struct rb_node *nd; | ||
250 | int n = 0; | ||
251 | |||
252 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | ||
253 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
254 | n += callchain_node__set_folding(node, unfold); | ||
255 | } | ||
256 | |||
257 | return n; | ||
258 | } | ||
259 | |||
260 | static void hist_entry__set_folding(struct hist_entry *self, bool unfold) | ||
261 | { | ||
262 | hist_entry__init_have_children(self); | ||
263 | map_symbol__set_folding(&self->ms, unfold); | ||
264 | |||
265 | if (self->ms.has_children) { | ||
266 | int n = callchain__set_folding(&self->sorted_chain, unfold); | ||
267 | self->nr_rows = unfold ? n : 0; | ||
268 | } else | ||
269 | self->nr_rows = 0; | ||
270 | } | ||
271 | |||
272 | static void hists__set_folding(struct hists *self, bool unfold) | ||
273 | { | ||
274 | struct rb_node *nd; | ||
275 | |||
276 | self->nr_entries = 0; | ||
277 | |||
278 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
279 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); | ||
280 | hist_entry__set_folding(he, unfold); | ||
281 | self->nr_entries += 1 + he->nr_rows; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | static void hist_browser__set_folding(struct hist_browser *self, bool unfold) | ||
286 | { | ||
287 | hists__set_folding(self->hists, unfold); | ||
288 | self->b.nr_entries = self->hists->nr_entries; | ||
289 | /* Go to the start, we may be way after valid entries after a collapse */ | ||
290 | ui_browser__reset_index(&self->b); | ||
291 | } | ||
292 | |||
293 | static int hist_browser__run(struct hist_browser *self, const char *title) | ||
294 | { | ||
295 | int key; | ||
296 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', | ||
297 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, | ||
298 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; | ||
203 | 299 | ||
204 | self->b.entries = &self->hists->entries; | 300 | self->b.entries = &self->hists->entries; |
205 | self->b.nr_entries = self->hists->nr_entries; | 301 | self->b.nr_entries = self->hists->nr_entries; |
206 | 302 | ||
207 | hist_browser__refresh_dimensions(self); | 303 | hist_browser__refresh_dimensions(self); |
208 | 304 | ||
209 | nr_events = convert_unit(nr_events, &unit); | ||
210 | snprintf(str, sizeof(str), "Events: %lu%c ", | ||
211 | nr_events, unit); | ||
212 | newtDrawRootText(0, 0, str); | ||
213 | |||
214 | if (ui_browser__show(&self->b, title, | 305 | if (ui_browser__show(&self->b, title, |
215 | "Press '?' for help on key bindings") < 0) | 306 | "Press '?' for help on key bindings") < 0) |
216 | return -1; | 307 | return -1; |
217 | 308 | ||
218 | newtFormAddHotKey(self->b.form, 'a'); | 309 | ui_browser__add_exit_keys(&self->b, exit_keys); |
219 | newtFormAddHotKey(self->b.form, '?'); | ||
220 | newtFormAddHotKey(self->b.form, 'h'); | ||
221 | newtFormAddHotKey(self->b.form, 'd'); | ||
222 | newtFormAddHotKey(self->b.form, 'D'); | ||
223 | newtFormAddHotKey(self->b.form, 't'); | ||
224 | |||
225 | newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); | ||
226 | newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); | ||
227 | newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); | ||
228 | 310 | ||
229 | while (1) { | 311 | while (1) { |
230 | ui_browser__run(&self->b, es); | 312 | key = ui_browser__run(&self->b); |
231 | 313 | ||
232 | if (es->reason != NEWT_EXIT_HOTKEY) | 314 | switch (key) { |
233 | break; | ||
234 | switch (es->u.key) { | ||
235 | case 'D': { /* Debug */ | 315 | case 'D': { /* Debug */ |
236 | static int seq; | 316 | static int seq; |
237 | struct hist_entry *h = rb_entry(self->b.top, | 317 | struct hist_entry *h = rb_entry(self->b.top, |
@@ -245,18 +325,26 @@ static int hist_browser__run(struct hist_browser *self, const char *title, | |||
245 | self->b.top_idx, | 325 | self->b.top_idx, |
246 | h->row_offset, h->nr_rows); | 326 | h->row_offset, h->nr_rows); |
247 | } | 327 | } |
248 | continue; | 328 | break; |
329 | case 'C': | ||
330 | /* Collapse the whole world. */ | ||
331 | hist_browser__set_folding(self, false); | ||
332 | break; | ||
333 | case 'E': | ||
334 | /* Expand the whole world. */ | ||
335 | hist_browser__set_folding(self, true); | ||
336 | break; | ||
249 | case NEWT_KEY_ENTER: | 337 | case NEWT_KEY_ENTER: |
250 | if (hist_browser__toggle_fold(self)) | 338 | if (hist_browser__toggle_fold(self)) |
251 | break; | 339 | break; |
252 | /* fall thru */ | 340 | /* fall thru */ |
253 | default: | 341 | default: |
254 | return 0; | 342 | goto out; |
255 | } | 343 | } |
256 | } | 344 | } |
257 | 345 | out: | |
258 | ui_browser__hide(&self->b); | 346 | ui_browser__hide(&self->b); |
259 | return 0; | 347 | return key; |
260 | } | 348 | } |
261 | 349 | ||
262 | static char *callchain_list__sym_name(struct callchain_list *self, | 350 | static char *callchain_list__sym_name(struct callchain_list *self, |
@@ -265,7 +353,7 @@ static char *callchain_list__sym_name(struct callchain_list *self, | |||
265 | if (self->ms.sym) | 353 | if (self->ms.sym) |
266 | return self->ms.sym->name; | 354 | return self->ms.sym->name; |
267 | 355 | ||
268 | snprintf(bf, bfsize, "%#Lx", self->ip); | 356 | snprintf(bf, bfsize, "%#" PRIx64, self->ip); |
269 | return bf; | 357 | return bf; |
270 | } | 358 | } |
271 | 359 | ||
@@ -292,7 +380,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | |||
292 | while (node) { | 380 | while (node) { |
293 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 381 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
294 | struct rb_node *next = rb_next(node); | 382 | struct rb_node *next = rb_next(node); |
295 | u64 cumul = cumul_hits(child); | 383 | u64 cumul = callchain_cumul_hits(child); |
296 | struct callchain_list *chain; | 384 | struct callchain_list *chain; |
297 | char folded_sign = ' '; | 385 | char folded_sign = ' '; |
298 | int first = true; | 386 | int first = true; |
@@ -306,15 +394,10 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | |||
306 | int color; | 394 | int color; |
307 | bool was_first = first; | 395 | bool was_first = first; |
308 | 396 | ||
309 | if (first) { | 397 | if (first) |
310 | first = false; | 398 | first = false; |
311 | chain->ms.has_children = chain->list.next != &child->val || | 399 | else |
312 | rb_first(&child->rb_root) != NULL; | ||
313 | } else { | ||
314 | extra_offset = LEVEL_OFFSET_STEP; | 400 | extra_offset = LEVEL_OFFSET_STEP; |
315 | chain->ms.has_children = chain->list.next == &child->val && | ||
316 | rb_first(&child->rb_root) != NULL; | ||
317 | } | ||
318 | 401 | ||
319 | folded_sign = callchain_list__folded(chain); | 402 | folded_sign = callchain_list__folded(chain); |
320 | if (*row_offset != 0) { | 403 | if (*row_offset != 0) { |
@@ -341,8 +424,8 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | |||
341 | *is_current_entry = true; | 424 | *is_current_entry = true; |
342 | } | 425 | } |
343 | 426 | ||
344 | SLsmg_set_color(color); | 427 | ui_browser__set_color(&self->b, color); |
345 | SLsmg_gotorc(self->b.y + row, self->b.x); | 428 | ui_browser__gotorc(&self->b, row, 0); |
346 | slsmg_write_nstring(" ", offset + extra_offset); | 429 | slsmg_write_nstring(" ", offset + extra_offset); |
347 | slsmg_printf("%c ", folded_sign); | 430 | slsmg_printf("%c ", folded_sign); |
348 | slsmg_write_nstring(str, width); | 431 | slsmg_write_nstring(str, width); |
@@ -384,12 +467,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self, | |||
384 | list_for_each_entry(chain, &node->val, list) { | 467 | list_for_each_entry(chain, &node->val, list) { |
385 | char ipstr[BITS_PER_LONG / 4 + 1], *s; | 468 | char ipstr[BITS_PER_LONG / 4 + 1], *s; |
386 | int color; | 469 | int color; |
387 | /* | 470 | |
388 | * FIXME: This should be moved to somewhere else, | ||
389 | * probably when the callchain is created, so as not to | ||
390 | * traverse it all over again | ||
391 | */ | ||
392 | chain->ms.has_children = rb_first(&node->rb_root) != NULL; | ||
393 | folded_sign = callchain_list__folded(chain); | 471 | folded_sign = callchain_list__folded(chain); |
394 | 472 | ||
395 | if (*row_offset != 0) { | 473 | if (*row_offset != 0) { |
@@ -405,8 +483,8 @@ static int hist_browser__show_callchain_node(struct hist_browser *self, | |||
405 | } | 483 | } |
406 | 484 | ||
407 | s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | 485 | s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); |
408 | SLsmg_gotorc(self->b.y + row, self->b.x); | 486 | ui_browser__gotorc(&self->b, row, 0); |
409 | SLsmg_set_color(color); | 487 | ui_browser__set_color(&self->b, color); |
410 | slsmg_write_nstring(" ", offset); | 488 | slsmg_write_nstring(" ", offset); |
411 | slsmg_printf("%c ", folded_sign); | 489 | slsmg_printf("%c ", folded_sign); |
412 | slsmg_write_nstring(s, width - 2); | 490 | slsmg_write_nstring(s, width - 2); |
@@ -465,7 +543,7 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
465 | } | 543 | } |
466 | 544 | ||
467 | if (symbol_conf.use_callchain) { | 545 | if (symbol_conf.use_callchain) { |
468 | entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); | 546 | hist_entry__init_have_children(entry); |
469 | folded_sign = hist_entry__folded(entry); | 547 | folded_sign = hist_entry__folded(entry); |
470 | } | 548 | } |
471 | 549 | ||
@@ -484,8 +562,8 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
484 | color = HE_COLORSET_NORMAL; | 562 | color = HE_COLORSET_NORMAL; |
485 | } | 563 | } |
486 | 564 | ||
487 | SLsmg_set_color(color); | 565 | ui_browser__set_color(&self->b, color); |
488 | SLsmg_gotorc(self->b.y + row, self->b.x); | 566 | ui_browser__gotorc(&self->b, row, 0); |
489 | if (symbol_conf.use_callchain) { | 567 | if (symbol_conf.use_callchain) { |
490 | slsmg_printf("%c ", folded_sign); | 568 | slsmg_printf("%c ", folded_sign); |
491 | width -= 2; | 569 | width -= 2; |
@@ -563,6 +641,9 @@ static void ui_browser__hists_seek(struct ui_browser *self, | |||
563 | struct rb_node *nd; | 641 | struct rb_node *nd; |
564 | bool first = true; | 642 | bool first = true; |
565 | 643 | ||
644 | if (self->nr_entries == 0) | ||
645 | return; | ||
646 | |||
566 | switch (whence) { | 647 | switch (whence) { |
567 | case SEEK_SET: | 648 | case SEEK_SET: |
568 | nd = hists__filter_entries(rb_first(self->entries)); | 649 | nd = hists__filter_entries(rb_first(self->entries)); |
@@ -687,8 +768,6 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
687 | 768 | ||
688 | static void hist_browser__delete(struct hist_browser *self) | 769 | static void hist_browser__delete(struct hist_browser *self) |
689 | { | 770 | { |
690 | newtFormDestroy(self->b.form); | ||
691 | newtPopWindow(); | ||
692 | free(self); | 771 | free(self); |
693 | } | 772 | } |
694 | 773 | ||
@@ -702,30 +781,37 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) | |||
702 | return self->he_selection->thread; | 781 | return self->he_selection->thread; |
703 | } | 782 | } |
704 | 783 | ||
705 | static int hist_browser__title(char *bf, size_t size, const char *ev_name, | 784 | static int hists__browser_title(struct hists *self, char *bf, size_t size, |
706 | const struct dso *dso, const struct thread *thread) | 785 | const char *ev_name, const struct dso *dso, |
786 | const struct thread *thread) | ||
707 | { | 787 | { |
708 | int printed = 0; | 788 | char unit; |
789 | int printed; | ||
790 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
791 | |||
792 | nr_events = convert_unit(nr_events, &unit); | ||
793 | printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); | ||
709 | 794 | ||
710 | if (thread) | 795 | if (thread) |
711 | printed += snprintf(bf + printed, size - printed, | 796 | printed += snprintf(bf + printed, size - printed, |
712 | "Thread: %s(%d)", | 797 | ", Thread: %s(%d)", |
713 | (thread->comm_set ? thread->comm : ""), | 798 | (thread->comm_set ? thread->comm : ""), |
714 | thread->pid); | 799 | thread->pid); |
715 | if (dso) | 800 | if (dso) |
716 | printed += snprintf(bf + printed, size - printed, | 801 | printed += snprintf(bf + printed, size - printed, |
717 | "%sDSO: %s", thread ? " " : "", | 802 | ", DSO: %s", dso->short_name); |
718 | dso->short_name); | 803 | return printed; |
719 | return printed ?: snprintf(bf, size, "Event: %s", ev_name); | ||
720 | } | 804 | } |
721 | 805 | ||
722 | int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | 806 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, |
807 | const char *helpline, const char *ev_name, | ||
808 | bool left_exits) | ||
723 | { | 809 | { |
810 | struct hists *self = &evsel->hists; | ||
724 | struct hist_browser *browser = hist_browser__new(self); | 811 | struct hist_browser *browser = hist_browser__new(self); |
725 | struct pstack *fstack; | 812 | struct pstack *fstack; |
726 | const struct thread *thread_filter = NULL; | 813 | const struct thread *thread_filter = NULL; |
727 | const struct dso *dso_filter = NULL; | 814 | const struct dso *dso_filter = NULL; |
728 | struct newtExitStruct es; | ||
729 | char msg[160]; | 815 | char msg[160]; |
730 | int key = -1; | 816 | int key = -1; |
731 | 817 | ||
@@ -738,84 +824,88 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
738 | 824 | ||
739 | ui_helpline__push(helpline); | 825 | ui_helpline__push(helpline); |
740 | 826 | ||
741 | hist_browser__title(msg, sizeof(msg), ev_name, | 827 | hists__browser_title(self, msg, sizeof(msg), ev_name, |
742 | dso_filter, thread_filter); | 828 | dso_filter, thread_filter); |
743 | |||
744 | while (1) { | 829 | while (1) { |
745 | const struct thread *thread; | 830 | const struct thread *thread = NULL; |
746 | const struct dso *dso; | 831 | const struct dso *dso = NULL; |
747 | char *options[16]; | 832 | char *options[16]; |
748 | int nr_options = 0, choice = 0, i, | 833 | int nr_options = 0, choice = 0, i, |
749 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 834 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
750 | browse_map = -2; | 835 | browse_map = -2; |
751 | 836 | ||
752 | if (hist_browser__run(browser, msg, &es)) | 837 | key = hist_browser__run(browser, msg); |
753 | break; | ||
754 | 838 | ||
755 | thread = hist_browser__selected_thread(browser); | 839 | if (browser->he_selection != NULL) { |
756 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | 840 | thread = hist_browser__selected_thread(browser); |
841 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | ||
842 | } | ||
757 | 843 | ||
758 | if (es.reason == NEWT_EXIT_HOTKEY) { | 844 | switch (key) { |
759 | key = es.u.key; | 845 | case NEWT_KEY_TAB: |
846 | case NEWT_KEY_UNTAB: | ||
847 | /* | ||
848 | * Exit the browser, let hists__browser_tree | ||
849 | * go to the next or previous | ||
850 | */ | ||
851 | goto out_free_stack; | ||
852 | case 'a': | ||
853 | if (browser->selection == NULL || | ||
854 | browser->selection->sym == NULL || | ||
855 | browser->selection->map->dso->annotate_warned) | ||
856 | continue; | ||
857 | goto do_annotate; | ||
858 | case 'd': | ||
859 | goto zoom_dso; | ||
860 | case 't': | ||
861 | goto zoom_thread; | ||
862 | case NEWT_KEY_F1: | ||
863 | case 'h': | ||
864 | case '?': | ||
865 | ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" | ||
866 | "<- Zoom out\n" | ||
867 | "a Annotate current symbol\n" | ||
868 | "h/?/F1 Show this window\n" | ||
869 | "C Collapse all callchains\n" | ||
870 | "E Expand all callchains\n" | ||
871 | "d Zoom into current DSO\n" | ||
872 | "t Zoom into current Thread\n" | ||
873 | "TAB/UNTAB Switch events\n" | ||
874 | "q/CTRL+C Exit browser"); | ||
875 | continue; | ||
876 | case NEWT_KEY_ENTER: | ||
877 | case NEWT_KEY_RIGHT: | ||
878 | /* menu */ | ||
879 | break; | ||
880 | case NEWT_KEY_LEFT: { | ||
881 | const void *top; | ||
760 | 882 | ||
761 | switch (key) { | 883 | if (pstack__empty(fstack)) { |
762 | case NEWT_KEY_F1: | ||
763 | goto do_help; | ||
764 | case NEWT_KEY_TAB: | ||
765 | case NEWT_KEY_UNTAB: | ||
766 | /* | 884 | /* |
767 | * Exit the browser, let hists__browser_tree | 885 | * Go back to the perf_evsel_menu__run or other user |
768 | * go to the next or previous | ||
769 | */ | 886 | */ |
770 | goto out_free_stack; | 887 | if (left_exits) |
771 | default:; | 888 | goto out_free_stack; |
772 | } | ||
773 | |||
774 | switch (key) { | ||
775 | case 'a': | ||
776 | if (browser->selection->map == NULL || | ||
777 | browser->selection->map->dso->annotate_warned) | ||
778 | continue; | ||
779 | goto do_annotate; | ||
780 | case 'd': | ||
781 | goto zoom_dso; | ||
782 | case 't': | ||
783 | goto zoom_thread; | ||
784 | case 'h': | ||
785 | case '?': | ||
786 | do_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; | 889 | continue; |
795 | default:; | ||
796 | } | 890 | } |
797 | if (is_exit_key(key)) { | 891 | top = pstack__pop(fstack); |
798 | if (key == NEWT_KEY_ESCAPE && | 892 | if (top == &dso_filter) |
799 | !ui__dialog_yesno("Do you really want to exit?")) | 893 | goto zoom_out_dso; |
800 | continue; | 894 | if (top == &thread_filter) |
801 | break; | 895 | goto zoom_out_thread; |
802 | } | 896 | continue; |
803 | 897 | } | |
804 | if (es.u.key == NEWT_KEY_LEFT) { | 898 | case NEWT_KEY_ESCAPE: |
805 | const void *top; | 899 | if (!left_exits && |
806 | 900 | !ui__dialog_yesno("Do you really want to exit?")) | |
807 | if (pstack__empty(fstack)) | ||
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; | 901 | continue; |
815 | } | 902 | /* Fall thru */ |
903 | default: | ||
904 | goto out_free_stack; | ||
816 | } | 905 | } |
817 | 906 | ||
818 | if (browser->selection->sym != NULL && | 907 | if (browser->selection != NULL && |
908 | browser->selection->sym != NULL && | ||
819 | !browser->selection->map->dso->annotate_warned && | 909 | !browser->selection->map->dso->annotate_warned && |
820 | asprintf(&options[nr_options], "Annotate %s", | 910 | asprintf(&options[nr_options], "Annotate %s", |
821 | browser->selection->sym->name) > 0) | 911 | browser->selection->sym->name) > 0) |
@@ -834,7 +924,8 @@ do_help: | |||
834 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | 924 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) |
835 | zoom_dso = nr_options++; | 925 | zoom_dso = nr_options++; |
836 | 926 | ||
837 | if (browser->selection->map != NULL && | 927 | if (browser->selection != NULL && |
928 | browser->selection->map != NULL && | ||
838 | asprintf(&options[nr_options], "Browse map details") > 0) | 929 | asprintf(&options[nr_options], "Browse map details") > 0) |
839 | browse_map = nr_options++; | 930 | browse_map = nr_options++; |
840 | 931 | ||
@@ -854,19 +945,11 @@ do_help: | |||
854 | if (choice == annotate) { | 945 | if (choice == annotate) { |
855 | struct hist_entry *he; | 946 | struct hist_entry *he; |
856 | do_annotate: | 947 | do_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); | 948 | he = hist_browser__selected_entry(browser); |
866 | if (he == NULL) | 949 | if (he == NULL) |
867 | continue; | 950 | continue; |
868 | 951 | ||
869 | hist_entry__tui_annotate(he); | 952 | hist_entry__tui_annotate(he, evsel->idx); |
870 | } else if (choice == browse_map) | 953 | } else if (choice == browse_map) |
871 | map__browse(browser->selection->map); | 954 | map__browse(browser->selection->map); |
872 | else if (choice == zoom_dso) { | 955 | else if (choice == zoom_dso) { |
@@ -885,8 +968,8 @@ zoom_out_dso: | |||
885 | pstack__push(fstack, &dso_filter); | 968 | pstack__push(fstack, &dso_filter); |
886 | } | 969 | } |
887 | hists__filter_by_dso(self, dso_filter); | 970 | hists__filter_by_dso(self, dso_filter); |
888 | hist_browser__title(msg, sizeof(msg), ev_name, | 971 | hists__browser_title(self, msg, sizeof(msg), ev_name, |
889 | dso_filter, thread_filter); | 972 | dso_filter, thread_filter); |
890 | hist_browser__reset(browser); | 973 | hist_browser__reset(browser); |
891 | } else if (choice == zoom_thread) { | 974 | } else if (choice == zoom_thread) { |
892 | zoom_thread: | 975 | zoom_thread: |
@@ -903,8 +986,8 @@ zoom_out_thread: | |||
903 | pstack__push(fstack, &thread_filter); | 986 | pstack__push(fstack, &thread_filter); |
904 | } | 987 | } |
905 | hists__filter_by_thread(self, thread_filter); | 988 | hists__filter_by_thread(self, thread_filter); |
906 | hist_browser__title(msg, sizeof(msg), ev_name, | 989 | hists__browser_title(self, msg, sizeof(msg), ev_name, |
907 | dso_filter, thread_filter); | 990 | dso_filter, thread_filter); |
908 | hist_browser__reset(browser); | 991 | hist_browser__reset(browser); |
909 | } | 992 | } |
910 | } | 993 | } |
@@ -915,34 +998,141 @@ out: | |||
915 | return key; | 998 | return key; |
916 | } | 999 | } |
917 | 1000 | ||
918 | int hists__tui_browse_tree(struct rb_root *self, const char *help) | 1001 | struct perf_evsel_menu { |
1002 | struct ui_browser b; | ||
1003 | struct perf_evsel *selection; | ||
1004 | }; | ||
1005 | |||
1006 | static void perf_evsel_menu__write(struct ui_browser *browser, | ||
1007 | void *entry, int row) | ||
919 | { | 1008 | { |
920 | struct rb_node *first = rb_first(self), *nd = first, *next; | 1009 | struct perf_evsel_menu *menu = container_of(browser, |
921 | int key = 0; | 1010 | struct perf_evsel_menu, b); |
1011 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); | ||
1012 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
1013 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1014 | const char *ev_name = event_name(evsel); | ||
1015 | char bf[256], unit; | ||
1016 | |||
1017 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | ||
1018 | HE_COLORSET_NORMAL); | ||
1019 | |||
1020 | nr_events = convert_unit(nr_events, &unit); | ||
1021 | snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | ||
1022 | unit, unit == ' ' ? "" : " ", ev_name); | ||
1023 | slsmg_write_nstring(bf, browser->width); | ||
922 | 1024 | ||
923 | while (nd) { | 1025 | if (current_entry) |
924 | struct hists *hists = rb_entry(nd, struct hists, rb_node); | 1026 | menu->selection = evsel; |
925 | const char *ev_name = __event_name(hists->type, hists->config); | 1027 | } |
926 | 1028 | ||
927 | key = hists__browse(hists, help, ev_name); | 1029 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) |
1030 | { | ||
1031 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
1032 | struct perf_evlist *evlist = menu->b.priv; | ||
1033 | struct perf_evsel *pos; | ||
1034 | const char *ev_name, *title = "Available samples"; | ||
1035 | int key; | ||
1036 | |||
1037 | if (ui_browser__show(&menu->b, title, | ||
1038 | "ESC: exit, ENTER|->: Browse histograms") < 0) | ||
1039 | return -1; | ||
928 | 1040 | ||
929 | if (is_exit_key(key)) | 1041 | ui_browser__add_exit_keys(&menu->b, exit_keys); |
1042 | |||
1043 | while (1) { | ||
1044 | key = ui_browser__run(&menu->b); | ||
1045 | |||
1046 | switch (key) { | ||
1047 | case NEWT_KEY_RIGHT: | ||
1048 | case NEWT_KEY_ENTER: | ||
1049 | if (!menu->selection) | ||
1050 | continue; | ||
1051 | pos = menu->selection; | ||
1052 | browse_hists: | ||
1053 | ev_name = event_name(pos); | ||
1054 | key = perf_evsel__hists_browse(pos, help, ev_name, true); | ||
1055 | ui_browser__show_title(&menu->b, title); | ||
930 | break; | 1056 | break; |
1057 | case NEWT_KEY_LEFT: | ||
1058 | continue; | ||
1059 | case NEWT_KEY_ESCAPE: | ||
1060 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
1061 | continue; | ||
1062 | /* Fall thru */ | ||
1063 | default: | ||
1064 | goto out; | ||
1065 | } | ||
931 | 1066 | ||
932 | switch (key) { | 1067 | switch (key) { |
933 | case NEWT_KEY_TAB: | 1068 | case NEWT_KEY_TAB: |
934 | next = rb_next(nd); | 1069 | if (pos->node.next == &evlist->entries) |
935 | if (next) | 1070 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); |
936 | nd = next; | 1071 | else |
937 | break; | 1072 | pos = list_entry(pos->node.next, struct perf_evsel, node); |
1073 | goto browse_hists; | ||
938 | case NEWT_KEY_UNTAB: | 1074 | case NEWT_KEY_UNTAB: |
939 | if (nd == first) | 1075 | if (pos->node.prev == &evlist->entries) |
940 | continue; | 1076 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); |
941 | nd = rb_prev(nd); | 1077 | else |
1078 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
1079 | goto browse_hists; | ||
1080 | case 'q': | ||
1081 | case CTRL('c'): | ||
1082 | goto out; | ||
942 | default: | 1083 | default: |
943 | break; | 1084 | break; |
944 | } | 1085 | } |
945 | } | 1086 | } |
946 | 1087 | ||
1088 | out: | ||
1089 | ui_browser__hide(&menu->b); | ||
947 | return key; | 1090 | return key; |
948 | } | 1091 | } |
1092 | |||
1093 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | ||
1094 | const char *help) | ||
1095 | { | ||
1096 | struct perf_evsel *pos; | ||
1097 | struct perf_evsel_menu menu = { | ||
1098 | .b = { | ||
1099 | .entries = &evlist->entries, | ||
1100 | .refresh = ui_browser__list_head_refresh, | ||
1101 | .seek = ui_browser__list_head_seek, | ||
1102 | .write = perf_evsel_menu__write, | ||
1103 | .nr_entries = evlist->nr_entries, | ||
1104 | .priv = evlist, | ||
1105 | }, | ||
1106 | }; | ||
1107 | |||
1108 | ui_helpline__push("Press ESC to exit"); | ||
1109 | |||
1110 | list_for_each_entry(pos, &evlist->entries, node) { | ||
1111 | const char *ev_name = event_name(pos); | ||
1112 | size_t line_len = strlen(ev_name) + 7; | ||
1113 | |||
1114 | if (menu.b.width < line_len) | ||
1115 | menu.b.width = line_len; | ||
1116 | /* | ||
1117 | * Cache the evsel name, tracepoints have a _high_ cost per | ||
1118 | * event_name() call. | ||
1119 | */ | ||
1120 | if (pos->name == NULL) | ||
1121 | pos->name = strdup(ev_name); | ||
1122 | } | ||
1123 | |||
1124 | return perf_evsel_menu__run(&menu, help); | ||
1125 | } | ||
1126 | |||
1127 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) | ||
1128 | { | ||
1129 | |||
1130 | if (evlist->nr_entries == 1) { | ||
1131 | struct perf_evsel *first = list_entry(evlist->entries.next, | ||
1132 | struct perf_evsel, node); | ||
1133 | const char *ev_name = event_name(first); | ||
1134 | return perf_evsel__hists_browse(first, help, ev_name, false); | ||
1135 | } | ||
1136 | |||
1137 | return __perf_evlist__tui_browse_hists(evlist, help); | ||
1138 | } | ||
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index 142b825b42bf..8462bffe20bc 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c | |||
@@ -1,6 +1,6 @@ | |||
1 | #include "../libslang.h" | 1 | #include "../libslang.h" |
2 | #include <elf.h> | 2 | #include <elf.h> |
3 | #include <newt.h> | 3 | #include <inttypes.h> |
4 | #include <sys/ttydefaults.h> | 4 | #include <sys/ttydefaults.h> |
5 | #include <ctype.h> | 5 | #include <ctype.h> |
6 | #include <string.h> | 6 | #include <string.h> |
@@ -41,13 +41,12 @@ static int ui_entry__read(const char *title, char *bf, size_t size, int width) | |||
41 | out_free_form: | 41 | out_free_form: |
42 | newtPopWindow(); | 42 | newtPopWindow(); |
43 | newtFormDestroy(form); | 43 | newtFormDestroy(form); |
44 | return 0; | 44 | return err; |
45 | } | 45 | } |
46 | 46 | ||
47 | struct map_browser { | 47 | struct map_browser { |
48 | struct ui_browser b; | 48 | struct ui_browser b; |
49 | struct map *map; | 49 | struct map *map; |
50 | u16 namelen; | ||
51 | u8 addrlen; | 50 | u8 addrlen; |
52 | }; | 51 | }; |
53 | 52 | ||
@@ -56,14 +55,16 @@ static void map_browser__write(struct ui_browser *self, void *nd, int row) | |||
56 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | 55 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); |
57 | struct map_browser *mb = container_of(self, struct map_browser, b); | 56 | struct map_browser *mb = container_of(self, struct map_browser, b); |
58 | bool current_entry = ui_browser__is_current_entry(self, row); | 57 | bool current_entry = ui_browser__is_current_entry(self, row); |
59 | int color = ui_browser__percent_color(0, current_entry); | 58 | int width; |
60 | 59 | ||
61 | SLsmg_set_color(color); | 60 | ui_browser__set_percent_color(self, 0, current_entry); |
62 | slsmg_printf("%*llx %*llx %c ", | 61 | slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ", |
63 | mb->addrlen, sym->start, mb->addrlen, sym->end, | 62 | mb->addrlen, sym->start, mb->addrlen, sym->end, |
64 | sym->binding == STB_GLOBAL ? 'g' : | 63 | sym->binding == STB_GLOBAL ? 'g' : |
65 | sym->binding == STB_LOCAL ? 'l' : 'w'); | 64 | sym->binding == STB_LOCAL ? 'l' : 'w'); |
66 | slsmg_write_nstring(sym->name, mb->namelen); | 65 | width = self->width - ((mb->addrlen * 2) + 4); |
66 | if (width > 0) | ||
67 | slsmg_write_nstring(sym->name, width); | ||
67 | } | 68 | } |
68 | 69 | ||
69 | /* FIXME uber-kludgy, see comment on cmd_report... */ | 70 | /* FIXME uber-kludgy, see comment on cmd_report... */ |
@@ -98,31 +99,29 @@ static int map_browser__search(struct map_browser *self) | |||
98 | return 0; | 99 | return 0; |
99 | } | 100 | } |
100 | 101 | ||
101 | static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) | 102 | static int map_browser__run(struct map_browser *self) |
102 | { | 103 | { |
104 | int key; | ||
105 | |||
103 | if (ui_browser__show(&self->b, self->map->dso->long_name, | 106 | if (ui_browser__show(&self->b, self->map->dso->long_name, |
104 | "Press <- or ESC to exit, %s / to search", | 107 | "Press <- or ESC to exit, %s / to search", |
105 | verbose ? "" : "restart with -v to use") < 0) | 108 | verbose ? "" : "restart with -v to use") < 0) |
106 | return -1; | 109 | return -1; |
107 | 110 | ||
108 | newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); | ||
109 | newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); | ||
110 | if (verbose) | 111 | if (verbose) |
111 | newtFormAddHotKey(self->b.form, '/'); | 112 | ui_browser__add_exit_key(&self->b, '/'); |
112 | 113 | ||
113 | while (1) { | 114 | while (1) { |
114 | ui_browser__run(&self->b, es); | 115 | key = ui_browser__run(&self->b); |
115 | 116 | ||
116 | if (es->reason != NEWT_EXIT_HOTKEY) | 117 | if (verbose && key == '/') |
117 | break; | ||
118 | if (verbose && es->u.key == '/') | ||
119 | map_browser__search(self); | 118 | map_browser__search(self); |
120 | else | 119 | else |
121 | break; | 120 | break; |
122 | } | 121 | } |
123 | 122 | ||
124 | ui_browser__hide(&self->b); | 123 | ui_browser__hide(&self->b); |
125 | return 0; | 124 | return key; |
126 | } | 125 | } |
127 | 126 | ||
128 | int map__browse(struct map *self) | 127 | int map__browse(struct map *self) |
@@ -136,7 +135,6 @@ int map__browse(struct map *self) | |||
136 | }, | 135 | }, |
137 | .map = self, | 136 | .map = self, |
138 | }; | 137 | }; |
139 | struct newtExitStruct es; | ||
140 | struct rb_node *nd; | 138 | struct rb_node *nd; |
141 | char tmp[BITS_PER_LONG / 4]; | 139 | char tmp[BITS_PER_LONG / 4]; |
142 | u64 maxaddr = 0; | 140 | u64 maxaddr = 0; |
@@ -144,8 +142,6 @@ int map__browse(struct map *self) | |||
144 | for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { | 142 | for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { |
145 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 143 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
146 | 144 | ||
147 | if (mb.namelen < pos->namelen) | ||
148 | mb.namelen = pos->namelen; | ||
149 | if (maxaddr < pos->end) | 145 | if (maxaddr < pos->end) |
150 | maxaddr = pos->end; | 146 | maxaddr = pos->end; |
151 | if (verbose) { | 147 | if (verbose) { |
@@ -155,7 +151,6 @@ int map__browse(struct map *self) | |||
155 | ++mb.b.nr_entries; | 151 | ++mb.b.nr_entries; |
156 | } | 152 | } |
157 | 153 | ||
158 | mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); | 154 | mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr); |
159 | mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; | 155 | return map_browser__run(&mb); |
160 | return map_browser__run(&mb, &es); | ||
161 | } | 156 | } |
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c new file mode 100644 index 000000000000..5a06538532af --- /dev/null +++ b/tools/perf/util/ui/browsers/top.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | #include "../browser.h" | ||
10 | #include "../../annotate.h" | ||
11 | #include "../helpline.h" | ||
12 | #include "../libslang.h" | ||
13 | #include "../util.h" | ||
14 | #include "../../evlist.h" | ||
15 | #include "../../hist.h" | ||
16 | #include "../../sort.h" | ||
17 | #include "../../symbol.h" | ||
18 | #include "../../top.h" | ||
19 | |||
20 | struct perf_top_browser { | ||
21 | struct ui_browser b; | ||
22 | struct rb_root root; | ||
23 | struct sym_entry *selection; | ||
24 | float sum_ksamples; | ||
25 | int dso_width; | ||
26 | int dso_short_width; | ||
27 | int sym_width; | ||
28 | }; | ||
29 | |||
30 | static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) | ||
31 | { | ||
32 | struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); | ||
33 | struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); | ||
34 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
35 | struct symbol *symbol = sym_entry__symbol(syme); | ||
36 | struct perf_top *top = browser->priv; | ||
37 | int width = browser->width; | ||
38 | double pcnt; | ||
39 | |||
40 | pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / | ||
41 | top_browser->sum_ksamples)); | ||
42 | ui_browser__set_percent_color(browser, pcnt, current_entry); | ||
43 | |||
44 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
45 | slsmg_printf("%20.2f ", syme->weight); | ||
46 | width -= 24; | ||
47 | } else { | ||
48 | slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
49 | width -= 23; | ||
50 | } | ||
51 | |||
52 | slsmg_printf("%4.1f%%", pcnt); | ||
53 | width -= 7; | ||
54 | |||
55 | if (verbose) { | ||
56 | slsmg_printf(" %016" PRIx64, symbol->start); | ||
57 | width -= 17; | ||
58 | } | ||
59 | |||
60 | slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, | ||
61 | symbol->name); | ||
62 | width -= top_browser->sym_width; | ||
63 | slsmg_write_nstring(width >= syme->map->dso->long_name_len ? | ||
64 | syme->map->dso->long_name : | ||
65 | syme->map->dso->short_name, width); | ||
66 | |||
67 | if (current_entry) | ||
68 | top_browser->selection = syme; | ||
69 | } | ||
70 | |||
71 | static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) | ||
72 | { | ||
73 | struct perf_top *top = browser->b.priv; | ||
74 | u64 top_idx = browser->b.top_idx; | ||
75 | |||
76 | browser->root = RB_ROOT; | ||
77 | browser->b.top = NULL; | ||
78 | browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); | ||
79 | /* | ||
80 | * No active symbols | ||
81 | */ | ||
82 | if (top->rb_entries == 0) | ||
83 | return; | ||
84 | |||
85 | perf_top__find_widths(top, &browser->root, &browser->dso_width, | ||
86 | &browser->dso_short_width, | ||
87 | &browser->sym_width); | ||
88 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) { | ||
89 | browser->dso_width = browser->dso_short_width; | ||
90 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) | ||
91 | browser->sym_width = browser->b.width - browser->dso_width - 29; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Adjust the ui_browser indexes since the entries in the browser->root | ||
96 | * rb_tree may have changed, then seek it from start, so that we get a | ||
97 | * possible new top of the screen. | ||
98 | */ | ||
99 | browser->b.nr_entries = top->rb_entries; | ||
100 | |||
101 | if (top_idx >= browser->b.nr_entries) { | ||
102 | if (browser->b.height >= browser->b.nr_entries) | ||
103 | top_idx = browser->b.nr_entries - browser->b.height; | ||
104 | else | ||
105 | top_idx = 0; | ||
106 | } | ||
107 | |||
108 | if (browser->b.index >= top_idx + browser->b.height) | ||
109 | browser->b.index = top_idx + browser->b.index - browser->b.top_idx; | ||
110 | |||
111 | if (browser->b.index >= browser->b.nr_entries) | ||
112 | browser->b.index = browser->b.nr_entries - 1; | ||
113 | |||
114 | browser->b.top_idx = top_idx; | ||
115 | browser->b.seek(&browser->b, top_idx, SEEK_SET); | ||
116 | } | ||
117 | |||
118 | static void perf_top_browser__annotate(struct perf_top_browser *browser) | ||
119 | { | ||
120 | struct sym_entry *syme = browser->selection; | ||
121 | struct symbol *sym = sym_entry__symbol(syme); | ||
122 | struct annotation *notes = symbol__annotation(sym); | ||
123 | struct perf_top *top = browser->b.priv; | ||
124 | |||
125 | if (notes->src != NULL) | ||
126 | goto do_annotation; | ||
127 | |||
128 | pthread_mutex_lock(¬es->lock); | ||
129 | |||
130 | top->sym_filter_entry = NULL; | ||
131 | |||
132 | if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) { | ||
133 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
134 | sym->name); | ||
135 | pthread_mutex_unlock(¬es->lock); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | top->sym_filter_entry = syme; | ||
140 | |||
141 | pthread_mutex_unlock(¬es->lock); | ||
142 | do_annotation: | ||
143 | symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); | ||
144 | } | ||
145 | |||
146 | static int perf_top_browser__run(struct perf_top_browser *browser) | ||
147 | { | ||
148 | int key; | ||
149 | char title[160]; | ||
150 | struct perf_top *top = browser->b.priv; | ||
151 | int delay_msecs = top->delay_secs * 1000; | ||
152 | int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
153 | |||
154 | perf_top_browser__update_rb_tree(browser); | ||
155 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
156 | perf_top__reset_sample_counters(top); | ||
157 | |||
158 | if (ui_browser__show(&browser->b, title, | ||
159 | "ESC: exit, ENTER|->|a: Live Annotate") < 0) | ||
160 | return -1; | ||
161 | |||
162 | newtFormSetTimer(browser->b.form, delay_msecs); | ||
163 | ui_browser__add_exit_keys(&browser->b, exit_keys); | ||
164 | |||
165 | while (1) { | ||
166 | key = ui_browser__run(&browser->b); | ||
167 | |||
168 | switch (key) { | ||
169 | case -1: | ||
170 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
171 | perf_top_browser__update_rb_tree(browser); | ||
172 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
173 | perf_top__reset_sample_counters(top); | ||
174 | ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); | ||
175 | SLsmg_gotorc(0, 0); | ||
176 | slsmg_write_nstring(title, browser->b.width); | ||
177 | break; | ||
178 | case 'a': | ||
179 | case NEWT_KEY_RIGHT: | ||
180 | case NEWT_KEY_ENTER: | ||
181 | if (browser->selection) | ||
182 | perf_top_browser__annotate(browser); | ||
183 | break; | ||
184 | case NEWT_KEY_LEFT: | ||
185 | continue; | ||
186 | case NEWT_KEY_ESCAPE: | ||
187 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
188 | continue; | ||
189 | /* Fall thru */ | ||
190 | default: | ||
191 | goto out; | ||
192 | } | ||
193 | } | ||
194 | out: | ||
195 | ui_browser__hide(&browser->b); | ||
196 | return key; | ||
197 | } | ||
198 | |||
199 | int perf_top__tui_browser(struct perf_top *top) | ||
200 | { | ||
201 | struct perf_top_browser browser = { | ||
202 | .b = { | ||
203 | .entries = &browser.root, | ||
204 | .refresh = ui_browser__rb_tree_refresh, | ||
205 | .seek = ui_browser__rb_tree_seek, | ||
206 | .write = perf_top_browser__write, | ||
207 | .priv = top, | ||
208 | }, | ||
209 | }; | ||
210 | |||
211 | ui_helpline__push("Press <- or ESC to exit"); | ||
212 | return perf_top_browser__run(&browser); | ||
213 | } | ||
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 8d79daa4458a..f36d2ff509ed 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | #include "../debug.h" | 6 | #include "../debug.h" |
7 | #include "helpline.h" | 7 | #include "helpline.h" |
8 | #include "ui.h" | ||
8 | 9 | ||
9 | void ui_helpline__pop(void) | 10 | void ui_helpline__pop(void) |
10 | { | 11 | { |
@@ -55,7 +56,8 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
55 | int ret; | 56 | int ret; |
56 | static int backlog; | 57 | static int backlog; |
57 | 58 | ||
58 | ret = vsnprintf(ui_helpline__last_msg + backlog, | 59 | pthread_mutex_lock(&ui__lock); |
60 | ret = vsnprintf(ui_helpline__last_msg + backlog, | ||
59 | sizeof(ui_helpline__last_msg) - backlog, format, ap); | 61 | sizeof(ui_helpline__last_msg) - backlog, format, ap); |
60 | backlog += ret; | 62 | backlog += ret; |
61 | 63 | ||
@@ -64,6 +66,7 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
64 | newtRefresh(); | 66 | newtRefresh(); |
65 | backlog = 0; | 67 | backlog = 0; |
66 | } | 68 | } |
69 | pthread_mutex_unlock(&ui__lock); | ||
67 | 70 | ||
68 | return ret; | 71 | return ret; |
69 | } | 72 | } |
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h index 5623da8e8080..2b63e1c9b181 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/util/ui/libslang.h | |||
@@ -13,11 +13,11 @@ | |||
13 | 13 | ||
14 | #if SLANG_VERSION < 20104 | 14 | #if SLANG_VERSION < 20104 |
15 | #define slsmg_printf(msg, args...) \ | 15 | #define slsmg_printf(msg, args...) \ |
16 | SLsmg_printf((char *)msg, ##args) | 16 | SLsmg_printf((char *)(msg), ##args) |
17 | #define slsmg_write_nstring(msg, len) \ | 17 | #define slsmg_write_nstring(msg, len) \ |
18 | SLsmg_write_nstring((char *)msg, len) | 18 | SLsmg_write_nstring((char *)(msg), len) |
19 | #define sltt_set_color(obj, name, fg, bg) \ | 19 | #define sltt_set_color(obj, name, fg, bg) \ |
20 | SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) | 20 | SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg)) |
21 | #else | 21 | #else |
22 | #define slsmg_printf SLsmg_printf | 22 | #define slsmg_printf SLsmg_printf |
23 | #define slsmg_write_nstring SLsmg_write_nstring | 23 | #define slsmg_write_nstring SLsmg_write_nstring |
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index 662085032eb7..ee46d671db59 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c | |||
@@ -6,6 +6,9 @@ | |||
6 | #include "../debug.h" | 6 | #include "../debug.h" |
7 | #include "browser.h" | 7 | #include "browser.h" |
8 | #include "helpline.h" | 8 | #include "helpline.h" |
9 | #include "ui.h" | ||
10 | |||
11 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; | ||
9 | 12 | ||
10 | static void newt_suspend(void *d __used) | 13 | static void newt_suspend(void *d __used) |
11 | { | 14 | { |
@@ -14,11 +17,12 @@ static void newt_suspend(void *d __used) | |||
14 | newtResume(); | 17 | newtResume(); |
15 | } | 18 | } |
16 | 19 | ||
17 | void setup_browser(void) | 20 | void setup_browser(bool fallback_to_pager) |
18 | { | 21 | { |
19 | if (!isatty(1) || !use_browser || dump_trace) { | 22 | if (!isatty(1) || !use_browser || dump_trace) { |
20 | use_browser = 0; | 23 | use_browser = 0; |
21 | setup_pager(); | 24 | if (fallback_to_pager) |
25 | setup_pager(); | ||
22 | return; | 26 | return; |
23 | } | 27 | } |
24 | 28 | ||
diff --git a/tools/perf/util/ui/ui.h b/tools/perf/util/ui/ui.h new file mode 100644 index 000000000000..d264e059c829 --- /dev/null +++ b/tools/perf/util/ui/ui.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _PERF_UI_H_ | ||
2 | #define _PERF_UI_H_ 1 | ||
3 | |||
4 | #include <pthread.h> | ||
5 | |||
6 | extern pthread_mutex_t ui__lock; | ||
7 | |||
8 | #endif /* _PERF_UI_H_ */ | ||
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 04600e26ceea..fdf1fc8f08bc 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
@@ -9,10 +9,9 @@ | |||
9 | #include "../debug.h" | 9 | #include "../debug.h" |
10 | #include "browser.h" | 10 | #include "browser.h" |
11 | #include "helpline.h" | 11 | #include "helpline.h" |
12 | #include "ui.h" | ||
12 | #include "util.h" | 13 | #include "util.h" |
13 | 14 | ||
14 | newtComponent newt_form__new(void); | ||
15 | |||
16 | static void newt_form__set_exit_keys(newtComponent self) | 15 | static void newt_form__set_exit_keys(newtComponent self) |
17 | { | 16 | { |
18 | newtFormAddHotKey(self, NEWT_KEY_LEFT); | 17 | newtFormAddHotKey(self, NEWT_KEY_LEFT); |
@@ -22,7 +21,7 @@ static void newt_form__set_exit_keys(newtComponent self) | |||
22 | newtFormAddHotKey(self, CTRL('c')); | 21 | newtFormAddHotKey(self, CTRL('c')); |
23 | } | 22 | } |
24 | 23 | ||
25 | newtComponent newt_form__new(void) | 24 | static newtComponent newt_form__new(void) |
26 | { | 25 | { |
27 | newtComponent self = newtForm(NULL, NULL, 0); | 26 | newtComponent self = newtForm(NULL, NULL, 0); |
28 | if (self) | 27 | if (self) |
@@ -106,9 +105,26 @@ out_destroy_form: | |||
106 | return rc; | 105 | return rc; |
107 | } | 106 | } |
108 | 107 | ||
108 | static const char yes[] = "Yes", no[] = "No", | ||
109 | warning_str[] = "Warning!", ok[] = "Ok"; | ||
110 | |||
109 | bool ui__dialog_yesno(const char *msg) | 111 | bool ui__dialog_yesno(const char *msg) |
110 | { | 112 | { |
111 | /* newtWinChoice should really be accepting const char pointers... */ | 113 | /* newtWinChoice should really be accepting const char pointers... */ |
112 | char yes[] = "Yes", no[] = "No"; | 114 | return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1; |
113 | return newtWinChoice(NULL, yes, no, (char *)msg) == 1; | 115 | } |
116 | |||
117 | void ui__warning(const char *format, ...) | ||
118 | { | ||
119 | va_list args; | ||
120 | |||
121 | va_start(args, format); | ||
122 | if (use_browser > 0) { | ||
123 | pthread_mutex_lock(&ui__lock); | ||
124 | newtWinMessagev((char *)warning_str, (char *)ok, | ||
125 | (char *)format, args); | ||
126 | pthread_mutex_unlock(&ui__lock); | ||
127 | } else | ||
128 | vfprintf(stderr, format, args); | ||
129 | va_end(args); | ||
114 | } | 130 | } |