xref: /linux/tools/perf/ui/browsers/hists.c (revision d24e3c98ac11b941669885cc09d88b3b970e9d66)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
276b31a29SArnaldo Carvalho de Melo #include <dirent.h>
3a43783aeSArnaldo Carvalho de Melo #include <errno.h>
4fd20e811SArnaldo Carvalho de Melo #include <inttypes.h>
5aca7a94dSNamhyung Kim #include <stdio.h>
6aca7a94dSNamhyung Kim #include <stdlib.h>
7aca7a94dSNamhyung Kim #include <string.h>
8aca7a94dSNamhyung Kim #include <linux/rbtree.h>
9b0742e90SArnaldo Carvalho de Melo #include <sys/ttydefaults.h>
10aca7a94dSNamhyung Kim 
11aca7a94dSNamhyung Kim #include "../../util/evsel.h"
12aca7a94dSNamhyung Kim #include "../../util/evlist.h"
13aca7a94dSNamhyung Kim #include "../../util/hist.h"
14aca7a94dSNamhyung Kim #include "../../util/pstack.h"
15aca7a94dSNamhyung Kim #include "../../util/sort.h"
16aca7a94dSNamhyung Kim #include "../../util/util.h"
1742337a22SNamhyung Kim #include "../../util/top.h"
18e7ff8920SArnaldo Carvalho de Melo #include "../../util/thread.h"
1968d80758SNamhyung Kim #include "../../arch/common.h"
20aca7a94dSNamhyung Kim 
21f758990fSJiri Olsa #include "../browsers/hists.h"
22aca7a94dSNamhyung Kim #include "../helpline.h"
23aca7a94dSNamhyung Kim #include "../util.h"
24aca7a94dSNamhyung Kim #include "../ui.h"
25aca7a94dSNamhyung Kim #include "map.h"
26d755330cSJiri Olsa #include "annotate.h"
27632a5cabSArnaldo Carvalho de Melo #include "srcline.h"
28a067558eSArnaldo Carvalho de Melo #include "string2.h"
2958db1d6eSArnaldo Carvalho de Melo #include "units.h"
30aca7a94dSNamhyung Kim 
313d689ed6SArnaldo Carvalho de Melo #include "sane_ctype.h"
323d689ed6SArnaldo Carvalho de Melo 
33f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
34f5951d56SNamhyung Kim 
35f016d24aSArnaldo Carvalho de Melo static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
36112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
37aca7a94dSNamhyung Kim 
38c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
39c3b78952SNamhyung Kim 					     float min_pcnt);
40c3b78952SNamhyung Kim 
41268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
42268397cbSNamhyung Kim {
435a1a99cdSJiri Olsa 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
44268397cbSNamhyung Kim }
45268397cbSNamhyung Kim 
464fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
474fabf3d1SHe Kuang {
484fabf3d1SHe Kuang 	struct rb_node *nd;
494fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
504fabf3d1SHe Kuang 	int unfolded_rows = 0;
514fabf3d1SHe Kuang 
524fabf3d1SHe Kuang 	for (nd = rb_first(&hists->entries);
534fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
54f5b763feSNamhyung Kim 	     nd = rb_hierarchy_next(nd)) {
554fabf3d1SHe Kuang 		struct hist_entry *he =
564fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
574fabf3d1SHe Kuang 
58f5b763feSNamhyung Kim 		if (he->leaf && he->unfolded)
594fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
604fabf3d1SHe Kuang 	}
614fabf3d1SHe Kuang 	return unfolded_rows;
624fabf3d1SHe Kuang }
634fabf3d1SHe Kuang 
64ef9ff601SArnaldo Carvalho de Melo static void hist_browser__set_title_space(struct hist_browser *hb)
65ef9ff601SArnaldo Carvalho de Melo {
66ef9ff601SArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
67ef9ff601SArnaldo Carvalho de Melo 	struct hists *hists = hb->hists;
68ef9ff601SArnaldo Carvalho de Melo 	struct perf_hpp_list *hpp_list = hists->hpp_list;
69ef9ff601SArnaldo Carvalho de Melo 
70ef9ff601SArnaldo Carvalho de Melo 	browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
71ef9ff601SArnaldo Carvalho de Melo }
72ef9ff601SArnaldo Carvalho de Melo 
73c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
74c3b78952SNamhyung Kim {
75c3b78952SNamhyung Kim 	u32 nr_entries;
76c3b78952SNamhyung Kim 
77f5b763feSNamhyung Kim 	if (symbol_conf.report_hierarchy)
78f5b763feSNamhyung Kim 		nr_entries = hb->nr_hierarchy_entries;
79f5b763feSNamhyung Kim 	else if (hist_browser__has_filter(hb))
80c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
81c3b78952SNamhyung Kim 	else
82c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
83c3b78952SNamhyung Kim 
844fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
85c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
86c3b78952SNamhyung Kim }
87c3b78952SNamhyung Kim 
88025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
89025bf7eaSArnaldo Carvalho de Melo {
90025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
91f8e6710dSJiri Olsa 	struct hists *hists = hb->hists;
92f8e6710dSJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
93ef9ff601SArnaldo Carvalho de Melo 	u16 index_row;
94025bf7eaSArnaldo Carvalho de Melo 
95ef9ff601SArnaldo Carvalho de Melo 	if (!hb->show_headers) {
96ef9ff601SArnaldo Carvalho de Melo 		browser->rows += browser->extra_title_lines;
97ef9ff601SArnaldo Carvalho de Melo 		browser->extra_title_lines = 0;
98ef9ff601SArnaldo Carvalho de Melo 		return;
99ef9ff601SArnaldo Carvalho de Melo 	}
100ef9ff601SArnaldo Carvalho de Melo 
101ef9ff601SArnaldo Carvalho de Melo 	browser->extra_title_lines = hpp_list->nr_header_lines;
102ef9ff601SArnaldo Carvalho de Melo 	browser->rows -= browser->extra_title_lines;
103025bf7eaSArnaldo Carvalho de Melo 	/*
104025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
105025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
106025bf7eaSArnaldo Carvalho de Melo 	 */
107025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
108025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
109025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
110025bf7eaSArnaldo Carvalho de Melo }
111025bf7eaSArnaldo Carvalho de Melo 
112357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
113aca7a94dSNamhyung Kim {
114357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
115357cfff1SArnaldo Carvalho de Melo 
116aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
117357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
118357cfff1SArnaldo Carvalho de Melo 	/*
119357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
120357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
121357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
122357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
123357cfff1SArnaldo Carvalho de Melo  	 */
124357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
125ca3ff33bSArnaldo Carvalho de Melo }
126ca3ff33bSArnaldo Carvalho de Melo 
12705e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
128aca7a94dSNamhyung Kim {
129c3b78952SNamhyung Kim 	/*
130c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
131c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
132c3b78952SNamhyung Kim 	 */
133c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
134c3b78952SNamhyung Kim 
135268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
136c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
137357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
13805e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
139aca7a94dSNamhyung Kim }
140aca7a94dSNamhyung Kim 
141aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
142aca7a94dSNamhyung Kim {
143aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
144aca7a94dSNamhyung Kim }
145aca7a94dSNamhyung Kim 
14605e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
147aca7a94dSNamhyung Kim {
1483698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
149aca7a94dSNamhyung Kim }
150aca7a94dSNamhyung Kim 
15105e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
152aca7a94dSNamhyung Kim {
1533698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
154aca7a94dSNamhyung Kim }
155aca7a94dSNamhyung Kim 
1563698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
157aca7a94dSNamhyung Kim {
1583698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
159aca7a94dSNamhyung Kim }
160aca7a94dSNamhyung Kim 
16105e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
162aca7a94dSNamhyung Kim {
1632a704fc8SMilian Wolff 	int n = 0;
164aca7a94dSNamhyung Kim 	struct rb_node *nd;
165aca7a94dSNamhyung Kim 
16605e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
167aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
168aca7a94dSNamhyung Kim 		struct callchain_list *chain;
169aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
170aca7a94dSNamhyung Kim 
171aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
172aca7a94dSNamhyung Kim 			++n;
1730d3eb0b7SJin Yao 
174aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
175aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
176aca7a94dSNamhyung Kim 			if (folded_sign == '+')
177aca7a94dSNamhyung Kim 				break;
178aca7a94dSNamhyung Kim 		}
179aca7a94dSNamhyung Kim 
180aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
181aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
182aca7a94dSNamhyung Kim 	}
183aca7a94dSNamhyung Kim 
184aca7a94dSNamhyung Kim 	return n;
185aca7a94dSNamhyung Kim }
186aca7a94dSNamhyung Kim 
1874b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
1884b3a3212SNamhyung Kim {
1894b3a3212SNamhyung Kim 	struct callchain_list *chain;
1904b3a3212SNamhyung Kim 	char folded_sign = 0;
1914b3a3212SNamhyung Kim 	int n = 0;
1924b3a3212SNamhyung Kim 
1934b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
1944b3a3212SNamhyung Kim 		if (!folded_sign) {
1954b3a3212SNamhyung Kim 			/* only check first chain list entry */
1964b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1974b3a3212SNamhyung Kim 			if (folded_sign == '+')
1984b3a3212SNamhyung Kim 				return 1;
1994b3a3212SNamhyung Kim 		}
2004b3a3212SNamhyung Kim 		n++;
2014b3a3212SNamhyung Kim 	}
2024b3a3212SNamhyung Kim 
2034b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
2044b3a3212SNamhyung Kim 		if (!folded_sign) {
2054b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
2064b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2074b3a3212SNamhyung Kim 			if (folded_sign == '+')
2084b3a3212SNamhyung Kim 				return 1;
2094b3a3212SNamhyung Kim 		}
2104b3a3212SNamhyung Kim 		n++;
2114b3a3212SNamhyung Kim 	}
2124b3a3212SNamhyung Kim 
2134b3a3212SNamhyung Kim 	return n;
2144b3a3212SNamhyung Kim }
2154b3a3212SNamhyung Kim 
2168c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
2178c430a34SNamhyung Kim {
2188c430a34SNamhyung Kim 	return 1;
2198c430a34SNamhyung Kim }
2208c430a34SNamhyung Kim 
221aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
222aca7a94dSNamhyung Kim {
223aca7a94dSNamhyung Kim 	struct callchain_list *chain;
224aca7a94dSNamhyung Kim 	bool unfolded = false;
2252a704fc8SMilian Wolff 	int n = 0;
226aca7a94dSNamhyung Kim 
2274b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2284b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2298c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2308c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2314b3a3212SNamhyung Kim 
232aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
233aca7a94dSNamhyung Kim 		++n;
2340d3eb0b7SJin Yao 
2353698dab1SNamhyung Kim 		unfolded = chain->unfolded;
236aca7a94dSNamhyung Kim 	}
237aca7a94dSNamhyung Kim 
238aca7a94dSNamhyung Kim 	if (unfolded)
239aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
240aca7a94dSNamhyung Kim 
241aca7a94dSNamhyung Kim 	return n;
242aca7a94dSNamhyung Kim }
243aca7a94dSNamhyung Kim 
244aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
245aca7a94dSNamhyung Kim {
246aca7a94dSNamhyung Kim 	struct rb_node *nd;
247aca7a94dSNamhyung Kim 	int n = 0;
248aca7a94dSNamhyung Kim 
249aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
250aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
251aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
252aca7a94dSNamhyung Kim 	}
253aca7a94dSNamhyung Kim 
254aca7a94dSNamhyung Kim 	return n;
255aca7a94dSNamhyung Kim }
256aca7a94dSNamhyung Kim 
257f5b763feSNamhyung Kim static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
258f5b763feSNamhyung Kim 				bool include_children)
259f5b763feSNamhyung Kim {
260f5b763feSNamhyung Kim 	int count = 0;
261f5b763feSNamhyung Kim 	struct rb_node *node;
262f5b763feSNamhyung Kim 	struct hist_entry *child;
263f5b763feSNamhyung Kim 
264f5b763feSNamhyung Kim 	if (he->leaf)
265f5b763feSNamhyung Kim 		return callchain__count_rows(&he->sorted_chain);
266f5b763feSNamhyung Kim 
26779dded87SNamhyung Kim 	if (he->has_no_entry)
26879dded87SNamhyung Kim 		return 1;
26979dded87SNamhyung Kim 
270f5b763feSNamhyung Kim 	node = rb_first(&he->hroot_out);
271f5b763feSNamhyung Kim 	while (node) {
272f5b763feSNamhyung Kim 		float percent;
273f5b763feSNamhyung Kim 
274f5b763feSNamhyung Kim 		child = rb_entry(node, struct hist_entry, rb_node);
275f5b763feSNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
276f5b763feSNamhyung Kim 
277f5b763feSNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt) {
278f5b763feSNamhyung Kim 			count++;
279f5b763feSNamhyung Kim 
280f5b763feSNamhyung Kim 			if (include_children && child->unfolded)
281f5b763feSNamhyung Kim 				count += hierarchy_count_rows(hb, child, true);
282f5b763feSNamhyung Kim 		}
283f5b763feSNamhyung Kim 
284f5b763feSNamhyung Kim 		node = rb_next(node);
285f5b763feSNamhyung Kim 	}
286f5b763feSNamhyung Kim 	return count;
287f5b763feSNamhyung Kim }
288f5b763feSNamhyung Kim 
2893698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
290aca7a94dSNamhyung Kim {
2913698dab1SNamhyung Kim 	if (!he)
292aca7a94dSNamhyung Kim 		return false;
293aca7a94dSNamhyung Kim 
2943698dab1SNamhyung Kim 	if (!he->has_children)
295aca7a94dSNamhyung Kim 		return false;
296aca7a94dSNamhyung Kim 
2973698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
2983698dab1SNamhyung Kim 	return true;
2993698dab1SNamhyung Kim }
3003698dab1SNamhyung Kim 
3013698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
3023698dab1SNamhyung Kim {
3033698dab1SNamhyung Kim 	if (!cl)
3043698dab1SNamhyung Kim 		return false;
3053698dab1SNamhyung Kim 
3063698dab1SNamhyung Kim 	if (!cl->has_children)
3073698dab1SNamhyung Kim 		return false;
3083698dab1SNamhyung Kim 
3093698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
310aca7a94dSNamhyung Kim 	return true;
311aca7a94dSNamhyung Kim }
312aca7a94dSNamhyung Kim 
31305e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
314aca7a94dSNamhyung Kim {
31505e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
316aca7a94dSNamhyung Kim 
31705e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
318aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
319aca7a94dSNamhyung Kim 		struct callchain_list *chain;
320aca7a94dSNamhyung Kim 		bool first = true;
321aca7a94dSNamhyung Kim 
322aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
323aca7a94dSNamhyung Kim 			if (first) {
324aca7a94dSNamhyung Kim 				first = false;
3253698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
326aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
327aca7a94dSNamhyung Kim 			} else
3283698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
329aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
330aca7a94dSNamhyung Kim 		}
331aca7a94dSNamhyung Kim 
332aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
333aca7a94dSNamhyung Kim 	}
334aca7a94dSNamhyung Kim }
335aca7a94dSNamhyung Kim 
336a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
337a7444af6SNamhyung Kim 					       bool has_sibling)
338aca7a94dSNamhyung Kim {
339aca7a94dSNamhyung Kim 	struct callchain_list *chain;
340aca7a94dSNamhyung Kim 
341a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3423698dab1SNamhyung Kim 	chain->has_children = has_sibling;
343a7444af6SNamhyung Kim 
34490989035SAndres Freund 	if (!list_empty(&node->val)) {
34582162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3463698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
34782162b5aSNamhyung Kim 	}
348aca7a94dSNamhyung Kim 
34905e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
350aca7a94dSNamhyung Kim }
351aca7a94dSNamhyung Kim 
35205e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
353aca7a94dSNamhyung Kim {
354a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
355a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
356aca7a94dSNamhyung Kim 
35705e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
358aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
359a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3608c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3618c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3624b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
363aca7a94dSNamhyung Kim 	}
364aca7a94dSNamhyung Kim }
365aca7a94dSNamhyung Kim 
36605e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
367aca7a94dSNamhyung Kim {
368f5b763feSNamhyung Kim 	if (he->init_have_children)
369f5b763feSNamhyung Kim 		return;
370f5b763feSNamhyung Kim 
371f5b763feSNamhyung Kim 	if (he->leaf) {
3723698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
37305e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
374f5b763feSNamhyung Kim 	} else {
375f5b763feSNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
376aca7a94dSNamhyung Kim 	}
377f5b763feSNamhyung Kim 
378f5b763feSNamhyung Kim 	he->init_have_children = true;
379aca7a94dSNamhyung Kim }
380aca7a94dSNamhyung Kim 
38105e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
382aca7a94dSNamhyung Kim {
38305e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
3843698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
3853698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
3863698dab1SNamhyung Kim 	bool has_children;
387aca7a94dSNamhyung Kim 
3884938cf0cSWang Nan 	if (!he || !ms)
3894938cf0cSWang Nan 		return false;
3904938cf0cSWang Nan 
3913698dab1SNamhyung Kim 	if (ms == &he->ms)
3923698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
3933698dab1SNamhyung Kim 	else
3943698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
3953698dab1SNamhyung Kim 
3963698dab1SNamhyung Kim 	if (has_children) {
397f5b763feSNamhyung Kim 		int child_rows = 0;
398f5b763feSNamhyung Kim 
399aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
400c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
401aca7a94dSNamhyung Kim 
402f5b763feSNamhyung Kim 		if (he->leaf)
403f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
404f5b763feSNamhyung Kim 		else
405f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
406f5b763feSNamhyung Kim 
407f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
408f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
409f5b763feSNamhyung Kim 
410f5b763feSNamhyung Kim 		if (he->unfolded) {
411f5b763feSNamhyung Kim 			if (he->leaf)
4120d3eb0b7SJin Yao 				he->nr_rows = callchain__count_rows(
4130d3eb0b7SJin Yao 						&he->sorted_chain);
414aca7a94dSNamhyung Kim 			else
415f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
416f5b763feSNamhyung Kim 
417f5b763feSNamhyung Kim 			/* account grand children */
418f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
419f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
42079dded87SNamhyung Kim 
42179dded87SNamhyung Kim 			if (!he->leaf && he->nr_rows == 0) {
42279dded87SNamhyung Kim 				he->has_no_entry = true;
42379dded87SNamhyung Kim 				he->nr_rows = 1;
42479dded87SNamhyung Kim 			}
425f5b763feSNamhyung Kim 		} else {
426f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
427f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
428f5b763feSNamhyung Kim 
42979dded87SNamhyung Kim 			if (he->has_no_entry)
43079dded87SNamhyung Kim 				he->has_no_entry = false;
43179dded87SNamhyung Kim 
432aca7a94dSNamhyung Kim 			he->nr_rows = 0;
433f5b763feSNamhyung Kim 		}
434c3b78952SNamhyung Kim 
435c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
436f5b763feSNamhyung Kim 
437f5b763feSNamhyung Kim 		if (he->leaf)
438c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
439f5b763feSNamhyung Kim 		else
440f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
441aca7a94dSNamhyung Kim 
442aca7a94dSNamhyung Kim 		return true;
443aca7a94dSNamhyung Kim 	}
444aca7a94dSNamhyung Kim 
445aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
446aca7a94dSNamhyung Kim 	return false;
447aca7a94dSNamhyung Kim }
448aca7a94dSNamhyung Kim 
44905e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
450aca7a94dSNamhyung Kim {
451aca7a94dSNamhyung Kim 	int n = 0;
452aca7a94dSNamhyung Kim 	struct rb_node *nd;
453aca7a94dSNamhyung Kim 
45405e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
455aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
456aca7a94dSNamhyung Kim 		struct callchain_list *chain;
457aca7a94dSNamhyung Kim 		bool has_children = false;
458aca7a94dSNamhyung Kim 
459aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
460aca7a94dSNamhyung Kim 			++n;
4613698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
4623698dab1SNamhyung Kim 			has_children = chain->has_children;
463aca7a94dSNamhyung Kim 		}
464aca7a94dSNamhyung Kim 
465aca7a94dSNamhyung Kim 		if (has_children)
466aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
467aca7a94dSNamhyung Kim 	}
468aca7a94dSNamhyung Kim 
469aca7a94dSNamhyung Kim 	return n;
470aca7a94dSNamhyung Kim }
471aca7a94dSNamhyung Kim 
472aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
473aca7a94dSNamhyung Kim {
474aca7a94dSNamhyung Kim 	struct callchain_list *chain;
475aca7a94dSNamhyung Kim 	bool has_children = false;
476aca7a94dSNamhyung Kim 	int n = 0;
477aca7a94dSNamhyung Kim 
478aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
479aca7a94dSNamhyung Kim 		++n;
4803698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
4813698dab1SNamhyung Kim 		has_children = chain->has_children;
482aca7a94dSNamhyung Kim 	}
483aca7a94dSNamhyung Kim 
484aca7a94dSNamhyung Kim 	if (has_children)
485aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
486aca7a94dSNamhyung Kim 
487aca7a94dSNamhyung Kim 	return n;
488aca7a94dSNamhyung Kim }
489aca7a94dSNamhyung Kim 
490aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
491aca7a94dSNamhyung Kim {
492aca7a94dSNamhyung Kim 	struct rb_node *nd;
493aca7a94dSNamhyung Kim 	int n = 0;
494aca7a94dSNamhyung Kim 
495aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
496aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
497aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
498aca7a94dSNamhyung Kim 	}
499aca7a94dSNamhyung Kim 
500aca7a94dSNamhyung Kim 	return n;
501aca7a94dSNamhyung Kim }
502aca7a94dSNamhyung Kim 
503492b1010SNamhyung Kim static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
504492b1010SNamhyung Kim 				 bool unfold __maybe_unused)
505492b1010SNamhyung Kim {
506492b1010SNamhyung Kim 	float percent;
507492b1010SNamhyung Kim 	struct rb_node *nd;
508492b1010SNamhyung Kim 	struct hist_entry *child;
509492b1010SNamhyung Kim 	int n = 0;
510492b1010SNamhyung Kim 
511492b1010SNamhyung Kim 	for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
512492b1010SNamhyung Kim 		child = rb_entry(nd, struct hist_entry, rb_node);
513492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
514492b1010SNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt)
515492b1010SNamhyung Kim 			n++;
516492b1010SNamhyung Kim 	}
517492b1010SNamhyung Kim 
518492b1010SNamhyung Kim 	return n;
519492b1010SNamhyung Kim }
520492b1010SNamhyung Kim 
521b33f9226SJiri Olsa static void __hist_entry__set_folding(struct hist_entry *he,
522492b1010SNamhyung Kim 				      struct hist_browser *hb, bool unfold)
523aca7a94dSNamhyung Kim {
52405e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
5253698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
526aca7a94dSNamhyung Kim 
5273698dab1SNamhyung Kim 	if (he->has_children) {
528492b1010SNamhyung Kim 		int n;
529492b1010SNamhyung Kim 
530492b1010SNamhyung Kim 		if (he->leaf)
531492b1010SNamhyung Kim 			n = callchain__set_folding(&he->sorted_chain, unfold);
532492b1010SNamhyung Kim 		else
533492b1010SNamhyung Kim 			n = hierarchy_set_folding(hb, he, unfold);
534492b1010SNamhyung Kim 
53505e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
536aca7a94dSNamhyung Kim 	} else
53705e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
538aca7a94dSNamhyung Kim }
539aca7a94dSNamhyung Kim 
540b33f9226SJiri Olsa static void hist_entry__set_folding(struct hist_entry *he,
541b33f9226SJiri Olsa 				    struct hist_browser *browser, bool unfold)
542aca7a94dSNamhyung Kim {
543492b1010SNamhyung Kim 	double percent;
544aca7a94dSNamhyung Kim 
545492b1010SNamhyung Kim 	percent = hist_entry__get_percent_limit(he);
546492b1010SNamhyung Kim 	if (he->filtered || percent < browser->min_pcnt)
547b33f9226SJiri Olsa 		return;
548b33f9226SJiri Olsa 
549b33f9226SJiri Olsa 	__hist_entry__set_folding(he, browser, unfold);
550492b1010SNamhyung Kim 
551492b1010SNamhyung Kim 	if (!he->depth || unfold)
552492b1010SNamhyung Kim 		browser->nr_hierarchy_entries++;
553492b1010SNamhyung Kim 	if (he->leaf)
554c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
55579dded87SNamhyung Kim 	else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
55679dded87SNamhyung Kim 		browser->nr_hierarchy_entries++;
55779dded87SNamhyung Kim 		he->has_no_entry = true;
55879dded87SNamhyung Kim 		he->nr_rows = 1;
55979dded87SNamhyung Kim 	} else
56079dded87SNamhyung Kim 		he->has_no_entry = false;
561aca7a94dSNamhyung Kim }
562b33f9226SJiri Olsa 
563b33f9226SJiri Olsa static void
564b33f9226SJiri Olsa __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
565b33f9226SJiri Olsa {
566b33f9226SJiri Olsa 	struct rb_node *nd;
567b33f9226SJiri Olsa 	struct hist_entry *he;
568b33f9226SJiri Olsa 
569b33f9226SJiri Olsa 	nd = rb_first(&browser->hists->entries);
570b33f9226SJiri Olsa 	while (nd) {
571b33f9226SJiri Olsa 		he = rb_entry(nd, struct hist_entry, rb_node);
572b33f9226SJiri Olsa 
573b33f9226SJiri Olsa 		/* set folding state even if it's currently folded */
574b33f9226SJiri Olsa 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
575b33f9226SJiri Olsa 
576b33f9226SJiri Olsa 		hist_entry__set_folding(he, browser, unfold);
577b33f9226SJiri Olsa 	}
578aca7a94dSNamhyung Kim }
579aca7a94dSNamhyung Kim 
58005e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
581aca7a94dSNamhyung Kim {
582492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
583c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
584c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
585c3b78952SNamhyung Kim 
586c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
587aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
58805e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
589aca7a94dSNamhyung Kim }
590aca7a94dSNamhyung Kim 
5910e3fa7a7SJiri Olsa static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
5920e3fa7a7SJiri Olsa {
5930e3fa7a7SJiri Olsa 	if (!browser->he_selection)
5940e3fa7a7SJiri Olsa 		return;
5950e3fa7a7SJiri Olsa 
5960e3fa7a7SJiri Olsa 	hist_entry__set_folding(browser->he_selection, browser, unfold);
5970e3fa7a7SJiri Olsa 	browser->b.nr_entries = hist_browser__nr_entries(browser);
5980e3fa7a7SJiri Olsa }
5990e3fa7a7SJiri Olsa 
600aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
601aca7a94dSNamhyung Kim {
602aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
603aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
604aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
605aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
606aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
607aca7a94dSNamhyung Kim }
608aca7a94dSNamhyung Kim 
6095b91a86fSJiri Olsa static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
6105b91a86fSJiri Olsa {
6115b91a86fSJiri Olsa 	return browser->title ? browser->title(browser, bf, size) : 0;
6125b91a86fSJiri Olsa }
6135b91a86fSJiri Olsa 
61406cc1a47SKan Liang int hist_browser__run(struct hist_browser *browser, const char *help,
61506cc1a47SKan Liang 		      bool warn_lost_event)
616aca7a94dSNamhyung Kim {
617aca7a94dSNamhyung Kim 	int key;
618aca7a94dSNamhyung Kim 	char title[160];
619c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
6209783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
621aca7a94dSNamhyung Kim 
62205e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
623c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
624aca7a94dSNamhyung Kim 
6255b91a86fSJiri Olsa 	hist_browser__title(browser, title, sizeof(title));
626aca7a94dSNamhyung Kim 
627090cff3eSNamhyung Kim 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
628aca7a94dSNamhyung Kim 		return -1;
629aca7a94dSNamhyung Kim 
630aca7a94dSNamhyung Kim 	while (1) {
63105e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
632aca7a94dSNamhyung Kim 
633aca7a94dSNamhyung Kim 		switch (key) {
634fa5df943SNamhyung Kim 		case K_TIMER: {
635fa5df943SNamhyung Kim 			u64 nr_entries;
6369783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
637fa5df943SNamhyung Kim 
638c6111523SNamhyung Kim 			if (hist_browser__has_filter(browser) ||
639c6111523SNamhyung Kim 			    symbol_conf.report_hierarchy)
640112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
641fa5df943SNamhyung Kim 
642c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
643fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
644aca7a94dSNamhyung Kim 
64506cc1a47SKan Liang 			if (warn_lost_event &&
64606cc1a47SKan Liang 			    (browser->hists->stats.nr_lost_warned !=
64706cc1a47SKan Liang 			    browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
64805e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
64905e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
65005e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
651aca7a94dSNamhyung Kim 			}
652aca7a94dSNamhyung Kim 
6535b91a86fSJiri Olsa 			hist_browser__title(browser, title, sizeof(title));
65405e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
655aca7a94dSNamhyung Kim 			continue;
656fa5df943SNamhyung Kim 		}
657aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
658aca7a94dSNamhyung Kim 			static int seq;
65905e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
660aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
661aca7a94dSNamhyung Kim 			ui_helpline__pop();
662fdae6400SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
66305e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
66405e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
665fdae6400SArnaldo Carvalho de Melo 					   browser->b.extra_title_lines,
66662c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
66705e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
66805e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
669aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
670aca7a94dSNamhyung Kim 		}
671aca7a94dSNamhyung Kim 			break;
672aca7a94dSNamhyung Kim 		case 'C':
673aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
67405e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
675aca7a94dSNamhyung Kim 			break;
6760e3fa7a7SJiri Olsa 		case 'c':
6770e3fa7a7SJiri Olsa 			/* Collapse the selected entry. */
6780e3fa7a7SJiri Olsa 			hist_browser__set_folding_selected(browser, false);
6790e3fa7a7SJiri Olsa 			break;
680aca7a94dSNamhyung Kim 		case 'E':
681aca7a94dSNamhyung Kim 			/* Expand the whole world. */
68205e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
683aca7a94dSNamhyung Kim 			break;
6840e3fa7a7SJiri Olsa 		case 'e':
6850e3fa7a7SJiri Olsa 			/* Expand the selected entry. */
6860e3fa7a7SJiri Olsa 			hist_browser__set_folding_selected(browser, true);
6870e3fa7a7SJiri Olsa 			break;
688025bf7eaSArnaldo Carvalho de Melo 		case 'H':
689025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
690025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
691025bf7eaSArnaldo Carvalho de Melo 			break;
692aca7a94dSNamhyung Kim 		case K_ENTER:
69305e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
694aca7a94dSNamhyung Kim 				break;
695aca7a94dSNamhyung Kim 			/* fall thru */
696aca7a94dSNamhyung Kim 		default:
697aca7a94dSNamhyung Kim 			goto out;
698aca7a94dSNamhyung Kim 		}
699aca7a94dSNamhyung Kim 	}
700aca7a94dSNamhyung Kim out:
70105e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
702aca7a94dSNamhyung Kim 	return key;
703aca7a94dSNamhyung Kim }
704aca7a94dSNamhyung Kim 
70539ee533fSNamhyung Kim struct callchain_print_arg {
70639ee533fSNamhyung Kim 	/* for hists browser */
70739ee533fSNamhyung Kim 	off_t	row_offset;
70839ee533fSNamhyung Kim 	bool	is_current_entry;
70939ee533fSNamhyung Kim 
71039ee533fSNamhyung Kim 	/* for file dump */
71139ee533fSNamhyung Kim 	FILE	*fp;
71239ee533fSNamhyung Kim 	int	printed;
71339ee533fSNamhyung Kim };
71439ee533fSNamhyung Kim 
71539ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
71639ee533fSNamhyung Kim 					 struct callchain_list *chain,
71739ee533fSNamhyung Kim 					 const char *str, int offset,
71839ee533fSNamhyung Kim 					 unsigned short row,
71939ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
72039ee533fSNamhyung Kim 
721f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
722f4536dddSNamhyung Kim 					       struct callchain_list *chain,
72339ee533fSNamhyung Kim 					       const char *str, int offset,
72439ee533fSNamhyung Kim 					       unsigned short row,
72539ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
726f4536dddSNamhyung Kim {
727f4536dddSNamhyung Kim 	int color, width;
72839ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
72970e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
730f4536dddSNamhyung Kim 
731f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
732f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
733f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
734f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
735f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
73639ee533fSNamhyung Kim 		arg->is_current_entry = true;
737f4536dddSNamhyung Kim 	}
738f4536dddSNamhyung Kim 
739f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
740ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
74126270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
742517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
74370e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
74426270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
745f4536dddSNamhyung Kim }
746f4536dddSNamhyung Kim 
74739ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
74839ee533fSNamhyung Kim 						  struct callchain_list *chain,
74939ee533fSNamhyung Kim 						  const char *str, int offset,
75039ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
75139ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
75239ee533fSNamhyung Kim {
75339ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
75439ee533fSNamhyung Kim 
75539ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
75639ee533fSNamhyung Kim 				folded_sign, str);
75739ee533fSNamhyung Kim }
75839ee533fSNamhyung Kim 
75939ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
76039ee533fSNamhyung Kim 				     unsigned short row);
76139ee533fSNamhyung Kim 
76239ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
76339ee533fSNamhyung Kim 					    unsigned short row)
76439ee533fSNamhyung Kim {
76539ee533fSNamhyung Kim 	return browser->b.rows == row;
76639ee533fSNamhyung Kim }
76739ee533fSNamhyung Kim 
76839ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
76939ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
77039ee533fSNamhyung Kim {
77139ee533fSNamhyung Kim 	return false;
77239ee533fSNamhyung Kim }
77339ee533fSNamhyung Kim 
774aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
775aca7a94dSNamhyung Kim 
77618bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
77718bb8381SNamhyung Kim 					     struct callchain_node *node,
77818bb8381SNamhyung Kim 					     struct callchain_list *chain,
77918bb8381SNamhyung Kim 					     unsigned short row, u64 total,
78018bb8381SNamhyung Kim 					     bool need_percent, int offset,
78118bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
78218bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
78318bb8381SNamhyung Kim {
78418bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
785fef51ecdSJin Yao 	char buf[64], *alloc_str2;
78618bb8381SNamhyung Kim 	const char *str;
7872a704fc8SMilian Wolff 	int ret = 1;
78818bb8381SNamhyung Kim 
78918bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
79018bb8381SNamhyung Kim 		arg->row_offset--;
79118bb8381SNamhyung Kim 		return 0;
79218bb8381SNamhyung Kim 	}
79318bb8381SNamhyung Kim 
79418bb8381SNamhyung Kim 	alloc_str = NULL;
795fef51ecdSJin Yao 	alloc_str2 = NULL;
796fef51ecdSJin Yao 
79718bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
79818bb8381SNamhyung Kim 				       browser->show_dso);
79918bb8381SNamhyung Kim 
800fef51ecdSJin Yao 	if (symbol_conf.show_branchflag_count) {
801c4ee0625SJin Yao 		callchain_list_counts__printf_value(chain, NULL,
802fef51ecdSJin Yao 						    buf, sizeof(buf));
80318bb8381SNamhyung Kim 
804fef51ecdSJin Yao 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
805fef51ecdSJin Yao 			str = "Not enough memory!";
806fef51ecdSJin Yao 		else
807fef51ecdSJin Yao 			str = alloc_str2;
808fef51ecdSJin Yao 	}
809fef51ecdSJin Yao 
810fef51ecdSJin Yao 	if (need_percent) {
81118bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
81218bb8381SNamhyung Kim 						total);
81318bb8381SNamhyung Kim 
81418bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
81518bb8381SNamhyung Kim 			str = "Not enough memory!";
81618bb8381SNamhyung Kim 		else
81718bb8381SNamhyung Kim 			str = alloc_str;
81818bb8381SNamhyung Kim 	}
81918bb8381SNamhyung Kim 
82018bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
82118bb8381SNamhyung Kim 	free(alloc_str);
822fef51ecdSJin Yao 	free(alloc_str2);
8230d3eb0b7SJin Yao 
8242a704fc8SMilian Wolff 	return ret;
82518bb8381SNamhyung Kim }
82618bb8381SNamhyung Kim 
82759c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
82859c624e2SNamhyung Kim {
82959c624e2SNamhyung Kim 	struct callchain_node *child;
83059c624e2SNamhyung Kim 
83159c624e2SNamhyung Kim 	if (node == NULL)
83259c624e2SNamhyung Kim 		return false;
83359c624e2SNamhyung Kim 
83459c624e2SNamhyung Kim 	if (rb_next(node))
83559c624e2SNamhyung Kim 		return true;
83659c624e2SNamhyung Kim 
83759c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
83859c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
83959c624e2SNamhyung Kim }
84059c624e2SNamhyung Kim 
8414b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
8424b3a3212SNamhyung Kim 					     struct rb_root *root,
8434b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
84459c624e2SNamhyung Kim 					     u64 parent_total,
8454b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
8464b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
8474b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
8484b3a3212SNamhyung Kim {
8494b3a3212SNamhyung Kim 	struct rb_node *node;
8504b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
8514b3a3212SNamhyung Kim 	bool need_percent;
8524b3a3212SNamhyung Kim 
8534b3a3212SNamhyung Kim 	node = rb_first(root);
85459c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
8554b3a3212SNamhyung Kim 
8564b3a3212SNamhyung Kim 	while (node) {
8574b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
8584b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
8594b3a3212SNamhyung Kim 		struct callchain_list *chain;
8604b3a3212SNamhyung Kim 		char folded_sign = ' ';
8614b3a3212SNamhyung Kim 		int first = true;
8624b3a3212SNamhyung Kim 		int extra_offset = 0;
8634b3a3212SNamhyung Kim 
8644b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
8654b3a3212SNamhyung Kim 			bool was_first = first;
8664b3a3212SNamhyung Kim 
8674b3a3212SNamhyung Kim 			if (first)
8684b3a3212SNamhyung Kim 				first = false;
8694b3a3212SNamhyung Kim 			else if (need_percent)
8704b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8714b3a3212SNamhyung Kim 
8724b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8734b3a3212SNamhyung Kim 
8744b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8754b3a3212SNamhyung Kim 							chain, row, total,
8764b3a3212SNamhyung Kim 							was_first && need_percent,
8774b3a3212SNamhyung Kim 							offset + extra_offset,
8784b3a3212SNamhyung Kim 							print, arg);
8794b3a3212SNamhyung Kim 
8804b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8814b3a3212SNamhyung Kim 				goto out;
8824b3a3212SNamhyung Kim 
8834b3a3212SNamhyung Kim 			if (folded_sign == '+')
8844b3a3212SNamhyung Kim 				goto next;
8854b3a3212SNamhyung Kim 		}
8864b3a3212SNamhyung Kim 
8874b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
8884b3a3212SNamhyung Kim 			bool was_first = first;
8894b3a3212SNamhyung Kim 
8904b3a3212SNamhyung Kim 			if (first)
8914b3a3212SNamhyung Kim 				first = false;
8924b3a3212SNamhyung Kim 			else if (need_percent)
8934b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8944b3a3212SNamhyung Kim 
8954b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8964b3a3212SNamhyung Kim 
8974b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8984b3a3212SNamhyung Kim 							chain, row, total,
8994b3a3212SNamhyung Kim 							was_first && need_percent,
9004b3a3212SNamhyung Kim 							offset + extra_offset,
9014b3a3212SNamhyung Kim 							print, arg);
9024b3a3212SNamhyung Kim 
9034b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
9044b3a3212SNamhyung Kim 				goto out;
9054b3a3212SNamhyung Kim 
9064b3a3212SNamhyung Kim 			if (folded_sign == '+')
9074b3a3212SNamhyung Kim 				break;
9084b3a3212SNamhyung Kim 		}
9094b3a3212SNamhyung Kim 
9104b3a3212SNamhyung Kim next:
9114b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
9124b3a3212SNamhyung Kim 			break;
9134b3a3212SNamhyung Kim 		node = next;
9144b3a3212SNamhyung Kim 	}
9154b3a3212SNamhyung Kim out:
9164b3a3212SNamhyung Kim 	return row - first_row;
9174b3a3212SNamhyung Kim }
9184b3a3212SNamhyung Kim 
9198c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
9208c430a34SNamhyung Kim 						struct callchain_list *chain,
9218c430a34SNamhyung Kim 						char *value_str, char *old_str)
9228c430a34SNamhyung Kim {
9238c430a34SNamhyung Kim 	char bf[1024];
9248c430a34SNamhyung Kim 	const char *str;
9258c430a34SNamhyung Kim 	char *new;
9268c430a34SNamhyung Kim 
9278c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
9288c430a34SNamhyung Kim 				       browser->show_dso);
9298c430a34SNamhyung Kim 	if (old_str) {
9308c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
9318c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
9328c430a34SNamhyung Kim 			new = NULL;
9338c430a34SNamhyung Kim 	} else {
9348c430a34SNamhyung Kim 		if (value_str) {
9358c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
9368c430a34SNamhyung Kim 				new = NULL;
9378c430a34SNamhyung Kim 		} else {
9388c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
9398c430a34SNamhyung Kim 				new = NULL;
9408c430a34SNamhyung Kim 		}
9418c430a34SNamhyung Kim 	}
9428c430a34SNamhyung Kim 	return new;
9438c430a34SNamhyung Kim }
9448c430a34SNamhyung Kim 
9458c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
9468c430a34SNamhyung Kim 					       struct rb_root *root,
9478c430a34SNamhyung Kim 					       unsigned short row, u64 total,
94859c624e2SNamhyung Kim 					       u64 parent_total,
9498c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
9508c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
9518c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
9528c430a34SNamhyung Kim {
9538c430a34SNamhyung Kim 	struct rb_node *node;
9548c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
9558c430a34SNamhyung Kim 	bool need_percent;
9568c430a34SNamhyung Kim 
9578c430a34SNamhyung Kim 	node = rb_first(root);
95859c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9598c430a34SNamhyung Kim 
9608c430a34SNamhyung Kim 	while (node) {
9618c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
9628c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
9638c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
9648c430a34SNamhyung Kim 		int first = true;
9658c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
9668c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
9678c430a34SNamhyung Kim 
9688c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
9698c430a34SNamhyung Kim 			arg->row_offset--;
9708c430a34SNamhyung Kim 			goto next;
9718c430a34SNamhyung Kim 		}
9728c430a34SNamhyung Kim 
9738c430a34SNamhyung Kim 		if (need_percent) {
9748c430a34SNamhyung Kim 			char buf[64];
9758c430a34SNamhyung Kim 
9768c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
9778c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
9788c430a34SNamhyung Kim 				value_str = (char *)"<...>";
9798c430a34SNamhyung Kim 				goto do_print;
9808c430a34SNamhyung Kim 			}
9818c430a34SNamhyung Kim 			value_str_alloc = value_str;
9828c430a34SNamhyung Kim 		}
9838c430a34SNamhyung Kim 
9848c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9858c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9868c430a34SNamhyung Kim 						chain, value_str, chain_str);
9878c430a34SNamhyung Kim 			if (first) {
9888c430a34SNamhyung Kim 				first = false;
9898c430a34SNamhyung Kim 				first_chain = chain;
9908c430a34SNamhyung Kim 			}
9918c430a34SNamhyung Kim 
9928c430a34SNamhyung Kim 			if (chain_str == NULL) {
9938c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9948c430a34SNamhyung Kim 				goto do_print;
9958c430a34SNamhyung Kim 			}
9968c430a34SNamhyung Kim 
9978c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9988c430a34SNamhyung Kim 		}
9998c430a34SNamhyung Kim 
10008c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
10018c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
10028c430a34SNamhyung Kim 						chain, value_str, chain_str);
10038c430a34SNamhyung Kim 			if (first) {
10048c430a34SNamhyung Kim 				first = false;
10058c430a34SNamhyung Kim 				first_chain = chain;
10068c430a34SNamhyung Kim 			}
10078c430a34SNamhyung Kim 
10088c430a34SNamhyung Kim 			if (chain_str == NULL) {
10098c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
10108c430a34SNamhyung Kim 				goto do_print;
10118c430a34SNamhyung Kim 			}
10128c430a34SNamhyung Kim 
10138c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
10148c430a34SNamhyung Kim 		}
10158c430a34SNamhyung Kim 
10168c430a34SNamhyung Kim do_print:
10178c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
10188c430a34SNamhyung Kim 		free(value_str_alloc);
10198c430a34SNamhyung Kim 		free(chain_str_alloc);
10208c430a34SNamhyung Kim 
10218c430a34SNamhyung Kim next:
10228c430a34SNamhyung Kim 		if (is_output_full(browser, row))
10238c430a34SNamhyung Kim 			break;
10248c430a34SNamhyung Kim 		node = next;
10258c430a34SNamhyung Kim 	}
10268c430a34SNamhyung Kim 
10278c430a34SNamhyung Kim 	return row - first_row;
10288c430a34SNamhyung Kim }
10298c430a34SNamhyung Kim 
10300c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1031c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
103239ee533fSNamhyung Kim 					unsigned short row, u64 total,
10335eca104eSNamhyung Kim 					u64 parent_total,
103439ee533fSNamhyung Kim 					print_callchain_entry_fn print,
103539ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
103639ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
1037aca7a94dSNamhyung Kim {
1038aca7a94dSNamhyung Kim 	struct rb_node *node;
1039f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
10404087d11cSNamhyung Kim 	bool need_percent;
10415eca104eSNamhyung Kim 	u64 percent_total = total;
10425eca104eSNamhyung Kim 
10435eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
10445eca104eSNamhyung Kim 		percent_total = parent_total;
1045aca7a94dSNamhyung Kim 
1046c09a7e75SNamhyung Kim 	node = rb_first(root);
104759c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
10484087d11cSNamhyung Kim 
1049aca7a94dSNamhyung Kim 	while (node) {
1050aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1051aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
1052aca7a94dSNamhyung Kim 		struct callchain_list *chain;
1053aca7a94dSNamhyung Kim 		char folded_sign = ' ';
1054aca7a94dSNamhyung Kim 		int first = true;
1055aca7a94dSNamhyung Kim 		int extra_offset = 0;
1056aca7a94dSNamhyung Kim 
1057aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
1058aca7a94dSNamhyung Kim 			bool was_first = first;
1059aca7a94dSNamhyung Kim 
1060aca7a94dSNamhyung Kim 			if (first)
1061aca7a94dSNamhyung Kim 				first = false;
10624087d11cSNamhyung Kim 			else if (need_percent)
1063aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1064aca7a94dSNamhyung Kim 
1065aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1066aca7a94dSNamhyung Kim 
106718bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
10685eca104eSNamhyung Kim 							chain, row, percent_total,
106918bb8381SNamhyung Kim 							was_first && need_percent,
107018bb8381SNamhyung Kim 							offset + extra_offset,
107118bb8381SNamhyung Kim 							print, arg);
1072c09a7e75SNamhyung Kim 
107318bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1074aca7a94dSNamhyung Kim 				goto out;
107518bb8381SNamhyung Kim 
1076aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1077aca7a94dSNamhyung Kim 				break;
1078aca7a94dSNamhyung Kim 		}
1079aca7a94dSNamhyung Kim 
1080aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1081aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1082c09a7e75SNamhyung Kim 
10830c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
10845eca104eSNamhyung Kim 							    new_level, row, total,
10855eca104eSNamhyung Kim 							    child->children_hit,
108639ee533fSNamhyung Kim 							    print, arg, is_output_full);
1087aca7a94dSNamhyung Kim 		}
108839ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1089c09a7e75SNamhyung Kim 			break;
1090aca7a94dSNamhyung Kim 		node = next;
1091aca7a94dSNamhyung Kim 	}
1092aca7a94dSNamhyung Kim out:
1093aca7a94dSNamhyung Kim 	return row - first_row;
1094aca7a94dSNamhyung Kim }
1095aca7a94dSNamhyung Kim 
10960c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
10970c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
10980c841c6cSNamhyung Kim 					unsigned short row,
10990c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
11000c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
11010c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
11020c841c6cSNamhyung Kim {
11030c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
11045eca104eSNamhyung Kim 	u64 parent_total;
11050c841c6cSNamhyung Kim 	int printed;
11060c841c6cSNamhyung Kim 
11070c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
11085eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
11090c841c6cSNamhyung Kim 	else
11105eca104eSNamhyung Kim 		parent_total = entry->stat.period;
11110c841c6cSNamhyung Kim 
11120c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
11130c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
11145eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11155eca104eSNamhyung Kim 						total, parent_total, print, arg,
11165eca104eSNamhyung Kim 						is_output_full);
11170c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
11180c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
11195eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11205eca104eSNamhyung Kim 						total, parent_total, print, arg,
11215eca104eSNamhyung Kim 						is_output_full);
11220c841c6cSNamhyung Kim 	} else {
11230c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
11245eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
11255eca104eSNamhyung Kim 						total, parent_total, print, arg,
11265eca104eSNamhyung Kim 						is_output_full);
11270c841c6cSNamhyung Kim 	}
11280c841c6cSNamhyung Kim 
11290c841c6cSNamhyung Kim 	if (arg->is_current_entry)
11300c841c6cSNamhyung Kim 		browser->he_selection = entry;
11310c841c6cSNamhyung Kim 
11320c841c6cSNamhyung Kim 	return printed;
11330c841c6cSNamhyung Kim }
11340c841c6cSNamhyung Kim 
113589701460SNamhyung Kim struct hpp_arg {
113689701460SNamhyung Kim 	struct ui_browser *b;
113789701460SNamhyung Kim 	char folded_sign;
113889701460SNamhyung Kim 	bool current_entry;
113989701460SNamhyung Kim };
114089701460SNamhyung Kim 
114198ba1609SJiri Olsa int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
11422f6d9009SNamhyung Kim {
11432f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1144d675107cSNamhyung Kim 	int ret, len;
11452f6d9009SNamhyung Kim 	va_list args;
11462f6d9009SNamhyung Kim 	double percent;
11472f6d9009SNamhyung Kim 
11482f6d9009SNamhyung Kim 	va_start(args, fmt);
1149d675107cSNamhyung Kim 	len = va_arg(args, int);
11502f6d9009SNamhyung Kim 	percent = va_arg(args, double);
11512f6d9009SNamhyung Kim 	va_end(args);
11525aed9d24SNamhyung Kim 
115389701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
11545aed9d24SNamhyung Kim 
1155d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1156517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
115789701460SNamhyung Kim 
11585aed9d24SNamhyung Kim 	return ret;
1159f5951d56SNamhyung Kim }
1160f5951d56SNamhyung Kim 
1161fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
11625aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
11635aed9d24SNamhyung Kim {									\
11645aed9d24SNamhyung Kim 	return he->stat._field;						\
11655aed9d24SNamhyung Kim }									\
11665aed9d24SNamhyung Kim 									\
11672c5d4b4aSJiri Olsa static int								\
11685b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11692c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
11705aed9d24SNamhyung Kim 				struct hist_entry *he)			\
11715aed9d24SNamhyung Kim {									\
11725b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
11732f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
11745aed9d24SNamhyung Kim }
1175f5951d56SNamhyung Kim 
11760434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
11770434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
11780434ddd2SNamhyung Kim {									\
11790434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
11800434ddd2SNamhyung Kim }									\
11810434ddd2SNamhyung Kim 									\
11820434ddd2SNamhyung Kim static int								\
11835b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11840434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
11850434ddd2SNamhyung Kim 				struct hist_entry *he)			\
11860434ddd2SNamhyung Kim {									\
11870434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1188517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
11895b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1190d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
11915b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1192517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
11930434ddd2SNamhyung Kim 									\
11940434ddd2SNamhyung Kim 		return ret;						\
11950434ddd2SNamhyung Kim 	}								\
11965b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
11975b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
11980434ddd2SNamhyung Kim }
11990434ddd2SNamhyung Kim 
1200fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1201fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1202fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1203fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1204fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
12050434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
12065aed9d24SNamhyung Kim 
12075aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
12080434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1209f5951d56SNamhyung Kim 
1210f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1211f5951d56SNamhyung Kim {
1212f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1213f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1214f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1215f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1216f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1217f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1218f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1219f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1220f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1221f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
12220434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
12230434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
1224f5951d56SNamhyung Kim }
1225f5951d56SNamhyung Kim 
122605e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1227aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1228aca7a94dSNamhyung Kim 				    unsigned short row)
1229aca7a94dSNamhyung Kim {
12301240005eSJiri Olsa 	int printed = 0;
123167d25916SNamhyung Kim 	int width = browser->b.width;
1232aca7a94dSNamhyung Kim 	char folded_sign = ' ';
123305e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1234fabd37b8SArnaldo Carvalho de Melo 	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1235aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
123663a1a3d8SNamhyung Kim 	bool first = true;
12371240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1238aca7a94dSNamhyung Kim 
1239aca7a94dSNamhyung Kim 	if (current_entry) {
124005e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
124105e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1242aca7a94dSNamhyung Kim 	}
1243aca7a94dSNamhyung Kim 
1244fabd37b8SArnaldo Carvalho de Melo 	if (use_callchain) {
1245aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1246aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1247aca7a94dSNamhyung Kim 	}
1248aca7a94dSNamhyung Kim 
1249aca7a94dSNamhyung Kim 	if (row_offset == 0) {
125089701460SNamhyung Kim 		struct hpp_arg arg = {
125189701460SNamhyung Kim 			.b		= &browser->b,
125289701460SNamhyung Kim 			.folded_sign	= folded_sign,
125389701460SNamhyung Kim 			.current_entry	= current_entry,
125489701460SNamhyung Kim 		};
1255c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1256f5951d56SNamhyung Kim 
1257ef9ff601SArnaldo Carvalho de Melo 		ui_browser__gotorc(&browser->b, row, 0);
1258f5951d56SNamhyung Kim 
1259f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
126089fee709SArnaldo Carvalho de Melo 			char s[2048];
126189fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
126289fee709SArnaldo Carvalho de Melo 				.buf	= s,
126389fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
126489fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
126589fee709SArnaldo Carvalho de Melo 			};
126689fee709SArnaldo Carvalho de Melo 
1267361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1268361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1269e67d49a7SNamhyung Kim 				continue;
1270e67d49a7SNamhyung Kim 
1271fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1272fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1273fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1274fb821c9eSNamhyung Kim 			} else {
1275fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1276fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1277fb821c9eSNamhyung Kim 			}
1278fb821c9eSNamhyung Kim 
1279fb821c9eSNamhyung Kim 			if (first) {
1280fabd37b8SArnaldo Carvalho de Melo 				if (use_callchain) {
1281517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1282f5951d56SNamhyung Kim 					width -= 2;
1283f5951d56SNamhyung Kim 				}
128463a1a3d8SNamhyung Kim 				first = false;
1285fb821c9eSNamhyung Kim 			} else {
1286517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1287fb821c9eSNamhyung Kim 				width -= 2;
1288fb821c9eSNamhyung Kim 			}
1289f5951d56SNamhyung Kim 
12901240005eSJiri Olsa 			if (fmt->color) {
129189fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
129289fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
129389fee709SArnaldo Carvalho de Melo 				/*
129489fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
129589fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
129689fee709SArnaldo Carvalho de Melo 				 */
129789fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1298f5951d56SNamhyung Kim 			} else {
129989fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1300517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1301f5951d56SNamhyung Kim 			}
130289fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1303f5951d56SNamhyung Kim 		}
1304aca7a94dSNamhyung Kim 
1305aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
130605e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1307aca7a94dSNamhyung Kim 			width += 1;
1308aca7a94dSNamhyung Kim 
130926270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
131026d8b338SNamhyung Kim 
1311aca7a94dSNamhyung Kim 		++row;
1312aca7a94dSNamhyung Kim 		++printed;
1313aca7a94dSNamhyung Kim 	} else
1314aca7a94dSNamhyung Kim 		--row_offset;
1315aca7a94dSNamhyung Kim 
131662c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
131739ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
131839ee533fSNamhyung Kim 			.row_offset = row_offset,
131939ee533fSNamhyung Kim 			.is_current_entry = current_entry,
132039ee533fSNamhyung Kim 		};
1321c09a7e75SNamhyung Kim 
13220d3eb0b7SJin Yao 		printed += hist_browser__show_callchain(browser,
13230d3eb0b7SJin Yao 				entry, 1, row,
13240d3eb0b7SJin Yao 				hist_browser__show_callchain_entry,
13250d3eb0b7SJin Yao 				&arg,
13264b3a3212SNamhyung Kim 				hist_browser__check_output_full);
1327aca7a94dSNamhyung Kim 	}
1328aca7a94dSNamhyung Kim 
1329aca7a94dSNamhyung Kim 	return printed;
1330aca7a94dSNamhyung Kim }
1331aca7a94dSNamhyung Kim 
1332d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1333d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1334d0506edbSNamhyung Kim 					      unsigned short row,
13352dbbe9f2SNamhyung Kim 					      int level)
1336d0506edbSNamhyung Kim {
1337d0506edbSNamhyung Kim 	int printed = 0;
1338d0506edbSNamhyung Kim 	int width = browser->b.width;
1339d0506edbSNamhyung Kim 	char folded_sign = ' ';
1340d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1341d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1342d0506edbSNamhyung Kim 	bool first = true;
1343d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1344a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1345d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1346d0506edbSNamhyung Kim 		.b		= &browser->b,
1347d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1348d0506edbSNamhyung Kim 	};
1349d0506edbSNamhyung Kim 	int column = 0;
13502dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1351d0506edbSNamhyung Kim 
1352d0506edbSNamhyung Kim 	if (current_entry) {
1353d0506edbSNamhyung Kim 		browser->he_selection = entry;
1354d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1355d0506edbSNamhyung Kim 	}
1356d0506edbSNamhyung Kim 
1357d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1358d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1359d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1360d0506edbSNamhyung Kim 
1361d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1362d0506edbSNamhyung Kim 		row_offset--;
1363d0506edbSNamhyung Kim 		goto show_callchain;
1364d0506edbSNamhyung Kim 	}
1365d0506edbSNamhyung Kim 
1366ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
1367d0506edbSNamhyung Kim 
1368d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1369d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1370d0506edbSNamhyung Kim 	else
1371d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1372d0506edbSNamhyung Kim 
1373d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1374d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1375d0506edbSNamhyung Kim 
1376a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1377a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1378a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1379a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1380d0506edbSNamhyung Kim 		char s[2048];
1381d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1382d0506edbSNamhyung Kim 			.buf		= s,
1383d0506edbSNamhyung Kim 			.size		= sizeof(s),
1384d0506edbSNamhyung Kim 			.ptr		= &arg,
1385d0506edbSNamhyung Kim 		};
1386d0506edbSNamhyung Kim 
1387d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1388d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1389d0506edbSNamhyung Kim 			continue;
1390d0506edbSNamhyung Kim 
1391d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1392d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1393d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1394d0506edbSNamhyung Kim 		} else {
1395d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1396d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1397d0506edbSNamhyung Kim 		}
1398d0506edbSNamhyung Kim 
1399d0506edbSNamhyung Kim 		if (first) {
1400d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c ", folded_sign);
14013d9f4683SNamhyung Kim 			width -= 2;
1402d0506edbSNamhyung Kim 			first = false;
1403d0506edbSNamhyung Kim 		} else {
1404d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1405d0506edbSNamhyung Kim 			width -= 2;
1406d0506edbSNamhyung Kim 		}
1407d0506edbSNamhyung Kim 
1408d0506edbSNamhyung Kim 		if (fmt->color) {
1409d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1410d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1411d0506edbSNamhyung Kim 			/*
1412d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1413d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1414d0506edbSNamhyung Kim 			 */
1415d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1416d0506edbSNamhyung Kim 		} else {
1417d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1418d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1419d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1420d0506edbSNamhyung Kim 		}
1421d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1422d0506edbSNamhyung Kim 	}
1423d0506edbSNamhyung Kim 
1424b9bf911eSNamhyung Kim 	if (!first) {
1425d0506edbSNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1426d0506edbSNamhyung Kim 		width -= hierarchy_indent;
1427b9bf911eSNamhyung Kim 	}
1428d0506edbSNamhyung Kim 
1429d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1430d0506edbSNamhyung Kim 		char s[2048];
1431d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1432d0506edbSNamhyung Kim 			.buf		= s,
1433d0506edbSNamhyung Kim 			.size		= sizeof(s),
1434d0506edbSNamhyung Kim 			.ptr		= &arg,
1435d0506edbSNamhyung Kim 		};
1436d0506edbSNamhyung Kim 
1437d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1438d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1439d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1440d0506edbSNamhyung Kim 		} else {
1441d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1442d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1443d0506edbSNamhyung Kim 		}
1444d0506edbSNamhyung Kim 
14451b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1446131d51ebSNamhyung Kim 			if (first) {
1447131d51ebSNamhyung Kim 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1448131d51ebSNamhyung Kim 				first = false;
1449131d51ebSNamhyung Kim 			} else {
1450d0506edbSNamhyung Kim 				ui_browser__write_nstring(&browser->b, "", 2);
1451131d51ebSNamhyung Kim 			}
1452131d51ebSNamhyung Kim 
1453d0506edbSNamhyung Kim 			width -= 2;
1454d0506edbSNamhyung Kim 
1455d0506edbSNamhyung Kim 			/*
1456d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1457d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1458d0506edbSNamhyung Kim 			 * hierarchy mode.
1459d0506edbSNamhyung Kim 			 */
1460d0506edbSNamhyung Kim 			if (fmt->color) {
1461d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1462d0506edbSNamhyung Kim 			} else {
1463cb1fab91SNamhyung Kim 				int i = 0;
1464cb1fab91SNamhyung Kim 
1465d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
1466cb1fab91SNamhyung Kim 				ui_browser__printf(&browser->b, "%s", ltrim(s));
1467cb1fab91SNamhyung Kim 
1468cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1469cb1fab91SNamhyung Kim 					width++;
1470d0506edbSNamhyung Kim 			}
1471d0506edbSNamhyung Kim 		}
14721b2dbbf4SNamhyung Kim 	}
1473d0506edbSNamhyung Kim 
1474d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1475d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1476d0506edbSNamhyung Kim 		width += 1;
1477d0506edbSNamhyung Kim 
1478d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1479d0506edbSNamhyung Kim 
1480d0506edbSNamhyung Kim 	++row;
1481d0506edbSNamhyung Kim 	++printed;
1482d0506edbSNamhyung Kim 
1483d0506edbSNamhyung Kim show_callchain:
1484d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1485d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1486d0506edbSNamhyung Kim 			.row_offset = row_offset,
1487d0506edbSNamhyung Kim 		};
1488d0506edbSNamhyung Kim 
1489d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1490d0506edbSNamhyung Kim 					level + 1, row,
1491d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1492d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1493d0506edbSNamhyung Kim 	}
1494d0506edbSNamhyung Kim 
1495d0506edbSNamhyung Kim 	return printed;
1496d0506edbSNamhyung Kim }
1497d0506edbSNamhyung Kim 
149879dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
14992dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
150079dded87SNamhyung Kim {
150179dded87SNamhyung Kim 	int width = browser->b.width;
150279dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
150379dded87SNamhyung Kim 	bool first = true;
150479dded87SNamhyung Kim 	int column = 0;
150579dded87SNamhyung Kim 	int ret;
150679dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1507a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
15082dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
150979dded87SNamhyung Kim 
151079dded87SNamhyung Kim 	if (current_entry) {
151179dded87SNamhyung Kim 		browser->he_selection = NULL;
151279dded87SNamhyung Kim 		browser->selection = NULL;
151379dded87SNamhyung Kim 	}
151479dded87SNamhyung Kim 
1515ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
151679dded87SNamhyung Kim 
151779dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
151879dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
151979dded87SNamhyung Kim 	else
152079dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
152179dded87SNamhyung Kim 
152279dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
152379dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
152479dded87SNamhyung Kim 
1525a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1526a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1527a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1528a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
152979dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
153079dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
153179dded87SNamhyung Kim 			continue;
153279dded87SNamhyung Kim 
1533da1b0407SJiri Olsa 		ret = fmt->width(fmt, NULL, browser->hists);
153479dded87SNamhyung Kim 
153579dded87SNamhyung Kim 		if (first) {
153679dded87SNamhyung Kim 			/* for folded sign */
153779dded87SNamhyung Kim 			first = false;
153879dded87SNamhyung Kim 			ret++;
153979dded87SNamhyung Kim 		} else {
154079dded87SNamhyung Kim 			/* space between columns */
154179dded87SNamhyung Kim 			ret += 2;
154279dded87SNamhyung Kim 		}
154379dded87SNamhyung Kim 
154479dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
154579dded87SNamhyung Kim 		width -= ret;
154679dded87SNamhyung Kim 	}
154779dded87SNamhyung Kim 
15482dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
15492dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
155079dded87SNamhyung Kim 
155179dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
155279dded87SNamhyung Kim 		char buf[32];
155379dded87SNamhyung Kim 
155479dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
155579dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
155679dded87SNamhyung Kim 		width -= ret + 2;
155779dded87SNamhyung Kim 	}
155879dded87SNamhyung Kim 
155979dded87SNamhyung Kim 	/* The scroll bar isn't being used */
156079dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
156179dded87SNamhyung Kim 		width += 1;
156279dded87SNamhyung Kim 
156379dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
156479dded87SNamhyung Kim 	return 1;
156579dded87SNamhyung Kim }
156679dded87SNamhyung Kim 
156781a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
156881a888feSJiri Olsa {
156981a888feSJiri Olsa 	advance_hpp(hpp, inc);
157081a888feSJiri Olsa 	return hpp->size <= 0;
157181a888feSJiri Olsa }
157281a888feSJiri Olsa 
157369705b35SJiri Olsa static int
157469705b35SJiri Olsa hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
157569705b35SJiri Olsa 				 size_t size, int line)
157681a888feSJiri Olsa {
1577c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
157881a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
157981a888feSJiri Olsa 		.buf    = buf,
158081a888feSJiri Olsa 		.size   = size,
158181a888feSJiri Olsa 	};
158281a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
158381a888feSJiri Olsa 	size_t ret = 0;
1584c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
158529659ab4SJiri Olsa 	int span = 0;
158681a888feSJiri Olsa 
1587fabd37b8SArnaldo Carvalho de Melo 	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
158881a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
158981a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
159081a888feSJiri Olsa 			return ret;
159181a888feSJiri Olsa 	}
159281a888feSJiri Olsa 
1593f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1594361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
159581a888feSJiri Olsa 			continue;
159681a888feSJiri Olsa 
159729659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
159881a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
159981a888feSJiri Olsa 			break;
160081a888feSJiri Olsa 
160129659ab4SJiri Olsa 		if (span)
160229659ab4SJiri Olsa 			continue;
160329659ab4SJiri Olsa 
160481a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
160581a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
160681a888feSJiri Olsa 			break;
160781a888feSJiri Olsa 	}
160881a888feSJiri Olsa 
160981a888feSJiri Olsa 	return ret;
161081a888feSJiri Olsa }
161181a888feSJiri Olsa 
1612d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1613d8b92400SNamhyung Kim {
1614d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1615d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1616d8b92400SNamhyung Kim 		.buf    = buf,
1617d8b92400SNamhyung Kim 		.size   = size,
1618d8b92400SNamhyung Kim 	};
1619d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1620a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1621d8b92400SNamhyung Kim 	size_t ret = 0;
1622d8b92400SNamhyung Kim 	int column = 0;
16232dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1624a61a22f6SNamhyung Kim 	bool first_node, first_col;
1625d8b92400SNamhyung Kim 
1626d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, "  ");
1627d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1628d8b92400SNamhyung Kim 		return ret;
1629d8b92400SNamhyung Kim 
1630b9bf911eSNamhyung Kim 	first_node = true;
1631a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1632a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1633a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1634a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1635d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1636d8b92400SNamhyung Kim 			continue;
1637d8b92400SNamhyung Kim 
163829659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1639d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1640d8b92400SNamhyung Kim 			break;
1641d8b92400SNamhyung Kim 
1642d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1643d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1644d8b92400SNamhyung Kim 			break;
1645b9bf911eSNamhyung Kim 
1646b9bf911eSNamhyung Kim 		first_node = false;
1647d8b92400SNamhyung Kim 	}
1648d8b92400SNamhyung Kim 
1649b9bf911eSNamhyung Kim 	if (!first_node) {
1650d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
16512dbbe9f2SNamhyung Kim 				indent * HIERARCHY_INDENT, "");
1652d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1653d8b92400SNamhyung Kim 			return ret;
1654b9bf911eSNamhyung Kim 	}
1655d8b92400SNamhyung Kim 
1656a61a22f6SNamhyung Kim 	first_node = true;
1657a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1658a61a22f6SNamhyung Kim 		if (!first_node) {
1659d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1660d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1661d8b92400SNamhyung Kim 				break;
1662d8b92400SNamhyung Kim 		}
1663a61a22f6SNamhyung Kim 		first_node = false;
1664a61a22f6SNamhyung Kim 
1665a61a22f6SNamhyung Kim 		first_col = true;
1666a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1667a61a22f6SNamhyung Kim 			char *start;
1668a61a22f6SNamhyung Kim 
1669a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1670a61a22f6SNamhyung Kim 				continue;
1671a61a22f6SNamhyung Kim 
1672a61a22f6SNamhyung Kim 			if (!first_col) {
1673a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1674a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1675a61a22f6SNamhyung Kim 					break;
1676a61a22f6SNamhyung Kim 			}
1677a61a22f6SNamhyung Kim 			first_col = false;
1678d8b92400SNamhyung Kim 
167929659ab4SJiri Olsa 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1680d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1681d8b92400SNamhyung Kim 
16827d6a7e78SJiri Olsa 			start = trim(dummy_hpp.buf);
1683cb1fab91SNamhyung Kim 			ret = strlen(start);
1684cb1fab91SNamhyung Kim 
1685cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1686cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1687cb1fab91SNamhyung Kim 
1688d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1689d8b92400SNamhyung Kim 				break;
1690d8b92400SNamhyung Kim 		}
1691a61a22f6SNamhyung Kim 	}
1692d8b92400SNamhyung Kim 
1693d8b92400SNamhyung Kim 	return ret;
1694d8b92400SNamhyung Kim }
1695d8b92400SNamhyung Kim 
169601b4770dSJiri Olsa static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1697025bf7eaSArnaldo Carvalho de Melo {
169881a888feSJiri Olsa 	char headers[1024];
169981a888feSJiri Olsa 
1700d8b92400SNamhyung Kim 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1701d8b92400SNamhyung Kim 						   sizeof(headers));
170201b4770dSJiri Olsa 
1703025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1704025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
170526270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1706025bf7eaSArnaldo Carvalho de Melo }
1707025bf7eaSArnaldo Carvalho de Melo 
170801b4770dSJiri Olsa static void hists_browser__headers(struct hist_browser *browser)
170901b4770dSJiri Olsa {
171069705b35SJiri Olsa 	struct hists *hists = browser->hists;
171169705b35SJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
171269705b35SJiri Olsa 
171369705b35SJiri Olsa 	int line;
171469705b35SJiri Olsa 
171569705b35SJiri Olsa 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
171601b4770dSJiri Olsa 		char headers[1024];
171701b4770dSJiri Olsa 
171801b4770dSJiri Olsa 		hists_browser__scnprintf_headers(browser, headers,
171969705b35SJiri Olsa 						 sizeof(headers), line);
172001b4770dSJiri Olsa 
1721ef9ff601SArnaldo Carvalho de Melo 		ui_browser__gotorc_title(&browser->b, line, 0);
172201b4770dSJiri Olsa 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
172301b4770dSJiri Olsa 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
172401b4770dSJiri Olsa 	}
172569705b35SJiri Olsa }
172601b4770dSJiri Olsa 
172701b4770dSJiri Olsa static void hist_browser__show_headers(struct hist_browser *browser)
172801b4770dSJiri Olsa {
172901b4770dSJiri Olsa 	if (symbol_conf.report_hierarchy)
173001b4770dSJiri Olsa 		hists_browser__hierarchy_headers(browser);
173101b4770dSJiri Olsa 	else
173201b4770dSJiri Olsa 		hists_browser__headers(browser);
173301b4770dSJiri Olsa }
173401b4770dSJiri Olsa 
1735aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1736aca7a94dSNamhyung Kim {
1737aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1738aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1739aca7a94dSNamhyung Kim 
1740aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
1741aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
1742aca7a94dSNamhyung Kim 	}
1743aca7a94dSNamhyung Kim }
1744aca7a94dSNamhyung Kim 
174505e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1746aca7a94dSNamhyung Kim {
1747aca7a94dSNamhyung Kim 	unsigned row = 0;
1748aca7a94dSNamhyung Kim 	struct rb_node *nd;
174905e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1750aca7a94dSNamhyung Kim 
175194e87a8bSArnaldo Carvalho de Melo 	if (hb->show_headers)
1752025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1753025bf7eaSArnaldo Carvalho de Melo 
175405e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1755979d2cacSWang Nan 	hb->he_selection = NULL;
1756979d2cacSWang Nan 	hb->selection = NULL;
1757aca7a94dSNamhyung Kim 
1758d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1759aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
176014135663SNamhyung Kim 		float percent;
1761aca7a94dSNamhyung Kim 
1762d0506edbSNamhyung Kim 		if (h->filtered) {
1763d0506edbSNamhyung Kim 			/* let it move to sibling */
1764d0506edbSNamhyung Kim 			h->unfolded = false;
1765aca7a94dSNamhyung Kim 			continue;
1766d0506edbSNamhyung Kim 		}
1767aca7a94dSNamhyung Kim 
176814135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
1769064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1770064f1981SNamhyung Kim 			continue;
1771064f1981SNamhyung Kim 
1772d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1773d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
17742dbbe9f2SNamhyung Kim 								  h->depth);
177579dded87SNamhyung Kim 			if (row == browser->rows)
177679dded87SNamhyung Kim 				break;
177779dded87SNamhyung Kim 
177879dded87SNamhyung Kim 			if (h->has_no_entry) {
1779a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
178079dded87SNamhyung Kim 				row++;
178179dded87SNamhyung Kim 			}
1782d0506edbSNamhyung Kim 		} else {
1783aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1784d0506edbSNamhyung Kim 		}
1785d0506edbSNamhyung Kim 
178662c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1787aca7a94dSNamhyung Kim 			break;
1788aca7a94dSNamhyung Kim 	}
1789aca7a94dSNamhyung Kim 
179094e87a8bSArnaldo Carvalho de Melo 	return row;
1791aca7a94dSNamhyung Kim }
1792aca7a94dSNamhyung Kim 
1793064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1794064f1981SNamhyung Kim 					     float min_pcnt)
1795aca7a94dSNamhyung Kim {
1796aca7a94dSNamhyung Kim 	while (nd != NULL) {
1797aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
179814135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1799064f1981SNamhyung Kim 
1800c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1801aca7a94dSNamhyung Kim 			return nd;
1802aca7a94dSNamhyung Kim 
1803d0506edbSNamhyung Kim 		/*
1804d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1805d0506edbSNamhyung Kim 		 * So move to sibling node.
1806d0506edbSNamhyung Kim 		 */
1807d0506edbSNamhyung Kim 		if (rb_next(nd))
1808aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1809d0506edbSNamhyung Kim 		else
1810d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1811aca7a94dSNamhyung Kim 	}
1812aca7a94dSNamhyung Kim 
1813aca7a94dSNamhyung Kim 	return NULL;
1814aca7a94dSNamhyung Kim }
1815aca7a94dSNamhyung Kim 
1816064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1817064f1981SNamhyung Kim 						  float min_pcnt)
1818aca7a94dSNamhyung Kim {
1819aca7a94dSNamhyung Kim 	while (nd != NULL) {
1820aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
182114135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1822064f1981SNamhyung Kim 
1823064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1824aca7a94dSNamhyung Kim 			return nd;
1825aca7a94dSNamhyung Kim 
1826d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1827aca7a94dSNamhyung Kim 	}
1828aca7a94dSNamhyung Kim 
1829aca7a94dSNamhyung Kim 	return NULL;
1830aca7a94dSNamhyung Kim }
1831aca7a94dSNamhyung Kim 
183205e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1833aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1834aca7a94dSNamhyung Kim {
1835aca7a94dSNamhyung Kim 	struct hist_entry *h;
1836aca7a94dSNamhyung Kim 	struct rb_node *nd;
1837aca7a94dSNamhyung Kim 	bool first = true;
1838064f1981SNamhyung Kim 	struct hist_browser *hb;
1839064f1981SNamhyung Kim 
1840064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1841aca7a94dSNamhyung Kim 
184205e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1843aca7a94dSNamhyung Kim 		return;
1844aca7a94dSNamhyung Kim 
184505e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1846aca7a94dSNamhyung Kim 
1847aca7a94dSNamhyung Kim 	switch (whence) {
1848aca7a94dSNamhyung Kim 	case SEEK_SET:
1849064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
185014135663SNamhyung Kim 					   hb->min_pcnt);
1851aca7a94dSNamhyung Kim 		break;
1852aca7a94dSNamhyung Kim 	case SEEK_CUR:
185305e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1854aca7a94dSNamhyung Kim 		goto do_offset;
1855aca7a94dSNamhyung Kim 	case SEEK_END:
1856d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1857d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1858aca7a94dSNamhyung Kim 		first = false;
1859aca7a94dSNamhyung Kim 		break;
1860aca7a94dSNamhyung Kim 	default:
1861aca7a94dSNamhyung Kim 		return;
1862aca7a94dSNamhyung Kim 	}
1863aca7a94dSNamhyung Kim 
1864aca7a94dSNamhyung Kim 	/*
1865aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1866aca7a94dSNamhyung Kim 	 * row_offset:
1867aca7a94dSNamhyung Kim 	 */
186805e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1869aca7a94dSNamhyung Kim 	h->row_offset = 0;
1870aca7a94dSNamhyung Kim 
1871aca7a94dSNamhyung Kim 	/*
1872aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1873aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1874aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1875aca7a94dSNamhyung Kim 	 *
1876aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1877aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1878aca7a94dSNamhyung Kim 	 *
1879aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1880aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1881aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1882aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1883aca7a94dSNamhyung Kim 	 */
1884aca7a94dSNamhyung Kim do_offset:
1885837eeb75SWang Nan 	if (!nd)
1886837eeb75SWang Nan 		return;
1887837eeb75SWang Nan 
1888aca7a94dSNamhyung Kim 	if (offset > 0) {
1889aca7a94dSNamhyung Kim 		do {
1890aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1891d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1892aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1893aca7a94dSNamhyung Kim 				if (offset > remaining) {
1894aca7a94dSNamhyung Kim 					offset -= remaining;
1895aca7a94dSNamhyung Kim 					h->row_offset = 0;
1896aca7a94dSNamhyung Kim 				} else {
1897aca7a94dSNamhyung Kim 					h->row_offset += offset;
1898aca7a94dSNamhyung Kim 					offset = 0;
189905e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1900aca7a94dSNamhyung Kim 					break;
1901aca7a94dSNamhyung Kim 				}
1902aca7a94dSNamhyung Kim 			}
1903d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1904d0506edbSNamhyung Kim 						   hb->min_pcnt);
1905aca7a94dSNamhyung Kim 			if (nd == NULL)
1906aca7a94dSNamhyung Kim 				break;
1907aca7a94dSNamhyung Kim 			--offset;
190805e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1909aca7a94dSNamhyung Kim 		} while (offset != 0);
1910aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1911aca7a94dSNamhyung Kim 		while (1) {
1912aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1913d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1914aca7a94dSNamhyung Kim 				if (first) {
1915aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1916aca7a94dSNamhyung Kim 						offset += h->row_offset;
1917aca7a94dSNamhyung Kim 						h->row_offset = 0;
1918aca7a94dSNamhyung Kim 					} else {
1919aca7a94dSNamhyung Kim 						h->row_offset += offset;
1920aca7a94dSNamhyung Kim 						offset = 0;
192105e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1922aca7a94dSNamhyung Kim 						break;
1923aca7a94dSNamhyung Kim 					}
1924aca7a94dSNamhyung Kim 				} else {
1925aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1926aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1927aca7a94dSNamhyung Kim 						h->row_offset = 0;
1928aca7a94dSNamhyung Kim 					} else {
1929aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1930aca7a94dSNamhyung Kim 						offset = 0;
193105e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1932aca7a94dSNamhyung Kim 						break;
1933aca7a94dSNamhyung Kim 					}
1934aca7a94dSNamhyung Kim 				}
1935aca7a94dSNamhyung Kim 			}
1936aca7a94dSNamhyung Kim 
1937d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1938064f1981SNamhyung Kim 							hb->min_pcnt);
1939aca7a94dSNamhyung Kim 			if (nd == NULL)
1940aca7a94dSNamhyung Kim 				break;
1941aca7a94dSNamhyung Kim 			++offset;
194205e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1943aca7a94dSNamhyung Kim 			if (offset == 0) {
1944aca7a94dSNamhyung Kim 				/*
1945aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1946aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1947aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1948aca7a94dSNamhyung Kim 				 */
1949aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
1950d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
1951aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1952aca7a94dSNamhyung Kim 				break;
1953aca7a94dSNamhyung Kim 			}
1954aca7a94dSNamhyung Kim 			first = false;
1955aca7a94dSNamhyung Kim 		}
1956aca7a94dSNamhyung Kim 	} else {
195705e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1958aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1959aca7a94dSNamhyung Kim 		h->row_offset = 0;
1960aca7a94dSNamhyung Kim 	}
1961aca7a94dSNamhyung Kim }
1962aca7a94dSNamhyung Kim 
1963aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1964d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
1965d0506edbSNamhyung Kim 					   int level)
1966aff3f3f6SArnaldo Carvalho de Melo {
196739ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
196839ee533fSNamhyung Kim 		.fp = fp,
196939ee533fSNamhyung Kim 	};
1970aff3f3f6SArnaldo Carvalho de Melo 
1971d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
197239ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
197339ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
197439ee533fSNamhyung Kim 	return arg.printed;
1975aff3f3f6SArnaldo Carvalho de Melo }
1976aff3f3f6SArnaldo Carvalho de Melo 
1977aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1978aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1979aff3f3f6SArnaldo Carvalho de Melo {
1980aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1981aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1982aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
198326d8b338SNamhyung Kim 	struct perf_hpp hpp = {
198426d8b338SNamhyung Kim 		.buf = s,
198526d8b338SNamhyung Kim 		.size = sizeof(s),
198626d8b338SNamhyung Kim 	};
198726d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
198826d8b338SNamhyung Kim 	bool first = true;
198926d8b338SNamhyung Kim 	int ret;
1990aff3f3f6SArnaldo Carvalho de Melo 
1991fabd37b8SArnaldo Carvalho de Melo 	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
1992aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1993aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
19941b6b678eSArnaldo Carvalho de Melo 	}
1995aff3f3f6SArnaldo Carvalho de Melo 
1996f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1997361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
1998e67d49a7SNamhyung Kim 			continue;
1999e67d49a7SNamhyung Kim 
200026d8b338SNamhyung Kim 		if (!first) {
200126d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
200226d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
200326d8b338SNamhyung Kim 		} else
200426d8b338SNamhyung Kim 			first = false;
2005aff3f3f6SArnaldo Carvalho de Melo 
200626d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
200789fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
200826d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
200926d8b338SNamhyung Kim 	}
201089fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
2011aff3f3f6SArnaldo Carvalho de Melo 
2012aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
2013d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2014d0506edbSNamhyung Kim 
2015d0506edbSNamhyung Kim 	return printed;
2016d0506edbSNamhyung Kim }
2017d0506edbSNamhyung Kim 
2018d0506edbSNamhyung Kim 
2019d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2020d0506edbSNamhyung Kim 						 struct hist_entry *he,
2021325a6283SNamhyung Kim 						 FILE *fp, int level)
2022d0506edbSNamhyung Kim {
2023d0506edbSNamhyung Kim 	char s[8192];
2024d0506edbSNamhyung Kim 	int printed = 0;
2025d0506edbSNamhyung Kim 	char folded_sign = ' ';
2026d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
2027d0506edbSNamhyung Kim 		.buf = s,
2028d0506edbSNamhyung Kim 		.size = sizeof(s),
2029d0506edbSNamhyung Kim 	};
2030d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
2031325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
2032d0506edbSNamhyung Kim 	bool first = true;
2033d0506edbSNamhyung Kim 	int ret;
2034325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2035d0506edbSNamhyung Kim 
2036d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2037d0506edbSNamhyung Kim 
2038d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
2039d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
2040d0506edbSNamhyung Kim 
2041325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
2042325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2043325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
2044325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2045d0506edbSNamhyung Kim 		if (!first) {
2046d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2047d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
2048d0506edbSNamhyung Kim 		} else
2049d0506edbSNamhyung Kim 			first = false;
2050d0506edbSNamhyung Kim 
2051d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2052d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
2053d0506edbSNamhyung Kim 	}
2054d0506edbSNamhyung Kim 
2055d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2056d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
2057d0506edbSNamhyung Kim 
20581b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
20591b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
20601b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
20611b2dbbf4SNamhyung Kim 
2062d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2063d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
20641b2dbbf4SNamhyung Kim 	}
2065d0506edbSNamhyung Kim 
2066d0506edbSNamhyung Kim 	printed += fprintf(fp, "%s\n", rtrim(s));
2067d0506edbSNamhyung Kim 
2068d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
2069d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2070d0506edbSNamhyung Kim 							   he->depth + 1);
2071d0506edbSNamhyung Kim 	}
2072aff3f3f6SArnaldo Carvalho de Melo 
2073aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2074aff3f3f6SArnaldo Carvalho de Melo }
2075aff3f3f6SArnaldo Carvalho de Melo 
2076aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2077aff3f3f6SArnaldo Carvalho de Melo {
2078064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2079064f1981SNamhyung Kim 						   browser->min_pcnt);
2080aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2081aff3f3f6SArnaldo Carvalho de Melo 
2082aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
2083aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2084aff3f3f6SArnaldo Carvalho de Melo 
2085d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
2086d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2087d0506edbSNamhyung Kim 									 h, fp,
2088325a6283SNamhyung Kim 									 h->depth);
2089d0506edbSNamhyung Kim 		} else {
2090aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
2091d0506edbSNamhyung Kim 		}
2092d0506edbSNamhyung Kim 
2093d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2094d0506edbSNamhyung Kim 					   browser->min_pcnt);
2095aff3f3f6SArnaldo Carvalho de Melo 	}
2096aff3f3f6SArnaldo Carvalho de Melo 
2097aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2098aff3f3f6SArnaldo Carvalho de Melo }
2099aff3f3f6SArnaldo Carvalho de Melo 
2100aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2101aff3f3f6SArnaldo Carvalho de Melo {
2102aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2103aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2104aff3f3f6SArnaldo Carvalho de Melo 
2105aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2106aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2107aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2108aff3f3f6SArnaldo Carvalho de Melo 			break;
2109aff3f3f6SArnaldo Carvalho de Melo 		/*
2110aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2111aff3f3f6SArnaldo Carvalho de Melo  		 */
2112aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2113aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2114aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2115aff3f3f6SArnaldo Carvalho de Melo 		}
2116aff3f3f6SArnaldo Carvalho de Melo 	}
2117aff3f3f6SArnaldo Carvalho de Melo 
2118aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2119aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2120aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
2121c8b5f2c9SArnaldo Carvalho de Melo 		const char *err = str_error_r(errno, bf, sizeof(bf));
21224cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2123aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2124aff3f3f6SArnaldo Carvalho de Melo 	}
2125aff3f3f6SArnaldo Carvalho de Melo 
2126aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2127aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2128aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2129aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2130aff3f3f6SArnaldo Carvalho de Melo 
2131aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2132aff3f3f6SArnaldo Carvalho de Melo }
2133aff3f3f6SArnaldo Carvalho de Melo 
2134fcd86426SJiri Olsa void hist_browser__init(struct hist_browser *browser,
2135fcd86426SJiri Olsa 			struct hists *hists)
2136aca7a94dSNamhyung Kim {
2137b1c7a8f7SJiri Olsa 	struct perf_hpp_fmt *fmt;
2138b1c7a8f7SJiri Olsa 
213905e8b080SArnaldo Carvalho de Melo 	browser->hists			= hists;
214005e8b080SArnaldo Carvalho de Melo 	browser->b.refresh		= hist_browser__refresh;
2141357cfff1SArnaldo Carvalho de Melo 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
214205e8b080SArnaldo Carvalho de Melo 	browser->b.seek			= ui_browser__hists_seek;
214305e8b080SArnaldo Carvalho de Melo 	browser->b.use_navkeypressed	= true;
2144c8302367SJiri Olsa 	browser->show_headers		= symbol_conf.show_hist_headers;
2145ef9ff601SArnaldo Carvalho de Melo 	hist_browser__set_title_space(browser);
2146b1c7a8f7SJiri Olsa 
21478a06b0beSNamhyung Kim 	if (symbol_conf.report_hierarchy) {
21488a06b0beSNamhyung Kim 		struct perf_hpp_list_node *fmt_node;
21498a06b0beSNamhyung Kim 
21508a06b0beSNamhyung Kim 		/* count overhead columns (in the first node) */
21518a06b0beSNamhyung Kim 		fmt_node = list_first_entry(&hists->hpp_formats,
21528a06b0beSNamhyung Kim 					    struct perf_hpp_list_node, list);
21538a06b0beSNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
21548a06b0beSNamhyung Kim 			++browser->b.columns;
21558a06b0beSNamhyung Kim 
21568a06b0beSNamhyung Kim 		/* add a single column for whole hierarchy sort keys*/
21578a06b0beSNamhyung Kim 		++browser->b.columns;
21588a06b0beSNamhyung Kim 	} else {
2159e3b60bc9SNamhyung Kim 		hists__for_each_format(hists, fmt)
2160b1c7a8f7SJiri Olsa 			++browser->b.columns;
21618a06b0beSNamhyung Kim 	}
2162e3b60bc9SNamhyung Kim 
2163e3b60bc9SNamhyung Kim 	hists__reset_column_width(hists);
2164aca7a94dSNamhyung Kim }
2165aca7a94dSNamhyung Kim 
2166fcd86426SJiri Olsa struct hist_browser *hist_browser__new(struct hists *hists)
2167fcd86426SJiri Olsa {
2168fcd86426SJiri Olsa 	struct hist_browser *browser = zalloc(sizeof(*browser));
2169fcd86426SJiri Olsa 
2170fcd86426SJiri Olsa 	if (browser)
2171fcd86426SJiri Olsa 		hist_browser__init(browser, hists);
2172fcd86426SJiri Olsa 
217305e8b080SArnaldo Carvalho de Melo 	return browser;
2174aca7a94dSNamhyung Kim }
2175aca7a94dSNamhyung Kim 
2176a6ec894dSJiri Olsa static struct hist_browser *
2177a6ec894dSJiri Olsa perf_evsel_browser__new(struct perf_evsel *evsel,
2178a6ec894dSJiri Olsa 			struct hist_browser_timer *hbt,
2179cd0cccbaSArnaldo Carvalho de Melo 			struct perf_env *env,
2180cd0cccbaSArnaldo Carvalho de Melo 			struct annotation_options *annotation_opts)
2181a6ec894dSJiri Olsa {
2182a6ec894dSJiri Olsa 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2183a6ec894dSJiri Olsa 
2184a6ec894dSJiri Olsa 	if (browser) {
2185a6ec894dSJiri Olsa 		browser->hbt   = hbt;
2186a6ec894dSJiri Olsa 		browser->env   = env;
2187f016d24aSArnaldo Carvalho de Melo 		browser->title = hists_browser__scnprintf_title;
2188cd0cccbaSArnaldo Carvalho de Melo 		browser->annotation_opts = annotation_opts;
2189a6ec894dSJiri Olsa 	}
2190a6ec894dSJiri Olsa 	return browser;
2191a6ec894dSJiri Olsa }
2192a6ec894dSJiri Olsa 
2193dabd2012SJiri Olsa void hist_browser__delete(struct hist_browser *browser)
2194aca7a94dSNamhyung Kim {
219505e8b080SArnaldo Carvalho de Melo 	free(browser);
2196aca7a94dSNamhyung Kim }
2197aca7a94dSNamhyung Kim 
219805e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2199aca7a94dSNamhyung Kim {
220005e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2201aca7a94dSNamhyung Kim }
2202aca7a94dSNamhyung Kim 
220305e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2204aca7a94dSNamhyung Kim {
220505e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2206aca7a94dSNamhyung Kim }
2207aca7a94dSNamhyung Kim 
22081e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
22091e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
22101e378ebdSTaeung Song {
22111e378ebdSTaeung Song 	return timer == NULL;
22121e378ebdSTaeung Song }
22131e378ebdSTaeung Song 
2214967a464aSArnaldo Carvalho de Melo static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2215967a464aSArnaldo Carvalho de Melo {
2216967a464aSArnaldo Carvalho de Melo 	struct hist_browser_timer *hbt = browser->hbt;
2217967a464aSArnaldo Carvalho de Melo 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2218967a464aSArnaldo Carvalho de Melo 
22191e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
22201e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
22211e378ebdSTaeung Song 
2222*d24e3c98SJiri Olsa 		printed += scnprintf(bf + printed, size - printed,
2223*d24e3c98SJiri Olsa 				     " lost: %" PRIu64 "/%" PRIu64,
2224*d24e3c98SJiri Olsa 				     top->lost, top->lost_total);
2225*d24e3c98SJiri Olsa 
22261e378ebdSTaeung Song 		if (top->zero)
22271e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
22281e378ebdSTaeung Song 	}
22291e378ebdSTaeung Song 
2230aca7a94dSNamhyung Kim 	return printed;
2231aca7a94dSNamhyung Kim }
2232aca7a94dSNamhyung Kim 
2233aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2234aca7a94dSNamhyung Kim {
2235aca7a94dSNamhyung Kim 	int i;
2236aca7a94dSNamhyung Kim 
223704662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
223804662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2239aca7a94dSNamhyung Kim }
2240aca7a94dSNamhyung Kim 
2241341487abSFeng Tang /*
2242341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2243341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2244341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2245341487abSFeng Tang  */
2246341487abSFeng Tang static bool is_input_name_malloced = false;
2247341487abSFeng Tang 
2248341487abSFeng Tang static int switch_data_file(void)
2249341487abSFeng Tang {
2250341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2251341487abSFeng Tang 	DIR *pwd_dir;
2252341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2253341487abSFeng Tang 	struct dirent *dent;
2254341487abSFeng Tang 
2255341487abSFeng Tang 	pwd = getenv("PWD");
2256341487abSFeng Tang 	if (!pwd)
2257341487abSFeng Tang 		return ret;
2258341487abSFeng Tang 
2259341487abSFeng Tang 	pwd_dir = opendir(pwd);
2260341487abSFeng Tang 	if (!pwd_dir)
2261341487abSFeng Tang 		return ret;
2262341487abSFeng Tang 
2263341487abSFeng Tang 	memset(options, 0, sizeof(options));
22643ef5b402SChangbin Du 	memset(abs_path, 0, sizeof(abs_path));
2265341487abSFeng Tang 
2266341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2267341487abSFeng Tang 		char path[PATH_MAX];
2268341487abSFeng Tang 		u64 magic;
2269341487abSFeng Tang 		char *name = dent->d_name;
2270341487abSFeng Tang 		FILE *file;
2271341487abSFeng Tang 
2272341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2273341487abSFeng Tang 			continue;
2274341487abSFeng Tang 
2275341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2276341487abSFeng Tang 
2277341487abSFeng Tang 		file = fopen(path, "r");
2278341487abSFeng Tang 		if (!file)
2279341487abSFeng Tang 			continue;
2280341487abSFeng Tang 
2281341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2282341487abSFeng Tang 			goto close_file_and_continue;
2283341487abSFeng Tang 
2284341487abSFeng Tang 		if (is_perf_magic(magic)) {
2285341487abSFeng Tang 			options[nr_options] = strdup(name);
2286341487abSFeng Tang 			if (!options[nr_options])
2287341487abSFeng Tang 				goto close_file_and_continue;
2288341487abSFeng Tang 
2289341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2290341487abSFeng Tang 			if (!abs_path[nr_options]) {
229174cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2292341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2293341487abSFeng Tang 				fclose(file);
2294341487abSFeng Tang 				break;
2295341487abSFeng Tang 			}
2296341487abSFeng Tang 
2297341487abSFeng Tang 			nr_options++;
2298341487abSFeng Tang 		}
2299341487abSFeng Tang 
2300341487abSFeng Tang close_file_and_continue:
2301341487abSFeng Tang 		fclose(file);
2302341487abSFeng Tang 		if (nr_options >= 32) {
2303341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2304341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2305341487abSFeng Tang 			break;
2306341487abSFeng Tang 		}
2307341487abSFeng Tang 	}
2308341487abSFeng Tang 	closedir(pwd_dir);
2309341487abSFeng Tang 
2310341487abSFeng Tang 	if (nr_options) {
2311341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
2312341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2313341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2314341487abSFeng Tang 			if (tmp) {
2315341487abSFeng Tang 				if (is_input_name_malloced)
2316341487abSFeng Tang 					free((void *)input_name);
2317341487abSFeng Tang 				input_name = tmp;
2318341487abSFeng Tang 				is_input_name_malloced = true;
2319341487abSFeng Tang 				ret = 0;
2320341487abSFeng Tang 			} else
2321341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2322341487abSFeng Tang 		}
2323341487abSFeng Tang 	}
2324341487abSFeng Tang 
2325341487abSFeng Tang 	free_popup_options(options, nr_options);
2326341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2327341487abSFeng Tang 	return ret;
2328341487abSFeng Tang }
2329341487abSFeng Tang 
2330ea7cd592SNamhyung Kim struct popup_action {
2331ea7cd592SNamhyung Kim 	struct thread 		*thread;
2332ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
233384734b06SKan Liang 	int			socket;
2334ea7cd592SNamhyung Kim 
2335ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2336ea7cd592SNamhyung Kim };
2337ea7cd592SNamhyung Kim 
2338bc7cad42SNamhyung Kim static int
2339ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2340bc7cad42SNamhyung Kim {
2341bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
2342bc7cad42SNamhyung Kim 	struct annotation *notes;
2343bc7cad42SNamhyung Kim 	struct hist_entry *he;
2344bc7cad42SNamhyung Kim 	int err;
2345bc7cad42SNamhyung Kim 
2346f178fd2dSArnaldo Carvalho de Melo 	if (!browser->annotation_opts->objdump_path &&
2347f178fd2dSArnaldo Carvalho de Melo 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2348bc7cad42SNamhyung Kim 		return 0;
2349bc7cad42SNamhyung Kim 
2350ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2351bc7cad42SNamhyung Kim 	if (!notes->src)
2352bc7cad42SNamhyung Kim 		return 0;
2353bc7cad42SNamhyung Kim 
2354bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
2355cd0cccbaSArnaldo Carvalho de Melo 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2356cd0cccbaSArnaldo Carvalho de Melo 				       browser->annotation_opts);
2357bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2358bc7cad42SNamhyung Kim 	/*
2359bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2360bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2361bc7cad42SNamhyung Kim 	 */
2362bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2363bc7cad42SNamhyung Kim 		return 1;
2364bc7cad42SNamhyung Kim 
2365bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2366bc7cad42SNamhyung Kim 	if (err)
2367bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2368bc7cad42SNamhyung Kim 	return 0;
2369bc7cad42SNamhyung Kim }
2370bc7cad42SNamhyung Kim 
2371bc7cad42SNamhyung Kim static int
2372ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2373ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
2374ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
2375bc7cad42SNamhyung Kim {
2376ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
2377ea7cd592SNamhyung Kim 		return 0;
2378ea7cd592SNamhyung Kim 
2379ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2380ea7cd592SNamhyung Kim 		return 0;
2381ea7cd592SNamhyung Kim 
2382ea7cd592SNamhyung Kim 	act->ms.map = map;
2383ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2384ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2385ea7cd592SNamhyung Kim 	return 1;
2386ea7cd592SNamhyung Kim }
2387ea7cd592SNamhyung Kim 
2388ea7cd592SNamhyung Kim static int
2389ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2390ea7cd592SNamhyung Kim {
2391ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2392ea7cd592SNamhyung Kim 
23937cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
23947cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2395599a2f38SNamhyung Kim 		return 0;
2396599a2f38SNamhyung Kim 
2397bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2398bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2399bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2400bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2401bc7cad42SNamhyung Kim 		ui_helpline__pop();
2402bc7cad42SNamhyung Kim 	} else {
2403fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
24047727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2405bc7cad42SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "",
2406bc7cad42SNamhyung Kim 					   thread->tid);
24076962ccb3SNamhyung Kim 		} else {
24086962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
24096962ccb3SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "");
24106962ccb3SNamhyung Kim 		}
24116962ccb3SNamhyung Kim 
2412bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2413bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2414bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2415bc7cad42SNamhyung Kim 	}
2416bc7cad42SNamhyung Kim 
2417bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2418bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2419bc7cad42SNamhyung Kim 	return 0;
2420bc7cad42SNamhyung Kim }
2421bc7cad42SNamhyung Kim 
2422bc7cad42SNamhyung Kim static int
2423ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2424ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2425bc7cad42SNamhyung Kim {
24266962ccb3SNamhyung Kim 	int ret;
24276962ccb3SNamhyung Kim 
24287cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
24297cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2430ea7cd592SNamhyung Kim 		return 0;
2431ea7cd592SNamhyung Kim 
2432fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
24336962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2434ea7cd592SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
2435ea7cd592SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "",
24366962ccb3SNamhyung Kim 			       thread->tid);
24376962ccb3SNamhyung Kim 	} else {
24386962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s thread",
24396962ccb3SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
24406962ccb3SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "");
24416962ccb3SNamhyung Kim 	}
24426962ccb3SNamhyung Kim 	if (ret < 0)
2443ea7cd592SNamhyung Kim 		return 0;
2444ea7cd592SNamhyung Kim 
2445ea7cd592SNamhyung Kim 	act->thread = thread;
2446ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2447ea7cd592SNamhyung Kim 	return 1;
2448ea7cd592SNamhyung Kim }
2449ea7cd592SNamhyung Kim 
2450ea7cd592SNamhyung Kim static int
2451ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2452ea7cd592SNamhyung Kim {
2453045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
2454ea7cd592SNamhyung Kim 
245569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2456599a2f38SNamhyung Kim 		return 0;
2457599a2f38SNamhyung Kim 
2458bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2459bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2460bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2461bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2462bc7cad42SNamhyung Kim 		ui_helpline__pop();
2463bc7cad42SNamhyung Kim 	} else {
24647727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2465045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2466045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
2467bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2468bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2469bc7cad42SNamhyung Kim 	}
2470bc7cad42SNamhyung Kim 
2471bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2472bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2473bc7cad42SNamhyung Kim 	return 0;
2474bc7cad42SNamhyung Kim }
2475bc7cad42SNamhyung Kim 
2476bc7cad42SNamhyung Kim static int
2477ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2478045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2479bc7cad42SNamhyung Kim {
248069849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2481ea7cd592SNamhyung Kim 		return 0;
2482ea7cd592SNamhyung Kim 
2483ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
2484ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
2485045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2486ea7cd592SNamhyung Kim 		return 0;
2487ea7cd592SNamhyung Kim 
2488045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2489ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2490ea7cd592SNamhyung Kim 	return 1;
2491ea7cd592SNamhyung Kim }
2492ea7cd592SNamhyung Kim 
2493ea7cd592SNamhyung Kim static int
2494ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2495ea7cd592SNamhyung Kim 	      struct popup_action *act)
2496ea7cd592SNamhyung Kim {
2497ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2498bc7cad42SNamhyung Kim 	return 0;
2499bc7cad42SNamhyung Kim }
2500bc7cad42SNamhyung Kim 
2501bc7cad42SNamhyung Kim static int
250269849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2503ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2504ea7cd592SNamhyung Kim {
250569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2506ea7cd592SNamhyung Kim 		return 0;
2507ea7cd592SNamhyung Kim 
2508ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2509ea7cd592SNamhyung Kim 		return 0;
2510ea7cd592SNamhyung Kim 
2511ea7cd592SNamhyung Kim 	act->ms.map = map;
2512ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2513ea7cd592SNamhyung Kim 	return 1;
2514ea7cd592SNamhyung Kim }
2515ea7cd592SNamhyung Kim 
2516ea7cd592SNamhyung Kim static int
2517bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2518ea7cd592SNamhyung Kim 	      struct popup_action *act)
2519bc7cad42SNamhyung Kim {
2520bc7cad42SNamhyung Kim 	char script_opt[64];
2521bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
2522bc7cad42SNamhyung Kim 
2523ea7cd592SNamhyung Kim 	if (act->thread) {
2524bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2525ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2526ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
2527bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2528ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2529bc7cad42SNamhyung Kim 	}
2530bc7cad42SNamhyung Kim 
2531bc7cad42SNamhyung Kim 	script_browse(script_opt);
2532bc7cad42SNamhyung Kim 	return 0;
2533bc7cad42SNamhyung Kim }
2534bc7cad42SNamhyung Kim 
2535bc7cad42SNamhyung Kim static int
2536ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
2537ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
2538ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
2539ea7cd592SNamhyung Kim {
2540ea7cd592SNamhyung Kim 	if (thread) {
2541ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2542ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
2543ea7cd592SNamhyung Kim 			return 0;
2544ea7cd592SNamhyung Kim 	} else if (sym) {
2545ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2546ea7cd592SNamhyung Kim 			     sym->name) < 0)
2547ea7cd592SNamhyung Kim 			return 0;
2548ea7cd592SNamhyung Kim 	} else {
2549ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2550ea7cd592SNamhyung Kim 			return 0;
2551ea7cd592SNamhyung Kim 	}
2552ea7cd592SNamhyung Kim 
2553ea7cd592SNamhyung Kim 	act->thread = thread;
2554ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2555ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2556ea7cd592SNamhyung Kim 	return 1;
2557ea7cd592SNamhyung Kim }
2558ea7cd592SNamhyung Kim 
2559ea7cd592SNamhyung Kim static int
2560ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2561ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2562bc7cad42SNamhyung Kim {
2563bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2564bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2565bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2566ea7cd592SNamhyung Kim 		return 0;
2567bc7cad42SNamhyung Kim 	}
2568bc7cad42SNamhyung Kim 
2569bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2570bc7cad42SNamhyung Kim }
2571bc7cad42SNamhyung Kim 
2572ea7cd592SNamhyung Kim static int
2573ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2574ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2575ea7cd592SNamhyung Kim {
2576ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2577ea7cd592SNamhyung Kim 		return 0;
2578ea7cd592SNamhyung Kim 
2579ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2580ea7cd592SNamhyung Kim 		return 0;
2581ea7cd592SNamhyung Kim 
2582ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2583ea7cd592SNamhyung Kim 	return 1;
2584ea7cd592SNamhyung Kim }
2585ea7cd592SNamhyung Kim 
2586ea7cd592SNamhyung Kim static int
2587ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2588ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2589ea7cd592SNamhyung Kim {
2590ea7cd592SNamhyung Kim 	return 0;
2591ea7cd592SNamhyung Kim }
2592ea7cd592SNamhyung Kim 
2593ea7cd592SNamhyung Kim static int
2594ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2595ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2596ea7cd592SNamhyung Kim {
2597ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2598ea7cd592SNamhyung Kim 		return 0;
2599ea7cd592SNamhyung Kim 
2600ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2601ea7cd592SNamhyung Kim 	return 1;
2602ea7cd592SNamhyung Kim }
2603ea7cd592SNamhyung Kim 
260484734b06SKan Liang static int
260584734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
260684734b06SKan Liang {
260735a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2608599a2f38SNamhyung Kim 		return 0;
2609599a2f38SNamhyung Kim 
261084734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
261184734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
261284734b06SKan Liang 		browser->hists->socket_filter = -1;
261384734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
261484734b06SKan Liang 	} else {
261584734b06SKan Liang 		browser->hists->socket_filter = act->socket;
261684734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
261784734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
261884734b06SKan Liang 	}
261984734b06SKan Liang 
262084734b06SKan Liang 	hists__filter_by_socket(browser->hists);
262184734b06SKan Liang 	hist_browser__reset(browser);
262284734b06SKan Liang 	return 0;
262384734b06SKan Liang }
262484734b06SKan Liang 
262584734b06SKan Liang static int
262684734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
262784734b06SKan Liang 	       char **optstr, int socket_id)
262884734b06SKan Liang {
262935a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
263084734b06SKan Liang 		return 0;
263184734b06SKan Liang 
263284734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
263384734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
263484734b06SKan Liang 		     socket_id) < 0)
263584734b06SKan Liang 		return 0;
263684734b06SKan Liang 
263784734b06SKan Liang 	act->socket = socket_id;
263884734b06SKan Liang 	act->fn = do_zoom_socket;
263984734b06SKan Liang 	return 1;
264084734b06SKan Liang }
264184734b06SKan Liang 
2642112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2643064f1981SNamhyung Kim {
2644064f1981SNamhyung Kim 	u64 nr_entries = 0;
2645064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2646064f1981SNamhyung Kim 
2647f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2648268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2649268397cbSNamhyung Kim 		return;
2650268397cbSNamhyung Kim 	}
2651268397cbSNamhyung Kim 
265214135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2653064f1981SNamhyung Kim 		nr_entries++;
2654f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2655064f1981SNamhyung Kim 	}
2656064f1981SNamhyung Kim 
2657112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2658f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2659064f1981SNamhyung Kim }
2660341487abSFeng Tang 
2661b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2662b62e8dfcSNamhyung Kim 					       double percent)
2663b62e8dfcSNamhyung Kim {
2664b62e8dfcSNamhyung Kim 	struct hist_entry *he;
2665b62e8dfcSNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2666b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2667b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2668b62e8dfcSNamhyung Kim 
2669b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2670b62e8dfcSNamhyung Kim 
2671b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2672b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2673b62e8dfcSNamhyung Kim 
267479dded87SNamhyung Kim 		if (he->has_no_entry) {
267579dded87SNamhyung Kim 			he->has_no_entry = false;
267679dded87SNamhyung Kim 			he->nr_rows = 0;
267779dded87SNamhyung Kim 		}
267879dded87SNamhyung Kim 
2679fabd37b8SArnaldo Carvalho de Melo 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2680d0506edbSNamhyung Kim 			goto next;
2681d0506edbSNamhyung Kim 
2682b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2683b62e8dfcSNamhyung Kim 			total = he->stat.period;
2684b62e8dfcSNamhyung Kim 
2685b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2686b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2687b62e8dfcSNamhyung Kim 
2688b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2689b62e8dfcSNamhyung Kim 		}
2690b62e8dfcSNamhyung Kim 
2691b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2692b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2693b62e8dfcSNamhyung Kim 
2694d0506edbSNamhyung Kim next:
2695201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2696d0506edbSNamhyung Kim 
2697b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2698b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2699492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2700b62e8dfcSNamhyung Kim 	}
2701b62e8dfcSNamhyung Kim }
2702b62e8dfcSNamhyung Kim 
2703aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2704dd00d486SJiri Olsa 				    const char *helpline,
2705aca7a94dSNamhyung Kim 				    bool left_exits,
270668d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2707064f1981SNamhyung Kim 				    float min_pcnt,
270806cc1a47SKan Liang 				    struct perf_env *env,
2709cd0cccbaSArnaldo Carvalho de Melo 				    bool warn_lost_event,
2710cd0cccbaSArnaldo Carvalho de Melo 				    struct annotation_options *annotation_opts)
2711aca7a94dSNamhyung Kim {
27124ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2713cd0cccbaSArnaldo Carvalho de Melo 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2714aca7a94dSNamhyung Kim 	struct branch_info *bi;
2715f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2716f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2717ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2718aca7a94dSNamhyung Kim 	int nr_options = 0;
2719aca7a94dSNamhyung Kim 	int key = -1;
2720aca7a94dSNamhyung Kim 	char buf[64];
27219783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2722aca7a94dSNamhyung Kim 
2723e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2724e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2725e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2726e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
27276a02f06eSAndi Kleen 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2728e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2729e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2730e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
27317727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
27327727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
2733e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2734e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2735e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2736e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2737105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2738025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2739b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
274031eb4360SNamhyung Kim 	"m             Display context menu\n"				\
274184734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2742e8e684a5SNamhyung Kim 
2743e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
2744e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
27456dd60135SNamhyung Kim 	"i             Show header information\n"
2746e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2747e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2748e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2749e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2750e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2751e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2752e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2753e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2754e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2755e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
275642337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2757fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2758e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2759e8e684a5SNamhyung Kim 
2760aca7a94dSNamhyung Kim 	if (browser == NULL)
2761aca7a94dSNamhyung Kim 		return -1;
2762aca7a94dSNamhyung Kim 
2763ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2764ed426915SNamhyung Kim 	SLang_reset_tty();
2765ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2766ed426915SNamhyung Kim 
276703905048SNamhyung Kim 	if (min_pcnt)
2768064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2769112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2770064f1981SNamhyung Kim 
277184734b06SKan Liang 	browser->pstack = pstack__new(3);
277201f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2773aca7a94dSNamhyung Kim 		goto out;
2774aca7a94dSNamhyung Kim 
2775aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2776aca7a94dSNamhyung Kim 
2777aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2778ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2779aca7a94dSNamhyung Kim 
27805b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
27815b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
27825b591669SNamhyung Kim 
2783aca7a94dSNamhyung Kim 	while (1) {
2784f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2785045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
2786ea7cd592SNamhyung Kim 		int choice = 0;
278784734b06SKan Liang 		int socked_id = -1;
2788aca7a94dSNamhyung Kim 
2789aca7a94dSNamhyung Kim 		nr_options = 0;
2790aca7a94dSNamhyung Kim 
279106cc1a47SKan Liang 		key = hist_browser__run(browser, helpline,
279206cc1a47SKan Liang 					warn_lost_event);
2793aca7a94dSNamhyung Kim 
2794aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
2795aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
2796045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
279784734b06SKan Liang 			socked_id = browser->he_selection->socket;
2798aca7a94dSNamhyung Kim 		}
2799aca7a94dSNamhyung Kim 		switch (key) {
2800aca7a94dSNamhyung Kim 		case K_TAB:
2801aca7a94dSNamhyung Kim 		case K_UNTAB:
2802aca7a94dSNamhyung Kim 			if (nr_events == 1)
2803aca7a94dSNamhyung Kim 				continue;
2804aca7a94dSNamhyung Kim 			/*
2805aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
2806aca7a94dSNamhyung Kim 			 * go to the next or previous
2807aca7a94dSNamhyung Kim 			 */
2808aca7a94dSNamhyung Kim 			goto out_free_stack;
2809aca7a94dSNamhyung Kim 		case 'a':
28102e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
2811aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
2812aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
2813aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
2814aca7a94dSNamhyung Kim 				continue;
2815aca7a94dSNamhyung Kim 			}
2816aca7a94dSNamhyung Kim 
2817aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
2818aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
2819aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
2820aca7a94dSNamhyung Kim 				continue;
2821bc7cad42SNamhyung Kim 
2822ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
2823ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
2824ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
2825bc7cad42SNamhyung Kim 			continue;
2826aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
2827aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
2828aff3f3f6SArnaldo Carvalho de Melo 			continue;
2829aca7a94dSNamhyung Kim 		case 'd':
2830fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
2831ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
2832bc7cad42SNamhyung Kim 			continue;
2833a7cb8863SArnaldo Carvalho de Melo 		case 'V':
283421e8c810SAlexis Berlemont 			verbose = (verbose + 1) % 4;
283521e8c810SAlexis Berlemont 			browser->show_dso = verbose > 0;
283621e8c810SAlexis Berlemont 			ui_helpline__fpush("Verbosity level set to %d\n",
283721e8c810SAlexis Berlemont 					   verbose);
2838a7cb8863SArnaldo Carvalho de Melo 			continue;
2839aca7a94dSNamhyung Kim 		case 't':
2840ea7cd592SNamhyung Kim 			actions->thread = thread;
2841ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
2842bc7cad42SNamhyung Kim 			continue;
284384734b06SKan Liang 		case 'S':
284484734b06SKan Liang 			actions->socket = socked_id;
284584734b06SKan Liang 			do_zoom_socket(browser, actions);
284684734b06SKan Liang 			continue;
28475a5626b1SArnaldo Carvalho de Melo 		case '/':
2848aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
28494aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
28504aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
2851aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2852aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
285305e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
285405e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
2855aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
2856aca7a94dSNamhyung Kim 			}
2857aca7a94dSNamhyung Kim 			continue;
2858cdbab7c2SFeng Tang 		case 'r':
2859ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
2860ea7cd592SNamhyung Kim 				actions->thread = NULL;
2861ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
2862ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
2863ea7cd592SNamhyung Kim 			}
2864c77d8d70SFeng Tang 			continue;
2865341487abSFeng Tang 		case 's':
2866bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
2867ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
2868bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
2869bc7cad42SNamhyung Kim 					goto out_free_stack;
2870bc7cad42SNamhyung Kim 			}
2871341487abSFeng Tang 			continue;
28726dd60135SNamhyung Kim 		case 'i':
28736dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
28746dd60135SNamhyung Kim 			if (env->arch)
28756dd60135SNamhyung Kim 				tui__header_window(env);
28766dd60135SNamhyung Kim 			continue;
2877105eb30fSNamhyung Kim 		case 'F':
2878105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
2879105eb30fSNamhyung Kim 			continue;
288042337a22SNamhyung Kim 		case 'z':
288142337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
288242337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
288342337a22SNamhyung Kim 
288442337a22SNamhyung Kim 				top->zero = !top->zero;
288542337a22SNamhyung Kim 			}
288642337a22SNamhyung Kim 			continue;
2887b62e8dfcSNamhyung Kim 		case 'L':
2888b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
2889b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
2890b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2891b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
2892b62e8dfcSNamhyung Kim 				char *end;
2893b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
2894b62e8dfcSNamhyung Kim 
2895b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
2896b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
2897b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
2898b62e8dfcSNamhyung Kim 					continue;
2899b62e8dfcSNamhyung Kim 				}
2900b62e8dfcSNamhyung Kim 
2901b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
2902b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
2903b62e8dfcSNamhyung Kim 			}
2904b62e8dfcSNamhyung Kim 			continue;
2905aca7a94dSNamhyung Kim 		case K_F1:
2906aca7a94dSNamhyung Kim 		case 'h':
2907aca7a94dSNamhyung Kim 		case '?':
2908aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
2909e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
2910aca7a94dSNamhyung Kim 			continue;
2911aca7a94dSNamhyung Kim 		case K_ENTER:
2912aca7a94dSNamhyung Kim 		case K_RIGHT:
291331eb4360SNamhyung Kim 		case 'm':
2914aca7a94dSNamhyung Kim 			/* menu */
2915aca7a94dSNamhyung Kim 			break;
291663ab1749SArnaldo Carvalho de Melo 		case K_ESC:
2917aca7a94dSNamhyung Kim 		case K_LEFT: {
2918aca7a94dSNamhyung Kim 			const void *top;
2919aca7a94dSNamhyung Kim 
292001f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
2921aca7a94dSNamhyung Kim 				/*
2922aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
2923aca7a94dSNamhyung Kim 				 */
2924aca7a94dSNamhyung Kim 				if (left_exits)
2925aca7a94dSNamhyung Kim 					goto out_free_stack;
292663ab1749SArnaldo Carvalho de Melo 
292763ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
292863ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
292963ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
293063ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
293163ab1749SArnaldo Carvalho de Melo 
2932aca7a94dSNamhyung Kim 				continue;
2933aca7a94dSNamhyung Kim 			}
29346422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
2935bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
29366422184bSNamhyung Kim 				/*
29376422184bSNamhyung Kim 				 * No need to set actions->dso here since
29386422184bSNamhyung Kim 				 * it's just to remove the current filter.
29396422184bSNamhyung Kim 				 * Ditto for thread below.
29406422184bSNamhyung Kim 				 */
29416422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
294284734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
29436422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
294484734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
294584734b06SKan Liang 				do_zoom_socket(browser, actions);
294684734b06SKan Liang 			}
2947aca7a94dSNamhyung Kim 			continue;
2948aca7a94dSNamhyung Kim 		}
2949aca7a94dSNamhyung Kim 		case 'q':
2950aca7a94dSNamhyung Kim 		case CTRL('c'):
2951516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
2952fbb7997eSArnaldo Carvalho de Melo 		case 'f':
295313d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
295413d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
295513d1e536SNamhyung Kim 
295613d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
295713d1e536SNamhyung Kim 				/*
295813d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
295913d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
296013d1e536SNamhyung Kim 				 */
296113d1e536SNamhyung Kim 				if (top->evlist->enabled) {
296213d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
296313d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
296413d1e536SNamhyung Kim 				} else {
296513d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
296613d1e536SNamhyung Kim 					hbt->refresh = 0;
296713d1e536SNamhyung Kim 				}
296813d1e536SNamhyung Kim 				continue;
296913d1e536SNamhyung Kim 			}
29703e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
2971aca7a94dSNamhyung Kim 		default:
29723e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
2973aca7a94dSNamhyung Kim 			continue;
2974aca7a94dSNamhyung Kim 		}
2975aca7a94dSNamhyung Kim 
29762e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
29770ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
29780ba332f7SArnaldo Carvalho de Melo 
297955369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
2980aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
29810ba332f7SArnaldo Carvalho de Melo 
29820ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
29830ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
29840ba332f7SArnaldo Carvalho de Melo 
2985ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2986ea7cd592SNamhyung Kim 						       &actions[nr_options],
2987ea7cd592SNamhyung Kim 						       &options[nr_options],
2988ea7cd592SNamhyung Kim 						       bi->from.map,
2989ea7cd592SNamhyung Kim 						       bi->from.sym);
2990ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
2991ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
2992ea7cd592SNamhyung Kim 							&actions[nr_options],
2993ea7cd592SNamhyung Kim 							&options[nr_options],
2994ea7cd592SNamhyung Kim 							bi->to.map,
2995ea7cd592SNamhyung Kim 							bi->to.sym);
2996aca7a94dSNamhyung Kim 		} else {
2997ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2998ea7cd592SNamhyung Kim 						       &actions[nr_options],
2999ea7cd592SNamhyung Kim 						       &options[nr_options],
3000ea7cd592SNamhyung Kim 						       browser->selection->map,
3001ea7cd592SNamhyung Kim 						       browser->selection->sym);
3002446fb96cSArnaldo Carvalho de Melo 		}
30030ba332f7SArnaldo Carvalho de Melo skip_annotation:
3004ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
3005ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
3006ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
3007045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
3008ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
3009ea7cd592SNamhyung Kim 					  &options[nr_options],
3010bd315aabSWang Nan 					  browser->selection ?
3011bd315aabSWang Nan 						browser->selection->map : NULL);
301284734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
301384734b06SKan Liang 					     &options[nr_options],
301484734b06SKan Liang 					     socked_id);
3015cdbab7c2SFeng Tang 		/* perf script support */
3016b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
3017b1baae89SNamhyung Kim 			goto skip_scripting;
3018b1baae89SNamhyung Kim 
3019cdbab7c2SFeng Tang 		if (browser->he_selection) {
3020fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
3021ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3022ea7cd592SNamhyung Kim 							     &actions[nr_options],
3023ea7cd592SNamhyung Kim 							     &options[nr_options],
3024ea7cd592SNamhyung Kim 							     thread, NULL);
30252eafd410SNamhyung Kim 			}
3026bd315aabSWang Nan 			/*
3027bd315aabSWang Nan 			 * Note that browser->selection != NULL
3028bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
3029bd315aabSWang Nan 			 * so we don't need to check browser->selection
3030bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
3031bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
3032bd315aabSWang Nan 			 *
3033bd315aabSWang Nan 			 * See hist_browser__show_entry.
3034bd315aabSWang Nan 			 */
30352e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
3036ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3037ea7cd592SNamhyung Kim 							     &actions[nr_options],
3038ea7cd592SNamhyung Kim 							     &options[nr_options],
3039ea7cd592SNamhyung Kim 							     NULL, browser->selection->sym);
3040cdbab7c2SFeng Tang 			}
3041c221acb0SNamhyung Kim 		}
3042ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
3043ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
3044ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
3045ea7cd592SNamhyung Kim 					     &options[nr_options]);
3046b1baae89SNamhyung Kim skip_scripting:
3047ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
3048ea7cd592SNamhyung Kim 					   &options[nr_options]);
3049aca7a94dSNamhyung Kim 
3050ea7cd592SNamhyung Kim 		do {
3051ea7cd592SNamhyung Kim 			struct popup_action *act;
3052ea7cd592SNamhyung Kim 
3053ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
3054ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
3055aca7a94dSNamhyung Kim 				break;
3056aca7a94dSNamhyung Kim 
3057ea7cd592SNamhyung Kim 			act = &actions[choice];
3058ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
3059ea7cd592SNamhyung Kim 		} while (key == 1);
3060aca7a94dSNamhyung Kim 
3061bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3062341487abSFeng Tang 			break;
3063341487abSFeng Tang 	}
3064aca7a94dSNamhyung Kim out_free_stack:
306501f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3066aca7a94dSNamhyung Kim out:
3067aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3068f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3069aca7a94dSNamhyung Kim 	return key;
3070aca7a94dSNamhyung Kim }
3071aca7a94dSNamhyung Kim 
3072aca7a94dSNamhyung Kim struct perf_evsel_menu {
3073aca7a94dSNamhyung Kim 	struct ui_browser b;
3074aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
3075cd0cccbaSArnaldo Carvalho de Melo 	struct annotation_options *annotation_opts;
3076aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3077064f1981SNamhyung Kim 	float min_pcnt;
3078ce80d3beSKan Liang 	struct perf_env *env;
3079aca7a94dSNamhyung Kim };
3080aca7a94dSNamhyung Kim 
3081aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3082aca7a94dSNamhyung Kim 				   void *entry, int row)
3083aca7a94dSNamhyung Kim {
3084aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
3085aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
3086aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
30874ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3088aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
30894ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
30907289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
3091aca7a94dSNamhyung Kim 	char bf[256], unit;
3092aca7a94dSNamhyung Kim 	const char *warn = " ";
3093aca7a94dSNamhyung Kim 	size_t printed;
3094aca7a94dSNamhyung Kim 
3095aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3096aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3097aca7a94dSNamhyung Kim 
3098759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
3099717e263fSNamhyung Kim 		struct perf_evsel *pos;
3100717e263fSNamhyung Kim 
3101717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
3102717e263fSNamhyung Kim 
3103717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
31044ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
31054ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3106717e263fSNamhyung Kim 		}
3107717e263fSNamhyung Kim 	}
3108717e263fSNamhyung Kim 
3109aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3110aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3111aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3112517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3113aca7a94dSNamhyung Kim 
31144ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3115aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3116aca7a94dSNamhyung Kim 		menu->lost_events = true;
3117aca7a94dSNamhyung Kim 		if (!current_entry)
3118aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3119aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3120aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3121aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3122aca7a94dSNamhyung Kim 		warn = bf;
3123aca7a94dSNamhyung Kim 	}
3124aca7a94dSNamhyung Kim 
312526270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3126aca7a94dSNamhyung Kim 
3127aca7a94dSNamhyung Kim 	if (current_entry)
3128aca7a94dSNamhyung Kim 		menu->selection = evsel;
3129aca7a94dSNamhyung Kim }
3130aca7a94dSNamhyung Kim 
3131aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3132aca7a94dSNamhyung Kim 				int nr_events, const char *help,
313306cc1a47SKan Liang 				struct hist_browser_timer *hbt,
313406cc1a47SKan Liang 				bool warn_lost_event)
3135aca7a94dSNamhyung Kim {
3136aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
3137aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3138dd00d486SJiri Olsa 	const char *title = "Available samples";
31399783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3140aca7a94dSNamhyung Kim 	int key;
3141aca7a94dSNamhyung Kim 
3142aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3143aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3144aca7a94dSNamhyung Kim 		return -1;
3145aca7a94dSNamhyung Kim 
3146aca7a94dSNamhyung Kim 	while (1) {
3147aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3148aca7a94dSNamhyung Kim 
3149aca7a94dSNamhyung Kim 		switch (key) {
3150aca7a94dSNamhyung Kim 		case K_TIMER:
31519783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
3152aca7a94dSNamhyung Kim 
315306cc1a47SKan Liang 			if (!menu->lost_events_warned &&
315406cc1a47SKan Liang 			    menu->lost_events &&
315506cc1a47SKan Liang 			    warn_lost_event) {
3156aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3157aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3158aca7a94dSNamhyung Kim 			}
3159aca7a94dSNamhyung Kim 			continue;
3160aca7a94dSNamhyung Kim 		case K_RIGHT:
3161aca7a94dSNamhyung Kim 		case K_ENTER:
3162aca7a94dSNamhyung Kim 			if (!menu->selection)
3163aca7a94dSNamhyung Kim 				continue;
3164aca7a94dSNamhyung Kim 			pos = menu->selection;
3165aca7a94dSNamhyung Kim browse_hists:
3166aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
3167aca7a94dSNamhyung Kim 			/*
3168aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3169aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3170aca7a94dSNamhyung Kim 			 */
31719783adf7SNamhyung Kim 			if (hbt)
31729783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3173aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
3174dd00d486SJiri Olsa 						       true, hbt,
3175064f1981SNamhyung Kim 						       menu->min_pcnt,
317606cc1a47SKan Liang 						       menu->env,
3177cd0cccbaSArnaldo Carvalho de Melo 						       warn_lost_event,
3178cd0cccbaSArnaldo Carvalho de Melo 						       menu->annotation_opts);
3179aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3180aca7a94dSNamhyung Kim 			switch (key) {
3181aca7a94dSNamhyung Kim 			case K_TAB:
3182aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
31839a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
3184aca7a94dSNamhyung Kim 				else
31859a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
3186aca7a94dSNamhyung Kim 				goto browse_hists;
3187aca7a94dSNamhyung Kim 			case K_UNTAB:
3188aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
31899a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
3190aca7a94dSNamhyung Kim 				else
3191d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
3192aca7a94dSNamhyung Kim 				goto browse_hists;
3193341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
3194aca7a94dSNamhyung Kim 			case 'q':
3195aca7a94dSNamhyung Kim 			case CTRL('c'):
3196aca7a94dSNamhyung Kim 				goto out;
319763ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3198aca7a94dSNamhyung Kim 			default:
3199aca7a94dSNamhyung Kim 				continue;
3200aca7a94dSNamhyung Kim 			}
3201aca7a94dSNamhyung Kim 		case K_LEFT:
3202aca7a94dSNamhyung Kim 			continue;
3203aca7a94dSNamhyung Kim 		case K_ESC:
3204aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3205aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3206aca7a94dSNamhyung Kim 				continue;
3207aca7a94dSNamhyung Kim 			/* Fall thru */
3208aca7a94dSNamhyung Kim 		case 'q':
3209aca7a94dSNamhyung Kim 		case CTRL('c'):
3210aca7a94dSNamhyung Kim 			goto out;
3211aca7a94dSNamhyung Kim 		default:
3212aca7a94dSNamhyung Kim 			continue;
3213aca7a94dSNamhyung Kim 		}
3214aca7a94dSNamhyung Kim 	}
3215aca7a94dSNamhyung Kim 
3216aca7a94dSNamhyung Kim out:
3217aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3218aca7a94dSNamhyung Kim 	return key;
3219aca7a94dSNamhyung Kim }
3220aca7a94dSNamhyung Kim 
3221316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3222fc24d7c2SNamhyung Kim 				 void *entry)
3223fc24d7c2SNamhyung Kim {
3224fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3225fc24d7c2SNamhyung Kim 
3226fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3227fc24d7c2SNamhyung Kim 		return true;
3228fc24d7c2SNamhyung Kim 
3229fc24d7c2SNamhyung Kim 	return false;
3230fc24d7c2SNamhyung Kim }
3231fc24d7c2SNamhyung Kim 
3232aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3233fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
323468d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
3235064f1981SNamhyung Kim 					   float min_pcnt,
323606cc1a47SKan Liang 					   struct perf_env *env,
3237cd0cccbaSArnaldo Carvalho de Melo 					   bool warn_lost_event,
3238cd0cccbaSArnaldo Carvalho de Melo 					   struct annotation_options *annotation_opts)
3239aca7a94dSNamhyung Kim {
3240aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3241aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
3242aca7a94dSNamhyung Kim 		.b = {
3243aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
3244aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3245aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3246aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3247fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3248fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3249aca7a94dSNamhyung Kim 			.priv	    = evlist,
3250aca7a94dSNamhyung Kim 		},
3251064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
325268d80758SNamhyung Kim 		.env = env,
3253cd0cccbaSArnaldo Carvalho de Melo 		.annotation_opts = annotation_opts,
3254aca7a94dSNamhyung Kim 	};
3255aca7a94dSNamhyung Kim 
3256aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3257aca7a94dSNamhyung Kim 
3258e5cadb93SArnaldo Carvalho de Melo 	evlist__for_each_entry(evlist, pos) {
32597289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
3260aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3261aca7a94dSNamhyung Kim 
3262aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3263aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3264aca7a94dSNamhyung Kim 	}
3265aca7a94dSNamhyung Kim 
326606cc1a47SKan Liang 	return perf_evsel_menu__run(&menu, nr_entries, help,
326706cc1a47SKan Liang 				    hbt, warn_lost_event);
3268aca7a94dSNamhyung Kim }
3269aca7a94dSNamhyung Kim 
3270aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
327168d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
3272064f1981SNamhyung Kim 				  float min_pcnt,
327306cc1a47SKan Liang 				  struct perf_env *env,
3274cd0cccbaSArnaldo Carvalho de Melo 				  bool warn_lost_event,
3275cd0cccbaSArnaldo Carvalho de Melo 				  struct annotation_options *annotation_opts)
3276aca7a94dSNamhyung Kim {
3277fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
3278fc24d7c2SNamhyung Kim 
3279fc24d7c2SNamhyung Kim single_entry:
3280fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
32819a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
3282fc24d7c2SNamhyung Kim 
3283fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
3284dd00d486SJiri Olsa 						false, hbt, min_pcnt,
3285cd0cccbaSArnaldo Carvalho de Melo 						env, warn_lost_event,
3286cd0cccbaSArnaldo Carvalho de Melo 						annotation_opts);
3287aca7a94dSNamhyung Kim 	}
3288aca7a94dSNamhyung Kim 
3289fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
3290fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
3291fc24d7c2SNamhyung Kim 
3292fc24d7c2SNamhyung Kim 		nr_entries = 0;
3293e5cadb93SArnaldo Carvalho de Melo 		evlist__for_each_entry(evlist, pos) {
3294fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
3295fc24d7c2SNamhyung Kim 				nr_entries++;
32960050f7aaSArnaldo Carvalho de Melo 		}
3297fc24d7c2SNamhyung Kim 
3298fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3299fc24d7c2SNamhyung Kim 			goto single_entry;
3300fc24d7c2SNamhyung Kim 	}
3301fc24d7c2SNamhyung Kim 
3302fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
330306cc1a47SKan Liang 					       hbt, min_pcnt, env,
3304cd0cccbaSArnaldo Carvalho de Melo 					       warn_lost_event,
3305cd0cccbaSArnaldo Carvalho de Melo 					       annotation_opts);
3306aca7a94dSNamhyung Kim }
3307