blob: 23f3b7d31bffdc2b86e8ec5492bc3829323e7ad4 [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 Melo55755362010-08-08 19:48:31 -030027#include "ui/helpline.h"
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030028
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -030029#if SLANG_VERSION < 20104
30#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
31#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
32#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
33 (char *)fg, (char *)bg)
34#else
35#define slsmg_printf SLsmg_printf
36#define slsmg_write_nstring SLsmg_write_nstring
37#define sltt_set_color SLtt_set_color
38#endif
39
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -030040newtComponent newt_form__new(void);
41
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030042struct ui_progress {
43 newtComponent form, scale;
44};
45
46struct ui_progress *ui_progress__new(const char *title, u64 total)
47{
48 struct ui_progress *self = malloc(sizeof(*self));
49
50 if (self != NULL) {
51 int cols;
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030052
53 if (use_browser <= 0)
54 return self;
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030055 newtGetScreenSize(&cols, NULL);
56 cols -= 4;
57 newtCenteredWindow(cols, 1, title);
58 self->form = newtForm(NULL, NULL, 0);
59 if (self->form == NULL)
60 goto out_free_self;
61 self->scale = newtScale(0, 0, cols, total);
62 if (self->scale == NULL)
63 goto out_free_form;
Arnaldo Carvalho de Melo7f826452010-05-10 10:51:25 -030064 newtFormAddComponent(self->form, self->scale);
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030065 newtRefresh();
66 }
67
68 return self;
69
70out_free_form:
71 newtFormDestroy(self->form);
72out_free_self:
73 free(self);
74 return NULL;
75}
76
77void ui_progress__update(struct ui_progress *self, u64 curr)
78{
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030079 /*
80 * FIXME: We should have a per UI backend way of showing progress,
81 * stdio will just show a percentage as NN%, etc.
82 */
83 if (use_browser <= 0)
84 return;
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030085 newtScaleSet(self->scale, curr);
86 newtRefresh();
87}
88
89void ui_progress__delete(struct ui_progress *self)
90{
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030091 if (use_browser > 0) {
92 newtFormDestroy(self->form);
93 newtPopWindow();
94 }
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030095 free(self);
96}
97
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -030098static int ui_entry__read(const char *title, char *bf, size_t size, int width)
99{
100 struct newtExitStruct es;
101 newtComponent form, entry;
102 const char *result;
103 int err = -1;
104
105 newtCenteredWindow(width, 1, title);
106 form = newtForm(NULL, NULL, 0);
107 if (form == NULL)
108 return -1;
109
110 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
111 if (entry == NULL)
112 goto out_free_form;
113
114 newtFormAddComponent(form, entry);
115 newtFormAddHotKey(form, NEWT_KEY_ENTER);
116 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
117 newtFormAddHotKey(form, NEWT_KEY_LEFT);
118 newtFormAddHotKey(form, CTRL('c'));
119 newtFormRun(form, &es);
120
121 if (result != NULL) {
122 strncpy(bf, result, size);
123 err = 0;
124 }
125out_free_form:
126 newtPopWindow();
127 newtFormDestroy(form);
128 return 0;
129}
130
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -0300131static char browser__last_msg[1024];
132
133int browser__show_help(const char *format, va_list ap)
134{
135 int ret;
136 static int backlog;
137
138 ret = vsnprintf(browser__last_msg + backlog,
139 sizeof(browser__last_msg) - backlog, format, ap);
140 backlog += ret;
141
142 if (browser__last_msg[backlog - 1] == '\n') {
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300143 ui_helpline__puts(browser__last_msg);
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -0300144 newtRefresh();
145 backlog = 0;
146 }
147
148 return ret;
149}
150
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300151static void newt_form__set_exit_keys(newtComponent self)
152{
Arnaldo Carvalho de Meloa308f3a2010-05-16 20:29:38 -0300153 newtFormAddHotKey(self, NEWT_KEY_LEFT);
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300154 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
155 newtFormAddHotKey(self, 'Q');
156 newtFormAddHotKey(self, 'q');
157 newtFormAddHotKey(self, CTRL('c'));
158}
159
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -0300160newtComponent newt_form__new(void)
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300161{
162 newtComponent self = newtForm(NULL, NULL, 0);
163 if (self)
164 newt_form__set_exit_keys(self);
165 return self;
166}
167
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300168static int popup_menu(int argc, char * const argv[])
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300169{
170 struct newtExitStruct es;
171 int i, rc = -1, max_len = 5;
172 newtComponent listbox, form = newt_form__new();
173
174 if (form == NULL)
175 return -1;
176
177 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
178 if (listbox == NULL)
179 goto out_destroy_form;
180
Arnaldo Carvalho de Melo7f826452010-05-10 10:51:25 -0300181 newtFormAddComponent(form, listbox);
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300182
183 for (i = 0; i < argc; ++i) {
184 int len = strlen(argv[i]);
185 if (len > max_len)
186 max_len = len;
187 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
188 goto out_destroy_form;
189 }
190
191 newtCenteredWindow(max_len, argc, NULL);
192 newtFormRun(form, &es);
193 rc = newtListboxGetCurrent(listbox) - NULL;
194 if (es.reason == NEWT_EXIT_HOTKEY)
195 rc = -1;
196 newtPopWindow();
197out_destroy_form:
198 newtFormDestroy(form);
199 return rc;
200}
201
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300202static int ui__help_window(const char *text)
203{
204 struct newtExitStruct es;
205 newtComponent tb, form = newt_form__new();
206 int rc = -1;
207 int max_len = 0, nr_lines = 0;
208 const char *t;
209
210 if (form == NULL)
211 return -1;
212
213 t = text;
214 while (1) {
215 const char *sep = strchr(t, '\n');
216 int len;
217
218 if (sep == NULL)
219 sep = strchr(t, '\0');
220 len = sep - t;
221 if (max_len < len)
222 max_len = len;
223 ++nr_lines;
224 if (*sep == '\0')
225 break;
226 t = sep + 1;
227 }
228
229 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
230 if (tb == NULL)
231 goto out_destroy_form;
232
233 newtTextboxSetText(tb, text);
234 newtFormAddComponent(form, tb);
235 newtCenteredWindow(max_len, nr_lines, NULL);
236 newtFormRun(form, &es);
237 newtPopWindow();
238 rc = 0;
239out_destroy_form:
240 newtFormDestroy(form);
241 return rc;
242}
243
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300244static bool dialog_yesno(const char *msg)
245{
246 /* newtWinChoice should really be accepting const char pointers... */
247 char yes[] = "Yes", no[] = "No";
Arnaldo Carvalho de Meloc0ed55d2010-04-05 12:04:23 -0300248 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300249}
250
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300251static void ui__error_window(const char *fmt, ...)
252{
253 va_list ap;
254
255 va_start(ap, fmt);
256 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
257 va_end(ap);
258}
259
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300260static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300261{
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300262 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
263 bool current_entry = ui_browser__is_current_entry(self, row);
264 int width = self->width;
265
266 if (ol->offset != -1) {
267 struct hist_entry *he = self->priv;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300268 struct symbol *sym = he->ms.sym;
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300269 int len = he->ms.sym->end - he->ms.sym->start;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300270 unsigned int hits = 0;
271 double percent = 0.0;
272 int color;
273 struct sym_priv *priv = symbol__priv(sym);
274 struct sym_ext *sym_ext = priv->ext;
275 struct sym_hist *h = priv->hist;
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300276 s64 offset = ol->offset;
277 struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300278
279 while (offset < (s64)len &&
280 (next == NULL || offset < next->offset)) {
281 if (sym_ext) {
282 percent += sym_ext[offset].percent;
283 } else
284 hits += h->ip[offset];
285
286 ++offset;
287 }
288
289 if (sym_ext == NULL && h->sum)
290 percent = 100.0 * hits / h->sum;
291
292 color = ui_browser__percent_color(percent, current_entry);
293 SLsmg_set_color(color);
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300294 slsmg_printf(" %7.2f ", percent);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300295 if (!current_entry)
296 SLsmg_set_color(HE_COLORSET_CODE);
297 } else {
298 int color = ui_browser__percent_color(0, current_entry);
299 SLsmg_set_color(color);
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300300 slsmg_write_nstring(" ", 9);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300301 }
302
303 SLsmg_write_char(':');
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300304 slsmg_write_nstring(" ", 8);
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300305 if (!*ol->line)
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300306 slsmg_write_nstring(" ", width - 18);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300307 else
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300308 slsmg_write_nstring(ol->line, width - 18);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300309}
310
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300311static char *callchain_list__sym_name(struct callchain_list *self,
312 char *bf, size_t bfsize)
313{
Arnaldo Carvalho de Melob3c9ac02010-03-24 16:40:18 -0300314 if (self->ms.sym)
315 return self->ms.sym->name;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300316
317 snprintf(bf, bfsize, "%#Lx", self->ip);
318 return bf;
319}
320
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300321int hist_entry__tui_annotate(struct hist_entry *self)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300322{
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300323 struct newtExitStruct es;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300324 struct objdump_line *pos, *n;
325 LIST_HEAD(head);
Arnaldo Carvalho de Melo43730982010-08-06 16:51:12 -0300326 struct ui_browser browser = {
327 .entries = &head,
328 .refresh = ui_browser__list_head_refresh,
329 .seek = ui_browser__list_head_seek,
330 .write = annotate_browser__write,
331 .priv = self,
332 };
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300333 int ret;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300334
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300335 if (self->ms.sym == NULL)
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300336 return -1;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300337
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300338 if (self->ms.map->dso->annotate_warned)
339 return -1;
340
341 if (hist_entry__annotate(self, &head) < 0) {
342 ui__error_window(browser__last_msg);
343 return -1;
344 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300345
Arnaldo Carvalho de Melo60553902010-05-15 20:40:34 -0300346 ui_helpline__push("Press <- or ESC to exit");
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300347
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300348 list_for_each_entry(pos, &head, node) {
349 size_t line_len = strlen(pos->line);
350 if (browser.width < line_len)
351 browser.width = line_len;
352 ++browser.nr_entries;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300353 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300354
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300355 browser.width += 18; /* Percentage */
Arnaldo Carvalho de Melo13f499f2010-06-21 18:04:02 -0300356 ui_browser__show(&browser, self->ms.sym->name);
Arnaldo Carvalho de Melob61b55e2010-07-21 17:55:32 -0300357 newtFormAddHotKey(browser.form, ' ');
Srikar Dronamraju0879b102010-06-29 23:02:26 +0530358 ret = ui_browser__run(&browser, &es);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300359 newtFormDestroy(browser.form);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300360 newtPopWindow();
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300361 list_for_each_entry_safe(pos, n, &head, node) {
362 list_del(&pos->node);
363 objdump_line__free(pos);
364 }
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300365 ui_helpline__pop();
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300366 return ret;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300367}
368
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300369/* -------------------------------------------------------------------- */
370
371struct map_browser {
372 struct ui_browser b;
373 struct map *map;
374 u16 namelen;
375 u8 addrlen;
376};
377
378static void map_browser__write(struct ui_browser *self, void *nd, int row)
379{
380 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
381 struct map_browser *mb = container_of(self, struct map_browser, b);
382 bool current_entry = ui_browser__is_current_entry(self, row);
383 int color = ui_browser__percent_color(0, current_entry);
384
385 SLsmg_set_color(color);
386 slsmg_printf("%*llx %*llx %c ",
387 mb->addrlen, sym->start, mb->addrlen, sym->end,
388 sym->binding == STB_GLOBAL ? 'g' :
389 sym->binding == STB_LOCAL ? 'l' : 'w');
390 slsmg_write_nstring(sym->name, mb->namelen);
391}
392
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300393/* FIXME uber-kludgy, see comment on cmd_report... */
394static u32 *symbol__browser_index(struct symbol *self)
395{
396 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
397}
398
399static int map_browser__search(struct map_browser *self)
400{
401 char target[512];
402 struct symbol *sym;
403 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
404
405 if (err)
406 return err;
407
408 if (target[0] == '0' && tolower(target[1]) == 'x') {
409 u64 addr = strtoull(target, NULL, 16);
410 sym = map__find_symbol(self->map, addr, NULL);
411 } else
412 sym = map__find_symbol_by_name(self->map, target, NULL);
413
414 if (sym != NULL) {
415 u32 *idx = symbol__browser_index(sym);
416
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300417 self->b.top = &sym->rb_node;
418 self->b.index = self->b.top_idx = *idx;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300419 } else
420 ui_helpline__fpush("%s not found!", target);
421
422 return 0;
423}
424
425static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
426{
427 if (ui_browser__show(&self->b, self->map->dso->long_name) < 0)
428 return -1;
429
430 ui_helpline__fpush("Press <- or ESC to exit, %s / to search",
431 verbose ? "" : "restart with -v to use");
432 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
433 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
434 if (verbose)
435 newtFormAddHotKey(self->b.form, '/');
436
437 while (1) {
438 ui_browser__run(&self->b, es);
439
440 if (es->reason != NEWT_EXIT_HOTKEY)
441 break;
442 if (verbose && es->u.key == '/')
443 map_browser__search(self);
444 else
445 break;
446 }
447
448 newtFormDestroy(self->b.form);
449 newtPopWindow();
450 ui_helpline__pop();
451 return 0;
452}
453
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300454static int map__browse(struct map *self)
455{
456 struct map_browser mb = {
457 .b = {
458 .entries = &self->dso->symbols[self->type],
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300459 .refresh = ui_browser__rb_tree_refresh,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300460 .seek = ui_browser__rb_tree_seek,
461 .write = map_browser__write,
462 },
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300463 .map = self,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300464 };
465 struct newtExitStruct es;
466 struct rb_node *nd;
467 char tmp[BITS_PER_LONG / 4];
468 u64 maxaddr = 0;
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300469
470 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
471 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
472
473 if (mb.namelen < pos->namelen)
474 mb.namelen = pos->namelen;
475 if (maxaddr < pos->end)
476 maxaddr = pos->end;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300477 if (verbose) {
478 u32 *idx = symbol__browser_index(pos);
479 *idx = mb.b.nr_entries;
480 }
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300481 ++mb.b.nr_entries;
482 }
483
484 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
485 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300486 return map_browser__run(&mb, &es);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300487}
488
489/* -------------------------------------------------------------------- */
490
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300491struct hist_browser {
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300492 struct ui_browser b;
493 struct hists *hists;
494 struct hist_entry *he_selection;
495 struct map_symbol *selection;
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300496};
497
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300498static void hist_browser__reset(struct hist_browser *self);
499static int hist_browser__run(struct hist_browser *self, const char *title,
500 struct newtExitStruct *es);
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300501static unsigned int hist_browser__refresh(struct ui_browser *self);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300502static void ui_browser__hists_seek(struct ui_browser *self,
503 off_t offset, int whence);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300504
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300505static struct hist_browser *hist_browser__new(struct hists *hists)
506{
507 struct hist_browser *self = zalloc(sizeof(*self));
508
509 if (self) {
510 self->hists = hists;
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300511 self->b.refresh = hist_browser__refresh;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300512 self->b.seek = ui_browser__hists_seek;
513 }
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300514
515 return self;
516}
517
518static void hist_browser__delete(struct hist_browser *self)
519{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300520 newtFormDestroy(self->b.form);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300521 newtPopWindow();
522 free(self);
523}
524
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300525static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300526{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300527 return self->he_selection;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300528}
529
530static struct thread *hist_browser__selected_thread(struct hist_browser *self)
531{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300532 return self->he_selection->thread;
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300533}
534
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300535static int hist_browser__title(char *bf, size_t size, const char *ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300536 const struct dso *dso, const struct thread *thread)
537{
538 int printed = 0;
539
540 if (thread)
541 printed += snprintf(bf + printed, size - printed,
542 "Thread: %s(%d)",
543 (thread->comm_set ? thread->comm : ""),
544 thread->pid);
545 if (dso)
546 printed += snprintf(bf + printed, size - printed,
547 "%sDSO: %s", thread ? " " : "",
548 dso->short_name);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300549 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300550}
551
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300552int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300553{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300554 struct hist_browser *browser = hist_browser__new(self);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300555 struct pstack *fstack;
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300556 const struct thread *thread_filter = NULL;
557 const struct dso *dso_filter = NULL;
558 struct newtExitStruct es;
559 char msg[160];
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300560 int key = -1;
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300561
562 if (browser == NULL)
563 return -1;
564
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300565 fstack = pstack__new(2);
566 if (fstack == NULL)
567 goto out;
568
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300569 ui_helpline__push(helpline);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300570
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300571 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300572 dso_filter, thread_filter);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300573
574 while (1) {
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300575 const struct thread *thread;
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300576 const struct dso *dso;
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300577 char *options[16];
578 int nr_options = 0, choice = 0, i,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300579 annotate = -2, zoom_dso = -2, zoom_thread = -2,
580 browse_map = -2;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300581
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300582 if (hist_browser__run(browser, msg, &es))
583 break;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300584
585 thread = hist_browser__selected_thread(browser);
586 dso = browser->selection->map ? browser->selection->map->dso : NULL;
587
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300588 if (es.reason == NEWT_EXIT_HOTKEY) {
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300589 key = es.u.key;
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300590
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300591 switch (key) {
592 case NEWT_KEY_F1:
593 goto do_help;
594 case NEWT_KEY_TAB:
595 case NEWT_KEY_UNTAB:
596 /*
597 * Exit the browser, let hists__browser_tree
598 * go to the next or previous
599 */
600 goto out_free_stack;
601 default:;
602 }
603
604 key = toupper(key);
605 switch (key) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300606 case 'A':
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300607 if (browser->selection->map == NULL &&
608 browser->selection->map->dso->annotate_warned)
609 continue;
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300610 goto do_annotate;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300611 case 'D':
612 goto zoom_dso;
613 case 'T':
614 goto zoom_thread;
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300615 case 'H':
616 case '?':
617do_help:
618 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
619 "<- Zoom out\n"
620 "a Annotate current symbol\n"
621 "h/?/F1 Show this window\n"
622 "d Zoom into current DSO\n"
623 "t Zoom into current Thread\n"
624 "q/CTRL+C Exit browser");
625 continue;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300626 default:;
627 }
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300628 if (is_exit_key(key)) {
629 if (key == NEWT_KEY_ESCAPE) {
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300630 if (dialog_yesno("Do you really want to exit?"))
631 break;
632 else
633 continue;
634 } else
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300635 break;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300636 }
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300637
638 if (es.u.key == NEWT_KEY_LEFT) {
639 const void *top;
640
641 if (pstack__empty(fstack))
642 continue;
643 top = pstack__pop(fstack);
644 if (top == &dso_filter)
645 goto zoom_out_dso;
646 if (top == &thread_filter)
647 goto zoom_out_thread;
648 continue;
649 }
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300650 }
651
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300652 if (browser->selection->sym != NULL &&
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300653 !browser->selection->map->dso->annotate_warned &&
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300654 asprintf(&options[nr_options], "Annotate %s",
655 browser->selection->sym->name) > 0)
656 annotate = nr_options++;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300657
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300658 if (thread != NULL &&
659 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300660 (thread_filter ? "out of" : "into"),
661 (thread->comm_set ? thread->comm : ""),
662 thread->pid) > 0)
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300663 zoom_thread = nr_options++;
664
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300665 if (dso != NULL &&
666 asprintf(&options[nr_options], "Zoom %s %s DSO",
667 (dso_filter ? "out of" : "into"),
668 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
669 zoom_dso = nr_options++;
670
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300671 if (browser->selection->map != NULL &&
672 asprintf(&options[nr_options], "Browse map details") > 0)
673 browse_map = nr_options++;
674
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300675 options[nr_options++] = (char *)"Exit";
676
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300677 choice = popup_menu(nr_options, options);
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300678
679 for (i = 0; i < nr_options - 1; ++i)
680 free(options[i]);
681
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300682 if (choice == nr_options - 1)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300683 break;
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300684
685 if (choice == -1)
686 continue;
Arnaldo Carvalho de Meloc1ec5fe2010-05-15 20:45:31 -0300687
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300688 if (choice == annotate) {
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300689 struct hist_entry *he;
Arnaldo Carvalho de Meloc1ec5fe2010-05-15 20:45:31 -0300690do_annotate:
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300691 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300692 browser->selection->map->dso->annotate_warned = 1;
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300693 ui_helpline__puts("No vmlinux file found, can't "
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300694 "annotate with just a "
695 "kallsyms file");
696 continue;
697 }
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300698
699 he = hist_browser__selected_entry(browser);
700 if (he == NULL)
701 continue;
702
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300703 hist_entry__tui_annotate(he);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300704 } else if (choice == browse_map)
705 map__browse(browser->selection->map);
706 else if (choice == zoom_dso) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300707zoom_dso:
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300708 if (dso_filter) {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300709 pstack__remove(fstack, &dso_filter);
710zoom_out_dso:
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300711 ui_helpline__pop();
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300712 dso_filter = NULL;
713 } else {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300714 if (dso == NULL)
715 continue;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300716 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300717 dso->kernel ? "the Kernel" : dso->short_name);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300718 dso_filter = dso;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300719 pstack__push(fstack, &dso_filter);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300720 }
Arnaldo Carvalho de Melob09e0192010-05-11 11:10:15 -0300721 hists__filter_by_dso(self, dso_filter);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300722 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300723 dso_filter, thread_filter);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300724 hist_browser__reset(browser);
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300725 } else if (choice == zoom_thread) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300726zoom_thread:
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300727 if (thread_filter) {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300728 pstack__remove(fstack, &thread_filter);
729zoom_out_thread:
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300730 ui_helpline__pop();
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300731 thread_filter = NULL;
732 } else {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300733 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300734 thread->comm_set ? thread->comm : "",
735 thread->pid);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300736 thread_filter = thread;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300737 pstack__push(fstack, &thread_filter);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300738 }
Arnaldo Carvalho de Melob09e0192010-05-11 11:10:15 -0300739 hists__filter_by_thread(self, thread_filter);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300740 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300741 dso_filter, thread_filter);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300742 hist_browser__reset(browser);
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300743 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300744 }
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300745out_free_stack:
746 pstack__delete(fstack);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300747out:
748 hist_browser__delete(browser);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300749 return key;
750}
751
752int hists__tui_browse_tree(struct rb_root *self, const char *help)
753{
754 struct rb_node *first = rb_first(self), *nd = first, *next;
755 int key = 0;
756
757 while (nd) {
758 struct hists *hists = rb_entry(nd, struct hists, rb_node);
759 const char *ev_name = __event_name(hists->type, hists->config);
760
761 key = hists__browse(hists, help, ev_name);
762
763 if (is_exit_key(key))
764 break;
765
766 switch (key) {
767 case NEWT_KEY_TAB:
768 next = rb_next(nd);
769 if (next)
770 nd = next;
771 break;
772 case NEWT_KEY_UNTAB:
773 if (nd == first)
774 continue;
775 nd = rb_prev(nd);
776 default:
777 break;
778 }
779 }
780
781 return key;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300782}
783
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -0300784static void newt_suspend(void *d __used)
785{
786 newtSuspend();
787 raise(SIGTSTP);
788 newtResume();
789}
790
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300791void setup_browser(void)
792{
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300793 if (!isatty(1) || !use_browser || dump_trace) {
Arnaldo Carvalho de Melo62e34362010-05-26 13:22:26 -0300794 use_browser = 0;
Arnaldo Carvalho de Melo5d06e692010-05-20 22:01:10 -0300795 setup_pager();
796 return;
797 }
798
799 use_browser = 1;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300800 newtInit();
801 newtCls();
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -0300802 newtSetSuspendCallback(newt_suspend, NULL);
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300803 ui_helpline__puts(" ");
Arnaldo Carvalho de Meloef8f34a2010-08-06 17:35:02 -0300804 ui_browser__init();
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300805}
806
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300807void exit_browser(bool wait_for_ok)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300808{
Arnaldo Carvalho de Melo5d06e692010-05-20 22:01:10 -0300809 if (use_browser > 0) {
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300810 if (wait_for_ok) {
811 char title[] = "Fatal Error", ok[] = "Ok";
812 newtWinMessage(title, ok, browser__last_msg);
813 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300814 newtFinished();
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300815 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300816}
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300817
818static void hist_browser__refresh_dimensions(struct hist_browser *self)
819{
820 /* 3 == +/- toggle symbol before actual hist_entry rendering */
821 self->b.width = 3 + (hists__sort_list_width(self->hists) +
822 sizeof("[k]"));
823}
824
825static void hist_browser__reset(struct hist_browser *self)
826{
827 self->b.nr_entries = self->hists->nr_entries;
828 hist_browser__refresh_dimensions(self);
829 ui_browser__reset_index(&self->b);
830}
831
832static char tree__folded_sign(bool unfolded)
833{
834 return unfolded ? '-' : '+';
835}
836
837static char map_symbol__folded(const struct map_symbol *self)
838{
839 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
840}
841
842static char hist_entry__folded(const struct hist_entry *self)
843{
844 return map_symbol__folded(&self->ms);
845}
846
847static char callchain_list__folded(const struct callchain_list *self)
848{
849 return map_symbol__folded(&self->ms);
850}
851
852static bool map_symbol__toggle_fold(struct map_symbol *self)
853{
854 if (!self->has_children)
855 return false;
856
857 self->unfolded = !self->unfolded;
858 return true;
859}
860
861#define LEVEL_OFFSET_STEP 3
862
863static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
864 struct callchain_node *chain_node,
865 u64 total, int level,
866 unsigned short row,
867 off_t *row_offset,
868 bool *is_current_entry)
869{
870 struct rb_node *node;
871 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
872 u64 new_total, remaining;
873
874 if (callchain_param.mode == CHAIN_GRAPH_REL)
875 new_total = chain_node->children_hit;
876 else
877 new_total = total;
878
879 remaining = new_total;
880 node = rb_first(&chain_node->rb_root);
881 while (node) {
882 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
883 struct rb_node *next = rb_next(node);
884 u64 cumul = cumul_hits(child);
885 struct callchain_list *chain;
886 char folded_sign = ' ';
887 int first = true;
888 int extra_offset = 0;
889
890 remaining -= cumul;
891
892 list_for_each_entry(chain, &child->val, list) {
893 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
894 const char *str;
895 int color;
896 bool was_first = first;
897
898 if (first) {
899 first = false;
900 chain->ms.has_children = chain->list.next != &child->val ||
901 rb_first(&child->rb_root) != NULL;
902 } else {
903 extra_offset = LEVEL_OFFSET_STEP;
904 chain->ms.has_children = chain->list.next == &child->val &&
905 rb_first(&child->rb_root) != NULL;
906 }
907
908 folded_sign = callchain_list__folded(chain);
909 if (*row_offset != 0) {
910 --*row_offset;
911 goto do_next;
912 }
913
914 alloc_str = NULL;
915 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
916 if (was_first) {
917 double percent = cumul * 100.0 / new_total;
918
919 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
920 str = "Not enough memory!";
921 else
922 str = alloc_str;
923 }
924
925 color = HE_COLORSET_NORMAL;
926 width = self->b.width - (offset + extra_offset + 2);
927 if (ui_browser__is_current_entry(&self->b, row)) {
928 self->selection = &chain->ms;
929 color = HE_COLORSET_SELECTED;
930 *is_current_entry = true;
931 }
932
933 SLsmg_set_color(color);
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300934 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300935 slsmg_write_nstring(" ", offset + extra_offset);
936 slsmg_printf("%c ", folded_sign);
937 slsmg_write_nstring(str, width);
938 free(alloc_str);
939
940 if (++row == self->b.height)
941 goto out;
942do_next:
943 if (folded_sign == '+')
944 break;
945 }
946
947 if (folded_sign == '-') {
948 const int new_level = level + (extra_offset ? 2 : 1);
949 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
950 new_level, row, row_offset,
951 is_current_entry);
952 }
953 if (row == self->b.height)
954 goto out;
955 node = next;
956 }
957out:
958 return row - first_row;
959}
960
961static int hist_browser__show_callchain_node(struct hist_browser *self,
962 struct callchain_node *node,
963 int level, unsigned short row,
964 off_t *row_offset,
965 bool *is_current_entry)
966{
967 struct callchain_list *chain;
968 int first_row = row,
969 offset = level * LEVEL_OFFSET_STEP,
970 width = self->b.width - offset;
971 char folded_sign = ' ';
972
973 list_for_each_entry(chain, &node->val, list) {
974 char ipstr[BITS_PER_LONG / 4 + 1], *s;
975 int color;
976 /*
977 * FIXME: This should be moved to somewhere else,
978 * probably when the callchain is created, so as not to
979 * traverse it all over again
980 */
981 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
982 folded_sign = callchain_list__folded(chain);
983
984 if (*row_offset != 0) {
985 --*row_offset;
986 continue;
987 }
988
989 color = HE_COLORSET_NORMAL;
990 if (ui_browser__is_current_entry(&self->b, row)) {
991 self->selection = &chain->ms;
992 color = HE_COLORSET_SELECTED;
993 *is_current_entry = true;
994 }
995
996 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -0300997 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300998 SLsmg_set_color(color);
999 slsmg_write_nstring(" ", offset);
1000 slsmg_printf("%c ", folded_sign);
1001 slsmg_write_nstring(s, width - 2);
1002
1003 if (++row == self->b.height)
1004 goto out;
1005 }
1006
1007 if (folded_sign == '-')
1008 row += hist_browser__show_callchain_node_rb_tree(self, node,
1009 self->hists->stats.total_period,
1010 level + 1, row,
1011 row_offset,
1012 is_current_entry);
1013out:
1014 return row - first_row;
1015}
1016
1017static int hist_browser__show_callchain(struct hist_browser *self,
1018 struct rb_root *chain,
1019 int level, unsigned short row,
1020 off_t *row_offset,
1021 bool *is_current_entry)
1022{
1023 struct rb_node *nd;
1024 int first_row = row;
1025
1026 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1027 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1028
1029 row += hist_browser__show_callchain_node(self, node, level,
1030 row, row_offset,
1031 is_current_entry);
1032 if (row == self->b.height)
1033 break;
1034 }
1035
1036 return row - first_row;
1037}
1038
1039static int hist_browser__show_entry(struct hist_browser *self,
1040 struct hist_entry *entry,
1041 unsigned short row)
1042{
1043 char s[256];
1044 double percent;
1045 int printed = 0;
1046 int color, width = self->b.width;
1047 char folded_sign = ' ';
1048 bool current_entry = ui_browser__is_current_entry(&self->b, row);
1049 off_t row_offset = entry->row_offset;
1050
1051 if (current_entry) {
1052 self->he_selection = entry;
1053 self->selection = &entry->ms;
1054 }
1055
1056 if (symbol_conf.use_callchain) {
1057 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
1058 folded_sign = hist_entry__folded(entry);
1059 }
1060
1061 if (row_offset == 0) {
1062 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
1063 0, false, self->hists->stats.total_period);
1064 percent = (entry->period * 100.0) / self->hists->stats.total_period;
1065
1066 color = HE_COLORSET_SELECTED;
1067 if (!current_entry) {
1068 if (percent >= MIN_RED)
1069 color = HE_COLORSET_TOP;
1070 else if (percent >= MIN_GREEN)
1071 color = HE_COLORSET_MEDIUM;
1072 else
1073 color = HE_COLORSET_NORMAL;
1074 }
1075
1076 SLsmg_set_color(color);
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001077 SLsmg_gotorc(self->b.y + row, self->b.x);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001078 if (symbol_conf.use_callchain) {
1079 slsmg_printf("%c ", folded_sign);
1080 width -= 2;
1081 }
1082 slsmg_write_nstring(s, width);
1083 ++row;
1084 ++printed;
1085 } else
1086 --row_offset;
1087
1088 if (folded_sign == '-' && row != self->b.height) {
1089 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
1090 1, row, &row_offset,
1091 &current_entry);
1092 if (current_entry)
1093 self->he_selection = entry;
1094 }
1095
1096 return printed;
1097}
1098
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -03001099static unsigned int hist_browser__refresh(struct ui_browser *self)
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001100{
1101 unsigned row = 0;
1102 struct rb_node *nd;
1103 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1104
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001105 if (self->top == NULL)
1106 self->top = rb_first(&hb->hists->entries);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001107
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001108 for (nd = self->top; nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001109 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1110
1111 if (h->filtered)
1112 continue;
1113
1114 row += hist_browser__show_entry(hb, h, row);
1115 if (row == self->height)
1116 break;
1117 }
1118
1119 return row;
1120}
1121
1122static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
1123{
1124 struct rb_node *nd = rb_first(&self->rb_root);
1125
1126 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1127 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1128 struct callchain_list *chain;
1129 int first = true;
1130
1131 list_for_each_entry(chain, &child->val, list) {
1132 if (first) {
1133 first = false;
1134 chain->ms.has_children = chain->list.next != &child->val ||
1135 rb_first(&child->rb_root) != NULL;
1136 } else
1137 chain->ms.has_children = chain->list.next == &child->val &&
1138 rb_first(&child->rb_root) != NULL;
1139 }
1140
1141 callchain_node__init_have_children_rb_tree(child);
1142 }
1143}
1144
1145static void callchain_node__init_have_children(struct callchain_node *self)
1146{
1147 struct callchain_list *chain;
1148
1149 list_for_each_entry(chain, &self->val, list)
1150 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
1151
1152 callchain_node__init_have_children_rb_tree(self);
1153}
1154
1155static void callchain__init_have_children(struct rb_root *self)
1156{
1157 struct rb_node *nd;
1158
1159 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
1160 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1161 callchain_node__init_have_children(node);
1162 }
1163}
1164
1165static void hist_entry__init_have_children(struct hist_entry *self)
1166{
1167 if (!self->init_have_children) {
1168 callchain__init_have_children(&self->sorted_chain);
1169 self->init_have_children = true;
1170 }
1171}
1172
1173static struct rb_node *hists__filter_entries(struct rb_node *nd)
1174{
1175 while (nd != NULL) {
1176 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1177 if (!h->filtered)
1178 return nd;
1179
1180 nd = rb_next(nd);
1181 }
1182
1183 return NULL;
1184}
1185
1186static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
1187{
1188 while (nd != NULL) {
1189 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1190 if (!h->filtered)
1191 return nd;
1192
1193 nd = rb_prev(nd);
1194 }
1195
1196 return NULL;
1197}
1198
1199static void ui_browser__hists_seek(struct ui_browser *self,
1200 off_t offset, int whence)
1201{
1202 struct hist_entry *h;
1203 struct rb_node *nd;
1204 bool first = true;
1205
1206 switch (whence) {
1207 case SEEK_SET:
1208 nd = hists__filter_entries(rb_first(self->entries));
1209 break;
1210 case SEEK_CUR:
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001211 nd = self->top;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001212 goto do_offset;
1213 case SEEK_END:
1214 nd = hists__filter_prev_entries(rb_last(self->entries));
1215 first = false;
1216 break;
1217 default:
1218 return;
1219 }
1220
1221 /*
1222 * Moves not relative to the first visible entry invalidates its
1223 * row_offset:
1224 */
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001225 h = rb_entry(self->top, struct hist_entry, rb_node);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001226 h->row_offset = 0;
1227
1228 /*
1229 * Here we have to check if nd is expanded (+), if it is we can't go
1230 * the next top level hist_entry, instead we must compute an offset of
1231 * what _not_ to show and not change the first visible entry.
1232 *
1233 * This offset increments when we are going from top to bottom and
1234 * decreases when we're going from bottom to top.
1235 *
1236 * As we don't have backpointers to the top level in the callchains
1237 * structure, we need to always print the whole hist_entry callchain,
1238 * skipping the first ones that are before the first visible entry
1239 * and stop when we printed enough lines to fill the screen.
1240 */
1241do_offset:
1242 if (offset > 0) {
1243 do {
1244 h = rb_entry(nd, struct hist_entry, rb_node);
1245 if (h->ms.unfolded) {
1246 u16 remaining = h->nr_rows - h->row_offset;
1247 if (offset > remaining) {
1248 offset -= remaining;
1249 h->row_offset = 0;
1250 } else {
1251 h->row_offset += offset;
1252 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001253 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001254 break;
1255 }
1256 }
1257 nd = hists__filter_entries(rb_next(nd));
1258 if (nd == NULL)
1259 break;
1260 --offset;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001261 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001262 } while (offset != 0);
1263 } else if (offset < 0) {
1264 while (1) {
1265 h = rb_entry(nd, struct hist_entry, rb_node);
1266 if (h->ms.unfolded) {
1267 if (first) {
1268 if (-offset > h->row_offset) {
1269 offset += h->row_offset;
1270 h->row_offset = 0;
1271 } else {
1272 h->row_offset += offset;
1273 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001274 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001275 break;
1276 }
1277 } else {
1278 if (-offset > h->nr_rows) {
1279 offset += h->nr_rows;
1280 h->row_offset = 0;
1281 } else {
1282 h->row_offset = h->nr_rows + offset;
1283 offset = 0;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001284 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001285 break;
1286 }
1287 }
1288 }
1289
1290 nd = hists__filter_prev_entries(rb_prev(nd));
1291 if (nd == NULL)
1292 break;
1293 ++offset;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001294 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001295 if (offset == 0) {
1296 /*
1297 * Last unfiltered hist_entry, check if it is
1298 * unfolded, if it is then we should have
1299 * row_offset at its last entry.
1300 */
1301 h = rb_entry(nd, struct hist_entry, rb_node);
1302 if (h->ms.unfolded)
1303 h->row_offset = h->nr_rows;
1304 break;
1305 }
1306 first = false;
1307 }
1308 } else {
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001309 self->top = nd;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001310 h = rb_entry(nd, struct hist_entry, rb_node);
1311 h->row_offset = 0;
1312 }
1313}
1314
1315static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
1316{
1317 int n = 0;
1318 struct rb_node *nd;
1319
1320 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1321 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1322 struct callchain_list *chain;
1323 char folded_sign = ' '; /* No children */
1324
1325 list_for_each_entry(chain, &child->val, list) {
1326 ++n;
1327 /* We need this because we may not have children */
1328 folded_sign = callchain_list__folded(chain);
1329 if (folded_sign == '+')
1330 break;
1331 }
1332
1333 if (folded_sign == '-') /* Have children and they're unfolded */
1334 n += callchain_node__count_rows_rb_tree(child);
1335 }
1336
1337 return n;
1338}
1339
1340static int callchain_node__count_rows(struct callchain_node *node)
1341{
1342 struct callchain_list *chain;
1343 bool unfolded = false;
1344 int n = 0;
1345
1346 list_for_each_entry(chain, &node->val, list) {
1347 ++n;
1348 unfolded = chain->ms.unfolded;
1349 }
1350
1351 if (unfolded)
1352 n += callchain_node__count_rows_rb_tree(node);
1353
1354 return n;
1355}
1356
1357static int callchain__count_rows(struct rb_root *chain)
1358{
1359 struct rb_node *nd;
1360 int n = 0;
1361
1362 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1363 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1364 n += callchain_node__count_rows(node);
1365 }
1366
1367 return n;
1368}
1369
1370static bool hist_browser__toggle_fold(struct hist_browser *self)
1371{
1372 if (map_symbol__toggle_fold(self->selection)) {
1373 struct hist_entry *he = self->he_selection;
1374
1375 hist_entry__init_have_children(he);
1376 self->hists->nr_entries -= he->nr_rows;
1377
1378 if (he->ms.unfolded)
1379 he->nr_rows = callchain__count_rows(&he->sorted_chain);
1380 else
1381 he->nr_rows = 0;
1382 self->hists->nr_entries += he->nr_rows;
1383 self->b.nr_entries = self->hists->nr_entries;
1384
1385 return true;
1386 }
1387
1388 /* If it doesn't have children, no toggling performed */
1389 return false;
1390}
1391
1392static int hist_browser__run(struct hist_browser *self, const char *title,
1393 struct newtExitStruct *es)
1394{
1395 char str[256], unit;
1396 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
1397
1398 self->b.entries = &self->hists->entries;
1399 self->b.nr_entries = self->hists->nr_entries;
1400
1401 hist_browser__refresh_dimensions(self);
1402
1403 nr_events = convert_unit(nr_events, &unit);
1404 snprintf(str, sizeof(str), "Events: %lu%c ",
1405 nr_events, unit);
1406 newtDrawRootText(0, 0, str);
1407
1408 if (ui_browser__show(&self->b, title) < 0)
1409 return -1;
1410
1411 newtFormAddHotKey(self->b.form, 'A');
1412 newtFormAddHotKey(self->b.form, 'a');
1413 newtFormAddHotKey(self->b.form, '?');
1414 newtFormAddHotKey(self->b.form, 'h');
1415 newtFormAddHotKey(self->b.form, 'H');
1416 newtFormAddHotKey(self->b.form, 'd');
1417
1418 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
1419 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
1420 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1421
1422 while (1) {
1423 ui_browser__run(&self->b, es);
1424
1425 if (es->reason != NEWT_EXIT_HOTKEY)
1426 break;
1427 switch (es->u.key) {
1428 case 'd': { /* Debug */
1429 static int seq;
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001430 struct hist_entry *h = rb_entry(self->b.top,
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001431 struct hist_entry, rb_node);
1432 ui_helpline__pop();
1433 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1434 seq++, self->b.nr_entries,
1435 self->hists->nr_entries,
1436 self->b.height,
1437 self->b.index,
Arnaldo Carvalho de Melod247eb62010-08-07 13:56:04 -03001438 self->b.top_idx,
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001439 h->row_offset, h->nr_rows);
1440 }
1441 continue;
1442 case NEWT_KEY_ENTER:
1443 if (hist_browser__toggle_fold(self))
1444 break;
1445 /* fall thru */
1446 default:
1447 return 0;
1448 }
1449 }
1450 return 0;
1451}