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