blob: e2deae0704d702444edbe0e4cf55b644c74f80fd [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"
26
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -030027#if SLANG_VERSION < 20104
28#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
29#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
30#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
31 (char *)fg, (char *)bg)
32#else
33#define slsmg_printf SLsmg_printf
34#define slsmg_write_nstring SLsmg_write_nstring
35#define sltt_set_color SLtt_set_color
36#endif
37
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030038struct ui_progress {
39 newtComponent form, scale;
40};
41
42struct ui_progress *ui_progress__new(const char *title, u64 total)
43{
44 struct ui_progress *self = malloc(sizeof(*self));
45
46 if (self != NULL) {
47 int cols;
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030048
49 if (use_browser <= 0)
50 return self;
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030051 newtGetScreenSize(&cols, NULL);
52 cols -= 4;
53 newtCenteredWindow(cols, 1, title);
54 self->form = newtForm(NULL, NULL, 0);
55 if (self->form == NULL)
56 goto out_free_self;
57 self->scale = newtScale(0, 0, cols, total);
58 if (self->scale == NULL)
59 goto out_free_form;
Arnaldo Carvalho de Melo7f826452010-05-10 10:51:25 -030060 newtFormAddComponent(self->form, self->scale);
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030061 newtRefresh();
62 }
63
64 return self;
65
66out_free_form:
67 newtFormDestroy(self->form);
68out_free_self:
69 free(self);
70 return NULL;
71}
72
73void ui_progress__update(struct ui_progress *self, u64 curr)
74{
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030075 /*
76 * FIXME: We should have a per UI backend way of showing progress,
77 * stdio will just show a percentage as NN%, etc.
78 */
79 if (use_browser <= 0)
80 return;
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030081 newtScaleSet(self->scale, curr);
82 newtRefresh();
83}
84
85void ui_progress__delete(struct ui_progress *self)
86{
Arnaldo Carvalho de Melo1d90f2e2010-06-09 07:13:16 -030087 if (use_browser > 0) {
88 newtFormDestroy(self->form);
89 newtPopWindow();
90 }
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -030091 free(self);
92}
93
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -030094static void ui_helpline__pop(void)
95{
96 newtPopHelpLine();
97}
98
99static void ui_helpline__push(const char *msg)
100{
101 newtPushHelpLine(msg);
102}
103
104static void ui_helpline__vpush(const char *fmt, va_list ap)
105{
106 char *s;
107
108 if (vasprintf(&s, fmt, ap) < 0)
109 vfprintf(stderr, fmt, ap);
110 else {
111 ui_helpline__push(s);
112 free(s);
113 }
114}
115
116static void ui_helpline__fpush(const char *fmt, ...)
117{
118 va_list ap;
119
120 va_start(ap, fmt);
121 ui_helpline__vpush(fmt, ap);
122 va_end(ap);
123}
124
125static void ui_helpline__puts(const char *msg)
126{
127 ui_helpline__pop();
128 ui_helpline__push(msg);
129}
130
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300131static int ui_entry__read(const char *title, char *bf, size_t size, int width)
132{
133 struct newtExitStruct es;
134 newtComponent form, entry;
135 const char *result;
136 int err = -1;
137
138 newtCenteredWindow(width, 1, title);
139 form = newtForm(NULL, NULL, 0);
140 if (form == NULL)
141 return -1;
142
143 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
144 if (entry == NULL)
145 goto out_free_form;
146
147 newtFormAddComponent(form, entry);
148 newtFormAddHotKey(form, NEWT_KEY_ENTER);
149 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
150 newtFormAddHotKey(form, NEWT_KEY_LEFT);
151 newtFormAddHotKey(form, CTRL('c'));
152 newtFormRun(form, &es);
153
154 if (result != NULL) {
155 strncpy(bf, result, size);
156 err = 0;
157 }
158out_free_form:
159 newtPopWindow();
160 newtFormDestroy(form);
161 return 0;
162}
163
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -0300164static char browser__last_msg[1024];
165
166int browser__show_help(const char *format, va_list ap)
167{
168 int ret;
169 static int backlog;
170
171 ret = vsnprintf(browser__last_msg + backlog,
172 sizeof(browser__last_msg) - backlog, format, ap);
173 backlog += ret;
174
175 if (browser__last_msg[backlog - 1] == '\n') {
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300176 ui_helpline__puts(browser__last_msg);
Arnaldo Carvalho de Melo5f4d3f82010-03-26 21:16:22 -0300177 newtRefresh();
178 backlog = 0;
179 }
180
181 return ret;
182}
183
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300184static void newt_form__set_exit_keys(newtComponent self)
185{
Arnaldo Carvalho de Meloa308f3a2010-05-16 20:29:38 -0300186 newtFormAddHotKey(self, NEWT_KEY_LEFT);
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300187 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
188 newtFormAddHotKey(self, 'Q');
189 newtFormAddHotKey(self, 'q');
190 newtFormAddHotKey(self, CTRL('c'));
191}
192
193static newtComponent newt_form__new(void)
194{
195 newtComponent self = newtForm(NULL, NULL, 0);
196 if (self)
197 newt_form__set_exit_keys(self);
198 return self;
199}
200
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300201static int popup_menu(int argc, char * const argv[])
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300202{
203 struct newtExitStruct es;
204 int i, rc = -1, max_len = 5;
205 newtComponent listbox, form = newt_form__new();
206
207 if (form == NULL)
208 return -1;
209
210 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
211 if (listbox == NULL)
212 goto out_destroy_form;
213
Arnaldo Carvalho de Melo7f826452010-05-10 10:51:25 -0300214 newtFormAddComponent(form, listbox);
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300215
216 for (i = 0; i < argc; ++i) {
217 int len = strlen(argv[i]);
218 if (len > max_len)
219 max_len = len;
220 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
221 goto out_destroy_form;
222 }
223
224 newtCenteredWindow(max_len, argc, NULL);
225 newtFormRun(form, &es);
226 rc = newtListboxGetCurrent(listbox) - NULL;
227 if (es.reason == NEWT_EXIT_HOTKEY)
228 rc = -1;
229 newtPopWindow();
230out_destroy_form:
231 newtFormDestroy(form);
232 return rc;
233}
234
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300235static int ui__help_window(const char *text)
236{
237 struct newtExitStruct es;
238 newtComponent tb, form = newt_form__new();
239 int rc = -1;
240 int max_len = 0, nr_lines = 0;
241 const char *t;
242
243 if (form == NULL)
244 return -1;
245
246 t = text;
247 while (1) {
248 const char *sep = strchr(t, '\n');
249 int len;
250
251 if (sep == NULL)
252 sep = strchr(t, '\0');
253 len = sep - t;
254 if (max_len < len)
255 max_len = len;
256 ++nr_lines;
257 if (*sep == '\0')
258 break;
259 t = sep + 1;
260 }
261
262 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
263 if (tb == NULL)
264 goto out_destroy_form;
265
266 newtTextboxSetText(tb, text);
267 newtFormAddComponent(form, tb);
268 newtCenteredWindow(max_len, nr_lines, NULL);
269 newtFormRun(form, &es);
270 newtPopWindow();
271 rc = 0;
272out_destroy_form:
273 newtFormDestroy(form);
274 return rc;
275}
276
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300277static bool dialog_yesno(const char *msg)
278{
279 /* newtWinChoice should really be accepting const char pointers... */
280 char yes[] = "Yes", no[] = "No";
Arnaldo Carvalho de Meloc0ed55d2010-04-05 12:04:23 -0300281 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300282}
283
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300284static void ui__error_window(const char *fmt, ...)
285{
286 va_list ap;
287
288 va_start(ap, fmt);
289 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
290 va_end(ap);
291}
292
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300293#define HE_COLORSET_TOP 50
294#define HE_COLORSET_MEDIUM 51
295#define HE_COLORSET_NORMAL 52
296#define HE_COLORSET_SELECTED 53
297#define HE_COLORSET_CODE 54
298
299static int ui_browser__percent_color(double percent, bool current)
300{
301 if (current)
302 return HE_COLORSET_SELECTED;
303 if (percent >= MIN_RED)
304 return HE_COLORSET_TOP;
305 if (percent >= MIN_GREEN)
306 return HE_COLORSET_MEDIUM;
307 return HE_COLORSET_NORMAL;
308}
309
310struct ui_browser {
311 newtComponent form, sb;
312 u64 index, first_visible_entry_idx;
313 void *first_visible_entry, *entries;
314 u16 top, left, width, height;
315 void *priv;
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300316 unsigned int (*refresh)(struct ui_browser *self);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300317 void (*write)(struct ui_browser *self, void *entry, int row);
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300318 void (*seek)(struct ui_browser *self,
319 off_t offset, int whence);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300320 u32 nr_entries;
321};
322
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300323static void ui_browser__list_head_seek(struct ui_browser *self,
324 off_t offset, int whence)
325{
326 struct list_head *head = self->entries;
327 struct list_head *pos;
328
329 switch (whence) {
330 case SEEK_SET:
331 pos = head->next;
332 break;
333 case SEEK_CUR:
334 pos = self->first_visible_entry;
335 break;
336 case SEEK_END:
337 pos = head->prev;
338 break;
339 default:
340 return;
341 }
342
343 if (offset > 0) {
344 while (offset-- != 0)
345 pos = pos->next;
346 } else {
347 while (offset++ != 0)
348 pos = pos->prev;
349 }
350
351 self->first_visible_entry = pos;
352}
353
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300354static void ui_browser__rb_tree_seek(struct ui_browser *self,
355 off_t offset, int whence)
356{
357 struct rb_root *root = self->entries;
358 struct rb_node *nd;
359
360 switch (whence) {
361 case SEEK_SET:
362 nd = rb_first(root);
363 break;
364 case SEEK_CUR:
365 nd = self->first_visible_entry;
366 break;
367 case SEEK_END:
368 nd = rb_last(root);
369 break;
370 default:
371 return;
372 }
373
374 if (offset > 0) {
375 while (offset-- != 0)
376 nd = rb_next(nd);
377 } else {
378 while (offset++ != 0)
379 nd = rb_prev(nd);
380 }
381
382 self->first_visible_entry = nd;
383}
384
385static unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
386{
387 struct rb_node *nd;
388 int row = 0;
389
390 if (self->first_visible_entry == NULL)
391 self->first_visible_entry = rb_first(self->entries);
392
393 nd = self->first_visible_entry;
394
395 while (nd != NULL) {
396 SLsmg_gotorc(self->top + row, self->left);
397 self->write(self, nd, row);
398 if (++row == self->height)
399 break;
400 nd = rb_next(nd);
401 }
402
403 return row;
404}
405
Arnaldo Carvalho de Melo8c694d22010-06-21 12:44:42 -0300406static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
407{
408 return (self->first_visible_entry_idx + row) == self->index;
409}
410
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300411static void ui_browser__refresh_dimensions(struct ui_browser *self)
412{
413 int cols, rows;
414 newtGetScreenSize(&cols, &rows);
415
416 if (self->width > cols - 4)
417 self->width = cols - 4;
418 self->height = rows - 5;
419 if (self->height > self->nr_entries)
420 self->height = self->nr_entries;
421 self->top = (rows - self->height) / 2;
422 self->left = (cols - self->width) / 2;
423}
424
425static void ui_browser__reset_index(struct ui_browser *self)
426{
Arnaldo Carvalho de Melo8c694d22010-06-21 12:44:42 -0300427 self->index = self->first_visible_entry_idx = 0;
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300428 self->seek(self, 0, SEEK_SET);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300429}
430
Arnaldo Carvalho de Melo13f499f2010-06-21 18:04:02 -0300431static int ui_browser__show(struct ui_browser *self, const char *title)
432{
Arnaldo Carvalho de Melo63160f72010-07-26 13:47:15 -0300433 if (self->form != NULL) {
434 newtFormDestroy(self->form);
435 newtPopWindow();
436 }
Arnaldo Carvalho de Melo13f499f2010-06-21 18:04:02 -0300437 ui_browser__refresh_dimensions(self);
Arnaldo Carvalho de Melo8d8c3692010-07-26 14:08:48 -0300438 newtCenteredWindow(self->width, self->height, title);
Arnaldo Carvalho de Melo13f499f2010-06-21 18:04:02 -0300439 self->form = newt_form__new();
440 if (self->form == NULL)
441 return -1;
442
Arnaldo Carvalho de Melo8d8c3692010-07-26 14:08:48 -0300443 self->sb = newtVerticalScrollbar(self->width, 0, self->height,
Arnaldo Carvalho de Melo13f499f2010-06-21 18:04:02 -0300444 HE_COLORSET_NORMAL,
445 HE_COLORSET_SELECTED);
446 if (self->sb == NULL)
447 return -1;
448
449 newtFormAddHotKey(self->form, NEWT_KEY_UP);
450 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
451 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
452 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
453 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
454 newtFormAddHotKey(self->form, NEWT_KEY_END);
455 newtFormAddComponent(self->form, self->sb);
456 return 0;
457}
458
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300459static int objdump_line__show(struct objdump_line *self, struct list_head *head,
460 int width, struct hist_entry *he, int len,
461 bool current_entry)
462{
463 if (self->offset != -1) {
464 struct symbol *sym = he->ms.sym;
465 unsigned int hits = 0;
466 double percent = 0.0;
467 int color;
468 struct sym_priv *priv = symbol__priv(sym);
469 struct sym_ext *sym_ext = priv->ext;
470 struct sym_hist *h = priv->hist;
471 s64 offset = self->offset;
472 struct objdump_line *next = objdump__get_next_ip_line(head, self);
473
474 while (offset < (s64)len &&
475 (next == NULL || offset < next->offset)) {
476 if (sym_ext) {
477 percent += sym_ext[offset].percent;
478 } else
479 hits += h->ip[offset];
480
481 ++offset;
482 }
483
484 if (sym_ext == NULL && h->sum)
485 percent = 100.0 * hits / h->sum;
486
487 color = ui_browser__percent_color(percent, current_entry);
488 SLsmg_set_color(color);
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300489 slsmg_printf(" %7.2f ", percent);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300490 if (!current_entry)
491 SLsmg_set_color(HE_COLORSET_CODE);
492 } else {
493 int color = ui_browser__percent_color(0, current_entry);
494 SLsmg_set_color(color);
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300495 slsmg_write_nstring(" ", 9);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300496 }
497
498 SLsmg_write_char(':');
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300499 slsmg_write_nstring(" ", 8);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300500 if (!*self->line)
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300501 slsmg_write_nstring(" ", width - 18);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300502 else
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -0300503 slsmg_write_nstring(self->line, width - 18);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300504
505 return 0;
506}
507
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300508static int ui_browser__refresh(struct ui_browser *self)
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300509{
Arnaldo Carvalho de Melo9f61d852010-06-21 19:38:33 -0300510 int row;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300511
Arnaldo Carvalho de Melo9f61d852010-06-21 19:38:33 -0300512 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300513 row = self->refresh(self);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300514 SLsmg_set_color(HE_COLORSET_NORMAL);
515 SLsmg_fill_region(self->top + row, self->left,
516 self->height - row, self->width, ' ');
517
518 return 0;
519}
520
Arnaldo Carvalho de Melo13f499f2010-06-21 18:04:02 -0300521static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300522{
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300523 if (ui_browser__refresh(self) < 0)
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300524 return -1;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300525
526 while (1) {
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300527 off_t offset;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300528
529 newtFormRun(self->form, es);
530
531 if (es->reason != NEWT_EXIT_HOTKEY)
532 break;
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300533 if (is_exit_key(es->u.key))
534 return es->u.key;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300535 switch (es->u.key) {
536 case NEWT_KEY_DOWN:
537 if (self->index == self->nr_entries - 1)
538 break;
539 ++self->index;
540 if (self->index == self->first_visible_entry_idx + self->height) {
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300541 ++self->first_visible_entry_idx;
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300542 self->seek(self, +1, SEEK_CUR);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300543 }
544 break;
545 case NEWT_KEY_UP:
546 if (self->index == 0)
547 break;
548 --self->index;
549 if (self->index < self->first_visible_entry_idx) {
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300550 --self->first_visible_entry_idx;
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300551 self->seek(self, -1, SEEK_CUR);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300552 }
553 break;
554 case NEWT_KEY_PGDN:
Arnaldo Carvalho de Melo17930b42010-05-19 16:03:51 -0300555 case ' ':
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300556 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
557 break;
558
559 offset = self->height;
560 if (self->index + offset > self->nr_entries - 1)
561 offset = self->nr_entries - 1 - self->index;
562 self->index += offset;
563 self->first_visible_entry_idx += offset;
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300564 self->seek(self, +offset, SEEK_CUR);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300565 break;
566 case NEWT_KEY_PGUP:
567 if (self->first_visible_entry_idx == 0)
568 break;
569
570 if (self->first_visible_entry_idx < self->height)
571 offset = self->first_visible_entry_idx;
572 else
573 offset = self->height;
574
575 self->index -= offset;
576 self->first_visible_entry_idx -= offset;
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300577 self->seek(self, -offset, SEEK_CUR);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300578 break;
579 case NEWT_KEY_HOME:
580 ui_browser__reset_index(self);
581 break;
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300582 case NEWT_KEY_END:
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300583 offset = self->height - 1;
Arnaldo Carvalho de Melo63f20e72010-07-15 07:21:07 -0300584 if (offset >= self->nr_entries)
585 offset = self->nr_entries - 1;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300586
Arnaldo Carvalho de Melo63f20e72010-07-15 07:21:07 -0300587 self->index = self->nr_entries - 1;
588 self->first_visible_entry_idx = self->index - offset;
Arnaldo Carvalho de Melo46b0a072010-06-21 13:36:20 -0300589 self->seek(self, -offset, SEEK_END);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300590 break;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300591 default:
Arnaldo Carvalho de Melob66ecd92010-07-15 07:24:30 -0300592 return es->u.key;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300593 }
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300594 if (ui_browser__refresh(self) < 0)
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300595 return -1;
596 }
597 return 0;
598}
599
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300600static char *callchain_list__sym_name(struct callchain_list *self,
601 char *bf, size_t bfsize)
602{
Arnaldo Carvalho de Melob3c9ac02010-03-24 16:40:18 -0300603 if (self->ms.sym)
604 return self->ms.sym->name;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300605
606 snprintf(bf, bfsize, "%#Lx", self->ip);
607 return bf;
608}
609
Arnaldo Carvalho de Melo9f61d852010-06-21 19:38:33 -0300610static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self)
611{
612 struct objdump_line *pos;
613 struct list_head *head = self->entries;
614 struct hist_entry *he = self->priv;
615 int row = 0;
616 int len = he->ms.sym->end - he->ms.sym->start;
617
618 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
619 self->first_visible_entry = head->next;
620
621 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
622
623 list_for_each_entry_from(pos, head, node) {
624 bool current_entry = ui_browser__is_current_entry(self, row);
625 SLsmg_gotorc(self->top + row, self->left);
626 objdump_line__show(pos, head, self->width,
627 he, len, current_entry);
628 if (++row == self->height)
629 break;
630 }
631
632 return row;
633}
634
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300635int hist_entry__tui_annotate(struct hist_entry *self)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300636{
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300637 struct ui_browser browser;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300638 struct newtExitStruct es;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300639 struct objdump_line *pos, *n;
640 LIST_HEAD(head);
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300641 int ret;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300642
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300643 if (self->ms.sym == NULL)
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300644 return -1;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300645
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300646 if (self->ms.map->dso->annotate_warned)
647 return -1;
648
649 if (hist_entry__annotate(self, &head) < 0) {
650 ui__error_window(browser__last_msg);
651 return -1;
652 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300653
Arnaldo Carvalho de Melo60553902010-05-15 20:40:34 -0300654 ui_helpline__push("Press <- or ESC to exit");
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300655
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300656 memset(&browser, 0, sizeof(browser));
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300657 browser.entries = &head;
658 browser.refresh = hist_entry__annotate_browser_refresh;
659 browser.seek = ui_browser__list_head_seek;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300660 browser.priv = self;
661 list_for_each_entry(pos, &head, node) {
662 size_t line_len = strlen(pos->line);
663 if (browser.width < line_len)
664 browser.width = line_len;
665 ++browser.nr_entries;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300666 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300667
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300668 browser.width += 18; /* Percentage */
Arnaldo Carvalho de Melo13f499f2010-06-21 18:04:02 -0300669 ui_browser__show(&browser, self->ms.sym->name);
Arnaldo Carvalho de Melob61b55e2010-07-21 17:55:32 -0300670 newtFormAddHotKey(browser.form, ' ');
Srikar Dronamraju0879b102010-06-29 23:02:26 +0530671 ret = ui_browser__run(&browser, &es);
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300672 newtFormDestroy(browser.form);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300673 newtPopWindow();
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300674 list_for_each_entry_safe(pos, n, &head, node) {
675 list_del(&pos->node);
676 objdump_line__free(pos);
677 }
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300678 ui_helpline__pop();
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300679 return ret;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300680}
681
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300682/* -------------------------------------------------------------------- */
683
684struct map_browser {
685 struct ui_browser b;
686 struct map *map;
687 u16 namelen;
688 u8 addrlen;
689};
690
691static void map_browser__write(struct ui_browser *self, void *nd, int row)
692{
693 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
694 struct map_browser *mb = container_of(self, struct map_browser, b);
695 bool current_entry = ui_browser__is_current_entry(self, row);
696 int color = ui_browser__percent_color(0, current_entry);
697
698 SLsmg_set_color(color);
699 slsmg_printf("%*llx %*llx %c ",
700 mb->addrlen, sym->start, mb->addrlen, sym->end,
701 sym->binding == STB_GLOBAL ? 'g' :
702 sym->binding == STB_LOCAL ? 'l' : 'w');
703 slsmg_write_nstring(sym->name, mb->namelen);
704}
705
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300706/* FIXME uber-kludgy, see comment on cmd_report... */
707static u32 *symbol__browser_index(struct symbol *self)
708{
709 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
710}
711
712static int map_browser__search(struct map_browser *self)
713{
714 char target[512];
715 struct symbol *sym;
716 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
717
718 if (err)
719 return err;
720
721 if (target[0] == '0' && tolower(target[1]) == 'x') {
722 u64 addr = strtoull(target, NULL, 16);
723 sym = map__find_symbol(self->map, addr, NULL);
724 } else
725 sym = map__find_symbol_by_name(self->map, target, NULL);
726
727 if (sym != NULL) {
728 u32 *idx = symbol__browser_index(sym);
729
730 self->b.first_visible_entry = &sym->rb_node;
731 self->b.index = self->b.first_visible_entry_idx = *idx;
732 } else
733 ui_helpline__fpush("%s not found!", target);
734
735 return 0;
736}
737
738static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
739{
740 if (ui_browser__show(&self->b, self->map->dso->long_name) < 0)
741 return -1;
742
743 ui_helpline__fpush("Press <- or ESC to exit, %s / to search",
744 verbose ? "" : "restart with -v to use");
745 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
746 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
747 if (verbose)
748 newtFormAddHotKey(self->b.form, '/');
749
750 while (1) {
751 ui_browser__run(&self->b, es);
752
753 if (es->reason != NEWT_EXIT_HOTKEY)
754 break;
755 if (verbose && es->u.key == '/')
756 map_browser__search(self);
757 else
758 break;
759 }
760
761 newtFormDestroy(self->b.form);
762 newtPopWindow();
763 ui_helpline__pop();
764 return 0;
765}
766
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300767static int map__browse(struct map *self)
768{
769 struct map_browser mb = {
770 .b = {
771 .entries = &self->dso->symbols[self->type],
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300772 .refresh = ui_browser__rb_tree_refresh,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300773 .seek = ui_browser__rb_tree_seek,
774 .write = map_browser__write,
775 },
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300776 .map = self,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300777 };
778 struct newtExitStruct es;
779 struct rb_node *nd;
780 char tmp[BITS_PER_LONG / 4];
781 u64 maxaddr = 0;
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300782
783 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
784 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
785
786 if (mb.namelen < pos->namelen)
787 mb.namelen = pos->namelen;
788 if (maxaddr < pos->end)
789 maxaddr = pos->end;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300790 if (verbose) {
791 u32 *idx = symbol__browser_index(pos);
792 *idx = mb.b.nr_entries;
793 }
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300794 ++mb.b.nr_entries;
795 }
796
797 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
798 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
Arnaldo Carvalho de Melo80d50ca2010-08-05 19:28:27 -0300799 return map_browser__run(&mb, &es);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300800}
801
802/* -------------------------------------------------------------------- */
803
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300804struct hist_browser {
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300805 struct ui_browser b;
806 struct hists *hists;
807 struct hist_entry *he_selection;
808 struct map_symbol *selection;
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300809};
810
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300811static void hist_browser__reset(struct hist_browser *self);
812static int hist_browser__run(struct hist_browser *self, const char *title,
813 struct newtExitStruct *es);
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300814static unsigned int hist_browser__refresh(struct ui_browser *self);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300815static void ui_browser__hists_seek(struct ui_browser *self,
816 off_t offset, int whence);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300817
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300818static struct hist_browser *hist_browser__new(struct hists *hists)
819{
820 struct hist_browser *self = zalloc(sizeof(*self));
821
822 if (self) {
823 self->hists = hists;
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -0300824 self->b.refresh = hist_browser__refresh;
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300825 self->b.seek = ui_browser__hists_seek;
826 }
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300827
828 return self;
829}
830
831static void hist_browser__delete(struct hist_browser *self)
832{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300833 newtFormDestroy(self->b.form);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300834 newtPopWindow();
835 free(self);
836}
837
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300838static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300839{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300840 return self->he_selection;
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -0300841}
842
843static struct thread *hist_browser__selected_thread(struct hist_browser *self)
844{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300845 return self->he_selection->thread;
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300846}
847
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300848static int hist_browser__title(char *bf, size_t size, const char *ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300849 const struct dso *dso, const struct thread *thread)
850{
851 int printed = 0;
852
853 if (thread)
854 printed += snprintf(bf + printed, size - printed,
855 "Thread: %s(%d)",
856 (thread->comm_set ? thread->comm : ""),
857 thread->pid);
858 if (dso)
859 printed += snprintf(bf + printed, size - printed,
860 "%sDSO: %s", thread ? " " : "",
861 dso->short_name);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300862 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300863}
864
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300865int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300866{
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300867 struct hist_browser *browser = hist_browser__new(self);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300868 struct pstack *fstack;
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300869 const struct thread *thread_filter = NULL;
870 const struct dso *dso_filter = NULL;
871 struct newtExitStruct es;
872 char msg[160];
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300873 int key = -1;
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300874
875 if (browser == NULL)
876 return -1;
877
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300878 fstack = pstack__new(2);
879 if (fstack == NULL)
880 goto out;
881
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -0300882 ui_helpline__push(helpline);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -0300883
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300884 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300885 dso_filter, thread_filter);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300886
887 while (1) {
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300888 const struct thread *thread;
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300889 const struct dso *dso;
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300890 char *options[16];
891 int nr_options = 0, choice = 0, i,
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300892 annotate = -2, zoom_dso = -2, zoom_thread = -2,
893 browse_map = -2;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300894
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -0300895 if (hist_browser__run(browser, msg, &es))
896 break;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300897
898 thread = hist_browser__selected_thread(browser);
899 dso = browser->selection->map ? browser->selection->map->dso : NULL;
900
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300901 if (es.reason == NEWT_EXIT_HOTKEY) {
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300902 key = es.u.key;
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300903
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300904 switch (key) {
905 case NEWT_KEY_F1:
906 goto do_help;
907 case NEWT_KEY_TAB:
908 case NEWT_KEY_UNTAB:
909 /*
910 * Exit the browser, let hists__browser_tree
911 * go to the next or previous
912 */
913 goto out_free_stack;
914 default:;
915 }
916
917 key = toupper(key);
918 switch (key) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300919 case 'A':
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300920 if (browser->selection->map == NULL &&
921 browser->selection->map->dso->annotate_warned)
922 continue;
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -0300923 goto do_annotate;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300924 case 'D':
925 goto zoom_dso;
926 case 'T':
927 goto zoom_thread;
Arnaldo Carvalho de Meloa9a4ab72010-05-16 21:04:27 -0300928 case 'H':
929 case '?':
930do_help:
931 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
932 "<- Zoom out\n"
933 "a Annotate current symbol\n"
934 "h/?/F1 Show this window\n"
935 "d Zoom into current DSO\n"
936 "t Zoom into current Thread\n"
937 "q/CTRL+C Exit browser");
938 continue;
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -0300939 default:;
940 }
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -0300941 if (is_exit_key(key)) {
942 if (key == NEWT_KEY_ESCAPE) {
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -0300943 if (dialog_yesno("Do you really want to exit?"))
944 break;
945 else
946 continue;
947 } else
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300948 break;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300949 }
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -0300950
951 if (es.u.key == NEWT_KEY_LEFT) {
952 const void *top;
953
954 if (pstack__empty(fstack))
955 continue;
956 top = pstack__pop(fstack);
957 if (top == &dso_filter)
958 goto zoom_out_dso;
959 if (top == &thread_filter)
960 goto zoom_out_thread;
961 continue;
962 }
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300963 }
964
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300965 if (browser->selection->sym != NULL &&
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -0300966 !browser->selection->map->dso->annotate_warned &&
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300967 asprintf(&options[nr_options], "Annotate %s",
968 browser->selection->sym->name) > 0)
969 annotate = nr_options++;
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300970
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300971 if (thread != NULL &&
972 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300973 (thread_filter ? "out of" : "into"),
974 (thread->comm_set ? thread->comm : ""),
975 thread->pid) > 0)
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300976 zoom_thread = nr_options++;
977
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -0300978 if (dso != NULL &&
979 asprintf(&options[nr_options], "Zoom %s %s DSO",
980 (dso_filter ? "out of" : "into"),
981 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
982 zoom_dso = nr_options++;
983
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -0300984 if (browser->selection->map != NULL &&
985 asprintf(&options[nr_options], "Browse map details") > 0)
986 browse_map = nr_options++;
987
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300988 options[nr_options++] = (char *)"Exit";
989
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300990 choice = popup_menu(nr_options, options);
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -0300991
992 for (i = 0; i < nr_options - 1; ++i)
993 free(options[i]);
994
Arnaldo Carvalho de Melo53c54012010-03-24 16:40:14 -0300995 if (choice == nr_options - 1)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300996 break;
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -0300997
998 if (choice == -1)
999 continue;
Arnaldo Carvalho de Meloc1ec5fe2010-05-15 20:45:31 -03001000
Arnaldo Carvalho de Melo83753192010-04-03 16:30:44 -03001001 if (choice == annotate) {
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -03001002 struct hist_entry *he;
Arnaldo Carvalho de Meloc1ec5fe2010-05-15 20:45:31 -03001003do_annotate:
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -03001004 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
Arnaldo Carvalho de Melo6e78c9f2010-05-22 11:20:24 -03001005 browser->selection->map->dso->annotate_warned = 1;
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -03001006 ui_helpline__puts("No vmlinux file found, can't "
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -03001007 "annotate with just a "
1008 "kallsyms file");
1009 continue;
1010 }
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -03001011
1012 he = hist_browser__selected_entry(browser);
1013 if (he == NULL)
1014 continue;
1015
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -03001016 hist_entry__tui_annotate(he);
Arnaldo Carvalho de Melo9a725992010-08-05 17:00:42 -03001017 } else if (choice == browse_map)
1018 map__browse(browser->selection->map);
1019 else if (choice == zoom_dso) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -03001020zoom_dso:
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001021 if (dso_filter) {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -03001022 pstack__remove(fstack, &dso_filter);
1023zoom_out_dso:
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -03001024 ui_helpline__pop();
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001025 dso_filter = NULL;
1026 } else {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -03001027 if (dso == NULL)
1028 continue;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -03001029 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -03001030 dso->kernel ? "the Kernel" : dso->short_name);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001031 dso_filter = dso;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -03001032 pstack__push(fstack, &dso_filter);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001033 }
Arnaldo Carvalho de Melob09e0192010-05-11 11:10:15 -03001034 hists__filter_by_dso(self, dso_filter);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -03001035 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001036 dso_filter, thread_filter);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001037 hist_browser__reset(browser);
Arnaldo Carvalho de Meloa5e29ac2010-04-03 22:44:37 -03001038 } else if (choice == zoom_thread) {
Arnaldo Carvalho de Melo9d192e12010-05-15 21:15:01 -03001039zoom_thread:
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001040 if (thread_filter) {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -03001041 pstack__remove(fstack, &thread_filter);
1042zoom_out_thread:
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -03001043 ui_helpline__pop();
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001044 thread_filter = NULL;
1045 } else {
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -03001046 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -03001047 thread->comm_set ? thread->comm : "",
1048 thread->pid);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001049 thread_filter = thread;
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -03001050 pstack__push(fstack, &thread_filter);
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001051 }
Arnaldo Carvalho de Melob09e0192010-05-11 11:10:15 -03001052 hists__filter_by_thread(self, thread_filter);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -03001053 hist_browser__title(msg, sizeof(msg), ev_name,
Arnaldo Carvalho de Melo6e7ab4c2010-04-05 12:02:18 -03001054 dso_filter, thread_filter);
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001055 hist_browser__reset(browser);
Arnaldo Carvalho de Melod5679ae2010-03-24 16:40:19 -03001056 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001057 }
Arnaldo Carvalho de Melo3e1bbdc32010-05-14 20:05:21 -03001058out_free_stack:
1059 pstack__delete(fstack);
Arnaldo Carvalho de Meloe65713e2010-04-03 11:25:56 -03001060out:
1061 hist_browser__delete(browser);
Arnaldo Carvalho de Melod67f0882010-05-23 22:36:51 -03001062 return key;
1063}
1064
1065int hists__tui_browse_tree(struct rb_root *self, const char *help)
1066{
1067 struct rb_node *first = rb_first(self), *nd = first, *next;
1068 int key = 0;
1069
1070 while (nd) {
1071 struct hists *hists = rb_entry(nd, struct hists, rb_node);
1072 const char *ev_name = __event_name(hists->type, hists->config);
1073
1074 key = hists__browse(hists, help, ev_name);
1075
1076 if (is_exit_key(key))
1077 break;
1078
1079 switch (key) {
1080 case NEWT_KEY_TAB:
1081 next = rb_next(nd);
1082 if (next)
1083 nd = next;
1084 break;
1085 case NEWT_KEY_UNTAB:
1086 if (nd == first)
1087 continue;
1088 nd = rb_prev(nd);
1089 default:
1090 break;
1091 }
1092 }
1093
1094 return key;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001095}
1096
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -03001097static struct newtPercentTreeColors {
1098 const char *topColorFg, *topColorBg;
1099 const char *mediumColorFg, *mediumColorBg;
1100 const char *normalColorFg, *normalColorBg;
1101 const char *selColorFg, *selColorBg;
1102 const char *codeColorFg, *codeColorBg;
1103} defaultPercentTreeColors = {
1104 "red", "lightgray",
1105 "green", "lightgray",
1106 "black", "lightgray",
1107 "lightgray", "magenta",
1108 "blue", "lightgray",
1109};
1110
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -03001111static void newt_suspend(void *d __used)
1112{
1113 newtSuspend();
1114 raise(SIGTSTP);
1115 newtResume();
1116}
1117
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001118void setup_browser(void)
1119{
Arnaldo Carvalho de Meloef7b93a2010-05-11 23:18:06 -03001120 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001121
Arnaldo Carvalho de Melo46e3e052010-05-22 11:25:40 -03001122 if (!isatty(1) || !use_browser || dump_trace) {
Arnaldo Carvalho de Melo62e34362010-05-26 13:22:26 -03001123 use_browser = 0;
Arnaldo Carvalho de Melo5d06e692010-05-20 22:01:10 -03001124 setup_pager();
1125 return;
1126 }
1127
1128 use_browser = 1;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001129 newtInit();
1130 newtCls();
Arnaldo Carvalho de Melo73ae8f82010-07-30 10:06:06 -03001131 newtSetSuspendCallback(newt_suspend, NULL);
Arnaldo Carvalho de Melo3798ed72010-05-11 18:01:23 -03001132 ui_helpline__puts(" ");
Arnaldo Carvalho de Melodc4ff192010-05-17 12:25:09 -03001133 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1134 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
1135 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
1136 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
1137 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001138}
1139
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -03001140void exit_browser(bool wait_for_ok)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001141{
Arnaldo Carvalho de Melo5d06e692010-05-20 22:01:10 -03001142 if (use_browser > 0) {
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -03001143 if (wait_for_ok) {
1144 char title[] = "Fatal Error", ok[] = "Ok";
1145 newtWinMessage(title, ok, browser__last_msg);
1146 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001147 newtFinished();
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -03001148 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001149}
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001150
1151static void hist_browser__refresh_dimensions(struct hist_browser *self)
1152{
1153 /* 3 == +/- toggle symbol before actual hist_entry rendering */
1154 self->b.width = 3 + (hists__sort_list_width(self->hists) +
1155 sizeof("[k]"));
1156}
1157
1158static void hist_browser__reset(struct hist_browser *self)
1159{
1160 self->b.nr_entries = self->hists->nr_entries;
1161 hist_browser__refresh_dimensions(self);
1162 ui_browser__reset_index(&self->b);
1163}
1164
1165static char tree__folded_sign(bool unfolded)
1166{
1167 return unfolded ? '-' : '+';
1168}
1169
1170static char map_symbol__folded(const struct map_symbol *self)
1171{
1172 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
1173}
1174
1175static char hist_entry__folded(const struct hist_entry *self)
1176{
1177 return map_symbol__folded(&self->ms);
1178}
1179
1180static char callchain_list__folded(const struct callchain_list *self)
1181{
1182 return map_symbol__folded(&self->ms);
1183}
1184
1185static bool map_symbol__toggle_fold(struct map_symbol *self)
1186{
1187 if (!self->has_children)
1188 return false;
1189
1190 self->unfolded = !self->unfolded;
1191 return true;
1192}
1193
1194#define LEVEL_OFFSET_STEP 3
1195
1196static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
1197 struct callchain_node *chain_node,
1198 u64 total, int level,
1199 unsigned short row,
1200 off_t *row_offset,
1201 bool *is_current_entry)
1202{
1203 struct rb_node *node;
1204 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
1205 u64 new_total, remaining;
1206
1207 if (callchain_param.mode == CHAIN_GRAPH_REL)
1208 new_total = chain_node->children_hit;
1209 else
1210 new_total = total;
1211
1212 remaining = new_total;
1213 node = rb_first(&chain_node->rb_root);
1214 while (node) {
1215 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1216 struct rb_node *next = rb_next(node);
1217 u64 cumul = cumul_hits(child);
1218 struct callchain_list *chain;
1219 char folded_sign = ' ';
1220 int first = true;
1221 int extra_offset = 0;
1222
1223 remaining -= cumul;
1224
1225 list_for_each_entry(chain, &child->val, list) {
1226 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
1227 const char *str;
1228 int color;
1229 bool was_first = first;
1230
1231 if (first) {
1232 first = false;
1233 chain->ms.has_children = chain->list.next != &child->val ||
1234 rb_first(&child->rb_root) != NULL;
1235 } else {
1236 extra_offset = LEVEL_OFFSET_STEP;
1237 chain->ms.has_children = chain->list.next == &child->val &&
1238 rb_first(&child->rb_root) != NULL;
1239 }
1240
1241 folded_sign = callchain_list__folded(chain);
1242 if (*row_offset != 0) {
1243 --*row_offset;
1244 goto do_next;
1245 }
1246
1247 alloc_str = NULL;
1248 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1249 if (was_first) {
1250 double percent = cumul * 100.0 / new_total;
1251
1252 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1253 str = "Not enough memory!";
1254 else
1255 str = alloc_str;
1256 }
1257
1258 color = HE_COLORSET_NORMAL;
1259 width = self->b.width - (offset + extra_offset + 2);
1260 if (ui_browser__is_current_entry(&self->b, row)) {
1261 self->selection = &chain->ms;
1262 color = HE_COLORSET_SELECTED;
1263 *is_current_entry = true;
1264 }
1265
1266 SLsmg_set_color(color);
1267 SLsmg_gotorc(self->b.top + row, self->b.left);
1268 slsmg_write_nstring(" ", offset + extra_offset);
1269 slsmg_printf("%c ", folded_sign);
1270 slsmg_write_nstring(str, width);
1271 free(alloc_str);
1272
1273 if (++row == self->b.height)
1274 goto out;
1275do_next:
1276 if (folded_sign == '+')
1277 break;
1278 }
1279
1280 if (folded_sign == '-') {
1281 const int new_level = level + (extra_offset ? 2 : 1);
1282 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
1283 new_level, row, row_offset,
1284 is_current_entry);
1285 }
1286 if (row == self->b.height)
1287 goto out;
1288 node = next;
1289 }
1290out:
1291 return row - first_row;
1292}
1293
1294static int hist_browser__show_callchain_node(struct hist_browser *self,
1295 struct callchain_node *node,
1296 int level, unsigned short row,
1297 off_t *row_offset,
1298 bool *is_current_entry)
1299{
1300 struct callchain_list *chain;
1301 int first_row = row,
1302 offset = level * LEVEL_OFFSET_STEP,
1303 width = self->b.width - offset;
1304 char folded_sign = ' ';
1305
1306 list_for_each_entry(chain, &node->val, list) {
1307 char ipstr[BITS_PER_LONG / 4 + 1], *s;
1308 int color;
1309 /*
1310 * FIXME: This should be moved to somewhere else,
1311 * probably when the callchain is created, so as not to
1312 * traverse it all over again
1313 */
1314 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
1315 folded_sign = callchain_list__folded(chain);
1316
1317 if (*row_offset != 0) {
1318 --*row_offset;
1319 continue;
1320 }
1321
1322 color = HE_COLORSET_NORMAL;
1323 if (ui_browser__is_current_entry(&self->b, row)) {
1324 self->selection = &chain->ms;
1325 color = HE_COLORSET_SELECTED;
1326 *is_current_entry = true;
1327 }
1328
1329 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1330 SLsmg_gotorc(self->b.top + row, self->b.left);
1331 SLsmg_set_color(color);
1332 slsmg_write_nstring(" ", offset);
1333 slsmg_printf("%c ", folded_sign);
1334 slsmg_write_nstring(s, width - 2);
1335
1336 if (++row == self->b.height)
1337 goto out;
1338 }
1339
1340 if (folded_sign == '-')
1341 row += hist_browser__show_callchain_node_rb_tree(self, node,
1342 self->hists->stats.total_period,
1343 level + 1, row,
1344 row_offset,
1345 is_current_entry);
1346out:
1347 return row - first_row;
1348}
1349
1350static int hist_browser__show_callchain(struct hist_browser *self,
1351 struct rb_root *chain,
1352 int level, unsigned short row,
1353 off_t *row_offset,
1354 bool *is_current_entry)
1355{
1356 struct rb_node *nd;
1357 int first_row = row;
1358
1359 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1360 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1361
1362 row += hist_browser__show_callchain_node(self, node, level,
1363 row, row_offset,
1364 is_current_entry);
1365 if (row == self->b.height)
1366 break;
1367 }
1368
1369 return row - first_row;
1370}
1371
1372static int hist_browser__show_entry(struct hist_browser *self,
1373 struct hist_entry *entry,
1374 unsigned short row)
1375{
1376 char s[256];
1377 double percent;
1378 int printed = 0;
1379 int color, width = self->b.width;
1380 char folded_sign = ' ';
1381 bool current_entry = ui_browser__is_current_entry(&self->b, row);
1382 off_t row_offset = entry->row_offset;
1383
1384 if (current_entry) {
1385 self->he_selection = entry;
1386 self->selection = &entry->ms;
1387 }
1388
1389 if (symbol_conf.use_callchain) {
1390 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
1391 folded_sign = hist_entry__folded(entry);
1392 }
1393
1394 if (row_offset == 0) {
1395 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
1396 0, false, self->hists->stats.total_period);
1397 percent = (entry->period * 100.0) / self->hists->stats.total_period;
1398
1399 color = HE_COLORSET_SELECTED;
1400 if (!current_entry) {
1401 if (percent >= MIN_RED)
1402 color = HE_COLORSET_TOP;
1403 else if (percent >= MIN_GREEN)
1404 color = HE_COLORSET_MEDIUM;
1405 else
1406 color = HE_COLORSET_NORMAL;
1407 }
1408
1409 SLsmg_set_color(color);
1410 SLsmg_gotorc(self->b.top + row, self->b.left);
1411 if (symbol_conf.use_callchain) {
1412 slsmg_printf("%c ", folded_sign);
1413 width -= 2;
1414 }
1415 slsmg_write_nstring(s, width);
1416 ++row;
1417 ++printed;
1418 } else
1419 --row_offset;
1420
1421 if (folded_sign == '-' && row != self->b.height) {
1422 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
1423 1, row, &row_offset,
1424 &current_entry);
1425 if (current_entry)
1426 self->he_selection = entry;
1427 }
1428
1429 return printed;
1430}
1431
Arnaldo Carvalho de Melo76ce93d2010-08-05 17:02:54 -03001432static unsigned int hist_browser__refresh(struct ui_browser *self)
Arnaldo Carvalho de Melo0f0cbf72010-07-26 17:13:40 -03001433{
1434 unsigned row = 0;
1435 struct rb_node *nd;
1436 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1437
1438 if (self->first_visible_entry == NULL)
1439 self->first_visible_entry = rb_first(&hb->hists->entries);
1440
1441 for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) {
1442 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1443
1444 if (h->filtered)
1445 continue;
1446
1447 row += hist_browser__show_entry(hb, h, row);
1448 if (row == self->height)
1449 break;
1450 }
1451
1452 return row;
1453}
1454
1455static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
1456{
1457 struct rb_node *nd = rb_first(&self->rb_root);
1458
1459 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1460 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1461 struct callchain_list *chain;
1462 int first = true;
1463
1464 list_for_each_entry(chain, &child->val, list) {
1465 if (first) {
1466 first = false;
1467 chain->ms.has_children = chain->list.next != &child->val ||
1468 rb_first(&child->rb_root) != NULL;
1469 } else
1470 chain->ms.has_children = chain->list.next == &child->val &&
1471 rb_first(&child->rb_root) != NULL;
1472 }
1473
1474 callchain_node__init_have_children_rb_tree(child);
1475 }
1476}
1477
1478static void callchain_node__init_have_children(struct callchain_node *self)
1479{
1480 struct callchain_list *chain;
1481
1482 list_for_each_entry(chain, &self->val, list)
1483 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
1484
1485 callchain_node__init_have_children_rb_tree(self);
1486}
1487
1488static void callchain__init_have_children(struct rb_root *self)
1489{
1490 struct rb_node *nd;
1491
1492 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
1493 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1494 callchain_node__init_have_children(node);
1495 }
1496}
1497
1498static void hist_entry__init_have_children(struct hist_entry *self)
1499{
1500 if (!self->init_have_children) {
1501 callchain__init_have_children(&self->sorted_chain);
1502 self->init_have_children = true;
1503 }
1504}
1505
1506static struct rb_node *hists__filter_entries(struct rb_node *nd)
1507{
1508 while (nd != NULL) {
1509 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1510 if (!h->filtered)
1511 return nd;
1512
1513 nd = rb_next(nd);
1514 }
1515
1516 return NULL;
1517}
1518
1519static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
1520{
1521 while (nd != NULL) {
1522 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1523 if (!h->filtered)
1524 return nd;
1525
1526 nd = rb_prev(nd);
1527 }
1528
1529 return NULL;
1530}
1531
1532static void ui_browser__hists_seek(struct ui_browser *self,
1533 off_t offset, int whence)
1534{
1535 struct hist_entry *h;
1536 struct rb_node *nd;
1537 bool first = true;
1538
1539 switch (whence) {
1540 case SEEK_SET:
1541 nd = hists__filter_entries(rb_first(self->entries));
1542 break;
1543 case SEEK_CUR:
1544 nd = self->first_visible_entry;
1545 goto do_offset;
1546 case SEEK_END:
1547 nd = hists__filter_prev_entries(rb_last(self->entries));
1548 first = false;
1549 break;
1550 default:
1551 return;
1552 }
1553
1554 /*
1555 * Moves not relative to the first visible entry invalidates its
1556 * row_offset:
1557 */
1558 h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node);
1559 h->row_offset = 0;
1560
1561 /*
1562 * Here we have to check if nd is expanded (+), if it is we can't go
1563 * the next top level hist_entry, instead we must compute an offset of
1564 * what _not_ to show and not change the first visible entry.
1565 *
1566 * This offset increments when we are going from top to bottom and
1567 * decreases when we're going from bottom to top.
1568 *
1569 * As we don't have backpointers to the top level in the callchains
1570 * structure, we need to always print the whole hist_entry callchain,
1571 * skipping the first ones that are before the first visible entry
1572 * and stop when we printed enough lines to fill the screen.
1573 */
1574do_offset:
1575 if (offset > 0) {
1576 do {
1577 h = rb_entry(nd, struct hist_entry, rb_node);
1578 if (h->ms.unfolded) {
1579 u16 remaining = h->nr_rows - h->row_offset;
1580 if (offset > remaining) {
1581 offset -= remaining;
1582 h->row_offset = 0;
1583 } else {
1584 h->row_offset += offset;
1585 offset = 0;
1586 self->first_visible_entry = nd;
1587 break;
1588 }
1589 }
1590 nd = hists__filter_entries(rb_next(nd));
1591 if (nd == NULL)
1592 break;
1593 --offset;
1594 self->first_visible_entry = nd;
1595 } while (offset != 0);
1596 } else if (offset < 0) {
1597 while (1) {
1598 h = rb_entry(nd, struct hist_entry, rb_node);
1599 if (h->ms.unfolded) {
1600 if (first) {
1601 if (-offset > h->row_offset) {
1602 offset += h->row_offset;
1603 h->row_offset = 0;
1604 } else {
1605 h->row_offset += offset;
1606 offset = 0;
1607 self->first_visible_entry = nd;
1608 break;
1609 }
1610 } else {
1611 if (-offset > h->nr_rows) {
1612 offset += h->nr_rows;
1613 h->row_offset = 0;
1614 } else {
1615 h->row_offset = h->nr_rows + offset;
1616 offset = 0;
1617 self->first_visible_entry = nd;
1618 break;
1619 }
1620 }
1621 }
1622
1623 nd = hists__filter_prev_entries(rb_prev(nd));
1624 if (nd == NULL)
1625 break;
1626 ++offset;
1627 self->first_visible_entry = nd;
1628 if (offset == 0) {
1629 /*
1630 * Last unfiltered hist_entry, check if it is
1631 * unfolded, if it is then we should have
1632 * row_offset at its last entry.
1633 */
1634 h = rb_entry(nd, struct hist_entry, rb_node);
1635 if (h->ms.unfolded)
1636 h->row_offset = h->nr_rows;
1637 break;
1638 }
1639 first = false;
1640 }
1641 } else {
1642 self->first_visible_entry = nd;
1643 h = rb_entry(nd, struct hist_entry, rb_node);
1644 h->row_offset = 0;
1645 }
1646}
1647
1648static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
1649{
1650 int n = 0;
1651 struct rb_node *nd;
1652
1653 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1654 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1655 struct callchain_list *chain;
1656 char folded_sign = ' '; /* No children */
1657
1658 list_for_each_entry(chain, &child->val, list) {
1659 ++n;
1660 /* We need this because we may not have children */
1661 folded_sign = callchain_list__folded(chain);
1662 if (folded_sign == '+')
1663 break;
1664 }
1665
1666 if (folded_sign == '-') /* Have children and they're unfolded */
1667 n += callchain_node__count_rows_rb_tree(child);
1668 }
1669
1670 return n;
1671}
1672
1673static int callchain_node__count_rows(struct callchain_node *node)
1674{
1675 struct callchain_list *chain;
1676 bool unfolded = false;
1677 int n = 0;
1678
1679 list_for_each_entry(chain, &node->val, list) {
1680 ++n;
1681 unfolded = chain->ms.unfolded;
1682 }
1683
1684 if (unfolded)
1685 n += callchain_node__count_rows_rb_tree(node);
1686
1687 return n;
1688}
1689
1690static int callchain__count_rows(struct rb_root *chain)
1691{
1692 struct rb_node *nd;
1693 int n = 0;
1694
1695 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1696 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1697 n += callchain_node__count_rows(node);
1698 }
1699
1700 return n;
1701}
1702
1703static bool hist_browser__toggle_fold(struct hist_browser *self)
1704{
1705 if (map_symbol__toggle_fold(self->selection)) {
1706 struct hist_entry *he = self->he_selection;
1707
1708 hist_entry__init_have_children(he);
1709 self->hists->nr_entries -= he->nr_rows;
1710
1711 if (he->ms.unfolded)
1712 he->nr_rows = callchain__count_rows(&he->sorted_chain);
1713 else
1714 he->nr_rows = 0;
1715 self->hists->nr_entries += he->nr_rows;
1716 self->b.nr_entries = self->hists->nr_entries;
1717
1718 return true;
1719 }
1720
1721 /* If it doesn't have children, no toggling performed */
1722 return false;
1723}
1724
1725static int hist_browser__run(struct hist_browser *self, const char *title,
1726 struct newtExitStruct *es)
1727{
1728 char str[256], unit;
1729 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
1730
1731 self->b.entries = &self->hists->entries;
1732 self->b.nr_entries = self->hists->nr_entries;
1733
1734 hist_browser__refresh_dimensions(self);
1735
1736 nr_events = convert_unit(nr_events, &unit);
1737 snprintf(str, sizeof(str), "Events: %lu%c ",
1738 nr_events, unit);
1739 newtDrawRootText(0, 0, str);
1740
1741 if (ui_browser__show(&self->b, title) < 0)
1742 return -1;
1743
1744 newtFormAddHotKey(self->b.form, 'A');
1745 newtFormAddHotKey(self->b.form, 'a');
1746 newtFormAddHotKey(self->b.form, '?');
1747 newtFormAddHotKey(self->b.form, 'h');
1748 newtFormAddHotKey(self->b.form, 'H');
1749 newtFormAddHotKey(self->b.form, 'd');
1750
1751 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
1752 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
1753 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1754
1755 while (1) {
1756 ui_browser__run(&self->b, es);
1757
1758 if (es->reason != NEWT_EXIT_HOTKEY)
1759 break;
1760 switch (es->u.key) {
1761 case 'd': { /* Debug */
1762 static int seq;
1763 struct hist_entry *h = rb_entry(self->b.first_visible_entry,
1764 struct hist_entry, rb_node);
1765 ui_helpline__pop();
1766 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1767 seq++, self->b.nr_entries,
1768 self->hists->nr_entries,
1769 self->b.height,
1770 self->b.index,
1771 self->b.first_visible_entry_idx,
1772 h->row_offset, h->nr_rows);
1773 }
1774 continue;
1775 case NEWT_KEY_ENTER:
1776 if (hist_browser__toggle_fold(self))
1777 break;
1778 /* fall thru */
1779 default:
1780 return 0;
1781 }
1782 }
1783 return 0;
1784}