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)
512 static char *callchain_list__sym_name(struct callchain_list
*self
,
513 char *bf
, size_t bfsize
)
516 return self
->ms
.sym
->name
;
518 snprintf(bf
, bfsize
, "%#Lx", self
->ip
);
522 static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser
*self
)
524 struct objdump_line
*pos
;
525 struct list_head
*head
= self
->entries
;
526 struct hist_entry
*he
= self
->priv
;
528 int len
= he
->ms
.sym
->end
- he
->ms
.sym
->start
;
530 if (self
->first_visible_entry
== NULL
|| self
->first_visible_entry
== self
->entries
)
531 self
->first_visible_entry
= head
->next
;
533 pos
= list_entry(self
->first_visible_entry
, struct objdump_line
, node
);
535 list_for_each_entry_from(pos
, head
, node
) {
536 bool current_entry
= ui_browser__is_current_entry(self
, row
);
537 SLsmg_gotorc(self
->top
+ row
, self
->left
);
538 objdump_line__show(pos
, head
, self
->width
,
539 he
, len
, current_entry
);
540 if (++row
== self
->height
)
547 int hist_entry__tui_annotate(struct hist_entry
*self
)
549 struct ui_browser browser
;
550 struct newtExitStruct es
;
551 struct objdump_line
*pos
, *n
;
555 if (self
->ms
.sym
== NULL
)
558 if (self
->ms
.map
->dso
->annotate_warned
)
561 if (hist_entry__annotate(self
, &head
) < 0) {
562 ui__error_window(browser__last_msg
);
566 ui_helpline__push("Press <- or ESC to exit");
568 memset(&browser
, 0, sizeof(browser
));
569 browser
.entries
= &head
;
570 browser
.refresh_entries
= hist_entry__annotate_browser_refresh
;
571 browser
.seek
= ui_browser__list_head_seek
;
573 list_for_each_entry(pos
, &head
, node
) {
574 size_t line_len
= strlen(pos
->line
);
575 if (browser
.width
< line_len
)
576 browser
.width
= line_len
;
577 ++browser
.nr_entries
;
580 browser
.width
+= 18; /* Percentage */
581 ui_browser__show(&browser
, self
->ms
.sym
->name
);
582 newtFormAddHotKey(browser
.form
, ' ');
583 ret
= ui_browser__run(&browser
, &es
);
584 newtFormDestroy(browser
.form
);
586 list_for_each_entry_safe(pos
, n
, &head
, node
) {
587 list_del(&pos
->node
);
588 objdump_line__free(pos
);
594 struct hist_browser
{
597 struct hist_entry
*he_selection
;
598 struct map_symbol
*selection
;
601 static void hist_browser__reset(struct hist_browser
*self
);
602 static int hist_browser__run(struct hist_browser
*self
, const char *title
,
603 struct newtExitStruct
*es
);
604 static unsigned int hist_browser__refresh_entries(struct ui_browser
*self
);
605 static void ui_browser__hists_seek(struct ui_browser
*self
,
606 off_t offset
, int whence
);
608 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
610 struct hist_browser
*self
= zalloc(sizeof(*self
));
614 self
->b
.refresh_entries
= hist_browser__refresh_entries
;
615 self
->b
.seek
= ui_browser__hists_seek
;
621 static void hist_browser__delete(struct hist_browser
*self
)
623 newtFormDestroy(self
->b
.form
);
628 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*self
)
630 return self
->he_selection
;
633 static struct thread
*hist_browser__selected_thread(struct hist_browser
*self
)
635 return self
->he_selection
->thread
;
638 static int hist_browser__title(char *bf
, size_t size
, const char *ev_name
,
639 const struct dso
*dso
, const struct thread
*thread
)
644 printed
+= snprintf(bf
+ printed
, size
- printed
,
646 (thread
->comm_set
? thread
->comm
: ""),
649 printed
+= snprintf(bf
+ printed
, size
- printed
,
650 "%sDSO: %s", thread
? " " : "",
652 return printed
?: snprintf(bf
, size
, "Event: %s", ev_name
);
655 int hists__browse(struct hists
*self
, const char *helpline
, const char *ev_name
)
657 struct hist_browser
*browser
= hist_browser__new(self
);
658 struct pstack
*fstack
;
659 const struct thread
*thread_filter
= NULL
;
660 const struct dso
*dso_filter
= NULL
;
661 struct newtExitStruct es
;
668 fstack
= pstack__new(2);
672 ui_helpline__push(helpline
);
674 hist_browser__title(msg
, sizeof(msg
), ev_name
,
675 dso_filter
, thread_filter
);
678 const struct thread
*thread
;
679 const struct dso
*dso
;
681 int nr_options
= 0, choice
= 0, i
,
682 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2;
684 if (hist_browser__run(browser
, msg
, &es
))
687 thread
= hist_browser__selected_thread(browser
);
688 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
690 if (es
.reason
== NEWT_EXIT_HOTKEY
) {
699 * Exit the browser, let hists__browser_tree
700 * go to the next or previous
709 if (browser
->selection
->map
== NULL
&&
710 browser
->selection
->map
->dso
->annotate_warned
)
720 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
722 "a Annotate current symbol\n"
723 "h/?/F1 Show this window\n"
724 "d Zoom into current DSO\n"
725 "t Zoom into current Thread\n"
726 "q/CTRL+C Exit browser");
730 if (is_exit_key(key
)) {
731 if (key
== NEWT_KEY_ESCAPE
) {
732 if (dialog_yesno("Do you really want to exit?"))
740 if (es
.u
.key
== NEWT_KEY_LEFT
) {
743 if (pstack__empty(fstack
))
745 top
= pstack__pop(fstack
);
746 if (top
== &dso_filter
)
748 if (top
== &thread_filter
)
749 goto zoom_out_thread
;
754 if (browser
->selection
->sym
!= NULL
&&
755 !browser
->selection
->map
->dso
->annotate_warned
&&
756 asprintf(&options
[nr_options
], "Annotate %s",
757 browser
->selection
->sym
->name
) > 0)
758 annotate
= nr_options
++;
760 if (thread
!= NULL
&&
761 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
762 (thread_filter
? "out of" : "into"),
763 (thread
->comm_set
? thread
->comm
: ""),
765 zoom_thread
= nr_options
++;
768 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
769 (dso_filter
? "out of" : "into"),
770 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
771 zoom_dso
= nr_options
++;
773 options
[nr_options
++] = (char *)"Exit";
775 choice
= popup_menu(nr_options
, options
);
777 for (i
= 0; i
< nr_options
- 1; ++i
)
780 if (choice
== nr_options
- 1)
786 if (choice
== annotate
) {
787 struct hist_entry
*he
;
789 if (browser
->selection
->map
->dso
->origin
== DSO__ORIG_KERNEL
) {
790 browser
->selection
->map
->dso
->annotate_warned
= 1;
791 ui_helpline__puts("No vmlinux file found, can't "
792 "annotate with just a "
797 he
= hist_browser__selected_entry(browser
);
801 hist_entry__tui_annotate(he
);
802 } else if (choice
== zoom_dso
) {
805 pstack__remove(fstack
, &dso_filter
);
812 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
813 dso
->kernel
? "the Kernel" : dso
->short_name
);
815 pstack__push(fstack
, &dso_filter
);
817 hists__filter_by_dso(self
, dso_filter
);
818 hist_browser__title(msg
, sizeof(msg
), ev_name
,
819 dso_filter
, thread_filter
);
820 hist_browser__reset(browser
);
821 } else if (choice
== zoom_thread
) {
824 pstack__remove(fstack
, &thread_filter
);
827 thread_filter
= NULL
;
829 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
830 thread
->comm_set
? thread
->comm
: "",
832 thread_filter
= thread
;
833 pstack__push(fstack
, &thread_filter
);
835 hists__filter_by_thread(self
, thread_filter
);
836 hist_browser__title(msg
, sizeof(msg
), ev_name
,
837 dso_filter
, thread_filter
);
838 hist_browser__reset(browser
);
842 pstack__delete(fstack
);
844 hist_browser__delete(browser
);
848 int hists__tui_browse_tree(struct rb_root
*self
, const char *help
)
850 struct rb_node
*first
= rb_first(self
), *nd
= first
, *next
;
854 struct hists
*hists
= rb_entry(nd
, struct hists
, rb_node
);
855 const char *ev_name
= __event_name(hists
->type
, hists
->config
);
857 key
= hists__browse(hists
, help
, ev_name
);
859 if (is_exit_key(key
))
880 static struct newtPercentTreeColors
{
881 const char *topColorFg
, *topColorBg
;
882 const char *mediumColorFg
, *mediumColorBg
;
883 const char *normalColorFg
, *normalColorBg
;
884 const char *selColorFg
, *selColorBg
;
885 const char *codeColorFg
, *codeColorBg
;
886 } defaultPercentTreeColors
= {
888 "green", "lightgray",
889 "black", "lightgray",
890 "lightgray", "magenta",
894 void setup_browser(void)
896 struct newtPercentTreeColors
*c
= &defaultPercentTreeColors
;
898 if (!isatty(1) || !use_browser
|| dump_trace
) {
907 ui_helpline__puts(" ");
908 sltt_set_color(HE_COLORSET_TOP
, NULL
, c
->topColorFg
, c
->topColorBg
);
909 sltt_set_color(HE_COLORSET_MEDIUM
, NULL
, c
->mediumColorFg
, c
->mediumColorBg
);
910 sltt_set_color(HE_COLORSET_NORMAL
, NULL
, c
->normalColorFg
, c
->normalColorBg
);
911 sltt_set_color(HE_COLORSET_SELECTED
, NULL
, c
->selColorFg
, c
->selColorBg
);
912 sltt_set_color(HE_COLORSET_CODE
, NULL
, c
->codeColorFg
, c
->codeColorBg
);
915 void exit_browser(bool wait_for_ok
)
917 if (use_browser
> 0) {
919 char title
[] = "Fatal Error", ok
[] = "Ok";
920 newtWinMessage(title
, ok
, browser__last_msg
);
926 static void hist_browser__refresh_dimensions(struct hist_browser
*self
)
928 /* 3 == +/- toggle symbol before actual hist_entry rendering */
929 self
->b
.width
= 3 + (hists__sort_list_width(self
->hists
) +
933 static void hist_browser__reset(struct hist_browser
*self
)
935 self
->b
.nr_entries
= self
->hists
->nr_entries
;
936 hist_browser__refresh_dimensions(self
);
937 ui_browser__reset_index(&self
->b
);
940 static char tree__folded_sign(bool unfolded
)
942 return unfolded
? '-' : '+';
945 static char map_symbol__folded(const struct map_symbol
*self
)
947 return self
->has_children
? tree__folded_sign(self
->unfolded
) : ' ';
950 static char hist_entry__folded(const struct hist_entry
*self
)
952 return map_symbol__folded(&self
->ms
);
955 static char callchain_list__folded(const struct callchain_list
*self
)
957 return map_symbol__folded(&self
->ms
);
960 static bool map_symbol__toggle_fold(struct map_symbol
*self
)
962 if (!self
->has_children
)
965 self
->unfolded
= !self
->unfolded
;
969 #define LEVEL_OFFSET_STEP 3
971 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*self
,
972 struct callchain_node
*chain_node
,
973 u64 total
, int level
,
976 bool *is_current_entry
)
978 struct rb_node
*node
;
979 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
980 u64 new_total
, remaining
;
982 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
983 new_total
= chain_node
->children_hit
;
987 remaining
= new_total
;
988 node
= rb_first(&chain_node
->rb_root
);
990 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
991 struct rb_node
*next
= rb_next(node
);
992 u64 cumul
= cumul_hits(child
);
993 struct callchain_list
*chain
;
994 char folded_sign
= ' ';
996 int extra_offset
= 0;
1000 list_for_each_entry(chain
, &child
->val
, list
) {
1001 char ipstr
[BITS_PER_LONG
/ 4 + 1], *alloc_str
;
1004 bool was_first
= first
;
1008 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
1009 rb_first(&child
->rb_root
) != NULL
;
1011 extra_offset
= LEVEL_OFFSET_STEP
;
1012 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
1013 rb_first(&child
->rb_root
) != NULL
;
1016 folded_sign
= callchain_list__folded(chain
);
1017 if (*row_offset
!= 0) {
1023 str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
1025 double percent
= cumul
* 100.0 / new_total
;
1027 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
1028 str
= "Not enough memory!";
1033 color
= HE_COLORSET_NORMAL
;
1034 width
= self
->b
.width
- (offset
+ extra_offset
+ 2);
1035 if (ui_browser__is_current_entry(&self
->b
, row
)) {
1036 self
->selection
= &chain
->ms
;
1037 color
= HE_COLORSET_SELECTED
;
1038 *is_current_entry
= true;
1041 SLsmg_set_color(color
);
1042 SLsmg_gotorc(self
->b
.top
+ row
, self
->b
.left
);
1043 slsmg_write_nstring(" ", offset
+ extra_offset
);
1044 slsmg_printf("%c ", folded_sign
);
1045 slsmg_write_nstring(str
, width
);
1048 if (++row
== self
->b
.height
)
1051 if (folded_sign
== '+')
1055 if (folded_sign
== '-') {
1056 const int new_level
= level
+ (extra_offset
? 2 : 1);
1057 row
+= hist_browser__show_callchain_node_rb_tree(self
, child
, new_total
,
1058 new_level
, row
, row_offset
,
1061 if (row
== self
->b
.height
)
1066 return row
- first_row
;
1069 static int hist_browser__show_callchain_node(struct hist_browser
*self
,
1070 struct callchain_node
*node
,
1071 int level
, unsigned short row
,
1073 bool *is_current_entry
)
1075 struct callchain_list
*chain
;
1076 int first_row
= row
,
1077 offset
= level
* LEVEL_OFFSET_STEP
,
1078 width
= self
->b
.width
- offset
;
1079 char folded_sign
= ' ';
1081 list_for_each_entry(chain
, &node
->val
, list
) {
1082 char ipstr
[BITS_PER_LONG
/ 4 + 1], *s
;
1085 * FIXME: This should be moved to somewhere else,
1086 * probably when the callchain is created, so as not to
1087 * traverse it all over again
1089 chain
->ms
.has_children
= rb_first(&node
->rb_root
) != NULL
;
1090 folded_sign
= callchain_list__folded(chain
);
1092 if (*row_offset
!= 0) {
1097 color
= HE_COLORSET_NORMAL
;
1098 if (ui_browser__is_current_entry(&self
->b
, row
)) {
1099 self
->selection
= &chain
->ms
;
1100 color
= HE_COLORSET_SELECTED
;
1101 *is_current_entry
= true;
1104 s
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
1105 SLsmg_gotorc(self
->b
.top
+ row
, self
->b
.left
);
1106 SLsmg_set_color(color
);
1107 slsmg_write_nstring(" ", offset
);
1108 slsmg_printf("%c ", folded_sign
);
1109 slsmg_write_nstring(s
, width
- 2);
1111 if (++row
== self
->b
.height
)
1115 if (folded_sign
== '-')
1116 row
+= hist_browser__show_callchain_node_rb_tree(self
, node
,
1117 self
->hists
->stats
.total_period
,
1122 return row
- first_row
;
1125 static int hist_browser__show_callchain(struct hist_browser
*self
,
1126 struct rb_root
*chain
,
1127 int level
, unsigned short row
,
1129 bool *is_current_entry
)
1132 int first_row
= row
;
1134 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
1135 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
1137 row
+= hist_browser__show_callchain_node(self
, node
, level
,
1140 if (row
== self
->b
.height
)
1144 return row
- first_row
;
1147 static int hist_browser__show_entry(struct hist_browser
*self
,
1148 struct hist_entry
*entry
,
1154 int color
, width
= self
->b
.width
;
1155 char folded_sign
= ' ';
1156 bool current_entry
= ui_browser__is_current_entry(&self
->b
, row
);
1157 off_t row_offset
= entry
->row_offset
;
1159 if (current_entry
) {
1160 self
->he_selection
= entry
;
1161 self
->selection
= &entry
->ms
;
1164 if (symbol_conf
.use_callchain
) {
1165 entry
->ms
.has_children
= !RB_EMPTY_ROOT(&entry
->sorted_chain
);
1166 folded_sign
= hist_entry__folded(entry
);
1169 if (row_offset
== 0) {
1170 hist_entry__snprintf(entry
, s
, sizeof(s
), self
->hists
, NULL
, false,
1171 0, false, self
->hists
->stats
.total_period
);
1172 percent
= (entry
->period
* 100.0) / self
->hists
->stats
.total_period
;
1174 color
= HE_COLORSET_SELECTED
;
1175 if (!current_entry
) {
1176 if (percent
>= MIN_RED
)
1177 color
= HE_COLORSET_TOP
;
1178 else if (percent
>= MIN_GREEN
)
1179 color
= HE_COLORSET_MEDIUM
;
1181 color
= HE_COLORSET_NORMAL
;
1184 SLsmg_set_color(color
);
1185 SLsmg_gotorc(self
->b
.top
+ row
, self
->b
.left
);
1186 if (symbol_conf
.use_callchain
) {
1187 slsmg_printf("%c ", folded_sign
);
1190 slsmg_write_nstring(s
, width
);
1196 if (folded_sign
== '-' && row
!= self
->b
.height
) {
1197 printed
+= hist_browser__show_callchain(self
, &entry
->sorted_chain
,
1198 1, row
, &row_offset
,
1201 self
->he_selection
= entry
;
1207 static unsigned int hist_browser__refresh_entries(struct ui_browser
*self
)
1211 struct hist_browser
*hb
= container_of(self
, struct hist_browser
, b
);
1213 if (self
->first_visible_entry
== NULL
)
1214 self
->first_visible_entry
= rb_first(&hb
->hists
->entries
);
1216 for (nd
= self
->first_visible_entry
; nd
; nd
= rb_next(nd
)) {
1217 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1222 row
+= hist_browser__show_entry(hb
, h
, row
);
1223 if (row
== self
->height
)
1230 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*self
)
1232 struct rb_node
*nd
= rb_first(&self
->rb_root
);
1234 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
1235 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
1236 struct callchain_list
*chain
;
1239 list_for_each_entry(chain
, &child
->val
, list
) {
1242 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
1243 rb_first(&child
->rb_root
) != NULL
;
1245 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
1246 rb_first(&child
->rb_root
) != NULL
;
1249 callchain_node__init_have_children_rb_tree(child
);
1253 static void callchain_node__init_have_children(struct callchain_node
*self
)
1255 struct callchain_list
*chain
;
1257 list_for_each_entry(chain
, &self
->val
, list
)
1258 chain
->ms
.has_children
= rb_first(&self
->rb_root
) != NULL
;
1260 callchain_node__init_have_children_rb_tree(self
);
1263 static void callchain__init_have_children(struct rb_root
*self
)
1267 for (nd
= rb_first(self
); nd
; nd
= rb_next(nd
)) {
1268 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
1269 callchain_node__init_have_children(node
);
1273 static void hist_entry__init_have_children(struct hist_entry
*self
)
1275 if (!self
->init_have_children
) {
1276 callchain__init_have_children(&self
->sorted_chain
);
1277 self
->init_have_children
= true;
1281 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
)
1283 while (nd
!= NULL
) {
1284 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1294 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
)
1296 while (nd
!= NULL
) {
1297 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1307 static void ui_browser__hists_seek(struct ui_browser
*self
,
1308 off_t offset
, int whence
)
1310 struct hist_entry
*h
;
1316 nd
= hists__filter_entries(rb_first(self
->entries
));
1319 nd
= self
->first_visible_entry
;
1322 nd
= hists__filter_prev_entries(rb_last(self
->entries
));
1330 * Moves not relative to the first visible entry invalidates its
1333 h
= rb_entry(self
->first_visible_entry
, struct hist_entry
, rb_node
);
1337 * Here we have to check if nd is expanded (+), if it is we can't go
1338 * the next top level hist_entry, instead we must compute an offset of
1339 * what _not_ to show and not change the first visible entry.
1341 * This offset increments when we are going from top to bottom and
1342 * decreases when we're going from bottom to top.
1344 * As we don't have backpointers to the top level in the callchains
1345 * structure, we need to always print the whole hist_entry callchain,
1346 * skipping the first ones that are before the first visible entry
1347 * and stop when we printed enough lines to fill the screen.
1352 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1353 if (h
->ms
.unfolded
) {
1354 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1355 if (offset
> remaining
) {
1356 offset
-= remaining
;
1359 h
->row_offset
+= offset
;
1361 self
->first_visible_entry
= nd
;
1365 nd
= hists__filter_entries(rb_next(nd
));
1369 self
->first_visible_entry
= nd
;
1370 } while (offset
!= 0);
1371 } else if (offset
< 0) {
1373 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1374 if (h
->ms
.unfolded
) {
1376 if (-offset
> h
->row_offset
) {
1377 offset
+= h
->row_offset
;
1380 h
->row_offset
+= offset
;
1382 self
->first_visible_entry
= nd
;
1386 if (-offset
> h
->nr_rows
) {
1387 offset
+= h
->nr_rows
;
1390 h
->row_offset
= h
->nr_rows
+ offset
;
1392 self
->first_visible_entry
= nd
;
1398 nd
= hists__filter_prev_entries(rb_prev(nd
));
1402 self
->first_visible_entry
= nd
;
1405 * Last unfiltered hist_entry, check if it is
1406 * unfolded, if it is then we should have
1407 * row_offset at its last entry.
1409 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1411 h
->row_offset
= h
->nr_rows
;
1417 self
->first_visible_entry
= nd
;
1418 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1423 static int callchain_node__count_rows_rb_tree(struct callchain_node
*self
)
1428 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
1429 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
1430 struct callchain_list
*chain
;
1431 char folded_sign
= ' '; /* No children */
1433 list_for_each_entry(chain
, &child
->val
, list
) {
1435 /* We need this because we may not have children */
1436 folded_sign
= callchain_list__folded(chain
);
1437 if (folded_sign
== '+')
1441 if (folded_sign
== '-') /* Have children and they're unfolded */
1442 n
+= callchain_node__count_rows_rb_tree(child
);
1448 static int callchain_node__count_rows(struct callchain_node
*node
)
1450 struct callchain_list
*chain
;
1451 bool unfolded
= false;
1454 list_for_each_entry(chain
, &node
->val
, list
) {
1456 unfolded
= chain
->ms
.unfolded
;
1460 n
+= callchain_node__count_rows_rb_tree(node
);
1465 static int callchain__count_rows(struct rb_root
*chain
)
1470 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
1471 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
1472 n
+= callchain_node__count_rows(node
);
1478 static bool hist_browser__toggle_fold(struct hist_browser
*self
)
1480 if (map_symbol__toggle_fold(self
->selection
)) {
1481 struct hist_entry
*he
= self
->he_selection
;
1483 hist_entry__init_have_children(he
);
1484 self
->hists
->nr_entries
-= he
->nr_rows
;
1486 if (he
->ms
.unfolded
)
1487 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
1490 self
->hists
->nr_entries
+= he
->nr_rows
;
1491 self
->b
.nr_entries
= self
->hists
->nr_entries
;
1496 /* If it doesn't have children, no toggling performed */
1500 static int hist_browser__run(struct hist_browser
*self
, const char *title
,
1501 struct newtExitStruct
*es
)
1503 char str
[256], unit
;
1504 unsigned long nr_events
= self
->hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1506 self
->b
.entries
= &self
->hists
->entries
;
1507 self
->b
.nr_entries
= self
->hists
->nr_entries
;
1509 hist_browser__refresh_dimensions(self
);
1511 nr_events
= convert_unit(nr_events
, &unit
);
1512 snprintf(str
, sizeof(str
), "Events: %lu%c ",
1514 newtDrawRootText(0, 0, str
);
1516 if (ui_browser__show(&self
->b
, title
) < 0)
1519 newtFormAddHotKey(self
->b
.form
, 'A');
1520 newtFormAddHotKey(self
->b
.form
, 'a');
1521 newtFormAddHotKey(self
->b
.form
, '?');
1522 newtFormAddHotKey(self
->b
.form
, 'h');
1523 newtFormAddHotKey(self
->b
.form
, 'H');
1524 newtFormAddHotKey(self
->b
.form
, 'd');
1526 newtFormAddHotKey(self
->b
.form
, NEWT_KEY_LEFT
);
1527 newtFormAddHotKey(self
->b
.form
, NEWT_KEY_RIGHT
);
1528 newtFormAddHotKey(self
->b
.form
, NEWT_KEY_ENTER
);
1531 ui_browser__run(&self
->b
, es
);
1533 if (es
->reason
!= NEWT_EXIT_HOTKEY
)
1535 switch (es
->u
.key
) {
1536 case 'd': { /* Debug */
1538 struct hist_entry
*h
= rb_entry(self
->b
.first_visible_entry
,
1539 struct hist_entry
, rb_node
);
1541 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1542 seq
++, self
->b
.nr_entries
,
1543 self
->hists
->nr_entries
,
1546 self
->b
.first_visible_entry_idx
,
1547 h
->row_offset
, h
->nr_rows
);
1550 case NEWT_KEY_ENTER
:
1551 if (hist_browser__toggle_fold(self
))