blob: f1882589c8268a4aa74b4e04dda3d74fb91a2304 [file] [log] [blame]
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
Arnaldo Carvalho de Melo211ef122010-08-10 14:54:09 -03004#include "ui/libslang.h"
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -03005#include <signal.h>
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03006#include <stdlib.h>
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -03007#include <elf.h>
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03008#include <newt.h>
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -03009#include <sys/ttydefaults.h>
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030010
11#include "cache.h"
12#include "hist.h"
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -030013#include "pstack.h"
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030014#include "session.h"
15#include "sort.h"
16#include "symbol.h"
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -030017#include "ui/browser.h"
Arnaldo Carvalho de Melo55755362010-08-08 19:48:31 -030018#include "ui/helpline.h"
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030019
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -030020newtComponent newt_form__new(void);
21
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -030022static int ui_entry__read(const char *title, char *bf, size_t size, int width)
23{
24 struct newtExitStruct es;
25 newtComponent form, entry;
26 const char *result;
27 int err = -1;
28
29 newtCenteredWindow(width, 1, title);
30 form = newtForm(NULL, NULL, 0);
31 if (form == NULL)
32 return -1;
33
34 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
35 if (entry == NULL)
36 goto out_free_form;
37
38 newtFormAddComponent(form, entry);
39 newtFormAddHotKey(form, NEWT_KEY_ENTER);
40 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
41 newtFormAddHotKey(form, NEWT_KEY_LEFT);
42 newtFormAddHotKey(form, CTRL('c'));
43 newtFormRun(form, &es);
44
45 if (result != NULL) {
46 strncpy(bf, result, size);
47 err = 0;
48 }
49out_free_form:
50 newtPopWindow();
51 newtFormDestroy(form);
52 return 0;
53}
54
Arnaldo Carvalho de Melo211ef122010-08-10 14:54:09 -030055char browser__last_msg[1024];
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030056
57int browser__show_help(const char *format, va_list ap)
58{
59 int ret;
60 static int backlog;
61
62 ret = vsnprintf(browser__last_msg + backlog,
63 sizeof(browser__last_msg) - backlog, format, ap);
64 backlog += ret;
65
66 if (browser__last_msg[backlog - 1] == '\n') {
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -030067 ui_helpline__puts(browser__last_msg);
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030068 newtRefresh();
69 backlog = 0;
70 }
71
72 return ret;
73}
74
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -030075static void newt_form__set_exit_keys(newtComponent self)
76{
Arnaldo Carvalho de Meloa308f3a2010-05-16 20:29:38 -030077 newtFormAddHotKey(self, NEWT_KEY_LEFT);
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -030078 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
79 newtFormAddHotKey(self, 'Q');
80 newtFormAddHotKey(self, 'q');
81 newtFormAddHotKey(self, CTRL('c'));
82}
83
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -030084newtComponent newt_form__new(void)
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -030085{
86 newtComponent self = newtForm(NULL, NULL, 0);
87 if (self)
88 newt_form__set_exit_keys(self);
89 return self;
90}
91
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -030092static int popup_menu(int argc, char * const argv[])
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -030093{
94 struct newtExitStruct es;
95 int i, rc = -1, max_len = 5;
96 newtComponent listbox, form = newt_form__new();
97
98 if (form == NULL)
99 return -1;
100
101 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
102 if (listbox == NULL)
103 goto out_destroy_form;
104
Arnaldo Carvalho de Melo7f826452010-05-10 10:51:25 -0300105 newtFormAddComponent(form, listbox);
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300106
107 for (i = 0; i < argc; ++i) {
108 int len = strlen(argv[i]);
109 if (len > max_len)
110 max_len = len;
111 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
112 goto out_destroy_form;
113 }
114
115 newtCenteredWindow(max_len, argc, NULL);
116 newtFormRun(form, &es);
117 rc = newtListboxGetCurrent(listbox) - NULL;
118 if (es.reason == NEWT_EXIT_HOTKEY)
119 rc = -1;
120 newtPopWindow();
121out_destroy_form:
122 newtFormDestroy(form);
123 return rc;
124}
125
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300126static int ui__help_window(const char *text)
127{
128 struct newtExitStruct es;
129 newtComponent tb, form = newt_form__new();
130 int rc = -1;
131 int max_len = 0, nr_lines = 0;
132 const char *t;
133
134 if (form == NULL)
135 return -1;
136
137 t = text;
138 while (1) {
139 const char *sep = strchr(t, '\n');
140 int len;
141
142 if (sep == NULL)
143 sep = strchr(t, '\0');
144 len = sep - t;
145 if (max_len < len)
146 max_len = len;
147 ++nr_lines;
148 if (*sep == '\0')
149 break;
150 t = sep + 1;
151 }
152
153 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
154 if (tb == NULL)
155 goto out_destroy_form;
156
157 newtTextboxSetText(tb, text);
158 newtFormAddComponent(form, tb);
159 newtCenteredWindow(max_len, nr_lines, NULL);
160 newtFormRun(form, &es);
161 newtPopWindow();
162 rc = 0;
163out_destroy_form:
164 newtFormDestroy(form);
165 return rc;
166}
167
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300168static bool dialog_yesno(const char *msg)
169{
170 /* newtWinChoice should really be accepting const char pointers... */
171 char yes[] = "Yes", no[] = "No";
Arnaldo Carvalho de Meloc0ed55d2010-04-05 12:04:23 -0300172 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300173}
174
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300175static char *callchain_list__sym_name(struct callchain_list *self,
176 char *bf, size_t bfsize)
177{
Arnaldo Carvalho de Melob3c9ac02010-03-24 16:40:18 -0300178 if (self->ms.sym)
179 return self->ms.sym->name;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300180
181 snprintf(bf, bfsize, "%#Lx", self->ip);
182 return bf;
183}
184
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300185/* -------------------------------------------------------------------- */
186
187struct map_browser {
188 struct ui_browser b;
189 struct map *map;
190 u16 namelen;
191 u8 addrlen;
192};
193
194static void map_browser__write(struct ui_browser *self, void *nd, int row)
195{
196 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
197 struct map_browser *mb = container_of(self, struct map_browser, b);
198 bool current_entry = ui_browser__is_current_entry(self, row);
199 int color = ui_browser__percent_color(0, current_entry);
200
201 SLsmg_set_color(color);
202 slsmg_printf("%*llx %*llx %c ",
203 mb->addrlen, sym->start, mb->addrlen, sym->end,
204 sym->binding == STB_GLOBAL ? 'g' :
205 sym->binding == STB_LOCAL ? 'l' : 'w');
206 slsmg_write_nstring(sym->name, mb->namelen);
207}
208
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300209/* FIXME uber-kludgy, see comment on cmd_report... */
210static u32 *symbol__browser_index(struct symbol *self)
211{
212 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
213}
214
215static int map_browser__search(struct map_browser *self)
216{
217 char target[512];
218 struct symbol *sym;
219 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
220
221 if (err)
222 return err;
223
224 if (target[0] == '0' && tolower(target[1]) == 'x') {
225 u64 addr = strtoull(target, NULL, 16);
226 sym = map__find_symbol(self->map, addr, NULL);
227 } else
228 sym = map__find_symbol_by_name(self->map, target, NULL);
229
230 if (sym != NULL) {
231 u32 *idx = symbol__browser_index(sym);
232
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300233 self->b.top = &sym->rb_node;
234 self->b.index = self->b.top_idx = *idx;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300235 } else
236 ui_helpline__fpush("%s not found!", target);
237
238 return 0;
239}
240
241static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
242{
243 if (ui_browser__show(&self->b, self->map->dso->long_name) < 0)
244 return -1;
245
246 ui_helpline__fpush("Press <- or ESC to exit, %s / to search",
247 verbose ? "" : "restart with -v to use");
248 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
249 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
250 if (verbose)
251 newtFormAddHotKey(self->b.form, '/');
252
253 while (1) {
254 ui_browser__run(&self->b, es);
255
256 if (es->reason != NEWT_EXIT_HOTKEY)
257 break;
258 if (verbose && es->u.key == '/')
259 map_browser__search(self);
260 else
261 break;
262 }
263
264 newtFormDestroy(self->b.form);
265 newtPopWindow();
266 ui_helpline__pop();
267 return 0;
268}
269
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300270static int map__browse(struct map *self)
271{
272 struct map_browser mb = {
273 .b = {
274 .entries = &self->dso->symbols[self->type],
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300275 .refresh = ui_browser__rb_tree_refresh,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300276 .seek = ui_browser__rb_tree_seek,
277 .write = map_browser__write,
278 },
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300279 .map = self,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300280 };
281 struct newtExitStruct es;
282 struct rb_node *nd;
283 char tmp[BITS_PER_LONG / 4];
284 u64 maxaddr = 0;
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300285
286 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
287 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
288
289 if (mb.namelen < pos->namelen)
290 mb.namelen = pos->namelen;
291 if (maxaddr < pos->end)
292 maxaddr = pos->end;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300293 if (verbose) {
294 u32 *idx = symbol__browser_index(pos);
295 *idx = mb.b.nr_entries;
296 }
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300297 ++mb.b.nr_entries;
298 }
299
300 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
301 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300302 return map_browser__run(&mb, &es);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300303}
304
305/* -------------------------------------------------------------------- */
306
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300307struct hist_browser {
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300308 struct ui_browser b;
309 struct hists *hists;
310 struct hist_entry *he_selection;
311 struct map_symbol *selection;
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300312};
313
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300314static void hist_browser__reset(struct hist_browser *self);
315static int hist_browser__run(struct hist_browser *self, const char *title,
316 struct newtExitStruct *es);
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300317static unsigned int hist_browser__refresh(struct ui_browser *self);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300318static void ui_browser__hists_seek(struct ui_browser *self,
319 off_t offset, int whence);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300320
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300321static struct hist_browser *hist_browser__new(struct hists *hists)
322{
323 struct hist_browser *self = zalloc(sizeof(*self));
324
325 if (self) {
326 self->hists = hists;
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300327 self->b.refresh = hist_browser__refresh;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300328 self->b.seek = ui_browser__hists_seek;
329 }
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300330
331 return self;
332}
333
334static void hist_browser__delete(struct hist_browser *self)
335{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300336 newtFormDestroy(self->b.form);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300337 newtPopWindow();
338 free(self);
339}
340
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300341static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300342{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300343 return self->he_selection;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300344}
345
346static struct thread *hist_browser__selected_thread(struct hist_browser *self)
347{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300348 return self->he_selection->thread;
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300349}
350
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300351static int hist_browser__title(char *bf, size_t size, const char *ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300352 const struct dso *dso, const struct thread *thread)
353{
354 int printed = 0;
355
356 if (thread)
357 printed += snprintf(bf + printed, size - printed,
358 "Thread: %s(%d)",
359 (thread->comm_set ? thread->comm : ""),
360 thread->pid);
361 if (dso)
362 printed += snprintf(bf + printed, size - printed,
363 "%sDSO: %s", thread ? " " : "",
364 dso->short_name);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300365 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300366}
367
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300368int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300369{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300370 struct hist_browser *browser = hist_browser__new(self);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300371 struct pstack *fstack;
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300372 const struct thread *thread_filter = NULL;
373 const struct dso *dso_filter = NULL;
374 struct newtExitStruct es;
375 char msg[160];
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300376 int key = -1;
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300377
378 if (browser == NULL)
379 return -1;
380
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300381 fstack = pstack__new(2);
382 if (fstack == NULL)
383 goto out;
384
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300385 ui_helpline__push(helpline);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300386
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300387 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300388 dso_filter, thread_filter);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300389
390 while (1) {
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300391 const struct thread *thread;
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300392 const struct dso *dso;
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300393 char *options[16];
394 int nr_options = 0, choice = 0, i,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300395 annotate = -2, zoom_dso = -2, zoom_thread = -2,
396 browse_map = -2;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300397
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300398 if (hist_browser__run(browser, msg, &es))
399 break;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300400
401 thread = hist_browser__selected_thread(browser);
402 dso = browser->selection->map ? browser->selection->map->dso : NULL;
403
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300404 if (es.reason == NEWT_EXIT_HOTKEY) {
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300405 key = es.u.key;
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300406
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300407 switch (key) {
408 case NEWT_KEY_F1:
409 goto do_help;
410 case NEWT_KEY_TAB:
411 case NEWT_KEY_UNTAB:
412 /*
413 * Exit the browser, let hists__browser_tree
414 * go to the next or previous
415 */
416 goto out_free_stack;
417 default:;
418 }
419
420 key = toupper(key);
421 switch (key) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300422 case 'A':
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300423 if (browser->selection->map == NULL &&
424 browser->selection->map->dso->annotate_warned)
425 continue;
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300426 goto do_annotate;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300427 case 'D':
428 goto zoom_dso;
429 case 'T':
430 goto zoom_thread;
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300431 case 'H':
432 case '?':
433do_help:
434 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
435 "<- Zoom out\n"
436 "a Annotate current symbol\n"
437 "h/?/F1 Show this window\n"
438 "d Zoom into current DSO\n"
439 "t Zoom into current Thread\n"
440 "q/CTRL+C Exit browser");
441 continue;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300442 default:;
443 }
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300444 if (is_exit_key(key)) {
445 if (key == NEWT_KEY_ESCAPE) {
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300446 if (dialog_yesno("Do you really want to exit?"))
447 break;
448 else
449 continue;
450 } else
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300451 break;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300452 }
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300453
454 if (es.u.key == NEWT_KEY_LEFT) {
455 const void *top;
456
457 if (pstack__empty(fstack))
458 continue;
459 top = pstack__pop(fstack);
460 if (top == &dso_filter)
461 goto zoom_out_dso;
462 if (top == &thread_filter)
463 goto zoom_out_thread;
464 continue;
465 }
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300466 }
467
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300468 if (browser->selection->sym != NULL &&
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300469 !browser->selection->map->dso->annotate_warned &&
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300470 asprintf(&options[nr_options], "Annotate %s",
471 browser->selection->sym->name) > 0)
472 annotate = nr_options++;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300473
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300474 if (thread != NULL &&
475 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300476 (thread_filter ? "out of" : "into"),
477 (thread->comm_set ? thread->comm : ""),
478 thread->pid) > 0)
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300479 zoom_thread = nr_options++;
480
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300481 if (dso != NULL &&
482 asprintf(&options[nr_options], "Zoom %s %s DSO",
483 (dso_filter ? "out of" : "into"),
484 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
485 zoom_dso = nr_options++;
486
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300487 if (browser->selection->map != NULL &&
488 asprintf(&options[nr_options], "Browse map details") > 0)
489 browse_map = nr_options++;
490
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300491 options[nr_options++] = (char *)"Exit";
492
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300493 choice = popup_menu(nr_options, options);
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300494
495 for (i = 0; i < nr_options - 1; ++i)
496 free(options[i]);
497
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300498 if (choice == nr_options - 1)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300499 break;
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300500
501 if (choice == -1)
502 continue;
Arnaldo Carvalho de Meloc1ec5fe2010-05-15 20:45:31 -0300503
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300504 if (choice == annotate) {
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300505 struct hist_entry *he;
Arnaldo Carvalho de Meloc1ec5fe2010-05-15 20:45:31 -0300506do_annotate:
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300507 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300508 browser->selection->map->dso->annotate_warned = 1;
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300509 ui_helpline__puts("No vmlinux file found, can't "
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300510 "annotate with just a "
511 "kallsyms file");
512 continue;
513 }
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300514
515 he = hist_browser__selected_entry(browser);
516 if (he == NULL)
517 continue;
518
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300519 hist_entry__tui_annotate(he);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300520 } else if (choice == browse_map)
521 map__browse(browser->selection->map);
522 else if (choice == zoom_dso) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300523zoom_dso:
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300524 if (dso_filter) {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300525 pstack__remove(fstack, &dso_filter);
526zoom_out_dso:
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300527 ui_helpline__pop();
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300528 dso_filter = NULL;
529 } else {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300530 if (dso == NULL)
531 continue;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300532 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300533 dso->kernel ? "the Kernel" : dso->short_name);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300534 dso_filter = dso;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300535 pstack__push(fstack, &dso_filter);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300536 }
Arnaldo Carvalho de Melob09e0192010-05-11 11:10:15 -0300537 hists__filter_by_dso(self, dso_filter);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300538 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300539 dso_filter, thread_filter);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300540 hist_browser__reset(browser);
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300541 } else if (choice == zoom_thread) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300542zoom_thread:
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300543 if (thread_filter) {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300544 pstack__remove(fstack, &thread_filter);
545zoom_out_thread:
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300546 ui_helpline__pop();
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300547 thread_filter = NULL;
548 } else {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300549 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300550 thread->comm_set ? thread->comm : "",
551 thread->pid);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300552 thread_filter = thread;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300553 pstack__push(fstack, &thread_filter);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300554 }
Arnaldo Carvalho de Melob09e0192010-05-11 11:10:15 -0300555 hists__filter_by_thread(self, thread_filter);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300556 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300557 dso_filter, thread_filter);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300558 hist_browser__reset(browser);
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300559 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300560 }
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300561out_free_stack:
562 pstack__delete(fstack);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300563out:
564 hist_browser__delete(browser);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300565 return key;
566}
567
568int hists__tui_browse_tree(struct rb_root *self, const char *help)
569{
570 struct rb_node *first = rb_first(self), *nd = first, *next;
571 int key = 0;
572
573 while (nd) {
574 struct hists *hists = rb_entry(nd, struct hists, rb_node);
575 const char *ev_name = __event_name(hists->type, hists->config);
576
577 key = hists__browse(hists, help, ev_name);
578
579 if (is_exit_key(key))
580 break;
581
582 switch (key) {
583 case NEWT_KEY_TAB:
584 next = rb_next(nd);
585 if (next)
586 nd = next;
587 break;
588 case NEWT_KEY_UNTAB:
589 if (nd == first)
590 continue;
591 nd = rb_prev(nd);
592 default:
593 break;
594 }
595 }
596
597 return key;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300598}
599
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -0300600static void newt_suspend(void *d __used)
601{
602 newtSuspend();
603 raise(SIGTSTP);
604 newtResume();
605}
606
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300607void setup_browser(void)
608{
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300609 if (!isatty(1) || !use_browser || dump_trace) {
Arnaldo Carvalho de Melo62e34362010-05-26 13:22:26 -0300610 use_browser = 0;
Arnaldo Carvalho de Melo5d06e692010-05-20 22:01:10 -0300611 setup_pager();
612 return;
613 }
614
615 use_browser = 1;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300616 newtInit();
617 newtCls();
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -0300618 newtSetSuspendCallback(newt_suspend, NULL);
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300619 ui_helpline__puts(" ");
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -0300620 ui_browser__init();
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300621}
622
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300623void exit_browser(bool wait_for_ok)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300624{
Arnaldo Carvalho de Melo5d06e692010-05-20 22:01:10 -0300625 if (use_browser > 0) {
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300626 if (wait_for_ok) {
627 char title[] = "Fatal Error", ok[] = "Ok";
628 newtWinMessage(title, ok, browser__last_msg);
629 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300630 newtFinished();
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300631 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300632}
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300633
634static void hist_browser__refresh_dimensions(struct hist_browser *self)
635{
636 /* 3 == +/- toggle symbol before actual hist_entry rendering */
637 self->b.width = 3 + (hists__sort_list_width(self->hists) +
638 sizeof("[k]"));
639}
640
641static void hist_browser__reset(struct hist_browser *self)
642{
643 self->b.nr_entries = self->hists->nr_entries;
644 hist_browser__refresh_dimensions(self);
645 ui_browser__reset_index(&self->b);
646}
647
648static char tree__folded_sign(bool unfolded)
649{
650 return unfolded ? '-' : '+';
651}
652
653static char map_symbol__folded(const struct map_symbol *self)
654{
655 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
656}
657
658static char hist_entry__folded(const struct hist_entry *self)
659{
660 return map_symbol__folded(&self->ms);
661}
662
663static char callchain_list__folded(const struct callchain_list *self)
664{
665 return map_symbol__folded(&self->ms);
666}
667
668static bool map_symbol__toggle_fold(struct map_symbol *self)
669{
670 if (!self->has_children)
671 return false;
672
673 self->unfolded = !self->unfolded;
674 return true;
675}
676
677#define LEVEL_OFFSET_STEP 3
678
679static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
680 struct callchain_node *chain_node,
681 u64 total, int level,
682 unsigned short row,
683 off_t *row_offset,
684 bool *is_current_entry)
685{
686 struct rb_node *node;
687 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
688 u64 new_total, remaining;
689
690 if (callchain_param.mode == CHAIN_GRAPH_REL)
691 new_total = chain_node->children_hit;
692 else
693 new_total = total;
694
695 remaining = new_total;
696 node = rb_first(&chain_node->rb_root);
697 while (node) {
698 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
699 struct rb_node *next = rb_next(node);
700 u64 cumul = cumul_hits(child);
701 struct callchain_list *chain;
702 char folded_sign = ' ';
703 int first = true;
704 int extra_offset = 0;
705
706 remaining -= cumul;
707
708 list_for_each_entry(chain, &child->val, list) {
709 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
710 const char *str;
711 int color;
712 bool was_first = first;
713
714 if (first) {
715 first = false;
716 chain->ms.has_children = chain->list.next != &child->val ||
717 rb_first(&child->rb_root) != NULL;
718 } else {
719 extra_offset = LEVEL_OFFSET_STEP;
720 chain->ms.has_children = chain->list.next == &child->val &&
721 rb_first(&child->rb_root) != NULL;
722 }
723
724 folded_sign = callchain_list__folded(chain);
725 if (*row_offset != 0) {
726 --*row_offset;
727 goto do_next;
728 }
729
730 alloc_str = NULL;
731 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
732 if (was_first) {
733 double percent = cumul * 100.0 / new_total;
734
735 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
736 str = "Not enough memory!";
737 else
738 str = alloc_str;
739 }
740
741 color = HE_COLORSET_NORMAL;
742 width = self->b.width - (offset + extra_offset + 2);
743 if (ui_browser__is_current_entry(&self->b, row)) {
744 self->selection = &chain->ms;
745 color = HE_COLORSET_SELECTED;
746 *is_current_entry = true;
747 }
748
749 SLsmg_set_color(color);
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300750 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300751 slsmg_write_nstring(" ", offset + extra_offset);
752 slsmg_printf("%c ", folded_sign);
753 slsmg_write_nstring(str, width);
754 free(alloc_str);
755
756 if (++row == self->b.height)
757 goto out;
758do_next:
759 if (folded_sign == '+')
760 break;
761 }
762
763 if (folded_sign == '-') {
764 const int new_level = level + (extra_offset ? 2 : 1);
765 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
766 new_level, row, row_offset,
767 is_current_entry);
768 }
769 if (row == self->b.height)
770 goto out;
771 node = next;
772 }
773out:
774 return row - first_row;
775}
776
777static int hist_browser__show_callchain_node(struct hist_browser *self,
778 struct callchain_node *node,
779 int level, unsigned short row,
780 off_t *row_offset,
781 bool *is_current_entry)
782{
783 struct callchain_list *chain;
784 int first_row = row,
785 offset = level * LEVEL_OFFSET_STEP,
786 width = self->b.width - offset;
787 char folded_sign = ' ';
788
789 list_for_each_entry(chain, &node->val, list) {
790 char ipstr[BITS_PER_LONG / 4 + 1], *s;
791 int color;
792 /*
793 * FIXME: This should be moved to somewhere else,
794 * probably when the callchain is created, so as not to
795 * traverse it all over again
796 */
797 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
798 folded_sign = callchain_list__folded(chain);
799
800 if (*row_offset != 0) {
801 --*row_offset;
802 continue;
803 }
804
805 color = HE_COLORSET_NORMAL;
806 if (ui_browser__is_current_entry(&self->b, row)) {
807 self->selection = &chain->ms;
808 color = HE_COLORSET_SELECTED;
809 *is_current_entry = true;
810 }
811
812 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300813 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300814 SLsmg_set_color(color);
815 slsmg_write_nstring(" ", offset);
816 slsmg_printf("%c ", folded_sign);
817 slsmg_write_nstring(s, width - 2);
818
819 if (++row == self->b.height)
820 goto out;
821 }
822
823 if (folded_sign == '-')
824 row += hist_browser__show_callchain_node_rb_tree(self, node,
825 self->hists->stats.total_period,
826 level + 1, row,
827 row_offset,
828 is_current_entry);
829out:
830 return row - first_row;
831}
832
833static int hist_browser__show_callchain(struct hist_browser *self,
834 struct rb_root *chain,
835 int level, unsigned short row,
836 off_t *row_offset,
837 bool *is_current_entry)
838{
839 struct rb_node *nd;
840 int first_row = row;
841
842 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
843 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
844
845 row += hist_browser__show_callchain_node(self, node, level,
846 row, row_offset,
847 is_current_entry);
848 if (row == self->b.height)
849 break;
850 }
851
852 return row - first_row;
853}
854
855static int hist_browser__show_entry(struct hist_browser *self,
856 struct hist_entry *entry,
857 unsigned short row)
858{
859 char s[256];
860 double percent;
861 int printed = 0;
862 int color, width = self->b.width;
863 char folded_sign = ' ';
864 bool current_entry = ui_browser__is_current_entry(&self->b, row);
865 off_t row_offset = entry->row_offset;
866
867 if (current_entry) {
868 self->he_selection = entry;
869 self->selection = &entry->ms;
870 }
871
872 if (symbol_conf.use_callchain) {
873 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
874 folded_sign = hist_entry__folded(entry);
875 }
876
877 if (row_offset == 0) {
878 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
879 0, false, self->hists->stats.total_period);
880 percent = (entry->period * 100.0) / self->hists->stats.total_period;
881
882 color = HE_COLORSET_SELECTED;
883 if (!current_entry) {
884 if (percent >= MIN_RED)
885 color = HE_COLORSET_TOP;
886 else if (percent >= MIN_GREEN)
887 color = HE_COLORSET_MEDIUM;
888 else
889 color = HE_COLORSET_NORMAL;
890 }
891
892 SLsmg_set_color(color);
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300893 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300894 if (symbol_conf.use_callchain) {
895 slsmg_printf("%c ", folded_sign);
896 width -= 2;
897 }
898 slsmg_write_nstring(s, width);
899 ++row;
900 ++printed;
901 } else
902 --row_offset;
903
904 if (folded_sign == '-' && row != self->b.height) {
905 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
906 1, row, &row_offset,
907 &current_entry);
908 if (current_entry)
909 self->he_selection = entry;
910 }
911
912 return printed;
913}
914
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300915static unsigned int hist_browser__refresh(struct ui_browser *self)
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300916{
917 unsigned row = 0;
918 struct rb_node *nd;
919 struct hist_browser *hb = container_of(self, struct hist_browser, b);
920
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300921 if (self->top == NULL)
922 self->top = rb_first(&hb->hists->entries);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300923
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300924 for (nd = self->top; nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300925 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
926
927 if (h->filtered)
928 continue;
929
930 row += hist_browser__show_entry(hb, h, row);
931 if (row == self->height)
932 break;
933 }
934
935 return row;
936}
937
938static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
939{
940 struct rb_node *nd = rb_first(&self->rb_root);
941
942 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
943 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
944 struct callchain_list *chain;
945 int first = true;
946
947 list_for_each_entry(chain, &child->val, list) {
948 if (first) {
949 first = false;
950 chain->ms.has_children = chain->list.next != &child->val ||
951 rb_first(&child->rb_root) != NULL;
952 } else
953 chain->ms.has_children = chain->list.next == &child->val &&
954 rb_first(&child->rb_root) != NULL;
955 }
956
957 callchain_node__init_have_children_rb_tree(child);
958 }
959}
960
961static void callchain_node__init_have_children(struct callchain_node *self)
962{
963 struct callchain_list *chain;
964
965 list_for_each_entry(chain, &self->val, list)
966 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
967
968 callchain_node__init_have_children_rb_tree(self);
969}
970
971static void callchain__init_have_children(struct rb_root *self)
972{
973 struct rb_node *nd;
974
975 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
976 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
977 callchain_node__init_have_children(node);
978 }
979}
980
981static void hist_entry__init_have_children(struct hist_entry *self)
982{
983 if (!self->init_have_children) {
984 callchain__init_have_children(&self->sorted_chain);
985 self->init_have_children = true;
986 }
987}
988
989static struct rb_node *hists__filter_entries(struct rb_node *nd)
990{
991 while (nd != NULL) {
992 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
993 if (!h->filtered)
994 return nd;
995
996 nd = rb_next(nd);
997 }
998
999 return NULL;
1000}
1001
1002static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
1003{
1004 while (nd != NULL) {
1005 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1006 if (!h->filtered)
1007 return nd;
1008
1009 nd = rb_prev(nd);
1010 }
1011
1012 return NULL;
1013}
1014
1015static void ui_browser__hists_seek(struct ui_browser *self,
1016 off_t offset, int whence)
1017{
1018 struct hist_entry *h;
1019 struct rb_node *nd;
1020 bool first = true;
1021
1022 switch (whence) {
1023 case SEEK_SET:
1024 nd = hists__filter_entries(rb_first(self->entries));
1025 break;
1026 case SEEK_CUR:
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001027 nd = self->top;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001028 goto do_offset;
1029 case SEEK_END:
1030 nd = hists__filter_prev_entries(rb_last(self->entries));
1031 first = false;
1032 break;
1033 default:
1034 return;
1035 }
1036
1037 /*
1038 * Moves not relative to the first visible entry invalidates its
1039 * row_offset:
1040 */
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001041 h = rb_entry(self->top, struct hist_entry, rb_node);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001042 h->row_offset = 0;
1043
1044 /*
1045 * Here we have to check if nd is expanded (+), if it is we can't go
1046 * the next top level hist_entry, instead we must compute an offset of
1047 * what _not_ to show and not change the first visible entry.
1048 *
1049 * This offset increments when we are going from top to bottom and
1050 * decreases when we're going from bottom to top.
1051 *
1052 * As we don't have backpointers to the top level in the callchains
1053 * structure, we need to always print the whole hist_entry callchain,
1054 * skipping the first ones that are before the first visible entry
1055 * and stop when we printed enough lines to fill the screen.
1056 */
1057do_offset:
1058 if (offset > 0) {
1059 do {
1060 h = rb_entry(nd, struct hist_entry, rb_node);
1061 if (h->ms.unfolded) {
1062 u16 remaining = h->nr_rows - h->row_offset;
1063 if (offset > remaining) {
1064 offset -= remaining;
1065 h->row_offset = 0;
1066 } else {
1067 h->row_offset += offset;
1068 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001069 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001070 break;
1071 }
1072 }
1073 nd = hists__filter_entries(rb_next(nd));
1074 if (nd == NULL)
1075 break;
1076 --offset;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001077 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001078 } while (offset != 0);
1079 } else if (offset < 0) {
1080 while (1) {
1081 h = rb_entry(nd, struct hist_entry, rb_node);
1082 if (h->ms.unfolded) {
1083 if (first) {
1084 if (-offset > h->row_offset) {
1085 offset += h->row_offset;
1086 h->row_offset = 0;
1087 } else {
1088 h->row_offset += offset;
1089 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001090 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001091 break;
1092 }
1093 } else {
1094 if (-offset > h->nr_rows) {
1095 offset += h->nr_rows;
1096 h->row_offset = 0;
1097 } else {
1098 h->row_offset = h->nr_rows + offset;
1099 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001100 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001101 break;
1102 }
1103 }
1104 }
1105
1106 nd = hists__filter_prev_entries(rb_prev(nd));
1107 if (nd == NULL)
1108 break;
1109 ++offset;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001110 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001111 if (offset == 0) {
1112 /*
1113 * Last unfiltered hist_entry, check if it is
1114 * unfolded, if it is then we should have
1115 * row_offset at its last entry.
1116 */
1117 h = rb_entry(nd, struct hist_entry, rb_node);
1118 if (h->ms.unfolded)
1119 h->row_offset = h->nr_rows;
1120 break;
1121 }
1122 first = false;
1123 }
1124 } else {
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001125 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001126 h = rb_entry(nd, struct hist_entry, rb_node);
1127 h->row_offset = 0;
1128 }
1129}
1130
1131static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
1132{
1133 int n = 0;
1134 struct rb_node *nd;
1135
1136 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1137 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1138 struct callchain_list *chain;
1139 char folded_sign = ' '; /* No children */
1140
1141 list_for_each_entry(chain, &child->val, list) {
1142 ++n;
1143 /* We need this because we may not have children */
1144 folded_sign = callchain_list__folded(chain);
1145 if (folded_sign == '+')
1146 break;
1147 }
1148
1149 if (folded_sign == '-') /* Have children and they're unfolded */
1150 n += callchain_node__count_rows_rb_tree(child);
1151 }
1152
1153 return n;
1154}
1155
1156static int callchain_node__count_rows(struct callchain_node *node)
1157{
1158 struct callchain_list *chain;
1159 bool unfolded = false;
1160 int n = 0;
1161
1162 list_for_each_entry(chain, &node->val, list) {
1163 ++n;
1164 unfolded = chain->ms.unfolded;
1165 }
1166
1167 if (unfolded)
1168 n += callchain_node__count_rows_rb_tree(node);
1169
1170 return n;
1171}
1172
1173static int callchain__count_rows(struct rb_root *chain)
1174{
1175 struct rb_node *nd;
1176 int n = 0;
1177
1178 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1179 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1180 n += callchain_node__count_rows(node);
1181 }
1182
1183 return n;
1184}
1185
1186static bool hist_browser__toggle_fold(struct hist_browser *self)
1187{
1188 if (map_symbol__toggle_fold(self->selection)) {
1189 struct hist_entry *he = self->he_selection;
1190
1191 hist_entry__init_have_children(he);
1192 self->hists->nr_entries -= he->nr_rows;
1193
1194 if (he->ms.unfolded)
1195 he->nr_rows = callchain__count_rows(&he->sorted_chain);
1196 else
1197 he->nr_rows = 0;
1198 self->hists->nr_entries += he->nr_rows;
1199 self->b.nr_entries = self->hists->nr_entries;
1200
1201 return true;
1202 }
1203
1204 /* If it doesn't have children, no toggling performed */
1205 return false;
1206}
1207
1208static int hist_browser__run(struct hist_browser *self, const char *title,
1209 struct newtExitStruct *es)
1210{
1211 char str[256], unit;
1212 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
1213
1214 self->b.entries = &self->hists->entries;
1215 self->b.nr_entries = self->hists->nr_entries;
1216
1217 hist_browser__refresh_dimensions(self);
1218
1219 nr_events = convert_unit(nr_events, &unit);
1220 snprintf(str, sizeof(str), "Events: %lu%c ",
1221 nr_events, unit);
1222 newtDrawRootText(0, 0, str);
1223
1224 if (ui_browser__show(&self->b, title) < 0)
1225 return -1;
1226
1227 newtFormAddHotKey(self->b.form, 'A');
1228 newtFormAddHotKey(self->b.form, 'a');
1229 newtFormAddHotKey(self->b.form, '?');
1230 newtFormAddHotKey(self->b.form, 'h');
1231 newtFormAddHotKey(self->b.form, 'H');
1232 newtFormAddHotKey(self->b.form, 'd');
1233
1234 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
1235 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
1236 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1237
1238 while (1) {
1239 ui_browser__run(&self->b, es);
1240
1241 if (es->reason != NEWT_EXIT_HOTKEY)
1242 break;
1243 switch (es->u.key) {
1244 case 'd': { /* Debug */
1245 static int seq;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001246 struct hist_entry *h = rb_entry(self->b.top,
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001247 struct hist_entry, rb_node);
1248 ui_helpline__pop();
1249 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1250 seq++, self->b.nr_entries,
1251 self->hists->nr_entries,
1252 self->b.height,
1253 self->b.index,
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001254 self->b.top_idx,
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001255 h->row_offset, h->nr_rows);
1256 }
1257 continue;
1258 case NEWT_KEY_ENTER:
1259 if (hist_browser__toggle_fold(self))
1260 break;
1261 /* fall thru */
1262 default:
1263 return 0;
1264 }
1265 }
1266 return 0;
1267}