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
10 #ifndef HAVE_LONG_LONG
11 #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
16 #include <sys/ttydefaults.h>
25 #if SLANG_VERSION < 20104
26 #define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
27 #define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
28 #define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
29 (char *)fg, (char *)bg)
31 #define slsmg_printf SLsmg_printf
32 #define slsmg_write_nstring SLsmg_write_nstring
33 #define sltt_set_color SLtt_set_color
37 newtComponent form
, scale
;
40 struct ui_progress
*ui_progress__new(const char *title
, u64 total
)
42 struct ui_progress
*self
= malloc(sizeof(*self
));
49 newtGetScreenSize(&cols
, NULL
);
51 newtCenteredWindow(cols
, 1, title
);
52 self
->form
= newtForm(NULL
, NULL
, 0);
53 if (self
->form
== NULL
)
55 self
->scale
= newtScale(0, 0, cols
, total
);
56 if (self
->scale
== NULL
)
58 newtFormAddComponent(self
->form
, self
->scale
);
65 newtFormDestroy(self
->form
);
71 void ui_progress__update(struct ui_progress
*self
, u64 curr
)
74 * FIXME: We should have a per UI backend way of showing progress,
75 * stdio will just show a percentage as NN%, etc.
79 newtScaleSet(self
->scale
, curr
);
83 void ui_progress__delete(struct ui_progress
*self
)
85 if (use_browser
> 0) {
86 newtFormDestroy(self
->form
);
92 static void ui_helpline__pop(void)
97 static void ui_helpline__push(const char *msg
)
99 newtPushHelpLine(msg
);
102 static void ui_helpline__vpush(const char *fmt
, va_list ap
)
106 if (vasprintf(&s
, fmt
, ap
) < 0)
107 vfprintf(stderr
, fmt
, ap
);
109 ui_helpline__push(s
);
114 static void ui_helpline__fpush(const char *fmt
, ...)
119 ui_helpline__vpush(fmt
, ap
);
123 static void ui_helpline__puts(const char *msg
)
126 ui_helpline__push(msg
);
129 static char browser__last_msg
[1024];
131 int browser__show_help(const char *format
, va_list ap
)
136 ret
= vsnprintf(browser__last_msg
+ backlog
,
137 sizeof(browser__last_msg
) - backlog
, format
, ap
);
140 if (browser__last_msg
[backlog
- 1] == '\n') {
141 ui_helpline__puts(browser__last_msg
);
149 static void newt_form__set_exit_keys(newtComponent self
)
151 newtFormAddHotKey(self
, NEWT_KEY_LEFT
);
152 newtFormAddHotKey(self
, NEWT_KEY_ESCAPE
);
153 newtFormAddHotKey(self
, 'Q');
154 newtFormAddHotKey(self
, 'q');
155 newtFormAddHotKey(self
, CTRL('c'));
158 static newtComponent
newt_form__new(void)
160 newtComponent self
= newtForm(NULL
, NULL
, 0);
162 newt_form__set_exit_keys(self
);
166 static int popup_menu(int argc
, char * const argv
[])
168 struct newtExitStruct es
;
169 int i
, rc
= -1, max_len
= 5;
170 newtComponent listbox
, form
= newt_form__new();
175 listbox
= newtListbox(0, 0, argc
, NEWT_FLAG_RETURNEXIT
);
177 goto out_destroy_form
;
179 newtFormAddComponent(form
, listbox
);
181 for (i
= 0; i
< argc
; ++i
) {
182 int len
= strlen(argv
[i
]);
185 if (newtListboxAddEntry(listbox
, argv
[i
], (void *)(long)i
))
186 goto out_destroy_form
;
189 newtCenteredWindow(max_len
, argc
, NULL
);
190 newtFormRun(form
, &es
);
191 rc
= newtListboxGetCurrent(listbox
) - NULL
;
192 if (es
.reason
== NEWT_EXIT_HOTKEY
)
196 newtFormDestroy(form
);
200 static int ui__help_window(const char *text
)
202 struct newtExitStruct es
;
203 newtComponent tb
, form
= newt_form__new();
205 int max_len
= 0, nr_lines
= 0;
213 const char *sep
= strchr(t
, '\n');
217 sep
= strchr(t
, '\0');
227 tb
= newtTextbox(0, 0, max_len
, nr_lines
, 0);
229 goto out_destroy_form
;
231 newtTextboxSetText(tb
, text
);
232 newtFormAddComponent(form
, tb
);
233 newtCenteredWindow(max_len
, nr_lines
, NULL
);
234 newtFormRun(form
, &es
);
238 newtFormDestroy(form
);
242 static bool dialog_yesno(const char *msg
)
244 /* newtWinChoice should really be accepting const char pointers... */
245 char yes
[] = "Yes", no
[] = "No";
246 return newtWinChoice(NULL
, yes
, no
, (char *)msg
) == 1;
249 static void ui__error_window(const char *fmt
, ...)
254 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt
, ap
);
258 #define HE_COLORSET_TOP 50
259 #define HE_COLORSET_MEDIUM 51
260 #define HE_COLORSET_NORMAL 52
261 #define HE_COLORSET_SELECTED 53
262 #define HE_COLORSET_CODE 54
264 static int ui_browser__percent_color(double percent
, bool current
)
267 return HE_COLORSET_SELECTED
;
268 if (percent
>= MIN_RED
)
269 return HE_COLORSET_TOP
;
270 if (percent
>= MIN_GREEN
)
271 return HE_COLORSET_MEDIUM
;
272 return HE_COLORSET_NORMAL
;
276 newtComponent form
, sb
;
277 u64 index
, first_visible_entry_idx
;
278 void *first_visible_entry
, *entries
;
279 u16 top
, left
, width
, height
;
281 unsigned int (*refresh_entries
)(struct ui_browser
*self
);
282 void (*seek
)(struct ui_browser
*self
,
283 off_t offset
, int whence
);
287 static void ui_browser__list_head_seek(struct ui_browser
*self
,
288 off_t offset
, int whence
)
290 struct list_head
*head
= self
->entries
;
291 struct list_head
*pos
;
298 pos
= self
->first_visible_entry
;
308 while (offset
-- != 0)
311 while (offset
++ != 0)
315 self
->first_visible_entry
= pos
;
318 static bool ui_browser__is_current_entry(struct ui_browser
*self
, unsigned row
)
320 return (self
->first_visible_entry_idx
+ row
) == self
->index
;
323 static void ui_browser__refresh_dimensions(struct ui_browser
*self
)
326 newtGetScreenSize(&cols
, &rows
);
328 if (self
->width
> cols
- 4)
329 self
->width
= cols
- 4;
330 self
->height
= rows
- 5;
331 if (self
->height
> self
->nr_entries
)
332 self
->height
= self
->nr_entries
;
333 self
->top
= (rows
- self
->height
) / 2;
334 self
->left
= (cols
- self
->width
) / 2;
337 static void ui_browser__reset_index(struct ui_browser
*self
)
339 self
->index
= self
->first_visible_entry_idx
= 0;
340 self
->seek(self
, 0, SEEK_SET
);
343 static int ui_browser__show(struct ui_browser
*self
, const char *title
)
345 if (self
->form
!= NULL
) {
346 newtFormDestroy(self
->form
);
349 ui_browser__refresh_dimensions(self
);
350 newtCenteredWindow(self
->width
, self
->height
, title
);
351 self
->form
= newt_form__new();
352 if (self
->form
== NULL
)
355 self
->sb
= newtVerticalScrollbar(self
->width
, 0, self
->height
,
357 HE_COLORSET_SELECTED
);
358 if (self
->sb
== NULL
)
361 newtFormAddHotKey(self
->form
, NEWT_KEY_UP
);
362 newtFormAddHotKey(self
->form
, NEWT_KEY_DOWN
);
363 newtFormAddHotKey(self
->form
, NEWT_KEY_PGUP
);
364 newtFormAddHotKey(self
->form
, NEWT_KEY_PGDN
);
365 newtFormAddHotKey(self
->form
, NEWT_KEY_HOME
);
366 newtFormAddHotKey(self
->form
, NEWT_KEY_END
);
367 newtFormAddComponent(self
->form
, self
->sb
);
371 static int objdump_line__show(struct objdump_line
*self
, struct list_head
*head
,
372 int width
, struct hist_entry
*he
, int len
,
375 if (self
->offset
!= -1) {
376 struct symbol
*sym
= he
->ms
.sym
;
377 unsigned int hits
= 0;
378 double percent
= 0.0;
380 struct sym_priv
*priv
= symbol__priv(sym
);
381 struct sym_ext
*sym_ext
= priv
->ext
;
382 struct sym_hist
*h
= priv
->hist
;
383 s64 offset
= self
->offset
;
384 struct objdump_line
*next
= objdump__get_next_ip_line(head
, self
);
386 while (offset
< (s64
)len
&&
387 (next
== NULL
|| offset
< next
->offset
)) {
389 percent
+= sym_ext
[offset
].percent
;
391 hits
+= h
->ip
[offset
];
396 if (sym_ext
== NULL
&& h
->sum
)
397 percent
= 100.0 * hits
/ h
->sum
;
399 color
= ui_browser__percent_color(percent
, current_entry
);
400 SLsmg_set_color(color
);
401 slsmg_printf(" %7.2f ", percent
);
403 SLsmg_set_color(HE_COLORSET_CODE
);
405 int color
= ui_browser__percent_color(0, current_entry
);
406 SLsmg_set_color(color
);
407 slsmg_write_nstring(" ", 9);
410 SLsmg_write_char(':');
411 slsmg_write_nstring(" ", 8);
413 slsmg_write_nstring(" ", width
- 18);
415 slsmg_write_nstring(self
->line
, width
- 18);
420 static int ui_browser__refresh_entries(struct ui_browser
*self
)
424 newtScrollbarSet(self
->sb
, self
->index
, self
->nr_entries
- 1);
425 row
= self
->refresh_entries(self
);
426 SLsmg_set_color(HE_COLORSET_NORMAL
);
427 SLsmg_fill_region(self
->top
+ row
, self
->left
,
428 self
->height
- row
, self
->width
, ' ');
433 static int ui_browser__run(struct ui_browser
*self
, struct newtExitStruct
*es
)
435 if (ui_browser__refresh_entries(self
) < 0)
441 newtFormRun(self
->form
, es
);
443 if (es
->reason
!= NEWT_EXIT_HOTKEY
)
445 if (is_exit_key(es
->u
.key
))
449 if (self
->index
== self
->nr_entries
- 1)
452 if (self
->index
== self
->first_visible_entry_idx
+ self
->height
) {
453 ++self
->first_visible_entry_idx
;
454 self
->seek(self
, +1, SEEK_CUR
);
458 if (self
->index
== 0)
461 if (self
->index
< self
->first_visible_entry_idx
) {
462 --self
->first_visible_entry_idx
;
463 self
->seek(self
, -1, SEEK_CUR
);
468 if (self
->first_visible_entry_idx
+ self
->height
> self
->nr_entries
- 1)
471 offset
= self
->height
;
472 if (self
->index
+ offset
> self
->nr_entries
- 1)
473 offset
= self
->nr_entries
- 1 - self
->index
;
474 self
->index
+= offset
;
475 self
->first_visible_entry_idx
+= offset
;
476 self
->seek(self
, +offset
, SEEK_CUR
);
479 if (self
->first_visible_entry_idx
== 0)
482 if (self
->first_visible_entry_idx
< self
->height
)
483 offset
= self
->first_visible_entry_idx
;
485 offset
= self
->height
;
487 self
->index
-= offset
;
488 self
->first_visible_entry_idx
-= offset
;
489 self
->seek(self
, -offset
, SEEK_CUR
);
492 ui_browser__reset_index(self
);
495 offset
= self
->height
- 1;
496 if (offset
>= self
->nr_entries
)
497 offset
= self
->nr_entries
- 1;
499 self
->index
= self
->nr_entries
- 1;
500 self
->first_visible_entry_idx
= self
->index
- offset
;
501 self
->seek(self
, -offset
, SEEK_END
);
506 if (ui_browser__refresh_entries(self
) < 0)
513 * When debugging newt problems it was useful to be able to "unroll"
514 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
515 * a source file with the sequence of calls to these methods, to then
516 * tweak the arrays to get the intended results, so I'm keeping this code
517 * here, may be useful again in the future.
521 static void newt_checkbox_tree__add(newtComponent tree
, const char *str
,
522 void *priv
, int *indexes
)
525 /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
526 int i
= 0, len
= 40 - strlen(str
);
529 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
530 len
, len
, " ", str
, priv
);
531 while (indexes
[i
] != NEWT_ARG_LAST
) {
532 if (indexes
[i
] != NEWT_ARG_APPEND
)
533 fprintf(stderr
, " %d,", indexes
[i
]);
535 fprintf(stderr
, " %s,", "NEWT_ARG_APPEND");
538 fprintf(stderr
, " %s", " NEWT_ARG_LAST);\n");
541 newtCheckboxTreeAddArray(tree
, str
, priv
, 0, indexes
);
544 static char *callchain_list__sym_name(struct callchain_list
*self
,
545 char *bf
, size_t bfsize
)
548 return self
->ms
.sym
->name
;
550 snprintf(bf
, bfsize
, "%#Lx", self
->ip
);
554 static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser
*self
)
556 struct objdump_line
*pos
;
557 struct list_head
*head
= self
->entries
;
558 struct hist_entry
*he
= self
->priv
;
560 int len
= he
->ms
.sym
->end
- he
->ms
.sym
->start
;
562 if (self
->first_visible_entry
== NULL
|| self
->first_visible_entry
== self
->entries
)
563 self
->first_visible_entry
= head
->next
;
565 pos
= list_entry(self
->first_visible_entry
, struct objdump_line
, node
);
567 list_for_each_entry_from(pos
, head
, node
) {
568 bool current_entry
= ui_browser__is_current_entry(self
, row
);
569 SLsmg_gotorc(self
->top
+ row
, self
->left
);
570 objdump_line__show(pos
, head
, self
->width
,
571 he
, len
, current_entry
);
572 if (++row
== self
->height
)
579 static void __callchain__append_graph_browser(struct callchain_node
*self
,
580 newtComponent tree
, u64 total
,
581 int *indexes
, int depth
)
583 struct rb_node
*node
;
584 u64 new_total
, remaining
;
587 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
588 new_total
= self
->children_hit
;
592 remaining
= new_total
;
593 node
= rb_first(&self
->rb_root
);
595 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
596 struct rb_node
*next
= rb_next(node
);
597 u64 cumul
= cumul_hits(child
);
598 struct callchain_list
*chain
;
599 int first
= true, printed
= 0;
603 indexes
[depth
] = NEWT_ARG_APPEND
;
604 indexes
[depth
+ 1] = NEWT_ARG_LAST
;
606 list_for_each_entry(chain
, &child
->val
, list
) {
607 char ipstr
[BITS_PER_LONG
/ 4 + 1],
609 const char *str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
612 double percent
= cumul
* 100.0 / new_total
;
615 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
616 str
= "Not enough memory!";
620 indexes
[depth
] = idx
;
621 indexes
[depth
+ 1] = NEWT_ARG_APPEND
;
622 indexes
[depth
+ 2] = NEWT_ARG_LAST
;
625 newt_checkbox_tree__add(tree
, str
, &chain
->ms
, indexes
);
630 indexes
[depth
] = idx
;
632 indexes
[depth
+ 1] = chain_idx
;
635 __callchain__append_graph_browser(child
, tree
, new_total
, indexes
,
636 depth
+ (chain_idx
!= -1 ? 2 : 1));
641 static void callchain__append_graph_browser(struct callchain_node
*self
,
642 newtComponent tree
, u64 total
,
643 int *indexes
, int parent_idx
)
645 struct callchain_list
*chain
;
648 indexes
[1] = NEWT_ARG_APPEND
;
649 indexes
[2] = NEWT_ARG_LAST
;
651 list_for_each_entry(chain
, &self
->val
, list
) {
652 char ipstr
[BITS_PER_LONG
/ 4 + 1], *str
;
654 if (chain
->ip
>= PERF_CONTEXT_MAX
)
657 if (!i
++ && sort__first_dimension
== SORT_SYM
)
660 str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
661 newt_checkbox_tree__add(tree
, str
, &chain
->ms
, indexes
);
664 indexes
[1] = parent_idx
;
665 indexes
[2] = NEWT_ARG_APPEND
;
666 indexes
[3] = NEWT_ARG_LAST
;
667 __callchain__append_graph_browser(self
, tree
, total
, indexes
, 2);
670 static void hist_entry__append_callchain_browser(struct hist_entry
*self
,
671 newtComponent tree
, u64 total
, int parent_idx
)
673 struct rb_node
*rb_node
;
674 int indexes
[1024] = { [0] = parent_idx
, };
676 struct callchain_node
*chain
;
678 rb_node
= rb_first(&self
->sorted_chain
);
680 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
681 switch (callchain_param
.mode
) {
684 case CHAIN_GRAPH_ABS
: /* falldown */
685 case CHAIN_GRAPH_REL
:
686 callchain__append_graph_browser(chain
, tree
, total
, indexes
, idx
++);
692 rb_node
= rb_next(rb_node
);
696 static size_t hist_entry__append_browser(struct hist_entry
*self
,
703 if (symbol_conf
.exclude_other
&& !self
->parent
)
706 ret
= hist_entry__snprintf(self
, s
, sizeof(s
), hists
, NULL
,
707 false, 0, false, hists
->stats
.total_period
);
708 if (symbol_conf
.use_callchain
) {
711 indexes
[0] = NEWT_ARG_APPEND
;
712 indexes
[1] = NEWT_ARG_LAST
;
713 newt_checkbox_tree__add(tree
, s
, &self
->ms
, indexes
);
715 newtListboxAppendEntry(tree
, s
, &self
->ms
);
720 int hist_entry__tui_annotate(struct hist_entry
*self
)
722 struct ui_browser browser
;
723 struct newtExitStruct es
;
724 struct objdump_line
*pos
, *n
;
728 if (self
->ms
.sym
== NULL
)
731 if (self
->ms
.map
->dso
->annotate_warned
)
734 if (hist_entry__annotate(self
, &head
) < 0) {
735 ui__error_window(browser__last_msg
);
739 ui_helpline__push("Press <- or ESC to exit");
741 memset(&browser
, 0, sizeof(browser
));
742 browser
.entries
= &head
;
743 browser
.refresh_entries
= hist_entry__annotate_browser_refresh
;
744 browser
.seek
= ui_browser__list_head_seek
;
746 list_for_each_entry(pos
, &head
, node
) {
747 size_t line_len
= strlen(pos
->line
);
748 if (browser
.width
< line_len
)
749 browser
.width
= line_len
;
750 ++browser
.nr_entries
;
753 browser
.width
+= 18; /* Percentage */
754 ui_browser__show(&browser
, self
->ms
.sym
->name
);
755 newtFormAddHotKey(browser
.form
, ' ');
756 ret
= ui_browser__run(&browser
, &es
);
757 newtFormDestroy(browser
.form
);
759 list_for_each_entry_safe(pos
, n
, &head
, node
) {
760 list_del(&pos
->node
);
761 objdump_line__free(pos
);
767 static const void *newt__symbol_tree_get_current(newtComponent self
)
769 if (symbol_conf
.use_callchain
)
770 return newtCheckboxTreeGetCurrent(self
);
771 return newtListboxGetCurrent(self
);
774 static void hist_browser__selection(newtComponent self
, void *data
)
776 const struct map_symbol
**symbol_ptr
= data
;
777 *symbol_ptr
= newt__symbol_tree_get_current(self
);
780 struct hist_browser
{
781 newtComponent form
, tree
;
782 const struct map_symbol
*selection
;
785 static struct hist_browser
*hist_browser__new(void)
787 struct hist_browser
*self
= malloc(sizeof(*self
));
795 static void hist_browser__delete(struct hist_browser
*self
)
797 newtFormDestroy(self
->form
);
802 static int hist_browser__populate(struct hist_browser
*self
, struct hists
*hists
,
805 int max_len
= 0, idx
, cols
, rows
;
806 struct ui_progress
*progress
;
809 char seq
[] = ".", unit
;
811 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
814 newtFormDestroy(self
->form
);
818 nr_events
= convert_unit(nr_events
, &unit
);
819 snprintf(str
, sizeof(str
), "Events: %lu%c ",
821 newtDrawRootText(0, 0, str
);
823 newtGetScreenSize(NULL
, &rows
);
825 if (symbol_conf
.use_callchain
)
826 self
->tree
= newtCheckboxTreeMulti(0, 0, rows
- 5, seq
,
829 self
->tree
= newtListbox(0, 0, rows
- 5,
831 NEWT_FLAG_RETURNEXIT
));
833 newtComponentAddCallback(self
->tree
, hist_browser__selection
,
836 progress
= ui_progress__new("Adding entries to the browser...",
838 if (progress
== NULL
)
842 for (nd
= rb_first(&hists
->entries
); nd
; nd
= rb_next(nd
)) {
843 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
849 len
= hist_entry__append_browser(h
, self
->tree
, hists
);
852 if (symbol_conf
.use_callchain
)
853 hist_entry__append_callchain_browser(h
, self
->tree
,
854 hists
->stats
.total_period
, idx
++);
857 ui_progress__update(progress
, curr_hist
);
860 ui_progress__delete(progress
);
862 newtGetScreenSize(&cols
, &rows
);
867 if (!symbol_conf
.use_callchain
)
868 newtListboxSetWidth(self
->tree
, max_len
);
870 newtCenteredWindow(max_len
+ (symbol_conf
.use_callchain
? 5 : 0),
872 self
->form
= newt_form__new();
873 if (self
->form
== NULL
)
876 newtFormAddHotKey(self
->form
, 'A');
877 newtFormAddHotKey(self
->form
, 'a');
878 newtFormAddHotKey(self
->form
, 'D');
879 newtFormAddHotKey(self
->form
, 'd');
880 newtFormAddHotKey(self
->form
, 'T');
881 newtFormAddHotKey(self
->form
, 't');
882 newtFormAddHotKey(self
->form
, '?');
883 newtFormAddHotKey(self
->form
, 'H');
884 newtFormAddHotKey(self
->form
, 'h');
885 newtFormAddHotKey(self
->form
, NEWT_KEY_F1
);
886 newtFormAddHotKey(self
->form
, NEWT_KEY_RIGHT
);
887 newtFormAddHotKey(self
->form
, NEWT_KEY_TAB
);
888 newtFormAddHotKey(self
->form
, NEWT_KEY_UNTAB
);
889 newtFormAddComponents(self
->form
, self
->tree
, NULL
);
890 self
->selection
= newt__symbol_tree_get_current(self
->tree
);
895 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*self
)
899 if (!symbol_conf
.use_callchain
)
902 indexes
= newtCheckboxTreeFindItem(self
->tree
, (void *)self
->selection
);
904 bool is_hist_entry
= indexes
[1] == NEWT_ARG_LAST
;
911 return container_of(self
->selection
, struct hist_entry
, ms
);
914 static struct thread
*hist_browser__selected_thread(struct hist_browser
*self
)
916 struct hist_entry
*he
= hist_browser__selected_entry(self
);
917 return he
? he
->thread
: NULL
;
920 static int hist_browser__title(char *bf
, size_t size
, const char *ev_name
,
921 const struct dso
*dso
, const struct thread
*thread
)
926 printed
+= snprintf(bf
+ printed
, size
- printed
,
928 (thread
->comm_set
? thread
->comm
: ""),
931 printed
+= snprintf(bf
+ printed
, size
- printed
,
932 "%sDSO: %s", thread
? " " : "",
934 return printed
?: snprintf(bf
, size
, "Event: %s", ev_name
);
937 int hists__browse(struct hists
*self
, const char *helpline
, const char *ev_name
)
939 struct hist_browser
*browser
= hist_browser__new();
940 struct pstack
*fstack
;
941 const struct thread
*thread_filter
= NULL
;
942 const struct dso
*dso_filter
= NULL
;
943 struct newtExitStruct es
;
950 fstack
= pstack__new(2);
954 ui_helpline__push(helpline
);
956 hist_browser__title(msg
, sizeof(msg
), ev_name
,
957 dso_filter
, thread_filter
);
958 if (hist_browser__populate(browser
, self
, msg
) < 0)
962 const struct thread
*thread
;
963 const struct dso
*dso
;
965 int nr_options
= 0, choice
= 0, i
,
966 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2;
968 newtFormRun(browser
->form
, &es
);
970 thread
= hist_browser__selected_thread(browser
);
971 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
973 if (es
.reason
== NEWT_EXIT_HOTKEY
) {
982 * Exit the browser, let hists__browser_tree
983 * go to the next or previous
992 if (browser
->selection
->map
== NULL
&&
993 browser
->selection
->map
->dso
->annotate_warned
)
1003 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
1005 "a Annotate current symbol\n"
1006 "h/?/F1 Show this window\n"
1007 "d Zoom into current DSO\n"
1008 "t Zoom into current Thread\n"
1009 "q/CTRL+C Exit browser");
1013 if (is_exit_key(key
)) {
1014 if (key
== NEWT_KEY_ESCAPE
) {
1015 if (dialog_yesno("Do you really want to exit?"))
1023 if (es
.u
.key
== NEWT_KEY_LEFT
) {
1026 if (pstack__empty(fstack
))
1028 top
= pstack__pop(fstack
);
1029 if (top
== &dso_filter
)
1031 if (top
== &thread_filter
)
1032 goto zoom_out_thread
;
1037 if (browser
->selection
->sym
!= NULL
&&
1038 !browser
->selection
->map
->dso
->annotate_warned
&&
1039 asprintf(&options
[nr_options
], "Annotate %s",
1040 browser
->selection
->sym
->name
) > 0)
1041 annotate
= nr_options
++;
1043 if (thread
!= NULL
&&
1044 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1045 (thread_filter
? "out of" : "into"),
1046 (thread
->comm_set
? thread
->comm
: ""),
1048 zoom_thread
= nr_options
++;
1051 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1052 (dso_filter
? "out of" : "into"),
1053 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1054 zoom_dso
= nr_options
++;
1056 options
[nr_options
++] = (char *)"Exit";
1058 choice
= popup_menu(nr_options
, options
);
1060 for (i
= 0; i
< nr_options
- 1; ++i
)
1063 if (choice
== nr_options
- 1)
1069 if (choice
== annotate
) {
1070 struct hist_entry
*he
;
1072 if (browser
->selection
->map
->dso
->origin
== DSO__ORIG_KERNEL
) {
1073 browser
->selection
->map
->dso
->annotate_warned
= 1;
1074 ui_helpline__puts("No vmlinux file found, can't "
1075 "annotate with just a "
1080 he
= hist_browser__selected_entry(browser
);
1084 hist_entry__tui_annotate(he
);
1085 } else if (choice
== zoom_dso
) {
1088 pstack__remove(fstack
, &dso_filter
);
1095 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1096 dso
->kernel
? "the Kernel" : dso
->short_name
);
1098 pstack__push(fstack
, &dso_filter
);
1100 hists__filter_by_dso(self
, dso_filter
);
1101 hist_browser__title(msg
, sizeof(msg
), ev_name
,
1102 dso_filter
, thread_filter
);
1103 if (hist_browser__populate(browser
, self
, msg
) < 0)
1105 } else if (choice
== zoom_thread
) {
1107 if (thread_filter
) {
1108 pstack__remove(fstack
, &thread_filter
);
1111 thread_filter
= NULL
;
1113 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1114 thread
->comm_set
? thread
->comm
: "",
1116 thread_filter
= thread
;
1117 pstack__push(fstack
, &thread_filter
);
1119 hists__filter_by_thread(self
, thread_filter
);
1120 hist_browser__title(msg
, sizeof(msg
), ev_name
,
1121 dso_filter
, thread_filter
);
1122 if (hist_browser__populate(browser
, self
, msg
) < 0)
1127 pstack__delete(fstack
);
1129 hist_browser__delete(browser
);
1133 int hists__tui_browse_tree(struct rb_root
*self
, const char *help
)
1135 struct rb_node
*first
= rb_first(self
), *nd
= first
, *next
;
1139 struct hists
*hists
= rb_entry(nd
, struct hists
, rb_node
);
1140 const char *ev_name
= __event_name(hists
->type
, hists
->config
);
1142 key
= hists__browse(hists
, help
, ev_name
);
1144 if (is_exit_key(key
))
1153 case NEWT_KEY_UNTAB
:
1165 static struct newtPercentTreeColors
{
1166 const char *topColorFg
, *topColorBg
;
1167 const char *mediumColorFg
, *mediumColorBg
;
1168 const char *normalColorFg
, *normalColorBg
;
1169 const char *selColorFg
, *selColorBg
;
1170 const char *codeColorFg
, *codeColorBg
;
1171 } defaultPercentTreeColors
= {
1173 "green", "lightgray",
1174 "black", "lightgray",
1175 "lightgray", "magenta",
1176 "blue", "lightgray",
1179 void setup_browser(void)
1181 struct newtPercentTreeColors
*c
= &defaultPercentTreeColors
;
1183 if (!isatty(1) || !use_browser
|| dump_trace
) {
1192 ui_helpline__puts(" ");
1193 sltt_set_color(HE_COLORSET_TOP
, NULL
, c
->topColorFg
, c
->topColorBg
);
1194 sltt_set_color(HE_COLORSET_MEDIUM
, NULL
, c
->mediumColorFg
, c
->mediumColorBg
);
1195 sltt_set_color(HE_COLORSET_NORMAL
, NULL
, c
->normalColorFg
, c
->normalColorBg
);
1196 sltt_set_color(HE_COLORSET_SELECTED
, NULL
, c
->selColorFg
, c
->selColorBg
);
1197 sltt_set_color(HE_COLORSET_CODE
, NULL
, c
->codeColorFg
, c
->codeColorBg
);
1200 void exit_browser(bool wait_for_ok
)
1202 if (use_browser
> 0) {
1204 char title
[] = "Fatal Error", ok
[] = "Ok";
1205 newtWinMessage(title
, ok
, browser__last_msg
);