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