xref: /linux/tools/perf/ui/browsers/hists.c (revision 7cecb7fe8388d5c39708d7f07e642ed31bb9d4fe)
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 
15aca7a94dSNamhyung Kim #include "../browser.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 
22aca7a94dSNamhyung Kim struct hist_browser {
23aca7a94dSNamhyung Kim 	struct ui_browser   b;
24aca7a94dSNamhyung Kim 	struct hists	    *hists;
25aca7a94dSNamhyung Kim 	struct hist_entry   *he_selection;
26aca7a94dSNamhyung Kim 	struct map_symbol   *selection;
27c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt;
2801f00a1cSNamhyung Kim 	struct pstack	    *pstack;
29ce80d3beSKan Liang 	struct perf_env *env;
30aff3f3f6SArnaldo Carvalho de Melo 	int		     print_seq;
31a7cb8863SArnaldo Carvalho de Melo 	bool		     show_dso;
32025bf7eaSArnaldo Carvalho de Melo 	bool		     show_headers;
33064f1981SNamhyung Kim 	float		     min_pcnt;
34112f761fSNamhyung Kim 	u64		     nr_non_filtered_entries;
35f5b763feSNamhyung Kim 	u64		     nr_hierarchy_entries;
36c3b78952SNamhyung Kim 	u64		     nr_callchain_rows;
37aca7a94dSNamhyung Kim };
38aca7a94dSNamhyung Kim 
39f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
40f5951d56SNamhyung Kim 
411e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
421e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
431e378ebdSTaeung Song 				char *bf, size_t size);
44112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
45aca7a94dSNamhyung Kim 
46c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
47c3b78952SNamhyung Kim 					     float min_pcnt);
48c3b78952SNamhyung Kim 
49268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
50268397cbSNamhyung Kim {
519c0fa8ddSArnaldo Carvalho de Melo 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
52268397cbSNamhyung Kim }
53268397cbSNamhyung Kim 
544fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
554fabf3d1SHe Kuang {
564fabf3d1SHe Kuang 	struct rb_node *nd;
574fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
584fabf3d1SHe Kuang 	int unfolded_rows = 0;
594fabf3d1SHe Kuang 
604fabf3d1SHe Kuang 	for (nd = rb_first(&hists->entries);
614fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
62f5b763feSNamhyung Kim 	     nd = rb_hierarchy_next(nd)) {
634fabf3d1SHe Kuang 		struct hist_entry *he =
644fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
654fabf3d1SHe Kuang 
66f5b763feSNamhyung Kim 		if (he->leaf && he->unfolded)
674fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
684fabf3d1SHe Kuang 	}
694fabf3d1SHe Kuang 	return unfolded_rows;
704fabf3d1SHe Kuang }
714fabf3d1SHe Kuang 
72c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
73c3b78952SNamhyung Kim {
74c3b78952SNamhyung Kim 	u32 nr_entries;
75c3b78952SNamhyung Kim 
76f5b763feSNamhyung Kim 	if (symbol_conf.report_hierarchy)
77f5b763feSNamhyung Kim 		nr_entries = hb->nr_hierarchy_entries;
78f5b763feSNamhyung Kim 	else if (hist_browser__has_filter(hb))
79c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
80c3b78952SNamhyung Kim 	else
81c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
82c3b78952SNamhyung Kim 
834fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
84c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
85c3b78952SNamhyung Kim }
86c3b78952SNamhyung Kim 
87025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
88025bf7eaSArnaldo Carvalho de Melo {
89025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
90025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
91025bf7eaSArnaldo Carvalho de Melo 
92025bf7eaSArnaldo Carvalho de Melo 	browser->rows = browser->height - header_offset;
93025bf7eaSArnaldo Carvalho de Melo 	/*
94025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
95025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
96025bf7eaSArnaldo Carvalho de Melo 	 */
97025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
98025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
99025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
100025bf7eaSArnaldo Carvalho de Melo }
101025bf7eaSArnaldo Carvalho de Melo 
102357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
103aca7a94dSNamhyung Kim {
104357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
105357cfff1SArnaldo Carvalho de Melo 
106aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
107357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
108357cfff1SArnaldo Carvalho de Melo 	/*
109357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
110357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
111357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
112357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
113357cfff1SArnaldo Carvalho de Melo  	 */
114357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
115025bf7eaSArnaldo Carvalho de Melo 	hist_browser__update_rows(hb);
116aca7a94dSNamhyung Kim }
117aca7a94dSNamhyung Kim 
118ca3ff33bSArnaldo Carvalho de Melo static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
119ca3ff33bSArnaldo Carvalho de Melo {
120025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = browser->show_headers ? 1 : 0;
121025bf7eaSArnaldo Carvalho de Melo 
122025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row + header_offset, column);
123ca3ff33bSArnaldo Carvalho de Melo }
124ca3ff33bSArnaldo Carvalho de Melo 
12505e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
126aca7a94dSNamhyung Kim {
127c3b78952SNamhyung Kim 	/*
128c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
129c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
130c3b78952SNamhyung Kim 	 */
131c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
132c3b78952SNamhyung Kim 
133268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
134c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
135357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
13605e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
137aca7a94dSNamhyung Kim }
138aca7a94dSNamhyung Kim 
139aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
140aca7a94dSNamhyung Kim {
141aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
142aca7a94dSNamhyung Kim }
143aca7a94dSNamhyung Kim 
14405e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
145aca7a94dSNamhyung Kim {
1463698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
147aca7a94dSNamhyung Kim }
148aca7a94dSNamhyung Kim 
14905e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
150aca7a94dSNamhyung Kim {
1513698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
152aca7a94dSNamhyung Kim }
153aca7a94dSNamhyung Kim 
1543698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
155aca7a94dSNamhyung Kim {
1563698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
157aca7a94dSNamhyung Kim }
158aca7a94dSNamhyung Kim 
15905e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
160aca7a94dSNamhyung Kim {
161aca7a94dSNamhyung Kim 	int n = 0;
162aca7a94dSNamhyung Kim 	struct rb_node *nd;
163aca7a94dSNamhyung Kim 
16405e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
165aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
166aca7a94dSNamhyung Kim 		struct callchain_list *chain;
167aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
168aca7a94dSNamhyung Kim 
169aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
170aca7a94dSNamhyung Kim 			++n;
171aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
172aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
173aca7a94dSNamhyung Kim 			if (folded_sign == '+')
174aca7a94dSNamhyung Kim 				break;
175aca7a94dSNamhyung Kim 		}
176aca7a94dSNamhyung Kim 
177aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
178aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
179aca7a94dSNamhyung Kim 	}
180aca7a94dSNamhyung Kim 
181aca7a94dSNamhyung Kim 	return n;
182aca7a94dSNamhyung Kim }
183aca7a94dSNamhyung Kim 
1844b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
1854b3a3212SNamhyung Kim {
1864b3a3212SNamhyung Kim 	struct callchain_list *chain;
1874b3a3212SNamhyung Kim 	char folded_sign = 0;
1884b3a3212SNamhyung Kim 	int n = 0;
1894b3a3212SNamhyung Kim 
1904b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
1914b3a3212SNamhyung Kim 		if (!folded_sign) {
1924b3a3212SNamhyung Kim 			/* only check first chain list entry */
1934b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1944b3a3212SNamhyung Kim 			if (folded_sign == '+')
1954b3a3212SNamhyung Kim 				return 1;
1964b3a3212SNamhyung Kim 		}
1974b3a3212SNamhyung Kim 		n++;
1984b3a3212SNamhyung Kim 	}
1994b3a3212SNamhyung Kim 
2004b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
2014b3a3212SNamhyung Kim 		if (!folded_sign) {
2024b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
2034b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2044b3a3212SNamhyung Kim 			if (folded_sign == '+')
2054b3a3212SNamhyung Kim 				return 1;
2064b3a3212SNamhyung Kim 		}
2074b3a3212SNamhyung Kim 		n++;
2084b3a3212SNamhyung Kim 	}
2094b3a3212SNamhyung Kim 
2104b3a3212SNamhyung Kim 	return n;
2114b3a3212SNamhyung Kim }
2124b3a3212SNamhyung Kim 
2138c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
2148c430a34SNamhyung Kim {
2158c430a34SNamhyung Kim 	return 1;
2168c430a34SNamhyung Kim }
2178c430a34SNamhyung Kim 
218aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
219aca7a94dSNamhyung Kim {
220aca7a94dSNamhyung Kim 	struct callchain_list *chain;
221aca7a94dSNamhyung Kim 	bool unfolded = false;
222aca7a94dSNamhyung Kim 	int n = 0;
223aca7a94dSNamhyung Kim 
2244b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2254b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2268c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2278c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2284b3a3212SNamhyung Kim 
229aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
230aca7a94dSNamhyung Kim 		++n;
2313698dab1SNamhyung Kim 		unfolded = chain->unfolded;
232aca7a94dSNamhyung Kim 	}
233aca7a94dSNamhyung Kim 
234aca7a94dSNamhyung Kim 	if (unfolded)
235aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
236aca7a94dSNamhyung Kim 
237aca7a94dSNamhyung Kim 	return n;
238aca7a94dSNamhyung Kim }
239aca7a94dSNamhyung Kim 
240aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
241aca7a94dSNamhyung Kim {
242aca7a94dSNamhyung Kim 	struct rb_node *nd;
243aca7a94dSNamhyung Kim 	int n = 0;
244aca7a94dSNamhyung Kim 
245aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
246aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
247aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
248aca7a94dSNamhyung Kim 	}
249aca7a94dSNamhyung Kim 
250aca7a94dSNamhyung Kim 	return n;
251aca7a94dSNamhyung Kim }
252aca7a94dSNamhyung Kim 
253f5b763feSNamhyung Kim static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
254f5b763feSNamhyung Kim 				bool include_children)
255f5b763feSNamhyung Kim {
256f5b763feSNamhyung Kim 	int count = 0;
257f5b763feSNamhyung Kim 	struct rb_node *node;
258f5b763feSNamhyung Kim 	struct hist_entry *child;
259f5b763feSNamhyung Kim 
260f5b763feSNamhyung Kim 	if (he->leaf)
261f5b763feSNamhyung Kim 		return callchain__count_rows(&he->sorted_chain);
262f5b763feSNamhyung Kim 
26379dded87SNamhyung Kim 	if (he->has_no_entry)
26479dded87SNamhyung Kim 		return 1;
26579dded87SNamhyung Kim 
266f5b763feSNamhyung Kim 	node = rb_first(&he->hroot_out);
267f5b763feSNamhyung Kim 	while (node) {
268f5b763feSNamhyung Kim 		float percent;
269f5b763feSNamhyung Kim 
270f5b763feSNamhyung Kim 		child = rb_entry(node, struct hist_entry, rb_node);
271f5b763feSNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
272f5b763feSNamhyung Kim 
273f5b763feSNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt) {
274f5b763feSNamhyung Kim 			count++;
275f5b763feSNamhyung Kim 
276f5b763feSNamhyung Kim 			if (include_children && child->unfolded)
277f5b763feSNamhyung Kim 				count += hierarchy_count_rows(hb, child, true);
278f5b763feSNamhyung Kim 		}
279f5b763feSNamhyung Kim 
280f5b763feSNamhyung Kim 		node = rb_next(node);
281f5b763feSNamhyung Kim 	}
282f5b763feSNamhyung Kim 	return count;
283f5b763feSNamhyung Kim }
284f5b763feSNamhyung Kim 
2853698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
286aca7a94dSNamhyung Kim {
2873698dab1SNamhyung Kim 	if (!he)
288aca7a94dSNamhyung Kim 		return false;
289aca7a94dSNamhyung Kim 
2903698dab1SNamhyung Kim 	if (!he->has_children)
291aca7a94dSNamhyung Kim 		return false;
292aca7a94dSNamhyung Kim 
2933698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
2943698dab1SNamhyung Kim 	return true;
2953698dab1SNamhyung Kim }
2963698dab1SNamhyung Kim 
2973698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
2983698dab1SNamhyung Kim {
2993698dab1SNamhyung Kim 	if (!cl)
3003698dab1SNamhyung Kim 		return false;
3013698dab1SNamhyung Kim 
3023698dab1SNamhyung Kim 	if (!cl->has_children)
3033698dab1SNamhyung Kim 		return false;
3043698dab1SNamhyung Kim 
3053698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
306aca7a94dSNamhyung Kim 	return true;
307aca7a94dSNamhyung Kim }
308aca7a94dSNamhyung Kim 
30905e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
310aca7a94dSNamhyung Kim {
31105e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
312aca7a94dSNamhyung Kim 
31305e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
314aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
315aca7a94dSNamhyung Kim 		struct callchain_list *chain;
316aca7a94dSNamhyung Kim 		bool first = true;
317aca7a94dSNamhyung Kim 
318aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
319aca7a94dSNamhyung Kim 			if (first) {
320aca7a94dSNamhyung Kim 				first = false;
3213698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
322aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
323aca7a94dSNamhyung Kim 			} else
3243698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
325aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
326aca7a94dSNamhyung Kim 		}
327aca7a94dSNamhyung Kim 
328aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
329aca7a94dSNamhyung Kim 	}
330aca7a94dSNamhyung Kim }
331aca7a94dSNamhyung Kim 
332a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
333a7444af6SNamhyung Kim 					       bool has_sibling)
334aca7a94dSNamhyung Kim {
335aca7a94dSNamhyung Kim 	struct callchain_list *chain;
336aca7a94dSNamhyung Kim 
337a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3383698dab1SNamhyung Kim 	chain->has_children = has_sibling;
339a7444af6SNamhyung Kim 
34090989035SAndres Freund 	if (!list_empty(&node->val)) {
34182162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3423698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
34382162b5aSNamhyung Kim 	}
344aca7a94dSNamhyung Kim 
34505e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
346aca7a94dSNamhyung Kim }
347aca7a94dSNamhyung Kim 
34805e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
349aca7a94dSNamhyung Kim {
350a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
351a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
352aca7a94dSNamhyung Kim 
35305e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
354aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
355a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3568c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3578c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3584b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
359aca7a94dSNamhyung Kim 	}
360aca7a94dSNamhyung Kim }
361aca7a94dSNamhyung Kim 
36205e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
363aca7a94dSNamhyung Kim {
364f5b763feSNamhyung Kim 	if (he->init_have_children)
365f5b763feSNamhyung Kim 		return;
366f5b763feSNamhyung Kim 
367f5b763feSNamhyung Kim 	if (he->leaf) {
3683698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
36905e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
370f5b763feSNamhyung Kim 	} else {
371f5b763feSNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
372aca7a94dSNamhyung Kim 	}
373f5b763feSNamhyung Kim 
374f5b763feSNamhyung Kim 	he->init_have_children = true;
375aca7a94dSNamhyung Kim }
376aca7a94dSNamhyung Kim 
37705e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
378aca7a94dSNamhyung Kim {
37905e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
3803698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
3813698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
3823698dab1SNamhyung Kim 	bool has_children;
383aca7a94dSNamhyung Kim 
3844938cf0cSWang Nan 	if (!he || !ms)
3854938cf0cSWang Nan 		return false;
3864938cf0cSWang Nan 
3873698dab1SNamhyung Kim 	if (ms == &he->ms)
3883698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
3893698dab1SNamhyung Kim 	else
3903698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
3913698dab1SNamhyung Kim 
3923698dab1SNamhyung Kim 	if (has_children) {
393f5b763feSNamhyung Kim 		int child_rows = 0;
394f5b763feSNamhyung Kim 
395aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
396c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
397aca7a94dSNamhyung Kim 
398f5b763feSNamhyung Kim 		if (he->leaf)
399f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
400f5b763feSNamhyung Kim 		else
401f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
402f5b763feSNamhyung Kim 
403f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
404f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
405f5b763feSNamhyung Kim 
406f5b763feSNamhyung Kim 		if (he->unfolded) {
407f5b763feSNamhyung Kim 			if (he->leaf)
408aca7a94dSNamhyung Kim 				he->nr_rows = callchain__count_rows(&he->sorted_chain);
409aca7a94dSNamhyung Kim 			else
410f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
411f5b763feSNamhyung Kim 
412f5b763feSNamhyung Kim 			/* account grand children */
413f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
414f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
41579dded87SNamhyung Kim 
41679dded87SNamhyung Kim 			if (!he->leaf && he->nr_rows == 0) {
41779dded87SNamhyung Kim 				he->has_no_entry = true;
41879dded87SNamhyung Kim 				he->nr_rows = 1;
41979dded87SNamhyung Kim 			}
420f5b763feSNamhyung Kim 		} else {
421f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
422f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
423f5b763feSNamhyung Kim 
42479dded87SNamhyung Kim 			if (he->has_no_entry)
42579dded87SNamhyung Kim 				he->has_no_entry = false;
42679dded87SNamhyung Kim 
427aca7a94dSNamhyung Kim 			he->nr_rows = 0;
428f5b763feSNamhyung Kim 		}
429c3b78952SNamhyung Kim 
430c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
431f5b763feSNamhyung Kim 
432f5b763feSNamhyung Kim 		if (he->leaf)
433c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
434f5b763feSNamhyung Kim 		else
435f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
436aca7a94dSNamhyung Kim 
437aca7a94dSNamhyung Kim 		return true;
438aca7a94dSNamhyung Kim 	}
439aca7a94dSNamhyung Kim 
440aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
441aca7a94dSNamhyung Kim 	return false;
442aca7a94dSNamhyung Kim }
443aca7a94dSNamhyung Kim 
44405e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
445aca7a94dSNamhyung Kim {
446aca7a94dSNamhyung Kim 	int n = 0;
447aca7a94dSNamhyung Kim 	struct rb_node *nd;
448aca7a94dSNamhyung Kim 
44905e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
450aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
451aca7a94dSNamhyung Kim 		struct callchain_list *chain;
452aca7a94dSNamhyung Kim 		bool has_children = false;
453aca7a94dSNamhyung Kim 
454aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
455aca7a94dSNamhyung Kim 			++n;
4563698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
4573698dab1SNamhyung Kim 			has_children = chain->has_children;
458aca7a94dSNamhyung Kim 		}
459aca7a94dSNamhyung Kim 
460aca7a94dSNamhyung Kim 		if (has_children)
461aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
462aca7a94dSNamhyung Kim 	}
463aca7a94dSNamhyung Kim 
464aca7a94dSNamhyung Kim 	return n;
465aca7a94dSNamhyung Kim }
466aca7a94dSNamhyung Kim 
467aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
468aca7a94dSNamhyung Kim {
469aca7a94dSNamhyung Kim 	struct callchain_list *chain;
470aca7a94dSNamhyung Kim 	bool has_children = false;
471aca7a94dSNamhyung Kim 	int n = 0;
472aca7a94dSNamhyung Kim 
473aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
474aca7a94dSNamhyung Kim 		++n;
4753698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
4763698dab1SNamhyung Kim 		has_children = chain->has_children;
477aca7a94dSNamhyung Kim 	}
478aca7a94dSNamhyung Kim 
479aca7a94dSNamhyung Kim 	if (has_children)
480aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
481aca7a94dSNamhyung Kim 
482aca7a94dSNamhyung Kim 	return n;
483aca7a94dSNamhyung Kim }
484aca7a94dSNamhyung Kim 
485aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
486aca7a94dSNamhyung Kim {
487aca7a94dSNamhyung Kim 	struct rb_node *nd;
488aca7a94dSNamhyung Kim 	int n = 0;
489aca7a94dSNamhyung Kim 
490aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
491aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
492aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
493aca7a94dSNamhyung Kim 	}
494aca7a94dSNamhyung Kim 
495aca7a94dSNamhyung Kim 	return n;
496aca7a94dSNamhyung Kim }
497aca7a94dSNamhyung Kim 
498492b1010SNamhyung Kim static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
499492b1010SNamhyung Kim 				 bool unfold __maybe_unused)
500492b1010SNamhyung Kim {
501492b1010SNamhyung Kim 	float percent;
502492b1010SNamhyung Kim 	struct rb_node *nd;
503492b1010SNamhyung Kim 	struct hist_entry *child;
504492b1010SNamhyung Kim 	int n = 0;
505492b1010SNamhyung Kim 
506492b1010SNamhyung Kim 	for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
507492b1010SNamhyung Kim 		child = rb_entry(nd, struct hist_entry, rb_node);
508492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
509492b1010SNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt)
510492b1010SNamhyung Kim 			n++;
511492b1010SNamhyung Kim 	}
512492b1010SNamhyung Kim 
513492b1010SNamhyung Kim 	return n;
514492b1010SNamhyung Kim }
515492b1010SNamhyung Kim 
516492b1010SNamhyung Kim static void hist_entry__set_folding(struct hist_entry *he,
517492b1010SNamhyung Kim 				    struct hist_browser *hb, bool unfold)
518aca7a94dSNamhyung Kim {
51905e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
5203698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
521aca7a94dSNamhyung Kim 
5223698dab1SNamhyung Kim 	if (he->has_children) {
523492b1010SNamhyung Kim 		int n;
524492b1010SNamhyung Kim 
525492b1010SNamhyung Kim 		if (he->leaf)
526492b1010SNamhyung Kim 			n = callchain__set_folding(&he->sorted_chain, unfold);
527492b1010SNamhyung Kim 		else
528492b1010SNamhyung Kim 			n = hierarchy_set_folding(hb, he, unfold);
529492b1010SNamhyung Kim 
53005e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
531aca7a94dSNamhyung Kim 	} else
53205e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
533aca7a94dSNamhyung Kim }
534aca7a94dSNamhyung Kim 
535c3b78952SNamhyung Kim static void
536c3b78952SNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
537aca7a94dSNamhyung Kim {
538aca7a94dSNamhyung Kim 	struct rb_node *nd;
539492b1010SNamhyung Kim 	struct hist_entry *he;
540492b1010SNamhyung Kim 	double percent;
541aca7a94dSNamhyung Kim 
542492b1010SNamhyung Kim 	nd = rb_first(&browser->hists->entries);
543492b1010SNamhyung Kim 	while (nd) {
544492b1010SNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
545492b1010SNamhyung Kim 
546492b1010SNamhyung Kim 		/* set folding state even if it's currently folded */
547492b1010SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
548492b1010SNamhyung Kim 
549492b1010SNamhyung Kim 		hist_entry__set_folding(he, browser, unfold);
550492b1010SNamhyung Kim 
551492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(he);
552492b1010SNamhyung Kim 		if (he->filtered || percent < browser->min_pcnt)
553492b1010SNamhyung Kim 			continue;
554492b1010SNamhyung Kim 
555492b1010SNamhyung Kim 		if (!he->depth || unfold)
556492b1010SNamhyung Kim 			browser->nr_hierarchy_entries++;
557492b1010SNamhyung Kim 		if (he->leaf)
558c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
55979dded87SNamhyung Kim 		else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
56079dded87SNamhyung Kim 			browser->nr_hierarchy_entries++;
56179dded87SNamhyung Kim 			he->has_no_entry = true;
56279dded87SNamhyung Kim 			he->nr_rows = 1;
56379dded87SNamhyung Kim 		} else
56479dded87SNamhyung Kim 			he->has_no_entry = false;
565aca7a94dSNamhyung Kim 	}
566aca7a94dSNamhyung Kim }
567aca7a94dSNamhyung Kim 
56805e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
569aca7a94dSNamhyung Kim {
570492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
571c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
572c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
573c3b78952SNamhyung Kim 
574c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
575aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
57605e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
577aca7a94dSNamhyung Kim }
578aca7a94dSNamhyung Kim 
579aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
580aca7a94dSNamhyung Kim {
581aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
582aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
583aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
584aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
585aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
586aca7a94dSNamhyung Kim }
587aca7a94dSNamhyung Kim 
5885f00b0f4SArnaldo Carvalho de Melo static int hist_browser__run(struct hist_browser *browser, const char *help)
589aca7a94dSNamhyung Kim {
590aca7a94dSNamhyung Kim 	int key;
591aca7a94dSNamhyung Kim 	char title[160];
592c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
5939783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
594aca7a94dSNamhyung Kim 
59505e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
596c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
597aca7a94dSNamhyung Kim 
5981e378ebdSTaeung Song 	hists__browser_title(browser->hists, hbt, title, sizeof(title));
599aca7a94dSNamhyung Kim 
600090cff3eSNamhyung Kim 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
601aca7a94dSNamhyung Kim 		return -1;
602aca7a94dSNamhyung Kim 
603aca7a94dSNamhyung Kim 	while (1) {
60405e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
605aca7a94dSNamhyung Kim 
606aca7a94dSNamhyung Kim 		switch (key) {
607fa5df943SNamhyung Kim 		case K_TIMER: {
608fa5df943SNamhyung Kim 			u64 nr_entries;
6099783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
610fa5df943SNamhyung Kim 
611c3b78952SNamhyung Kim 			if (hist_browser__has_filter(browser))
612112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
613fa5df943SNamhyung Kim 
614c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
615fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
616aca7a94dSNamhyung Kim 
61705e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
61805e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
61905e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
62005e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
62105e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
622aca7a94dSNamhyung Kim 			}
623aca7a94dSNamhyung Kim 
6241e378ebdSTaeung Song 			hists__browser_title(browser->hists,
6251e378ebdSTaeung Song 					     hbt, title, sizeof(title));
62605e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
627aca7a94dSNamhyung Kim 			continue;
628fa5df943SNamhyung Kim 		}
629aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
630aca7a94dSNamhyung Kim 			static int seq;
63105e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
632aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
633aca7a94dSNamhyung Kim 			ui_helpline__pop();
63462c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
63505e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
63605e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
63762c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
63805e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
63905e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
640aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
641aca7a94dSNamhyung Kim 		}
642aca7a94dSNamhyung Kim 			break;
643aca7a94dSNamhyung Kim 		case 'C':
644aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
64505e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
646aca7a94dSNamhyung Kim 			break;
647aca7a94dSNamhyung Kim 		case 'E':
648aca7a94dSNamhyung Kim 			/* Expand the whole world. */
64905e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
650aca7a94dSNamhyung Kim 			break;
651025bf7eaSArnaldo Carvalho de Melo 		case 'H':
652025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
653025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
654025bf7eaSArnaldo Carvalho de Melo 			break;
655aca7a94dSNamhyung Kim 		case K_ENTER:
65605e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
657aca7a94dSNamhyung Kim 				break;
658aca7a94dSNamhyung Kim 			/* fall thru */
659aca7a94dSNamhyung Kim 		default:
660aca7a94dSNamhyung Kim 			goto out;
661aca7a94dSNamhyung Kim 		}
662aca7a94dSNamhyung Kim 	}
663aca7a94dSNamhyung Kim out:
66405e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
665aca7a94dSNamhyung Kim 	return key;
666aca7a94dSNamhyung Kim }
667aca7a94dSNamhyung Kim 
66839ee533fSNamhyung Kim struct callchain_print_arg {
66939ee533fSNamhyung Kim 	/* for hists browser */
67039ee533fSNamhyung Kim 	off_t	row_offset;
67139ee533fSNamhyung Kim 	bool	is_current_entry;
67239ee533fSNamhyung Kim 
67339ee533fSNamhyung Kim 	/* for file dump */
67439ee533fSNamhyung Kim 	FILE	*fp;
67539ee533fSNamhyung Kim 	int	printed;
67639ee533fSNamhyung Kim };
67739ee533fSNamhyung Kim 
67839ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
67939ee533fSNamhyung Kim 					 struct callchain_list *chain,
68039ee533fSNamhyung Kim 					 const char *str, int offset,
68139ee533fSNamhyung Kim 					 unsigned short row,
68239ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
68339ee533fSNamhyung Kim 
684f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
685f4536dddSNamhyung Kim 					       struct callchain_list *chain,
68639ee533fSNamhyung Kim 					       const char *str, int offset,
68739ee533fSNamhyung Kim 					       unsigned short row,
68839ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
689f4536dddSNamhyung Kim {
690f4536dddSNamhyung Kim 	int color, width;
69139ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
69270e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
693f4536dddSNamhyung Kim 
694f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
695f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
696f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
697f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
698f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
69939ee533fSNamhyung Kim 		arg->is_current_entry = true;
700f4536dddSNamhyung Kim 	}
701f4536dddSNamhyung Kim 
702f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
703f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
70426270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
705517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
70670e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
70726270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
708f4536dddSNamhyung Kim }
709f4536dddSNamhyung Kim 
71039ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
71139ee533fSNamhyung Kim 						  struct callchain_list *chain,
71239ee533fSNamhyung Kim 						  const char *str, int offset,
71339ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
71439ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
71539ee533fSNamhyung Kim {
71639ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
71739ee533fSNamhyung Kim 
71839ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
71939ee533fSNamhyung Kim 				folded_sign, str);
72039ee533fSNamhyung Kim }
72139ee533fSNamhyung Kim 
72239ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
72339ee533fSNamhyung Kim 				     unsigned short row);
72439ee533fSNamhyung Kim 
72539ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
72639ee533fSNamhyung Kim 					    unsigned short row)
72739ee533fSNamhyung Kim {
72839ee533fSNamhyung Kim 	return browser->b.rows == row;
72939ee533fSNamhyung Kim }
73039ee533fSNamhyung Kim 
73139ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
73239ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
73339ee533fSNamhyung Kim {
73439ee533fSNamhyung Kim 	return false;
73539ee533fSNamhyung Kim }
73639ee533fSNamhyung Kim 
737aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
738aca7a94dSNamhyung Kim 
73918bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
74018bb8381SNamhyung Kim 					     struct callchain_node *node,
74118bb8381SNamhyung Kim 					     struct callchain_list *chain,
74218bb8381SNamhyung Kim 					     unsigned short row, u64 total,
74318bb8381SNamhyung Kim 					     bool need_percent, int offset,
74418bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
74518bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
74618bb8381SNamhyung Kim {
74718bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
74818bb8381SNamhyung Kim 	const char *str;
74918bb8381SNamhyung Kim 
75018bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
75118bb8381SNamhyung Kim 		arg->row_offset--;
75218bb8381SNamhyung Kim 		return 0;
75318bb8381SNamhyung Kim 	}
75418bb8381SNamhyung Kim 
75518bb8381SNamhyung Kim 	alloc_str = NULL;
75618bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
75718bb8381SNamhyung Kim 				       browser->show_dso);
75818bb8381SNamhyung Kim 
75918bb8381SNamhyung Kim 	if (need_percent) {
76018bb8381SNamhyung Kim 		char buf[64];
76118bb8381SNamhyung Kim 
76218bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
76318bb8381SNamhyung Kim 						total);
76418bb8381SNamhyung Kim 
76518bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
76618bb8381SNamhyung Kim 			str = "Not enough memory!";
76718bb8381SNamhyung Kim 		else
76818bb8381SNamhyung Kim 			str = alloc_str;
76918bb8381SNamhyung Kim 	}
77018bb8381SNamhyung Kim 
77118bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
77218bb8381SNamhyung Kim 
77318bb8381SNamhyung Kim 	free(alloc_str);
77418bb8381SNamhyung Kim 	return 1;
77518bb8381SNamhyung Kim }
77618bb8381SNamhyung Kim 
77759c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
77859c624e2SNamhyung Kim {
77959c624e2SNamhyung Kim 	struct callchain_node *child;
78059c624e2SNamhyung Kim 
78159c624e2SNamhyung Kim 	if (node == NULL)
78259c624e2SNamhyung Kim 		return false;
78359c624e2SNamhyung Kim 
78459c624e2SNamhyung Kim 	if (rb_next(node))
78559c624e2SNamhyung Kim 		return true;
78659c624e2SNamhyung Kim 
78759c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
78859c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
78959c624e2SNamhyung Kim }
79059c624e2SNamhyung Kim 
7914b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
7924b3a3212SNamhyung Kim 					     struct rb_root *root,
7934b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
79459c624e2SNamhyung Kim 					     u64 parent_total,
7954b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
7964b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
7974b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
7984b3a3212SNamhyung Kim {
7994b3a3212SNamhyung Kim 	struct rb_node *node;
8004b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
8014b3a3212SNamhyung Kim 	bool need_percent;
8024b3a3212SNamhyung Kim 
8034b3a3212SNamhyung Kim 	node = rb_first(root);
80459c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
8054b3a3212SNamhyung Kim 
8064b3a3212SNamhyung Kim 	while (node) {
8074b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
8084b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
8094b3a3212SNamhyung Kim 		struct callchain_list *chain;
8104b3a3212SNamhyung Kim 		char folded_sign = ' ';
8114b3a3212SNamhyung Kim 		int first = true;
8124b3a3212SNamhyung Kim 		int extra_offset = 0;
8134b3a3212SNamhyung Kim 
8144b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
8154b3a3212SNamhyung Kim 			bool was_first = first;
8164b3a3212SNamhyung Kim 
8174b3a3212SNamhyung Kim 			if (first)
8184b3a3212SNamhyung Kim 				first = false;
8194b3a3212SNamhyung Kim 			else if (need_percent)
8204b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8214b3a3212SNamhyung Kim 
8224b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8234b3a3212SNamhyung Kim 
8244b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8254b3a3212SNamhyung Kim 							chain, row, total,
8264b3a3212SNamhyung Kim 							was_first && need_percent,
8274b3a3212SNamhyung Kim 							offset + extra_offset,
8284b3a3212SNamhyung Kim 							print, arg);
8294b3a3212SNamhyung Kim 
8304b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8314b3a3212SNamhyung Kim 				goto out;
8324b3a3212SNamhyung Kim 
8334b3a3212SNamhyung Kim 			if (folded_sign == '+')
8344b3a3212SNamhyung Kim 				goto next;
8354b3a3212SNamhyung Kim 		}
8364b3a3212SNamhyung Kim 
8374b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
8384b3a3212SNamhyung Kim 			bool was_first = first;
8394b3a3212SNamhyung Kim 
8404b3a3212SNamhyung Kim 			if (first)
8414b3a3212SNamhyung Kim 				first = false;
8424b3a3212SNamhyung Kim 			else if (need_percent)
8434b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8444b3a3212SNamhyung Kim 
8454b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8464b3a3212SNamhyung Kim 
8474b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8484b3a3212SNamhyung Kim 							chain, row, total,
8494b3a3212SNamhyung Kim 							was_first && need_percent,
8504b3a3212SNamhyung Kim 							offset + extra_offset,
8514b3a3212SNamhyung Kim 							print, arg);
8524b3a3212SNamhyung Kim 
8534b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8544b3a3212SNamhyung Kim 				goto out;
8554b3a3212SNamhyung Kim 
8564b3a3212SNamhyung Kim 			if (folded_sign == '+')
8574b3a3212SNamhyung Kim 				break;
8584b3a3212SNamhyung Kim 		}
8594b3a3212SNamhyung Kim 
8604b3a3212SNamhyung Kim next:
8614b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
8624b3a3212SNamhyung Kim 			break;
8634b3a3212SNamhyung Kim 		node = next;
8644b3a3212SNamhyung Kim 	}
8654b3a3212SNamhyung Kim out:
8664b3a3212SNamhyung Kim 	return row - first_row;
8674b3a3212SNamhyung Kim }
8684b3a3212SNamhyung Kim 
8698c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
8708c430a34SNamhyung Kim 						struct callchain_list *chain,
8718c430a34SNamhyung Kim 						char *value_str, char *old_str)
8728c430a34SNamhyung Kim {
8738c430a34SNamhyung Kim 	char bf[1024];
8748c430a34SNamhyung Kim 	const char *str;
8758c430a34SNamhyung Kim 	char *new;
8768c430a34SNamhyung Kim 
8778c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
8788c430a34SNamhyung Kim 				       browser->show_dso);
8798c430a34SNamhyung Kim 	if (old_str) {
8808c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
8818c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
8828c430a34SNamhyung Kim 			new = NULL;
8838c430a34SNamhyung Kim 	} else {
8848c430a34SNamhyung Kim 		if (value_str) {
8858c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
8868c430a34SNamhyung Kim 				new = NULL;
8878c430a34SNamhyung Kim 		} else {
8888c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
8898c430a34SNamhyung Kim 				new = NULL;
8908c430a34SNamhyung Kim 		}
8918c430a34SNamhyung Kim 	}
8928c430a34SNamhyung Kim 	return new;
8938c430a34SNamhyung Kim }
8948c430a34SNamhyung Kim 
8958c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
8968c430a34SNamhyung Kim 					       struct rb_root *root,
8978c430a34SNamhyung Kim 					       unsigned short row, u64 total,
89859c624e2SNamhyung Kim 					       u64 parent_total,
8998c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
9008c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
9018c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
9028c430a34SNamhyung Kim {
9038c430a34SNamhyung Kim 	struct rb_node *node;
9048c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
9058c430a34SNamhyung Kim 	bool need_percent;
9068c430a34SNamhyung Kim 
9078c430a34SNamhyung Kim 	node = rb_first(root);
90859c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9098c430a34SNamhyung Kim 
9108c430a34SNamhyung Kim 	while (node) {
9118c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
9128c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
9138c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
9148c430a34SNamhyung Kim 		int first = true;
9158c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
9168c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
9178c430a34SNamhyung Kim 
9188c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
9198c430a34SNamhyung Kim 			arg->row_offset--;
9208c430a34SNamhyung Kim 			goto next;
9218c430a34SNamhyung Kim 		}
9228c430a34SNamhyung Kim 
9238c430a34SNamhyung Kim 		if (need_percent) {
9248c430a34SNamhyung Kim 			char buf[64];
9258c430a34SNamhyung Kim 
9268c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
9278c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
9288c430a34SNamhyung Kim 				value_str = (char *)"<...>";
9298c430a34SNamhyung Kim 				goto do_print;
9308c430a34SNamhyung Kim 			}
9318c430a34SNamhyung Kim 			value_str_alloc = value_str;
9328c430a34SNamhyung Kim 		}
9338c430a34SNamhyung Kim 
9348c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9358c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9368c430a34SNamhyung Kim 						chain, value_str, chain_str);
9378c430a34SNamhyung Kim 			if (first) {
9388c430a34SNamhyung Kim 				first = false;
9398c430a34SNamhyung Kim 				first_chain = chain;
9408c430a34SNamhyung Kim 			}
9418c430a34SNamhyung Kim 
9428c430a34SNamhyung Kim 			if (chain_str == NULL) {
9438c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9448c430a34SNamhyung Kim 				goto do_print;
9458c430a34SNamhyung Kim 			}
9468c430a34SNamhyung Kim 
9478c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9488c430a34SNamhyung Kim 		}
9498c430a34SNamhyung Kim 
9508c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
9518c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9528c430a34SNamhyung Kim 						chain, value_str, chain_str);
9538c430a34SNamhyung Kim 			if (first) {
9548c430a34SNamhyung Kim 				first = false;
9558c430a34SNamhyung Kim 				first_chain = chain;
9568c430a34SNamhyung Kim 			}
9578c430a34SNamhyung Kim 
9588c430a34SNamhyung Kim 			if (chain_str == NULL) {
9598c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9608c430a34SNamhyung Kim 				goto do_print;
9618c430a34SNamhyung Kim 			}
9628c430a34SNamhyung Kim 
9638c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9648c430a34SNamhyung Kim 		}
9658c430a34SNamhyung Kim 
9668c430a34SNamhyung Kim do_print:
9678c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
9688c430a34SNamhyung Kim 		free(value_str_alloc);
9698c430a34SNamhyung Kim 		free(chain_str_alloc);
9708c430a34SNamhyung Kim 
9718c430a34SNamhyung Kim next:
9728c430a34SNamhyung Kim 		if (is_output_full(browser, row))
9738c430a34SNamhyung Kim 			break;
9748c430a34SNamhyung Kim 		node = next;
9758c430a34SNamhyung Kim 	}
9768c430a34SNamhyung Kim 
9778c430a34SNamhyung Kim 	return row - first_row;
9788c430a34SNamhyung Kim }
9798c430a34SNamhyung Kim 
9800c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
981c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
98239ee533fSNamhyung Kim 					unsigned short row, u64 total,
9835eca104eSNamhyung Kim 					u64 parent_total,
98439ee533fSNamhyung Kim 					print_callchain_entry_fn print,
98539ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
98639ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
987aca7a94dSNamhyung Kim {
988aca7a94dSNamhyung Kim 	struct rb_node *node;
989f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
9904087d11cSNamhyung Kim 	bool need_percent;
9915eca104eSNamhyung Kim 	u64 percent_total = total;
9925eca104eSNamhyung Kim 
9935eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
9945eca104eSNamhyung Kim 		percent_total = parent_total;
995aca7a94dSNamhyung Kim 
996c09a7e75SNamhyung Kim 	node = rb_first(root);
99759c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9984087d11cSNamhyung Kim 
999aca7a94dSNamhyung Kim 	while (node) {
1000aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1001aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
1002aca7a94dSNamhyung Kim 		struct callchain_list *chain;
1003aca7a94dSNamhyung Kim 		char folded_sign = ' ';
1004aca7a94dSNamhyung Kim 		int first = true;
1005aca7a94dSNamhyung Kim 		int extra_offset = 0;
1006aca7a94dSNamhyung Kim 
1007aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
1008aca7a94dSNamhyung Kim 			bool was_first = first;
1009aca7a94dSNamhyung Kim 
1010aca7a94dSNamhyung Kim 			if (first)
1011aca7a94dSNamhyung Kim 				first = false;
10124087d11cSNamhyung Kim 			else if (need_percent)
1013aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1014aca7a94dSNamhyung Kim 
1015aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1016aca7a94dSNamhyung Kim 
101718bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
10185eca104eSNamhyung Kim 							chain, row, percent_total,
101918bb8381SNamhyung Kim 							was_first && need_percent,
102018bb8381SNamhyung Kim 							offset + extra_offset,
102118bb8381SNamhyung Kim 							print, arg);
1022c09a7e75SNamhyung Kim 
102318bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1024aca7a94dSNamhyung Kim 				goto out;
102518bb8381SNamhyung Kim 
1026aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1027aca7a94dSNamhyung Kim 				break;
1028aca7a94dSNamhyung Kim 		}
1029aca7a94dSNamhyung Kim 
1030aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1031aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1032c09a7e75SNamhyung Kim 
10330c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
10345eca104eSNamhyung Kim 							    new_level, row, total,
10355eca104eSNamhyung Kim 							    child->children_hit,
103639ee533fSNamhyung Kim 							    print, arg, is_output_full);
1037aca7a94dSNamhyung Kim 		}
103839ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1039c09a7e75SNamhyung Kim 			break;
1040aca7a94dSNamhyung Kim 		node = next;
1041aca7a94dSNamhyung Kim 	}
1042aca7a94dSNamhyung Kim out:
1043aca7a94dSNamhyung Kim 	return row - first_row;
1044aca7a94dSNamhyung Kim }
1045aca7a94dSNamhyung Kim 
10460c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
10470c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
10480c841c6cSNamhyung Kim 					unsigned short row,
10490c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
10500c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
10510c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
10520c841c6cSNamhyung Kim {
10530c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
10545eca104eSNamhyung Kim 	u64 parent_total;
10550c841c6cSNamhyung Kim 	int printed;
10560c841c6cSNamhyung Kim 
10570c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
10585eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
10590c841c6cSNamhyung Kim 	else
10605eca104eSNamhyung Kim 		parent_total = entry->stat.period;
10610c841c6cSNamhyung Kim 
10620c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
10630c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
10645eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10655eca104eSNamhyung Kim 						total, parent_total, print, arg,
10665eca104eSNamhyung Kim 						is_output_full);
10670c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
10680c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
10695eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10705eca104eSNamhyung Kim 						total, parent_total, print, arg,
10715eca104eSNamhyung Kim 						is_output_full);
10720c841c6cSNamhyung Kim 	} else {
10730c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
10745eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
10755eca104eSNamhyung Kim 						total, parent_total, print, arg,
10765eca104eSNamhyung Kim 						is_output_full);
10770c841c6cSNamhyung Kim 	}
10780c841c6cSNamhyung Kim 
10790c841c6cSNamhyung Kim 	if (arg->is_current_entry)
10800c841c6cSNamhyung Kim 		browser->he_selection = entry;
10810c841c6cSNamhyung Kim 
10820c841c6cSNamhyung Kim 	return printed;
10830c841c6cSNamhyung Kim }
10840c841c6cSNamhyung Kim 
108589701460SNamhyung Kim struct hpp_arg {
108689701460SNamhyung Kim 	struct ui_browser *b;
108789701460SNamhyung Kim 	char folded_sign;
108889701460SNamhyung Kim 	bool current_entry;
108989701460SNamhyung Kim };
109089701460SNamhyung Kim 
10912f6d9009SNamhyung Kim static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
10922f6d9009SNamhyung Kim {
10932f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1094d675107cSNamhyung Kim 	int ret, len;
10952f6d9009SNamhyung Kim 	va_list args;
10962f6d9009SNamhyung Kim 	double percent;
10972f6d9009SNamhyung Kim 
10982f6d9009SNamhyung Kim 	va_start(args, fmt);
1099d675107cSNamhyung Kim 	len = va_arg(args, int);
11002f6d9009SNamhyung Kim 	percent = va_arg(args, double);
11012f6d9009SNamhyung Kim 	va_end(args);
11025aed9d24SNamhyung Kim 
110389701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
11045aed9d24SNamhyung Kim 
1105d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1106517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
110789701460SNamhyung Kim 
11082f6d9009SNamhyung Kim 	advance_hpp(hpp, ret);
11095aed9d24SNamhyung Kim 	return ret;
1110f5951d56SNamhyung Kim }
1111f5951d56SNamhyung Kim 
1112fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
11135aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
11145aed9d24SNamhyung Kim {									\
11155aed9d24SNamhyung Kim 	return he->stat._field;						\
11165aed9d24SNamhyung Kim }									\
11175aed9d24SNamhyung Kim 									\
11182c5d4b4aSJiri Olsa static int								\
11195b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11202c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
11215aed9d24SNamhyung Kim 				struct hist_entry *he)			\
11225aed9d24SNamhyung Kim {									\
11235b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
11242f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
11255aed9d24SNamhyung Kim }
1126f5951d56SNamhyung Kim 
11270434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
11280434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
11290434ddd2SNamhyung Kim {									\
11300434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
11310434ddd2SNamhyung Kim }									\
11320434ddd2SNamhyung Kim 									\
11330434ddd2SNamhyung Kim static int								\
11345b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11350434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
11360434ddd2SNamhyung Kim 				struct hist_entry *he)			\
11370434ddd2SNamhyung Kim {									\
11380434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1139517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
11405b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1141d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
11425b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1143517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
11440434ddd2SNamhyung Kim 									\
11450434ddd2SNamhyung Kim 		return ret;						\
11460434ddd2SNamhyung Kim 	}								\
11475b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
11485b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
11490434ddd2SNamhyung Kim }
11500434ddd2SNamhyung Kim 
1151fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1152fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1153fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1154fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1155fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
11560434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
11575aed9d24SNamhyung Kim 
11585aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
11590434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1160f5951d56SNamhyung Kim 
1161f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1162f5951d56SNamhyung Kim {
1163f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1164f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1165f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1166f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1167f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1168f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1169f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1170f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1171f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1172f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
11730434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
11740434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
1175f5951d56SNamhyung Kim }
1176f5951d56SNamhyung Kim 
117705e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1178aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1179aca7a94dSNamhyung Kim 				    unsigned short row)
1180aca7a94dSNamhyung Kim {
11811240005eSJiri Olsa 	int printed = 0;
118267d25916SNamhyung Kim 	int width = browser->b.width;
1183aca7a94dSNamhyung Kim 	char folded_sign = ' ';
118405e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1185aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
118663a1a3d8SNamhyung Kim 	bool first = true;
11871240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1188aca7a94dSNamhyung Kim 
1189aca7a94dSNamhyung Kim 	if (current_entry) {
119005e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
119105e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1192aca7a94dSNamhyung Kim 	}
1193aca7a94dSNamhyung Kim 
1194aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
1195aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1196aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1197aca7a94dSNamhyung Kim 	}
1198aca7a94dSNamhyung Kim 
1199aca7a94dSNamhyung Kim 	if (row_offset == 0) {
120089701460SNamhyung Kim 		struct hpp_arg arg = {
120189701460SNamhyung Kim 			.b		= &browser->b,
120289701460SNamhyung Kim 			.folded_sign	= folded_sign,
120389701460SNamhyung Kim 			.current_entry	= current_entry,
120489701460SNamhyung Kim 		};
1205c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1206f5951d56SNamhyung Kim 
1207ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
1208f5951d56SNamhyung Kim 
1209f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
121089fee709SArnaldo Carvalho de Melo 			char s[2048];
121189fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
121289fee709SArnaldo Carvalho de Melo 				.buf	= s,
121389fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
121489fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
121589fee709SArnaldo Carvalho de Melo 			};
121689fee709SArnaldo Carvalho de Melo 
1217361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1218361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1219e67d49a7SNamhyung Kim 				continue;
1220e67d49a7SNamhyung Kim 
1221fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1222fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1223fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1224fb821c9eSNamhyung Kim 			} else {
1225fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1226fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1227fb821c9eSNamhyung Kim 			}
1228fb821c9eSNamhyung Kim 
1229fb821c9eSNamhyung Kim 			if (first) {
1230fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
1231517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1232f5951d56SNamhyung Kim 					width -= 2;
1233f5951d56SNamhyung Kim 				}
123463a1a3d8SNamhyung Kim 				first = false;
1235fb821c9eSNamhyung Kim 			} else {
1236517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1237fb821c9eSNamhyung Kim 				width -= 2;
1238fb821c9eSNamhyung Kim 			}
1239f5951d56SNamhyung Kim 
12401240005eSJiri Olsa 			if (fmt->color) {
124189fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
124289fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
124389fee709SArnaldo Carvalho de Melo 				/*
124489fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
124589fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
124689fee709SArnaldo Carvalho de Melo 				 */
124789fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1248f5951d56SNamhyung Kim 			} else {
124989fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1250517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1251f5951d56SNamhyung Kim 			}
125289fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1253f5951d56SNamhyung Kim 		}
1254aca7a94dSNamhyung Kim 
1255aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
125605e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1257aca7a94dSNamhyung Kim 			width += 1;
1258aca7a94dSNamhyung Kim 
125926270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
126026d8b338SNamhyung Kim 
1261aca7a94dSNamhyung Kim 		++row;
1262aca7a94dSNamhyung Kim 		++printed;
1263aca7a94dSNamhyung Kim 	} else
1264aca7a94dSNamhyung Kim 		--row_offset;
1265aca7a94dSNamhyung Kim 
126662c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
126739ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
126839ee533fSNamhyung Kim 			.row_offset = row_offset,
126939ee533fSNamhyung Kim 			.is_current_entry = current_entry,
127039ee533fSNamhyung Kim 		};
1271c09a7e75SNamhyung Kim 
12720c841c6cSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry, 1, row,
12734b3a3212SNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
12744b3a3212SNamhyung Kim 					hist_browser__check_output_full);
1275aca7a94dSNamhyung Kim 	}
1276aca7a94dSNamhyung Kim 
1277aca7a94dSNamhyung Kim 	return printed;
1278aca7a94dSNamhyung Kim }
1279aca7a94dSNamhyung Kim 
1280d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1281d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1282d0506edbSNamhyung Kim 					      unsigned short row,
12832dbbe9f2SNamhyung Kim 					      int level)
1284d0506edbSNamhyung Kim {
1285d0506edbSNamhyung Kim 	int printed = 0;
1286d0506edbSNamhyung Kim 	int width = browser->b.width;
1287d0506edbSNamhyung Kim 	char folded_sign = ' ';
1288d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1289d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1290d0506edbSNamhyung Kim 	bool first = true;
1291d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1292a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1293d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1294d0506edbSNamhyung Kim 		.b		= &browser->b,
1295d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1296d0506edbSNamhyung Kim 	};
1297d0506edbSNamhyung Kim 	int column = 0;
12982dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1299d0506edbSNamhyung Kim 
1300d0506edbSNamhyung Kim 	if (current_entry) {
1301d0506edbSNamhyung Kim 		browser->he_selection = entry;
1302d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1303d0506edbSNamhyung Kim 	}
1304d0506edbSNamhyung Kim 
1305d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1306d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1307d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1308d0506edbSNamhyung Kim 
1309d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1310d0506edbSNamhyung Kim 		row_offset--;
1311d0506edbSNamhyung Kim 		goto show_callchain;
1312d0506edbSNamhyung Kim 	}
1313d0506edbSNamhyung Kim 
1314d0506edbSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
1315d0506edbSNamhyung Kim 
1316d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1317d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1318d0506edbSNamhyung Kim 	else
1319d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1320d0506edbSNamhyung Kim 
1321d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1322d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1323d0506edbSNamhyung Kim 
1324a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1325a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1326a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1327a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1328d0506edbSNamhyung Kim 		char s[2048];
1329d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1330d0506edbSNamhyung Kim 			.buf		= s,
1331d0506edbSNamhyung Kim 			.size		= sizeof(s),
1332d0506edbSNamhyung Kim 			.ptr		= &arg,
1333d0506edbSNamhyung Kim 		};
1334d0506edbSNamhyung Kim 
1335d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1336d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1337d0506edbSNamhyung Kim 			continue;
1338d0506edbSNamhyung Kim 
1339d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1340d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1341d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1342d0506edbSNamhyung Kim 		} else {
1343d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1344d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1345d0506edbSNamhyung Kim 		}
1346d0506edbSNamhyung Kim 
1347d0506edbSNamhyung Kim 		if (first) {
1348d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c", folded_sign);
1349d0506edbSNamhyung Kim 			width--;
1350d0506edbSNamhyung Kim 			first = false;
1351d0506edbSNamhyung Kim 		} else {
1352d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1353d0506edbSNamhyung Kim 			width -= 2;
1354d0506edbSNamhyung Kim 		}
1355d0506edbSNamhyung Kim 
1356d0506edbSNamhyung Kim 		if (fmt->color) {
1357d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1358d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1359d0506edbSNamhyung Kim 			/*
1360d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1361d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1362d0506edbSNamhyung Kim 			 */
1363d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1364d0506edbSNamhyung Kim 		} else {
1365d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1366d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1367d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1368d0506edbSNamhyung Kim 		}
1369d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1370d0506edbSNamhyung Kim 	}
1371d0506edbSNamhyung Kim 
1372d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1373d0506edbSNamhyung Kim 	width -= hierarchy_indent;
1374d0506edbSNamhyung Kim 
1375d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1376d0506edbSNamhyung Kim 		char s[2048];
1377d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1378d0506edbSNamhyung Kim 			.buf		= s,
1379d0506edbSNamhyung Kim 			.size		= sizeof(s),
1380d0506edbSNamhyung Kim 			.ptr		= &arg,
1381d0506edbSNamhyung Kim 		};
1382d0506edbSNamhyung Kim 
1383d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1384d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1385d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1386d0506edbSNamhyung Kim 		} else {
1387d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1388d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1389d0506edbSNamhyung Kim 		}
1390d0506edbSNamhyung Kim 
13911b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1392d0506edbSNamhyung Kim 			ui_browser__write_nstring(&browser->b, "", 2);
1393d0506edbSNamhyung Kim 			width -= 2;
1394d0506edbSNamhyung Kim 
1395d0506edbSNamhyung Kim 			/*
1396d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1397d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1398d0506edbSNamhyung Kim 			 * hierarchy mode.
1399d0506edbSNamhyung Kim 			 */
1400d0506edbSNamhyung Kim 			if (fmt->color) {
1401d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1402d0506edbSNamhyung Kim 			} else {
1403cb1fab91SNamhyung Kim 				int i = 0;
1404cb1fab91SNamhyung Kim 
1405d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
1406cb1fab91SNamhyung Kim 				ui_browser__printf(&browser->b, "%s", ltrim(s));
1407cb1fab91SNamhyung Kim 
1408cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1409cb1fab91SNamhyung Kim 					width++;
1410d0506edbSNamhyung Kim 			}
1411d0506edbSNamhyung Kim 		}
14121b2dbbf4SNamhyung Kim 	}
1413d0506edbSNamhyung Kim 
1414d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1415d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1416d0506edbSNamhyung Kim 		width += 1;
1417d0506edbSNamhyung Kim 
1418d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1419d0506edbSNamhyung Kim 
1420d0506edbSNamhyung Kim 	++row;
1421d0506edbSNamhyung Kim 	++printed;
1422d0506edbSNamhyung Kim 
1423d0506edbSNamhyung Kim show_callchain:
1424d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1425d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1426d0506edbSNamhyung Kim 			.row_offset = row_offset,
1427d0506edbSNamhyung Kim 		};
1428d0506edbSNamhyung Kim 
1429d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1430d0506edbSNamhyung Kim 					level + 1, row,
1431d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1432d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1433d0506edbSNamhyung Kim 	}
1434d0506edbSNamhyung Kim 
1435d0506edbSNamhyung Kim 	return printed;
1436d0506edbSNamhyung Kim }
1437d0506edbSNamhyung Kim 
143879dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
14392dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
144079dded87SNamhyung Kim {
144179dded87SNamhyung Kim 	int width = browser->b.width;
144279dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
144379dded87SNamhyung Kim 	bool first = true;
144479dded87SNamhyung Kim 	int column = 0;
144579dded87SNamhyung Kim 	int ret;
144679dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1447a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
14482dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
144979dded87SNamhyung Kim 
145079dded87SNamhyung Kim 	if (current_entry) {
145179dded87SNamhyung Kim 		browser->he_selection = NULL;
145279dded87SNamhyung Kim 		browser->selection = NULL;
145379dded87SNamhyung Kim 	}
145479dded87SNamhyung Kim 
145579dded87SNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
145679dded87SNamhyung Kim 
145779dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
145879dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
145979dded87SNamhyung Kim 	else
146079dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
146179dded87SNamhyung Kim 
146279dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
146379dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
146479dded87SNamhyung Kim 
1465a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1466a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1467a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1468a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
146979dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
147079dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
147179dded87SNamhyung Kim 			continue;
147279dded87SNamhyung Kim 
147379dded87SNamhyung Kim 		ret = fmt->width(fmt, NULL, hists_to_evsel(browser->hists));
147479dded87SNamhyung Kim 
147579dded87SNamhyung Kim 		if (first) {
147679dded87SNamhyung Kim 			/* for folded sign */
147779dded87SNamhyung Kim 			first = false;
147879dded87SNamhyung Kim 			ret++;
147979dded87SNamhyung Kim 		} else {
148079dded87SNamhyung Kim 			/* space between columns */
148179dded87SNamhyung Kim 			ret += 2;
148279dded87SNamhyung Kim 		}
148379dded87SNamhyung Kim 
148479dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
148579dded87SNamhyung Kim 		width -= ret;
148679dded87SNamhyung Kim 	}
148779dded87SNamhyung Kim 
14882dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
14892dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
149079dded87SNamhyung Kim 
149179dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
149279dded87SNamhyung Kim 		char buf[32];
149379dded87SNamhyung Kim 
149479dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
149579dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
149679dded87SNamhyung Kim 		width -= ret + 2;
149779dded87SNamhyung Kim 	}
149879dded87SNamhyung Kim 
149979dded87SNamhyung Kim 	/* The scroll bar isn't being used */
150079dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
150179dded87SNamhyung Kim 		width += 1;
150279dded87SNamhyung Kim 
150379dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
150479dded87SNamhyung Kim 	return 1;
150579dded87SNamhyung Kim }
150679dded87SNamhyung Kim 
150781a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
150881a888feSJiri Olsa {
150981a888feSJiri Olsa 	advance_hpp(hpp, inc);
151081a888feSJiri Olsa 	return hpp->size <= 0;
151181a888feSJiri Olsa }
151281a888feSJiri Olsa 
1513c6c3c02dSArnaldo Carvalho de Melo static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
151481a888feSJiri Olsa {
1515c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
151681a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
151781a888feSJiri Olsa 		.buf    = buf,
151881a888feSJiri Olsa 		.size   = size,
151981a888feSJiri Olsa 	};
152081a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
152181a888feSJiri Olsa 	size_t ret = 0;
1522c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
152381a888feSJiri Olsa 
152481a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
152581a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
152681a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
152781a888feSJiri Olsa 			return ret;
152881a888feSJiri Olsa 	}
152981a888feSJiri Olsa 
1530f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1531361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
153281a888feSJiri Olsa 			continue;
153381a888feSJiri Olsa 
153481a888feSJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
153581a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
153681a888feSJiri Olsa 			break;
153781a888feSJiri Olsa 
153881a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
153981a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
154081a888feSJiri Olsa 			break;
154181a888feSJiri Olsa 	}
154281a888feSJiri Olsa 
154381a888feSJiri Olsa 	return ret;
154481a888feSJiri Olsa }
154581a888feSJiri Olsa 
1546d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1547d8b92400SNamhyung Kim {
1548d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1549d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1550d8b92400SNamhyung Kim 		.buf    = buf,
1551d8b92400SNamhyung Kim 		.size   = size,
1552d8b92400SNamhyung Kim 	};
1553d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1554a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1555d8b92400SNamhyung Kim 	size_t ret = 0;
1556d8b92400SNamhyung Kim 	int column = 0;
15572dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1558a61a22f6SNamhyung Kim 	bool first_node, first_col;
1559d8b92400SNamhyung Kim 
1560d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, " ");
1561d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1562d8b92400SNamhyung Kim 		return ret;
1563d8b92400SNamhyung Kim 
1564a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1565a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1566a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1567a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1568d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1569d8b92400SNamhyung Kim 			continue;
1570d8b92400SNamhyung Kim 
1571d8b92400SNamhyung Kim 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1572d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1573d8b92400SNamhyung Kim 			break;
1574d8b92400SNamhyung Kim 
1575d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1576d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1577d8b92400SNamhyung Kim 			break;
1578d8b92400SNamhyung Kim 	}
1579d8b92400SNamhyung Kim 
1580d8b92400SNamhyung Kim 	ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
15812dbbe9f2SNamhyung Kim 			indent * HIERARCHY_INDENT, "");
1582d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1583d8b92400SNamhyung Kim 		return ret;
1584d8b92400SNamhyung Kim 
1585a61a22f6SNamhyung Kim 	first_node = true;
1586a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1587a61a22f6SNamhyung Kim 		if (!first_node) {
1588d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1589d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1590d8b92400SNamhyung Kim 				break;
1591d8b92400SNamhyung Kim 		}
1592a61a22f6SNamhyung Kim 		first_node = false;
1593a61a22f6SNamhyung Kim 
1594a61a22f6SNamhyung Kim 		first_col = true;
1595a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1596a61a22f6SNamhyung Kim 			char *start;
1597a61a22f6SNamhyung Kim 
1598a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1599a61a22f6SNamhyung Kim 				continue;
1600a61a22f6SNamhyung Kim 
1601a61a22f6SNamhyung Kim 			if (!first_col) {
1602a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1603a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1604a61a22f6SNamhyung Kim 					break;
1605a61a22f6SNamhyung Kim 			}
1606a61a22f6SNamhyung Kim 			first_col = false;
1607d8b92400SNamhyung Kim 
1608d8b92400SNamhyung Kim 			ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1609d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1610d8b92400SNamhyung Kim 
16117d6a7e78SJiri Olsa 			start = trim(dummy_hpp.buf);
1612cb1fab91SNamhyung Kim 			ret = strlen(start);
1613cb1fab91SNamhyung Kim 
1614cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1615cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1616cb1fab91SNamhyung Kim 
1617d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1618d8b92400SNamhyung Kim 				break;
1619d8b92400SNamhyung Kim 		}
1620a61a22f6SNamhyung Kim 	}
1621d8b92400SNamhyung Kim 
1622d8b92400SNamhyung Kim 	return ret;
1623d8b92400SNamhyung Kim }
1624d8b92400SNamhyung Kim 
1625025bf7eaSArnaldo Carvalho de Melo static void hist_browser__show_headers(struct hist_browser *browser)
1626025bf7eaSArnaldo Carvalho de Melo {
162781a888feSJiri Olsa 	char headers[1024];
162881a888feSJiri Olsa 
1629d8b92400SNamhyung Kim 	if (symbol_conf.report_hierarchy)
1630d8b92400SNamhyung Kim 		hists_browser__scnprintf_hierarchy_headers(browser, headers,
1631d8b92400SNamhyung Kim 							   sizeof(headers));
1632d8b92400SNamhyung Kim 	else
1633d8b92400SNamhyung Kim 		hists_browser__scnprintf_headers(browser, headers,
1634d8b92400SNamhyung Kim 						 sizeof(headers));
1635025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1636025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
163726270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1638025bf7eaSArnaldo Carvalho de Melo }
1639025bf7eaSArnaldo Carvalho de Melo 
1640aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1641aca7a94dSNamhyung Kim {
1642aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1643aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1644aca7a94dSNamhyung Kim 
1645aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
1646aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
1647aca7a94dSNamhyung Kim 	}
1648aca7a94dSNamhyung Kim }
1649aca7a94dSNamhyung Kim 
165005e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1651aca7a94dSNamhyung Kim {
1652aca7a94dSNamhyung Kim 	unsigned row = 0;
1653025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
1654aca7a94dSNamhyung Kim 	struct rb_node *nd;
165505e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1656aca7a94dSNamhyung Kim 
1657025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
1658025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1659025bf7eaSArnaldo Carvalho de Melo 		header_offset = 1;
1660025bf7eaSArnaldo Carvalho de Melo 	}
1661025bf7eaSArnaldo Carvalho de Melo 
166205e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1663979d2cacSWang Nan 	hb->he_selection = NULL;
1664979d2cacSWang Nan 	hb->selection = NULL;
1665aca7a94dSNamhyung Kim 
1666d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1667aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
166814135663SNamhyung Kim 		float percent;
1669aca7a94dSNamhyung Kim 
1670d0506edbSNamhyung Kim 		if (h->filtered) {
1671d0506edbSNamhyung Kim 			/* let it move to sibling */
1672d0506edbSNamhyung Kim 			h->unfolded = false;
1673aca7a94dSNamhyung Kim 			continue;
1674d0506edbSNamhyung Kim 		}
1675aca7a94dSNamhyung Kim 
167614135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
1677064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1678064f1981SNamhyung Kim 			continue;
1679064f1981SNamhyung Kim 
1680d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1681d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
16822dbbe9f2SNamhyung Kim 								  h->depth);
168379dded87SNamhyung Kim 			if (row == browser->rows)
168479dded87SNamhyung Kim 				break;
168579dded87SNamhyung Kim 
168679dded87SNamhyung Kim 			if (h->has_no_entry) {
1687a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
168879dded87SNamhyung Kim 				row++;
168979dded87SNamhyung Kim 			}
1690d0506edbSNamhyung Kim 		} else {
1691aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1692d0506edbSNamhyung Kim 		}
1693d0506edbSNamhyung Kim 
169462c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1695aca7a94dSNamhyung Kim 			break;
1696aca7a94dSNamhyung Kim 	}
1697aca7a94dSNamhyung Kim 
1698025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
1699aca7a94dSNamhyung Kim }
1700aca7a94dSNamhyung Kim 
1701064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1702064f1981SNamhyung Kim 					     float min_pcnt)
1703aca7a94dSNamhyung Kim {
1704aca7a94dSNamhyung Kim 	while (nd != NULL) {
1705aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
170614135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1707064f1981SNamhyung Kim 
1708c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1709aca7a94dSNamhyung Kim 			return nd;
1710aca7a94dSNamhyung Kim 
1711d0506edbSNamhyung Kim 		/*
1712d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1713d0506edbSNamhyung Kim 		 * So move to sibling node.
1714d0506edbSNamhyung Kim 		 */
1715d0506edbSNamhyung Kim 		if (rb_next(nd))
1716aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1717d0506edbSNamhyung Kim 		else
1718d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1719aca7a94dSNamhyung Kim 	}
1720aca7a94dSNamhyung Kim 
1721aca7a94dSNamhyung Kim 	return NULL;
1722aca7a94dSNamhyung Kim }
1723aca7a94dSNamhyung Kim 
1724064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1725064f1981SNamhyung Kim 						  float min_pcnt)
1726aca7a94dSNamhyung Kim {
1727aca7a94dSNamhyung Kim 	while (nd != NULL) {
1728aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
172914135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1730064f1981SNamhyung Kim 
1731064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1732aca7a94dSNamhyung Kim 			return nd;
1733aca7a94dSNamhyung Kim 
1734d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1735aca7a94dSNamhyung Kim 	}
1736aca7a94dSNamhyung Kim 
1737aca7a94dSNamhyung Kim 	return NULL;
1738aca7a94dSNamhyung Kim }
1739aca7a94dSNamhyung Kim 
174005e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1741aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1742aca7a94dSNamhyung Kim {
1743aca7a94dSNamhyung Kim 	struct hist_entry *h;
1744aca7a94dSNamhyung Kim 	struct rb_node *nd;
1745aca7a94dSNamhyung Kim 	bool first = true;
1746064f1981SNamhyung Kim 	struct hist_browser *hb;
1747064f1981SNamhyung Kim 
1748064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1749aca7a94dSNamhyung Kim 
175005e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1751aca7a94dSNamhyung Kim 		return;
1752aca7a94dSNamhyung Kim 
175305e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1754aca7a94dSNamhyung Kim 
1755aca7a94dSNamhyung Kim 	switch (whence) {
1756aca7a94dSNamhyung Kim 	case SEEK_SET:
1757064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
175814135663SNamhyung Kim 					   hb->min_pcnt);
1759aca7a94dSNamhyung Kim 		break;
1760aca7a94dSNamhyung Kim 	case SEEK_CUR:
176105e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1762aca7a94dSNamhyung Kim 		goto do_offset;
1763aca7a94dSNamhyung Kim 	case SEEK_END:
1764d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1765d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1766aca7a94dSNamhyung Kim 		first = false;
1767aca7a94dSNamhyung Kim 		break;
1768aca7a94dSNamhyung Kim 	default:
1769aca7a94dSNamhyung Kim 		return;
1770aca7a94dSNamhyung Kim 	}
1771aca7a94dSNamhyung Kim 
1772aca7a94dSNamhyung Kim 	/*
1773aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1774aca7a94dSNamhyung Kim 	 * row_offset:
1775aca7a94dSNamhyung Kim 	 */
177605e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1777aca7a94dSNamhyung Kim 	h->row_offset = 0;
1778aca7a94dSNamhyung Kim 
1779aca7a94dSNamhyung Kim 	/*
1780aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1781aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1782aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1783aca7a94dSNamhyung Kim 	 *
1784aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1785aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1786aca7a94dSNamhyung Kim 	 *
1787aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1788aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1789aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1790aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1791aca7a94dSNamhyung Kim 	 */
1792aca7a94dSNamhyung Kim do_offset:
1793837eeb75SWang Nan 	if (!nd)
1794837eeb75SWang Nan 		return;
1795837eeb75SWang Nan 
1796aca7a94dSNamhyung Kim 	if (offset > 0) {
1797aca7a94dSNamhyung Kim 		do {
1798aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1799d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1800aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1801aca7a94dSNamhyung Kim 				if (offset > remaining) {
1802aca7a94dSNamhyung Kim 					offset -= remaining;
1803aca7a94dSNamhyung Kim 					h->row_offset = 0;
1804aca7a94dSNamhyung Kim 				} else {
1805aca7a94dSNamhyung Kim 					h->row_offset += offset;
1806aca7a94dSNamhyung Kim 					offset = 0;
180705e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1808aca7a94dSNamhyung Kim 					break;
1809aca7a94dSNamhyung Kim 				}
1810aca7a94dSNamhyung Kim 			}
1811d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1812d0506edbSNamhyung Kim 						   hb->min_pcnt);
1813aca7a94dSNamhyung Kim 			if (nd == NULL)
1814aca7a94dSNamhyung Kim 				break;
1815aca7a94dSNamhyung Kim 			--offset;
181605e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1817aca7a94dSNamhyung Kim 		} while (offset != 0);
1818aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1819aca7a94dSNamhyung Kim 		while (1) {
1820aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1821d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1822aca7a94dSNamhyung Kim 				if (first) {
1823aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1824aca7a94dSNamhyung Kim 						offset += h->row_offset;
1825aca7a94dSNamhyung Kim 						h->row_offset = 0;
1826aca7a94dSNamhyung Kim 					} else {
1827aca7a94dSNamhyung Kim 						h->row_offset += offset;
1828aca7a94dSNamhyung Kim 						offset = 0;
182905e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1830aca7a94dSNamhyung Kim 						break;
1831aca7a94dSNamhyung Kim 					}
1832aca7a94dSNamhyung Kim 				} else {
1833aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1834aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1835aca7a94dSNamhyung Kim 						h->row_offset = 0;
1836aca7a94dSNamhyung Kim 					} else {
1837aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1838aca7a94dSNamhyung Kim 						offset = 0;
183905e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1840aca7a94dSNamhyung Kim 						break;
1841aca7a94dSNamhyung Kim 					}
1842aca7a94dSNamhyung Kim 				}
1843aca7a94dSNamhyung Kim 			}
1844aca7a94dSNamhyung Kim 
1845d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1846064f1981SNamhyung Kim 							hb->min_pcnt);
1847aca7a94dSNamhyung Kim 			if (nd == NULL)
1848aca7a94dSNamhyung Kim 				break;
1849aca7a94dSNamhyung Kim 			++offset;
185005e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1851aca7a94dSNamhyung Kim 			if (offset == 0) {
1852aca7a94dSNamhyung Kim 				/*
1853aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1854aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1855aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1856aca7a94dSNamhyung Kim 				 */
1857aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
1858d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
1859aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1860aca7a94dSNamhyung Kim 				break;
1861aca7a94dSNamhyung Kim 			}
1862aca7a94dSNamhyung Kim 			first = false;
1863aca7a94dSNamhyung Kim 		}
1864aca7a94dSNamhyung Kim 	} else {
186505e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1866aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1867aca7a94dSNamhyung Kim 		h->row_offset = 0;
1868aca7a94dSNamhyung Kim 	}
1869aca7a94dSNamhyung Kim }
1870aca7a94dSNamhyung Kim 
1871aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1872d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
1873d0506edbSNamhyung Kim 					   int level)
1874aff3f3f6SArnaldo Carvalho de Melo {
187539ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
187639ee533fSNamhyung Kim 		.fp = fp,
187739ee533fSNamhyung Kim 	};
1878aff3f3f6SArnaldo Carvalho de Melo 
1879d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
188039ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
188139ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
188239ee533fSNamhyung Kim 	return arg.printed;
1883aff3f3f6SArnaldo Carvalho de Melo }
1884aff3f3f6SArnaldo Carvalho de Melo 
1885aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1886aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1887aff3f3f6SArnaldo Carvalho de Melo {
1888aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1889aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1890aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
189126d8b338SNamhyung Kim 	struct perf_hpp hpp = {
189226d8b338SNamhyung Kim 		.buf = s,
189326d8b338SNamhyung Kim 		.size = sizeof(s),
189426d8b338SNamhyung Kim 	};
189526d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
189626d8b338SNamhyung Kim 	bool first = true;
189726d8b338SNamhyung Kim 	int ret;
1898aff3f3f6SArnaldo Carvalho de Melo 
18991b6b678eSArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain) {
1900aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1901aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
19021b6b678eSArnaldo Carvalho de Melo 	}
1903aff3f3f6SArnaldo Carvalho de Melo 
1904f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1905361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
1906e67d49a7SNamhyung Kim 			continue;
1907e67d49a7SNamhyung Kim 
190826d8b338SNamhyung Kim 		if (!first) {
190926d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
191026d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
191126d8b338SNamhyung Kim 		} else
191226d8b338SNamhyung Kim 			first = false;
1913aff3f3f6SArnaldo Carvalho de Melo 
191426d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
191589fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
191626d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
191726d8b338SNamhyung Kim 	}
191889fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
1919aff3f3f6SArnaldo Carvalho de Melo 
1920aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1921d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1922d0506edbSNamhyung Kim 
1923d0506edbSNamhyung Kim 	return printed;
1924d0506edbSNamhyung Kim }
1925d0506edbSNamhyung Kim 
1926d0506edbSNamhyung Kim 
1927d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1928d0506edbSNamhyung Kim 						 struct hist_entry *he,
1929325a6283SNamhyung Kim 						 FILE *fp, int level)
1930d0506edbSNamhyung Kim {
1931d0506edbSNamhyung Kim 	char s[8192];
1932d0506edbSNamhyung Kim 	int printed = 0;
1933d0506edbSNamhyung Kim 	char folded_sign = ' ';
1934d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
1935d0506edbSNamhyung Kim 		.buf = s,
1936d0506edbSNamhyung Kim 		.size = sizeof(s),
1937d0506edbSNamhyung Kim 	};
1938d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1939325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1940d0506edbSNamhyung Kim 	bool first = true;
1941d0506edbSNamhyung Kim 	int ret;
1942325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1943d0506edbSNamhyung Kim 
1944d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1945d0506edbSNamhyung Kim 
1946d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
1947d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
1948d0506edbSNamhyung Kim 
1949325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1950325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
1951325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
1952325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1953d0506edbSNamhyung Kim 		if (!first) {
1954d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1955d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
1956d0506edbSNamhyung Kim 		} else
1957d0506edbSNamhyung Kim 			first = false;
1958d0506edbSNamhyung Kim 
1959d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
1960d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
1961d0506edbSNamhyung Kim 	}
1962d0506edbSNamhyung Kim 
1963d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1964d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
1965d0506edbSNamhyung Kim 
19661b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
19671b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
19681b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
19691b2dbbf4SNamhyung Kim 
1970d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
1971d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
19721b2dbbf4SNamhyung Kim 	}
1973d0506edbSNamhyung Kim 
1974d0506edbSNamhyung Kim 	printed += fprintf(fp, "%s\n", rtrim(s));
1975d0506edbSNamhyung Kim 
1976d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
1977d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
1978d0506edbSNamhyung Kim 							   he->depth + 1);
1979d0506edbSNamhyung Kim 	}
1980aff3f3f6SArnaldo Carvalho de Melo 
1981aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1982aff3f3f6SArnaldo Carvalho de Melo }
1983aff3f3f6SArnaldo Carvalho de Melo 
1984aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1985aff3f3f6SArnaldo Carvalho de Melo {
1986064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1987064f1981SNamhyung Kim 						   browser->min_pcnt);
1988aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1989aff3f3f6SArnaldo Carvalho de Melo 
1990aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1991aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1992aff3f3f6SArnaldo Carvalho de Melo 
1993d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1994d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
1995d0506edbSNamhyung Kim 									 h, fp,
1996325a6283SNamhyung Kim 									 h->depth);
1997d0506edbSNamhyung Kim 		} else {
1998aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
1999d0506edbSNamhyung Kim 		}
2000d0506edbSNamhyung Kim 
2001d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2002d0506edbSNamhyung Kim 					   browser->min_pcnt);
2003aff3f3f6SArnaldo Carvalho de Melo 	}
2004aff3f3f6SArnaldo Carvalho de Melo 
2005aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2006aff3f3f6SArnaldo Carvalho de Melo }
2007aff3f3f6SArnaldo Carvalho de Melo 
2008aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2009aff3f3f6SArnaldo Carvalho de Melo {
2010aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2011aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2012aff3f3f6SArnaldo Carvalho de Melo 
2013aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2014aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2015aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2016aff3f3f6SArnaldo Carvalho de Melo 			break;
2017aff3f3f6SArnaldo Carvalho de Melo 		/*
2018aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2019aff3f3f6SArnaldo Carvalho de Melo  		 */
2020aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2021aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2022aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2023aff3f3f6SArnaldo Carvalho de Melo 		}
2024aff3f3f6SArnaldo Carvalho de Melo 	}
2025aff3f3f6SArnaldo Carvalho de Melo 
2026aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2027aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2028aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
20294cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
20304cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2031aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2032aff3f3f6SArnaldo Carvalho de Melo 	}
2033aff3f3f6SArnaldo Carvalho de Melo 
2034aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2035aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2036aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2037aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2038aff3f3f6SArnaldo Carvalho de Melo 
2039aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2040aff3f3f6SArnaldo Carvalho de Melo }
2041aff3f3f6SArnaldo Carvalho de Melo 
2042c2a51ab8SNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists,
2043b1a9ceefSNamhyung Kim 					      struct hist_browser_timer *hbt,
2044ce80d3beSKan Liang 					      struct perf_env *env)
2045aca7a94dSNamhyung Kim {
204605e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
2047aca7a94dSNamhyung Kim 
204805e8b080SArnaldo Carvalho de Melo 	if (browser) {
204905e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
205005e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
2051357cfff1SArnaldo Carvalho de Melo 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
205205e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
205305e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
2054c8302367SJiri Olsa 		browser->show_headers = symbol_conf.show_hist_headers;
2055c2a51ab8SNamhyung Kim 		browser->hbt = hbt;
2056b1a9ceefSNamhyung Kim 		browser->env = env;
2057aca7a94dSNamhyung Kim 	}
2058aca7a94dSNamhyung Kim 
205905e8b080SArnaldo Carvalho de Melo 	return browser;
2060aca7a94dSNamhyung Kim }
2061aca7a94dSNamhyung Kim 
206205e8b080SArnaldo Carvalho de Melo static void hist_browser__delete(struct hist_browser *browser)
2063aca7a94dSNamhyung Kim {
206405e8b080SArnaldo Carvalho de Melo 	free(browser);
2065aca7a94dSNamhyung Kim }
2066aca7a94dSNamhyung Kim 
206705e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2068aca7a94dSNamhyung Kim {
206905e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2070aca7a94dSNamhyung Kim }
2071aca7a94dSNamhyung Kim 
207205e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2073aca7a94dSNamhyung Kim {
207405e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2075aca7a94dSNamhyung Kim }
2076aca7a94dSNamhyung Kim 
20771e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
20781e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
20791e378ebdSTaeung Song {
20801e378ebdSTaeung Song 	return timer == NULL;
20811e378ebdSTaeung Song }
20821e378ebdSTaeung Song 
20831e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
20841e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
20851e378ebdSTaeung Song 				char *bf, size_t size)
2086aca7a94dSNamhyung Kim {
2087aca7a94dSNamhyung Kim 	char unit;
2088aca7a94dSNamhyung Kim 	int printed;
208905e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
209005e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
209184734b06SKan Liang 	int socket_id = hists->socket_filter;
209205e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
209305e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
2094717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
2095dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
2096717e263fSNamhyung Kim 	char buf[512];
2097717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
20989e207ddfSKan Liang 	char ref[30] = " show reference callgraph, ";
20999e207ddfSKan Liang 	bool enable_ref = false;
2100717e263fSNamhyung Kim 
2101f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
2102f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
2103f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
2104f2148330SNamhyung Kim 	}
2105f2148330SNamhyung Kim 
2106759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
2107717e263fSNamhyung Kim 		struct perf_evsel *pos;
2108717e263fSNamhyung Kim 
2109717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
2110717e263fSNamhyung Kim 		ev_name = buf;
2111717e263fSNamhyung Kim 
2112717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
21134ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
21144ea062edSArnaldo Carvalho de Melo 
2115f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
21164ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
21174ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
2118f2148330SNamhyung Kim 			} else {
21194ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
21204ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
2121717e263fSNamhyung Kim 			}
2122717e263fSNamhyung Kim 		}
2123f2148330SNamhyung Kim 	}
2124aca7a94dSNamhyung Kim 
21259e207ddfSKan Liang 	if (symbol_conf.show_ref_callgraph &&
21269e207ddfSKan Liang 	    strstr(ev_name, "call-graph=no"))
21279e207ddfSKan Liang 		enable_ref = true;
2128aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
2129aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
21309e207ddfSKan Liang 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
21319e207ddfSKan Liang 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2132aca7a94dSNamhyung Kim 
2133aca7a94dSNamhyung Kim 
213405e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
2135aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
213605e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
21376962ccb3SNamhyung Kim 	if (thread) {
2138fa82911aSJiri Olsa 		if (hists__has(hists, thread)) {
2139aca7a94dSNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
2140aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
2141b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
214238051234SAdrian Hunter 				    thread->tid);
21436962ccb3SNamhyung Kim 		} else {
21446962ccb3SNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
21456962ccb3SNamhyung Kim 				    ", Thread: %s",
21466962ccb3SNamhyung Kim 				     (thread->comm_set ? thread__comm_str(thread) : ""));
21476962ccb3SNamhyung Kim 		}
21486962ccb3SNamhyung Kim 	}
2149aca7a94dSNamhyung Kim 	if (dso)
2150aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
2151aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
215284734b06SKan Liang 	if (socket_id > -1)
215321394d94SKan Liang 		printed += scnprintf(bf + printed, size - printed,
215484734b06SKan Liang 				    ", Processor Socket: %d", socket_id);
21551e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
21561e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
21571e378ebdSTaeung Song 
21581e378ebdSTaeung Song 		if (top->zero)
21591e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
21601e378ebdSTaeung Song 	}
21611e378ebdSTaeung Song 
2162aca7a94dSNamhyung Kim 	return printed;
2163aca7a94dSNamhyung Kim }
2164aca7a94dSNamhyung Kim 
2165aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2166aca7a94dSNamhyung Kim {
2167aca7a94dSNamhyung Kim 	int i;
2168aca7a94dSNamhyung Kim 
216904662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
217004662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2171aca7a94dSNamhyung Kim }
2172aca7a94dSNamhyung Kim 
2173341487abSFeng Tang /*
2174341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2175341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2176341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2177341487abSFeng Tang  */
2178341487abSFeng Tang static bool is_input_name_malloced = false;
2179341487abSFeng Tang 
2180341487abSFeng Tang static int switch_data_file(void)
2181341487abSFeng Tang {
2182341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2183341487abSFeng Tang 	DIR *pwd_dir;
2184341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2185341487abSFeng Tang 	struct dirent *dent;
2186341487abSFeng Tang 
2187341487abSFeng Tang 	pwd = getenv("PWD");
2188341487abSFeng Tang 	if (!pwd)
2189341487abSFeng Tang 		return ret;
2190341487abSFeng Tang 
2191341487abSFeng Tang 	pwd_dir = opendir(pwd);
2192341487abSFeng Tang 	if (!pwd_dir)
2193341487abSFeng Tang 		return ret;
2194341487abSFeng Tang 
2195341487abSFeng Tang 	memset(options, 0, sizeof(options));
2196341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
2197341487abSFeng Tang 
2198341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2199341487abSFeng Tang 		char path[PATH_MAX];
2200341487abSFeng Tang 		u64 magic;
2201341487abSFeng Tang 		char *name = dent->d_name;
2202341487abSFeng Tang 		FILE *file;
2203341487abSFeng Tang 
2204341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2205341487abSFeng Tang 			continue;
2206341487abSFeng Tang 
2207341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2208341487abSFeng Tang 
2209341487abSFeng Tang 		file = fopen(path, "r");
2210341487abSFeng Tang 		if (!file)
2211341487abSFeng Tang 			continue;
2212341487abSFeng Tang 
2213341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2214341487abSFeng Tang 			goto close_file_and_continue;
2215341487abSFeng Tang 
2216341487abSFeng Tang 		if (is_perf_magic(magic)) {
2217341487abSFeng Tang 			options[nr_options] = strdup(name);
2218341487abSFeng Tang 			if (!options[nr_options])
2219341487abSFeng Tang 				goto close_file_and_continue;
2220341487abSFeng Tang 
2221341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2222341487abSFeng Tang 			if (!abs_path[nr_options]) {
222374cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2224341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2225341487abSFeng Tang 				fclose(file);
2226341487abSFeng Tang 				break;
2227341487abSFeng Tang 			}
2228341487abSFeng Tang 
2229341487abSFeng Tang 			nr_options++;
2230341487abSFeng Tang 		}
2231341487abSFeng Tang 
2232341487abSFeng Tang close_file_and_continue:
2233341487abSFeng Tang 		fclose(file);
2234341487abSFeng Tang 		if (nr_options >= 32) {
2235341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2236341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2237341487abSFeng Tang 			break;
2238341487abSFeng Tang 		}
2239341487abSFeng Tang 	}
2240341487abSFeng Tang 	closedir(pwd_dir);
2241341487abSFeng Tang 
2242341487abSFeng Tang 	if (nr_options) {
2243341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
2244341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2245341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2246341487abSFeng Tang 			if (tmp) {
2247341487abSFeng Tang 				if (is_input_name_malloced)
2248341487abSFeng Tang 					free((void *)input_name);
2249341487abSFeng Tang 				input_name = tmp;
2250341487abSFeng Tang 				is_input_name_malloced = true;
2251341487abSFeng Tang 				ret = 0;
2252341487abSFeng Tang 			} else
2253341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2254341487abSFeng Tang 		}
2255341487abSFeng Tang 	}
2256341487abSFeng Tang 
2257341487abSFeng Tang 	free_popup_options(options, nr_options);
2258341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2259341487abSFeng Tang 	return ret;
2260341487abSFeng Tang }
2261341487abSFeng Tang 
2262ea7cd592SNamhyung Kim struct popup_action {
2263ea7cd592SNamhyung Kim 	struct thread 		*thread;
2264ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
226584734b06SKan Liang 	int			socket;
2266ea7cd592SNamhyung Kim 
2267ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2268ea7cd592SNamhyung Kim };
2269ea7cd592SNamhyung Kim 
2270bc7cad42SNamhyung Kim static int
2271ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2272bc7cad42SNamhyung Kim {
2273bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
2274bc7cad42SNamhyung Kim 	struct annotation *notes;
2275bc7cad42SNamhyung Kim 	struct hist_entry *he;
2276bc7cad42SNamhyung Kim 	int err;
2277bc7cad42SNamhyung Kim 
2278eebd0bfcSArnaldo Carvalho de Melo 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
2279bc7cad42SNamhyung Kim 		return 0;
2280bc7cad42SNamhyung Kim 
2281ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2282bc7cad42SNamhyung Kim 	if (!notes->src)
2283bc7cad42SNamhyung Kim 		return 0;
2284bc7cad42SNamhyung Kim 
2285bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
2286ea7cd592SNamhyung Kim 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2287bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2288bc7cad42SNamhyung Kim 	/*
2289bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2290bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2291bc7cad42SNamhyung Kim 	 */
2292bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2293bc7cad42SNamhyung Kim 		return 1;
2294bc7cad42SNamhyung Kim 
2295bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2296bc7cad42SNamhyung Kim 	if (err)
2297bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2298bc7cad42SNamhyung Kim 	return 0;
2299bc7cad42SNamhyung Kim }
2300bc7cad42SNamhyung Kim 
2301bc7cad42SNamhyung Kim static int
2302ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2303ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
2304ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
2305bc7cad42SNamhyung Kim {
2306ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
2307ea7cd592SNamhyung Kim 		return 0;
2308ea7cd592SNamhyung Kim 
2309ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2310ea7cd592SNamhyung Kim 		return 0;
2311ea7cd592SNamhyung Kim 
2312ea7cd592SNamhyung Kim 	act->ms.map = map;
2313ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2314ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2315ea7cd592SNamhyung Kim 	return 1;
2316ea7cd592SNamhyung Kim }
2317ea7cd592SNamhyung Kim 
2318ea7cd592SNamhyung Kim static int
2319ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2320ea7cd592SNamhyung Kim {
2321ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2322ea7cd592SNamhyung Kim 
2323*7cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
2324*7cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2325599a2f38SNamhyung Kim 		return 0;
2326599a2f38SNamhyung Kim 
2327bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2328bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2329bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2330bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2331bc7cad42SNamhyung Kim 		ui_helpline__pop();
2332bc7cad42SNamhyung Kim 	} else {
2333fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
23347727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2335bc7cad42SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "",
2336bc7cad42SNamhyung Kim 					   thread->tid);
23376962ccb3SNamhyung Kim 		} else {
23386962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
23396962ccb3SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "");
23406962ccb3SNamhyung Kim 		}
23416962ccb3SNamhyung Kim 
2342bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2343bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2344bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2345bc7cad42SNamhyung Kim 	}
2346bc7cad42SNamhyung Kim 
2347bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2348bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2349bc7cad42SNamhyung Kim 	return 0;
2350bc7cad42SNamhyung Kim }
2351bc7cad42SNamhyung Kim 
2352bc7cad42SNamhyung Kim static int
2353ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2354ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2355bc7cad42SNamhyung Kim {
23566962ccb3SNamhyung Kim 	int ret;
23576962ccb3SNamhyung Kim 
2358*7cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
2359*7cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2360ea7cd592SNamhyung Kim 		return 0;
2361ea7cd592SNamhyung Kim 
2362fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
23636962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2364ea7cd592SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
2365ea7cd592SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "",
23666962ccb3SNamhyung Kim 			       thread->tid);
23676962ccb3SNamhyung Kim 	} else {
23686962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s thread",
23696962ccb3SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
23706962ccb3SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "");
23716962ccb3SNamhyung Kim 	}
23726962ccb3SNamhyung Kim 	if (ret < 0)
2373ea7cd592SNamhyung Kim 		return 0;
2374ea7cd592SNamhyung Kim 
2375ea7cd592SNamhyung Kim 	act->thread = thread;
2376ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2377ea7cd592SNamhyung Kim 	return 1;
2378ea7cd592SNamhyung Kim }
2379ea7cd592SNamhyung Kim 
2380ea7cd592SNamhyung Kim static int
2381ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2382ea7cd592SNamhyung Kim {
2383045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
2384ea7cd592SNamhyung Kim 
238569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2386599a2f38SNamhyung Kim 		return 0;
2387599a2f38SNamhyung Kim 
2388bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2389bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2390bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2391bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2392bc7cad42SNamhyung Kim 		ui_helpline__pop();
2393bc7cad42SNamhyung Kim 	} else {
2394045b80ddSArnaldo Carvalho de Melo 		if (map == NULL)
2395bc7cad42SNamhyung Kim 			return 0;
23967727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2397045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2398045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
2399bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2400bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2401bc7cad42SNamhyung Kim 	}
2402bc7cad42SNamhyung Kim 
2403bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2404bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2405bc7cad42SNamhyung Kim 	return 0;
2406bc7cad42SNamhyung Kim }
2407bc7cad42SNamhyung Kim 
2408bc7cad42SNamhyung Kim static int
2409ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2410045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2411bc7cad42SNamhyung Kim {
241269849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2413ea7cd592SNamhyung Kim 		return 0;
2414ea7cd592SNamhyung Kim 
2415ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
2416ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
2417045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2418ea7cd592SNamhyung Kim 		return 0;
2419ea7cd592SNamhyung Kim 
2420045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2421ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2422ea7cd592SNamhyung Kim 	return 1;
2423ea7cd592SNamhyung Kim }
2424ea7cd592SNamhyung Kim 
2425ea7cd592SNamhyung Kim static int
2426ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2427ea7cd592SNamhyung Kim 	      struct popup_action *act)
2428ea7cd592SNamhyung Kim {
2429ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2430bc7cad42SNamhyung Kim 	return 0;
2431bc7cad42SNamhyung Kim }
2432bc7cad42SNamhyung Kim 
2433bc7cad42SNamhyung Kim static int
243469849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2435ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2436ea7cd592SNamhyung Kim {
243769849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2438ea7cd592SNamhyung Kim 		return 0;
2439ea7cd592SNamhyung Kim 
2440ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2441ea7cd592SNamhyung Kim 		return 0;
2442ea7cd592SNamhyung Kim 
2443ea7cd592SNamhyung Kim 	act->ms.map = map;
2444ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2445ea7cd592SNamhyung Kim 	return 1;
2446ea7cd592SNamhyung Kim }
2447ea7cd592SNamhyung Kim 
2448ea7cd592SNamhyung Kim static int
2449bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2450ea7cd592SNamhyung Kim 	      struct popup_action *act)
2451bc7cad42SNamhyung Kim {
2452bc7cad42SNamhyung Kim 	char script_opt[64];
2453bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
2454bc7cad42SNamhyung Kim 
2455ea7cd592SNamhyung Kim 	if (act->thread) {
2456bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2457ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2458ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
2459bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2460ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2461bc7cad42SNamhyung Kim 	}
2462bc7cad42SNamhyung Kim 
2463bc7cad42SNamhyung Kim 	script_browse(script_opt);
2464bc7cad42SNamhyung Kim 	return 0;
2465bc7cad42SNamhyung Kim }
2466bc7cad42SNamhyung Kim 
2467bc7cad42SNamhyung Kim static int
2468ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
2469ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
2470ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
2471ea7cd592SNamhyung Kim {
2472ea7cd592SNamhyung Kim 	if (thread) {
2473ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2474ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
2475ea7cd592SNamhyung Kim 			return 0;
2476ea7cd592SNamhyung Kim 	} else if (sym) {
2477ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2478ea7cd592SNamhyung Kim 			     sym->name) < 0)
2479ea7cd592SNamhyung Kim 			return 0;
2480ea7cd592SNamhyung Kim 	} else {
2481ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2482ea7cd592SNamhyung Kim 			return 0;
2483ea7cd592SNamhyung Kim 	}
2484ea7cd592SNamhyung Kim 
2485ea7cd592SNamhyung Kim 	act->thread = thread;
2486ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2487ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2488ea7cd592SNamhyung Kim 	return 1;
2489ea7cd592SNamhyung Kim }
2490ea7cd592SNamhyung Kim 
2491ea7cd592SNamhyung Kim static int
2492ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2493ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2494bc7cad42SNamhyung Kim {
2495bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2496bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2497bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2498ea7cd592SNamhyung Kim 		return 0;
2499bc7cad42SNamhyung Kim 	}
2500bc7cad42SNamhyung Kim 
2501bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2502bc7cad42SNamhyung Kim }
2503bc7cad42SNamhyung Kim 
2504ea7cd592SNamhyung Kim static int
2505ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2506ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2507ea7cd592SNamhyung Kim {
2508ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2509ea7cd592SNamhyung Kim 		return 0;
2510ea7cd592SNamhyung Kim 
2511ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2512ea7cd592SNamhyung Kim 		return 0;
2513ea7cd592SNamhyung Kim 
2514ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2515ea7cd592SNamhyung Kim 	return 1;
2516ea7cd592SNamhyung Kim }
2517ea7cd592SNamhyung Kim 
2518ea7cd592SNamhyung Kim static int
2519ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2520ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2521ea7cd592SNamhyung Kim {
2522ea7cd592SNamhyung Kim 	return 0;
2523ea7cd592SNamhyung Kim }
2524ea7cd592SNamhyung Kim 
2525ea7cd592SNamhyung Kim static int
2526ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2527ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2528ea7cd592SNamhyung Kim {
2529ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2530ea7cd592SNamhyung Kim 		return 0;
2531ea7cd592SNamhyung Kim 
2532ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2533ea7cd592SNamhyung Kim 	return 1;
2534ea7cd592SNamhyung Kim }
2535ea7cd592SNamhyung Kim 
253684734b06SKan Liang static int
253784734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
253884734b06SKan Liang {
253935a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2540599a2f38SNamhyung Kim 		return 0;
2541599a2f38SNamhyung Kim 
254284734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
254384734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
254484734b06SKan Liang 		browser->hists->socket_filter = -1;
254584734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
254684734b06SKan Liang 	} else {
254784734b06SKan Liang 		browser->hists->socket_filter = act->socket;
254884734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
254984734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
255084734b06SKan Liang 	}
255184734b06SKan Liang 
255284734b06SKan Liang 	hists__filter_by_socket(browser->hists);
255384734b06SKan Liang 	hist_browser__reset(browser);
255484734b06SKan Liang 	return 0;
255584734b06SKan Liang }
255684734b06SKan Liang 
255784734b06SKan Liang static int
255884734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
255984734b06SKan Liang 	       char **optstr, int socket_id)
256084734b06SKan Liang {
256135a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
256284734b06SKan Liang 		return 0;
256384734b06SKan Liang 
256484734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
256584734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
256684734b06SKan Liang 		     socket_id) < 0)
256784734b06SKan Liang 		return 0;
256884734b06SKan Liang 
256984734b06SKan Liang 	act->socket = socket_id;
257084734b06SKan Liang 	act->fn = do_zoom_socket;
257184734b06SKan Liang 	return 1;
257284734b06SKan Liang }
257384734b06SKan Liang 
2574112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2575064f1981SNamhyung Kim {
2576064f1981SNamhyung Kim 	u64 nr_entries = 0;
2577064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2578064f1981SNamhyung Kim 
2579f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2580268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2581268397cbSNamhyung Kim 		return;
2582268397cbSNamhyung Kim 	}
2583268397cbSNamhyung Kim 
258414135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2585064f1981SNamhyung Kim 		nr_entries++;
2586f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2587064f1981SNamhyung Kim 	}
2588064f1981SNamhyung Kim 
2589112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2590f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2591064f1981SNamhyung Kim }
2592341487abSFeng Tang 
2593b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2594b62e8dfcSNamhyung Kim 					       double percent)
2595b62e8dfcSNamhyung Kim {
2596b62e8dfcSNamhyung Kim 	struct hist_entry *he;
2597b62e8dfcSNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2598b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2599b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2600b62e8dfcSNamhyung Kim 
2601b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2602b62e8dfcSNamhyung Kim 
2603b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2604b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2605b62e8dfcSNamhyung Kim 
260679dded87SNamhyung Kim 		if (he->has_no_entry) {
260779dded87SNamhyung Kim 			he->has_no_entry = false;
260879dded87SNamhyung Kim 			he->nr_rows = 0;
260979dded87SNamhyung Kim 		}
261079dded87SNamhyung Kim 
2611d0506edbSNamhyung Kim 		if (!he->leaf || !symbol_conf.use_callchain)
2612d0506edbSNamhyung Kim 			goto next;
2613d0506edbSNamhyung Kim 
2614b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2615b62e8dfcSNamhyung Kim 			total = he->stat.period;
2616b62e8dfcSNamhyung Kim 
2617b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2618b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2619b62e8dfcSNamhyung Kim 
2620b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2621b62e8dfcSNamhyung Kim 		}
2622b62e8dfcSNamhyung Kim 
2623b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2624b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2625b62e8dfcSNamhyung Kim 
2626d0506edbSNamhyung Kim next:
2627201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2628d0506edbSNamhyung Kim 
2629b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2630b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2631492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2632b62e8dfcSNamhyung Kim 	}
2633b62e8dfcSNamhyung Kim }
2634b62e8dfcSNamhyung Kim 
2635aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2636dd00d486SJiri Olsa 				    const char *helpline,
2637aca7a94dSNamhyung Kim 				    bool left_exits,
263868d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2639064f1981SNamhyung Kim 				    float min_pcnt,
2640ce80d3beSKan Liang 				    struct perf_env *env)
2641aca7a94dSNamhyung Kim {
26424ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2643b1a9ceefSNamhyung Kim 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2644aca7a94dSNamhyung Kim 	struct branch_info *bi;
2645f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2646f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2647ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2648aca7a94dSNamhyung Kim 	int nr_options = 0;
2649aca7a94dSNamhyung Kim 	int key = -1;
2650aca7a94dSNamhyung Kim 	char buf[64];
26519783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
265259dc9f25SNamhyung Kim 	struct perf_hpp_fmt *fmt;
2653aca7a94dSNamhyung Kim 
2654e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2655e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2656e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2657e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
2658e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2659e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2660e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2661e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
26627727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
26637727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
2664e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2665e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2666e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2667e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2668105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2669025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2670b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
267131eb4360SNamhyung Kim 	"m             Display context menu\n"				\
267284734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2673e8e684a5SNamhyung Kim 
2674e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
2675e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
26766dd60135SNamhyung Kim 	"i             Show header information\n"
2677e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2678e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2679e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2680e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2681e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2682e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2683e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2684e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2685e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2686e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
268742337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2688fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2689e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2690e8e684a5SNamhyung Kim 
2691aca7a94dSNamhyung Kim 	if (browser == NULL)
2692aca7a94dSNamhyung Kim 		return -1;
2693aca7a94dSNamhyung Kim 
2694ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2695ed426915SNamhyung Kim 	SLang_reset_tty();
2696ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2697ed426915SNamhyung Kim 
269803905048SNamhyung Kim 	if (min_pcnt)
2699064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2700112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2701064f1981SNamhyung Kim 
270284734b06SKan Liang 	browser->pstack = pstack__new(3);
270301f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2704aca7a94dSNamhyung Kim 		goto out;
2705aca7a94dSNamhyung Kim 
2706aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2707aca7a94dSNamhyung Kim 
2708aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2709ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2710aca7a94dSNamhyung Kim 
2711f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
271259dc9f25SNamhyung Kim 		perf_hpp__reset_width(fmt, hists);
2713c6c3c02dSArnaldo Carvalho de Melo 		/*
2714c6c3c02dSArnaldo Carvalho de Melo 		 * This is done just once, and activates the horizontal scrolling
2715c6c3c02dSArnaldo Carvalho de Melo 		 * code in the ui_browser code, it would be better to have a the
2716c6c3c02dSArnaldo Carvalho de Melo 		 * counter in the perf_hpp code, but I couldn't find doing it here
2717c6c3c02dSArnaldo Carvalho de Melo 		 * works, FIXME by setting this in hist_browser__new, for now, be
2718c6c3c02dSArnaldo Carvalho de Melo 		 * clever 8-)
2719c6c3c02dSArnaldo Carvalho de Melo 		 */
2720c6c3c02dSArnaldo Carvalho de Melo 		++browser->b.columns;
2721c6c3c02dSArnaldo Carvalho de Melo 	}
272259dc9f25SNamhyung Kim 
27235b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
27245b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
27255b591669SNamhyung Kim 
2726aca7a94dSNamhyung Kim 	while (1) {
2727f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2728045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
2729ea7cd592SNamhyung Kim 		int choice = 0;
273084734b06SKan Liang 		int socked_id = -1;
2731aca7a94dSNamhyung Kim 
2732aca7a94dSNamhyung Kim 		nr_options = 0;
2733aca7a94dSNamhyung Kim 
27345f00b0f4SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline);
2735aca7a94dSNamhyung Kim 
2736aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
2737aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
2738045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
273984734b06SKan Liang 			socked_id = browser->he_selection->socket;
2740aca7a94dSNamhyung Kim 		}
2741aca7a94dSNamhyung Kim 		switch (key) {
2742aca7a94dSNamhyung Kim 		case K_TAB:
2743aca7a94dSNamhyung Kim 		case K_UNTAB:
2744aca7a94dSNamhyung Kim 			if (nr_events == 1)
2745aca7a94dSNamhyung Kim 				continue;
2746aca7a94dSNamhyung Kim 			/*
2747aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
2748aca7a94dSNamhyung Kim 			 * go to the next or previous
2749aca7a94dSNamhyung Kim 			 */
2750aca7a94dSNamhyung Kim 			goto out_free_stack;
2751aca7a94dSNamhyung Kim 		case 'a':
27522e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
2753aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
2754aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
2755aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
2756aca7a94dSNamhyung Kim 				continue;
2757aca7a94dSNamhyung Kim 			}
2758aca7a94dSNamhyung Kim 
2759aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
2760aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
2761aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
2762aca7a94dSNamhyung Kim 				continue;
2763bc7cad42SNamhyung Kim 
2764ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
2765ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
2766ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
2767bc7cad42SNamhyung Kim 			continue;
2768aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
2769aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
2770aff3f3f6SArnaldo Carvalho de Melo 			continue;
2771aca7a94dSNamhyung Kim 		case 'd':
2772fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
2773ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
2774bc7cad42SNamhyung Kim 			continue;
2775a7cb8863SArnaldo Carvalho de Melo 		case 'V':
2776a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
2777a7cb8863SArnaldo Carvalho de Melo 			continue;
2778aca7a94dSNamhyung Kim 		case 't':
2779ea7cd592SNamhyung Kim 			actions->thread = thread;
2780ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
2781bc7cad42SNamhyung Kim 			continue;
278284734b06SKan Liang 		case 'S':
278384734b06SKan Liang 			actions->socket = socked_id;
278484734b06SKan Liang 			do_zoom_socket(browser, actions);
278584734b06SKan Liang 			continue;
27865a5626b1SArnaldo Carvalho de Melo 		case '/':
2787aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
27884aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
27894aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
2790aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2791aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
279205e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
279305e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
2794aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
2795aca7a94dSNamhyung Kim 			}
2796aca7a94dSNamhyung Kim 			continue;
2797cdbab7c2SFeng Tang 		case 'r':
2798ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
2799ea7cd592SNamhyung Kim 				actions->thread = NULL;
2800ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
2801ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
2802ea7cd592SNamhyung Kim 			}
2803c77d8d70SFeng Tang 			continue;
2804341487abSFeng Tang 		case 's':
2805bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
2806ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
2807bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
2808bc7cad42SNamhyung Kim 					goto out_free_stack;
2809bc7cad42SNamhyung Kim 			}
2810341487abSFeng Tang 			continue;
28116dd60135SNamhyung Kim 		case 'i':
28126dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
28136dd60135SNamhyung Kim 			if (env->arch)
28146dd60135SNamhyung Kim 				tui__header_window(env);
28156dd60135SNamhyung Kim 			continue;
2816105eb30fSNamhyung Kim 		case 'F':
2817105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
2818105eb30fSNamhyung Kim 			continue;
281942337a22SNamhyung Kim 		case 'z':
282042337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
282142337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
282242337a22SNamhyung Kim 
282342337a22SNamhyung Kim 				top->zero = !top->zero;
282442337a22SNamhyung Kim 			}
282542337a22SNamhyung Kim 			continue;
2826b62e8dfcSNamhyung Kim 		case 'L':
2827b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
2828b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
2829b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2830b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
2831b62e8dfcSNamhyung Kim 				char *end;
2832b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
2833b62e8dfcSNamhyung Kim 
2834b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
2835b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
2836b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
2837b62e8dfcSNamhyung Kim 					continue;
2838b62e8dfcSNamhyung Kim 				}
2839b62e8dfcSNamhyung Kim 
2840b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
2841b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
2842b62e8dfcSNamhyung Kim 			}
2843b62e8dfcSNamhyung Kim 			continue;
2844aca7a94dSNamhyung Kim 		case K_F1:
2845aca7a94dSNamhyung Kim 		case 'h':
2846aca7a94dSNamhyung Kim 		case '?':
2847aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
2848e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
2849aca7a94dSNamhyung Kim 			continue;
2850aca7a94dSNamhyung Kim 		case K_ENTER:
2851aca7a94dSNamhyung Kim 		case K_RIGHT:
285231eb4360SNamhyung Kim 		case 'm':
2853aca7a94dSNamhyung Kim 			/* menu */
2854aca7a94dSNamhyung Kim 			break;
285563ab1749SArnaldo Carvalho de Melo 		case K_ESC:
2856aca7a94dSNamhyung Kim 		case K_LEFT: {
2857aca7a94dSNamhyung Kim 			const void *top;
2858aca7a94dSNamhyung Kim 
285901f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
2860aca7a94dSNamhyung Kim 				/*
2861aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
2862aca7a94dSNamhyung Kim 				 */
2863aca7a94dSNamhyung Kim 				if (left_exits)
2864aca7a94dSNamhyung Kim 					goto out_free_stack;
286563ab1749SArnaldo Carvalho de Melo 
286663ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
286763ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
286863ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
286963ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
287063ab1749SArnaldo Carvalho de Melo 
2871aca7a94dSNamhyung Kim 				continue;
2872aca7a94dSNamhyung Kim 			}
28736422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
2874bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
28756422184bSNamhyung Kim 				/*
28766422184bSNamhyung Kim 				 * No need to set actions->dso here since
28776422184bSNamhyung Kim 				 * it's just to remove the current filter.
28786422184bSNamhyung Kim 				 * Ditto for thread below.
28796422184bSNamhyung Kim 				 */
28806422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
288184734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
28826422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
288384734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
288484734b06SKan Liang 				do_zoom_socket(browser, actions);
288584734b06SKan Liang 			}
2886aca7a94dSNamhyung Kim 			continue;
2887aca7a94dSNamhyung Kim 		}
2888aca7a94dSNamhyung Kim 		case 'q':
2889aca7a94dSNamhyung Kim 		case CTRL('c'):
2890516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
2891fbb7997eSArnaldo Carvalho de Melo 		case 'f':
289213d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
289313d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
289413d1e536SNamhyung Kim 
289513d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
289613d1e536SNamhyung Kim 				/*
289713d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
289813d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
289913d1e536SNamhyung Kim 				 */
290013d1e536SNamhyung Kim 				if (top->evlist->enabled) {
290113d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
290213d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
290313d1e536SNamhyung Kim 				} else {
290413d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
290513d1e536SNamhyung Kim 					hbt->refresh = 0;
290613d1e536SNamhyung Kim 				}
290713d1e536SNamhyung Kim 				continue;
290813d1e536SNamhyung Kim 			}
29093e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
2910aca7a94dSNamhyung Kim 		default:
29113e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
2912aca7a94dSNamhyung Kim 			continue;
2913aca7a94dSNamhyung Kim 		}
2914aca7a94dSNamhyung Kim 
29152e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
29160ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
29170ba332f7SArnaldo Carvalho de Melo 
291855369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
2919aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
29200ba332f7SArnaldo Carvalho de Melo 
29210ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
29220ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
29230ba332f7SArnaldo Carvalho de Melo 
2924ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2925ea7cd592SNamhyung Kim 						       &actions[nr_options],
2926ea7cd592SNamhyung Kim 						       &options[nr_options],
2927ea7cd592SNamhyung Kim 						       bi->from.map,
2928ea7cd592SNamhyung Kim 						       bi->from.sym);
2929ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
2930ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
2931ea7cd592SNamhyung Kim 							&actions[nr_options],
2932ea7cd592SNamhyung Kim 							&options[nr_options],
2933ea7cd592SNamhyung Kim 							bi->to.map,
2934ea7cd592SNamhyung Kim 							bi->to.sym);
2935aca7a94dSNamhyung Kim 		} else {
2936ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2937ea7cd592SNamhyung Kim 						       &actions[nr_options],
2938ea7cd592SNamhyung Kim 						       &options[nr_options],
2939ea7cd592SNamhyung Kim 						       browser->selection->map,
2940ea7cd592SNamhyung Kim 						       browser->selection->sym);
2941446fb96cSArnaldo Carvalho de Melo 		}
29420ba332f7SArnaldo Carvalho de Melo skip_annotation:
2943ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
2944ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
2945ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
2946045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
2947ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
2948ea7cd592SNamhyung Kim 					  &options[nr_options],
2949bd315aabSWang Nan 					  browser->selection ?
2950bd315aabSWang Nan 						browser->selection->map : NULL);
295184734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
295284734b06SKan Liang 					     &options[nr_options],
295384734b06SKan Liang 					     socked_id);
2954cdbab7c2SFeng Tang 		/* perf script support */
2955b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
2956b1baae89SNamhyung Kim 			goto skip_scripting;
2957b1baae89SNamhyung Kim 
2958cdbab7c2SFeng Tang 		if (browser->he_selection) {
2959fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
2960ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
2961ea7cd592SNamhyung Kim 							     &actions[nr_options],
2962ea7cd592SNamhyung Kim 							     &options[nr_options],
2963ea7cd592SNamhyung Kim 							     thread, NULL);
29642eafd410SNamhyung Kim 			}
2965bd315aabSWang Nan 			/*
2966bd315aabSWang Nan 			 * Note that browser->selection != NULL
2967bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
2968bd315aabSWang Nan 			 * so we don't need to check browser->selection
2969bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
2970bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
2971bd315aabSWang Nan 			 *
2972bd315aabSWang Nan 			 * See hist_browser__show_entry.
2973bd315aabSWang Nan 			 */
29742e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
2975ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
2976ea7cd592SNamhyung Kim 							     &actions[nr_options],
2977ea7cd592SNamhyung Kim 							     &options[nr_options],
2978ea7cd592SNamhyung Kim 							     NULL, browser->selection->sym);
2979cdbab7c2SFeng Tang 			}
2980c221acb0SNamhyung Kim 		}
2981ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
2982ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
2983ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
2984ea7cd592SNamhyung Kim 					     &options[nr_options]);
2985b1baae89SNamhyung Kim skip_scripting:
2986ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
2987ea7cd592SNamhyung Kim 					   &options[nr_options]);
2988aca7a94dSNamhyung Kim 
2989ea7cd592SNamhyung Kim 		do {
2990ea7cd592SNamhyung Kim 			struct popup_action *act;
2991ea7cd592SNamhyung Kim 
2992ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
2993ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
2994aca7a94dSNamhyung Kim 				break;
2995aca7a94dSNamhyung Kim 
2996ea7cd592SNamhyung Kim 			act = &actions[choice];
2997ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
2998ea7cd592SNamhyung Kim 		} while (key == 1);
2999aca7a94dSNamhyung Kim 
3000bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3001341487abSFeng Tang 			break;
3002341487abSFeng Tang 	}
3003aca7a94dSNamhyung Kim out_free_stack:
300401f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3005aca7a94dSNamhyung Kim out:
3006aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3007f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3008aca7a94dSNamhyung Kim 	return key;
3009aca7a94dSNamhyung Kim }
3010aca7a94dSNamhyung Kim 
3011aca7a94dSNamhyung Kim struct perf_evsel_menu {
3012aca7a94dSNamhyung Kim 	struct ui_browser b;
3013aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
3014aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3015064f1981SNamhyung Kim 	float min_pcnt;
3016ce80d3beSKan Liang 	struct perf_env *env;
3017aca7a94dSNamhyung Kim };
3018aca7a94dSNamhyung Kim 
3019aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3020aca7a94dSNamhyung Kim 				   void *entry, int row)
3021aca7a94dSNamhyung Kim {
3022aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
3023aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
3024aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
30254ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3026aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
30274ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
30287289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
3029aca7a94dSNamhyung Kim 	char bf[256], unit;
3030aca7a94dSNamhyung Kim 	const char *warn = " ";
3031aca7a94dSNamhyung Kim 	size_t printed;
3032aca7a94dSNamhyung Kim 
3033aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3034aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3035aca7a94dSNamhyung Kim 
3036759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
3037717e263fSNamhyung Kim 		struct perf_evsel *pos;
3038717e263fSNamhyung Kim 
3039717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
3040717e263fSNamhyung Kim 
3041717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
30424ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
30434ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3044717e263fSNamhyung Kim 		}
3045717e263fSNamhyung Kim 	}
3046717e263fSNamhyung Kim 
3047aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3048aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3049aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3050517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3051aca7a94dSNamhyung Kim 
30524ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3053aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3054aca7a94dSNamhyung Kim 		menu->lost_events = true;
3055aca7a94dSNamhyung Kim 		if (!current_entry)
3056aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3057aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3058aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3059aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3060aca7a94dSNamhyung Kim 		warn = bf;
3061aca7a94dSNamhyung Kim 	}
3062aca7a94dSNamhyung Kim 
306326270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3064aca7a94dSNamhyung Kim 
3065aca7a94dSNamhyung Kim 	if (current_entry)
3066aca7a94dSNamhyung Kim 		menu->selection = evsel;
3067aca7a94dSNamhyung Kim }
3068aca7a94dSNamhyung Kim 
3069aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3070aca7a94dSNamhyung Kim 				int nr_events, const char *help,
30719783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
3072aca7a94dSNamhyung Kim {
3073aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
3074aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3075dd00d486SJiri Olsa 	const char *title = "Available samples";
30769783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3077aca7a94dSNamhyung Kim 	int key;
3078aca7a94dSNamhyung Kim 
3079aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3080aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3081aca7a94dSNamhyung Kim 		return -1;
3082aca7a94dSNamhyung Kim 
3083aca7a94dSNamhyung Kim 	while (1) {
3084aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3085aca7a94dSNamhyung Kim 
3086aca7a94dSNamhyung Kim 		switch (key) {
3087aca7a94dSNamhyung Kim 		case K_TIMER:
30889783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
3089aca7a94dSNamhyung Kim 
3090aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
3091aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3092aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3093aca7a94dSNamhyung Kim 			}
3094aca7a94dSNamhyung Kim 			continue;
3095aca7a94dSNamhyung Kim 		case K_RIGHT:
3096aca7a94dSNamhyung Kim 		case K_ENTER:
3097aca7a94dSNamhyung Kim 			if (!menu->selection)
3098aca7a94dSNamhyung Kim 				continue;
3099aca7a94dSNamhyung Kim 			pos = menu->selection;
3100aca7a94dSNamhyung Kim browse_hists:
3101aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
3102aca7a94dSNamhyung Kim 			/*
3103aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3104aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3105aca7a94dSNamhyung Kim 			 */
31069783adf7SNamhyung Kim 			if (hbt)
31079783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3108aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
3109dd00d486SJiri Olsa 						       true, hbt,
3110064f1981SNamhyung Kim 						       menu->min_pcnt,
311168d80758SNamhyung Kim 						       menu->env);
3112aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3113aca7a94dSNamhyung Kim 			switch (key) {
3114aca7a94dSNamhyung Kim 			case K_TAB:
3115aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
31169a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
3117aca7a94dSNamhyung Kim 				else
31189a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
3119aca7a94dSNamhyung Kim 				goto browse_hists;
3120aca7a94dSNamhyung Kim 			case K_UNTAB:
3121aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
31229a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
3123aca7a94dSNamhyung Kim 				else
3124d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
3125aca7a94dSNamhyung Kim 				goto browse_hists;
3126341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
3127aca7a94dSNamhyung Kim 			case 'q':
3128aca7a94dSNamhyung Kim 			case CTRL('c'):
3129aca7a94dSNamhyung Kim 				goto out;
313063ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3131aca7a94dSNamhyung Kim 			default:
3132aca7a94dSNamhyung Kim 				continue;
3133aca7a94dSNamhyung Kim 			}
3134aca7a94dSNamhyung Kim 		case K_LEFT:
3135aca7a94dSNamhyung Kim 			continue;
3136aca7a94dSNamhyung Kim 		case K_ESC:
3137aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3138aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3139aca7a94dSNamhyung Kim 				continue;
3140aca7a94dSNamhyung Kim 			/* Fall thru */
3141aca7a94dSNamhyung Kim 		case 'q':
3142aca7a94dSNamhyung Kim 		case CTRL('c'):
3143aca7a94dSNamhyung Kim 			goto out;
3144aca7a94dSNamhyung Kim 		default:
3145aca7a94dSNamhyung Kim 			continue;
3146aca7a94dSNamhyung Kim 		}
3147aca7a94dSNamhyung Kim 	}
3148aca7a94dSNamhyung Kim 
3149aca7a94dSNamhyung Kim out:
3150aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3151aca7a94dSNamhyung Kim 	return key;
3152aca7a94dSNamhyung Kim }
3153aca7a94dSNamhyung Kim 
3154316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3155fc24d7c2SNamhyung Kim 				 void *entry)
3156fc24d7c2SNamhyung Kim {
3157fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3158fc24d7c2SNamhyung Kim 
3159fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3160fc24d7c2SNamhyung Kim 		return true;
3161fc24d7c2SNamhyung Kim 
3162fc24d7c2SNamhyung Kim 	return false;
3163fc24d7c2SNamhyung Kim }
3164fc24d7c2SNamhyung Kim 
3165aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3166fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
316768d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
3168064f1981SNamhyung Kim 					   float min_pcnt,
3169ce80d3beSKan Liang 					   struct perf_env *env)
3170aca7a94dSNamhyung Kim {
3171aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3172aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
3173aca7a94dSNamhyung Kim 		.b = {
3174aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
3175aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3176aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3177aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3178fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3179fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3180aca7a94dSNamhyung Kim 			.priv	    = evlist,
3181aca7a94dSNamhyung Kim 		},
3182064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
318368d80758SNamhyung Kim 		.env = env,
3184aca7a94dSNamhyung Kim 	};
3185aca7a94dSNamhyung Kim 
3186aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3187aca7a94dSNamhyung Kim 
31880050f7aaSArnaldo Carvalho de Melo 	evlist__for_each(evlist, pos) {
31897289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
3190aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3191aca7a94dSNamhyung Kim 
3192aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3193aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3194aca7a94dSNamhyung Kim 	}
3195aca7a94dSNamhyung Kim 
3196fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3197aca7a94dSNamhyung Kim }
3198aca7a94dSNamhyung Kim 
3199aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
320068d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
3201064f1981SNamhyung Kim 				  float min_pcnt,
3202ce80d3beSKan Liang 				  struct perf_env *env)
3203aca7a94dSNamhyung Kim {
3204fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
3205fc24d7c2SNamhyung Kim 
3206fc24d7c2SNamhyung Kim single_entry:
3207fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
32089a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
3209fc24d7c2SNamhyung Kim 
3210fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
3211dd00d486SJiri Olsa 						false, hbt, min_pcnt,
3212064f1981SNamhyung Kim 						env);
3213aca7a94dSNamhyung Kim 	}
3214aca7a94dSNamhyung Kim 
3215fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
3216fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
3217fc24d7c2SNamhyung Kim 
3218fc24d7c2SNamhyung Kim 		nr_entries = 0;
32190050f7aaSArnaldo Carvalho de Melo 		evlist__for_each(evlist, pos) {
3220fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
3221fc24d7c2SNamhyung Kim 				nr_entries++;
32220050f7aaSArnaldo Carvalho de Melo 		}
3223fc24d7c2SNamhyung Kim 
3224fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3225fc24d7c2SNamhyung Kim 			goto single_entry;
3226fc24d7c2SNamhyung Kim 	}
3227fc24d7c2SNamhyung Kim 
3228fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3229064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
3230aca7a94dSNamhyung Kim }
3231