xref: /linux/tools/perf/ui/browsers/hists.c (revision 5b91a86f47669898d6f2fe625844ab65cf258c34)
1aca7a94dSNamhyung Kim #include <stdio.h>
2aca7a94dSNamhyung Kim #include <stdlib.h>
3aca7a94dSNamhyung Kim #include <string.h>
4aca7a94dSNamhyung Kim #include <linux/rbtree.h>
5aca7a94dSNamhyung Kim 
6aca7a94dSNamhyung Kim #include "../../util/evsel.h"
7aca7a94dSNamhyung Kim #include "../../util/evlist.h"
8aca7a94dSNamhyung Kim #include "../../util/hist.h"
9aca7a94dSNamhyung Kim #include "../../util/pstack.h"
10aca7a94dSNamhyung Kim #include "../../util/sort.h"
11aca7a94dSNamhyung Kim #include "../../util/util.h"
1242337a22SNamhyung Kim #include "../../util/top.h"
1368d80758SNamhyung Kim #include "../../arch/common.h"
14aca7a94dSNamhyung Kim 
15f758990fSJiri Olsa #include "../browsers/hists.h"
16aca7a94dSNamhyung Kim #include "../helpline.h"
17aca7a94dSNamhyung Kim #include "../util.h"
18aca7a94dSNamhyung Kim #include "../ui.h"
19aca7a94dSNamhyung Kim #include "map.h"
20d755330cSJiri Olsa #include "annotate.h"
21aca7a94dSNamhyung Kim 
22f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
23f5951d56SNamhyung Kim 
24*5b91a86fSJiri Olsa static int perf_evsel_browser_title(struct hist_browser *browser,
251e378ebdSTaeung Song 				    char *bf, size_t size);
26112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
27aca7a94dSNamhyung Kim 
28c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
29c3b78952SNamhyung Kim 					     float min_pcnt);
30c3b78952SNamhyung Kim 
31268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
32268397cbSNamhyung Kim {
339c0fa8ddSArnaldo Carvalho de Melo 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
34268397cbSNamhyung Kim }
35268397cbSNamhyung Kim 
364fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
374fabf3d1SHe Kuang {
384fabf3d1SHe Kuang 	struct rb_node *nd;
394fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
404fabf3d1SHe Kuang 	int unfolded_rows = 0;
414fabf3d1SHe Kuang 
424fabf3d1SHe Kuang 	for (nd = rb_first(&hists->entries);
434fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
44f5b763feSNamhyung Kim 	     nd = rb_hierarchy_next(nd)) {
454fabf3d1SHe Kuang 		struct hist_entry *he =
464fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
474fabf3d1SHe Kuang 
48f5b763feSNamhyung Kim 		if (he->leaf && he->unfolded)
494fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
504fabf3d1SHe Kuang 	}
514fabf3d1SHe Kuang 	return unfolded_rows;
524fabf3d1SHe Kuang }
534fabf3d1SHe Kuang 
54c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
55c3b78952SNamhyung Kim {
56c3b78952SNamhyung Kim 	u32 nr_entries;
57c3b78952SNamhyung Kim 
58f5b763feSNamhyung Kim 	if (symbol_conf.report_hierarchy)
59f5b763feSNamhyung Kim 		nr_entries = hb->nr_hierarchy_entries;
60f5b763feSNamhyung Kim 	else if (hist_browser__has_filter(hb))
61c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
62c3b78952SNamhyung Kim 	else
63c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
64c3b78952SNamhyung Kim 
654fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
66c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
67c3b78952SNamhyung Kim }
68c3b78952SNamhyung Kim 
69025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
70025bf7eaSArnaldo Carvalho de Melo {
71025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
72025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
73025bf7eaSArnaldo Carvalho de Melo 
74025bf7eaSArnaldo Carvalho de Melo 	browser->rows = browser->height - header_offset;
75025bf7eaSArnaldo Carvalho de Melo 	/*
76025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
77025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
78025bf7eaSArnaldo Carvalho de Melo 	 */
79025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
80025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
81025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
82025bf7eaSArnaldo Carvalho de Melo }
83025bf7eaSArnaldo Carvalho de Melo 
84357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
85aca7a94dSNamhyung Kim {
86357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
87357cfff1SArnaldo Carvalho de Melo 
88aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
89357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
90357cfff1SArnaldo Carvalho de Melo 	/*
91357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
92357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
93357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
94357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
95357cfff1SArnaldo Carvalho de Melo  	 */
96357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
97025bf7eaSArnaldo Carvalho de Melo 	hist_browser__update_rows(hb);
98aca7a94dSNamhyung Kim }
99aca7a94dSNamhyung Kim 
100ca3ff33bSArnaldo Carvalho de Melo static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
101ca3ff33bSArnaldo Carvalho de Melo {
102025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = browser->show_headers ? 1 : 0;
103025bf7eaSArnaldo Carvalho de Melo 
104025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row + header_offset, column);
105ca3ff33bSArnaldo Carvalho de Melo }
106ca3ff33bSArnaldo Carvalho de Melo 
10705e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
108aca7a94dSNamhyung Kim {
109c3b78952SNamhyung Kim 	/*
110c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
111c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
112c3b78952SNamhyung Kim 	 */
113c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
114c3b78952SNamhyung Kim 
115268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
116c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
117357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
11805e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
119aca7a94dSNamhyung Kim }
120aca7a94dSNamhyung Kim 
121aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
122aca7a94dSNamhyung Kim {
123aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
124aca7a94dSNamhyung Kim }
125aca7a94dSNamhyung Kim 
12605e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
127aca7a94dSNamhyung Kim {
1283698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
129aca7a94dSNamhyung Kim }
130aca7a94dSNamhyung Kim 
13105e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
132aca7a94dSNamhyung Kim {
1333698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
134aca7a94dSNamhyung Kim }
135aca7a94dSNamhyung Kim 
1363698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
137aca7a94dSNamhyung Kim {
1383698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
139aca7a94dSNamhyung Kim }
140aca7a94dSNamhyung Kim 
14105e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
142aca7a94dSNamhyung Kim {
143aca7a94dSNamhyung Kim 	int n = 0;
144aca7a94dSNamhyung Kim 	struct rb_node *nd;
145aca7a94dSNamhyung Kim 
14605e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
147aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
148aca7a94dSNamhyung Kim 		struct callchain_list *chain;
149aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
150aca7a94dSNamhyung Kim 
151aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
152aca7a94dSNamhyung Kim 			++n;
153aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
154aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
155aca7a94dSNamhyung Kim 			if (folded_sign == '+')
156aca7a94dSNamhyung Kim 				break;
157aca7a94dSNamhyung Kim 		}
158aca7a94dSNamhyung Kim 
159aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
160aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
161aca7a94dSNamhyung Kim 	}
162aca7a94dSNamhyung Kim 
163aca7a94dSNamhyung Kim 	return n;
164aca7a94dSNamhyung Kim }
165aca7a94dSNamhyung Kim 
1664b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
1674b3a3212SNamhyung Kim {
1684b3a3212SNamhyung Kim 	struct callchain_list *chain;
1694b3a3212SNamhyung Kim 	char folded_sign = 0;
1704b3a3212SNamhyung Kim 	int n = 0;
1714b3a3212SNamhyung Kim 
1724b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
1734b3a3212SNamhyung Kim 		if (!folded_sign) {
1744b3a3212SNamhyung Kim 			/* only check first chain list entry */
1754b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1764b3a3212SNamhyung Kim 			if (folded_sign == '+')
1774b3a3212SNamhyung Kim 				return 1;
1784b3a3212SNamhyung Kim 		}
1794b3a3212SNamhyung Kim 		n++;
1804b3a3212SNamhyung Kim 	}
1814b3a3212SNamhyung Kim 
1824b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
1834b3a3212SNamhyung Kim 		if (!folded_sign) {
1844b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
1854b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1864b3a3212SNamhyung Kim 			if (folded_sign == '+')
1874b3a3212SNamhyung Kim 				return 1;
1884b3a3212SNamhyung Kim 		}
1894b3a3212SNamhyung Kim 		n++;
1904b3a3212SNamhyung Kim 	}
1914b3a3212SNamhyung Kim 
1924b3a3212SNamhyung Kim 	return n;
1934b3a3212SNamhyung Kim }
1944b3a3212SNamhyung Kim 
1958c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
1968c430a34SNamhyung Kim {
1978c430a34SNamhyung Kim 	return 1;
1988c430a34SNamhyung Kim }
1998c430a34SNamhyung Kim 
200aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
201aca7a94dSNamhyung Kim {
202aca7a94dSNamhyung Kim 	struct callchain_list *chain;
203aca7a94dSNamhyung Kim 	bool unfolded = false;
204aca7a94dSNamhyung Kim 	int n = 0;
205aca7a94dSNamhyung Kim 
2064b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2074b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2088c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2098c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2104b3a3212SNamhyung Kim 
211aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
212aca7a94dSNamhyung Kim 		++n;
2133698dab1SNamhyung Kim 		unfolded = chain->unfolded;
214aca7a94dSNamhyung Kim 	}
215aca7a94dSNamhyung Kim 
216aca7a94dSNamhyung Kim 	if (unfolded)
217aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
218aca7a94dSNamhyung Kim 
219aca7a94dSNamhyung Kim 	return n;
220aca7a94dSNamhyung Kim }
221aca7a94dSNamhyung Kim 
222aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
223aca7a94dSNamhyung Kim {
224aca7a94dSNamhyung Kim 	struct rb_node *nd;
225aca7a94dSNamhyung Kim 	int n = 0;
226aca7a94dSNamhyung Kim 
227aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
228aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
229aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
230aca7a94dSNamhyung Kim 	}
231aca7a94dSNamhyung Kim 
232aca7a94dSNamhyung Kim 	return n;
233aca7a94dSNamhyung Kim }
234aca7a94dSNamhyung Kim 
235f5b763feSNamhyung Kim static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
236f5b763feSNamhyung Kim 				bool include_children)
237f5b763feSNamhyung Kim {
238f5b763feSNamhyung Kim 	int count = 0;
239f5b763feSNamhyung Kim 	struct rb_node *node;
240f5b763feSNamhyung Kim 	struct hist_entry *child;
241f5b763feSNamhyung Kim 
242f5b763feSNamhyung Kim 	if (he->leaf)
243f5b763feSNamhyung Kim 		return callchain__count_rows(&he->sorted_chain);
244f5b763feSNamhyung Kim 
24579dded87SNamhyung Kim 	if (he->has_no_entry)
24679dded87SNamhyung Kim 		return 1;
24779dded87SNamhyung Kim 
248f5b763feSNamhyung Kim 	node = rb_first(&he->hroot_out);
249f5b763feSNamhyung Kim 	while (node) {
250f5b763feSNamhyung Kim 		float percent;
251f5b763feSNamhyung Kim 
252f5b763feSNamhyung Kim 		child = rb_entry(node, struct hist_entry, rb_node);
253f5b763feSNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
254f5b763feSNamhyung Kim 
255f5b763feSNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt) {
256f5b763feSNamhyung Kim 			count++;
257f5b763feSNamhyung Kim 
258f5b763feSNamhyung Kim 			if (include_children && child->unfolded)
259f5b763feSNamhyung Kim 				count += hierarchy_count_rows(hb, child, true);
260f5b763feSNamhyung Kim 		}
261f5b763feSNamhyung Kim 
262f5b763feSNamhyung Kim 		node = rb_next(node);
263f5b763feSNamhyung Kim 	}
264f5b763feSNamhyung Kim 	return count;
265f5b763feSNamhyung Kim }
266f5b763feSNamhyung Kim 
2673698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
268aca7a94dSNamhyung Kim {
2693698dab1SNamhyung Kim 	if (!he)
270aca7a94dSNamhyung Kim 		return false;
271aca7a94dSNamhyung Kim 
2723698dab1SNamhyung Kim 	if (!he->has_children)
273aca7a94dSNamhyung Kim 		return false;
274aca7a94dSNamhyung Kim 
2753698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
2763698dab1SNamhyung Kim 	return true;
2773698dab1SNamhyung Kim }
2783698dab1SNamhyung Kim 
2793698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
2803698dab1SNamhyung Kim {
2813698dab1SNamhyung Kim 	if (!cl)
2823698dab1SNamhyung Kim 		return false;
2833698dab1SNamhyung Kim 
2843698dab1SNamhyung Kim 	if (!cl->has_children)
2853698dab1SNamhyung Kim 		return false;
2863698dab1SNamhyung Kim 
2873698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
288aca7a94dSNamhyung Kim 	return true;
289aca7a94dSNamhyung Kim }
290aca7a94dSNamhyung Kim 
29105e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
292aca7a94dSNamhyung Kim {
29305e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
294aca7a94dSNamhyung Kim 
29505e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
296aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
297aca7a94dSNamhyung Kim 		struct callchain_list *chain;
298aca7a94dSNamhyung Kim 		bool first = true;
299aca7a94dSNamhyung Kim 
300aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
301aca7a94dSNamhyung Kim 			if (first) {
302aca7a94dSNamhyung Kim 				first = false;
3033698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
304aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
305aca7a94dSNamhyung Kim 			} else
3063698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
307aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
308aca7a94dSNamhyung Kim 		}
309aca7a94dSNamhyung Kim 
310aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
311aca7a94dSNamhyung Kim 	}
312aca7a94dSNamhyung Kim }
313aca7a94dSNamhyung Kim 
314a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
315a7444af6SNamhyung Kim 					       bool has_sibling)
316aca7a94dSNamhyung Kim {
317aca7a94dSNamhyung Kim 	struct callchain_list *chain;
318aca7a94dSNamhyung Kim 
319a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3203698dab1SNamhyung Kim 	chain->has_children = has_sibling;
321a7444af6SNamhyung Kim 
32290989035SAndres Freund 	if (!list_empty(&node->val)) {
32382162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3243698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
32582162b5aSNamhyung Kim 	}
326aca7a94dSNamhyung Kim 
32705e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
328aca7a94dSNamhyung Kim }
329aca7a94dSNamhyung Kim 
33005e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
331aca7a94dSNamhyung Kim {
332a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
333a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
334aca7a94dSNamhyung Kim 
33505e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
336aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
337a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3388c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3398c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3404b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
341aca7a94dSNamhyung Kim 	}
342aca7a94dSNamhyung Kim }
343aca7a94dSNamhyung Kim 
34405e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
345aca7a94dSNamhyung Kim {
346f5b763feSNamhyung Kim 	if (he->init_have_children)
347f5b763feSNamhyung Kim 		return;
348f5b763feSNamhyung Kim 
349f5b763feSNamhyung Kim 	if (he->leaf) {
3503698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
35105e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
352f5b763feSNamhyung Kim 	} else {
353f5b763feSNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
354aca7a94dSNamhyung Kim 	}
355f5b763feSNamhyung Kim 
356f5b763feSNamhyung Kim 	he->init_have_children = true;
357aca7a94dSNamhyung Kim }
358aca7a94dSNamhyung Kim 
35905e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
360aca7a94dSNamhyung Kim {
36105e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
3623698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
3633698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
3643698dab1SNamhyung Kim 	bool has_children;
365aca7a94dSNamhyung Kim 
3664938cf0cSWang Nan 	if (!he || !ms)
3674938cf0cSWang Nan 		return false;
3684938cf0cSWang Nan 
3693698dab1SNamhyung Kim 	if (ms == &he->ms)
3703698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
3713698dab1SNamhyung Kim 	else
3723698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
3733698dab1SNamhyung Kim 
3743698dab1SNamhyung Kim 	if (has_children) {
375f5b763feSNamhyung Kim 		int child_rows = 0;
376f5b763feSNamhyung Kim 
377aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
378c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
379aca7a94dSNamhyung Kim 
380f5b763feSNamhyung Kim 		if (he->leaf)
381f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
382f5b763feSNamhyung Kim 		else
383f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
384f5b763feSNamhyung Kim 
385f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
386f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
387f5b763feSNamhyung Kim 
388f5b763feSNamhyung Kim 		if (he->unfolded) {
389f5b763feSNamhyung Kim 			if (he->leaf)
390aca7a94dSNamhyung Kim 				he->nr_rows = callchain__count_rows(&he->sorted_chain);
391aca7a94dSNamhyung Kim 			else
392f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
393f5b763feSNamhyung Kim 
394f5b763feSNamhyung Kim 			/* account grand children */
395f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
396f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
39779dded87SNamhyung Kim 
39879dded87SNamhyung Kim 			if (!he->leaf && he->nr_rows == 0) {
39979dded87SNamhyung Kim 				he->has_no_entry = true;
40079dded87SNamhyung Kim 				he->nr_rows = 1;
40179dded87SNamhyung Kim 			}
402f5b763feSNamhyung Kim 		} else {
403f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
404f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
405f5b763feSNamhyung Kim 
40679dded87SNamhyung Kim 			if (he->has_no_entry)
40779dded87SNamhyung Kim 				he->has_no_entry = false;
40879dded87SNamhyung Kim 
409aca7a94dSNamhyung Kim 			he->nr_rows = 0;
410f5b763feSNamhyung Kim 		}
411c3b78952SNamhyung Kim 
412c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
413f5b763feSNamhyung Kim 
414f5b763feSNamhyung Kim 		if (he->leaf)
415c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
416f5b763feSNamhyung Kim 		else
417f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
418aca7a94dSNamhyung Kim 
419aca7a94dSNamhyung Kim 		return true;
420aca7a94dSNamhyung Kim 	}
421aca7a94dSNamhyung Kim 
422aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
423aca7a94dSNamhyung Kim 	return false;
424aca7a94dSNamhyung Kim }
425aca7a94dSNamhyung Kim 
42605e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
427aca7a94dSNamhyung Kim {
428aca7a94dSNamhyung Kim 	int n = 0;
429aca7a94dSNamhyung Kim 	struct rb_node *nd;
430aca7a94dSNamhyung Kim 
43105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
432aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
433aca7a94dSNamhyung Kim 		struct callchain_list *chain;
434aca7a94dSNamhyung Kim 		bool has_children = false;
435aca7a94dSNamhyung Kim 
436aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
437aca7a94dSNamhyung Kim 			++n;
4383698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
4393698dab1SNamhyung Kim 			has_children = chain->has_children;
440aca7a94dSNamhyung Kim 		}
441aca7a94dSNamhyung Kim 
442aca7a94dSNamhyung Kim 		if (has_children)
443aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
444aca7a94dSNamhyung Kim 	}
445aca7a94dSNamhyung Kim 
446aca7a94dSNamhyung Kim 	return n;
447aca7a94dSNamhyung Kim }
448aca7a94dSNamhyung Kim 
449aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
450aca7a94dSNamhyung Kim {
451aca7a94dSNamhyung Kim 	struct callchain_list *chain;
452aca7a94dSNamhyung Kim 	bool has_children = false;
453aca7a94dSNamhyung Kim 	int n = 0;
454aca7a94dSNamhyung Kim 
455aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
456aca7a94dSNamhyung Kim 		++n;
4573698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
4583698dab1SNamhyung Kim 		has_children = chain->has_children;
459aca7a94dSNamhyung Kim 	}
460aca7a94dSNamhyung Kim 
461aca7a94dSNamhyung Kim 	if (has_children)
462aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
463aca7a94dSNamhyung Kim 
464aca7a94dSNamhyung Kim 	return n;
465aca7a94dSNamhyung Kim }
466aca7a94dSNamhyung Kim 
467aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
468aca7a94dSNamhyung Kim {
469aca7a94dSNamhyung Kim 	struct rb_node *nd;
470aca7a94dSNamhyung Kim 	int n = 0;
471aca7a94dSNamhyung Kim 
472aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
473aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
474aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
475aca7a94dSNamhyung Kim 	}
476aca7a94dSNamhyung Kim 
477aca7a94dSNamhyung Kim 	return n;
478aca7a94dSNamhyung Kim }
479aca7a94dSNamhyung Kim 
480492b1010SNamhyung Kim static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
481492b1010SNamhyung Kim 				 bool unfold __maybe_unused)
482492b1010SNamhyung Kim {
483492b1010SNamhyung Kim 	float percent;
484492b1010SNamhyung Kim 	struct rb_node *nd;
485492b1010SNamhyung Kim 	struct hist_entry *child;
486492b1010SNamhyung Kim 	int n = 0;
487492b1010SNamhyung Kim 
488492b1010SNamhyung Kim 	for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
489492b1010SNamhyung Kim 		child = rb_entry(nd, struct hist_entry, rb_node);
490492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
491492b1010SNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt)
492492b1010SNamhyung Kim 			n++;
493492b1010SNamhyung Kim 	}
494492b1010SNamhyung Kim 
495492b1010SNamhyung Kim 	return n;
496492b1010SNamhyung Kim }
497492b1010SNamhyung Kim 
498492b1010SNamhyung Kim static void hist_entry__set_folding(struct hist_entry *he,
499492b1010SNamhyung Kim 				    struct hist_browser *hb, bool unfold)
500aca7a94dSNamhyung Kim {
50105e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
5023698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
503aca7a94dSNamhyung Kim 
5043698dab1SNamhyung Kim 	if (he->has_children) {
505492b1010SNamhyung Kim 		int n;
506492b1010SNamhyung Kim 
507492b1010SNamhyung Kim 		if (he->leaf)
508492b1010SNamhyung Kim 			n = callchain__set_folding(&he->sorted_chain, unfold);
509492b1010SNamhyung Kim 		else
510492b1010SNamhyung Kim 			n = hierarchy_set_folding(hb, he, unfold);
511492b1010SNamhyung Kim 
51205e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
513aca7a94dSNamhyung Kim 	} else
51405e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
515aca7a94dSNamhyung Kim }
516aca7a94dSNamhyung Kim 
517c3b78952SNamhyung Kim static void
518c3b78952SNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
519aca7a94dSNamhyung Kim {
520aca7a94dSNamhyung Kim 	struct rb_node *nd;
521492b1010SNamhyung Kim 	struct hist_entry *he;
522492b1010SNamhyung Kim 	double percent;
523aca7a94dSNamhyung Kim 
524492b1010SNamhyung Kim 	nd = rb_first(&browser->hists->entries);
525492b1010SNamhyung Kim 	while (nd) {
526492b1010SNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
527492b1010SNamhyung Kim 
528492b1010SNamhyung Kim 		/* set folding state even if it's currently folded */
529492b1010SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
530492b1010SNamhyung Kim 
531492b1010SNamhyung Kim 		hist_entry__set_folding(he, browser, unfold);
532492b1010SNamhyung Kim 
533492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(he);
534492b1010SNamhyung Kim 		if (he->filtered || percent < browser->min_pcnt)
535492b1010SNamhyung Kim 			continue;
536492b1010SNamhyung Kim 
537492b1010SNamhyung Kim 		if (!he->depth || unfold)
538492b1010SNamhyung Kim 			browser->nr_hierarchy_entries++;
539492b1010SNamhyung Kim 		if (he->leaf)
540c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
54179dded87SNamhyung Kim 		else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
54279dded87SNamhyung Kim 			browser->nr_hierarchy_entries++;
54379dded87SNamhyung Kim 			he->has_no_entry = true;
54479dded87SNamhyung Kim 			he->nr_rows = 1;
54579dded87SNamhyung Kim 		} else
54679dded87SNamhyung Kim 			he->has_no_entry = false;
547aca7a94dSNamhyung Kim 	}
548aca7a94dSNamhyung Kim }
549aca7a94dSNamhyung Kim 
55005e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
551aca7a94dSNamhyung Kim {
552492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
553c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
554c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
555c3b78952SNamhyung Kim 
556c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
557aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
55805e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
559aca7a94dSNamhyung Kim }
560aca7a94dSNamhyung Kim 
561aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
562aca7a94dSNamhyung Kim {
563aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
564aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
565aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
566aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
567aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
568aca7a94dSNamhyung Kim }
569aca7a94dSNamhyung Kim 
570*5b91a86fSJiri Olsa static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
571*5b91a86fSJiri Olsa {
572*5b91a86fSJiri Olsa 	return browser->title ? browser->title(browser, bf, size) : 0;
573*5b91a86fSJiri Olsa }
574*5b91a86fSJiri Olsa 
575dabd2012SJiri Olsa int hist_browser__run(struct hist_browser *browser, const char *help)
576aca7a94dSNamhyung Kim {
577aca7a94dSNamhyung Kim 	int key;
578aca7a94dSNamhyung Kim 	char title[160];
579c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
5809783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
581aca7a94dSNamhyung Kim 
58205e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
583c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
584aca7a94dSNamhyung Kim 
585*5b91a86fSJiri Olsa 	hist_browser__title(browser, title, sizeof(title));
586aca7a94dSNamhyung Kim 
587090cff3eSNamhyung Kim 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
588aca7a94dSNamhyung Kim 		return -1;
589aca7a94dSNamhyung Kim 
590aca7a94dSNamhyung Kim 	while (1) {
59105e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
592aca7a94dSNamhyung Kim 
593aca7a94dSNamhyung Kim 		switch (key) {
594fa5df943SNamhyung Kim 		case K_TIMER: {
595fa5df943SNamhyung Kim 			u64 nr_entries;
5969783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
597fa5df943SNamhyung Kim 
598c3b78952SNamhyung Kim 			if (hist_browser__has_filter(browser))
599112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
600fa5df943SNamhyung Kim 
601c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
602fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
603aca7a94dSNamhyung Kim 
60405e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
60505e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
60605e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
60705e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
60805e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
609aca7a94dSNamhyung Kim 			}
610aca7a94dSNamhyung Kim 
611*5b91a86fSJiri Olsa 			hist_browser__title(browser, title, sizeof(title));
61205e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
613aca7a94dSNamhyung Kim 			continue;
614fa5df943SNamhyung Kim 		}
615aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
616aca7a94dSNamhyung Kim 			static int seq;
61705e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
618aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
619aca7a94dSNamhyung Kim 			ui_helpline__pop();
62062c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
62105e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
62205e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
62362c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
62405e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
62505e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
626aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
627aca7a94dSNamhyung Kim 		}
628aca7a94dSNamhyung Kim 			break;
629aca7a94dSNamhyung Kim 		case 'C':
630aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
63105e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
632aca7a94dSNamhyung Kim 			break;
633aca7a94dSNamhyung Kim 		case 'E':
634aca7a94dSNamhyung Kim 			/* Expand the whole world. */
63505e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
636aca7a94dSNamhyung Kim 			break;
637025bf7eaSArnaldo Carvalho de Melo 		case 'H':
638025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
639025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
640025bf7eaSArnaldo Carvalho de Melo 			break;
641aca7a94dSNamhyung Kim 		case K_ENTER:
64205e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
643aca7a94dSNamhyung Kim 				break;
644aca7a94dSNamhyung Kim 			/* fall thru */
645aca7a94dSNamhyung Kim 		default:
646aca7a94dSNamhyung Kim 			goto out;
647aca7a94dSNamhyung Kim 		}
648aca7a94dSNamhyung Kim 	}
649aca7a94dSNamhyung Kim out:
65005e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
651aca7a94dSNamhyung Kim 	return key;
652aca7a94dSNamhyung Kim }
653aca7a94dSNamhyung Kim 
65439ee533fSNamhyung Kim struct callchain_print_arg {
65539ee533fSNamhyung Kim 	/* for hists browser */
65639ee533fSNamhyung Kim 	off_t	row_offset;
65739ee533fSNamhyung Kim 	bool	is_current_entry;
65839ee533fSNamhyung Kim 
65939ee533fSNamhyung Kim 	/* for file dump */
66039ee533fSNamhyung Kim 	FILE	*fp;
66139ee533fSNamhyung Kim 	int	printed;
66239ee533fSNamhyung Kim };
66339ee533fSNamhyung Kim 
66439ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
66539ee533fSNamhyung Kim 					 struct callchain_list *chain,
66639ee533fSNamhyung Kim 					 const char *str, int offset,
66739ee533fSNamhyung Kim 					 unsigned short row,
66839ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
66939ee533fSNamhyung Kim 
670f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
671f4536dddSNamhyung Kim 					       struct callchain_list *chain,
67239ee533fSNamhyung Kim 					       const char *str, int offset,
67339ee533fSNamhyung Kim 					       unsigned short row,
67439ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
675f4536dddSNamhyung Kim {
676f4536dddSNamhyung Kim 	int color, width;
67739ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
67870e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
679f4536dddSNamhyung Kim 
680f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
681f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
682f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
683f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
684f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
68539ee533fSNamhyung Kim 		arg->is_current_entry = true;
686f4536dddSNamhyung Kim 	}
687f4536dddSNamhyung Kim 
688f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
689f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
69026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
691517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
69270e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
69326270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
694f4536dddSNamhyung Kim }
695f4536dddSNamhyung Kim 
69639ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
69739ee533fSNamhyung Kim 						  struct callchain_list *chain,
69839ee533fSNamhyung Kim 						  const char *str, int offset,
69939ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
70039ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
70139ee533fSNamhyung Kim {
70239ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
70339ee533fSNamhyung Kim 
70439ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
70539ee533fSNamhyung Kim 				folded_sign, str);
70639ee533fSNamhyung Kim }
70739ee533fSNamhyung Kim 
70839ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
70939ee533fSNamhyung Kim 				     unsigned short row);
71039ee533fSNamhyung Kim 
71139ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
71239ee533fSNamhyung Kim 					    unsigned short row)
71339ee533fSNamhyung Kim {
71439ee533fSNamhyung Kim 	return browser->b.rows == row;
71539ee533fSNamhyung Kim }
71639ee533fSNamhyung Kim 
71739ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
71839ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
71939ee533fSNamhyung Kim {
72039ee533fSNamhyung Kim 	return false;
72139ee533fSNamhyung Kim }
72239ee533fSNamhyung Kim 
723aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
724aca7a94dSNamhyung Kim 
72518bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
72618bb8381SNamhyung Kim 					     struct callchain_node *node,
72718bb8381SNamhyung Kim 					     struct callchain_list *chain,
72818bb8381SNamhyung Kim 					     unsigned short row, u64 total,
72918bb8381SNamhyung Kim 					     bool need_percent, int offset,
73018bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
73118bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
73218bb8381SNamhyung Kim {
73318bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
73418bb8381SNamhyung Kim 	const char *str;
73518bb8381SNamhyung Kim 
73618bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
73718bb8381SNamhyung Kim 		arg->row_offset--;
73818bb8381SNamhyung Kim 		return 0;
73918bb8381SNamhyung Kim 	}
74018bb8381SNamhyung Kim 
74118bb8381SNamhyung Kim 	alloc_str = NULL;
74218bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
74318bb8381SNamhyung Kim 				       browser->show_dso);
74418bb8381SNamhyung Kim 
74518bb8381SNamhyung Kim 	if (need_percent) {
74618bb8381SNamhyung Kim 		char buf[64];
74718bb8381SNamhyung Kim 
74818bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
74918bb8381SNamhyung Kim 						total);
75018bb8381SNamhyung Kim 
75118bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
75218bb8381SNamhyung Kim 			str = "Not enough memory!";
75318bb8381SNamhyung Kim 		else
75418bb8381SNamhyung Kim 			str = alloc_str;
75518bb8381SNamhyung Kim 	}
75618bb8381SNamhyung Kim 
75718bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
75818bb8381SNamhyung Kim 
75918bb8381SNamhyung Kim 	free(alloc_str);
76018bb8381SNamhyung Kim 	return 1;
76118bb8381SNamhyung Kim }
76218bb8381SNamhyung Kim 
76359c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
76459c624e2SNamhyung Kim {
76559c624e2SNamhyung Kim 	struct callchain_node *child;
76659c624e2SNamhyung Kim 
76759c624e2SNamhyung Kim 	if (node == NULL)
76859c624e2SNamhyung Kim 		return false;
76959c624e2SNamhyung Kim 
77059c624e2SNamhyung Kim 	if (rb_next(node))
77159c624e2SNamhyung Kim 		return true;
77259c624e2SNamhyung Kim 
77359c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
77459c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
77559c624e2SNamhyung Kim }
77659c624e2SNamhyung Kim 
7774b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
7784b3a3212SNamhyung Kim 					     struct rb_root *root,
7794b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
78059c624e2SNamhyung Kim 					     u64 parent_total,
7814b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
7824b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
7834b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
7844b3a3212SNamhyung Kim {
7854b3a3212SNamhyung Kim 	struct rb_node *node;
7864b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
7874b3a3212SNamhyung Kim 	bool need_percent;
7884b3a3212SNamhyung Kim 
7894b3a3212SNamhyung Kim 	node = rb_first(root);
79059c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
7914b3a3212SNamhyung Kim 
7924b3a3212SNamhyung Kim 	while (node) {
7934b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
7944b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
7954b3a3212SNamhyung Kim 		struct callchain_list *chain;
7964b3a3212SNamhyung Kim 		char folded_sign = ' ';
7974b3a3212SNamhyung Kim 		int first = true;
7984b3a3212SNamhyung Kim 		int extra_offset = 0;
7994b3a3212SNamhyung Kim 
8004b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
8014b3a3212SNamhyung Kim 			bool was_first = first;
8024b3a3212SNamhyung Kim 
8034b3a3212SNamhyung Kim 			if (first)
8044b3a3212SNamhyung Kim 				first = false;
8054b3a3212SNamhyung Kim 			else if (need_percent)
8064b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8074b3a3212SNamhyung Kim 
8084b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8094b3a3212SNamhyung Kim 
8104b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8114b3a3212SNamhyung Kim 							chain, row, total,
8124b3a3212SNamhyung Kim 							was_first && need_percent,
8134b3a3212SNamhyung Kim 							offset + extra_offset,
8144b3a3212SNamhyung Kim 							print, arg);
8154b3a3212SNamhyung Kim 
8164b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8174b3a3212SNamhyung Kim 				goto out;
8184b3a3212SNamhyung Kim 
8194b3a3212SNamhyung Kim 			if (folded_sign == '+')
8204b3a3212SNamhyung Kim 				goto next;
8214b3a3212SNamhyung Kim 		}
8224b3a3212SNamhyung Kim 
8234b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
8244b3a3212SNamhyung Kim 			bool was_first = first;
8254b3a3212SNamhyung Kim 
8264b3a3212SNamhyung Kim 			if (first)
8274b3a3212SNamhyung Kim 				first = false;
8284b3a3212SNamhyung Kim 			else if (need_percent)
8294b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8304b3a3212SNamhyung Kim 
8314b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8324b3a3212SNamhyung Kim 
8334b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8344b3a3212SNamhyung Kim 							chain, row, total,
8354b3a3212SNamhyung Kim 							was_first && need_percent,
8364b3a3212SNamhyung Kim 							offset + extra_offset,
8374b3a3212SNamhyung Kim 							print, arg);
8384b3a3212SNamhyung Kim 
8394b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8404b3a3212SNamhyung Kim 				goto out;
8414b3a3212SNamhyung Kim 
8424b3a3212SNamhyung Kim 			if (folded_sign == '+')
8434b3a3212SNamhyung Kim 				break;
8444b3a3212SNamhyung Kim 		}
8454b3a3212SNamhyung Kim 
8464b3a3212SNamhyung Kim next:
8474b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
8484b3a3212SNamhyung Kim 			break;
8494b3a3212SNamhyung Kim 		node = next;
8504b3a3212SNamhyung Kim 	}
8514b3a3212SNamhyung Kim out:
8524b3a3212SNamhyung Kim 	return row - first_row;
8534b3a3212SNamhyung Kim }
8544b3a3212SNamhyung Kim 
8558c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
8568c430a34SNamhyung Kim 						struct callchain_list *chain,
8578c430a34SNamhyung Kim 						char *value_str, char *old_str)
8588c430a34SNamhyung Kim {
8598c430a34SNamhyung Kim 	char bf[1024];
8608c430a34SNamhyung Kim 	const char *str;
8618c430a34SNamhyung Kim 	char *new;
8628c430a34SNamhyung Kim 
8638c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
8648c430a34SNamhyung Kim 				       browser->show_dso);
8658c430a34SNamhyung Kim 	if (old_str) {
8668c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
8678c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
8688c430a34SNamhyung Kim 			new = NULL;
8698c430a34SNamhyung Kim 	} else {
8708c430a34SNamhyung Kim 		if (value_str) {
8718c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
8728c430a34SNamhyung Kim 				new = NULL;
8738c430a34SNamhyung Kim 		} else {
8748c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
8758c430a34SNamhyung Kim 				new = NULL;
8768c430a34SNamhyung Kim 		}
8778c430a34SNamhyung Kim 	}
8788c430a34SNamhyung Kim 	return new;
8798c430a34SNamhyung Kim }
8808c430a34SNamhyung Kim 
8818c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
8828c430a34SNamhyung Kim 					       struct rb_root *root,
8838c430a34SNamhyung Kim 					       unsigned short row, u64 total,
88459c624e2SNamhyung Kim 					       u64 parent_total,
8858c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
8868c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
8878c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
8888c430a34SNamhyung Kim {
8898c430a34SNamhyung Kim 	struct rb_node *node;
8908c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
8918c430a34SNamhyung Kim 	bool need_percent;
8928c430a34SNamhyung Kim 
8938c430a34SNamhyung Kim 	node = rb_first(root);
89459c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
8958c430a34SNamhyung Kim 
8968c430a34SNamhyung Kim 	while (node) {
8978c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
8988c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
8998c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
9008c430a34SNamhyung Kim 		int first = true;
9018c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
9028c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
9038c430a34SNamhyung Kim 
9048c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
9058c430a34SNamhyung Kim 			arg->row_offset--;
9068c430a34SNamhyung Kim 			goto next;
9078c430a34SNamhyung Kim 		}
9088c430a34SNamhyung Kim 
9098c430a34SNamhyung Kim 		if (need_percent) {
9108c430a34SNamhyung Kim 			char buf[64];
9118c430a34SNamhyung Kim 
9128c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
9138c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
9148c430a34SNamhyung Kim 				value_str = (char *)"<...>";
9158c430a34SNamhyung Kim 				goto do_print;
9168c430a34SNamhyung Kim 			}
9178c430a34SNamhyung Kim 			value_str_alloc = value_str;
9188c430a34SNamhyung Kim 		}
9198c430a34SNamhyung Kim 
9208c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9218c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9228c430a34SNamhyung Kim 						chain, value_str, chain_str);
9238c430a34SNamhyung Kim 			if (first) {
9248c430a34SNamhyung Kim 				first = false;
9258c430a34SNamhyung Kim 				first_chain = chain;
9268c430a34SNamhyung Kim 			}
9278c430a34SNamhyung Kim 
9288c430a34SNamhyung Kim 			if (chain_str == NULL) {
9298c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9308c430a34SNamhyung Kim 				goto do_print;
9318c430a34SNamhyung Kim 			}
9328c430a34SNamhyung Kim 
9338c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9348c430a34SNamhyung Kim 		}
9358c430a34SNamhyung Kim 
9368c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
9378c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9388c430a34SNamhyung Kim 						chain, value_str, chain_str);
9398c430a34SNamhyung Kim 			if (first) {
9408c430a34SNamhyung Kim 				first = false;
9418c430a34SNamhyung Kim 				first_chain = chain;
9428c430a34SNamhyung Kim 			}
9438c430a34SNamhyung Kim 
9448c430a34SNamhyung Kim 			if (chain_str == NULL) {
9458c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9468c430a34SNamhyung Kim 				goto do_print;
9478c430a34SNamhyung Kim 			}
9488c430a34SNamhyung Kim 
9498c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9508c430a34SNamhyung Kim 		}
9518c430a34SNamhyung Kim 
9528c430a34SNamhyung Kim do_print:
9538c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
9548c430a34SNamhyung Kim 		free(value_str_alloc);
9558c430a34SNamhyung Kim 		free(chain_str_alloc);
9568c430a34SNamhyung Kim 
9578c430a34SNamhyung Kim next:
9588c430a34SNamhyung Kim 		if (is_output_full(browser, row))
9598c430a34SNamhyung Kim 			break;
9608c430a34SNamhyung Kim 		node = next;
9618c430a34SNamhyung Kim 	}
9628c430a34SNamhyung Kim 
9638c430a34SNamhyung Kim 	return row - first_row;
9648c430a34SNamhyung Kim }
9658c430a34SNamhyung Kim 
9660c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
967c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
96839ee533fSNamhyung Kim 					unsigned short row, u64 total,
9695eca104eSNamhyung Kim 					u64 parent_total,
97039ee533fSNamhyung Kim 					print_callchain_entry_fn print,
97139ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
97239ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
973aca7a94dSNamhyung Kim {
974aca7a94dSNamhyung Kim 	struct rb_node *node;
975f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
9764087d11cSNamhyung Kim 	bool need_percent;
9775eca104eSNamhyung Kim 	u64 percent_total = total;
9785eca104eSNamhyung Kim 
9795eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
9805eca104eSNamhyung Kim 		percent_total = parent_total;
981aca7a94dSNamhyung Kim 
982c09a7e75SNamhyung Kim 	node = rb_first(root);
98359c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9844087d11cSNamhyung Kim 
985aca7a94dSNamhyung Kim 	while (node) {
986aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
987aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
988aca7a94dSNamhyung Kim 		struct callchain_list *chain;
989aca7a94dSNamhyung Kim 		char folded_sign = ' ';
990aca7a94dSNamhyung Kim 		int first = true;
991aca7a94dSNamhyung Kim 		int extra_offset = 0;
992aca7a94dSNamhyung Kim 
993aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
994aca7a94dSNamhyung Kim 			bool was_first = first;
995aca7a94dSNamhyung Kim 
996aca7a94dSNamhyung Kim 			if (first)
997aca7a94dSNamhyung Kim 				first = false;
9984087d11cSNamhyung Kim 			else if (need_percent)
999aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1000aca7a94dSNamhyung Kim 
1001aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1002aca7a94dSNamhyung Kim 
100318bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
10045eca104eSNamhyung Kim 							chain, row, percent_total,
100518bb8381SNamhyung Kim 							was_first && need_percent,
100618bb8381SNamhyung Kim 							offset + extra_offset,
100718bb8381SNamhyung Kim 							print, arg);
1008c09a7e75SNamhyung Kim 
100918bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1010aca7a94dSNamhyung Kim 				goto out;
101118bb8381SNamhyung Kim 
1012aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1013aca7a94dSNamhyung Kim 				break;
1014aca7a94dSNamhyung Kim 		}
1015aca7a94dSNamhyung Kim 
1016aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1017aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1018c09a7e75SNamhyung Kim 
10190c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
10205eca104eSNamhyung Kim 							    new_level, row, total,
10215eca104eSNamhyung Kim 							    child->children_hit,
102239ee533fSNamhyung Kim 							    print, arg, is_output_full);
1023aca7a94dSNamhyung Kim 		}
102439ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1025c09a7e75SNamhyung Kim 			break;
1026aca7a94dSNamhyung Kim 		node = next;
1027aca7a94dSNamhyung Kim 	}
1028aca7a94dSNamhyung Kim out:
1029aca7a94dSNamhyung Kim 	return row - first_row;
1030aca7a94dSNamhyung Kim }
1031aca7a94dSNamhyung Kim 
10320c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
10330c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
10340c841c6cSNamhyung Kim 					unsigned short row,
10350c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
10360c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
10370c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
10380c841c6cSNamhyung Kim {
10390c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
10405eca104eSNamhyung Kim 	u64 parent_total;
10410c841c6cSNamhyung Kim 	int printed;
10420c841c6cSNamhyung Kim 
10430c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
10445eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
10450c841c6cSNamhyung Kim 	else
10465eca104eSNamhyung Kim 		parent_total = entry->stat.period;
10470c841c6cSNamhyung Kim 
10480c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
10490c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
10505eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10515eca104eSNamhyung Kim 						total, parent_total, print, arg,
10525eca104eSNamhyung Kim 						is_output_full);
10530c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
10540c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
10555eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10565eca104eSNamhyung Kim 						total, parent_total, print, arg,
10575eca104eSNamhyung Kim 						is_output_full);
10580c841c6cSNamhyung Kim 	} else {
10590c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
10605eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
10615eca104eSNamhyung Kim 						total, parent_total, print, arg,
10625eca104eSNamhyung Kim 						is_output_full);
10630c841c6cSNamhyung Kim 	}
10640c841c6cSNamhyung Kim 
10650c841c6cSNamhyung Kim 	if (arg->is_current_entry)
10660c841c6cSNamhyung Kim 		browser->he_selection = entry;
10670c841c6cSNamhyung Kim 
10680c841c6cSNamhyung Kim 	return printed;
10690c841c6cSNamhyung Kim }
10700c841c6cSNamhyung Kim 
107189701460SNamhyung Kim struct hpp_arg {
107289701460SNamhyung Kim 	struct ui_browser *b;
107389701460SNamhyung Kim 	char folded_sign;
107489701460SNamhyung Kim 	bool current_entry;
107589701460SNamhyung Kim };
107689701460SNamhyung Kim 
10772f6d9009SNamhyung Kim static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
10782f6d9009SNamhyung Kim {
10792f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1080d675107cSNamhyung Kim 	int ret, len;
10812f6d9009SNamhyung Kim 	va_list args;
10822f6d9009SNamhyung Kim 	double percent;
10832f6d9009SNamhyung Kim 
10842f6d9009SNamhyung Kim 	va_start(args, fmt);
1085d675107cSNamhyung Kim 	len = va_arg(args, int);
10862f6d9009SNamhyung Kim 	percent = va_arg(args, double);
10872f6d9009SNamhyung Kim 	va_end(args);
10885aed9d24SNamhyung Kim 
108989701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
10905aed9d24SNamhyung Kim 
1091d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1092517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
109389701460SNamhyung Kim 
10942f6d9009SNamhyung Kim 	advance_hpp(hpp, ret);
10955aed9d24SNamhyung Kim 	return ret;
1096f5951d56SNamhyung Kim }
1097f5951d56SNamhyung Kim 
1098fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
10995aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
11005aed9d24SNamhyung Kim {									\
11015aed9d24SNamhyung Kim 	return he->stat._field;						\
11025aed9d24SNamhyung Kim }									\
11035aed9d24SNamhyung Kim 									\
11042c5d4b4aSJiri Olsa static int								\
11055b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11062c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
11075aed9d24SNamhyung Kim 				struct hist_entry *he)			\
11085aed9d24SNamhyung Kim {									\
11095b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
11102f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
11115aed9d24SNamhyung Kim }
1112f5951d56SNamhyung Kim 
11130434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
11140434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
11150434ddd2SNamhyung Kim {									\
11160434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
11170434ddd2SNamhyung Kim }									\
11180434ddd2SNamhyung Kim 									\
11190434ddd2SNamhyung Kim static int								\
11205b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11210434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
11220434ddd2SNamhyung Kim 				struct hist_entry *he)			\
11230434ddd2SNamhyung Kim {									\
11240434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1125517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
11265b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1127d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
11285b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1129517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
11300434ddd2SNamhyung Kim 									\
11310434ddd2SNamhyung Kim 		return ret;						\
11320434ddd2SNamhyung Kim 	}								\
11335b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
11345b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
11350434ddd2SNamhyung Kim }
11360434ddd2SNamhyung Kim 
1137fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1138fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1139fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1140fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1141fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
11420434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
11435aed9d24SNamhyung Kim 
11445aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
11450434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1146f5951d56SNamhyung Kim 
1147f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1148f5951d56SNamhyung Kim {
1149f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1150f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1151f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1152f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1153f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1154f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1155f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1156f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1157f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1158f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
11590434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
11600434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
1161f5951d56SNamhyung Kim }
1162f5951d56SNamhyung Kim 
116305e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1164aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1165aca7a94dSNamhyung Kim 				    unsigned short row)
1166aca7a94dSNamhyung Kim {
11671240005eSJiri Olsa 	int printed = 0;
116867d25916SNamhyung Kim 	int width = browser->b.width;
1169aca7a94dSNamhyung Kim 	char folded_sign = ' ';
117005e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1171aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
117263a1a3d8SNamhyung Kim 	bool first = true;
11731240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1174aca7a94dSNamhyung Kim 
1175aca7a94dSNamhyung Kim 	if (current_entry) {
117605e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
117705e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1178aca7a94dSNamhyung Kim 	}
1179aca7a94dSNamhyung Kim 
1180aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
1181aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1182aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1183aca7a94dSNamhyung Kim 	}
1184aca7a94dSNamhyung Kim 
1185aca7a94dSNamhyung Kim 	if (row_offset == 0) {
118689701460SNamhyung Kim 		struct hpp_arg arg = {
118789701460SNamhyung Kim 			.b		= &browser->b,
118889701460SNamhyung Kim 			.folded_sign	= folded_sign,
118989701460SNamhyung Kim 			.current_entry	= current_entry,
119089701460SNamhyung Kim 		};
1191c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1192f5951d56SNamhyung Kim 
1193ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
1194f5951d56SNamhyung Kim 
1195f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
119689fee709SArnaldo Carvalho de Melo 			char s[2048];
119789fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
119889fee709SArnaldo Carvalho de Melo 				.buf	= s,
119989fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
120089fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
120189fee709SArnaldo Carvalho de Melo 			};
120289fee709SArnaldo Carvalho de Melo 
1203361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1204361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1205e67d49a7SNamhyung Kim 				continue;
1206e67d49a7SNamhyung Kim 
1207fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1208fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1209fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1210fb821c9eSNamhyung Kim 			} else {
1211fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1212fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1213fb821c9eSNamhyung Kim 			}
1214fb821c9eSNamhyung Kim 
1215fb821c9eSNamhyung Kim 			if (first) {
1216fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
1217517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1218f5951d56SNamhyung Kim 					width -= 2;
1219f5951d56SNamhyung Kim 				}
122063a1a3d8SNamhyung Kim 				first = false;
1221fb821c9eSNamhyung Kim 			} else {
1222517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1223fb821c9eSNamhyung Kim 				width -= 2;
1224fb821c9eSNamhyung Kim 			}
1225f5951d56SNamhyung Kim 
12261240005eSJiri Olsa 			if (fmt->color) {
122789fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
122889fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
122989fee709SArnaldo Carvalho de Melo 				/*
123089fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
123189fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
123289fee709SArnaldo Carvalho de Melo 				 */
123389fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1234f5951d56SNamhyung Kim 			} else {
123589fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1236517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1237f5951d56SNamhyung Kim 			}
123889fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1239f5951d56SNamhyung Kim 		}
1240aca7a94dSNamhyung Kim 
1241aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
124205e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1243aca7a94dSNamhyung Kim 			width += 1;
1244aca7a94dSNamhyung Kim 
124526270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
124626d8b338SNamhyung Kim 
1247aca7a94dSNamhyung Kim 		++row;
1248aca7a94dSNamhyung Kim 		++printed;
1249aca7a94dSNamhyung Kim 	} else
1250aca7a94dSNamhyung Kim 		--row_offset;
1251aca7a94dSNamhyung Kim 
125262c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
125339ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
125439ee533fSNamhyung Kim 			.row_offset = row_offset,
125539ee533fSNamhyung Kim 			.is_current_entry = current_entry,
125639ee533fSNamhyung Kim 		};
1257c09a7e75SNamhyung Kim 
12580c841c6cSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry, 1, row,
12594b3a3212SNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
12604b3a3212SNamhyung Kim 					hist_browser__check_output_full);
1261aca7a94dSNamhyung Kim 	}
1262aca7a94dSNamhyung Kim 
1263aca7a94dSNamhyung Kim 	return printed;
1264aca7a94dSNamhyung Kim }
1265aca7a94dSNamhyung Kim 
1266d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1267d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1268d0506edbSNamhyung Kim 					      unsigned short row,
12692dbbe9f2SNamhyung Kim 					      int level)
1270d0506edbSNamhyung Kim {
1271d0506edbSNamhyung Kim 	int printed = 0;
1272d0506edbSNamhyung Kim 	int width = browser->b.width;
1273d0506edbSNamhyung Kim 	char folded_sign = ' ';
1274d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1275d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1276d0506edbSNamhyung Kim 	bool first = true;
1277d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1278a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1279d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1280d0506edbSNamhyung Kim 		.b		= &browser->b,
1281d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1282d0506edbSNamhyung Kim 	};
1283d0506edbSNamhyung Kim 	int column = 0;
12842dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1285d0506edbSNamhyung Kim 
1286d0506edbSNamhyung Kim 	if (current_entry) {
1287d0506edbSNamhyung Kim 		browser->he_selection = entry;
1288d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1289d0506edbSNamhyung Kim 	}
1290d0506edbSNamhyung Kim 
1291d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1292d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1293d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1294d0506edbSNamhyung Kim 
1295d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1296d0506edbSNamhyung Kim 		row_offset--;
1297d0506edbSNamhyung Kim 		goto show_callchain;
1298d0506edbSNamhyung Kim 	}
1299d0506edbSNamhyung Kim 
1300d0506edbSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
1301d0506edbSNamhyung Kim 
1302d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1303d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1304d0506edbSNamhyung Kim 	else
1305d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1306d0506edbSNamhyung Kim 
1307d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1308d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1309d0506edbSNamhyung Kim 
1310a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1311a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1312a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1313a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1314d0506edbSNamhyung Kim 		char s[2048];
1315d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1316d0506edbSNamhyung Kim 			.buf		= s,
1317d0506edbSNamhyung Kim 			.size		= sizeof(s),
1318d0506edbSNamhyung Kim 			.ptr		= &arg,
1319d0506edbSNamhyung Kim 		};
1320d0506edbSNamhyung Kim 
1321d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1322d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1323d0506edbSNamhyung Kim 			continue;
1324d0506edbSNamhyung Kim 
1325d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1326d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1327d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1328d0506edbSNamhyung Kim 		} else {
1329d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1330d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1331d0506edbSNamhyung Kim 		}
1332d0506edbSNamhyung Kim 
1333d0506edbSNamhyung Kim 		if (first) {
1334d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c", folded_sign);
1335d0506edbSNamhyung Kim 			width--;
1336d0506edbSNamhyung Kim 			first = false;
1337d0506edbSNamhyung Kim 		} else {
1338d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1339d0506edbSNamhyung Kim 			width -= 2;
1340d0506edbSNamhyung Kim 		}
1341d0506edbSNamhyung Kim 
1342d0506edbSNamhyung Kim 		if (fmt->color) {
1343d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1344d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1345d0506edbSNamhyung Kim 			/*
1346d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1347d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1348d0506edbSNamhyung Kim 			 */
1349d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1350d0506edbSNamhyung Kim 		} else {
1351d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1352d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1353d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1354d0506edbSNamhyung Kim 		}
1355d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1356d0506edbSNamhyung Kim 	}
1357d0506edbSNamhyung Kim 
1358d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1359d0506edbSNamhyung Kim 	width -= hierarchy_indent;
1360d0506edbSNamhyung Kim 
1361d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1362d0506edbSNamhyung Kim 		char s[2048];
1363d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1364d0506edbSNamhyung Kim 			.buf		= s,
1365d0506edbSNamhyung Kim 			.size		= sizeof(s),
1366d0506edbSNamhyung Kim 			.ptr		= &arg,
1367d0506edbSNamhyung Kim 		};
1368d0506edbSNamhyung Kim 
1369d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1370d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1371d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1372d0506edbSNamhyung Kim 		} else {
1373d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1374d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1375d0506edbSNamhyung Kim 		}
1376d0506edbSNamhyung Kim 
13771b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1378d0506edbSNamhyung Kim 			ui_browser__write_nstring(&browser->b, "", 2);
1379d0506edbSNamhyung Kim 			width -= 2;
1380d0506edbSNamhyung Kim 
1381d0506edbSNamhyung Kim 			/*
1382d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1383d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1384d0506edbSNamhyung Kim 			 * hierarchy mode.
1385d0506edbSNamhyung Kim 			 */
1386d0506edbSNamhyung Kim 			if (fmt->color) {
1387d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1388d0506edbSNamhyung Kim 			} else {
1389cb1fab91SNamhyung Kim 				int i = 0;
1390cb1fab91SNamhyung Kim 
1391d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
1392cb1fab91SNamhyung Kim 				ui_browser__printf(&browser->b, "%s", ltrim(s));
1393cb1fab91SNamhyung Kim 
1394cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1395cb1fab91SNamhyung Kim 					width++;
1396d0506edbSNamhyung Kim 			}
1397d0506edbSNamhyung Kim 		}
13981b2dbbf4SNamhyung Kim 	}
1399d0506edbSNamhyung Kim 
1400d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1401d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1402d0506edbSNamhyung Kim 		width += 1;
1403d0506edbSNamhyung Kim 
1404d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1405d0506edbSNamhyung Kim 
1406d0506edbSNamhyung Kim 	++row;
1407d0506edbSNamhyung Kim 	++printed;
1408d0506edbSNamhyung Kim 
1409d0506edbSNamhyung Kim show_callchain:
1410d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1411d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1412d0506edbSNamhyung Kim 			.row_offset = row_offset,
1413d0506edbSNamhyung Kim 		};
1414d0506edbSNamhyung Kim 
1415d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1416d0506edbSNamhyung Kim 					level + 1, row,
1417d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1418d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1419d0506edbSNamhyung Kim 	}
1420d0506edbSNamhyung Kim 
1421d0506edbSNamhyung Kim 	return printed;
1422d0506edbSNamhyung Kim }
1423d0506edbSNamhyung Kim 
142479dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
14252dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
142679dded87SNamhyung Kim {
142779dded87SNamhyung Kim 	int width = browser->b.width;
142879dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
142979dded87SNamhyung Kim 	bool first = true;
143079dded87SNamhyung Kim 	int column = 0;
143179dded87SNamhyung Kim 	int ret;
143279dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1433a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
14342dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
143579dded87SNamhyung Kim 
143679dded87SNamhyung Kim 	if (current_entry) {
143779dded87SNamhyung Kim 		browser->he_selection = NULL;
143879dded87SNamhyung Kim 		browser->selection = NULL;
143979dded87SNamhyung Kim 	}
144079dded87SNamhyung Kim 
144179dded87SNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
144279dded87SNamhyung Kim 
144379dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
144479dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
144579dded87SNamhyung Kim 	else
144679dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
144779dded87SNamhyung Kim 
144879dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
144979dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
145079dded87SNamhyung Kim 
1451a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1452a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1453a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1454a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
145579dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
145679dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
145779dded87SNamhyung Kim 			continue;
145879dded87SNamhyung Kim 
1459da1b0407SJiri Olsa 		ret = fmt->width(fmt, NULL, browser->hists);
146079dded87SNamhyung Kim 
146179dded87SNamhyung Kim 		if (first) {
146279dded87SNamhyung Kim 			/* for folded sign */
146379dded87SNamhyung Kim 			first = false;
146479dded87SNamhyung Kim 			ret++;
146579dded87SNamhyung Kim 		} else {
146679dded87SNamhyung Kim 			/* space between columns */
146779dded87SNamhyung Kim 			ret += 2;
146879dded87SNamhyung Kim 		}
146979dded87SNamhyung Kim 
147079dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
147179dded87SNamhyung Kim 		width -= ret;
147279dded87SNamhyung Kim 	}
147379dded87SNamhyung Kim 
14742dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
14752dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
147679dded87SNamhyung Kim 
147779dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
147879dded87SNamhyung Kim 		char buf[32];
147979dded87SNamhyung Kim 
148079dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
148179dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
148279dded87SNamhyung Kim 		width -= ret + 2;
148379dded87SNamhyung Kim 	}
148479dded87SNamhyung Kim 
148579dded87SNamhyung Kim 	/* The scroll bar isn't being used */
148679dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
148779dded87SNamhyung Kim 		width += 1;
148879dded87SNamhyung Kim 
148979dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
149079dded87SNamhyung Kim 	return 1;
149179dded87SNamhyung Kim }
149279dded87SNamhyung Kim 
149381a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
149481a888feSJiri Olsa {
149581a888feSJiri Olsa 	advance_hpp(hpp, inc);
149681a888feSJiri Olsa 	return hpp->size <= 0;
149781a888feSJiri Olsa }
149881a888feSJiri Olsa 
1499c6c3c02dSArnaldo Carvalho de Melo static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
150081a888feSJiri Olsa {
1501c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
150281a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
150381a888feSJiri Olsa 		.buf    = buf,
150481a888feSJiri Olsa 		.size   = size,
150581a888feSJiri Olsa 	};
150681a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
150781a888feSJiri Olsa 	size_t ret = 0;
1508c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
150981a888feSJiri Olsa 
151081a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
151181a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
151281a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
151381a888feSJiri Olsa 			return ret;
151481a888feSJiri Olsa 	}
151581a888feSJiri Olsa 
1516f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1517361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
151881a888feSJiri Olsa 			continue;
151981a888feSJiri Olsa 
152005372173SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists);
152181a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
152281a888feSJiri Olsa 			break;
152381a888feSJiri Olsa 
152481a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
152581a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
152681a888feSJiri Olsa 			break;
152781a888feSJiri Olsa 	}
152881a888feSJiri Olsa 
152981a888feSJiri Olsa 	return ret;
153081a888feSJiri Olsa }
153181a888feSJiri Olsa 
1532d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1533d8b92400SNamhyung Kim {
1534d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1535d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1536d8b92400SNamhyung Kim 		.buf    = buf,
1537d8b92400SNamhyung Kim 		.size   = size,
1538d8b92400SNamhyung Kim 	};
1539d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1540a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1541d8b92400SNamhyung Kim 	size_t ret = 0;
1542d8b92400SNamhyung Kim 	int column = 0;
15432dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1544a61a22f6SNamhyung Kim 	bool first_node, first_col;
1545d8b92400SNamhyung Kim 
1546d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, " ");
1547d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1548d8b92400SNamhyung Kim 		return ret;
1549d8b92400SNamhyung Kim 
1550a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1551a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1552a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1553a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1554d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1555d8b92400SNamhyung Kim 			continue;
1556d8b92400SNamhyung Kim 
155705372173SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists);
1558d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1559d8b92400SNamhyung Kim 			break;
1560d8b92400SNamhyung Kim 
1561d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1562d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1563d8b92400SNamhyung Kim 			break;
1564d8b92400SNamhyung Kim 	}
1565d8b92400SNamhyung Kim 
1566d8b92400SNamhyung Kim 	ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
15672dbbe9f2SNamhyung Kim 			indent * HIERARCHY_INDENT, "");
1568d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1569d8b92400SNamhyung Kim 		return ret;
1570d8b92400SNamhyung Kim 
1571a61a22f6SNamhyung Kim 	first_node = true;
1572a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1573a61a22f6SNamhyung Kim 		if (!first_node) {
1574d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1575d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1576d8b92400SNamhyung Kim 				break;
1577d8b92400SNamhyung Kim 		}
1578a61a22f6SNamhyung Kim 		first_node = false;
1579a61a22f6SNamhyung Kim 
1580a61a22f6SNamhyung Kim 		first_col = true;
1581a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1582a61a22f6SNamhyung Kim 			char *start;
1583a61a22f6SNamhyung Kim 
1584a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1585a61a22f6SNamhyung Kim 				continue;
1586a61a22f6SNamhyung Kim 
1587a61a22f6SNamhyung Kim 			if (!first_col) {
1588a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1589a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1590a61a22f6SNamhyung Kim 					break;
1591a61a22f6SNamhyung Kim 			}
1592a61a22f6SNamhyung Kim 			first_col = false;
1593d8b92400SNamhyung Kim 
159405372173SJiri Olsa 			ret = fmt->header(fmt, &dummy_hpp, hists);
1595d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1596d8b92400SNamhyung Kim 
15977d6a7e78SJiri Olsa 			start = trim(dummy_hpp.buf);
1598cb1fab91SNamhyung Kim 			ret = strlen(start);
1599cb1fab91SNamhyung Kim 
1600cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1601cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1602cb1fab91SNamhyung Kim 
1603d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1604d8b92400SNamhyung Kim 				break;
1605d8b92400SNamhyung Kim 		}
1606a61a22f6SNamhyung Kim 	}
1607d8b92400SNamhyung Kim 
1608d8b92400SNamhyung Kim 	return ret;
1609d8b92400SNamhyung Kim }
1610d8b92400SNamhyung Kim 
161101b4770dSJiri Olsa static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1612025bf7eaSArnaldo Carvalho de Melo {
161381a888feSJiri Olsa 	char headers[1024];
161481a888feSJiri Olsa 
1615d8b92400SNamhyung Kim 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1616d8b92400SNamhyung Kim 						   sizeof(headers));
161701b4770dSJiri Olsa 
1618025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1619025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
162026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1621025bf7eaSArnaldo Carvalho de Melo }
1622025bf7eaSArnaldo Carvalho de Melo 
162301b4770dSJiri Olsa static void hists_browser__headers(struct hist_browser *browser)
162401b4770dSJiri Olsa {
162501b4770dSJiri Olsa 	char headers[1024];
162601b4770dSJiri Olsa 
162701b4770dSJiri Olsa 	hists_browser__scnprintf_headers(browser, headers,
162801b4770dSJiri Olsa 					 sizeof(headers));
162901b4770dSJiri Olsa 
163001b4770dSJiri Olsa 	ui_browser__gotorc(&browser->b, 0, 0);
163101b4770dSJiri Olsa 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
163201b4770dSJiri Olsa 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
163301b4770dSJiri Olsa }
163401b4770dSJiri Olsa 
163501b4770dSJiri Olsa static void hist_browser__show_headers(struct hist_browser *browser)
163601b4770dSJiri Olsa {
163701b4770dSJiri Olsa 	if (symbol_conf.report_hierarchy)
163801b4770dSJiri Olsa 		hists_browser__hierarchy_headers(browser);
163901b4770dSJiri Olsa 	else
164001b4770dSJiri Olsa 		hists_browser__headers(browser);
164101b4770dSJiri Olsa }
164201b4770dSJiri Olsa 
1643aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1644aca7a94dSNamhyung Kim {
1645aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1646aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1647aca7a94dSNamhyung Kim 
1648aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
1649aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
1650aca7a94dSNamhyung Kim 	}
1651aca7a94dSNamhyung Kim }
1652aca7a94dSNamhyung Kim 
165305e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1654aca7a94dSNamhyung Kim {
1655aca7a94dSNamhyung Kim 	unsigned row = 0;
1656025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
1657aca7a94dSNamhyung Kim 	struct rb_node *nd;
165805e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1659aca7a94dSNamhyung Kim 
1660025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
1661025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1662025bf7eaSArnaldo Carvalho de Melo 		header_offset = 1;
1663025bf7eaSArnaldo Carvalho de Melo 	}
1664025bf7eaSArnaldo Carvalho de Melo 
166505e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1666979d2cacSWang Nan 	hb->he_selection = NULL;
1667979d2cacSWang Nan 	hb->selection = NULL;
1668aca7a94dSNamhyung Kim 
1669d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1670aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
167114135663SNamhyung Kim 		float percent;
1672aca7a94dSNamhyung Kim 
1673d0506edbSNamhyung Kim 		if (h->filtered) {
1674d0506edbSNamhyung Kim 			/* let it move to sibling */
1675d0506edbSNamhyung Kim 			h->unfolded = false;
1676aca7a94dSNamhyung Kim 			continue;
1677d0506edbSNamhyung Kim 		}
1678aca7a94dSNamhyung Kim 
167914135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
1680064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1681064f1981SNamhyung Kim 			continue;
1682064f1981SNamhyung Kim 
1683d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1684d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
16852dbbe9f2SNamhyung Kim 								  h->depth);
168679dded87SNamhyung Kim 			if (row == browser->rows)
168779dded87SNamhyung Kim 				break;
168879dded87SNamhyung Kim 
168979dded87SNamhyung Kim 			if (h->has_no_entry) {
1690a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
169179dded87SNamhyung Kim 				row++;
169279dded87SNamhyung Kim 			}
1693d0506edbSNamhyung Kim 		} else {
1694aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1695d0506edbSNamhyung Kim 		}
1696d0506edbSNamhyung Kim 
169762c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1698aca7a94dSNamhyung Kim 			break;
1699aca7a94dSNamhyung Kim 	}
1700aca7a94dSNamhyung Kim 
1701025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
1702aca7a94dSNamhyung Kim }
1703aca7a94dSNamhyung Kim 
1704064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1705064f1981SNamhyung Kim 					     float min_pcnt)
1706aca7a94dSNamhyung Kim {
1707aca7a94dSNamhyung Kim 	while (nd != NULL) {
1708aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
170914135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1710064f1981SNamhyung Kim 
1711c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1712aca7a94dSNamhyung Kim 			return nd;
1713aca7a94dSNamhyung Kim 
1714d0506edbSNamhyung Kim 		/*
1715d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1716d0506edbSNamhyung Kim 		 * So move to sibling node.
1717d0506edbSNamhyung Kim 		 */
1718d0506edbSNamhyung Kim 		if (rb_next(nd))
1719aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1720d0506edbSNamhyung Kim 		else
1721d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1722aca7a94dSNamhyung Kim 	}
1723aca7a94dSNamhyung Kim 
1724aca7a94dSNamhyung Kim 	return NULL;
1725aca7a94dSNamhyung Kim }
1726aca7a94dSNamhyung Kim 
1727064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1728064f1981SNamhyung Kim 						  float min_pcnt)
1729aca7a94dSNamhyung Kim {
1730aca7a94dSNamhyung Kim 	while (nd != NULL) {
1731aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
173214135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1733064f1981SNamhyung Kim 
1734064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1735aca7a94dSNamhyung Kim 			return nd;
1736aca7a94dSNamhyung Kim 
1737d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1738aca7a94dSNamhyung Kim 	}
1739aca7a94dSNamhyung Kim 
1740aca7a94dSNamhyung Kim 	return NULL;
1741aca7a94dSNamhyung Kim }
1742aca7a94dSNamhyung Kim 
174305e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1744aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1745aca7a94dSNamhyung Kim {
1746aca7a94dSNamhyung Kim 	struct hist_entry *h;
1747aca7a94dSNamhyung Kim 	struct rb_node *nd;
1748aca7a94dSNamhyung Kim 	bool first = true;
1749064f1981SNamhyung Kim 	struct hist_browser *hb;
1750064f1981SNamhyung Kim 
1751064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1752aca7a94dSNamhyung Kim 
175305e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1754aca7a94dSNamhyung Kim 		return;
1755aca7a94dSNamhyung Kim 
175605e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1757aca7a94dSNamhyung Kim 
1758aca7a94dSNamhyung Kim 	switch (whence) {
1759aca7a94dSNamhyung Kim 	case SEEK_SET:
1760064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
176114135663SNamhyung Kim 					   hb->min_pcnt);
1762aca7a94dSNamhyung Kim 		break;
1763aca7a94dSNamhyung Kim 	case SEEK_CUR:
176405e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1765aca7a94dSNamhyung Kim 		goto do_offset;
1766aca7a94dSNamhyung Kim 	case SEEK_END:
1767d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1768d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1769aca7a94dSNamhyung Kim 		first = false;
1770aca7a94dSNamhyung Kim 		break;
1771aca7a94dSNamhyung Kim 	default:
1772aca7a94dSNamhyung Kim 		return;
1773aca7a94dSNamhyung Kim 	}
1774aca7a94dSNamhyung Kim 
1775aca7a94dSNamhyung Kim 	/*
1776aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1777aca7a94dSNamhyung Kim 	 * row_offset:
1778aca7a94dSNamhyung Kim 	 */
177905e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1780aca7a94dSNamhyung Kim 	h->row_offset = 0;
1781aca7a94dSNamhyung Kim 
1782aca7a94dSNamhyung Kim 	/*
1783aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1784aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1785aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1786aca7a94dSNamhyung Kim 	 *
1787aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1788aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1789aca7a94dSNamhyung Kim 	 *
1790aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1791aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1792aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1793aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1794aca7a94dSNamhyung Kim 	 */
1795aca7a94dSNamhyung Kim do_offset:
1796837eeb75SWang Nan 	if (!nd)
1797837eeb75SWang Nan 		return;
1798837eeb75SWang Nan 
1799aca7a94dSNamhyung Kim 	if (offset > 0) {
1800aca7a94dSNamhyung Kim 		do {
1801aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1802d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1803aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1804aca7a94dSNamhyung Kim 				if (offset > remaining) {
1805aca7a94dSNamhyung Kim 					offset -= remaining;
1806aca7a94dSNamhyung Kim 					h->row_offset = 0;
1807aca7a94dSNamhyung Kim 				} else {
1808aca7a94dSNamhyung Kim 					h->row_offset += offset;
1809aca7a94dSNamhyung Kim 					offset = 0;
181005e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1811aca7a94dSNamhyung Kim 					break;
1812aca7a94dSNamhyung Kim 				}
1813aca7a94dSNamhyung Kim 			}
1814d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1815d0506edbSNamhyung Kim 						   hb->min_pcnt);
1816aca7a94dSNamhyung Kim 			if (nd == NULL)
1817aca7a94dSNamhyung Kim 				break;
1818aca7a94dSNamhyung Kim 			--offset;
181905e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1820aca7a94dSNamhyung Kim 		} while (offset != 0);
1821aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1822aca7a94dSNamhyung Kim 		while (1) {
1823aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1824d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1825aca7a94dSNamhyung Kim 				if (first) {
1826aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1827aca7a94dSNamhyung Kim 						offset += h->row_offset;
1828aca7a94dSNamhyung Kim 						h->row_offset = 0;
1829aca7a94dSNamhyung Kim 					} else {
1830aca7a94dSNamhyung Kim 						h->row_offset += offset;
1831aca7a94dSNamhyung Kim 						offset = 0;
183205e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1833aca7a94dSNamhyung Kim 						break;
1834aca7a94dSNamhyung Kim 					}
1835aca7a94dSNamhyung Kim 				} else {
1836aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1837aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1838aca7a94dSNamhyung Kim 						h->row_offset = 0;
1839aca7a94dSNamhyung Kim 					} else {
1840aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1841aca7a94dSNamhyung Kim 						offset = 0;
184205e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1843aca7a94dSNamhyung Kim 						break;
1844aca7a94dSNamhyung Kim 					}
1845aca7a94dSNamhyung Kim 				}
1846aca7a94dSNamhyung Kim 			}
1847aca7a94dSNamhyung Kim 
1848d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1849064f1981SNamhyung Kim 							hb->min_pcnt);
1850aca7a94dSNamhyung Kim 			if (nd == NULL)
1851aca7a94dSNamhyung Kim 				break;
1852aca7a94dSNamhyung Kim 			++offset;
185305e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1854aca7a94dSNamhyung Kim 			if (offset == 0) {
1855aca7a94dSNamhyung Kim 				/*
1856aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1857aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1858aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1859aca7a94dSNamhyung Kim 				 */
1860aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
1861d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
1862aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1863aca7a94dSNamhyung Kim 				break;
1864aca7a94dSNamhyung Kim 			}
1865aca7a94dSNamhyung Kim 			first = false;
1866aca7a94dSNamhyung Kim 		}
1867aca7a94dSNamhyung Kim 	} else {
186805e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1869aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1870aca7a94dSNamhyung Kim 		h->row_offset = 0;
1871aca7a94dSNamhyung Kim 	}
1872aca7a94dSNamhyung Kim }
1873aca7a94dSNamhyung Kim 
1874aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1875d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
1876d0506edbSNamhyung Kim 					   int level)
1877aff3f3f6SArnaldo Carvalho de Melo {
187839ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
187939ee533fSNamhyung Kim 		.fp = fp,
188039ee533fSNamhyung Kim 	};
1881aff3f3f6SArnaldo Carvalho de Melo 
1882d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
188339ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
188439ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
188539ee533fSNamhyung Kim 	return arg.printed;
1886aff3f3f6SArnaldo Carvalho de Melo }
1887aff3f3f6SArnaldo Carvalho de Melo 
1888aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1889aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1890aff3f3f6SArnaldo Carvalho de Melo {
1891aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1892aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1893aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
189426d8b338SNamhyung Kim 	struct perf_hpp hpp = {
189526d8b338SNamhyung Kim 		.buf = s,
189626d8b338SNamhyung Kim 		.size = sizeof(s),
189726d8b338SNamhyung Kim 	};
189826d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
189926d8b338SNamhyung Kim 	bool first = true;
190026d8b338SNamhyung Kim 	int ret;
1901aff3f3f6SArnaldo Carvalho de Melo 
19021b6b678eSArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain) {
1903aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1904aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
19051b6b678eSArnaldo Carvalho de Melo 	}
1906aff3f3f6SArnaldo Carvalho de Melo 
1907f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1908361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
1909e67d49a7SNamhyung Kim 			continue;
1910e67d49a7SNamhyung Kim 
191126d8b338SNamhyung Kim 		if (!first) {
191226d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
191326d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
191426d8b338SNamhyung Kim 		} else
191526d8b338SNamhyung Kim 			first = false;
1916aff3f3f6SArnaldo Carvalho de Melo 
191726d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
191889fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
191926d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
192026d8b338SNamhyung Kim 	}
192189fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
1922aff3f3f6SArnaldo Carvalho de Melo 
1923aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1924d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1925d0506edbSNamhyung Kim 
1926d0506edbSNamhyung Kim 	return printed;
1927d0506edbSNamhyung Kim }
1928d0506edbSNamhyung Kim 
1929d0506edbSNamhyung Kim 
1930d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1931d0506edbSNamhyung Kim 						 struct hist_entry *he,
1932325a6283SNamhyung Kim 						 FILE *fp, int level)
1933d0506edbSNamhyung Kim {
1934d0506edbSNamhyung Kim 	char s[8192];
1935d0506edbSNamhyung Kim 	int printed = 0;
1936d0506edbSNamhyung Kim 	char folded_sign = ' ';
1937d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
1938d0506edbSNamhyung Kim 		.buf = s,
1939d0506edbSNamhyung Kim 		.size = sizeof(s),
1940d0506edbSNamhyung Kim 	};
1941d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1942325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1943d0506edbSNamhyung Kim 	bool first = true;
1944d0506edbSNamhyung Kim 	int ret;
1945325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1946d0506edbSNamhyung Kim 
1947d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1948d0506edbSNamhyung Kim 
1949d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
1950d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
1951d0506edbSNamhyung Kim 
1952325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1953325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
1954325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
1955325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1956d0506edbSNamhyung Kim 		if (!first) {
1957d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1958d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
1959d0506edbSNamhyung Kim 		} else
1960d0506edbSNamhyung Kim 			first = false;
1961d0506edbSNamhyung Kim 
1962d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
1963d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
1964d0506edbSNamhyung Kim 	}
1965d0506edbSNamhyung Kim 
1966d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1967d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
1968d0506edbSNamhyung Kim 
19691b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
19701b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
19711b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
19721b2dbbf4SNamhyung Kim 
1973d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
1974d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
19751b2dbbf4SNamhyung Kim 	}
1976d0506edbSNamhyung Kim 
1977d0506edbSNamhyung Kim 	printed += fprintf(fp, "%s\n", rtrim(s));
1978d0506edbSNamhyung Kim 
1979d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
1980d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
1981d0506edbSNamhyung Kim 							   he->depth + 1);
1982d0506edbSNamhyung Kim 	}
1983aff3f3f6SArnaldo Carvalho de Melo 
1984aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1985aff3f3f6SArnaldo Carvalho de Melo }
1986aff3f3f6SArnaldo Carvalho de Melo 
1987aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1988aff3f3f6SArnaldo Carvalho de Melo {
1989064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1990064f1981SNamhyung Kim 						   browser->min_pcnt);
1991aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1992aff3f3f6SArnaldo Carvalho de Melo 
1993aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1994aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1995aff3f3f6SArnaldo Carvalho de Melo 
1996d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1997d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
1998d0506edbSNamhyung Kim 									 h, fp,
1999325a6283SNamhyung Kim 									 h->depth);
2000d0506edbSNamhyung Kim 		} else {
2001aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
2002d0506edbSNamhyung Kim 		}
2003d0506edbSNamhyung Kim 
2004d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2005d0506edbSNamhyung Kim 					   browser->min_pcnt);
2006aff3f3f6SArnaldo Carvalho de Melo 	}
2007aff3f3f6SArnaldo Carvalho de Melo 
2008aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2009aff3f3f6SArnaldo Carvalho de Melo }
2010aff3f3f6SArnaldo Carvalho de Melo 
2011aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2012aff3f3f6SArnaldo Carvalho de Melo {
2013aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2014aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2015aff3f3f6SArnaldo Carvalho de Melo 
2016aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2017aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2018aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2019aff3f3f6SArnaldo Carvalho de Melo 			break;
2020aff3f3f6SArnaldo Carvalho de Melo 		/*
2021aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2022aff3f3f6SArnaldo Carvalho de Melo  		 */
2023aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2024aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2025aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2026aff3f3f6SArnaldo Carvalho de Melo 		}
2027aff3f3f6SArnaldo Carvalho de Melo 	}
2028aff3f3f6SArnaldo Carvalho de Melo 
2029aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2030aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2031aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
20324cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
20334cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2034aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2035aff3f3f6SArnaldo Carvalho de Melo 	}
2036aff3f3f6SArnaldo Carvalho de Melo 
2037aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2038aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2039aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2040aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2041aff3f3f6SArnaldo Carvalho de Melo 
2042aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2043aff3f3f6SArnaldo Carvalho de Melo }
2044aff3f3f6SArnaldo Carvalho de Melo 
2045dabd2012SJiri Olsa struct hist_browser *hist_browser__new(struct hists *hists,
2046b1a9ceefSNamhyung Kim 				       struct hist_browser_timer *hbt,
2047ce80d3beSKan Liang 				       struct perf_env *env)
2048aca7a94dSNamhyung Kim {
204905e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
2050aca7a94dSNamhyung Kim 
205105e8b080SArnaldo Carvalho de Melo 	if (browser) {
205205e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
205305e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
2054357cfff1SArnaldo Carvalho de Melo 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
205505e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
205605e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
2057c8302367SJiri Olsa 		browser->show_headers = symbol_conf.show_hist_headers;
2058c2a51ab8SNamhyung Kim 		browser->hbt = hbt;
2059b1a9ceefSNamhyung Kim 		browser->env = env;
2060*5b91a86fSJiri Olsa 		browser->title = perf_evsel_browser_title;
2061aca7a94dSNamhyung Kim 	}
2062aca7a94dSNamhyung Kim 
206305e8b080SArnaldo Carvalho de Melo 	return browser;
2064aca7a94dSNamhyung Kim }
2065aca7a94dSNamhyung Kim 
2066dabd2012SJiri Olsa void hist_browser__delete(struct hist_browser *browser)
2067aca7a94dSNamhyung Kim {
206805e8b080SArnaldo Carvalho de Melo 	free(browser);
2069aca7a94dSNamhyung Kim }
2070aca7a94dSNamhyung Kim 
207105e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2072aca7a94dSNamhyung Kim {
207305e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2074aca7a94dSNamhyung Kim }
2075aca7a94dSNamhyung Kim 
207605e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2077aca7a94dSNamhyung Kim {
207805e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2079aca7a94dSNamhyung Kim }
2080aca7a94dSNamhyung Kim 
20811e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
20821e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
20831e378ebdSTaeung Song {
20841e378ebdSTaeung Song 	return timer == NULL;
20851e378ebdSTaeung Song }
20861e378ebdSTaeung Song 
2087*5b91a86fSJiri Olsa static int perf_evsel_browser_title(struct hist_browser *browser,
20881e378ebdSTaeung Song 				char *bf, size_t size)
2089aca7a94dSNamhyung Kim {
2090*5b91a86fSJiri Olsa 	struct hist_browser_timer *hbt = browser->hbt;
2091*5b91a86fSJiri Olsa 	struct hists *hists = browser->hists;
2092aca7a94dSNamhyung Kim 	char unit;
2093aca7a94dSNamhyung Kim 	int printed;
209405e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
209505e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
209684734b06SKan Liang 	int socket_id = hists->socket_filter;
209705e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
209805e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
2099717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
2100dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
2101717e263fSNamhyung Kim 	char buf[512];
2102717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
21039e207ddfSKan Liang 	char ref[30] = " show reference callgraph, ";
21049e207ddfSKan Liang 	bool enable_ref = false;
2105717e263fSNamhyung Kim 
2106f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
2107f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
2108f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
2109f2148330SNamhyung Kim 	}
2110f2148330SNamhyung Kim 
2111759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
2112717e263fSNamhyung Kim 		struct perf_evsel *pos;
2113717e263fSNamhyung Kim 
2114717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
2115717e263fSNamhyung Kim 		ev_name = buf;
2116717e263fSNamhyung Kim 
2117717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
21184ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
21194ea062edSArnaldo Carvalho de Melo 
2120f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
21214ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
21224ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
2123f2148330SNamhyung Kim 			} else {
21244ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
21254ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
2126717e263fSNamhyung Kim 			}
2127717e263fSNamhyung Kim 		}
2128f2148330SNamhyung Kim 	}
2129aca7a94dSNamhyung Kim 
21309e207ddfSKan Liang 	if (symbol_conf.show_ref_callgraph &&
21319e207ddfSKan Liang 	    strstr(ev_name, "call-graph=no"))
21329e207ddfSKan Liang 		enable_ref = true;
2133aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
2134aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
21359e207ddfSKan Liang 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
21369e207ddfSKan Liang 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2137aca7a94dSNamhyung Kim 
2138aca7a94dSNamhyung Kim 
213905e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
2140aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
214105e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
21426962ccb3SNamhyung Kim 	if (thread) {
2143fa82911aSJiri Olsa 		if (hists__has(hists, thread)) {
2144aca7a94dSNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
2145aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
2146b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
214738051234SAdrian Hunter 				    thread->tid);
21486962ccb3SNamhyung Kim 		} else {
21496962ccb3SNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
21506962ccb3SNamhyung Kim 				    ", Thread: %s",
21516962ccb3SNamhyung Kim 				     (thread->comm_set ? thread__comm_str(thread) : ""));
21526962ccb3SNamhyung Kim 		}
21536962ccb3SNamhyung Kim 	}
2154aca7a94dSNamhyung Kim 	if (dso)
2155aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
2156aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
215784734b06SKan Liang 	if (socket_id > -1)
215821394d94SKan Liang 		printed += scnprintf(bf + printed, size - printed,
215984734b06SKan Liang 				    ", Processor Socket: %d", socket_id);
21601e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
21611e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
21621e378ebdSTaeung Song 
21631e378ebdSTaeung Song 		if (top->zero)
21641e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
21651e378ebdSTaeung Song 	}
21661e378ebdSTaeung Song 
2167aca7a94dSNamhyung Kim 	return printed;
2168aca7a94dSNamhyung Kim }
2169aca7a94dSNamhyung Kim 
2170aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2171aca7a94dSNamhyung Kim {
2172aca7a94dSNamhyung Kim 	int i;
2173aca7a94dSNamhyung Kim 
217404662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
217504662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2176aca7a94dSNamhyung Kim }
2177aca7a94dSNamhyung Kim 
2178341487abSFeng Tang /*
2179341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2180341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2181341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2182341487abSFeng Tang  */
2183341487abSFeng Tang static bool is_input_name_malloced = false;
2184341487abSFeng Tang 
2185341487abSFeng Tang static int switch_data_file(void)
2186341487abSFeng Tang {
2187341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2188341487abSFeng Tang 	DIR *pwd_dir;
2189341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2190341487abSFeng Tang 	struct dirent *dent;
2191341487abSFeng Tang 
2192341487abSFeng Tang 	pwd = getenv("PWD");
2193341487abSFeng Tang 	if (!pwd)
2194341487abSFeng Tang 		return ret;
2195341487abSFeng Tang 
2196341487abSFeng Tang 	pwd_dir = opendir(pwd);
2197341487abSFeng Tang 	if (!pwd_dir)
2198341487abSFeng Tang 		return ret;
2199341487abSFeng Tang 
2200341487abSFeng Tang 	memset(options, 0, sizeof(options));
2201341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
2202341487abSFeng Tang 
2203341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2204341487abSFeng Tang 		char path[PATH_MAX];
2205341487abSFeng Tang 		u64 magic;
2206341487abSFeng Tang 		char *name = dent->d_name;
2207341487abSFeng Tang 		FILE *file;
2208341487abSFeng Tang 
2209341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2210341487abSFeng Tang 			continue;
2211341487abSFeng Tang 
2212341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2213341487abSFeng Tang 
2214341487abSFeng Tang 		file = fopen(path, "r");
2215341487abSFeng Tang 		if (!file)
2216341487abSFeng Tang 			continue;
2217341487abSFeng Tang 
2218341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2219341487abSFeng Tang 			goto close_file_and_continue;
2220341487abSFeng Tang 
2221341487abSFeng Tang 		if (is_perf_magic(magic)) {
2222341487abSFeng Tang 			options[nr_options] = strdup(name);
2223341487abSFeng Tang 			if (!options[nr_options])
2224341487abSFeng Tang 				goto close_file_and_continue;
2225341487abSFeng Tang 
2226341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2227341487abSFeng Tang 			if (!abs_path[nr_options]) {
222874cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2229341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2230341487abSFeng Tang 				fclose(file);
2231341487abSFeng Tang 				break;
2232341487abSFeng Tang 			}
2233341487abSFeng Tang 
2234341487abSFeng Tang 			nr_options++;
2235341487abSFeng Tang 		}
2236341487abSFeng Tang 
2237341487abSFeng Tang close_file_and_continue:
2238341487abSFeng Tang 		fclose(file);
2239341487abSFeng Tang 		if (nr_options >= 32) {
2240341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2241341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2242341487abSFeng Tang 			break;
2243341487abSFeng Tang 		}
2244341487abSFeng Tang 	}
2245341487abSFeng Tang 	closedir(pwd_dir);
2246341487abSFeng Tang 
2247341487abSFeng Tang 	if (nr_options) {
2248341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
2249341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2250341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2251341487abSFeng Tang 			if (tmp) {
2252341487abSFeng Tang 				if (is_input_name_malloced)
2253341487abSFeng Tang 					free((void *)input_name);
2254341487abSFeng Tang 				input_name = tmp;
2255341487abSFeng Tang 				is_input_name_malloced = true;
2256341487abSFeng Tang 				ret = 0;
2257341487abSFeng Tang 			} else
2258341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2259341487abSFeng Tang 		}
2260341487abSFeng Tang 	}
2261341487abSFeng Tang 
2262341487abSFeng Tang 	free_popup_options(options, nr_options);
2263341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2264341487abSFeng Tang 	return ret;
2265341487abSFeng Tang }
2266341487abSFeng Tang 
2267ea7cd592SNamhyung Kim struct popup_action {
2268ea7cd592SNamhyung Kim 	struct thread 		*thread;
2269ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
227084734b06SKan Liang 	int			socket;
2271ea7cd592SNamhyung Kim 
2272ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2273ea7cd592SNamhyung Kim };
2274ea7cd592SNamhyung Kim 
2275bc7cad42SNamhyung Kim static int
2276ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2277bc7cad42SNamhyung Kim {
2278bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
2279bc7cad42SNamhyung Kim 	struct annotation *notes;
2280bc7cad42SNamhyung Kim 	struct hist_entry *he;
2281bc7cad42SNamhyung Kim 	int err;
2282bc7cad42SNamhyung Kim 
2283eebd0bfcSArnaldo Carvalho de Melo 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
2284bc7cad42SNamhyung Kim 		return 0;
2285bc7cad42SNamhyung Kim 
2286ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2287bc7cad42SNamhyung Kim 	if (!notes->src)
2288bc7cad42SNamhyung Kim 		return 0;
2289bc7cad42SNamhyung Kim 
2290bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
2291ea7cd592SNamhyung Kim 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2292bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2293bc7cad42SNamhyung Kim 	/*
2294bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2295bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2296bc7cad42SNamhyung Kim 	 */
2297bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2298bc7cad42SNamhyung Kim 		return 1;
2299bc7cad42SNamhyung Kim 
2300bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2301bc7cad42SNamhyung Kim 	if (err)
2302bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2303bc7cad42SNamhyung Kim 	return 0;
2304bc7cad42SNamhyung Kim }
2305bc7cad42SNamhyung Kim 
2306bc7cad42SNamhyung Kim static int
2307ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2308ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
2309ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
2310bc7cad42SNamhyung Kim {
2311ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
2312ea7cd592SNamhyung Kim 		return 0;
2313ea7cd592SNamhyung Kim 
2314ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2315ea7cd592SNamhyung Kim 		return 0;
2316ea7cd592SNamhyung Kim 
2317ea7cd592SNamhyung Kim 	act->ms.map = map;
2318ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2319ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2320ea7cd592SNamhyung Kim 	return 1;
2321ea7cd592SNamhyung Kim }
2322ea7cd592SNamhyung Kim 
2323ea7cd592SNamhyung Kim static int
2324ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2325ea7cd592SNamhyung Kim {
2326ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2327ea7cd592SNamhyung Kim 
23287cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
23297cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2330599a2f38SNamhyung Kim 		return 0;
2331599a2f38SNamhyung Kim 
2332bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2333bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2334bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2335bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2336bc7cad42SNamhyung Kim 		ui_helpline__pop();
2337bc7cad42SNamhyung Kim 	} else {
2338fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
23397727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2340bc7cad42SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "",
2341bc7cad42SNamhyung Kim 					   thread->tid);
23426962ccb3SNamhyung Kim 		} else {
23436962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
23446962ccb3SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "");
23456962ccb3SNamhyung Kim 		}
23466962ccb3SNamhyung Kim 
2347bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2348bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2349bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2350bc7cad42SNamhyung Kim 	}
2351bc7cad42SNamhyung Kim 
2352bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2353bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2354bc7cad42SNamhyung Kim 	return 0;
2355bc7cad42SNamhyung Kim }
2356bc7cad42SNamhyung Kim 
2357bc7cad42SNamhyung Kim static int
2358ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2359ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2360bc7cad42SNamhyung Kim {
23616962ccb3SNamhyung Kim 	int ret;
23626962ccb3SNamhyung Kim 
23637cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
23647cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2365ea7cd592SNamhyung Kim 		return 0;
2366ea7cd592SNamhyung Kim 
2367fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
23686962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2369ea7cd592SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
2370ea7cd592SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "",
23716962ccb3SNamhyung Kim 			       thread->tid);
23726962ccb3SNamhyung Kim 	} else {
23736962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s thread",
23746962ccb3SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
23756962ccb3SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "");
23766962ccb3SNamhyung Kim 	}
23776962ccb3SNamhyung Kim 	if (ret < 0)
2378ea7cd592SNamhyung Kim 		return 0;
2379ea7cd592SNamhyung Kim 
2380ea7cd592SNamhyung Kim 	act->thread = thread;
2381ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2382ea7cd592SNamhyung Kim 	return 1;
2383ea7cd592SNamhyung Kim }
2384ea7cd592SNamhyung Kim 
2385ea7cd592SNamhyung Kim static int
2386ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2387ea7cd592SNamhyung Kim {
2388045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
2389ea7cd592SNamhyung Kim 
239069849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2391599a2f38SNamhyung Kim 		return 0;
2392599a2f38SNamhyung Kim 
2393bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2394bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2395bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2396bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2397bc7cad42SNamhyung Kim 		ui_helpline__pop();
2398bc7cad42SNamhyung Kim 	} else {
2399045b80ddSArnaldo Carvalho de Melo 		if (map == NULL)
2400bc7cad42SNamhyung Kim 			return 0;
24017727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2402045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2403045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
2404bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2405bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2406bc7cad42SNamhyung Kim 	}
2407bc7cad42SNamhyung Kim 
2408bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2409bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2410bc7cad42SNamhyung Kim 	return 0;
2411bc7cad42SNamhyung Kim }
2412bc7cad42SNamhyung Kim 
2413bc7cad42SNamhyung Kim static int
2414ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2415045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2416bc7cad42SNamhyung Kim {
241769849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2418ea7cd592SNamhyung Kim 		return 0;
2419ea7cd592SNamhyung Kim 
2420ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
2421ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
2422045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2423ea7cd592SNamhyung Kim 		return 0;
2424ea7cd592SNamhyung Kim 
2425045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2426ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2427ea7cd592SNamhyung Kim 	return 1;
2428ea7cd592SNamhyung Kim }
2429ea7cd592SNamhyung Kim 
2430ea7cd592SNamhyung Kim static int
2431ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2432ea7cd592SNamhyung Kim 	      struct popup_action *act)
2433ea7cd592SNamhyung Kim {
2434ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2435bc7cad42SNamhyung Kim 	return 0;
2436bc7cad42SNamhyung Kim }
2437bc7cad42SNamhyung Kim 
2438bc7cad42SNamhyung Kim static int
243969849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2440ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2441ea7cd592SNamhyung Kim {
244269849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2443ea7cd592SNamhyung Kim 		return 0;
2444ea7cd592SNamhyung Kim 
2445ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2446ea7cd592SNamhyung Kim 		return 0;
2447ea7cd592SNamhyung Kim 
2448ea7cd592SNamhyung Kim 	act->ms.map = map;
2449ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2450ea7cd592SNamhyung Kim 	return 1;
2451ea7cd592SNamhyung Kim }
2452ea7cd592SNamhyung Kim 
2453ea7cd592SNamhyung Kim static int
2454bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2455ea7cd592SNamhyung Kim 	      struct popup_action *act)
2456bc7cad42SNamhyung Kim {
2457bc7cad42SNamhyung Kim 	char script_opt[64];
2458bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
2459bc7cad42SNamhyung Kim 
2460ea7cd592SNamhyung Kim 	if (act->thread) {
2461bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2462ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2463ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
2464bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2465ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2466bc7cad42SNamhyung Kim 	}
2467bc7cad42SNamhyung Kim 
2468bc7cad42SNamhyung Kim 	script_browse(script_opt);
2469bc7cad42SNamhyung Kim 	return 0;
2470bc7cad42SNamhyung Kim }
2471bc7cad42SNamhyung Kim 
2472bc7cad42SNamhyung Kim static int
2473ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
2474ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
2475ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
2476ea7cd592SNamhyung Kim {
2477ea7cd592SNamhyung Kim 	if (thread) {
2478ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2479ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
2480ea7cd592SNamhyung Kim 			return 0;
2481ea7cd592SNamhyung Kim 	} else if (sym) {
2482ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2483ea7cd592SNamhyung Kim 			     sym->name) < 0)
2484ea7cd592SNamhyung Kim 			return 0;
2485ea7cd592SNamhyung Kim 	} else {
2486ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2487ea7cd592SNamhyung Kim 			return 0;
2488ea7cd592SNamhyung Kim 	}
2489ea7cd592SNamhyung Kim 
2490ea7cd592SNamhyung Kim 	act->thread = thread;
2491ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2492ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2493ea7cd592SNamhyung Kim 	return 1;
2494ea7cd592SNamhyung Kim }
2495ea7cd592SNamhyung Kim 
2496ea7cd592SNamhyung Kim static int
2497ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2498ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2499bc7cad42SNamhyung Kim {
2500bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2501bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2502bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2503ea7cd592SNamhyung Kim 		return 0;
2504bc7cad42SNamhyung Kim 	}
2505bc7cad42SNamhyung Kim 
2506bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2507bc7cad42SNamhyung Kim }
2508bc7cad42SNamhyung Kim 
2509ea7cd592SNamhyung Kim static int
2510ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2511ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2512ea7cd592SNamhyung Kim {
2513ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2514ea7cd592SNamhyung Kim 		return 0;
2515ea7cd592SNamhyung Kim 
2516ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2517ea7cd592SNamhyung Kim 		return 0;
2518ea7cd592SNamhyung Kim 
2519ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2520ea7cd592SNamhyung Kim 	return 1;
2521ea7cd592SNamhyung Kim }
2522ea7cd592SNamhyung Kim 
2523ea7cd592SNamhyung Kim static int
2524ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2525ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2526ea7cd592SNamhyung Kim {
2527ea7cd592SNamhyung Kim 	return 0;
2528ea7cd592SNamhyung Kim }
2529ea7cd592SNamhyung Kim 
2530ea7cd592SNamhyung Kim static int
2531ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2532ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2533ea7cd592SNamhyung Kim {
2534ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2535ea7cd592SNamhyung Kim 		return 0;
2536ea7cd592SNamhyung Kim 
2537ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2538ea7cd592SNamhyung Kim 	return 1;
2539ea7cd592SNamhyung Kim }
2540ea7cd592SNamhyung Kim 
254184734b06SKan Liang static int
254284734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
254384734b06SKan Liang {
254435a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2545599a2f38SNamhyung Kim 		return 0;
2546599a2f38SNamhyung Kim 
254784734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
254884734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
254984734b06SKan Liang 		browser->hists->socket_filter = -1;
255084734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
255184734b06SKan Liang 	} else {
255284734b06SKan Liang 		browser->hists->socket_filter = act->socket;
255384734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
255484734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
255584734b06SKan Liang 	}
255684734b06SKan Liang 
255784734b06SKan Liang 	hists__filter_by_socket(browser->hists);
255884734b06SKan Liang 	hist_browser__reset(browser);
255984734b06SKan Liang 	return 0;
256084734b06SKan Liang }
256184734b06SKan Liang 
256284734b06SKan Liang static int
256384734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
256484734b06SKan Liang 	       char **optstr, int socket_id)
256584734b06SKan Liang {
256635a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
256784734b06SKan Liang 		return 0;
256884734b06SKan Liang 
256984734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
257084734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
257184734b06SKan Liang 		     socket_id) < 0)
257284734b06SKan Liang 		return 0;
257384734b06SKan Liang 
257484734b06SKan Liang 	act->socket = socket_id;
257584734b06SKan Liang 	act->fn = do_zoom_socket;
257684734b06SKan Liang 	return 1;
257784734b06SKan Liang }
257884734b06SKan Liang 
2579112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2580064f1981SNamhyung Kim {
2581064f1981SNamhyung Kim 	u64 nr_entries = 0;
2582064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2583064f1981SNamhyung Kim 
2584f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2585268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2586268397cbSNamhyung Kim 		return;
2587268397cbSNamhyung Kim 	}
2588268397cbSNamhyung Kim 
258914135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2590064f1981SNamhyung Kim 		nr_entries++;
2591f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2592064f1981SNamhyung Kim 	}
2593064f1981SNamhyung Kim 
2594112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2595f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2596064f1981SNamhyung Kim }
2597341487abSFeng Tang 
2598b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2599b62e8dfcSNamhyung Kim 					       double percent)
2600b62e8dfcSNamhyung Kim {
2601b62e8dfcSNamhyung Kim 	struct hist_entry *he;
2602b62e8dfcSNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2603b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2604b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2605b62e8dfcSNamhyung Kim 
2606b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2607b62e8dfcSNamhyung Kim 
2608b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2609b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2610b62e8dfcSNamhyung Kim 
261179dded87SNamhyung Kim 		if (he->has_no_entry) {
261279dded87SNamhyung Kim 			he->has_no_entry = false;
261379dded87SNamhyung Kim 			he->nr_rows = 0;
261479dded87SNamhyung Kim 		}
261579dded87SNamhyung Kim 
2616d0506edbSNamhyung Kim 		if (!he->leaf || !symbol_conf.use_callchain)
2617d0506edbSNamhyung Kim 			goto next;
2618d0506edbSNamhyung Kim 
2619b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2620b62e8dfcSNamhyung Kim 			total = he->stat.period;
2621b62e8dfcSNamhyung Kim 
2622b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2623b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2624b62e8dfcSNamhyung Kim 
2625b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2626b62e8dfcSNamhyung Kim 		}
2627b62e8dfcSNamhyung Kim 
2628b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2629b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2630b62e8dfcSNamhyung Kim 
2631d0506edbSNamhyung Kim next:
2632201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2633d0506edbSNamhyung Kim 
2634b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2635b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2636492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2637b62e8dfcSNamhyung Kim 	}
2638b62e8dfcSNamhyung Kim }
2639b62e8dfcSNamhyung Kim 
2640aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2641dd00d486SJiri Olsa 				    const char *helpline,
2642aca7a94dSNamhyung Kim 				    bool left_exits,
264368d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2644064f1981SNamhyung Kim 				    float min_pcnt,
2645ce80d3beSKan Liang 				    struct perf_env *env)
2646aca7a94dSNamhyung Kim {
26474ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2648b1a9ceefSNamhyung Kim 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2649aca7a94dSNamhyung Kim 	struct branch_info *bi;
2650f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2651f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2652ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2653aca7a94dSNamhyung Kim 	int nr_options = 0;
2654aca7a94dSNamhyung Kim 	int key = -1;
2655aca7a94dSNamhyung Kim 	char buf[64];
26569783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
265759dc9f25SNamhyung Kim 	struct perf_hpp_fmt *fmt;
2658aca7a94dSNamhyung Kim 
2659e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2660e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2661e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2662e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
2663e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2664e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2665e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2666e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
26677727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
26687727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
2669e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2670e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2671e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2672e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2673105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2674025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2675b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
267631eb4360SNamhyung Kim 	"m             Display context menu\n"				\
267784734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2678e8e684a5SNamhyung Kim 
2679e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
2680e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
26816dd60135SNamhyung Kim 	"i             Show header information\n"
2682e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2683e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2684e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2685e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2686e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2687e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2688e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2689e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2690e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2691e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
269242337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2693fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2694e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2695e8e684a5SNamhyung Kim 
2696aca7a94dSNamhyung Kim 	if (browser == NULL)
2697aca7a94dSNamhyung Kim 		return -1;
2698aca7a94dSNamhyung Kim 
2699ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2700ed426915SNamhyung Kim 	SLang_reset_tty();
2701ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2702ed426915SNamhyung Kim 
270303905048SNamhyung Kim 	if (min_pcnt)
2704064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2705112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2706064f1981SNamhyung Kim 
270784734b06SKan Liang 	browser->pstack = pstack__new(3);
270801f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2709aca7a94dSNamhyung Kim 		goto out;
2710aca7a94dSNamhyung Kim 
2711aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2712aca7a94dSNamhyung Kim 
2713aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2714ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2715aca7a94dSNamhyung Kim 
2716f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
271759dc9f25SNamhyung Kim 		perf_hpp__reset_width(fmt, hists);
2718c6c3c02dSArnaldo Carvalho de Melo 		/*
2719c6c3c02dSArnaldo Carvalho de Melo 		 * This is done just once, and activates the horizontal scrolling
2720c6c3c02dSArnaldo Carvalho de Melo 		 * code in the ui_browser code, it would be better to have a the
2721c6c3c02dSArnaldo Carvalho de Melo 		 * counter in the perf_hpp code, but I couldn't find doing it here
2722c6c3c02dSArnaldo Carvalho de Melo 		 * works, FIXME by setting this in hist_browser__new, for now, be
2723c6c3c02dSArnaldo Carvalho de Melo 		 * clever 8-)
2724c6c3c02dSArnaldo Carvalho de Melo 		 */
2725c6c3c02dSArnaldo Carvalho de Melo 		++browser->b.columns;
2726c6c3c02dSArnaldo Carvalho de Melo 	}
272759dc9f25SNamhyung Kim 
27285b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
27295b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
27305b591669SNamhyung Kim 
2731aca7a94dSNamhyung Kim 	while (1) {
2732f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2733045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
2734ea7cd592SNamhyung Kim 		int choice = 0;
273584734b06SKan Liang 		int socked_id = -1;
2736aca7a94dSNamhyung Kim 
2737aca7a94dSNamhyung Kim 		nr_options = 0;
2738aca7a94dSNamhyung Kim 
27395f00b0f4SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline);
2740aca7a94dSNamhyung Kim 
2741aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
2742aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
2743045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
274484734b06SKan Liang 			socked_id = browser->he_selection->socket;
2745aca7a94dSNamhyung Kim 		}
2746aca7a94dSNamhyung Kim 		switch (key) {
2747aca7a94dSNamhyung Kim 		case K_TAB:
2748aca7a94dSNamhyung Kim 		case K_UNTAB:
2749aca7a94dSNamhyung Kim 			if (nr_events == 1)
2750aca7a94dSNamhyung Kim 				continue;
2751aca7a94dSNamhyung Kim 			/*
2752aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
2753aca7a94dSNamhyung Kim 			 * go to the next or previous
2754aca7a94dSNamhyung Kim 			 */
2755aca7a94dSNamhyung Kim 			goto out_free_stack;
2756aca7a94dSNamhyung Kim 		case 'a':
27572e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
2758aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
2759aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
2760aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
2761aca7a94dSNamhyung Kim 				continue;
2762aca7a94dSNamhyung Kim 			}
2763aca7a94dSNamhyung Kim 
2764aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
2765aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
2766aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
2767aca7a94dSNamhyung Kim 				continue;
2768bc7cad42SNamhyung Kim 
2769ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
2770ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
2771ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
2772bc7cad42SNamhyung Kim 			continue;
2773aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
2774aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
2775aff3f3f6SArnaldo Carvalho de Melo 			continue;
2776aca7a94dSNamhyung Kim 		case 'd':
2777fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
2778ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
2779bc7cad42SNamhyung Kim 			continue;
2780a7cb8863SArnaldo Carvalho de Melo 		case 'V':
2781a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
2782a7cb8863SArnaldo Carvalho de Melo 			continue;
2783aca7a94dSNamhyung Kim 		case 't':
2784ea7cd592SNamhyung Kim 			actions->thread = thread;
2785ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
2786bc7cad42SNamhyung Kim 			continue;
278784734b06SKan Liang 		case 'S':
278884734b06SKan Liang 			actions->socket = socked_id;
278984734b06SKan Liang 			do_zoom_socket(browser, actions);
279084734b06SKan Liang 			continue;
27915a5626b1SArnaldo Carvalho de Melo 		case '/':
2792aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
27934aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
27944aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
2795aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2796aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
279705e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
279805e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
2799aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
2800aca7a94dSNamhyung Kim 			}
2801aca7a94dSNamhyung Kim 			continue;
2802cdbab7c2SFeng Tang 		case 'r':
2803ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
2804ea7cd592SNamhyung Kim 				actions->thread = NULL;
2805ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
2806ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
2807ea7cd592SNamhyung Kim 			}
2808c77d8d70SFeng Tang 			continue;
2809341487abSFeng Tang 		case 's':
2810bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
2811ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
2812bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
2813bc7cad42SNamhyung Kim 					goto out_free_stack;
2814bc7cad42SNamhyung Kim 			}
2815341487abSFeng Tang 			continue;
28166dd60135SNamhyung Kim 		case 'i':
28176dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
28186dd60135SNamhyung Kim 			if (env->arch)
28196dd60135SNamhyung Kim 				tui__header_window(env);
28206dd60135SNamhyung Kim 			continue;
2821105eb30fSNamhyung Kim 		case 'F':
2822105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
2823105eb30fSNamhyung Kim 			continue;
282442337a22SNamhyung Kim 		case 'z':
282542337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
282642337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
282742337a22SNamhyung Kim 
282842337a22SNamhyung Kim 				top->zero = !top->zero;
282942337a22SNamhyung Kim 			}
283042337a22SNamhyung Kim 			continue;
2831b62e8dfcSNamhyung Kim 		case 'L':
2832b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
2833b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
2834b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2835b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
2836b62e8dfcSNamhyung Kim 				char *end;
2837b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
2838b62e8dfcSNamhyung Kim 
2839b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
2840b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
2841b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
2842b62e8dfcSNamhyung Kim 					continue;
2843b62e8dfcSNamhyung Kim 				}
2844b62e8dfcSNamhyung Kim 
2845b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
2846b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
2847b62e8dfcSNamhyung Kim 			}
2848b62e8dfcSNamhyung Kim 			continue;
2849aca7a94dSNamhyung Kim 		case K_F1:
2850aca7a94dSNamhyung Kim 		case 'h':
2851aca7a94dSNamhyung Kim 		case '?':
2852aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
2853e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
2854aca7a94dSNamhyung Kim 			continue;
2855aca7a94dSNamhyung Kim 		case K_ENTER:
2856aca7a94dSNamhyung Kim 		case K_RIGHT:
285731eb4360SNamhyung Kim 		case 'm':
2858aca7a94dSNamhyung Kim 			/* menu */
2859aca7a94dSNamhyung Kim 			break;
286063ab1749SArnaldo Carvalho de Melo 		case K_ESC:
2861aca7a94dSNamhyung Kim 		case K_LEFT: {
2862aca7a94dSNamhyung Kim 			const void *top;
2863aca7a94dSNamhyung Kim 
286401f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
2865aca7a94dSNamhyung Kim 				/*
2866aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
2867aca7a94dSNamhyung Kim 				 */
2868aca7a94dSNamhyung Kim 				if (left_exits)
2869aca7a94dSNamhyung Kim 					goto out_free_stack;
287063ab1749SArnaldo Carvalho de Melo 
287163ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
287263ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
287363ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
287463ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
287563ab1749SArnaldo Carvalho de Melo 
2876aca7a94dSNamhyung Kim 				continue;
2877aca7a94dSNamhyung Kim 			}
28786422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
2879bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
28806422184bSNamhyung Kim 				/*
28816422184bSNamhyung Kim 				 * No need to set actions->dso here since
28826422184bSNamhyung Kim 				 * it's just to remove the current filter.
28836422184bSNamhyung Kim 				 * Ditto for thread below.
28846422184bSNamhyung Kim 				 */
28856422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
288684734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
28876422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
288884734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
288984734b06SKan Liang 				do_zoom_socket(browser, actions);
289084734b06SKan Liang 			}
2891aca7a94dSNamhyung Kim 			continue;
2892aca7a94dSNamhyung Kim 		}
2893aca7a94dSNamhyung Kim 		case 'q':
2894aca7a94dSNamhyung Kim 		case CTRL('c'):
2895516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
2896fbb7997eSArnaldo Carvalho de Melo 		case 'f':
289713d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
289813d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
289913d1e536SNamhyung Kim 
290013d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
290113d1e536SNamhyung Kim 				/*
290213d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
290313d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
290413d1e536SNamhyung Kim 				 */
290513d1e536SNamhyung Kim 				if (top->evlist->enabled) {
290613d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
290713d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
290813d1e536SNamhyung Kim 				} else {
290913d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
291013d1e536SNamhyung Kim 					hbt->refresh = 0;
291113d1e536SNamhyung Kim 				}
291213d1e536SNamhyung Kim 				continue;
291313d1e536SNamhyung Kim 			}
29143e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
2915aca7a94dSNamhyung Kim 		default:
29163e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
2917aca7a94dSNamhyung Kim 			continue;
2918aca7a94dSNamhyung Kim 		}
2919aca7a94dSNamhyung Kim 
29202e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
29210ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
29220ba332f7SArnaldo Carvalho de Melo 
292355369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
2924aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
29250ba332f7SArnaldo Carvalho de Melo 
29260ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
29270ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
29280ba332f7SArnaldo Carvalho de Melo 
2929ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2930ea7cd592SNamhyung Kim 						       &actions[nr_options],
2931ea7cd592SNamhyung Kim 						       &options[nr_options],
2932ea7cd592SNamhyung Kim 						       bi->from.map,
2933ea7cd592SNamhyung Kim 						       bi->from.sym);
2934ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
2935ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
2936ea7cd592SNamhyung Kim 							&actions[nr_options],
2937ea7cd592SNamhyung Kim 							&options[nr_options],
2938ea7cd592SNamhyung Kim 							bi->to.map,
2939ea7cd592SNamhyung Kim 							bi->to.sym);
2940aca7a94dSNamhyung Kim 		} else {
2941ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2942ea7cd592SNamhyung Kim 						       &actions[nr_options],
2943ea7cd592SNamhyung Kim 						       &options[nr_options],
2944ea7cd592SNamhyung Kim 						       browser->selection->map,
2945ea7cd592SNamhyung Kim 						       browser->selection->sym);
2946446fb96cSArnaldo Carvalho de Melo 		}
29470ba332f7SArnaldo Carvalho de Melo skip_annotation:
2948ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
2949ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
2950ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
2951045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
2952ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
2953ea7cd592SNamhyung Kim 					  &options[nr_options],
2954bd315aabSWang Nan 					  browser->selection ?
2955bd315aabSWang Nan 						browser->selection->map : NULL);
295684734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
295784734b06SKan Liang 					     &options[nr_options],
295884734b06SKan Liang 					     socked_id);
2959cdbab7c2SFeng Tang 		/* perf script support */
2960b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
2961b1baae89SNamhyung Kim 			goto skip_scripting;
2962b1baae89SNamhyung Kim 
2963cdbab7c2SFeng Tang 		if (browser->he_selection) {
2964fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
2965ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
2966ea7cd592SNamhyung Kim 							     &actions[nr_options],
2967ea7cd592SNamhyung Kim 							     &options[nr_options],
2968ea7cd592SNamhyung Kim 							     thread, NULL);
29692eafd410SNamhyung Kim 			}
2970bd315aabSWang Nan 			/*
2971bd315aabSWang Nan 			 * Note that browser->selection != NULL
2972bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
2973bd315aabSWang Nan 			 * so we don't need to check browser->selection
2974bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
2975bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
2976bd315aabSWang Nan 			 *
2977bd315aabSWang Nan 			 * See hist_browser__show_entry.
2978bd315aabSWang Nan 			 */
29792e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
2980ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
2981ea7cd592SNamhyung Kim 							     &actions[nr_options],
2982ea7cd592SNamhyung Kim 							     &options[nr_options],
2983ea7cd592SNamhyung Kim 							     NULL, browser->selection->sym);
2984cdbab7c2SFeng Tang 			}
2985c221acb0SNamhyung Kim 		}
2986ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
2987ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
2988ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
2989ea7cd592SNamhyung Kim 					     &options[nr_options]);
2990b1baae89SNamhyung Kim skip_scripting:
2991ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
2992ea7cd592SNamhyung Kim 					   &options[nr_options]);
2993aca7a94dSNamhyung Kim 
2994ea7cd592SNamhyung Kim 		do {
2995ea7cd592SNamhyung Kim 			struct popup_action *act;
2996ea7cd592SNamhyung Kim 
2997ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
2998ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
2999aca7a94dSNamhyung Kim 				break;
3000aca7a94dSNamhyung Kim 
3001ea7cd592SNamhyung Kim 			act = &actions[choice];
3002ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
3003ea7cd592SNamhyung Kim 		} while (key == 1);
3004aca7a94dSNamhyung Kim 
3005bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3006341487abSFeng Tang 			break;
3007341487abSFeng Tang 	}
3008aca7a94dSNamhyung Kim out_free_stack:
300901f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3010aca7a94dSNamhyung Kim out:
3011aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3012f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3013aca7a94dSNamhyung Kim 	return key;
3014aca7a94dSNamhyung Kim }
3015aca7a94dSNamhyung Kim 
3016aca7a94dSNamhyung Kim struct perf_evsel_menu {
3017aca7a94dSNamhyung Kim 	struct ui_browser b;
3018aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
3019aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3020064f1981SNamhyung Kim 	float min_pcnt;
3021ce80d3beSKan Liang 	struct perf_env *env;
3022aca7a94dSNamhyung Kim };
3023aca7a94dSNamhyung Kim 
3024aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3025aca7a94dSNamhyung Kim 				   void *entry, int row)
3026aca7a94dSNamhyung Kim {
3027aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
3028aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
3029aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
30304ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3031aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
30324ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
30337289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
3034aca7a94dSNamhyung Kim 	char bf[256], unit;
3035aca7a94dSNamhyung Kim 	const char *warn = " ";
3036aca7a94dSNamhyung Kim 	size_t printed;
3037aca7a94dSNamhyung Kim 
3038aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3039aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3040aca7a94dSNamhyung Kim 
3041759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
3042717e263fSNamhyung Kim 		struct perf_evsel *pos;
3043717e263fSNamhyung Kim 
3044717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
3045717e263fSNamhyung Kim 
3046717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
30474ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
30484ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3049717e263fSNamhyung Kim 		}
3050717e263fSNamhyung Kim 	}
3051717e263fSNamhyung Kim 
3052aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3053aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3054aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3055517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3056aca7a94dSNamhyung Kim 
30574ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3058aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3059aca7a94dSNamhyung Kim 		menu->lost_events = true;
3060aca7a94dSNamhyung Kim 		if (!current_entry)
3061aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3062aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3063aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3064aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3065aca7a94dSNamhyung Kim 		warn = bf;
3066aca7a94dSNamhyung Kim 	}
3067aca7a94dSNamhyung Kim 
306826270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3069aca7a94dSNamhyung Kim 
3070aca7a94dSNamhyung Kim 	if (current_entry)
3071aca7a94dSNamhyung Kim 		menu->selection = evsel;
3072aca7a94dSNamhyung Kim }
3073aca7a94dSNamhyung Kim 
3074aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3075aca7a94dSNamhyung Kim 				int nr_events, const char *help,
30769783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
3077aca7a94dSNamhyung Kim {
3078aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
3079aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3080dd00d486SJiri Olsa 	const char *title = "Available samples";
30819783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3082aca7a94dSNamhyung Kim 	int key;
3083aca7a94dSNamhyung Kim 
3084aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3085aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3086aca7a94dSNamhyung Kim 		return -1;
3087aca7a94dSNamhyung Kim 
3088aca7a94dSNamhyung Kim 	while (1) {
3089aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3090aca7a94dSNamhyung Kim 
3091aca7a94dSNamhyung Kim 		switch (key) {
3092aca7a94dSNamhyung Kim 		case K_TIMER:
30939783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
3094aca7a94dSNamhyung Kim 
3095aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
3096aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3097aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3098aca7a94dSNamhyung Kim 			}
3099aca7a94dSNamhyung Kim 			continue;
3100aca7a94dSNamhyung Kim 		case K_RIGHT:
3101aca7a94dSNamhyung Kim 		case K_ENTER:
3102aca7a94dSNamhyung Kim 			if (!menu->selection)
3103aca7a94dSNamhyung Kim 				continue;
3104aca7a94dSNamhyung Kim 			pos = menu->selection;
3105aca7a94dSNamhyung Kim browse_hists:
3106aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
3107aca7a94dSNamhyung Kim 			/*
3108aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3109aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3110aca7a94dSNamhyung Kim 			 */
31119783adf7SNamhyung Kim 			if (hbt)
31129783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3113aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
3114dd00d486SJiri Olsa 						       true, hbt,
3115064f1981SNamhyung Kim 						       menu->min_pcnt,
311668d80758SNamhyung Kim 						       menu->env);
3117aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3118aca7a94dSNamhyung Kim 			switch (key) {
3119aca7a94dSNamhyung Kim 			case K_TAB:
3120aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
31219a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
3122aca7a94dSNamhyung Kim 				else
31239a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
3124aca7a94dSNamhyung Kim 				goto browse_hists;
3125aca7a94dSNamhyung Kim 			case K_UNTAB:
3126aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
31279a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
3128aca7a94dSNamhyung Kim 				else
3129d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
3130aca7a94dSNamhyung Kim 				goto browse_hists;
3131341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
3132aca7a94dSNamhyung Kim 			case 'q':
3133aca7a94dSNamhyung Kim 			case CTRL('c'):
3134aca7a94dSNamhyung Kim 				goto out;
313563ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3136aca7a94dSNamhyung Kim 			default:
3137aca7a94dSNamhyung Kim 				continue;
3138aca7a94dSNamhyung Kim 			}
3139aca7a94dSNamhyung Kim 		case K_LEFT:
3140aca7a94dSNamhyung Kim 			continue;
3141aca7a94dSNamhyung Kim 		case K_ESC:
3142aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3143aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3144aca7a94dSNamhyung Kim 				continue;
3145aca7a94dSNamhyung Kim 			/* Fall thru */
3146aca7a94dSNamhyung Kim 		case 'q':
3147aca7a94dSNamhyung Kim 		case CTRL('c'):
3148aca7a94dSNamhyung Kim 			goto out;
3149aca7a94dSNamhyung Kim 		default:
3150aca7a94dSNamhyung Kim 			continue;
3151aca7a94dSNamhyung Kim 		}
3152aca7a94dSNamhyung Kim 	}
3153aca7a94dSNamhyung Kim 
3154aca7a94dSNamhyung Kim out:
3155aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3156aca7a94dSNamhyung Kim 	return key;
3157aca7a94dSNamhyung Kim }
3158aca7a94dSNamhyung Kim 
3159316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3160fc24d7c2SNamhyung Kim 				 void *entry)
3161fc24d7c2SNamhyung Kim {
3162fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3163fc24d7c2SNamhyung Kim 
3164fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3165fc24d7c2SNamhyung Kim 		return true;
3166fc24d7c2SNamhyung Kim 
3167fc24d7c2SNamhyung Kim 	return false;
3168fc24d7c2SNamhyung Kim }
3169fc24d7c2SNamhyung Kim 
3170aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3171fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
317268d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
3173064f1981SNamhyung Kim 					   float min_pcnt,
3174ce80d3beSKan Liang 					   struct perf_env *env)
3175aca7a94dSNamhyung Kim {
3176aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3177aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
3178aca7a94dSNamhyung Kim 		.b = {
3179aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
3180aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3181aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3182aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3183fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3184fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3185aca7a94dSNamhyung Kim 			.priv	    = evlist,
3186aca7a94dSNamhyung Kim 		},
3187064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
318868d80758SNamhyung Kim 		.env = env,
3189aca7a94dSNamhyung Kim 	};
3190aca7a94dSNamhyung Kim 
3191aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3192aca7a94dSNamhyung Kim 
31930050f7aaSArnaldo Carvalho de Melo 	evlist__for_each(evlist, pos) {
31947289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
3195aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3196aca7a94dSNamhyung Kim 
3197aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3198aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3199aca7a94dSNamhyung Kim 	}
3200aca7a94dSNamhyung Kim 
3201fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3202aca7a94dSNamhyung Kim }
3203aca7a94dSNamhyung Kim 
3204aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
320568d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
3206064f1981SNamhyung Kim 				  float min_pcnt,
3207ce80d3beSKan Liang 				  struct perf_env *env)
3208aca7a94dSNamhyung Kim {
3209fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
3210fc24d7c2SNamhyung Kim 
3211fc24d7c2SNamhyung Kim single_entry:
3212fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
32139a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
3214fc24d7c2SNamhyung Kim 
3215fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
3216dd00d486SJiri Olsa 						false, hbt, min_pcnt,
3217064f1981SNamhyung Kim 						env);
3218aca7a94dSNamhyung Kim 	}
3219aca7a94dSNamhyung Kim 
3220fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
3221fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
3222fc24d7c2SNamhyung Kim 
3223fc24d7c2SNamhyung Kim 		nr_entries = 0;
32240050f7aaSArnaldo Carvalho de Melo 		evlist__for_each(evlist, pos) {
3225fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
3226fc24d7c2SNamhyung Kim 				nr_entries++;
32270050f7aaSArnaldo Carvalho de Melo 		}
3228fc24d7c2SNamhyung Kim 
3229fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3230fc24d7c2SNamhyung Kim 			goto single_entry;
3231fc24d7c2SNamhyung Kim 	}
3232fc24d7c2SNamhyung Kim 
3233fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3234064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
3235aca7a94dSNamhyung Kim }
3236