4 #include "../libslang.h"
8 #include <linux/rbtree.h>
10 #include "../../hist.h"
11 #include "../../pstack.h"
12 #include "../../sort.h"
13 #include "../../util.h"
15 #include "../browser.h"
16 #include "../helpline.h"
23 struct hist_entry
*he_selection
;
24 struct map_symbol
*selection
;
27 static void hist_browser__refresh_dimensions(struct hist_browser
*self
)
29 /* 3 == +/- toggle symbol before actual hist_entry rendering */
30 self
->b
.width
= 3 + (hists__sort_list_width(self
->hists
) +
34 static void hist_browser__reset(struct hist_browser
*self
)
36 self
->b
.nr_entries
= self
->hists
->nr_entries
;
37 hist_browser__refresh_dimensions(self
);
38 ui_browser__reset_index(&self
->b
);
41 static char tree__folded_sign(bool unfolded
)
43 return unfolded
? '-' : '+';
46 static char map_symbol__folded(const struct map_symbol
*self
)
48 return self
->has_children
? tree__folded_sign(self
->unfolded
) : ' ';
51 static char hist_entry__folded(const struct hist_entry
*self
)
53 return map_symbol__folded(&self
->ms
);
56 static char callchain_list__folded(const struct callchain_list
*self
)
58 return map_symbol__folded(&self
->ms
);
61 static void map_symbol__set_folding(struct map_symbol
*self
, bool unfold
)
63 self
->unfolded
= unfold
? self
->has_children
: false;
66 static int callchain_node__count_rows_rb_tree(struct callchain_node
*self
)
71 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
72 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
73 struct callchain_list
*chain
;
74 char folded_sign
= ' '; /* No children */
76 list_for_each_entry(chain
, &child
->val
, list
) {
78 /* We need this because we may not have children */
79 folded_sign
= callchain_list__folded(chain
);
80 if (folded_sign
== '+')
84 if (folded_sign
== '-') /* Have children and they're unfolded */
85 n
+= callchain_node__count_rows_rb_tree(child
);
91 static int callchain_node__count_rows(struct callchain_node
*node
)
93 struct callchain_list
*chain
;
94 bool unfolded
= false;
97 list_for_each_entry(chain
, &node
->val
, list
) {
99 unfolded
= chain
->ms
.unfolded
;
103 n
+= callchain_node__count_rows_rb_tree(node
);
108 static int callchain__count_rows(struct rb_root
*chain
)
113 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
114 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
115 n
+= callchain_node__count_rows(node
);
121 static bool map_symbol__toggle_fold(struct map_symbol
*self
)
123 if (!self
->has_children
)
126 self
->unfolded
= !self
->unfolded
;
130 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*self
)
132 struct rb_node
*nd
= rb_first(&self
->rb_root
);
134 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
135 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
136 struct callchain_list
*chain
;
139 list_for_each_entry(chain
, &child
->val
, list
) {
142 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
143 !RB_EMPTY_ROOT(&child
->rb_root
);
145 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
146 !RB_EMPTY_ROOT(&child
->rb_root
);
149 callchain_node__init_have_children_rb_tree(child
);
153 static void callchain_node__init_have_children(struct callchain_node
*self
)
155 struct callchain_list
*chain
;
157 list_for_each_entry(chain
, &self
->val
, list
)
158 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&self
->rb_root
);
160 callchain_node__init_have_children_rb_tree(self
);
163 static void callchain__init_have_children(struct rb_root
*self
)
167 for (nd
= rb_first(self
); nd
; nd
= rb_next(nd
)) {
168 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
169 callchain_node__init_have_children(node
);
173 static void hist_entry__init_have_children(struct hist_entry
*self
)
175 if (!self
->init_have_children
) {
176 self
->ms
.has_children
= !RB_EMPTY_ROOT(&self
->sorted_chain
);
177 callchain__init_have_children(&self
->sorted_chain
);
178 self
->init_have_children
= true;
182 static bool hist_browser__toggle_fold(struct hist_browser
*self
)
184 if (map_symbol__toggle_fold(self
->selection
)) {
185 struct hist_entry
*he
= self
->he_selection
;
187 hist_entry__init_have_children(he
);
188 self
->hists
->nr_entries
-= he
->nr_rows
;
191 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
194 self
->hists
->nr_entries
+= he
->nr_rows
;
195 self
->b
.nr_entries
= self
->hists
->nr_entries
;
200 /* If it doesn't have children, no toggling performed */
204 static int callchain_node__set_folding_rb_tree(struct callchain_node
*self
, bool unfold
)
209 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
210 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
211 struct callchain_list
*chain
;
212 bool has_children
= false;
214 list_for_each_entry(chain
, &child
->val
, list
) {
216 map_symbol__set_folding(&chain
->ms
, unfold
);
217 has_children
= chain
->ms
.has_children
;
221 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
227 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
229 struct callchain_list
*chain
;
230 bool has_children
= false;
233 list_for_each_entry(chain
, &node
->val
, list
) {
235 map_symbol__set_folding(&chain
->ms
, unfold
);
236 has_children
= chain
->ms
.has_children
;
240 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
245 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
250 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
251 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
252 n
+= callchain_node__set_folding(node
, unfold
);
258 static void hist_entry__set_folding(struct hist_entry
*self
, bool unfold
)
260 hist_entry__init_have_children(self
);
261 map_symbol__set_folding(&self
->ms
, unfold
);
263 if (self
->ms
.has_children
) {
264 int n
= callchain__set_folding(&self
->sorted_chain
, unfold
);
265 self
->nr_rows
= unfold
? n
: 0;
270 static void hists__set_folding(struct hists
*self
, bool unfold
)
274 self
->nr_entries
= 0;
276 for (nd
= rb_first(&self
->entries
); nd
; nd
= rb_next(nd
)) {
277 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
278 hist_entry__set_folding(he
, unfold
);
279 self
->nr_entries
+= 1 + he
->nr_rows
;
283 static void hist_browser__set_folding(struct hist_browser
*self
, bool unfold
)
285 hists__set_folding(self
->hists
, unfold
);
286 self
->b
.nr_entries
= self
->hists
->nr_entries
;
287 /* Go to the start, we may be way after valid entries after a collapse */
288 ui_browser__reset_index(&self
->b
);
291 static int hist_browser__run(struct hist_browser
*self
, const char *title
)
294 int exit_keys
[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
295 NEWT_KEY_ENTER
, NEWT_KEY_RIGHT
, NEWT_KEY_LEFT
, 0, };
297 self
->b
.entries
= &self
->hists
->entries
;
298 self
->b
.nr_entries
= self
->hists
->nr_entries
;
300 hist_browser__refresh_dimensions(self
);
302 if (ui_browser__show(&self
->b
, title
,
303 "Press '?' for help on key bindings") < 0)
306 ui_browser__add_exit_keys(&self
->b
, exit_keys
);
309 key
= ui_browser__run(&self
->b
);
312 case 'D': { /* Debug */
314 struct hist_entry
*h
= rb_entry(self
->b
.top
,
315 struct hist_entry
, rb_node
);
317 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
318 seq
++, self
->b
.nr_entries
,
319 self
->hists
->nr_entries
,
323 h
->row_offset
, h
->nr_rows
);
327 /* Collapse the whole world. */
328 hist_browser__set_folding(self
, false);
331 /* Expand the whole world. */
332 hist_browser__set_folding(self
, true);
335 if (hist_browser__toggle_fold(self
))
343 ui_browser__hide(&self
->b
);
347 static char *callchain_list__sym_name(struct callchain_list
*self
,
348 char *bf
, size_t bfsize
)
351 return self
->ms
.sym
->name
;
353 snprintf(bf
, bfsize
, "%#" PRIx64
, self
->ip
);
357 #define LEVEL_OFFSET_STEP 3
359 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*self
,
360 struct callchain_node
*chain_node
,
361 u64 total
, int level
,
364 bool *is_current_entry
)
366 struct rb_node
*node
;
367 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
368 u64 new_total
, remaining
;
370 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
371 new_total
= chain_node
->children_hit
;
375 remaining
= new_total
;
376 node
= rb_first(&chain_node
->rb_root
);
378 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
379 struct rb_node
*next
= rb_next(node
);
380 u64 cumul
= callchain_cumul_hits(child
);
381 struct callchain_list
*chain
;
382 char folded_sign
= ' ';
384 int extra_offset
= 0;
388 list_for_each_entry(chain
, &child
->val
, list
) {
389 char ipstr
[BITS_PER_LONG
/ 4 + 1], *alloc_str
;
392 bool was_first
= first
;
397 extra_offset
= LEVEL_OFFSET_STEP
;
399 folded_sign
= callchain_list__folded(chain
);
400 if (*row_offset
!= 0) {
406 str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
408 double percent
= cumul
* 100.0 / new_total
;
410 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
411 str
= "Not enough memory!";
416 color
= HE_COLORSET_NORMAL
;
417 width
= self
->b
.width
- (offset
+ extra_offset
+ 2);
418 if (ui_browser__is_current_entry(&self
->b
, row
)) {
419 self
->selection
= &chain
->ms
;
420 color
= HE_COLORSET_SELECTED
;
421 *is_current_entry
= true;
424 ui_browser__set_color(&self
->b
, color
);
425 ui_browser__gotorc(&self
->b
, row
, 0);
426 slsmg_write_nstring(" ", offset
+ extra_offset
);
427 slsmg_printf("%c ", folded_sign
);
428 slsmg_write_nstring(str
, width
);
431 if (++row
== self
->b
.height
)
434 if (folded_sign
== '+')
438 if (folded_sign
== '-') {
439 const int new_level
= level
+ (extra_offset
? 2 : 1);
440 row
+= hist_browser__show_callchain_node_rb_tree(self
, child
, new_total
,
441 new_level
, row
, row_offset
,
444 if (row
== self
->b
.height
)
449 return row
- first_row
;
452 static int hist_browser__show_callchain_node(struct hist_browser
*self
,
453 struct callchain_node
*node
,
454 int level
, unsigned short row
,
456 bool *is_current_entry
)
458 struct callchain_list
*chain
;
460 offset
= level
* LEVEL_OFFSET_STEP
,
461 width
= self
->b
.width
- offset
;
462 char folded_sign
= ' ';
464 list_for_each_entry(chain
, &node
->val
, list
) {
465 char ipstr
[BITS_PER_LONG
/ 4 + 1], *s
;
468 folded_sign
= callchain_list__folded(chain
);
470 if (*row_offset
!= 0) {
475 color
= HE_COLORSET_NORMAL
;
476 if (ui_browser__is_current_entry(&self
->b
, row
)) {
477 self
->selection
= &chain
->ms
;
478 color
= HE_COLORSET_SELECTED
;
479 *is_current_entry
= true;
482 s
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
483 ui_browser__gotorc(&self
->b
, row
, 0);
484 ui_browser__set_color(&self
->b
, color
);
485 slsmg_write_nstring(" ", offset
);
486 slsmg_printf("%c ", folded_sign
);
487 slsmg_write_nstring(s
, width
- 2);
489 if (++row
== self
->b
.height
)
493 if (folded_sign
== '-')
494 row
+= hist_browser__show_callchain_node_rb_tree(self
, node
,
495 self
->hists
->stats
.total_period
,
500 return row
- first_row
;
503 static int hist_browser__show_callchain(struct hist_browser
*self
,
504 struct rb_root
*chain
,
505 int level
, unsigned short row
,
507 bool *is_current_entry
)
512 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
513 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
515 row
+= hist_browser__show_callchain_node(self
, node
, level
,
518 if (row
== self
->b
.height
)
522 return row
- first_row
;
525 static int hist_browser__show_entry(struct hist_browser
*self
,
526 struct hist_entry
*entry
,
532 int color
, width
= self
->b
.width
;
533 char folded_sign
= ' ';
534 bool current_entry
= ui_browser__is_current_entry(&self
->b
, row
);
535 off_t row_offset
= entry
->row_offset
;
538 self
->he_selection
= entry
;
539 self
->selection
= &entry
->ms
;
542 if (symbol_conf
.use_callchain
) {
543 hist_entry__init_have_children(entry
);
544 folded_sign
= hist_entry__folded(entry
);
547 if (row_offset
== 0) {
548 hist_entry__snprintf(entry
, s
, sizeof(s
), self
->hists
, NULL
, false,
549 0, false, self
->hists
->stats
.total_period
);
550 percent
= (entry
->period
* 100.0) / self
->hists
->stats
.total_period
;
552 color
= HE_COLORSET_SELECTED
;
553 if (!current_entry
) {
554 if (percent
>= MIN_RED
)
555 color
= HE_COLORSET_TOP
;
556 else if (percent
>= MIN_GREEN
)
557 color
= HE_COLORSET_MEDIUM
;
559 color
= HE_COLORSET_NORMAL
;
562 ui_browser__set_color(&self
->b
, color
);
563 ui_browser__gotorc(&self
->b
, row
, 0);
564 if (symbol_conf
.use_callchain
) {
565 slsmg_printf("%c ", folded_sign
);
568 slsmg_write_nstring(s
, width
);
574 if (folded_sign
== '-' && row
!= self
->b
.height
) {
575 printed
+= hist_browser__show_callchain(self
, &entry
->sorted_chain
,
579 self
->he_selection
= entry
;
585 static unsigned int hist_browser__refresh(struct ui_browser
*self
)
589 struct hist_browser
*hb
= container_of(self
, struct hist_browser
, b
);
591 if (self
->top
== NULL
)
592 self
->top
= rb_first(&hb
->hists
->entries
);
594 for (nd
= self
->top
; nd
; nd
= rb_next(nd
)) {
595 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
600 row
+= hist_browser__show_entry(hb
, h
, row
);
601 if (row
== self
->height
)
608 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
)
611 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
621 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
)
624 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
634 static void ui_browser__hists_seek(struct ui_browser
*self
,
635 off_t offset
, int whence
)
637 struct hist_entry
*h
;
643 nd
= hists__filter_entries(rb_first(self
->entries
));
649 nd
= hists__filter_prev_entries(rb_last(self
->entries
));
657 * Moves not relative to the first visible entry invalidates its
660 h
= rb_entry(self
->top
, struct hist_entry
, rb_node
);
664 * Here we have to check if nd is expanded (+), if it is we can't go
665 * the next top level hist_entry, instead we must compute an offset of
666 * what _not_ to show and not change the first visible entry.
668 * This offset increments when we are going from top to bottom and
669 * decreases when we're going from bottom to top.
671 * As we don't have backpointers to the top level in the callchains
672 * structure, we need to always print the whole hist_entry callchain,
673 * skipping the first ones that are before the first visible entry
674 * and stop when we printed enough lines to fill the screen.
679 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
680 if (h
->ms
.unfolded
) {
681 u16 remaining
= h
->nr_rows
- h
->row_offset
;
682 if (offset
> remaining
) {
686 h
->row_offset
+= offset
;
692 nd
= hists__filter_entries(rb_next(nd
));
697 } while (offset
!= 0);
698 } else if (offset
< 0) {
700 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
701 if (h
->ms
.unfolded
) {
703 if (-offset
> h
->row_offset
) {
704 offset
+= h
->row_offset
;
707 h
->row_offset
+= offset
;
713 if (-offset
> h
->nr_rows
) {
714 offset
+= h
->nr_rows
;
717 h
->row_offset
= h
->nr_rows
+ offset
;
725 nd
= hists__filter_prev_entries(rb_prev(nd
));
732 * Last unfiltered hist_entry, check if it is
733 * unfolded, if it is then we should have
734 * row_offset at its last entry.
736 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
738 h
->row_offset
= h
->nr_rows
;
745 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
750 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
752 struct hist_browser
*self
= zalloc(sizeof(*self
));
756 self
->b
.refresh
= hist_browser__refresh
;
757 self
->b
.seek
= ui_browser__hists_seek
;
763 static void hist_browser__delete(struct hist_browser
*self
)
768 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*self
)
770 return self
->he_selection
;
773 static struct thread
*hist_browser__selected_thread(struct hist_browser
*self
)
775 return self
->he_selection
->thread
;
778 static int hists__browser_title(struct hists
*self
, char *bf
, size_t size
,
779 const char *ev_name
, const struct dso
*dso
,
780 const struct thread
*thread
)
784 unsigned long nr_events
= self
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
786 nr_events
= convert_unit(nr_events
, &unit
);
787 printed
= snprintf(bf
, size
, "Events: %lu%c %s", nr_events
, unit
, ev_name
);
790 printed
+= snprintf(bf
+ printed
, size
- printed
,
792 (thread
->comm_set
? thread
->comm
: ""),
795 printed
+= snprintf(bf
+ printed
, size
- printed
,
796 ", DSO: %s", dso
->short_name
);
800 int hists__browse(struct hists
*self
, const char *helpline
, const char *ev_name
)
802 struct hist_browser
*browser
= hist_browser__new(self
);
803 struct pstack
*fstack
;
804 const struct thread
*thread_filter
= NULL
;
805 const struct dso
*dso_filter
= NULL
;
812 fstack
= pstack__new(2);
816 ui_helpline__push(helpline
);
818 hists__browser_title(self
, msg
, sizeof(msg
), ev_name
,
819 dso_filter
, thread_filter
);
821 const struct thread
*thread
;
822 const struct dso
*dso
;
824 int nr_options
= 0, choice
= 0, i
,
825 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
828 key
= hist_browser__run(browser
, msg
);
830 thread
= hist_browser__selected_thread(browser
);
831 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
837 * Exit the browser, let hists__browser_tree
838 * go to the next or previous
842 if (browser
->selection
->map
== NULL
&&
843 browser
->selection
->map
->dso
->annotate_warned
)
853 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
855 "a Annotate current symbol\n"
856 "h/?/F1 Show this window\n"
857 "C Collapse all callchains\n"
858 "E Expand all callchains\n"
859 "d Zoom into current DSO\n"
860 "t Zoom into current Thread\n"
861 "q/CTRL+C Exit browser");
867 case NEWT_KEY_LEFT
: {
870 if (pstack__empty(fstack
))
872 top
= pstack__pop(fstack
);
873 if (top
== &dso_filter
)
875 if (top
== &thread_filter
)
876 goto zoom_out_thread
;
879 case NEWT_KEY_ESCAPE
:
880 if (!ui__dialog_yesno("Do you really want to exit?"))
887 if (browser
->selection
->sym
!= NULL
&&
888 !browser
->selection
->map
->dso
->annotate_warned
&&
889 asprintf(&options
[nr_options
], "Annotate %s",
890 browser
->selection
->sym
->name
) > 0)
891 annotate
= nr_options
++;
893 if (thread
!= NULL
&&
894 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
895 (thread_filter
? "out of" : "into"),
896 (thread
->comm_set
? thread
->comm
: ""),
898 zoom_thread
= nr_options
++;
901 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
902 (dso_filter
? "out of" : "into"),
903 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
904 zoom_dso
= nr_options
++;
906 if (browser
->selection
->map
!= NULL
&&
907 asprintf(&options
[nr_options
], "Browse map details") > 0)
908 browse_map
= nr_options
++;
910 options
[nr_options
++] = (char *)"Exit";
912 choice
= ui__popup_menu(nr_options
, options
);
914 for (i
= 0; i
< nr_options
- 1; ++i
)
917 if (choice
== nr_options
- 1)
923 if (choice
== annotate
) {
924 struct hist_entry
*he
;
926 if (browser
->selection
->map
->dso
->origin
== DSO__ORIG_KERNEL
) {
927 browser
->selection
->map
->dso
->annotate_warned
= 1;
928 ui_helpline__puts("No vmlinux file found, can't "
929 "annotate with just a "
934 he
= hist_browser__selected_entry(browser
);
938 hist_entry__tui_annotate(he
);
939 } else if (choice
== browse_map
)
940 map__browse(browser
->selection
->map
);
941 else if (choice
== zoom_dso
) {
944 pstack__remove(fstack
, &dso_filter
);
951 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
952 dso
->kernel
? "the Kernel" : dso
->short_name
);
954 pstack__push(fstack
, &dso_filter
);
956 hists__filter_by_dso(self
, dso_filter
);
957 hists__browser_title(self
, msg
, sizeof(msg
), ev_name
,
958 dso_filter
, thread_filter
);
959 hist_browser__reset(browser
);
960 } else if (choice
== zoom_thread
) {
963 pstack__remove(fstack
, &thread_filter
);
966 thread_filter
= NULL
;
968 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
969 thread
->comm_set
? thread
->comm
: "",
971 thread_filter
= thread
;
972 pstack__push(fstack
, &thread_filter
);
974 hists__filter_by_thread(self
, thread_filter
);
975 hists__browser_title(self
, msg
, sizeof(msg
), ev_name
,
976 dso_filter
, thread_filter
);
977 hist_browser__reset(browser
);
981 pstack__delete(fstack
);
983 hist_browser__delete(browser
);
987 int hists__tui_browse_tree(struct rb_root
*self
, const char *help
)
989 struct rb_node
*first
= rb_first(self
), *nd
= first
, *next
;
993 struct hists
*hists
= rb_entry(nd
, struct hists
, rb_node
);
994 const char *ev_name
= __event_name(hists
->type
, hists
->config
);
996 key
= hists__browse(hists
, help
, ev_name
);
1003 case NEWT_KEY_UNTAB
: