]> bbs.cooldavid.org Git - net-next-2.6.git/blame - tools/perf/util/newt.c
perf tui: Add workaround for slang < 2.1.4
[net-next-2.6.git] / tools / perf / util / newt.c
CommitLineData
f9224c5c
ACM
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4
ef7b93a1 5#include <slang.h>
f9224c5c
ACM
6#include <stdlib.h>
7#include <newt.h>
7081e087 8#include <sys/ttydefaults.h>
f9224c5c
ACM
9
10#include "cache.h"
11#include "hist.h"
3e1bbdc3 12#include "pstack.h"
f9224c5c
ACM
13#include "session.h"
14#include "sort.h"
15#include "symbol.h"
16
dc4ff193
ACM
17#if SLANG_VERSION < 20104
18#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
19#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
20#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
21 (char *)fg, (char *)bg)
22#else
23#define slsmg_printf SLsmg_printf
24#define slsmg_write_nstring SLsmg_write_nstring
25#define sltt_set_color SLtt_set_color
26#endif
27
5f4d3f88
ACM
28struct ui_progress {
29 newtComponent form, scale;
30};
31
32struct ui_progress *ui_progress__new(const char *title, u64 total)
33{
34 struct ui_progress *self = malloc(sizeof(*self));
35
36 if (self != NULL) {
37 int cols;
38 newtGetScreenSize(&cols, NULL);
39 cols -= 4;
40 newtCenteredWindow(cols, 1, title);
41 self->form = newtForm(NULL, NULL, 0);
42 if (self->form == NULL)
43 goto out_free_self;
44 self->scale = newtScale(0, 0, cols, total);
45 if (self->scale == NULL)
46 goto out_free_form;
7f826453 47 newtFormAddComponent(self->form, self->scale);
5f4d3f88
ACM
48 newtRefresh();
49 }
50
51 return self;
52
53out_free_form:
54 newtFormDestroy(self->form);
55out_free_self:
56 free(self);
57 return NULL;
58}
59
60void ui_progress__update(struct ui_progress *self, u64 curr)
61{
62 newtScaleSet(self->scale, curr);
63 newtRefresh();
64}
65
66void ui_progress__delete(struct ui_progress *self)
67{
68 newtFormDestroy(self->form);
69 newtPopWindow();
70 free(self);
71}
72
3798ed7b
ACM
73static void ui_helpline__pop(void)
74{
75 newtPopHelpLine();
76}
77
78static void ui_helpline__push(const char *msg)
79{
80 newtPushHelpLine(msg);
81}
82
83static void ui_helpline__vpush(const char *fmt, va_list ap)
84{
85 char *s;
86
87 if (vasprintf(&s, fmt, ap) < 0)
88 vfprintf(stderr, fmt, ap);
89 else {
90 ui_helpline__push(s);
91 free(s);
92 }
93}
94
95static void ui_helpline__fpush(const char *fmt, ...)
96{
97 va_list ap;
98
99 va_start(ap, fmt);
100 ui_helpline__vpush(fmt, ap);
101 va_end(ap);
102}
103
104static void ui_helpline__puts(const char *msg)
105{
106 ui_helpline__pop();
107 ui_helpline__push(msg);
108}
109
5f4d3f88
ACM
110static char browser__last_msg[1024];
111
112int browser__show_help(const char *format, va_list ap)
113{
114 int ret;
115 static int backlog;
116
117 ret = vsnprintf(browser__last_msg + backlog,
118 sizeof(browser__last_msg) - backlog, format, ap);
119 backlog += ret;
120
121 if (browser__last_msg[backlog - 1] == '\n') {
3798ed7b 122 ui_helpline__puts(browser__last_msg);
5f4d3f88
ACM
123 newtRefresh();
124 backlog = 0;
125 }
126
127 return ret;
128}
129
7081e087
ACM
130static void newt_form__set_exit_keys(newtComponent self)
131{
a308f3a8 132 newtFormAddHotKey(self, NEWT_KEY_LEFT);
7081e087
ACM
133 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
134 newtFormAddHotKey(self, 'Q');
135 newtFormAddHotKey(self, 'q');
136 newtFormAddHotKey(self, CTRL('c'));
137}
138
139static newtComponent newt_form__new(void)
140{
141 newtComponent self = newtForm(NULL, NULL, 0);
142 if (self)
143 newt_form__set_exit_keys(self);
144 return self;
145}
146
83753190 147static int popup_menu(int argc, char * const argv[])
53c54019
ACM
148{
149 struct newtExitStruct es;
150 int i, rc = -1, max_len = 5;
151 newtComponent listbox, form = newt_form__new();
152
153 if (form == NULL)
154 return -1;
155
156 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
157 if (listbox == NULL)
158 goto out_destroy_form;
159
7f826453 160 newtFormAddComponent(form, listbox);
53c54019
ACM
161
162 for (i = 0; i < argc; ++i) {
163 int len = strlen(argv[i]);
164 if (len > max_len)
165 max_len = len;
166 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
167 goto out_destroy_form;
168 }
169
170 newtCenteredWindow(max_len, argc, NULL);
171 newtFormRun(form, &es);
172 rc = newtListboxGetCurrent(listbox) - NULL;
173 if (es.reason == NEWT_EXIT_HOTKEY)
174 rc = -1;
175 newtPopWindow();
176out_destroy_form:
177 newtFormDestroy(form);
178 return rc;
179}
180
a9a4ab74
ACM
181static int ui__help_window(const char *text)
182{
183 struct newtExitStruct es;
184 newtComponent tb, form = newt_form__new();
185 int rc = -1;
186 int max_len = 0, nr_lines = 0;
187 const char *t;
188
189 if (form == NULL)
190 return -1;
191
192 t = text;
193 while (1) {
194 const char *sep = strchr(t, '\n');
195 int len;
196
197 if (sep == NULL)
198 sep = strchr(t, '\0');
199 len = sep - t;
200 if (max_len < len)
201 max_len = len;
202 ++nr_lines;
203 if (*sep == '\0')
204 break;
205 t = sep + 1;
206 }
207
208 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
209 if (tb == NULL)
210 goto out_destroy_form;
211
212 newtTextboxSetText(tb, text);
213 newtFormAddComponent(form, tb);
214 newtCenteredWindow(max_len, nr_lines, NULL);
215 newtFormRun(form, &es);
216 newtPopWindow();
217 rc = 0;
218out_destroy_form:
219 newtFormDestroy(form);
220 return rc;
221}
222
53c54019
ACM
223static bool dialog_yesno(const char *msg)
224{
225 /* newtWinChoice should really be accepting const char pointers... */
226 char yes[] = "Yes", no[] = "No";
c0ed55d2 227 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
53c54019
ACM
228}
229
ef7b93a1
ACM
230#define HE_COLORSET_TOP 50
231#define HE_COLORSET_MEDIUM 51
232#define HE_COLORSET_NORMAL 52
233#define HE_COLORSET_SELECTED 53
234#define HE_COLORSET_CODE 54
235
236static int ui_browser__percent_color(double percent, bool current)
237{
238 if (current)
239 return HE_COLORSET_SELECTED;
240 if (percent >= MIN_RED)
241 return HE_COLORSET_TOP;
242 if (percent >= MIN_GREEN)
243 return HE_COLORSET_MEDIUM;
244 return HE_COLORSET_NORMAL;
245}
246
247struct ui_browser {
248 newtComponent form, sb;
249 u64 index, first_visible_entry_idx;
250 void *first_visible_entry, *entries;
251 u16 top, left, width, height;
252 void *priv;
253 u32 nr_entries;
254};
255
256static void ui_browser__refresh_dimensions(struct ui_browser *self)
257{
258 int cols, rows;
259 newtGetScreenSize(&cols, &rows);
260
261 if (self->width > cols - 4)
262 self->width = cols - 4;
263 self->height = rows - 5;
264 if (self->height > self->nr_entries)
265 self->height = self->nr_entries;
266 self->top = (rows - self->height) / 2;
267 self->left = (cols - self->width) / 2;
268}
269
270static void ui_browser__reset_index(struct ui_browser *self)
271{
272 self->index = self->first_visible_entry_idx = 0;
273 self->first_visible_entry = NULL;
274}
275
276static int objdump_line__show(struct objdump_line *self, struct list_head *head,
277 int width, struct hist_entry *he, int len,
278 bool current_entry)
279{
280 if (self->offset != -1) {
281 struct symbol *sym = he->ms.sym;
282 unsigned int hits = 0;
283 double percent = 0.0;
284 int color;
285 struct sym_priv *priv = symbol__priv(sym);
286 struct sym_ext *sym_ext = priv->ext;
287 struct sym_hist *h = priv->hist;
288 s64 offset = self->offset;
289 struct objdump_line *next = objdump__get_next_ip_line(head, self);
290
291 while (offset < (s64)len &&
292 (next == NULL || offset < next->offset)) {
293 if (sym_ext) {
294 percent += sym_ext[offset].percent;
295 } else
296 hits += h->ip[offset];
297
298 ++offset;
299 }
300
301 if (sym_ext == NULL && h->sum)
302 percent = 100.0 * hits / h->sum;
303
304 color = ui_browser__percent_color(percent, current_entry);
305 SLsmg_set_color(color);
dc4ff193 306 slsmg_printf(" %7.2f ", percent);
ef7b93a1
ACM
307 if (!current_entry)
308 SLsmg_set_color(HE_COLORSET_CODE);
309 } else {
310 int color = ui_browser__percent_color(0, current_entry);
311 SLsmg_set_color(color);
dc4ff193 312 slsmg_write_nstring(" ", 9);
ef7b93a1
ACM
313 }
314
315 SLsmg_write_char(':');
dc4ff193 316 slsmg_write_nstring(" ", 8);
ef7b93a1 317 if (!*self->line)
dc4ff193 318 slsmg_write_nstring(" ", width - 18);
ef7b93a1 319 else
dc4ff193 320 slsmg_write_nstring(self->line, width - 18);
ef7b93a1
ACM
321
322 return 0;
323}
324
325static int ui_browser__refresh_entries(struct ui_browser *self)
326{
327 struct objdump_line *pos;
328 struct list_head *head = self->entries;
329 struct hist_entry *he = self->priv;
330 int row = 0;
331 int len = he->ms.sym->end - he->ms.sym->start;
332
333 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
334 self->first_visible_entry = head->next;
335
336 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
337
338 list_for_each_entry_from(pos, head, node) {
339 bool current_entry = (self->first_visible_entry_idx + row) == self->index;
340 SLsmg_gotorc(self->top + row, self->left);
341 objdump_line__show(pos, head, self->width,
342 he, len, current_entry);
343 if (++row == self->height)
344 break;
345 }
346
347 SLsmg_set_color(HE_COLORSET_NORMAL);
348 SLsmg_fill_region(self->top + row, self->left,
349 self->height - row, self->width, ' ');
350
351 return 0;
352}
353
354static int ui_browser__run(struct ui_browser *self, const char *title,
355 struct newtExitStruct *es)
356{
357 if (self->form) {
358 newtFormDestroy(self->form);
359 newtPopWindow();
360 }
361
362 ui_browser__refresh_dimensions(self);
363 newtCenteredWindow(self->width + 2, self->height, title);
364 self->form = newt_form__new();
365 if (self->form == NULL)
366 return -1;
367
368 self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
369 HE_COLORSET_NORMAL,
370 HE_COLORSET_SELECTED);
371 if (self->sb == NULL)
372 return -1;
373
374 newtFormAddHotKey(self->form, NEWT_KEY_UP);
375 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
376 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
377 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
378 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
379 newtFormAddHotKey(self->form, NEWT_KEY_END);
380
381 if (ui_browser__refresh_entries(self) < 0)
382 return -1;
383 newtFormAddComponent(self->form, self->sb);
384
385 while (1) {
386 unsigned int offset;
387
388 newtFormRun(self->form, es);
389
390 if (es->reason != NEWT_EXIT_HOTKEY)
391 break;
392 switch (es->u.key) {
393 case NEWT_KEY_DOWN:
394 if (self->index == self->nr_entries - 1)
395 break;
396 ++self->index;
397 if (self->index == self->first_visible_entry_idx + self->height) {
398 struct list_head *pos = self->first_visible_entry;
399 ++self->first_visible_entry_idx;
400 self->first_visible_entry = pos->next;
401 }
402 break;
403 case NEWT_KEY_UP:
404 if (self->index == 0)
405 break;
406 --self->index;
407 if (self->index < self->first_visible_entry_idx) {
408 struct list_head *pos = self->first_visible_entry;
409 --self->first_visible_entry_idx;
410 self->first_visible_entry = pos->prev;
411 }
412 break;
413 case NEWT_KEY_PGDN:
414 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
415 break;
416
417 offset = self->height;
418 if (self->index + offset > self->nr_entries - 1)
419 offset = self->nr_entries - 1 - self->index;
420 self->index += offset;
421 self->first_visible_entry_idx += offset;
422
423 while (offset--) {
424 struct list_head *pos = self->first_visible_entry;
425 self->first_visible_entry = pos->next;
426 }
427
428 break;
429 case NEWT_KEY_PGUP:
430 if (self->first_visible_entry_idx == 0)
431 break;
432
433 if (self->first_visible_entry_idx < self->height)
434 offset = self->first_visible_entry_idx;
435 else
436 offset = self->height;
437
438 self->index -= offset;
439 self->first_visible_entry_idx -= offset;
440
441 while (offset--) {
442 struct list_head *pos = self->first_visible_entry;
443 self->first_visible_entry = pos->prev;
444 }
445 break;
446 case NEWT_KEY_HOME:
447 ui_browser__reset_index(self);
448 break;
449 case NEWT_KEY_END: {
450 struct list_head *head = self->entries;
451 offset = self->height - 1;
452
453 if (offset > self->nr_entries)
454 offset = self->nr_entries;
455
456 self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
457 self->first_visible_entry = head->prev;
458 while (offset-- != 0) {
459 struct list_head *pos = self->first_visible_entry;
460 self->first_visible_entry = pos->prev;
461 }
462 }
463 break;
464 case NEWT_KEY_ESCAPE:
60553903 465 case NEWT_KEY_LEFT:
ef7b93a1
ACM
466 case CTRL('c'):
467 case 'Q':
468 case 'q':
469 return 0;
470 default:
471 continue;
472 }
473 if (ui_browser__refresh_entries(self) < 0)
474 return -1;
475 }
476 return 0;
477}
478
4ded2b25
ACM
479/*
480 * When debugging newt problems it was useful to be able to "unroll"
481 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
482 * a source file with the sequence of calls to these methods, to then
483 * tweak the arrays to get the intended results, so I'm keeping this code
484 * here, may be useful again in the future.
485 */
486#undef NEWT_DEBUG
487
488static void newt_checkbox_tree__add(newtComponent tree, const char *str,
489 void *priv, int *indexes)
490{
491#ifdef NEWT_DEBUG
492 /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
493 int i = 0, len = 40 - strlen(str);
494
495 fprintf(stderr,
496 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
497 len, len, " ", str, priv);
498 while (indexes[i] != NEWT_ARG_LAST) {
499 if (indexes[i] != NEWT_ARG_APPEND)
500 fprintf(stderr, " %d,", indexes[i]);
501 else
502 fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
503 ++i;
504 }
505 fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
506 fflush(stderr);
507#endif
508 newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
509}
510
511static char *callchain_list__sym_name(struct callchain_list *self,
512 char *bf, size_t bfsize)
513{
b3c9ac08
ACM
514 if (self->ms.sym)
515 return self->ms.sym->name;
4ded2b25
ACM
516
517 snprintf(bf, bfsize, "%#Lx", self->ip);
518 return bf;
519}
520
521static void __callchain__append_graph_browser(struct callchain_node *self,
522 newtComponent tree, u64 total,
523 int *indexes, int depth)
524{
525 struct rb_node *node;
526 u64 new_total, remaining;
527 int idx = 0;
528
529 if (callchain_param.mode == CHAIN_GRAPH_REL)
530 new_total = self->children_hit;
531 else
532 new_total = total;
533
534 remaining = new_total;
535 node = rb_first(&self->rb_root);
536 while (node) {
537 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
538 struct rb_node *next = rb_next(node);
539 u64 cumul = cumul_hits(child);
540 struct callchain_list *chain;
541 int first = true, printed = 0;
542 int chain_idx = -1;
543 remaining -= cumul;
544
545 indexes[depth] = NEWT_ARG_APPEND;
546 indexes[depth + 1] = NEWT_ARG_LAST;
547
548 list_for_each_entry(chain, &child->val, list) {
549 char ipstr[BITS_PER_LONG / 4 + 1],
550 *alloc_str = NULL;
551 const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
552
553 if (first) {
554 double percent = cumul * 100.0 / new_total;
555
556 first = false;
557 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
558 str = "Not enough memory!";
559 else
560 str = alloc_str;
561 } else {
562 indexes[depth] = idx;
563 indexes[depth + 1] = NEWT_ARG_APPEND;
564 indexes[depth + 2] = NEWT_ARG_LAST;
565 ++chain_idx;
566 }
d5679ae4 567 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
4ded2b25
ACM
568 free(alloc_str);
569 ++printed;
570 }
571
572 indexes[depth] = idx;
573 if (chain_idx != -1)
574 indexes[depth + 1] = chain_idx;
575 if (printed != 0)
576 ++idx;
577 __callchain__append_graph_browser(child, tree, new_total, indexes,
578 depth + (chain_idx != -1 ? 2 : 1));
579 node = next;
580 }
581}
582
583static void callchain__append_graph_browser(struct callchain_node *self,
584 newtComponent tree, u64 total,
585 int *indexes, int parent_idx)
586{
587 struct callchain_list *chain;
588 int i = 0;
589
590 indexes[1] = NEWT_ARG_APPEND;
591 indexes[2] = NEWT_ARG_LAST;
592
593 list_for_each_entry(chain, &self->val, list) {
594 char ipstr[BITS_PER_LONG / 4 + 1], *str;
595
596 if (chain->ip >= PERF_CONTEXT_MAX)
597 continue;
598
599 if (!i++ && sort__first_dimension == SORT_SYM)
600 continue;
601
602 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
d5679ae4 603 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
4ded2b25
ACM
604 }
605
606 indexes[1] = parent_idx;
607 indexes[2] = NEWT_ARG_APPEND;
608 indexes[3] = NEWT_ARG_LAST;
609 __callchain__append_graph_browser(self, tree, total, indexes, 2);
610}
611
612static void hist_entry__append_callchain_browser(struct hist_entry *self,
613 newtComponent tree, u64 total, int parent_idx)
614{
615 struct rb_node *rb_node;
616 int indexes[1024] = { [0] = parent_idx, };
617 int idx = 0;
618 struct callchain_node *chain;
619
620 rb_node = rb_first(&self->sorted_chain);
621 while (rb_node) {
622 chain = rb_entry(rb_node, struct callchain_node, rb_node);
623 switch (callchain_param.mode) {
624 case CHAIN_FLAT:
625 break;
626 case CHAIN_GRAPH_ABS: /* falldown */
627 case CHAIN_GRAPH_REL:
628 callchain__append_graph_browser(chain, tree, total, indexes, idx++);
629 break;
630 case CHAIN_NONE:
631 default:
632 break;
633 }
634 rb_node = rb_next(rb_node);
635 }
636}
637
f9224c5c 638static size_t hist_entry__append_browser(struct hist_entry *self,
4ded2b25 639 newtComponent tree, u64 total)
f9224c5c 640{
a4e3b956
ACM
641 char s[256];
642 size_t ret;
f9224c5c
ACM
643
644 if (symbol_conf.exclude_other && !self->parent)
645 return 0;
646
a4e3b956
ACM
647 ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
648 false, 0, false, total);
4ded2b25
ACM
649 if (symbol_conf.use_callchain) {
650 int indexes[2];
651
652 indexes[0] = NEWT_ARG_APPEND;
653 indexes[1] = NEWT_ARG_LAST;
d5679ae4 654 newt_checkbox_tree__add(tree, s, &self->ms, indexes);
4ded2b25 655 } else
d5679ae4 656 newtListboxAppendEntry(tree, s, &self->ms);
4ded2b25 657
a4e3b956 658 return ret;
f9224c5c
ACM
659}
660
ef7b93a1 661static void hist_entry__annotate_browser(struct hist_entry *self)
f9224c5c 662{
ef7b93a1 663 struct ui_browser browser;
f9224c5c 664 struct newtExitStruct es;
ef7b93a1
ACM
665 struct objdump_line *pos, *n;
666 LIST_HEAD(head);
f9224c5c 667
ef7b93a1 668 if (self->ms.sym == NULL)
f9224c5c
ACM
669 return;
670
ef7b93a1 671 if (hist_entry__annotate(self, &head) < 0)
f9224c5c
ACM
672 return;
673
60553903 674 ui_helpline__push("Press <- or ESC to exit");
f9224c5c 675
ef7b93a1
ACM
676 memset(&browser, 0, sizeof(browser));
677 browser.entries = &head;
678 browser.priv = self;
679 list_for_each_entry(pos, &head, node) {
680 size_t line_len = strlen(pos->line);
681 if (browser.width < line_len)
682 browser.width = line_len;
683 ++browser.nr_entries;
f9224c5c 684 }
f9224c5c 685
ef7b93a1
ACM
686 browser.width += 18; /* Percentage */
687 ui_browser__run(&browser, self->ms.sym->name, &es);
688 newtFormDestroy(browser.form);
f9224c5c 689 newtPopWindow();
ef7b93a1
ACM
690 list_for_each_entry_safe(pos, n, &head, node) {
691 list_del(&pos->node);
692 objdump_line__free(pos);
693 }
3798ed7b 694 ui_helpline__pop();
f9224c5c
ACM
695}
696
53c54019
ACM
697static const void *newt__symbol_tree_get_current(newtComponent self)
698{
699 if (symbol_conf.use_callchain)
700 return newtCheckboxTreeGetCurrent(self);
701 return newtListboxGetCurrent(self);
702}
703
e65713ea 704static void hist_browser__selection(newtComponent self, void *data)
53c54019 705{
d5679ae4 706 const struct map_symbol **symbol_ptr = data;
53c54019
ACM
707 *symbol_ptr = newt__symbol_tree_get_current(self);
708}
709
e65713ea
ACM
710struct hist_browser {
711 newtComponent form, tree;
712 const struct map_symbol *selection;
713};
714
715static struct hist_browser *hist_browser__new(void)
716{
717 struct hist_browser *self = malloc(sizeof(*self));
718
83753190
ACM
719 if (self != NULL)
720 self->form = NULL;
e65713ea
ACM
721
722 return self;
723}
724
725static void hist_browser__delete(struct hist_browser *self)
726{
727 newtFormDestroy(self->form);
728 newtPopWindow();
729 free(self);
730}
731
b09e0190
ACM
732static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
733 const char *title)
f9224c5c 734{
e65713ea
ACM
735 int max_len = 0, idx, cols, rows;
736 struct ui_progress *progress;
f9224c5c 737 struct rb_node *nd;
5f4d3f88 738 u64 curr_hist = 0;
c82ee828 739 char seq[] = ".", unit;
83753190 740 char str[256];
c82ee828 741 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
83753190
ACM
742
743 if (self->form) {
744 newtFormDestroy(self->form);
745 newtPopWindow();
746 }
747
c82ee828
ACM
748 nr_events = convert_unit(nr_events, &unit);
749 snprintf(str, sizeof(str), "Events: %lu%c ",
750 nr_events, unit);
83753190
ACM
751 newtDrawRootText(0, 0, str);
752
753 newtGetScreenSize(NULL, &rows);
754
755 if (symbol_conf.use_callchain)
756 self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
757 NEWT_FLAG_SCROLL);
758 else
759 self->tree = newtListbox(0, 0, rows - 5,
760 (NEWT_FLAG_SCROLL |
761 NEWT_FLAG_RETURNEXIT));
762
763 newtComponentAddCallback(self->tree, hist_browser__selection,
764 &self->selection);
5f4d3f88 765
b09e0190
ACM
766 progress = ui_progress__new("Adding entries to the browser...",
767 hists->nr_entries);
5f4d3f88
ACM
768 if (progress == NULL)
769 return -1;
f9224c5c 770
4ded2b25 771 idx = 0;
b09e0190 772 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
f9224c5c 773 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
83753190
ACM
774 int len;
775
776 if (h->filtered)
777 continue;
778
cee75ac7 779 len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
f9224c5c
ACM
780 if (len > max_len)
781 max_len = len;
d5679ae4 782 if (symbol_conf.use_callchain)
e65713ea 783 hist_entry__append_callchain_browser(h, self->tree,
cee75ac7 784 hists->stats.total_period, idx++);
5f4d3f88
ACM
785 ++curr_hist;
786 if (curr_hist % 5)
787 ui_progress__update(progress, curr_hist);
f9224c5c
ACM
788 }
789
5f4d3f88
ACM
790 ui_progress__delete(progress);
791
e65713ea
ACM
792 newtGetScreenSize(&cols, &rows);
793
4ded2b25
ACM
794 if (max_len > cols)
795 max_len = cols - 3;
796
797 if (!symbol_conf.use_callchain)
e65713ea 798 newtListboxSetWidth(self->tree, max_len);
4ded2b25
ACM
799
800 newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
6e7ab4c6 801 rows - 5, title);
e65713ea 802 self->form = newt_form__new();
83753190
ACM
803 if (self->form == NULL)
804 return -1;
805
e65713ea
ACM
806 newtFormAddHotKey(self->form, 'A');
807 newtFormAddHotKey(self->form, 'a');
9d192e11
ACM
808 newtFormAddHotKey(self->form, 'D');
809 newtFormAddHotKey(self->form, 'd');
810 newtFormAddHotKey(self->form, 'T');
811 newtFormAddHotKey(self->form, 't');
a9a4ab74
ACM
812 newtFormAddHotKey(self->form, '?');
813 newtFormAddHotKey(self->form, 'H');
814 newtFormAddHotKey(self->form, 'h');
815 newtFormAddHotKey(self->form, NEWT_KEY_F1);
e65713ea
ACM
816 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
817 newtFormAddComponents(self->form, self->tree, NULL);
818 self->selection = newt__symbol_tree_get_current(self->tree);
819
820 return 0;
821}
822
ef7b93a1 823static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
a5e29aca
ACM
824{
825 int *indexes;
826
827 if (!symbol_conf.use_callchain)
828 goto out;
829
830 indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
831 if (indexes) {
832 bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
833 free(indexes);
834 if (is_hist_entry)
835 goto out;
836 }
837 return NULL;
838out:
ef7b93a1
ACM
839 return container_of(self->selection, struct hist_entry, ms);
840}
841
842static struct thread *hist_browser__selected_thread(struct hist_browser *self)
843{
844 struct hist_entry *he = hist_browser__selected_entry(self);
845 return he ? he->thread : NULL;
a5e29aca
ACM
846}
847
6e7ab4c6
ACM
848static int hist_browser__title(char *bf, size_t size, const char *input_name,
849 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);
862 return printed ?: snprintf(bf, size, "Report: %s", input_name);
863}
864
b09e0190 865int hists__browse(struct hists *self, const char *helpline, const char *input_name)
e65713ea 866{
6e7ab4c6 867 struct hist_browser *browser = hist_browser__new();
3e1bbdc3 868 struct pstack *fstack = pstack__new(2);
6e7ab4c6
ACM
869 const struct thread *thread_filter = NULL;
870 const struct dso *dso_filter = NULL;
e65713ea 871 struct newtExitStruct es;
6e7ab4c6 872 char msg[160];
e65713ea 873 int err = -1;
e65713ea
ACM
874
875 if (browser == NULL)
876 return -1;
877
3e1bbdc3
ACM
878 fstack = pstack__new(2);
879 if (fstack == NULL)
880 goto out;
881
3798ed7b 882 ui_helpline__push(helpline);
e65713ea 883
6e7ab4c6
ACM
884 hist_browser__title(msg, sizeof(msg), input_name,
885 dso_filter, thread_filter);
b09e0190 886 if (hist_browser__populate(browser, self, msg) < 0)
3e1bbdc3 887 goto out_free_stack;
f9224c5c
ACM
888
889 while (1) {
a5e29aca 890 const struct thread *thread;
6e7ab4c6 891 const struct dso *dso;
83753190
ACM
892 char *options[16];
893 int nr_options = 0, choice = 0, i,
a5e29aca 894 annotate = -2, zoom_dso = -2, zoom_thread = -2;
f9224c5c 895
e65713ea 896 newtFormRun(browser->form, &es);
9d192e11
ACM
897
898 thread = hist_browser__selected_thread(browser);
899 dso = browser->selection->map ? browser->selection->map->dso : NULL;
900
53c54019 901 if (es.reason == NEWT_EXIT_HOTKEY) {
a9a4ab74
ACM
902 if (es.u.key == NEWT_KEY_F1)
903 goto do_help;
904
9d192e11
ACM
905 switch (toupper(es.u.key)) {
906 case 'A':
d5679ae4 907 goto do_annotate;
9d192e11
ACM
908 case 'D':
909 goto zoom_dso;
910 case 'T':
911 goto zoom_thread;
a9a4ab74
ACM
912 case 'H':
913 case '?':
914do_help:
915 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
916 "<- Zoom out\n"
917 "a Annotate current symbol\n"
918 "h/?/F1 Show this window\n"
919 "d Zoom into current DSO\n"
920 "t Zoom into current Thread\n"
921 "q/CTRL+C Exit browser");
922 continue;
9d192e11
ACM
923 default:;
924 }
29351db6
ACM
925 if (toupper(es.u.key) == 'Q' ||
926 es.u.key == CTRL('c'))
927 break;
928 if (es.u.key == NEWT_KEY_ESCAPE) {
53c54019
ACM
929 if (dialog_yesno("Do you really want to exit?"))
930 break;
931 else
932 continue;
933 }
3e1bbdc3
ACM
934
935 if (es.u.key == NEWT_KEY_LEFT) {
936 const void *top;
937
938 if (pstack__empty(fstack))
939 continue;
940 top = pstack__pop(fstack);
941 if (top == &dso_filter)
942 goto zoom_out_dso;
943 if (top == &thread_filter)
944 goto zoom_out_thread;
945 continue;
946 }
53c54019
ACM
947 }
948
83753190
ACM
949 if (browser->selection->sym != NULL &&
950 asprintf(&options[nr_options], "Annotate %s",
951 browser->selection->sym->name) > 0)
952 annotate = nr_options++;
953
a5e29aca
ACM
954 if (thread != NULL &&
955 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
6e7ab4c6
ACM
956 (thread_filter ? "out of" : "into"),
957 (thread->comm_set ? thread->comm : ""),
958 thread->pid) > 0)
a5e29aca
ACM
959 zoom_thread = nr_options++;
960
6e7ab4c6
ACM
961 if (dso != NULL &&
962 asprintf(&options[nr_options], "Zoom %s %s DSO",
963 (dso_filter ? "out of" : "into"),
964 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
965 zoom_dso = nr_options++;
966
83753190 967 options[nr_options++] = (char *)"Exit";
53c54019 968
53c54019 969 choice = popup_menu(nr_options, options);
83753190
ACM
970
971 for (i = 0; i < nr_options - 1; ++i)
972 free(options[i]);
973
53c54019 974 if (choice == nr_options - 1)
f9224c5c 975 break;
a5e29aca
ACM
976
977 if (choice == -1)
978 continue;
c1ec5fef 979
83753190 980 if (choice == annotate) {
ef7b93a1 981 struct hist_entry *he;
c1ec5fef 982do_annotate:
e65713ea 983 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
3798ed7b 984 ui_helpline__puts("No vmlinux file found, can't "
d5679ae4
ACM
985 "annotate with just a "
986 "kallsyms file");
987 continue;
988 }
ef7b93a1
ACM
989
990 he = hist_browser__selected_entry(browser);
991 if (he == NULL)
992 continue;
993
994 hist_entry__annotate_browser(he);
a5e29aca 995 } else if (choice == zoom_dso) {
9d192e11 996zoom_dso:
6e7ab4c6 997 if (dso_filter) {
3e1bbdc3
ACM
998 pstack__remove(fstack, &dso_filter);
999zoom_out_dso:
3798ed7b 1000 ui_helpline__pop();
6e7ab4c6
ACM
1001 dso_filter = NULL;
1002 } else {
9d192e11
ACM
1003 if (dso == NULL)
1004 continue;
3e1bbdc3 1005 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
3798ed7b 1006 dso->kernel ? "the Kernel" : dso->short_name);
6e7ab4c6 1007 dso_filter = dso;
3e1bbdc3 1008 pstack__push(fstack, &dso_filter);
6e7ab4c6 1009 }
b09e0190 1010 hists__filter_by_dso(self, dso_filter);
6e7ab4c6
ACM
1011 hist_browser__title(msg, sizeof(msg), input_name,
1012 dso_filter, thread_filter);
b09e0190 1013 if (hist_browser__populate(browser, self, msg) < 0)
83753190 1014 goto out;
a5e29aca 1015 } else if (choice == zoom_thread) {
9d192e11 1016zoom_thread:
6e7ab4c6 1017 if (thread_filter) {
3e1bbdc3
ACM
1018 pstack__remove(fstack, &thread_filter);
1019zoom_out_thread:
3798ed7b 1020 ui_helpline__pop();
6e7ab4c6
ACM
1021 thread_filter = NULL;
1022 } else {
3e1bbdc3 1023 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
3798ed7b
ACM
1024 thread->comm_set ? thread->comm : "",
1025 thread->pid);
6e7ab4c6 1026 thread_filter = thread;
3e1bbdc3 1027 pstack__push(fstack, &thread_filter);
6e7ab4c6 1028 }
b09e0190 1029 hists__filter_by_thread(self, thread_filter);
6e7ab4c6
ACM
1030 hist_browser__title(msg, sizeof(msg), input_name,
1031 dso_filter, thread_filter);
b09e0190 1032 if (hist_browser__populate(browser, self, msg) < 0)
a5e29aca 1033 goto out;
d5679ae4 1034 }
f9224c5c 1035 }
e65713ea 1036 err = 0;
3e1bbdc3
ACM
1037out_free_stack:
1038 pstack__delete(fstack);
e65713ea
ACM
1039out:
1040 hist_browser__delete(browser);
1041 return err;
f9224c5c
ACM
1042}
1043
ef7b93a1
ACM
1044static struct newtPercentTreeColors {
1045 const char *topColorFg, *topColorBg;
1046 const char *mediumColorFg, *mediumColorBg;
1047 const char *normalColorFg, *normalColorBg;
1048 const char *selColorFg, *selColorBg;
1049 const char *codeColorFg, *codeColorBg;
1050} defaultPercentTreeColors = {
1051 "red", "lightgray",
1052 "green", "lightgray",
1053 "black", "lightgray",
1054 "lightgray", "magenta",
1055 "blue", "lightgray",
1056};
1057
f9224c5c
ACM
1058void setup_browser(void)
1059{
ef7b93a1 1060 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
f9224c5c
ACM
1061 if (!isatty(1))
1062 return;
1063
1064 use_browser = true;
1065 newtInit();
1066 newtCls();
3798ed7b 1067 ui_helpline__puts(" ");
dc4ff193
ACM
1068 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1069 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
1070 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
1071 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
1072 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
f9224c5c
ACM
1073}
1074
f3a1f0ea 1075void exit_browser(bool wait_for_ok)
f9224c5c 1076{
f3a1f0ea
ACM
1077 if (use_browser) {
1078 if (wait_for_ok) {
1079 char title[] = "Fatal Error", ok[] = "Ok";
1080 newtWinMessage(title, ok, browser__last_msg);
1081 }
f9224c5c 1082 newtFinished();
f3a1f0ea 1083 }
f9224c5c 1084}