xref: /linux/tools/perf/ui/browsers/hists.c (revision 26b433d0da062d6e19d75350c0171d3cf8ff560d)
1 #include <dirent.h>
2 #include <errno.h>
3 #include <inttypes.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <linux/rbtree.h>
8 #include <sys/ttydefaults.h>
9 
10 #include "../../util/evsel.h"
11 #include "../../util/evlist.h"
12 #include "../../util/hist.h"
13 #include "../../util/pstack.h"
14 #include "../../util/sort.h"
15 #include "../../util/util.h"
16 #include "../../util/top.h"
17 #include "../../util/thread.h"
18 #include "../../arch/common.h"
19 
20 #include "../browsers/hists.h"
21 #include "../helpline.h"
22 #include "../util.h"
23 #include "../ui.h"
24 #include "map.h"
25 #include "annotate.h"
26 #include "srcline.h"
27 #include "string2.h"
28 #include "units.h"
29 
30 #include "sane_ctype.h"
31 
32 extern void hist_browser__init_hpp(void);
33 
34 static int perf_evsel_browser_title(struct hist_browser *browser,
35 				    char *bf, size_t size);
36 static void hist_browser__update_nr_entries(struct hist_browser *hb);
37 
38 static struct rb_node *hists__filter_entries(struct rb_node *nd,
39 					     float min_pcnt);
40 
41 static bool hist_browser__has_filter(struct hist_browser *hb)
42 {
43 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
44 }
45 
46 static int hist_browser__get_folding(struct hist_browser *browser)
47 {
48 	struct rb_node *nd;
49 	struct hists *hists = browser->hists;
50 	int unfolded_rows = 0;
51 
52 	for (nd = rb_first(&hists->entries);
53 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
54 	     nd = rb_hierarchy_next(nd)) {
55 		struct hist_entry *he =
56 			rb_entry(nd, struct hist_entry, rb_node);
57 
58 		if (he->leaf && he->unfolded)
59 			unfolded_rows += he->nr_rows;
60 	}
61 	return unfolded_rows;
62 }
63 
64 static u32 hist_browser__nr_entries(struct hist_browser *hb)
65 {
66 	u32 nr_entries;
67 
68 	if (symbol_conf.report_hierarchy)
69 		nr_entries = hb->nr_hierarchy_entries;
70 	else if (hist_browser__has_filter(hb))
71 		nr_entries = hb->nr_non_filtered_entries;
72 	else
73 		nr_entries = hb->hists->nr_entries;
74 
75 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
76 	return nr_entries + hb->nr_callchain_rows;
77 }
78 
79 static void hist_browser__update_rows(struct hist_browser *hb)
80 {
81 	struct ui_browser *browser = &hb->b;
82 	struct hists *hists = hb->hists;
83 	struct perf_hpp_list *hpp_list = hists->hpp_list;
84 	u16 header_offset, index_row;
85 
86 	header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
87 	browser->rows = browser->height - header_offset;
88 	/*
89 	 * Verify if we were at the last line and that line isn't
90 	 * visibe because we now show the header line(s).
91 	 */
92 	index_row = browser->index - browser->top_idx;
93 	if (index_row >= browser->rows)
94 		browser->index -= index_row - browser->rows + 1;
95 }
96 
97 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
98 {
99 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
100 
101 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
102 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
103 	/*
104  	 * FIXME: Just keeping existing behaviour, but this really should be
105  	 *	  before updating browser->width, as it will invalidate the
106  	 *	  calculation above. Fix this and the fallout in another
107  	 *	  changeset.
108  	 */
109 	ui_browser__refresh_dimensions(browser);
110 	hist_browser__update_rows(hb);
111 }
112 
113 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
114 {
115 	struct hists *hists = browser->hists;
116 	struct perf_hpp_list *hpp_list = hists->hpp_list;
117 	u16 header_offset;
118 
119 	header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
120 	ui_browser__gotorc(&browser->b, row + header_offset, column);
121 }
122 
123 static void hist_browser__reset(struct hist_browser *browser)
124 {
125 	/*
126 	 * The hists__remove_entry_filter() already folds non-filtered
127 	 * entries so we can assume it has 0 callchain rows.
128 	 */
129 	browser->nr_callchain_rows = 0;
130 
131 	hist_browser__update_nr_entries(browser);
132 	browser->b.nr_entries = hist_browser__nr_entries(browser);
133 	hist_browser__refresh_dimensions(&browser->b);
134 	ui_browser__reset_index(&browser->b);
135 }
136 
137 static char tree__folded_sign(bool unfolded)
138 {
139 	return unfolded ? '-' : '+';
140 }
141 
142 static char hist_entry__folded(const struct hist_entry *he)
143 {
144 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
145 }
146 
147 static char callchain_list__folded(const struct callchain_list *cl)
148 {
149 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
150 }
151 
152 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
153 {
154 	cl->unfolded = unfold ? cl->has_children : false;
155 }
156 
157 static struct inline_node *inline_node__create(struct map *map, u64 ip)
158 {
159 	struct dso *dso;
160 	struct inline_node *node;
161 
162 	if (map == NULL)
163 		return NULL;
164 
165 	dso = map->dso;
166 	if (dso == NULL)
167 		return NULL;
168 
169 	node = dso__parse_addr_inlines(dso,
170 				       map__rip_2objdump(map, ip));
171 
172 	return node;
173 }
174 
175 static int inline__count_rows(struct inline_node *node)
176 {
177 	struct inline_list *ilist;
178 	int i = 0;
179 
180 	if (node == NULL)
181 		return 0;
182 
183 	list_for_each_entry(ilist, &node->val, list) {
184 		if ((ilist->filename != NULL) || (ilist->funcname != NULL))
185 			i++;
186 	}
187 
188 	return i;
189 }
190 
191 static int callchain_list__inline_rows(struct callchain_list *chain)
192 {
193 	struct inline_node *node;
194 	int rows;
195 
196 	node = inline_node__create(chain->ms.map, chain->ip);
197 	if (node == NULL)
198 		return 0;
199 
200 	rows = inline__count_rows(node);
201 	inline_node__delete(node);
202 	return rows;
203 }
204 
205 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
206 {
207 	int n = 0, inline_rows;
208 	struct rb_node *nd;
209 
210 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
211 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
212 		struct callchain_list *chain;
213 		char folded_sign = ' '; /* No children */
214 
215 		list_for_each_entry(chain, &child->val, list) {
216 			++n;
217 
218 			if (symbol_conf.inline_name) {
219 				inline_rows =
220 					callchain_list__inline_rows(chain);
221 				n += inline_rows;
222 			}
223 
224 			/* We need this because we may not have children */
225 			folded_sign = callchain_list__folded(chain);
226 			if (folded_sign == '+')
227 				break;
228 		}
229 
230 		if (folded_sign == '-') /* Have children and they're unfolded */
231 			n += callchain_node__count_rows_rb_tree(child);
232 	}
233 
234 	return n;
235 }
236 
237 static int callchain_node__count_flat_rows(struct callchain_node *node)
238 {
239 	struct callchain_list *chain;
240 	char folded_sign = 0;
241 	int n = 0;
242 
243 	list_for_each_entry(chain, &node->parent_val, list) {
244 		if (!folded_sign) {
245 			/* only check first chain list entry */
246 			folded_sign = callchain_list__folded(chain);
247 			if (folded_sign == '+')
248 				return 1;
249 		}
250 		n++;
251 	}
252 
253 	list_for_each_entry(chain, &node->val, list) {
254 		if (!folded_sign) {
255 			/* node->parent_val list might be empty */
256 			folded_sign = callchain_list__folded(chain);
257 			if (folded_sign == '+')
258 				return 1;
259 		}
260 		n++;
261 	}
262 
263 	return n;
264 }
265 
266 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
267 {
268 	return 1;
269 }
270 
271 static int callchain_node__count_rows(struct callchain_node *node)
272 {
273 	struct callchain_list *chain;
274 	bool unfolded = false;
275 	int n = 0, inline_rows;
276 
277 	if (callchain_param.mode == CHAIN_FLAT)
278 		return callchain_node__count_flat_rows(node);
279 	else if (callchain_param.mode == CHAIN_FOLDED)
280 		return callchain_node__count_folded_rows(node);
281 
282 	list_for_each_entry(chain, &node->val, list) {
283 		++n;
284 		if (symbol_conf.inline_name) {
285 			inline_rows = callchain_list__inline_rows(chain);
286 			n += inline_rows;
287 		}
288 
289 		unfolded = chain->unfolded;
290 	}
291 
292 	if (unfolded)
293 		n += callchain_node__count_rows_rb_tree(node);
294 
295 	return n;
296 }
297 
298 static int callchain__count_rows(struct rb_root *chain)
299 {
300 	struct rb_node *nd;
301 	int n = 0;
302 
303 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
304 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
305 		n += callchain_node__count_rows(node);
306 	}
307 
308 	return n;
309 }
310 
311 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
312 				bool include_children)
313 {
314 	int count = 0;
315 	struct rb_node *node;
316 	struct hist_entry *child;
317 
318 	if (he->leaf)
319 		return callchain__count_rows(&he->sorted_chain);
320 
321 	if (he->has_no_entry)
322 		return 1;
323 
324 	node = rb_first(&he->hroot_out);
325 	while (node) {
326 		float percent;
327 
328 		child = rb_entry(node, struct hist_entry, rb_node);
329 		percent = hist_entry__get_percent_limit(child);
330 
331 		if (!child->filtered && percent >= hb->min_pcnt) {
332 			count++;
333 
334 			if (include_children && child->unfolded)
335 				count += hierarchy_count_rows(hb, child, true);
336 		}
337 
338 		node = rb_next(node);
339 	}
340 	return count;
341 }
342 
343 static bool hist_entry__toggle_fold(struct hist_entry *he)
344 {
345 	if (!he)
346 		return false;
347 
348 	if (!he->has_children)
349 		return false;
350 
351 	he->unfolded = !he->unfolded;
352 	return true;
353 }
354 
355 static bool callchain_list__toggle_fold(struct callchain_list *cl)
356 {
357 	if (!cl)
358 		return false;
359 
360 	if (!cl->has_children)
361 		return false;
362 
363 	cl->unfolded = !cl->unfolded;
364 	return true;
365 }
366 
367 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
368 {
369 	struct rb_node *nd = rb_first(&node->rb_root);
370 
371 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
372 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
373 		struct callchain_list *chain;
374 		bool first = true;
375 
376 		list_for_each_entry(chain, &child->val, list) {
377 			if (first) {
378 				first = false;
379 				chain->has_children = chain->list.next != &child->val ||
380 							 !RB_EMPTY_ROOT(&child->rb_root);
381 			} else
382 				chain->has_children = chain->list.next == &child->val &&
383 							 !RB_EMPTY_ROOT(&child->rb_root);
384 		}
385 
386 		callchain_node__init_have_children_rb_tree(child);
387 	}
388 }
389 
390 static void callchain_node__init_have_children(struct callchain_node *node,
391 					       bool has_sibling)
392 {
393 	struct callchain_list *chain;
394 
395 	chain = list_entry(node->val.next, struct callchain_list, list);
396 	chain->has_children = has_sibling;
397 
398 	if (!list_empty(&node->val)) {
399 		chain = list_entry(node->val.prev, struct callchain_list, list);
400 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
401 	}
402 
403 	callchain_node__init_have_children_rb_tree(node);
404 }
405 
406 static void callchain__init_have_children(struct rb_root *root)
407 {
408 	struct rb_node *nd = rb_first(root);
409 	bool has_sibling = nd && rb_next(nd);
410 
411 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
412 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
413 		callchain_node__init_have_children(node, has_sibling);
414 		if (callchain_param.mode == CHAIN_FLAT ||
415 		    callchain_param.mode == CHAIN_FOLDED)
416 			callchain_node__make_parent_list(node);
417 	}
418 }
419 
420 static void hist_entry__init_have_children(struct hist_entry *he)
421 {
422 	if (he->init_have_children)
423 		return;
424 
425 	if (he->leaf) {
426 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
427 		callchain__init_have_children(&he->sorted_chain);
428 	} else {
429 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
430 	}
431 
432 	he->init_have_children = true;
433 }
434 
435 static void hist_entry_init_inline_node(struct hist_entry *he)
436 {
437 	if (he->inline_node)
438 		return;
439 
440 	he->inline_node = inline_node__create(he->ms.map, he->ip);
441 
442 	if (he->inline_node == NULL)
443 		return;
444 
445 	he->has_children = true;
446 }
447 
448 static bool hist_browser__toggle_fold(struct hist_browser *browser)
449 {
450 	struct hist_entry *he = browser->he_selection;
451 	struct map_symbol *ms = browser->selection;
452 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
453 	bool has_children;
454 
455 	if (!he || !ms)
456 		return false;
457 
458 	if (ms == &he->ms)
459 		has_children = hist_entry__toggle_fold(he);
460 	else
461 		has_children = callchain_list__toggle_fold(cl);
462 
463 	if (has_children) {
464 		int child_rows = 0;
465 
466 		hist_entry__init_have_children(he);
467 		browser->b.nr_entries -= he->nr_rows;
468 
469 		if (he->leaf)
470 			browser->nr_callchain_rows -= he->nr_rows;
471 		else
472 			browser->nr_hierarchy_entries -= he->nr_rows;
473 
474 		if (symbol_conf.report_hierarchy)
475 			child_rows = hierarchy_count_rows(browser, he, true);
476 
477 		if (he->unfolded) {
478 			if (he->leaf)
479 				if (he->inline_node)
480 					he->nr_rows = inline__count_rows(
481 							he->inline_node);
482 				else
483 					he->nr_rows = callchain__count_rows(
484 							&he->sorted_chain);
485 			else
486 				he->nr_rows = hierarchy_count_rows(browser, he, false);
487 
488 			/* account grand children */
489 			if (symbol_conf.report_hierarchy)
490 				browser->b.nr_entries += child_rows - he->nr_rows;
491 
492 			if (!he->leaf && he->nr_rows == 0) {
493 				he->has_no_entry = true;
494 				he->nr_rows = 1;
495 			}
496 		} else {
497 			if (symbol_conf.report_hierarchy)
498 				browser->b.nr_entries -= child_rows - he->nr_rows;
499 
500 			if (he->has_no_entry)
501 				he->has_no_entry = false;
502 
503 			he->nr_rows = 0;
504 		}
505 
506 		browser->b.nr_entries += he->nr_rows;
507 
508 		if (he->leaf)
509 			browser->nr_callchain_rows += he->nr_rows;
510 		else
511 			browser->nr_hierarchy_entries += he->nr_rows;
512 
513 		return true;
514 	}
515 
516 	/* If it doesn't have children, no toggling performed */
517 	return false;
518 }
519 
520 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
521 {
522 	int n = 0;
523 	struct rb_node *nd;
524 
525 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
526 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
527 		struct callchain_list *chain;
528 		bool has_children = false;
529 
530 		list_for_each_entry(chain, &child->val, list) {
531 			++n;
532 			callchain_list__set_folding(chain, unfold);
533 			has_children = chain->has_children;
534 		}
535 
536 		if (has_children)
537 			n += callchain_node__set_folding_rb_tree(child, unfold);
538 	}
539 
540 	return n;
541 }
542 
543 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
544 {
545 	struct callchain_list *chain;
546 	bool has_children = false;
547 	int n = 0;
548 
549 	list_for_each_entry(chain, &node->val, list) {
550 		++n;
551 		callchain_list__set_folding(chain, unfold);
552 		has_children = chain->has_children;
553 	}
554 
555 	if (has_children)
556 		n += callchain_node__set_folding_rb_tree(node, unfold);
557 
558 	return n;
559 }
560 
561 static int callchain__set_folding(struct rb_root *chain, bool unfold)
562 {
563 	struct rb_node *nd;
564 	int n = 0;
565 
566 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
567 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
568 		n += callchain_node__set_folding(node, unfold);
569 	}
570 
571 	return n;
572 }
573 
574 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
575 				 bool unfold __maybe_unused)
576 {
577 	float percent;
578 	struct rb_node *nd;
579 	struct hist_entry *child;
580 	int n = 0;
581 
582 	for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
583 		child = rb_entry(nd, struct hist_entry, rb_node);
584 		percent = hist_entry__get_percent_limit(child);
585 		if (!child->filtered && percent >= hb->min_pcnt)
586 			n++;
587 	}
588 
589 	return n;
590 }
591 
592 static void __hist_entry__set_folding(struct hist_entry *he,
593 				      struct hist_browser *hb, bool unfold)
594 {
595 	hist_entry__init_have_children(he);
596 	he->unfolded = unfold ? he->has_children : false;
597 
598 	if (he->has_children) {
599 		int n;
600 
601 		if (he->leaf)
602 			n = callchain__set_folding(&he->sorted_chain, unfold);
603 		else
604 			n = hierarchy_set_folding(hb, he, unfold);
605 
606 		he->nr_rows = unfold ? n : 0;
607 	} else
608 		he->nr_rows = 0;
609 }
610 
611 static void hist_entry__set_folding(struct hist_entry *he,
612 				    struct hist_browser *browser, bool unfold)
613 {
614 	double percent;
615 
616 	percent = hist_entry__get_percent_limit(he);
617 	if (he->filtered || percent < browser->min_pcnt)
618 		return;
619 
620 	__hist_entry__set_folding(he, browser, unfold);
621 
622 	if (!he->depth || unfold)
623 		browser->nr_hierarchy_entries++;
624 	if (he->leaf)
625 		browser->nr_callchain_rows += he->nr_rows;
626 	else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
627 		browser->nr_hierarchy_entries++;
628 		he->has_no_entry = true;
629 		he->nr_rows = 1;
630 	} else
631 		he->has_no_entry = false;
632 }
633 
634 static void
635 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
636 {
637 	struct rb_node *nd;
638 	struct hist_entry *he;
639 
640 	nd = rb_first(&browser->hists->entries);
641 	while (nd) {
642 		he = rb_entry(nd, struct hist_entry, rb_node);
643 
644 		/* set folding state even if it's currently folded */
645 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
646 
647 		hist_entry__set_folding(he, browser, unfold);
648 	}
649 }
650 
651 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
652 {
653 	browser->nr_hierarchy_entries = 0;
654 	browser->nr_callchain_rows = 0;
655 	__hist_browser__set_folding(browser, unfold);
656 
657 	browser->b.nr_entries = hist_browser__nr_entries(browser);
658 	/* Go to the start, we may be way after valid entries after a collapse */
659 	ui_browser__reset_index(&browser->b);
660 }
661 
662 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
663 {
664 	if (!browser->he_selection)
665 		return;
666 
667 	hist_entry__set_folding(browser->he_selection, browser, unfold);
668 	browser->b.nr_entries = hist_browser__nr_entries(browser);
669 }
670 
671 static void ui_browser__warn_lost_events(struct ui_browser *browser)
672 {
673 	ui_browser__warning(browser, 4,
674 		"Events are being lost, check IO/CPU overload!\n\n"
675 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
676 		" perf top -r 80\n\n"
677 		"Or reduce the sampling frequency.");
678 }
679 
680 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
681 {
682 	return browser->title ? browser->title(browser, bf, size) : 0;
683 }
684 
685 int hist_browser__run(struct hist_browser *browser, const char *help)
686 {
687 	int key;
688 	char title[160];
689 	struct hist_browser_timer *hbt = browser->hbt;
690 	int delay_secs = hbt ? hbt->refresh : 0;
691 
692 	browser->b.entries = &browser->hists->entries;
693 	browser->b.nr_entries = hist_browser__nr_entries(browser);
694 
695 	hist_browser__title(browser, title, sizeof(title));
696 
697 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
698 		return -1;
699 
700 	while (1) {
701 		key = ui_browser__run(&browser->b, delay_secs);
702 
703 		switch (key) {
704 		case K_TIMER: {
705 			u64 nr_entries;
706 			hbt->timer(hbt->arg);
707 
708 			if (hist_browser__has_filter(browser) ||
709 			    symbol_conf.report_hierarchy)
710 				hist_browser__update_nr_entries(browser);
711 
712 			nr_entries = hist_browser__nr_entries(browser);
713 			ui_browser__update_nr_entries(&browser->b, nr_entries);
714 
715 			if (browser->hists->stats.nr_lost_warned !=
716 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
717 				browser->hists->stats.nr_lost_warned =
718 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
719 				ui_browser__warn_lost_events(&browser->b);
720 			}
721 
722 			hist_browser__title(browser, title, sizeof(title));
723 			ui_browser__show_title(&browser->b, title);
724 			continue;
725 		}
726 		case 'D': { /* Debug */
727 			static int seq;
728 			struct hist_entry *h = rb_entry(browser->b.top,
729 							struct hist_entry, rb_node);
730 			ui_helpline__pop();
731 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
732 					   seq++, browser->b.nr_entries,
733 					   browser->hists->nr_entries,
734 					   browser->b.rows,
735 					   browser->b.index,
736 					   browser->b.top_idx,
737 					   h->row_offset, h->nr_rows);
738 		}
739 			break;
740 		case 'C':
741 			/* Collapse the whole world. */
742 			hist_browser__set_folding(browser, false);
743 			break;
744 		case 'c':
745 			/* Collapse the selected entry. */
746 			hist_browser__set_folding_selected(browser, false);
747 			break;
748 		case 'E':
749 			/* Expand the whole world. */
750 			hist_browser__set_folding(browser, true);
751 			break;
752 		case 'e':
753 			/* Expand the selected entry. */
754 			hist_browser__set_folding_selected(browser, true);
755 			break;
756 		case 'H':
757 			browser->show_headers = !browser->show_headers;
758 			hist_browser__update_rows(browser);
759 			break;
760 		case K_ENTER:
761 			if (hist_browser__toggle_fold(browser))
762 				break;
763 			/* fall thru */
764 		default:
765 			goto out;
766 		}
767 	}
768 out:
769 	ui_browser__hide(&browser->b);
770 	return key;
771 }
772 
773 struct callchain_print_arg {
774 	/* for hists browser */
775 	off_t	row_offset;
776 	bool	is_current_entry;
777 
778 	/* for file dump */
779 	FILE	*fp;
780 	int	printed;
781 };
782 
783 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
784 					 struct callchain_list *chain,
785 					 const char *str, int offset,
786 					 unsigned short row,
787 					 struct callchain_print_arg *arg);
788 
789 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
790 					       struct callchain_list *chain,
791 					       const char *str, int offset,
792 					       unsigned short row,
793 					       struct callchain_print_arg *arg)
794 {
795 	int color, width;
796 	char folded_sign = callchain_list__folded(chain);
797 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
798 
799 	color = HE_COLORSET_NORMAL;
800 	width = browser->b.width - (offset + 2);
801 	if (ui_browser__is_current_entry(&browser->b, row)) {
802 		browser->selection = &chain->ms;
803 		color = HE_COLORSET_SELECTED;
804 		arg->is_current_entry = true;
805 	}
806 
807 	ui_browser__set_color(&browser->b, color);
808 	hist_browser__gotorc(browser, row, 0);
809 	ui_browser__write_nstring(&browser->b, " ", offset);
810 	ui_browser__printf(&browser->b, "%c", folded_sign);
811 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
812 	ui_browser__write_nstring(&browser->b, str, width);
813 }
814 
815 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
816 						  struct callchain_list *chain,
817 						  const char *str, int offset,
818 						  unsigned short row __maybe_unused,
819 						  struct callchain_print_arg *arg)
820 {
821 	char folded_sign = callchain_list__folded(chain);
822 
823 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
824 				folded_sign, str);
825 }
826 
827 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
828 				     unsigned short row);
829 
830 static bool hist_browser__check_output_full(struct hist_browser *browser,
831 					    unsigned short row)
832 {
833 	return browser->b.rows == row;
834 }
835 
836 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
837 					  unsigned short row __maybe_unused)
838 {
839 	return false;
840 }
841 
842 #define LEVEL_OFFSET_STEP 3
843 
844 static int hist_browser__show_inline(struct hist_browser *browser,
845 				     struct inline_node *node,
846 				     unsigned short row,
847 				     int offset)
848 {
849 	struct inline_list *ilist;
850 	char buf[1024];
851 	int color, width, first_row;
852 
853 	first_row = row;
854 	width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
855 	list_for_each_entry(ilist, &node->val, list) {
856 		if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
857 			color = HE_COLORSET_NORMAL;
858 			if (ui_browser__is_current_entry(&browser->b, row))
859 				color = HE_COLORSET_SELECTED;
860 
861 			if (callchain_param.key == CCKEY_ADDRESS ||
862 			    callchain_param.key == CCKEY_SRCLINE) {
863 				if (ilist->filename != NULL)
864 					scnprintf(buf, sizeof(buf),
865 						  "%s:%d (inline)",
866 						  ilist->filename,
867 						  ilist->line_nr);
868 				else
869 					scnprintf(buf, sizeof(buf), "??");
870 			} else if (ilist->funcname != NULL)
871 				scnprintf(buf, sizeof(buf), "%s (inline)",
872 					  ilist->funcname);
873 			else if (ilist->filename != NULL)
874 				scnprintf(buf, sizeof(buf),
875 					  "%s:%d (inline)",
876 					  ilist->filename,
877 					  ilist->line_nr);
878 			else
879 				scnprintf(buf, sizeof(buf), "??");
880 
881 			ui_browser__set_color(&browser->b, color);
882 			hist_browser__gotorc(browser, row, 0);
883 			ui_browser__write_nstring(&browser->b, " ",
884 				LEVEL_OFFSET_STEP + offset);
885 			ui_browser__write_nstring(&browser->b, buf, width);
886 			row++;
887 		}
888 	}
889 
890 	return row - first_row;
891 }
892 
893 static size_t show_inline_list(struct hist_browser *browser, struct map *map,
894 			       u64 ip, int row, int offset)
895 {
896 	struct inline_node *node;
897 	int ret;
898 
899 	node = inline_node__create(map, ip);
900 	if (node == NULL)
901 		return 0;
902 
903 	ret = hist_browser__show_inline(browser, node, row, offset);
904 
905 	inline_node__delete(node);
906 	return ret;
907 }
908 
909 static int hist_browser__show_callchain_list(struct hist_browser *browser,
910 					     struct callchain_node *node,
911 					     struct callchain_list *chain,
912 					     unsigned short row, u64 total,
913 					     bool need_percent, int offset,
914 					     print_callchain_entry_fn print,
915 					     struct callchain_print_arg *arg)
916 {
917 	char bf[1024], *alloc_str;
918 	char buf[64], *alloc_str2;
919 	const char *str;
920 	int inline_rows = 0, ret = 1;
921 
922 	if (arg->row_offset != 0) {
923 		arg->row_offset--;
924 		return 0;
925 	}
926 
927 	alloc_str = NULL;
928 	alloc_str2 = NULL;
929 
930 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
931 				       browser->show_dso);
932 
933 	if (symbol_conf.show_branchflag_count) {
934 		if (need_percent)
935 			callchain_list_counts__printf_value(node, chain, NULL,
936 							    buf, sizeof(buf));
937 		else
938 			callchain_list_counts__printf_value(NULL, chain, NULL,
939 							    buf, sizeof(buf));
940 
941 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
942 			str = "Not enough memory!";
943 		else
944 			str = alloc_str2;
945 	}
946 
947 	if (need_percent) {
948 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
949 						total);
950 
951 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
952 			str = "Not enough memory!";
953 		else
954 			str = alloc_str;
955 	}
956 
957 	print(browser, chain, str, offset, row, arg);
958 	free(alloc_str);
959 	free(alloc_str2);
960 
961 	if (symbol_conf.inline_name) {
962 		inline_rows = show_inline_list(browser, chain->ms.map,
963 					       chain->ip, row + 1, offset);
964 	}
965 
966 	return ret + inline_rows;
967 }
968 
969 static bool check_percent_display(struct rb_node *node, u64 parent_total)
970 {
971 	struct callchain_node *child;
972 
973 	if (node == NULL)
974 		return false;
975 
976 	if (rb_next(node))
977 		return true;
978 
979 	child = rb_entry(node, struct callchain_node, rb_node);
980 	return callchain_cumul_hits(child) != parent_total;
981 }
982 
983 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
984 					     struct rb_root *root,
985 					     unsigned short row, u64 total,
986 					     u64 parent_total,
987 					     print_callchain_entry_fn print,
988 					     struct callchain_print_arg *arg,
989 					     check_output_full_fn is_output_full)
990 {
991 	struct rb_node *node;
992 	int first_row = row, offset = LEVEL_OFFSET_STEP;
993 	bool need_percent;
994 
995 	node = rb_first(root);
996 	need_percent = check_percent_display(node, parent_total);
997 
998 	while (node) {
999 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1000 		struct rb_node *next = rb_next(node);
1001 		struct callchain_list *chain;
1002 		char folded_sign = ' ';
1003 		int first = true;
1004 		int extra_offset = 0;
1005 
1006 		list_for_each_entry(chain, &child->parent_val, list) {
1007 			bool was_first = first;
1008 
1009 			if (first)
1010 				first = false;
1011 			else if (need_percent)
1012 				extra_offset = LEVEL_OFFSET_STEP;
1013 
1014 			folded_sign = callchain_list__folded(chain);
1015 
1016 			row += hist_browser__show_callchain_list(browser, child,
1017 							chain, row, total,
1018 							was_first && need_percent,
1019 							offset + extra_offset,
1020 							print, arg);
1021 
1022 			if (is_output_full(browser, row))
1023 				goto out;
1024 
1025 			if (folded_sign == '+')
1026 				goto next;
1027 		}
1028 
1029 		list_for_each_entry(chain, &child->val, list) {
1030 			bool was_first = first;
1031 
1032 			if (first)
1033 				first = false;
1034 			else if (need_percent)
1035 				extra_offset = LEVEL_OFFSET_STEP;
1036 
1037 			folded_sign = callchain_list__folded(chain);
1038 
1039 			row += hist_browser__show_callchain_list(browser, child,
1040 							chain, row, total,
1041 							was_first && need_percent,
1042 							offset + extra_offset,
1043 							print, arg);
1044 
1045 			if (is_output_full(browser, row))
1046 				goto out;
1047 
1048 			if (folded_sign == '+')
1049 				break;
1050 		}
1051 
1052 next:
1053 		if (is_output_full(browser, row))
1054 			break;
1055 		node = next;
1056 	}
1057 out:
1058 	return row - first_row;
1059 }
1060 
1061 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1062 						struct callchain_list *chain,
1063 						char *value_str, char *old_str)
1064 {
1065 	char bf[1024];
1066 	const char *str;
1067 	char *new;
1068 
1069 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
1070 				       browser->show_dso);
1071 	if (old_str) {
1072 		if (asprintf(&new, "%s%s%s", old_str,
1073 			     symbol_conf.field_sep ?: ";", str) < 0)
1074 			new = NULL;
1075 	} else {
1076 		if (value_str) {
1077 			if (asprintf(&new, "%s %s", value_str, str) < 0)
1078 				new = NULL;
1079 		} else {
1080 			if (asprintf(&new, "%s", str) < 0)
1081 				new = NULL;
1082 		}
1083 	}
1084 	return new;
1085 }
1086 
1087 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1088 					       struct rb_root *root,
1089 					       unsigned short row, u64 total,
1090 					       u64 parent_total,
1091 					       print_callchain_entry_fn print,
1092 					       struct callchain_print_arg *arg,
1093 					       check_output_full_fn is_output_full)
1094 {
1095 	struct rb_node *node;
1096 	int first_row = row, offset = LEVEL_OFFSET_STEP;
1097 	bool need_percent;
1098 
1099 	node = rb_first(root);
1100 	need_percent = check_percent_display(node, parent_total);
1101 
1102 	while (node) {
1103 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1104 		struct rb_node *next = rb_next(node);
1105 		struct callchain_list *chain, *first_chain = NULL;
1106 		int first = true;
1107 		char *value_str = NULL, *value_str_alloc = NULL;
1108 		char *chain_str = NULL, *chain_str_alloc = NULL;
1109 
1110 		if (arg->row_offset != 0) {
1111 			arg->row_offset--;
1112 			goto next;
1113 		}
1114 
1115 		if (need_percent) {
1116 			char buf[64];
1117 
1118 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1119 			if (asprintf(&value_str, "%s", buf) < 0) {
1120 				value_str = (char *)"<...>";
1121 				goto do_print;
1122 			}
1123 			value_str_alloc = value_str;
1124 		}
1125 
1126 		list_for_each_entry(chain, &child->parent_val, list) {
1127 			chain_str = hist_browser__folded_callchain_str(browser,
1128 						chain, value_str, chain_str);
1129 			if (first) {
1130 				first = false;
1131 				first_chain = chain;
1132 			}
1133 
1134 			if (chain_str == NULL) {
1135 				chain_str = (char *)"Not enough memory!";
1136 				goto do_print;
1137 			}
1138 
1139 			chain_str_alloc = chain_str;
1140 		}
1141 
1142 		list_for_each_entry(chain, &child->val, list) {
1143 			chain_str = hist_browser__folded_callchain_str(browser,
1144 						chain, value_str, chain_str);
1145 			if (first) {
1146 				first = false;
1147 				first_chain = chain;
1148 			}
1149 
1150 			if (chain_str == NULL) {
1151 				chain_str = (char *)"Not enough memory!";
1152 				goto do_print;
1153 			}
1154 
1155 			chain_str_alloc = chain_str;
1156 		}
1157 
1158 do_print:
1159 		print(browser, first_chain, chain_str, offset, row++, arg);
1160 		free(value_str_alloc);
1161 		free(chain_str_alloc);
1162 
1163 next:
1164 		if (is_output_full(browser, row))
1165 			break;
1166 		node = next;
1167 	}
1168 
1169 	return row - first_row;
1170 }
1171 
1172 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1173 					struct rb_root *root, int level,
1174 					unsigned short row, u64 total,
1175 					u64 parent_total,
1176 					print_callchain_entry_fn print,
1177 					struct callchain_print_arg *arg,
1178 					check_output_full_fn is_output_full)
1179 {
1180 	struct rb_node *node;
1181 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1182 	bool need_percent;
1183 	u64 percent_total = total;
1184 
1185 	if (callchain_param.mode == CHAIN_GRAPH_REL)
1186 		percent_total = parent_total;
1187 
1188 	node = rb_first(root);
1189 	need_percent = check_percent_display(node, parent_total);
1190 
1191 	while (node) {
1192 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1193 		struct rb_node *next = rb_next(node);
1194 		struct callchain_list *chain;
1195 		char folded_sign = ' ';
1196 		int first = true;
1197 		int extra_offset = 0;
1198 
1199 		list_for_each_entry(chain, &child->val, list) {
1200 			bool was_first = first;
1201 
1202 			if (first)
1203 				first = false;
1204 			else if (need_percent)
1205 				extra_offset = LEVEL_OFFSET_STEP;
1206 
1207 			folded_sign = callchain_list__folded(chain);
1208 
1209 			row += hist_browser__show_callchain_list(browser, child,
1210 							chain, row, percent_total,
1211 							was_first && need_percent,
1212 							offset + extra_offset,
1213 							print, arg);
1214 
1215 			if (is_output_full(browser, row))
1216 				goto out;
1217 
1218 			if (folded_sign == '+')
1219 				break;
1220 		}
1221 
1222 		if (folded_sign == '-') {
1223 			const int new_level = level + (extra_offset ? 2 : 1);
1224 
1225 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1226 							    new_level, row, total,
1227 							    child->children_hit,
1228 							    print, arg, is_output_full);
1229 		}
1230 		if (is_output_full(browser, row))
1231 			break;
1232 		node = next;
1233 	}
1234 out:
1235 	return row - first_row;
1236 }
1237 
1238 static int hist_browser__show_callchain(struct hist_browser *browser,
1239 					struct hist_entry *entry, int level,
1240 					unsigned short row,
1241 					print_callchain_entry_fn print,
1242 					struct callchain_print_arg *arg,
1243 					check_output_full_fn is_output_full)
1244 {
1245 	u64 total = hists__total_period(entry->hists);
1246 	u64 parent_total;
1247 	int printed;
1248 
1249 	if (symbol_conf.cumulate_callchain)
1250 		parent_total = entry->stat_acc->period;
1251 	else
1252 		parent_total = entry->stat.period;
1253 
1254 	if (callchain_param.mode == CHAIN_FLAT) {
1255 		printed = hist_browser__show_callchain_flat(browser,
1256 						&entry->sorted_chain, row,
1257 						total, parent_total, print, arg,
1258 						is_output_full);
1259 	} else if (callchain_param.mode == CHAIN_FOLDED) {
1260 		printed = hist_browser__show_callchain_folded(browser,
1261 						&entry->sorted_chain, row,
1262 						total, parent_total, print, arg,
1263 						is_output_full);
1264 	} else {
1265 		printed = hist_browser__show_callchain_graph(browser,
1266 						&entry->sorted_chain, level, row,
1267 						total, parent_total, print, arg,
1268 						is_output_full);
1269 	}
1270 
1271 	if (arg->is_current_entry)
1272 		browser->he_selection = entry;
1273 
1274 	return printed;
1275 }
1276 
1277 struct hpp_arg {
1278 	struct ui_browser *b;
1279 	char folded_sign;
1280 	bool current_entry;
1281 };
1282 
1283 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1284 {
1285 	struct hpp_arg *arg = hpp->ptr;
1286 	int ret, len;
1287 	va_list args;
1288 	double percent;
1289 
1290 	va_start(args, fmt);
1291 	len = va_arg(args, int);
1292 	percent = va_arg(args, double);
1293 	va_end(args);
1294 
1295 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1296 
1297 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1298 	ui_browser__printf(arg->b, "%s", hpp->buf);
1299 
1300 	return ret;
1301 }
1302 
1303 #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
1304 static u64 __hpp_get_##_field(struct hist_entry *he)			\
1305 {									\
1306 	return he->stat._field;						\
1307 }									\
1308 									\
1309 static int								\
1310 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1311 				struct perf_hpp *hpp,			\
1312 				struct hist_entry *he)			\
1313 {									\
1314 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
1315 			__hpp__slsmg_color_printf, true);		\
1316 }
1317 
1318 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
1319 static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
1320 {									\
1321 	return he->stat_acc->_field;					\
1322 }									\
1323 									\
1324 static int								\
1325 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1326 				struct perf_hpp *hpp,			\
1327 				struct hist_entry *he)			\
1328 {									\
1329 	if (!symbol_conf.cumulate_callchain) {				\
1330 		struct hpp_arg *arg = hpp->ptr;				\
1331 		int len = fmt->user_len ?: fmt->len;			\
1332 		int ret = scnprintf(hpp->buf, hpp->size,		\
1333 				    "%*s", len, "N/A");			\
1334 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
1335 									\
1336 		return ret;						\
1337 	}								\
1338 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
1339 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
1340 }
1341 
1342 __HPP_COLOR_PERCENT_FN(overhead, period)
1343 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1344 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1345 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1346 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1347 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1348 
1349 #undef __HPP_COLOR_PERCENT_FN
1350 #undef __HPP_COLOR_ACC_PERCENT_FN
1351 
1352 void hist_browser__init_hpp(void)
1353 {
1354 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1355 				hist_browser__hpp_color_overhead;
1356 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1357 				hist_browser__hpp_color_overhead_sys;
1358 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1359 				hist_browser__hpp_color_overhead_us;
1360 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1361 				hist_browser__hpp_color_overhead_guest_sys;
1362 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1363 				hist_browser__hpp_color_overhead_guest_us;
1364 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1365 				hist_browser__hpp_color_overhead_acc;
1366 }
1367 
1368 static int hist_browser__show_entry(struct hist_browser *browser,
1369 				    struct hist_entry *entry,
1370 				    unsigned short row)
1371 {
1372 	int printed = 0;
1373 	int width = browser->b.width;
1374 	char folded_sign = ' ';
1375 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1376 	off_t row_offset = entry->row_offset;
1377 	bool first = true;
1378 	struct perf_hpp_fmt *fmt;
1379 
1380 	if (current_entry) {
1381 		browser->he_selection = entry;
1382 		browser->selection = &entry->ms;
1383 	}
1384 
1385 	if (symbol_conf.use_callchain) {
1386 		hist_entry__init_have_children(entry);
1387 		folded_sign = hist_entry__folded(entry);
1388 	}
1389 
1390 	if (symbol_conf.inline_name &&
1391 	    (!entry->has_children)) {
1392 		hist_entry_init_inline_node(entry);
1393 		folded_sign = hist_entry__folded(entry);
1394 	}
1395 
1396 	if (row_offset == 0) {
1397 		struct hpp_arg arg = {
1398 			.b		= &browser->b,
1399 			.folded_sign	= folded_sign,
1400 			.current_entry	= current_entry,
1401 		};
1402 		int column = 0;
1403 
1404 		hist_browser__gotorc(browser, row, 0);
1405 
1406 		hists__for_each_format(browser->hists, fmt) {
1407 			char s[2048];
1408 			struct perf_hpp hpp = {
1409 				.buf	= s,
1410 				.size	= sizeof(s),
1411 				.ptr	= &arg,
1412 			};
1413 
1414 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1415 			    column++ < browser->b.horiz_scroll)
1416 				continue;
1417 
1418 			if (current_entry && browser->b.navkeypressed) {
1419 				ui_browser__set_color(&browser->b,
1420 						      HE_COLORSET_SELECTED);
1421 			} else {
1422 				ui_browser__set_color(&browser->b,
1423 						      HE_COLORSET_NORMAL);
1424 			}
1425 
1426 			if (first) {
1427 				if (symbol_conf.use_callchain ||
1428 					symbol_conf.inline_name) {
1429 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1430 					width -= 2;
1431 				}
1432 				first = false;
1433 			} else {
1434 				ui_browser__printf(&browser->b, "  ");
1435 				width -= 2;
1436 			}
1437 
1438 			if (fmt->color) {
1439 				int ret = fmt->color(fmt, &hpp, entry);
1440 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1441 				/*
1442 				 * fmt->color() already used ui_browser to
1443 				 * print the non alignment bits, skip it (+ret):
1444 				 */
1445 				ui_browser__printf(&browser->b, "%s", s + ret);
1446 			} else {
1447 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1448 				ui_browser__printf(&browser->b, "%s", s);
1449 			}
1450 			width -= hpp.buf - s;
1451 		}
1452 
1453 		/* The scroll bar isn't being used */
1454 		if (!browser->b.navkeypressed)
1455 			width += 1;
1456 
1457 		ui_browser__write_nstring(&browser->b, "", width);
1458 
1459 		++row;
1460 		++printed;
1461 	} else
1462 		--row_offset;
1463 
1464 	if (folded_sign == '-' && row != browser->b.rows) {
1465 		struct callchain_print_arg arg = {
1466 			.row_offset = row_offset,
1467 			.is_current_entry = current_entry,
1468 		};
1469 
1470 		if (entry->inline_node)
1471 			printed += hist_browser__show_inline(browser,
1472 					entry->inline_node, row, 0);
1473 		else
1474 			printed += hist_browser__show_callchain(browser,
1475 					entry, 1, row,
1476 					hist_browser__show_callchain_entry,
1477 					&arg,
1478 					hist_browser__check_output_full);
1479 	}
1480 
1481 	return printed;
1482 }
1483 
1484 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1485 					      struct hist_entry *entry,
1486 					      unsigned short row,
1487 					      int level)
1488 {
1489 	int printed = 0;
1490 	int width = browser->b.width;
1491 	char folded_sign = ' ';
1492 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1493 	off_t row_offset = entry->row_offset;
1494 	bool first = true;
1495 	struct perf_hpp_fmt *fmt;
1496 	struct perf_hpp_list_node *fmt_node;
1497 	struct hpp_arg arg = {
1498 		.b		= &browser->b,
1499 		.current_entry	= current_entry,
1500 	};
1501 	int column = 0;
1502 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1503 
1504 	if (current_entry) {
1505 		browser->he_selection = entry;
1506 		browser->selection = &entry->ms;
1507 	}
1508 
1509 	hist_entry__init_have_children(entry);
1510 	folded_sign = hist_entry__folded(entry);
1511 	arg.folded_sign = folded_sign;
1512 
1513 	if (entry->leaf && row_offset) {
1514 		row_offset--;
1515 		goto show_callchain;
1516 	}
1517 
1518 	hist_browser__gotorc(browser, row, 0);
1519 
1520 	if (current_entry && browser->b.navkeypressed)
1521 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1522 	else
1523 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1524 
1525 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1526 	width -= level * HIERARCHY_INDENT;
1527 
1528 	/* the first hpp_list_node is for overhead columns */
1529 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1530 				    struct perf_hpp_list_node, list);
1531 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1532 		char s[2048];
1533 		struct perf_hpp hpp = {
1534 			.buf		= s,
1535 			.size		= sizeof(s),
1536 			.ptr		= &arg,
1537 		};
1538 
1539 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1540 		    column++ < browser->b.horiz_scroll)
1541 			continue;
1542 
1543 		if (current_entry && browser->b.navkeypressed) {
1544 			ui_browser__set_color(&browser->b,
1545 					      HE_COLORSET_SELECTED);
1546 		} else {
1547 			ui_browser__set_color(&browser->b,
1548 					      HE_COLORSET_NORMAL);
1549 		}
1550 
1551 		if (first) {
1552 			ui_browser__printf(&browser->b, "%c ", folded_sign);
1553 			width -= 2;
1554 			first = false;
1555 		} else {
1556 			ui_browser__printf(&browser->b, "  ");
1557 			width -= 2;
1558 		}
1559 
1560 		if (fmt->color) {
1561 			int ret = fmt->color(fmt, &hpp, entry);
1562 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1563 			/*
1564 			 * fmt->color() already used ui_browser to
1565 			 * print the non alignment bits, skip it (+ret):
1566 			 */
1567 			ui_browser__printf(&browser->b, "%s", s + ret);
1568 		} else {
1569 			int ret = fmt->entry(fmt, &hpp, entry);
1570 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1571 			ui_browser__printf(&browser->b, "%s", s);
1572 		}
1573 		width -= hpp.buf - s;
1574 	}
1575 
1576 	if (!first) {
1577 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1578 		width -= hierarchy_indent;
1579 	}
1580 
1581 	if (column >= browser->b.horiz_scroll) {
1582 		char s[2048];
1583 		struct perf_hpp hpp = {
1584 			.buf		= s,
1585 			.size		= sizeof(s),
1586 			.ptr		= &arg,
1587 		};
1588 
1589 		if (current_entry && browser->b.navkeypressed) {
1590 			ui_browser__set_color(&browser->b,
1591 					      HE_COLORSET_SELECTED);
1592 		} else {
1593 			ui_browser__set_color(&browser->b,
1594 					      HE_COLORSET_NORMAL);
1595 		}
1596 
1597 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1598 			if (first) {
1599 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1600 				first = false;
1601 			} else {
1602 				ui_browser__write_nstring(&browser->b, "", 2);
1603 			}
1604 
1605 			width -= 2;
1606 
1607 			/*
1608 			 * No need to call hist_entry__snprintf_alignment()
1609 			 * since this fmt is always the last column in the
1610 			 * hierarchy mode.
1611 			 */
1612 			if (fmt->color) {
1613 				width -= fmt->color(fmt, &hpp, entry);
1614 			} else {
1615 				int i = 0;
1616 
1617 				width -= fmt->entry(fmt, &hpp, entry);
1618 				ui_browser__printf(&browser->b, "%s", ltrim(s));
1619 
1620 				while (isspace(s[i++]))
1621 					width++;
1622 			}
1623 		}
1624 	}
1625 
1626 	/* The scroll bar isn't being used */
1627 	if (!browser->b.navkeypressed)
1628 		width += 1;
1629 
1630 	ui_browser__write_nstring(&browser->b, "", width);
1631 
1632 	++row;
1633 	++printed;
1634 
1635 show_callchain:
1636 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1637 		struct callchain_print_arg carg = {
1638 			.row_offset = row_offset,
1639 		};
1640 
1641 		printed += hist_browser__show_callchain(browser, entry,
1642 					level + 1, row,
1643 					hist_browser__show_callchain_entry, &carg,
1644 					hist_browser__check_output_full);
1645 	}
1646 
1647 	return printed;
1648 }
1649 
1650 static int hist_browser__show_no_entry(struct hist_browser *browser,
1651 				       unsigned short row, int level)
1652 {
1653 	int width = browser->b.width;
1654 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1655 	bool first = true;
1656 	int column = 0;
1657 	int ret;
1658 	struct perf_hpp_fmt *fmt;
1659 	struct perf_hpp_list_node *fmt_node;
1660 	int indent = browser->hists->nr_hpp_node - 2;
1661 
1662 	if (current_entry) {
1663 		browser->he_selection = NULL;
1664 		browser->selection = NULL;
1665 	}
1666 
1667 	hist_browser__gotorc(browser, row, 0);
1668 
1669 	if (current_entry && browser->b.navkeypressed)
1670 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1671 	else
1672 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1673 
1674 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1675 	width -= level * HIERARCHY_INDENT;
1676 
1677 	/* the first hpp_list_node is for overhead columns */
1678 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1679 				    struct perf_hpp_list_node, list);
1680 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1681 		if (perf_hpp__should_skip(fmt, browser->hists) ||
1682 		    column++ < browser->b.horiz_scroll)
1683 			continue;
1684 
1685 		ret = fmt->width(fmt, NULL, browser->hists);
1686 
1687 		if (first) {
1688 			/* for folded sign */
1689 			first = false;
1690 			ret++;
1691 		} else {
1692 			/* space between columns */
1693 			ret += 2;
1694 		}
1695 
1696 		ui_browser__write_nstring(&browser->b, "", ret);
1697 		width -= ret;
1698 	}
1699 
1700 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1701 	width -= indent * HIERARCHY_INDENT;
1702 
1703 	if (column >= browser->b.horiz_scroll) {
1704 		char buf[32];
1705 
1706 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1707 		ui_browser__printf(&browser->b, "  %s", buf);
1708 		width -= ret + 2;
1709 	}
1710 
1711 	/* The scroll bar isn't being used */
1712 	if (!browser->b.navkeypressed)
1713 		width += 1;
1714 
1715 	ui_browser__write_nstring(&browser->b, "", width);
1716 	return 1;
1717 }
1718 
1719 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1720 {
1721 	advance_hpp(hpp, inc);
1722 	return hpp->size <= 0;
1723 }
1724 
1725 static int
1726 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1727 				 size_t size, int line)
1728 {
1729 	struct hists *hists = browser->hists;
1730 	struct perf_hpp dummy_hpp = {
1731 		.buf    = buf,
1732 		.size   = size,
1733 	};
1734 	struct perf_hpp_fmt *fmt;
1735 	size_t ret = 0;
1736 	int column = 0;
1737 	int span = 0;
1738 
1739 	if (symbol_conf.use_callchain) {
1740 		ret = scnprintf(buf, size, "  ");
1741 		if (advance_hpp_check(&dummy_hpp, ret))
1742 			return ret;
1743 	}
1744 
1745 	hists__for_each_format(browser->hists, fmt) {
1746 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1747 			continue;
1748 
1749 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1750 		if (advance_hpp_check(&dummy_hpp, ret))
1751 			break;
1752 
1753 		if (span)
1754 			continue;
1755 
1756 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1757 		if (advance_hpp_check(&dummy_hpp, ret))
1758 			break;
1759 	}
1760 
1761 	return ret;
1762 }
1763 
1764 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1765 {
1766 	struct hists *hists = browser->hists;
1767 	struct perf_hpp dummy_hpp = {
1768 		.buf    = buf,
1769 		.size   = size,
1770 	};
1771 	struct perf_hpp_fmt *fmt;
1772 	struct perf_hpp_list_node *fmt_node;
1773 	size_t ret = 0;
1774 	int column = 0;
1775 	int indent = hists->nr_hpp_node - 2;
1776 	bool first_node, first_col;
1777 
1778 	ret = scnprintf(buf, size, "  ");
1779 	if (advance_hpp_check(&dummy_hpp, ret))
1780 		return ret;
1781 
1782 	first_node = true;
1783 	/* the first hpp_list_node is for overhead columns */
1784 	fmt_node = list_first_entry(&hists->hpp_formats,
1785 				    struct perf_hpp_list_node, list);
1786 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1787 		if (column++ < browser->b.horiz_scroll)
1788 			continue;
1789 
1790 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1791 		if (advance_hpp_check(&dummy_hpp, ret))
1792 			break;
1793 
1794 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1795 		if (advance_hpp_check(&dummy_hpp, ret))
1796 			break;
1797 
1798 		first_node = false;
1799 	}
1800 
1801 	if (!first_node) {
1802 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1803 				indent * HIERARCHY_INDENT, "");
1804 		if (advance_hpp_check(&dummy_hpp, ret))
1805 			return ret;
1806 	}
1807 
1808 	first_node = true;
1809 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1810 		if (!first_node) {
1811 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1812 			if (advance_hpp_check(&dummy_hpp, ret))
1813 				break;
1814 		}
1815 		first_node = false;
1816 
1817 		first_col = true;
1818 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1819 			char *start;
1820 
1821 			if (perf_hpp__should_skip(fmt, hists))
1822 				continue;
1823 
1824 			if (!first_col) {
1825 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1826 				if (advance_hpp_check(&dummy_hpp, ret))
1827 					break;
1828 			}
1829 			first_col = false;
1830 
1831 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1832 			dummy_hpp.buf[ret] = '\0';
1833 
1834 			start = trim(dummy_hpp.buf);
1835 			ret = strlen(start);
1836 
1837 			if (start != dummy_hpp.buf)
1838 				memmove(dummy_hpp.buf, start, ret + 1);
1839 
1840 			if (advance_hpp_check(&dummy_hpp, ret))
1841 				break;
1842 		}
1843 	}
1844 
1845 	return ret;
1846 }
1847 
1848 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1849 {
1850 	char headers[1024];
1851 
1852 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1853 						   sizeof(headers));
1854 
1855 	ui_browser__gotorc(&browser->b, 0, 0);
1856 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1857 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1858 }
1859 
1860 static void hists_browser__headers(struct hist_browser *browser)
1861 {
1862 	struct hists *hists = browser->hists;
1863 	struct perf_hpp_list *hpp_list = hists->hpp_list;
1864 
1865 	int line;
1866 
1867 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
1868 		char headers[1024];
1869 
1870 		hists_browser__scnprintf_headers(browser, headers,
1871 						 sizeof(headers), line);
1872 
1873 		ui_browser__gotorc(&browser->b, line, 0);
1874 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1875 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1876 	}
1877 }
1878 
1879 static void hist_browser__show_headers(struct hist_browser *browser)
1880 {
1881 	if (symbol_conf.report_hierarchy)
1882 		hists_browser__hierarchy_headers(browser);
1883 	else
1884 		hists_browser__headers(browser);
1885 }
1886 
1887 static void ui_browser__hists_init_top(struct ui_browser *browser)
1888 {
1889 	if (browser->top == NULL) {
1890 		struct hist_browser *hb;
1891 
1892 		hb = container_of(browser, struct hist_browser, b);
1893 		browser->top = rb_first(&hb->hists->entries);
1894 	}
1895 }
1896 
1897 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1898 {
1899 	unsigned row = 0;
1900 	u16 header_offset = 0;
1901 	struct rb_node *nd;
1902 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1903 	struct hists *hists = hb->hists;
1904 
1905 	if (hb->show_headers) {
1906 		struct perf_hpp_list *hpp_list = hists->hpp_list;
1907 
1908 		hist_browser__show_headers(hb);
1909 		header_offset = hpp_list->nr_header_lines;
1910 	}
1911 
1912 	ui_browser__hists_init_top(browser);
1913 	hb->he_selection = NULL;
1914 	hb->selection = NULL;
1915 
1916 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1917 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1918 		float percent;
1919 
1920 		if (h->filtered) {
1921 			/* let it move to sibling */
1922 			h->unfolded = false;
1923 			continue;
1924 		}
1925 
1926 		percent = hist_entry__get_percent_limit(h);
1927 		if (percent < hb->min_pcnt)
1928 			continue;
1929 
1930 		if (symbol_conf.report_hierarchy) {
1931 			row += hist_browser__show_hierarchy_entry(hb, h, row,
1932 								  h->depth);
1933 			if (row == browser->rows)
1934 				break;
1935 
1936 			if (h->has_no_entry) {
1937 				hist_browser__show_no_entry(hb, row, h->depth + 1);
1938 				row++;
1939 			}
1940 		} else {
1941 			row += hist_browser__show_entry(hb, h, row);
1942 		}
1943 
1944 		if (row == browser->rows)
1945 			break;
1946 	}
1947 
1948 	return row + header_offset;
1949 }
1950 
1951 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1952 					     float min_pcnt)
1953 {
1954 	while (nd != NULL) {
1955 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1956 		float percent = hist_entry__get_percent_limit(h);
1957 
1958 		if (!h->filtered && percent >= min_pcnt)
1959 			return nd;
1960 
1961 		/*
1962 		 * If it's filtered, its all children also were filtered.
1963 		 * So move to sibling node.
1964 		 */
1965 		if (rb_next(nd))
1966 			nd = rb_next(nd);
1967 		else
1968 			nd = rb_hierarchy_next(nd);
1969 	}
1970 
1971 	return NULL;
1972 }
1973 
1974 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1975 						  float min_pcnt)
1976 {
1977 	while (nd != NULL) {
1978 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1979 		float percent = hist_entry__get_percent_limit(h);
1980 
1981 		if (!h->filtered && percent >= min_pcnt)
1982 			return nd;
1983 
1984 		nd = rb_hierarchy_prev(nd);
1985 	}
1986 
1987 	return NULL;
1988 }
1989 
1990 static void ui_browser__hists_seek(struct ui_browser *browser,
1991 				   off_t offset, int whence)
1992 {
1993 	struct hist_entry *h;
1994 	struct rb_node *nd;
1995 	bool first = true;
1996 	struct hist_browser *hb;
1997 
1998 	hb = container_of(browser, struct hist_browser, b);
1999 
2000 	if (browser->nr_entries == 0)
2001 		return;
2002 
2003 	ui_browser__hists_init_top(browser);
2004 
2005 	switch (whence) {
2006 	case SEEK_SET:
2007 		nd = hists__filter_entries(rb_first(browser->entries),
2008 					   hb->min_pcnt);
2009 		break;
2010 	case SEEK_CUR:
2011 		nd = browser->top;
2012 		goto do_offset;
2013 	case SEEK_END:
2014 		nd = rb_hierarchy_last(rb_last(browser->entries));
2015 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
2016 		first = false;
2017 		break;
2018 	default:
2019 		return;
2020 	}
2021 
2022 	/*
2023 	 * Moves not relative to the first visible entry invalidates its
2024 	 * row_offset:
2025 	 */
2026 	h = rb_entry(browser->top, struct hist_entry, rb_node);
2027 	h->row_offset = 0;
2028 
2029 	/*
2030 	 * Here we have to check if nd is expanded (+), if it is we can't go
2031 	 * the next top level hist_entry, instead we must compute an offset of
2032 	 * what _not_ to show and not change the first visible entry.
2033 	 *
2034 	 * This offset increments when we are going from top to bottom and
2035 	 * decreases when we're going from bottom to top.
2036 	 *
2037 	 * As we don't have backpointers to the top level in the callchains
2038 	 * structure, we need to always print the whole hist_entry callchain,
2039 	 * skipping the first ones that are before the first visible entry
2040 	 * and stop when we printed enough lines to fill the screen.
2041 	 */
2042 do_offset:
2043 	if (!nd)
2044 		return;
2045 
2046 	if (offset > 0) {
2047 		do {
2048 			h = rb_entry(nd, struct hist_entry, rb_node);
2049 			if (h->unfolded && h->leaf) {
2050 				u16 remaining = h->nr_rows - h->row_offset;
2051 				if (offset > remaining) {
2052 					offset -= remaining;
2053 					h->row_offset = 0;
2054 				} else {
2055 					h->row_offset += offset;
2056 					offset = 0;
2057 					browser->top = nd;
2058 					break;
2059 				}
2060 			}
2061 			nd = hists__filter_entries(rb_hierarchy_next(nd),
2062 						   hb->min_pcnt);
2063 			if (nd == NULL)
2064 				break;
2065 			--offset;
2066 			browser->top = nd;
2067 		} while (offset != 0);
2068 	} else if (offset < 0) {
2069 		while (1) {
2070 			h = rb_entry(nd, struct hist_entry, rb_node);
2071 			if (h->unfolded && h->leaf) {
2072 				if (first) {
2073 					if (-offset > h->row_offset) {
2074 						offset += h->row_offset;
2075 						h->row_offset = 0;
2076 					} else {
2077 						h->row_offset += offset;
2078 						offset = 0;
2079 						browser->top = nd;
2080 						break;
2081 					}
2082 				} else {
2083 					if (-offset > h->nr_rows) {
2084 						offset += h->nr_rows;
2085 						h->row_offset = 0;
2086 					} else {
2087 						h->row_offset = h->nr_rows + offset;
2088 						offset = 0;
2089 						browser->top = nd;
2090 						break;
2091 					}
2092 				}
2093 			}
2094 
2095 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2096 							hb->min_pcnt);
2097 			if (nd == NULL)
2098 				break;
2099 			++offset;
2100 			browser->top = nd;
2101 			if (offset == 0) {
2102 				/*
2103 				 * Last unfiltered hist_entry, check if it is
2104 				 * unfolded, if it is then we should have
2105 				 * row_offset at its last entry.
2106 				 */
2107 				h = rb_entry(nd, struct hist_entry, rb_node);
2108 				if (h->unfolded && h->leaf)
2109 					h->row_offset = h->nr_rows;
2110 				break;
2111 			}
2112 			first = false;
2113 		}
2114 	} else {
2115 		browser->top = nd;
2116 		h = rb_entry(nd, struct hist_entry, rb_node);
2117 		h->row_offset = 0;
2118 	}
2119 }
2120 
2121 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2122 					   struct hist_entry *he, FILE *fp,
2123 					   int level)
2124 {
2125 	struct callchain_print_arg arg  = {
2126 		.fp = fp,
2127 	};
2128 
2129 	hist_browser__show_callchain(browser, he, level, 0,
2130 				     hist_browser__fprintf_callchain_entry, &arg,
2131 				     hist_browser__check_dump_full);
2132 	return arg.printed;
2133 }
2134 
2135 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2136 				       struct hist_entry *he, FILE *fp)
2137 {
2138 	char s[8192];
2139 	int printed = 0;
2140 	char folded_sign = ' ';
2141 	struct perf_hpp hpp = {
2142 		.buf = s,
2143 		.size = sizeof(s),
2144 	};
2145 	struct perf_hpp_fmt *fmt;
2146 	bool first = true;
2147 	int ret;
2148 
2149 	if (symbol_conf.use_callchain) {
2150 		folded_sign = hist_entry__folded(he);
2151 		printed += fprintf(fp, "%c ", folded_sign);
2152 	}
2153 
2154 	hists__for_each_format(browser->hists, fmt) {
2155 		if (perf_hpp__should_skip(fmt, he->hists))
2156 			continue;
2157 
2158 		if (!first) {
2159 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2160 			advance_hpp(&hpp, ret);
2161 		} else
2162 			first = false;
2163 
2164 		ret = fmt->entry(fmt, &hpp, he);
2165 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2166 		advance_hpp(&hpp, ret);
2167 	}
2168 	printed += fprintf(fp, "%s\n", s);
2169 
2170 	if (folded_sign == '-')
2171 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2172 
2173 	return printed;
2174 }
2175 
2176 
2177 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2178 						 struct hist_entry *he,
2179 						 FILE *fp, int level)
2180 {
2181 	char s[8192];
2182 	int printed = 0;
2183 	char folded_sign = ' ';
2184 	struct perf_hpp hpp = {
2185 		.buf = s,
2186 		.size = sizeof(s),
2187 	};
2188 	struct perf_hpp_fmt *fmt;
2189 	struct perf_hpp_list_node *fmt_node;
2190 	bool first = true;
2191 	int ret;
2192 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2193 
2194 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2195 
2196 	folded_sign = hist_entry__folded(he);
2197 	printed += fprintf(fp, "%c", folded_sign);
2198 
2199 	/* the first hpp_list_node is for overhead columns */
2200 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2201 				    struct perf_hpp_list_node, list);
2202 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2203 		if (!first) {
2204 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2205 			advance_hpp(&hpp, ret);
2206 		} else
2207 			first = false;
2208 
2209 		ret = fmt->entry(fmt, &hpp, he);
2210 		advance_hpp(&hpp, ret);
2211 	}
2212 
2213 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2214 	advance_hpp(&hpp, ret);
2215 
2216 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2217 		ret = scnprintf(hpp.buf, hpp.size, "  ");
2218 		advance_hpp(&hpp, ret);
2219 
2220 		ret = fmt->entry(fmt, &hpp, he);
2221 		advance_hpp(&hpp, ret);
2222 	}
2223 
2224 	printed += fprintf(fp, "%s\n", rtrim(s));
2225 
2226 	if (he->leaf && folded_sign == '-') {
2227 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2228 							   he->depth + 1);
2229 	}
2230 
2231 	return printed;
2232 }
2233 
2234 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2235 {
2236 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2237 						   browser->min_pcnt);
2238 	int printed = 0;
2239 
2240 	while (nd) {
2241 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2242 
2243 		if (symbol_conf.report_hierarchy) {
2244 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2245 									 h, fp,
2246 									 h->depth);
2247 		} else {
2248 			printed += hist_browser__fprintf_entry(browser, h, fp);
2249 		}
2250 
2251 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2252 					   browser->min_pcnt);
2253 	}
2254 
2255 	return printed;
2256 }
2257 
2258 static int hist_browser__dump(struct hist_browser *browser)
2259 {
2260 	char filename[64];
2261 	FILE *fp;
2262 
2263 	while (1) {
2264 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2265 		if (access(filename, F_OK))
2266 			break;
2267 		/*
2268  		 * XXX: Just an arbitrary lazy upper limit
2269  		 */
2270 		if (++browser->print_seq == 8192) {
2271 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2272 			return -1;
2273 		}
2274 	}
2275 
2276 	fp = fopen(filename, "w");
2277 	if (fp == NULL) {
2278 		char bf[64];
2279 		const char *err = str_error_r(errno, bf, sizeof(bf));
2280 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2281 		return -1;
2282 	}
2283 
2284 	++browser->print_seq;
2285 	hist_browser__fprintf(browser, fp);
2286 	fclose(fp);
2287 	ui_helpline__fpush("%s written!", filename);
2288 
2289 	return 0;
2290 }
2291 
2292 void hist_browser__init(struct hist_browser *browser,
2293 			struct hists *hists)
2294 {
2295 	struct perf_hpp_fmt *fmt;
2296 
2297 	browser->hists			= hists;
2298 	browser->b.refresh		= hist_browser__refresh;
2299 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
2300 	browser->b.seek			= ui_browser__hists_seek;
2301 	browser->b.use_navkeypressed	= true;
2302 	browser->show_headers		= symbol_conf.show_hist_headers;
2303 
2304 	if (symbol_conf.report_hierarchy) {
2305 		struct perf_hpp_list_node *fmt_node;
2306 
2307 		/* count overhead columns (in the first node) */
2308 		fmt_node = list_first_entry(&hists->hpp_formats,
2309 					    struct perf_hpp_list_node, list);
2310 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2311 			++browser->b.columns;
2312 
2313 		/* add a single column for whole hierarchy sort keys*/
2314 		++browser->b.columns;
2315 	} else {
2316 		hists__for_each_format(hists, fmt)
2317 			++browser->b.columns;
2318 	}
2319 
2320 	hists__reset_column_width(hists);
2321 }
2322 
2323 struct hist_browser *hist_browser__new(struct hists *hists)
2324 {
2325 	struct hist_browser *browser = zalloc(sizeof(*browser));
2326 
2327 	if (browser)
2328 		hist_browser__init(browser, hists);
2329 
2330 	return browser;
2331 }
2332 
2333 static struct hist_browser *
2334 perf_evsel_browser__new(struct perf_evsel *evsel,
2335 			struct hist_browser_timer *hbt,
2336 			struct perf_env *env)
2337 {
2338 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2339 
2340 	if (browser) {
2341 		browser->hbt   = hbt;
2342 		browser->env   = env;
2343 		browser->title = perf_evsel_browser_title;
2344 	}
2345 	return browser;
2346 }
2347 
2348 void hist_browser__delete(struct hist_browser *browser)
2349 {
2350 	free(browser);
2351 }
2352 
2353 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2354 {
2355 	return browser->he_selection;
2356 }
2357 
2358 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2359 {
2360 	return browser->he_selection->thread;
2361 }
2362 
2363 /* Check whether the browser is for 'top' or 'report' */
2364 static inline bool is_report_browser(void *timer)
2365 {
2366 	return timer == NULL;
2367 }
2368 
2369 static int perf_evsel_browser_title(struct hist_browser *browser,
2370 				char *bf, size_t size)
2371 {
2372 	struct hist_browser_timer *hbt = browser->hbt;
2373 	struct hists *hists = browser->hists;
2374 	char unit;
2375 	int printed;
2376 	const struct dso *dso = hists->dso_filter;
2377 	const struct thread *thread = hists->thread_filter;
2378 	int socket_id = hists->socket_filter;
2379 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2380 	u64 nr_events = hists->stats.total_period;
2381 	struct perf_evsel *evsel = hists_to_evsel(hists);
2382 	const char *ev_name = perf_evsel__name(evsel);
2383 	char buf[512];
2384 	size_t buflen = sizeof(buf);
2385 	char ref[30] = " show reference callgraph, ";
2386 	bool enable_ref = false;
2387 
2388 	if (symbol_conf.filter_relative) {
2389 		nr_samples = hists->stats.nr_non_filtered_samples;
2390 		nr_events = hists->stats.total_non_filtered_period;
2391 	}
2392 
2393 	if (perf_evsel__is_group_event(evsel)) {
2394 		struct perf_evsel *pos;
2395 
2396 		perf_evsel__group_desc(evsel, buf, buflen);
2397 		ev_name = buf;
2398 
2399 		for_each_group_member(pos, evsel) {
2400 			struct hists *pos_hists = evsel__hists(pos);
2401 
2402 			if (symbol_conf.filter_relative) {
2403 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
2404 				nr_events += pos_hists->stats.total_non_filtered_period;
2405 			} else {
2406 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2407 				nr_events += pos_hists->stats.total_period;
2408 			}
2409 		}
2410 	}
2411 
2412 	if (symbol_conf.show_ref_callgraph &&
2413 	    strstr(ev_name, "call-graph=no"))
2414 		enable_ref = true;
2415 	nr_samples = convert_unit(nr_samples, &unit);
2416 	printed = scnprintf(bf, size,
2417 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2418 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2419 
2420 
2421 	if (hists->uid_filter_str)
2422 		printed += snprintf(bf + printed, size - printed,
2423 				    ", UID: %s", hists->uid_filter_str);
2424 	if (thread) {
2425 		if (hists__has(hists, thread)) {
2426 			printed += scnprintf(bf + printed, size - printed,
2427 				    ", Thread: %s(%d)",
2428 				     (thread->comm_set ? thread__comm_str(thread) : ""),
2429 				    thread->tid);
2430 		} else {
2431 			printed += scnprintf(bf + printed, size - printed,
2432 				    ", Thread: %s",
2433 				     (thread->comm_set ? thread__comm_str(thread) : ""));
2434 		}
2435 	}
2436 	if (dso)
2437 		printed += scnprintf(bf + printed, size - printed,
2438 				    ", DSO: %s", dso->short_name);
2439 	if (socket_id > -1)
2440 		printed += scnprintf(bf + printed, size - printed,
2441 				    ", Processor Socket: %d", socket_id);
2442 	if (!is_report_browser(hbt)) {
2443 		struct perf_top *top = hbt->arg;
2444 
2445 		if (top->zero)
2446 			printed += scnprintf(bf + printed, size - printed, " [z]");
2447 	}
2448 
2449 	return printed;
2450 }
2451 
2452 static inline void free_popup_options(char **options, int n)
2453 {
2454 	int i;
2455 
2456 	for (i = 0; i < n; ++i)
2457 		zfree(&options[i]);
2458 }
2459 
2460 /*
2461  * Only runtime switching of perf data file will make "input_name" point
2462  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2463  * whether we need to call free() for current "input_name" during the switch.
2464  */
2465 static bool is_input_name_malloced = false;
2466 
2467 static int switch_data_file(void)
2468 {
2469 	char *pwd, *options[32], *abs_path[32], *tmp;
2470 	DIR *pwd_dir;
2471 	int nr_options = 0, choice = -1, ret = -1;
2472 	struct dirent *dent;
2473 
2474 	pwd = getenv("PWD");
2475 	if (!pwd)
2476 		return ret;
2477 
2478 	pwd_dir = opendir(pwd);
2479 	if (!pwd_dir)
2480 		return ret;
2481 
2482 	memset(options, 0, sizeof(options));
2483 	memset(abs_path, 0, sizeof(abs_path));
2484 
2485 	while ((dent = readdir(pwd_dir))) {
2486 		char path[PATH_MAX];
2487 		u64 magic;
2488 		char *name = dent->d_name;
2489 		FILE *file;
2490 
2491 		if (!(dent->d_type == DT_REG))
2492 			continue;
2493 
2494 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2495 
2496 		file = fopen(path, "r");
2497 		if (!file)
2498 			continue;
2499 
2500 		if (fread(&magic, 1, 8, file) < 8)
2501 			goto close_file_and_continue;
2502 
2503 		if (is_perf_magic(magic)) {
2504 			options[nr_options] = strdup(name);
2505 			if (!options[nr_options])
2506 				goto close_file_and_continue;
2507 
2508 			abs_path[nr_options] = strdup(path);
2509 			if (!abs_path[nr_options]) {
2510 				zfree(&options[nr_options]);
2511 				ui__warning("Can't search all data files due to memory shortage.\n");
2512 				fclose(file);
2513 				break;
2514 			}
2515 
2516 			nr_options++;
2517 		}
2518 
2519 close_file_and_continue:
2520 		fclose(file);
2521 		if (nr_options >= 32) {
2522 			ui__warning("Too many perf data files in PWD!\n"
2523 				    "Only the first 32 files will be listed.\n");
2524 			break;
2525 		}
2526 	}
2527 	closedir(pwd_dir);
2528 
2529 	if (nr_options) {
2530 		choice = ui__popup_menu(nr_options, options);
2531 		if (choice < nr_options && choice >= 0) {
2532 			tmp = strdup(abs_path[choice]);
2533 			if (tmp) {
2534 				if (is_input_name_malloced)
2535 					free((void *)input_name);
2536 				input_name = tmp;
2537 				is_input_name_malloced = true;
2538 				ret = 0;
2539 			} else
2540 				ui__warning("Data switch failed due to memory shortage!\n");
2541 		}
2542 	}
2543 
2544 	free_popup_options(options, nr_options);
2545 	free_popup_options(abs_path, nr_options);
2546 	return ret;
2547 }
2548 
2549 struct popup_action {
2550 	struct thread 		*thread;
2551 	struct map_symbol 	ms;
2552 	int			socket;
2553 
2554 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2555 };
2556 
2557 static int
2558 do_annotate(struct hist_browser *browser, struct popup_action *act)
2559 {
2560 	struct perf_evsel *evsel;
2561 	struct annotation *notes;
2562 	struct hist_entry *he;
2563 	int err;
2564 
2565 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
2566 		return 0;
2567 
2568 	notes = symbol__annotation(act->ms.sym);
2569 	if (!notes->src)
2570 		return 0;
2571 
2572 	evsel = hists_to_evsel(browser->hists);
2573 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2574 	he = hist_browser__selected_entry(browser);
2575 	/*
2576 	 * offer option to annotate the other branch source or target
2577 	 * (if they exists) when returning from annotate
2578 	 */
2579 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2580 		return 1;
2581 
2582 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2583 	if (err)
2584 		ui_browser__handle_resize(&browser->b);
2585 	return 0;
2586 }
2587 
2588 static int
2589 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2590 		 struct popup_action *act, char **optstr,
2591 		 struct map *map, struct symbol *sym)
2592 {
2593 	if (sym == NULL || map->dso->annotate_warned)
2594 		return 0;
2595 
2596 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2597 		return 0;
2598 
2599 	act->ms.map = map;
2600 	act->ms.sym = sym;
2601 	act->fn = do_annotate;
2602 	return 1;
2603 }
2604 
2605 static int
2606 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2607 {
2608 	struct thread *thread = act->thread;
2609 
2610 	if ((!hists__has(browser->hists, thread) &&
2611 	     !hists__has(browser->hists, comm)) || thread == NULL)
2612 		return 0;
2613 
2614 	if (browser->hists->thread_filter) {
2615 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2616 		perf_hpp__set_elide(HISTC_THREAD, false);
2617 		thread__zput(browser->hists->thread_filter);
2618 		ui_helpline__pop();
2619 	} else {
2620 		if (hists__has(browser->hists, thread)) {
2621 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2622 					   thread->comm_set ? thread__comm_str(thread) : "",
2623 					   thread->tid);
2624 		} else {
2625 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2626 					   thread->comm_set ? thread__comm_str(thread) : "");
2627 		}
2628 
2629 		browser->hists->thread_filter = thread__get(thread);
2630 		perf_hpp__set_elide(HISTC_THREAD, false);
2631 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2632 	}
2633 
2634 	hists__filter_by_thread(browser->hists);
2635 	hist_browser__reset(browser);
2636 	return 0;
2637 }
2638 
2639 static int
2640 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2641 	       char **optstr, struct thread *thread)
2642 {
2643 	int ret;
2644 
2645 	if ((!hists__has(browser->hists, thread) &&
2646 	     !hists__has(browser->hists, comm)) || thread == NULL)
2647 		return 0;
2648 
2649 	if (hists__has(browser->hists, thread)) {
2650 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2651 			       browser->hists->thread_filter ? "out of" : "into",
2652 			       thread->comm_set ? thread__comm_str(thread) : "",
2653 			       thread->tid);
2654 	} else {
2655 		ret = asprintf(optstr, "Zoom %s %s thread",
2656 			       browser->hists->thread_filter ? "out of" : "into",
2657 			       thread->comm_set ? thread__comm_str(thread) : "");
2658 	}
2659 	if (ret < 0)
2660 		return 0;
2661 
2662 	act->thread = thread;
2663 	act->fn = do_zoom_thread;
2664 	return 1;
2665 }
2666 
2667 static int
2668 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2669 {
2670 	struct map *map = act->ms.map;
2671 
2672 	if (!hists__has(browser->hists, dso) || map == NULL)
2673 		return 0;
2674 
2675 	if (browser->hists->dso_filter) {
2676 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2677 		perf_hpp__set_elide(HISTC_DSO, false);
2678 		browser->hists->dso_filter = NULL;
2679 		ui_helpline__pop();
2680 	} else {
2681 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2682 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2683 		browser->hists->dso_filter = map->dso;
2684 		perf_hpp__set_elide(HISTC_DSO, true);
2685 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2686 	}
2687 
2688 	hists__filter_by_dso(browser->hists);
2689 	hist_browser__reset(browser);
2690 	return 0;
2691 }
2692 
2693 static int
2694 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2695 	    char **optstr, struct map *map)
2696 {
2697 	if (!hists__has(browser->hists, dso) || map == NULL)
2698 		return 0;
2699 
2700 	if (asprintf(optstr, "Zoom %s %s DSO",
2701 		     browser->hists->dso_filter ? "out of" : "into",
2702 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2703 		return 0;
2704 
2705 	act->ms.map = map;
2706 	act->fn = do_zoom_dso;
2707 	return 1;
2708 }
2709 
2710 static int
2711 do_browse_map(struct hist_browser *browser __maybe_unused,
2712 	      struct popup_action *act)
2713 {
2714 	map__browse(act->ms.map);
2715 	return 0;
2716 }
2717 
2718 static int
2719 add_map_opt(struct hist_browser *browser,
2720 	    struct popup_action *act, char **optstr, struct map *map)
2721 {
2722 	if (!hists__has(browser->hists, dso) || map == NULL)
2723 		return 0;
2724 
2725 	if (asprintf(optstr, "Browse map details") < 0)
2726 		return 0;
2727 
2728 	act->ms.map = map;
2729 	act->fn = do_browse_map;
2730 	return 1;
2731 }
2732 
2733 static int
2734 do_run_script(struct hist_browser *browser __maybe_unused,
2735 	      struct popup_action *act)
2736 {
2737 	char script_opt[64];
2738 	memset(script_opt, 0, sizeof(script_opt));
2739 
2740 	if (act->thread) {
2741 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2742 			  thread__comm_str(act->thread));
2743 	} else if (act->ms.sym) {
2744 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2745 			  act->ms.sym->name);
2746 	}
2747 
2748 	script_browse(script_opt);
2749 	return 0;
2750 }
2751 
2752 static int
2753 add_script_opt(struct hist_browser *browser __maybe_unused,
2754 	       struct popup_action *act, char **optstr,
2755 	       struct thread *thread, struct symbol *sym)
2756 {
2757 	if (thread) {
2758 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2759 			     thread__comm_str(thread)) < 0)
2760 			return 0;
2761 	} else if (sym) {
2762 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2763 			     sym->name) < 0)
2764 			return 0;
2765 	} else {
2766 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2767 			return 0;
2768 	}
2769 
2770 	act->thread = thread;
2771 	act->ms.sym = sym;
2772 	act->fn = do_run_script;
2773 	return 1;
2774 }
2775 
2776 static int
2777 do_switch_data(struct hist_browser *browser __maybe_unused,
2778 	       struct popup_action *act __maybe_unused)
2779 {
2780 	if (switch_data_file()) {
2781 		ui__warning("Won't switch the data files due to\n"
2782 			    "no valid data file get selected!\n");
2783 		return 0;
2784 	}
2785 
2786 	return K_SWITCH_INPUT_DATA;
2787 }
2788 
2789 static int
2790 add_switch_opt(struct hist_browser *browser,
2791 	       struct popup_action *act, char **optstr)
2792 {
2793 	if (!is_report_browser(browser->hbt))
2794 		return 0;
2795 
2796 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2797 		return 0;
2798 
2799 	act->fn = do_switch_data;
2800 	return 1;
2801 }
2802 
2803 static int
2804 do_exit_browser(struct hist_browser *browser __maybe_unused,
2805 		struct popup_action *act __maybe_unused)
2806 {
2807 	return 0;
2808 }
2809 
2810 static int
2811 add_exit_opt(struct hist_browser *browser __maybe_unused,
2812 	     struct popup_action *act, char **optstr)
2813 {
2814 	if (asprintf(optstr, "Exit") < 0)
2815 		return 0;
2816 
2817 	act->fn = do_exit_browser;
2818 	return 1;
2819 }
2820 
2821 static int
2822 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2823 {
2824 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2825 		return 0;
2826 
2827 	if (browser->hists->socket_filter > -1) {
2828 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2829 		browser->hists->socket_filter = -1;
2830 		perf_hpp__set_elide(HISTC_SOCKET, false);
2831 	} else {
2832 		browser->hists->socket_filter = act->socket;
2833 		perf_hpp__set_elide(HISTC_SOCKET, true);
2834 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2835 	}
2836 
2837 	hists__filter_by_socket(browser->hists);
2838 	hist_browser__reset(browser);
2839 	return 0;
2840 }
2841 
2842 static int
2843 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2844 	       char **optstr, int socket_id)
2845 {
2846 	if (!hists__has(browser->hists, socket) || socket_id < 0)
2847 		return 0;
2848 
2849 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2850 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2851 		     socket_id) < 0)
2852 		return 0;
2853 
2854 	act->socket = socket_id;
2855 	act->fn = do_zoom_socket;
2856 	return 1;
2857 }
2858 
2859 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2860 {
2861 	u64 nr_entries = 0;
2862 	struct rb_node *nd = rb_first(&hb->hists->entries);
2863 
2864 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2865 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2866 		return;
2867 	}
2868 
2869 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2870 		nr_entries++;
2871 		nd = rb_hierarchy_next(nd);
2872 	}
2873 
2874 	hb->nr_non_filtered_entries = nr_entries;
2875 	hb->nr_hierarchy_entries = nr_entries;
2876 }
2877 
2878 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2879 					       double percent)
2880 {
2881 	struct hist_entry *he;
2882 	struct rb_node *nd = rb_first(&hb->hists->entries);
2883 	u64 total = hists__total_period(hb->hists);
2884 	u64 min_callchain_hits = total * (percent / 100);
2885 
2886 	hb->min_pcnt = callchain_param.min_percent = percent;
2887 
2888 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2889 		he = rb_entry(nd, struct hist_entry, rb_node);
2890 
2891 		if (he->has_no_entry) {
2892 			he->has_no_entry = false;
2893 			he->nr_rows = 0;
2894 		}
2895 
2896 		if (!he->leaf || !symbol_conf.use_callchain)
2897 			goto next;
2898 
2899 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2900 			total = he->stat.period;
2901 
2902 			if (symbol_conf.cumulate_callchain)
2903 				total = he->stat_acc->period;
2904 
2905 			min_callchain_hits = total * (percent / 100);
2906 		}
2907 
2908 		callchain_param.sort(&he->sorted_chain, he->callchain,
2909 				     min_callchain_hits, &callchain_param);
2910 
2911 next:
2912 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2913 
2914 		/* force to re-evaluate folding state of callchains */
2915 		he->init_have_children = false;
2916 		hist_entry__set_folding(he, hb, false);
2917 	}
2918 }
2919 
2920 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2921 				    const char *helpline,
2922 				    bool left_exits,
2923 				    struct hist_browser_timer *hbt,
2924 				    float min_pcnt,
2925 				    struct perf_env *env)
2926 {
2927 	struct hists *hists = evsel__hists(evsel);
2928 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2929 	struct branch_info *bi;
2930 #define MAX_OPTIONS  16
2931 	char *options[MAX_OPTIONS];
2932 	struct popup_action actions[MAX_OPTIONS];
2933 	int nr_options = 0;
2934 	int key = -1;
2935 	char buf[64];
2936 	int delay_secs = hbt ? hbt->refresh : 0;
2937 
2938 #define HIST_BROWSER_HELP_COMMON					\
2939 	"h/?/F1        Show this window\n"				\
2940 	"UP/DOWN/PGUP\n"						\
2941 	"PGDN/SPACE    Navigate\n"					\
2942 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2943 	"For multiple event sessions:\n\n"				\
2944 	"TAB/UNTAB     Switch events\n\n"				\
2945 	"For symbolic views (--sort has sym):\n\n"			\
2946 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2947 	"ESC           Zoom out\n"					\
2948 	"a             Annotate current symbol\n"			\
2949 	"C             Collapse all callchains\n"			\
2950 	"d             Zoom into current DSO\n"				\
2951 	"E             Expand all callchains\n"				\
2952 	"F             Toggle percentage of filtered entries\n"		\
2953 	"H             Display column headers\n"			\
2954 	"L             Change percent limit\n"				\
2955 	"m             Display context menu\n"				\
2956 	"S             Zoom into current Processor Socket\n"		\
2957 
2958 	/* help messages are sorted by lexical order of the hotkey */
2959 	const char report_help[] = HIST_BROWSER_HELP_COMMON
2960 	"i             Show header information\n"
2961 	"P             Print histograms to perf.hist.N\n"
2962 	"r             Run available scripts\n"
2963 	"s             Switch to another data file in PWD\n"
2964 	"t             Zoom into current Thread\n"
2965 	"V             Verbose (DSO names in callchains, etc)\n"
2966 	"/             Filter symbol by name";
2967 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2968 	"P             Print histograms to perf.hist.N\n"
2969 	"t             Zoom into current Thread\n"
2970 	"V             Verbose (DSO names in callchains, etc)\n"
2971 	"z             Toggle zeroing of samples\n"
2972 	"f             Enable/Disable events\n"
2973 	"/             Filter symbol by name";
2974 
2975 	if (browser == NULL)
2976 		return -1;
2977 
2978 	/* reset abort key so that it can get Ctrl-C as a key */
2979 	SLang_reset_tty();
2980 	SLang_init_tty(0, 0, 0);
2981 
2982 	if (min_pcnt)
2983 		browser->min_pcnt = min_pcnt;
2984 	hist_browser__update_nr_entries(browser);
2985 
2986 	browser->pstack = pstack__new(3);
2987 	if (browser->pstack == NULL)
2988 		goto out;
2989 
2990 	ui_helpline__push(helpline);
2991 
2992 	memset(options, 0, sizeof(options));
2993 	memset(actions, 0, sizeof(actions));
2994 
2995 	if (symbol_conf.col_width_list_str)
2996 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2997 
2998 	while (1) {
2999 		struct thread *thread = NULL;
3000 		struct map *map = NULL;
3001 		int choice = 0;
3002 		int socked_id = -1;
3003 
3004 		nr_options = 0;
3005 
3006 		key = hist_browser__run(browser, helpline);
3007 
3008 		if (browser->he_selection != NULL) {
3009 			thread = hist_browser__selected_thread(browser);
3010 			map = browser->selection->map;
3011 			socked_id = browser->he_selection->socket;
3012 		}
3013 		switch (key) {
3014 		case K_TAB:
3015 		case K_UNTAB:
3016 			if (nr_events == 1)
3017 				continue;
3018 			/*
3019 			 * Exit the browser, let hists__browser_tree
3020 			 * go to the next or previous
3021 			 */
3022 			goto out_free_stack;
3023 		case 'a':
3024 			if (!hists__has(hists, sym)) {
3025 				ui_browser__warning(&browser->b, delay_secs * 2,
3026 			"Annotation is only available for symbolic views, "
3027 			"include \"sym*\" in --sort to use it.");
3028 				continue;
3029 			}
3030 
3031 			if (browser->selection == NULL ||
3032 			    browser->selection->sym == NULL ||
3033 			    browser->selection->map->dso->annotate_warned)
3034 				continue;
3035 
3036 			actions->ms.map = browser->selection->map;
3037 			actions->ms.sym = browser->selection->sym;
3038 			do_annotate(browser, actions);
3039 			continue;
3040 		case 'P':
3041 			hist_browser__dump(browser);
3042 			continue;
3043 		case 'd':
3044 			actions->ms.map = map;
3045 			do_zoom_dso(browser, actions);
3046 			continue;
3047 		case 'V':
3048 			verbose = (verbose + 1) % 4;
3049 			browser->show_dso = verbose > 0;
3050 			ui_helpline__fpush("Verbosity level set to %d\n",
3051 					   verbose);
3052 			continue;
3053 		case 't':
3054 			actions->thread = thread;
3055 			do_zoom_thread(browser, actions);
3056 			continue;
3057 		case 'S':
3058 			actions->socket = socked_id;
3059 			do_zoom_socket(browser, actions);
3060 			continue;
3061 		case '/':
3062 			if (ui_browser__input_window("Symbol to show",
3063 					"Please enter the name of symbol you want to see.\n"
3064 					"To remove the filter later, press / + ENTER.",
3065 					buf, "ENTER: OK, ESC: Cancel",
3066 					delay_secs * 2) == K_ENTER) {
3067 				hists->symbol_filter_str = *buf ? buf : NULL;
3068 				hists__filter_by_symbol(hists);
3069 				hist_browser__reset(browser);
3070 			}
3071 			continue;
3072 		case 'r':
3073 			if (is_report_browser(hbt)) {
3074 				actions->thread = NULL;
3075 				actions->ms.sym = NULL;
3076 				do_run_script(browser, actions);
3077 			}
3078 			continue;
3079 		case 's':
3080 			if (is_report_browser(hbt)) {
3081 				key = do_switch_data(browser, actions);
3082 				if (key == K_SWITCH_INPUT_DATA)
3083 					goto out_free_stack;
3084 			}
3085 			continue;
3086 		case 'i':
3087 			/* env->arch is NULL for live-mode (i.e. perf top) */
3088 			if (env->arch)
3089 				tui__header_window(env);
3090 			continue;
3091 		case 'F':
3092 			symbol_conf.filter_relative ^= 1;
3093 			continue;
3094 		case 'z':
3095 			if (!is_report_browser(hbt)) {
3096 				struct perf_top *top = hbt->arg;
3097 
3098 				top->zero = !top->zero;
3099 			}
3100 			continue;
3101 		case 'L':
3102 			if (ui_browser__input_window("Percent Limit",
3103 					"Please enter the value you want to hide entries under that percent.",
3104 					buf, "ENTER: OK, ESC: Cancel",
3105 					delay_secs * 2) == K_ENTER) {
3106 				char *end;
3107 				double new_percent = strtod(buf, &end);
3108 
3109 				if (new_percent < 0 || new_percent > 100) {
3110 					ui_browser__warning(&browser->b, delay_secs * 2,
3111 						"Invalid percent: %.2f", new_percent);
3112 					continue;
3113 				}
3114 
3115 				hist_browser__update_percent_limit(browser, new_percent);
3116 				hist_browser__reset(browser);
3117 			}
3118 			continue;
3119 		case K_F1:
3120 		case 'h':
3121 		case '?':
3122 			ui_browser__help_window(&browser->b,
3123 				is_report_browser(hbt) ? report_help : top_help);
3124 			continue;
3125 		case K_ENTER:
3126 		case K_RIGHT:
3127 		case 'm':
3128 			/* menu */
3129 			break;
3130 		case K_ESC:
3131 		case K_LEFT: {
3132 			const void *top;
3133 
3134 			if (pstack__empty(browser->pstack)) {
3135 				/*
3136 				 * Go back to the perf_evsel_menu__run or other user
3137 				 */
3138 				if (left_exits)
3139 					goto out_free_stack;
3140 
3141 				if (key == K_ESC &&
3142 				    ui_browser__dialog_yesno(&browser->b,
3143 							     "Do you really want to exit?"))
3144 					goto out_free_stack;
3145 
3146 				continue;
3147 			}
3148 			top = pstack__peek(browser->pstack);
3149 			if (top == &browser->hists->dso_filter) {
3150 				/*
3151 				 * No need to set actions->dso here since
3152 				 * it's just to remove the current filter.
3153 				 * Ditto for thread below.
3154 				 */
3155 				do_zoom_dso(browser, actions);
3156 			} else if (top == &browser->hists->thread_filter) {
3157 				do_zoom_thread(browser, actions);
3158 			} else if (top == &browser->hists->socket_filter) {
3159 				do_zoom_socket(browser, actions);
3160 			}
3161 			continue;
3162 		}
3163 		case 'q':
3164 		case CTRL('c'):
3165 			goto out_free_stack;
3166 		case 'f':
3167 			if (!is_report_browser(hbt)) {
3168 				struct perf_top *top = hbt->arg;
3169 
3170 				perf_evlist__toggle_enable(top->evlist);
3171 				/*
3172 				 * No need to refresh, resort/decay histogram
3173 				 * entries if we are not collecting samples:
3174 				 */
3175 				if (top->evlist->enabled) {
3176 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3177 					hbt->refresh = delay_secs;
3178 				} else {
3179 					helpline = "Press 'f' again to re-enable the events";
3180 					hbt->refresh = 0;
3181 				}
3182 				continue;
3183 			}
3184 			/* Fall thru */
3185 		default:
3186 			helpline = "Press '?' for help on key bindings";
3187 			continue;
3188 		}
3189 
3190 		if (!hists__has(hists, sym) || browser->selection == NULL)
3191 			goto skip_annotation;
3192 
3193 		if (sort__mode == SORT_MODE__BRANCH) {
3194 			bi = browser->he_selection->branch_info;
3195 
3196 			if (bi == NULL)
3197 				goto skip_annotation;
3198 
3199 			nr_options += add_annotate_opt(browser,
3200 						       &actions[nr_options],
3201 						       &options[nr_options],
3202 						       bi->from.map,
3203 						       bi->from.sym);
3204 			if (bi->to.sym != bi->from.sym)
3205 				nr_options += add_annotate_opt(browser,
3206 							&actions[nr_options],
3207 							&options[nr_options],
3208 							bi->to.map,
3209 							bi->to.sym);
3210 		} else {
3211 			nr_options += add_annotate_opt(browser,
3212 						       &actions[nr_options],
3213 						       &options[nr_options],
3214 						       browser->selection->map,
3215 						       browser->selection->sym);
3216 		}
3217 skip_annotation:
3218 		nr_options += add_thread_opt(browser, &actions[nr_options],
3219 					     &options[nr_options], thread);
3220 		nr_options += add_dso_opt(browser, &actions[nr_options],
3221 					  &options[nr_options], map);
3222 		nr_options += add_map_opt(browser, &actions[nr_options],
3223 					  &options[nr_options],
3224 					  browser->selection ?
3225 						browser->selection->map : NULL);
3226 		nr_options += add_socket_opt(browser, &actions[nr_options],
3227 					     &options[nr_options],
3228 					     socked_id);
3229 		/* perf script support */
3230 		if (!is_report_browser(hbt))
3231 			goto skip_scripting;
3232 
3233 		if (browser->he_selection) {
3234 			if (hists__has(hists, thread) && thread) {
3235 				nr_options += add_script_opt(browser,
3236 							     &actions[nr_options],
3237 							     &options[nr_options],
3238 							     thread, NULL);
3239 			}
3240 			/*
3241 			 * Note that browser->selection != NULL
3242 			 * when browser->he_selection is not NULL,
3243 			 * so we don't need to check browser->selection
3244 			 * before fetching browser->selection->sym like what
3245 			 * we do before fetching browser->selection->map.
3246 			 *
3247 			 * See hist_browser__show_entry.
3248 			 */
3249 			if (hists__has(hists, sym) && browser->selection->sym) {
3250 				nr_options += add_script_opt(browser,
3251 							     &actions[nr_options],
3252 							     &options[nr_options],
3253 							     NULL, browser->selection->sym);
3254 			}
3255 		}
3256 		nr_options += add_script_opt(browser, &actions[nr_options],
3257 					     &options[nr_options], NULL, NULL);
3258 		nr_options += add_switch_opt(browser, &actions[nr_options],
3259 					     &options[nr_options]);
3260 skip_scripting:
3261 		nr_options += add_exit_opt(browser, &actions[nr_options],
3262 					   &options[nr_options]);
3263 
3264 		do {
3265 			struct popup_action *act;
3266 
3267 			choice = ui__popup_menu(nr_options, options);
3268 			if (choice == -1 || choice >= nr_options)
3269 				break;
3270 
3271 			act = &actions[choice];
3272 			key = act->fn(browser, act);
3273 		} while (key == 1);
3274 
3275 		if (key == K_SWITCH_INPUT_DATA)
3276 			break;
3277 	}
3278 out_free_stack:
3279 	pstack__delete(browser->pstack);
3280 out:
3281 	hist_browser__delete(browser);
3282 	free_popup_options(options, MAX_OPTIONS);
3283 	return key;
3284 }
3285 
3286 struct perf_evsel_menu {
3287 	struct ui_browser b;
3288 	struct perf_evsel *selection;
3289 	bool lost_events, lost_events_warned;
3290 	float min_pcnt;
3291 	struct perf_env *env;
3292 };
3293 
3294 static void perf_evsel_menu__write(struct ui_browser *browser,
3295 				   void *entry, int row)
3296 {
3297 	struct perf_evsel_menu *menu = container_of(browser,
3298 						    struct perf_evsel_menu, b);
3299 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3300 	struct hists *hists = evsel__hists(evsel);
3301 	bool current_entry = ui_browser__is_current_entry(browser, row);
3302 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3303 	const char *ev_name = perf_evsel__name(evsel);
3304 	char bf[256], unit;
3305 	const char *warn = " ";
3306 	size_t printed;
3307 
3308 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3309 						       HE_COLORSET_NORMAL);
3310 
3311 	if (perf_evsel__is_group_event(evsel)) {
3312 		struct perf_evsel *pos;
3313 
3314 		ev_name = perf_evsel__group_name(evsel);
3315 
3316 		for_each_group_member(pos, evsel) {
3317 			struct hists *pos_hists = evsel__hists(pos);
3318 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3319 		}
3320 	}
3321 
3322 	nr_events = convert_unit(nr_events, &unit);
3323 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3324 			   unit, unit == ' ' ? "" : " ", ev_name);
3325 	ui_browser__printf(browser, "%s", bf);
3326 
3327 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3328 	if (nr_events != 0) {
3329 		menu->lost_events = true;
3330 		if (!current_entry)
3331 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3332 		nr_events = convert_unit(nr_events, &unit);
3333 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3334 				     nr_events, unit, unit == ' ' ? "" : " ");
3335 		warn = bf;
3336 	}
3337 
3338 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3339 
3340 	if (current_entry)
3341 		menu->selection = evsel;
3342 }
3343 
3344 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3345 				int nr_events, const char *help,
3346 				struct hist_browser_timer *hbt)
3347 {
3348 	struct perf_evlist *evlist = menu->b.priv;
3349 	struct perf_evsel *pos;
3350 	const char *title = "Available samples";
3351 	int delay_secs = hbt ? hbt->refresh : 0;
3352 	int key;
3353 
3354 	if (ui_browser__show(&menu->b, title,
3355 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3356 		return -1;
3357 
3358 	while (1) {
3359 		key = ui_browser__run(&menu->b, delay_secs);
3360 
3361 		switch (key) {
3362 		case K_TIMER:
3363 			hbt->timer(hbt->arg);
3364 
3365 			if (!menu->lost_events_warned && menu->lost_events) {
3366 				ui_browser__warn_lost_events(&menu->b);
3367 				menu->lost_events_warned = true;
3368 			}
3369 			continue;
3370 		case K_RIGHT:
3371 		case K_ENTER:
3372 			if (!menu->selection)
3373 				continue;
3374 			pos = menu->selection;
3375 browse_hists:
3376 			perf_evlist__set_selected(evlist, pos);
3377 			/*
3378 			 * Give the calling tool a chance to populate the non
3379 			 * default evsel resorted hists tree.
3380 			 */
3381 			if (hbt)
3382 				hbt->timer(hbt->arg);
3383 			key = perf_evsel__hists_browse(pos, nr_events, help,
3384 						       true, hbt,
3385 						       menu->min_pcnt,
3386 						       menu->env);
3387 			ui_browser__show_title(&menu->b, title);
3388 			switch (key) {
3389 			case K_TAB:
3390 				if (pos->node.next == &evlist->entries)
3391 					pos = perf_evlist__first(evlist);
3392 				else
3393 					pos = perf_evsel__next(pos);
3394 				goto browse_hists;
3395 			case K_UNTAB:
3396 				if (pos->node.prev == &evlist->entries)
3397 					pos = perf_evlist__last(evlist);
3398 				else
3399 					pos = perf_evsel__prev(pos);
3400 				goto browse_hists;
3401 			case K_SWITCH_INPUT_DATA:
3402 			case 'q':
3403 			case CTRL('c'):
3404 				goto out;
3405 			case K_ESC:
3406 			default:
3407 				continue;
3408 			}
3409 		case K_LEFT:
3410 			continue;
3411 		case K_ESC:
3412 			if (!ui_browser__dialog_yesno(&menu->b,
3413 					       "Do you really want to exit?"))
3414 				continue;
3415 			/* Fall thru */
3416 		case 'q':
3417 		case CTRL('c'):
3418 			goto out;
3419 		default:
3420 			continue;
3421 		}
3422 	}
3423 
3424 out:
3425 	ui_browser__hide(&menu->b);
3426 	return key;
3427 }
3428 
3429 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3430 				 void *entry)
3431 {
3432 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3433 
3434 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3435 		return true;
3436 
3437 	return false;
3438 }
3439 
3440 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3441 					   int nr_entries, const char *help,
3442 					   struct hist_browser_timer *hbt,
3443 					   float min_pcnt,
3444 					   struct perf_env *env)
3445 {
3446 	struct perf_evsel *pos;
3447 	struct perf_evsel_menu menu = {
3448 		.b = {
3449 			.entries    = &evlist->entries,
3450 			.refresh    = ui_browser__list_head_refresh,
3451 			.seek	    = ui_browser__list_head_seek,
3452 			.write	    = perf_evsel_menu__write,
3453 			.filter	    = filter_group_entries,
3454 			.nr_entries = nr_entries,
3455 			.priv	    = evlist,
3456 		},
3457 		.min_pcnt = min_pcnt,
3458 		.env = env,
3459 	};
3460 
3461 	ui_helpline__push("Press ESC to exit");
3462 
3463 	evlist__for_each_entry(evlist, pos) {
3464 		const char *ev_name = perf_evsel__name(pos);
3465 		size_t line_len = strlen(ev_name) + 7;
3466 
3467 		if (menu.b.width < line_len)
3468 			menu.b.width = line_len;
3469 	}
3470 
3471 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3472 }
3473 
3474 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3475 				  struct hist_browser_timer *hbt,
3476 				  float min_pcnt,
3477 				  struct perf_env *env)
3478 {
3479 	int nr_entries = evlist->nr_entries;
3480 
3481 single_entry:
3482 	if (nr_entries == 1) {
3483 		struct perf_evsel *first = perf_evlist__first(evlist);
3484 
3485 		return perf_evsel__hists_browse(first, nr_entries, help,
3486 						false, hbt, min_pcnt,
3487 						env);
3488 	}
3489 
3490 	if (symbol_conf.event_group) {
3491 		struct perf_evsel *pos;
3492 
3493 		nr_entries = 0;
3494 		evlist__for_each_entry(evlist, pos) {
3495 			if (perf_evsel__is_group_leader(pos))
3496 				nr_entries++;
3497 		}
3498 
3499 		if (nr_entries == 1)
3500 			goto single_entry;
3501 	}
3502 
3503 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3504 					       hbt, min_pcnt, env);
3505 }
3506