blob: 9768be31f9c594b0ac381149a720bd639994d727 [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 Melo32ec6ac2010-05-18 00:23:14 -03004/*
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
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -030013#include <slang.h>
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -030014#include <signal.h>
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030015#include <stdlib.h>
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -030016#include <elf.h>
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030017#include <newt.h>
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -030018#include <sys/ttydefaults.h>
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030019
20#include "cache.h"
21#include "hist.h"
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -030022#include "pstack.h"
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030023#include "session.h"
24#include "sort.h"
25#include "symbol.h"
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -030026#include "ui/browser.h"
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030027
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -030028#if SLANG_VERSION < 20104
29#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
30#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
31#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
32 (char *)fg, (char *)bg)
33#else
34#define slsmg_printf SLsmg_printf
35#define slsmg_write_nstring SLsmg_write_nstring
36#define sltt_set_color SLtt_set_color
37#endif
38
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -030039newtComponent newt_form__new(void);
40
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030041struct ui_progress {
42 newtComponent form, scale;
43};
44
45struct ui_progress *ui_progress__new(const char *title, u64 total)
46{
47 struct ui_progress *self = malloc(sizeof(*self));
48
49 if (self != NULL) {
50 int cols;
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030051
52 if (use_browser <= 0)
53 return self;
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030054 newtGetScreenSize(&cols, NULL);
55 cols -= 4;
56 newtCenteredWindow(cols, 1, title);
57 self->form = newtForm(NULL, NULL, 0);
58 if (self->form == NULL)
59 goto out_free_self;
60 self->scale = newtScale(0, 0, cols, total);
61 if (self->scale == NULL)
62 goto out_free_form;
Arnaldo Carvalho de Melo7f826452010-05-10 10:51:25 -030063 newtFormAddComponent(self->form, self->scale);
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030064 newtRefresh();
65 }
66
67 return self;
68
69out_free_form:
70 newtFormDestroy(self->form);
71out_free_self:
72 free(self);
73 return NULL;
74}
75
76void ui_progress__update(struct ui_progress *self, u64 curr)
77{
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030078 /*
79 * FIXME: We should have a per UI backend way of showing progress,
80 * stdio will just show a percentage as NN%, etc.
81 */
82 if (use_browser <= 0)
83 return;
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030084 newtScaleSet(self->scale, curr);
85 newtRefresh();
86}
87
88void ui_progress__delete(struct ui_progress *self)
89{
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030090 if (use_browser > 0) {
91 newtFormDestroy(self->form);
92 newtPopWindow();
93 }
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030094 free(self);
95}
96
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -030097static void ui_helpline__pop(void)
98{
99 newtPopHelpLine();
100}
101
102static void ui_helpline__push(const char *msg)
103{
104 newtPushHelpLine(msg);
105}
106
107static void ui_helpline__vpush(const char *fmt, va_list ap)
108{
109 char *s;
110
111 if (vasprintf(&s, fmt, ap) < 0)
112 vfprintf(stderr, fmt, ap);
113 else {
114 ui_helpline__push(s);
115 free(s);
116 }
117}
118
119static void ui_helpline__fpush(const char *fmt, ...)
120{
121 va_list ap;
122
123 va_start(ap, fmt);
124 ui_helpline__vpush(fmt, ap);
125 va_end(ap);
126}
127
128static void ui_helpline__puts(const char *msg)
129{
130 ui_helpline__pop();
131 ui_helpline__push(msg);
132}
133
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300134static int ui_entry__read(const char *title, char *bf, size_t size, int width)
135{
136 struct newtExitStruct es;
137 newtComponent form, entry;
138 const char *result;
139 int err = -1;
140
141 newtCenteredWindow(width, 1, title);
142 form = newtForm(NULL, NULL, 0);
143 if (form == NULL)
144 return -1;
145
146 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
147 if (entry == NULL)
148 goto out_free_form;
149
150 newtFormAddComponent(form, entry);
151 newtFormAddHotKey(form, NEWT_KEY_ENTER);
152 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
153 newtFormAddHotKey(form, NEWT_KEY_LEFT);
154 newtFormAddHotKey(form, CTRL('c'));
155 newtFormRun(form, &es);
156
157 if (result != NULL) {
158 strncpy(bf, result, size);
159 err = 0;
160 }
161out_free_form:
162 newtPopWindow();
163 newtFormDestroy(form);
164 return 0;
165}
166
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -0300167static char browser__last_msg[1024];
168
169int browser__show_help(const char *format, va_list ap)
170{
171 int ret;
172 static int backlog;
173
174 ret = vsnprintf(browser__last_msg + backlog,
175 sizeof(browser__last_msg) - backlog, format, ap);
176 backlog += ret;
177
178 if (browser__last_msg[backlog - 1] == '\n') {
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300179 ui_helpline__puts(browser__last_msg);
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -0300180 newtRefresh();
181 backlog = 0;
182 }
183
184 return ret;
185}
186
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300187static void newt_form__set_exit_keys(newtComponent self)
188{
Arnaldo Carvalho de Meloa308f3a2010-05-16 20:29:38 -0300189 newtFormAddHotKey(self, NEWT_KEY_LEFT);
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300190 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
191 newtFormAddHotKey(self, 'Q');
192 newtFormAddHotKey(self, 'q');
193 newtFormAddHotKey(self, CTRL('c'));
194}
195
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -0300196newtComponent newt_form__new(void)
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300197{
198 newtComponent self = newtForm(NULL, NULL, 0);
199 if (self)
200 newt_form__set_exit_keys(self);
201 return self;
202}
203
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300204static int popup_menu(int argc, char * const argv[])
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300205{
206 struct newtExitStruct es;
207 int i, rc = -1, max_len = 5;
208 newtComponent listbox, form = newt_form__new();
209
210 if (form == NULL)
211 return -1;
212
213 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
214 if (listbox == NULL)
215 goto out_destroy_form;
216
Arnaldo Carvalho de Melo7f826452010-05-10 10:51:25 -0300217 newtFormAddComponent(form, listbox);
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300218
219 for (i = 0; i < argc; ++i) {
220 int len = strlen(argv[i]);
221 if (len > max_len)
222 max_len = len;
223 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
224 goto out_destroy_form;
225 }
226
227 newtCenteredWindow(max_len, argc, NULL);
228 newtFormRun(form, &es);
229 rc = newtListboxGetCurrent(listbox) - NULL;
230 if (es.reason == NEWT_EXIT_HOTKEY)
231 rc = -1;
232 newtPopWindow();
233out_destroy_form:
234 newtFormDestroy(form);
235 return rc;
236}
237
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300238static int ui__help_window(const char *text)
239{
240 struct newtExitStruct es;
241 newtComponent tb, form = newt_form__new();
242 int rc = -1;
243 int max_len = 0, nr_lines = 0;
244 const char *t;
245
246 if (form == NULL)
247 return -1;
248
249 t = text;
250 while (1) {
251 const char *sep = strchr(t, '\n');
252 int len;
253
254 if (sep == NULL)
255 sep = strchr(t, '\0');
256 len = sep - t;
257 if (max_len < len)
258 max_len = len;
259 ++nr_lines;
260 if (*sep == '\0')
261 break;
262 t = sep + 1;
263 }
264
265 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
266 if (tb == NULL)
267 goto out_destroy_form;
268
269 newtTextboxSetText(tb, text);
270 newtFormAddComponent(form, tb);
271 newtCenteredWindow(max_len, nr_lines, NULL);
272 newtFormRun(form, &es);
273 newtPopWindow();
274 rc = 0;
275out_destroy_form:
276 newtFormDestroy(form);
277 return rc;
278}
279
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300280static bool dialog_yesno(const char *msg)
281{
282 /* newtWinChoice should really be accepting const char pointers... */
283 char yes[] = "Yes", no[] = "No";
Arnaldo Carvalho de Meloc0ed55d2010-04-05 12:04:23 -0300284 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300285}
286
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300287static void ui__error_window(const char *fmt, ...)
288{
289 va_list ap;
290
291 va_start(ap, fmt);
292 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
293 va_end(ap);
294}
295
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300296static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300297{
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300298 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
299 bool current_entry = ui_browser__is_current_entry(self, row);
300 int width = self->width;
301
302 if (ol->offset != -1) {
303 struct hist_entry *he = self->priv;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300304 struct symbol *sym = he->ms.sym;
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300305 int len = he->ms.sym->end - he->ms.sym->start;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300306 unsigned int hits = 0;
307 double percent = 0.0;
308 int color;
309 struct sym_priv *priv = symbol__priv(sym);
310 struct sym_ext *sym_ext = priv->ext;
311 struct sym_hist *h = priv->hist;
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300312 s64 offset = ol->offset;
313 struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300314
315 while (offset < (s64)len &&
316 (next == NULL || offset < next->offset)) {
317 if (sym_ext) {
318 percent += sym_ext[offset].percent;
319 } else
320 hits += h->ip[offset];
321
322 ++offset;
323 }
324
325 if (sym_ext == NULL && h->sum)
326 percent = 100.0 * hits / h->sum;
327
328 color = ui_browser__percent_color(percent, current_entry);
329 SLsmg_set_color(color);
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300330 slsmg_printf(" %7.2f ", percent);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300331 if (!current_entry)
332 SLsmg_set_color(HE_COLORSET_CODE);
333 } else {
334 int color = ui_browser__percent_color(0, current_entry);
335 SLsmg_set_color(color);
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300336 slsmg_write_nstring(" ", 9);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300337 }
338
339 SLsmg_write_char(':');
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300340 slsmg_write_nstring(" ", 8);
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300341 if (!*ol->line)
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300342 slsmg_write_nstring(" ", width - 18);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300343 else
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300344 slsmg_write_nstring(ol->line, width - 18);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300345}
346
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300347static char *callchain_list__sym_name(struct callchain_list *self,
348 char *bf, size_t bfsize)
349{
Arnaldo Carvalho de Melob3c9ac02010-03-24 16:40:18 -0300350 if (self->ms.sym)
351 return self->ms.sym->name;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300352
353 snprintf(bf, bfsize, "%#Lx", self->ip);
354 return bf;
355}
356
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300357int hist_entry__tui_annotate(struct hist_entry *self)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300358{
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300359 struct newtExitStruct es;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300360 struct objdump_line *pos, *n;
361 LIST_HEAD(head);
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300362 struct ui_browser browser = {
363 .entries = &head,
364 .refresh = ui_browser__list_head_refresh,
365 .seek = ui_browser__list_head_seek,
366 .write = annotate_browser__write,
367 .priv = self,
368 };
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300369 int ret;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300370
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300371 if (self->ms.sym == NULL)
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300372 return -1;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300373
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300374 if (self->ms.map->dso->annotate_warned)
375 return -1;
376
377 if (hist_entry__annotate(self, &head) < 0) {
378 ui__error_window(browser__last_msg);
379 return -1;
380 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300381
Arnaldo Carvalho de Melo60553902010-05-15 20:40:34 -0300382 ui_helpline__push("Press <- or ESC to exit");
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300383
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300384 list_for_each_entry(pos, &head, node) {
385 size_t line_len = strlen(pos->line);
386 if (browser.width < line_len)
387 browser.width = line_len;
388 ++browser.nr_entries;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300389 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300390
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300391 browser.width += 18; /* Percentage */
Arnaldo Carvalho de Melo13f499f2010-06-21 18:04:02 -0300392 ui_browser__show(&browser, self->ms.sym->name);
Arnaldo Carvalho de Melob61b55e2010-07-21 17:55:32 -0300393 newtFormAddHotKey(browser.form, ' ');
Srikar Dronamraju0879b102010-06-29 23:02:26 +0530394 ret = ui_browser__run(&browser, &es);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300395 newtFormDestroy(browser.form);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300396 newtPopWindow();
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300397 list_for_each_entry_safe(pos, n, &head, node) {
398 list_del(&pos->node);
399 objdump_line__free(pos);
400 }
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300401 ui_helpline__pop();
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300402 return ret;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300403}
404
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300405/* -------------------------------------------------------------------- */
406
407struct map_browser {
408 struct ui_browser b;
409 struct map *map;
410 u16 namelen;
411 u8 addrlen;
412};
413
414static void map_browser__write(struct ui_browser *self, void *nd, int row)
415{
416 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
417 struct map_browser *mb = container_of(self, struct map_browser, b);
418 bool current_entry = ui_browser__is_current_entry(self, row);
419 int color = ui_browser__percent_color(0, current_entry);
420
421 SLsmg_set_color(color);
422 slsmg_printf("%*llx %*llx %c ",
423 mb->addrlen, sym->start, mb->addrlen, sym->end,
424 sym->binding == STB_GLOBAL ? 'g' :
425 sym->binding == STB_LOCAL ? 'l' : 'w');
426 slsmg_write_nstring(sym->name, mb->namelen);
427}
428
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300429/* FIXME uber-kludgy, see comment on cmd_report... */
430static u32 *symbol__browser_index(struct symbol *self)
431{
432 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
433}
434
435static int map_browser__search(struct map_browser *self)
436{
437 char target[512];
438 struct symbol *sym;
439 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
440
441 if (err)
442 return err;
443
444 if (target[0] == '0' && tolower(target[1]) == 'x') {
445 u64 addr = strtoull(target, NULL, 16);
446 sym = map__find_symbol(self->map, addr, NULL);
447 } else
448 sym = map__find_symbol_by_name(self->map, target, NULL);
449
450 if (sym != NULL) {
451 u32 *idx = symbol__browser_index(sym);
452
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300453 self->b.top = &sym->rb_node;
454 self->b.index = self->b.top_idx = *idx;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300455 } else
456 ui_helpline__fpush("%s not found!", target);
457
458 return 0;
459}
460
461static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
462{
463 if (ui_browser__show(&self->b, self->map->dso->long_name) < 0)
464 return -1;
465
466 ui_helpline__fpush("Press <- or ESC to exit, %s / to search",
467 verbose ? "" : "restart with -v to use");
468 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
469 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
470 if (verbose)
471 newtFormAddHotKey(self->b.form, '/');
472
473 while (1) {
474 ui_browser__run(&self->b, es);
475
476 if (es->reason != NEWT_EXIT_HOTKEY)
477 break;
478 if (verbose && es->u.key == '/')
479 map_browser__search(self);
480 else
481 break;
482 }
483
484 newtFormDestroy(self->b.form);
485 newtPopWindow();
486 ui_helpline__pop();
487 return 0;
488}
489
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300490static int map__browse(struct map *self)
491{
492 struct map_browser mb = {
493 .b = {
494 .entries = &self->dso->symbols[self->type],
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300495 .refresh = ui_browser__rb_tree_refresh,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300496 .seek = ui_browser__rb_tree_seek,
497 .write = map_browser__write,
498 },
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300499 .map = self,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300500 };
501 struct newtExitStruct es;
502 struct rb_node *nd;
503 char tmp[BITS_PER_LONG / 4];
504 u64 maxaddr = 0;
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300505
506 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
507 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
508
509 if (mb.namelen < pos->namelen)
510 mb.namelen = pos->namelen;
511 if (maxaddr < pos->end)
512 maxaddr = pos->end;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300513 if (verbose) {
514 u32 *idx = symbol__browser_index(pos);
515 *idx = mb.b.nr_entries;
516 }
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300517 ++mb.b.nr_entries;
518 }
519
520 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
521 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300522 return map_browser__run(&mb, &es);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300523}
524
525/* -------------------------------------------------------------------- */
526
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300527struct hist_browser {
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300528 struct ui_browser b;
529 struct hists *hists;
530 struct hist_entry *he_selection;
531 struct map_symbol *selection;
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300532};
533
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300534static void hist_browser__reset(struct hist_browser *self);
535static int hist_browser__run(struct hist_browser *self, const char *title,
536 struct newtExitStruct *es);
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300537static unsigned int hist_browser__refresh(struct ui_browser *self);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300538static void ui_browser__hists_seek(struct ui_browser *self,
539 off_t offset, int whence);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300540
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300541static struct hist_browser *hist_browser__new(struct hists *hists)
542{
543 struct hist_browser *self = zalloc(sizeof(*self));
544
545 if (self) {
546 self->hists = hists;
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300547 self->b.refresh = hist_browser__refresh;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300548 self->b.seek = ui_browser__hists_seek;
549 }
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300550
551 return self;
552}
553
554static void hist_browser__delete(struct hist_browser *self)
555{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300556 newtFormDestroy(self->b.form);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300557 newtPopWindow();
558 free(self);
559}
560
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300561static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300562{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300563 return self->he_selection;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300564}
565
566static struct thread *hist_browser__selected_thread(struct hist_browser *self)
567{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300568 return self->he_selection->thread;
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300569}
570
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300571static int hist_browser__title(char *bf, size_t size, const char *ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300572 const struct dso *dso, const struct thread *thread)
573{
574 int printed = 0;
575
576 if (thread)
577 printed += snprintf(bf + printed, size - printed,
578 "Thread: %s(%d)",
579 (thread->comm_set ? thread->comm : ""),
580 thread->pid);
581 if (dso)
582 printed += snprintf(bf + printed, size - printed,
583 "%sDSO: %s", thread ? " " : "",
584 dso->short_name);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300585 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300586}
587
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300588int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300589{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300590 struct hist_browser *browser = hist_browser__new(self);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300591 struct pstack *fstack;
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300592 const struct thread *thread_filter = NULL;
593 const struct dso *dso_filter = NULL;
594 struct newtExitStruct es;
595 char msg[160];
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300596 int key = -1;
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300597
598 if (browser == NULL)
599 return -1;
600
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300601 fstack = pstack__new(2);
602 if (fstack == NULL)
603 goto out;
604
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300605 ui_helpline__push(helpline);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300606
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300607 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300608 dso_filter, thread_filter);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300609
610 while (1) {
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300611 const struct thread *thread;
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300612 const struct dso *dso;
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300613 char *options[16];
614 int nr_options = 0, choice = 0, i,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300615 annotate = -2, zoom_dso = -2, zoom_thread = -2,
616 browse_map = -2;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300617
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300618 if (hist_browser__run(browser, msg, &es))
619 break;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300620
621 thread = hist_browser__selected_thread(browser);
622 dso = browser->selection->map ? browser->selection->map->dso : NULL;
623
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300624 if (es.reason == NEWT_EXIT_HOTKEY) {
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300625 key = es.u.key;
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300626
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300627 switch (key) {
628 case NEWT_KEY_F1:
629 goto do_help;
630 case NEWT_KEY_TAB:
631 case NEWT_KEY_UNTAB:
632 /*
633 * Exit the browser, let hists__browser_tree
634 * go to the next or previous
635 */
636 goto out_free_stack;
637 default:;
638 }
639
640 key = toupper(key);
641 switch (key) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300642 case 'A':
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300643 if (browser->selection->map == NULL &&
644 browser->selection->map->dso->annotate_warned)
645 continue;
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300646 goto do_annotate;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300647 case 'D':
648 goto zoom_dso;
649 case 'T':
650 goto zoom_thread;
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300651 case 'H':
652 case '?':
653do_help:
654 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
655 "<- Zoom out\n"
656 "a Annotate current symbol\n"
657 "h/?/F1 Show this window\n"
658 "d Zoom into current DSO\n"
659 "t Zoom into current Thread\n"
660 "q/CTRL+C Exit browser");
661 continue;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300662 default:;
663 }
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300664 if (is_exit_key(key)) {
665 if (key == NEWT_KEY_ESCAPE) {
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300666 if (dialog_yesno("Do you really want to exit?"))
667 break;
668 else
669 continue;
670 } else
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300671 break;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300672 }
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300673
674 if (es.u.key == NEWT_KEY_LEFT) {
675 const void *top;
676
677 if (pstack__empty(fstack))
678 continue;
679 top = pstack__pop(fstack);
680 if (top == &dso_filter)
681 goto zoom_out_dso;
682 if (top == &thread_filter)
683 goto zoom_out_thread;
684 continue;
685 }
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300686 }
687
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300688 if (browser->selection->sym != NULL &&
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300689 !browser->selection->map->dso->annotate_warned &&
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300690 asprintf(&options[nr_options], "Annotate %s",
691 browser->selection->sym->name) > 0)
692 annotate = nr_options++;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300693
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300694 if (thread != NULL &&
695 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300696 (thread_filter ? "out of" : "into"),
697 (thread->comm_set ? thread->comm : ""),
698 thread->pid) > 0)
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300699 zoom_thread = nr_options++;
700
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300701 if (dso != NULL &&
702 asprintf(&options[nr_options], "Zoom %s %s DSO",
703 (dso_filter ? "out of" : "into"),
704 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
705 zoom_dso = nr_options++;
706
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300707 if (browser->selection->map != NULL &&
708 asprintf(&options[nr_options], "Browse map details") > 0)
709 browse_map = nr_options++;
710
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300711 options[nr_options++] = (char *)"Exit";
712
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300713 choice = popup_menu(nr_options, options);
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300714
715 for (i = 0; i < nr_options - 1; ++i)
716 free(options[i]);
717
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300718 if (choice == nr_options - 1)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300719 break;
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300720
721 if (choice == -1)
722 continue;
Arnaldo Carvalho de Meloc1ec5fe2010-05-15 20:45:31 -0300723
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300724 if (choice == annotate) {
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300725 struct hist_entry *he;
Arnaldo Carvalho de Meloc1ec5fe2010-05-15 20:45:31 -0300726do_annotate:
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300727 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300728 browser->selection->map->dso->annotate_warned = 1;
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300729 ui_helpline__puts("No vmlinux file found, can't "
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300730 "annotate with just a "
731 "kallsyms file");
732 continue;
733 }
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300734
735 he = hist_browser__selected_entry(browser);
736 if (he == NULL)
737 continue;
738
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300739 hist_entry__tui_annotate(he);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300740 } else if (choice == browse_map)
741 map__browse(browser->selection->map);
742 else if (choice == zoom_dso) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300743zoom_dso:
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300744 if (dso_filter) {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300745 pstack__remove(fstack, &dso_filter);
746zoom_out_dso:
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300747 ui_helpline__pop();
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300748 dso_filter = NULL;
749 } else {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300750 if (dso == NULL)
751 continue;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300752 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300753 dso->kernel ? "the Kernel" : dso->short_name);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300754 dso_filter = dso;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300755 pstack__push(fstack, &dso_filter);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300756 }
Arnaldo Carvalho de Melob09e0192010-05-11 11:10:15 -0300757 hists__filter_by_dso(self, dso_filter);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300758 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300759 dso_filter, thread_filter);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300760 hist_browser__reset(browser);
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300761 } else if (choice == zoom_thread) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300762zoom_thread:
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300763 if (thread_filter) {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300764 pstack__remove(fstack, &thread_filter);
765zoom_out_thread:
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300766 ui_helpline__pop();
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300767 thread_filter = NULL;
768 } else {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300769 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300770 thread->comm_set ? thread->comm : "",
771 thread->pid);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300772 thread_filter = thread;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300773 pstack__push(fstack, &thread_filter);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300774 }
Arnaldo Carvalho de Melob09e0192010-05-11 11:10:15 -0300775 hists__filter_by_thread(self, thread_filter);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300776 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300777 dso_filter, thread_filter);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300778 hist_browser__reset(browser);
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300779 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300780 }
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300781out_free_stack:
782 pstack__delete(fstack);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300783out:
784 hist_browser__delete(browser);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300785 return key;
786}
787
788int hists__tui_browse_tree(struct rb_root *self, const char *help)
789{
790 struct rb_node *first = rb_first(self), *nd = first, *next;
791 int key = 0;
792
793 while (nd) {
794 struct hists *hists = rb_entry(nd, struct hists, rb_node);
795 const char *ev_name = __event_name(hists->type, hists->config);
796
797 key = hists__browse(hists, help, ev_name);
798
799 if (is_exit_key(key))
800 break;
801
802 switch (key) {
803 case NEWT_KEY_TAB:
804 next = rb_next(nd);
805 if (next)
806 nd = next;
807 break;
808 case NEWT_KEY_UNTAB:
809 if (nd == first)
810 continue;
811 nd = rb_prev(nd);
812 default:
813 break;
814 }
815 }
816
817 return key;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300818}
819
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -0300820static void newt_suspend(void *d __used)
821{
822 newtSuspend();
823 raise(SIGTSTP);
824 newtResume();
825}
826
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300827void setup_browser(void)
828{
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300829 if (!isatty(1) || !use_browser || dump_trace) {
Arnaldo Carvalho de Melo62e34362010-05-26 13:22:26 -0300830 use_browser = 0;
Arnaldo Carvalho de Melo5d06e692010-05-20 22:01:10 -0300831 setup_pager();
832 return;
833 }
834
835 use_browser = 1;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300836 newtInit();
837 newtCls();
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -0300838 newtSetSuspendCallback(newt_suspend, NULL);
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300839 ui_helpline__puts(" ");
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -0300840 ui_browser__init();
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300841}
842
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300843void exit_browser(bool wait_for_ok)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300844{
Arnaldo Carvalho de Melo5d06e692010-05-20 22:01:10 -0300845 if (use_browser > 0) {
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300846 if (wait_for_ok) {
847 char title[] = "Fatal Error", ok[] = "Ok";
848 newtWinMessage(title, ok, browser__last_msg);
849 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300850 newtFinished();
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300851 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300852}
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300853
854static void hist_browser__refresh_dimensions(struct hist_browser *self)
855{
856 /* 3 == +/- toggle symbol before actual hist_entry rendering */
857 self->b.width = 3 + (hists__sort_list_width(self->hists) +
858 sizeof("[k]"));
859}
860
861static void hist_browser__reset(struct hist_browser *self)
862{
863 self->b.nr_entries = self->hists->nr_entries;
864 hist_browser__refresh_dimensions(self);
865 ui_browser__reset_index(&self->b);
866}
867
868static char tree__folded_sign(bool unfolded)
869{
870 return unfolded ? '-' : '+';
871}
872
873static char map_symbol__folded(const struct map_symbol *self)
874{
875 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
876}
877
878static char hist_entry__folded(const struct hist_entry *self)
879{
880 return map_symbol__folded(&self->ms);
881}
882
883static char callchain_list__folded(const struct callchain_list *self)
884{
885 return map_symbol__folded(&self->ms);
886}
887
888static bool map_symbol__toggle_fold(struct map_symbol *self)
889{
890 if (!self->has_children)
891 return false;
892
893 self->unfolded = !self->unfolded;
894 return true;
895}
896
897#define LEVEL_OFFSET_STEP 3
898
899static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
900 struct callchain_node *chain_node,
901 u64 total, int level,
902 unsigned short row,
903 off_t *row_offset,
904 bool *is_current_entry)
905{
906 struct rb_node *node;
907 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
908 u64 new_total, remaining;
909
910 if (callchain_param.mode == CHAIN_GRAPH_REL)
911 new_total = chain_node->children_hit;
912 else
913 new_total = total;
914
915 remaining = new_total;
916 node = rb_first(&chain_node->rb_root);
917 while (node) {
918 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
919 struct rb_node *next = rb_next(node);
920 u64 cumul = cumul_hits(child);
921 struct callchain_list *chain;
922 char folded_sign = ' ';
923 int first = true;
924 int extra_offset = 0;
925
926 remaining -= cumul;
927
928 list_for_each_entry(chain, &child->val, list) {
929 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
930 const char *str;
931 int color;
932 bool was_first = first;
933
934 if (first) {
935 first = false;
936 chain->ms.has_children = chain->list.next != &child->val ||
937 rb_first(&child->rb_root) != NULL;
938 } else {
939 extra_offset = LEVEL_OFFSET_STEP;
940 chain->ms.has_children = chain->list.next == &child->val &&
941 rb_first(&child->rb_root) != NULL;
942 }
943
944 folded_sign = callchain_list__folded(chain);
945 if (*row_offset != 0) {
946 --*row_offset;
947 goto do_next;
948 }
949
950 alloc_str = NULL;
951 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
952 if (was_first) {
953 double percent = cumul * 100.0 / new_total;
954
955 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
956 str = "Not enough memory!";
957 else
958 str = alloc_str;
959 }
960
961 color = HE_COLORSET_NORMAL;
962 width = self->b.width - (offset + extra_offset + 2);
963 if (ui_browser__is_current_entry(&self->b, row)) {
964 self->selection = &chain->ms;
965 color = HE_COLORSET_SELECTED;
966 *is_current_entry = true;
967 }
968
969 SLsmg_set_color(color);
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300970 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300971 slsmg_write_nstring(" ", offset + extra_offset);
972 slsmg_printf("%c ", folded_sign);
973 slsmg_write_nstring(str, width);
974 free(alloc_str);
975
976 if (++row == self->b.height)
977 goto out;
978do_next:
979 if (folded_sign == '+')
980 break;
981 }
982
983 if (folded_sign == '-') {
984 const int new_level = level + (extra_offset ? 2 : 1);
985 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
986 new_level, row, row_offset,
987 is_current_entry);
988 }
989 if (row == self->b.height)
990 goto out;
991 node = next;
992 }
993out:
994 return row - first_row;
995}
996
997static int hist_browser__show_callchain_node(struct hist_browser *self,
998 struct callchain_node *node,
999 int level, unsigned short row,
1000 off_t *row_offset,
1001 bool *is_current_entry)
1002{
1003 struct callchain_list *chain;
1004 int first_row = row,
1005 offset = level * LEVEL_OFFSET_STEP,
1006 width = self->b.width - offset;
1007 char folded_sign = ' ';
1008
1009 list_for_each_entry(chain, &node->val, list) {
1010 char ipstr[BITS_PER_LONG / 4 + 1], *s;
1011 int color;
1012 /*
1013 * FIXME: This should be moved to somewhere else,
1014 * probably when the callchain is created, so as not to
1015 * traverse it all over again
1016 */
1017 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
1018 folded_sign = callchain_list__folded(chain);
1019
1020 if (*row_offset != 0) {
1021 --*row_offset;
1022 continue;
1023 }
1024
1025 color = HE_COLORSET_NORMAL;
1026 if (ui_browser__is_current_entry(&self->b, row)) {
1027 self->selection = &chain->ms;
1028 color = HE_COLORSET_SELECTED;
1029 *is_current_entry = true;
1030 }
1031
1032 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001033 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001034 SLsmg_set_color(color);
1035 slsmg_write_nstring(" ", offset);
1036 slsmg_printf("%c ", folded_sign);
1037 slsmg_write_nstring(s, width - 2);
1038
1039 if (++row == self->b.height)
1040 goto out;
1041 }
1042
1043 if (folded_sign == '-')
1044 row += hist_browser__show_callchain_node_rb_tree(self, node,
1045 self->hists->stats.total_period,
1046 level + 1, row,
1047 row_offset,
1048 is_current_entry);
1049out:
1050 return row - first_row;
1051}
1052
1053static int hist_browser__show_callchain(struct hist_browser *self,
1054 struct rb_root *chain,
1055 int level, unsigned short row,
1056 off_t *row_offset,
1057 bool *is_current_entry)
1058{
1059 struct rb_node *nd;
1060 int first_row = row;
1061
1062 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1063 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1064
1065 row += hist_browser__show_callchain_node(self, node, level,
1066 row, row_offset,
1067 is_current_entry);
1068 if (row == self->b.height)
1069 break;
1070 }
1071
1072 return row - first_row;
1073}
1074
1075static int hist_browser__show_entry(struct hist_browser *self,
1076 struct hist_entry *entry,
1077 unsigned short row)
1078{
1079 char s[256];
1080 double percent;
1081 int printed = 0;
1082 int color, width = self->b.width;
1083 char folded_sign = ' ';
1084 bool current_entry = ui_browser__is_current_entry(&self->b, row);
1085 off_t row_offset = entry->row_offset;
1086
1087 if (current_entry) {
1088 self->he_selection = entry;
1089 self->selection = &entry->ms;
1090 }
1091
1092 if (symbol_conf.use_callchain) {
1093 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
1094 folded_sign = hist_entry__folded(entry);
1095 }
1096
1097 if (row_offset == 0) {
1098 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
1099 0, false, self->hists->stats.total_period);
1100 percent = (entry->period * 100.0) / self->hists->stats.total_period;
1101
1102 color = HE_COLORSET_SELECTED;
1103 if (!current_entry) {
1104 if (percent >= MIN_RED)
1105 color = HE_COLORSET_TOP;
1106 else if (percent >= MIN_GREEN)
1107 color = HE_COLORSET_MEDIUM;
1108 else
1109 color = HE_COLORSET_NORMAL;
1110 }
1111
1112 SLsmg_set_color(color);
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001113 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001114 if (symbol_conf.use_callchain) {
1115 slsmg_printf("%c ", folded_sign);
1116 width -= 2;
1117 }
1118 slsmg_write_nstring(s, width);
1119 ++row;
1120 ++printed;
1121 } else
1122 --row_offset;
1123
1124 if (folded_sign == '-' && row != self->b.height) {
1125 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
1126 1, row, &row_offset,
1127 &current_entry);
1128 if (current_entry)
1129 self->he_selection = entry;
1130 }
1131
1132 return printed;
1133}
1134
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -03001135static unsigned int hist_browser__refresh(struct ui_browser *self)
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001136{
1137 unsigned row = 0;
1138 struct rb_node *nd;
1139 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1140
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001141 if (self->top == NULL)
1142 self->top = rb_first(&hb->hists->entries);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001143
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001144 for (nd = self->top; nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001145 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1146
1147 if (h->filtered)
1148 continue;
1149
1150 row += hist_browser__show_entry(hb, h, row);
1151 if (row == self->height)
1152 break;
1153 }
1154
1155 return row;
1156}
1157
1158static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
1159{
1160 struct rb_node *nd = rb_first(&self->rb_root);
1161
1162 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1163 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1164 struct callchain_list *chain;
1165 int first = true;
1166
1167 list_for_each_entry(chain, &child->val, list) {
1168 if (first) {
1169 first = false;
1170 chain->ms.has_children = chain->list.next != &child->val ||
1171 rb_first(&child->rb_root) != NULL;
1172 } else
1173 chain->ms.has_children = chain->list.next == &child->val &&
1174 rb_first(&child->rb_root) != NULL;
1175 }
1176
1177 callchain_node__init_have_children_rb_tree(child);
1178 }
1179}
1180
1181static void callchain_node__init_have_children(struct callchain_node *self)
1182{
1183 struct callchain_list *chain;
1184
1185 list_for_each_entry(chain, &self->val, list)
1186 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
1187
1188 callchain_node__init_have_children_rb_tree(self);
1189}
1190
1191static void callchain__init_have_children(struct rb_root *self)
1192{
1193 struct rb_node *nd;
1194
1195 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
1196 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1197 callchain_node__init_have_children(node);
1198 }
1199}
1200
1201static void hist_entry__init_have_children(struct hist_entry *self)
1202{
1203 if (!self->init_have_children) {
1204 callchain__init_have_children(&self->sorted_chain);
1205 self->init_have_children = true;
1206 }
1207}
1208
1209static struct rb_node *hists__filter_entries(struct rb_node *nd)
1210{
1211 while (nd != NULL) {
1212 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1213 if (!h->filtered)
1214 return nd;
1215
1216 nd = rb_next(nd);
1217 }
1218
1219 return NULL;
1220}
1221
1222static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
1223{
1224 while (nd != NULL) {
1225 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1226 if (!h->filtered)
1227 return nd;
1228
1229 nd = rb_prev(nd);
1230 }
1231
1232 return NULL;
1233}
1234
1235static void ui_browser__hists_seek(struct ui_browser *self,
1236 off_t offset, int whence)
1237{
1238 struct hist_entry *h;
1239 struct rb_node *nd;
1240 bool first = true;
1241
1242 switch (whence) {
1243 case SEEK_SET:
1244 nd = hists__filter_entries(rb_first(self->entries));
1245 break;
1246 case SEEK_CUR:
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001247 nd = self->top;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001248 goto do_offset;
1249 case SEEK_END:
1250 nd = hists__filter_prev_entries(rb_last(self->entries));
1251 first = false;
1252 break;
1253 default:
1254 return;
1255 }
1256
1257 /*
1258 * Moves not relative to the first visible entry invalidates its
1259 * row_offset:
1260 */
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001261 h = rb_entry(self->top, struct hist_entry, rb_node);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001262 h->row_offset = 0;
1263
1264 /*
1265 * Here we have to check if nd is expanded (+), if it is we can't go
1266 * the next top level hist_entry, instead we must compute an offset of
1267 * what _not_ to show and not change the first visible entry.
1268 *
1269 * This offset increments when we are going from top to bottom and
1270 * decreases when we're going from bottom to top.
1271 *
1272 * As we don't have backpointers to the top level in the callchains
1273 * structure, we need to always print the whole hist_entry callchain,
1274 * skipping the first ones that are before the first visible entry
1275 * and stop when we printed enough lines to fill the screen.
1276 */
1277do_offset:
1278 if (offset > 0) {
1279 do {
1280 h = rb_entry(nd, struct hist_entry, rb_node);
1281 if (h->ms.unfolded) {
1282 u16 remaining = h->nr_rows - h->row_offset;
1283 if (offset > remaining) {
1284 offset -= remaining;
1285 h->row_offset = 0;
1286 } else {
1287 h->row_offset += offset;
1288 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001289 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001290 break;
1291 }
1292 }
1293 nd = hists__filter_entries(rb_next(nd));
1294 if (nd == NULL)
1295 break;
1296 --offset;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001297 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001298 } while (offset != 0);
1299 } else if (offset < 0) {
1300 while (1) {
1301 h = rb_entry(nd, struct hist_entry, rb_node);
1302 if (h->ms.unfolded) {
1303 if (first) {
1304 if (-offset > h->row_offset) {
1305 offset += h->row_offset;
1306 h->row_offset = 0;
1307 } else {
1308 h->row_offset += offset;
1309 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001310 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001311 break;
1312 }
1313 } else {
1314 if (-offset > h->nr_rows) {
1315 offset += h->nr_rows;
1316 h->row_offset = 0;
1317 } else {
1318 h->row_offset = h->nr_rows + offset;
1319 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001320 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001321 break;
1322 }
1323 }
1324 }
1325
1326 nd = hists__filter_prev_entries(rb_prev(nd));
1327 if (nd == NULL)
1328 break;
1329 ++offset;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001330 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001331 if (offset == 0) {
1332 /*
1333 * Last unfiltered hist_entry, check if it is
1334 * unfolded, if it is then we should have
1335 * row_offset at its last entry.
1336 */
1337 h = rb_entry(nd, struct hist_entry, rb_node);
1338 if (h->ms.unfolded)
1339 h->row_offset = h->nr_rows;
1340 break;
1341 }
1342 first = false;
1343 }
1344 } else {
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001345 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001346 h = rb_entry(nd, struct hist_entry, rb_node);
1347 h->row_offset = 0;
1348 }
1349}
1350
1351static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
1352{
1353 int n = 0;
1354 struct rb_node *nd;
1355
1356 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1357 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1358 struct callchain_list *chain;
1359 char folded_sign = ' '; /* No children */
1360
1361 list_for_each_entry(chain, &child->val, list) {
1362 ++n;
1363 /* We need this because we may not have children */
1364 folded_sign = callchain_list__folded(chain);
1365 if (folded_sign == '+')
1366 break;
1367 }
1368
1369 if (folded_sign == '-') /* Have children and they're unfolded */
1370 n += callchain_node__count_rows_rb_tree(child);
1371 }
1372
1373 return n;
1374}
1375
1376static int callchain_node__count_rows(struct callchain_node *node)
1377{
1378 struct callchain_list *chain;
1379 bool unfolded = false;
1380 int n = 0;
1381
1382 list_for_each_entry(chain, &node->val, list) {
1383 ++n;
1384 unfolded = chain->ms.unfolded;
1385 }
1386
1387 if (unfolded)
1388 n += callchain_node__count_rows_rb_tree(node);
1389
1390 return n;
1391}
1392
1393static int callchain__count_rows(struct rb_root *chain)
1394{
1395 struct rb_node *nd;
1396 int n = 0;
1397
1398 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1399 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1400 n += callchain_node__count_rows(node);
1401 }
1402
1403 return n;
1404}
1405
1406static bool hist_browser__toggle_fold(struct hist_browser *self)
1407{
1408 if (map_symbol__toggle_fold(self->selection)) {
1409 struct hist_entry *he = self->he_selection;
1410
1411 hist_entry__init_have_children(he);
1412 self->hists->nr_entries -= he->nr_rows;
1413
1414 if (he->ms.unfolded)
1415 he->nr_rows = callchain__count_rows(&he->sorted_chain);
1416 else
1417 he->nr_rows = 0;
1418 self->hists->nr_entries += he->nr_rows;
1419 self->b.nr_entries = self->hists->nr_entries;
1420
1421 return true;
1422 }
1423
1424 /* If it doesn't have children, no toggling performed */
1425 return false;
1426}
1427
1428static int hist_browser__run(struct hist_browser *self, const char *title,
1429 struct newtExitStruct *es)
1430{
1431 char str[256], unit;
1432 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
1433
1434 self->b.entries = &self->hists->entries;
1435 self->b.nr_entries = self->hists->nr_entries;
1436
1437 hist_browser__refresh_dimensions(self);
1438
1439 nr_events = convert_unit(nr_events, &unit);
1440 snprintf(str, sizeof(str), "Events: %lu%c ",
1441 nr_events, unit);
1442 newtDrawRootText(0, 0, str);
1443
1444 if (ui_browser__show(&self->b, title) < 0)
1445 return -1;
1446
1447 newtFormAddHotKey(self->b.form, 'A');
1448 newtFormAddHotKey(self->b.form, 'a');
1449 newtFormAddHotKey(self->b.form, '?');
1450 newtFormAddHotKey(self->b.form, 'h');
1451 newtFormAddHotKey(self->b.form, 'H');
1452 newtFormAddHotKey(self->b.form, 'd');
1453
1454 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
1455 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
1456 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1457
1458 while (1) {
1459 ui_browser__run(&self->b, es);
1460
1461 if (es->reason != NEWT_EXIT_HOTKEY)
1462 break;
1463 switch (es->u.key) {
1464 case 'd': { /* Debug */
1465 static int seq;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001466 struct hist_entry *h = rb_entry(self->b.top,
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001467 struct hist_entry, rb_node);
1468 ui_helpline__pop();
1469 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1470 seq++, self->b.nr_entries,
1471 self->hists->nr_entries,
1472 self->b.height,
1473 self->b.index,
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001474 self->b.top_idx,
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001475 h->row_offset, h->nr_rows);
1476 }
1477 continue;
1478 case NEWT_KEY_ENTER:
1479 if (hist_browser__toggle_fold(self))
1480 break;
1481 /* fall thru */
1482 default:
1483 return 0;
1484 }
1485 }
1486 return 0;
1487}