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