xref: /linux/tools/perf/ui/browsers/hists.c (revision c611152373e84a7677cd7d496e849de4debdab66)
1aca7a94dSNamhyung Kim #include <stdio.h>
2aca7a94dSNamhyung Kim #include <stdlib.h>
3aca7a94dSNamhyung Kim #include <string.h>
4aca7a94dSNamhyung Kim #include <linux/rbtree.h>
5aca7a94dSNamhyung Kim 
6aca7a94dSNamhyung Kim #include "../../util/evsel.h"
7aca7a94dSNamhyung Kim #include "../../util/evlist.h"
8aca7a94dSNamhyung Kim #include "../../util/hist.h"
9aca7a94dSNamhyung Kim #include "../../util/pstack.h"
10aca7a94dSNamhyung Kim #include "../../util/sort.h"
11aca7a94dSNamhyung Kim #include "../../util/util.h"
1242337a22SNamhyung Kim #include "../../util/top.h"
1368d80758SNamhyung Kim #include "../../arch/common.h"
14aca7a94dSNamhyung Kim 
15f758990fSJiri Olsa #include "../browsers/hists.h"
16aca7a94dSNamhyung Kim #include "../helpline.h"
17aca7a94dSNamhyung Kim #include "../util.h"
18aca7a94dSNamhyung Kim #include "../ui.h"
19aca7a94dSNamhyung Kim #include "map.h"
20d755330cSJiri Olsa #include "annotate.h"
21aca7a94dSNamhyung Kim 
22f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
23f5951d56SNamhyung Kim 
245b91a86fSJiri Olsa static int perf_evsel_browser_title(struct hist_browser *browser,
251e378ebdSTaeung Song 				    char *bf, size_t size);
26112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
27aca7a94dSNamhyung Kim 
28c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
29c3b78952SNamhyung Kim 					     float min_pcnt);
30c3b78952SNamhyung Kim 
31268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
32268397cbSNamhyung Kim {
339c0fa8ddSArnaldo Carvalho de Melo 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
34268397cbSNamhyung Kim }
35268397cbSNamhyung Kim 
364fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
374fabf3d1SHe Kuang {
384fabf3d1SHe Kuang 	struct rb_node *nd;
394fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
404fabf3d1SHe Kuang 	int unfolded_rows = 0;
414fabf3d1SHe Kuang 
424fabf3d1SHe Kuang 	for (nd = rb_first(&hists->entries);
434fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
44f5b763feSNamhyung Kim 	     nd = rb_hierarchy_next(nd)) {
454fabf3d1SHe Kuang 		struct hist_entry *he =
464fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
474fabf3d1SHe Kuang 
48f5b763feSNamhyung Kim 		if (he->leaf && he->unfolded)
494fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
504fabf3d1SHe Kuang 	}
514fabf3d1SHe Kuang 	return unfolded_rows;
524fabf3d1SHe Kuang }
534fabf3d1SHe Kuang 
54c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
55c3b78952SNamhyung Kim {
56c3b78952SNamhyung Kim 	u32 nr_entries;
57c3b78952SNamhyung Kim 
58f5b763feSNamhyung Kim 	if (symbol_conf.report_hierarchy)
59f5b763feSNamhyung Kim 		nr_entries = hb->nr_hierarchy_entries;
60f5b763feSNamhyung Kim 	else if (hist_browser__has_filter(hb))
61c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
62c3b78952SNamhyung Kim 	else
63c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
64c3b78952SNamhyung Kim 
654fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
66c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
67c3b78952SNamhyung Kim }
68c3b78952SNamhyung Kim 
69025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
70025bf7eaSArnaldo Carvalho de Melo {
71025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
72f8e6710dSJiri Olsa 	struct hists *hists = hb->hists;
73f8e6710dSJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
74f8e6710dSJiri Olsa 	u16 header_offset, index_row;
75025bf7eaSArnaldo Carvalho de Melo 
76f8e6710dSJiri Olsa 	header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
77025bf7eaSArnaldo Carvalho de Melo 	browser->rows = browser->height - header_offset;
78025bf7eaSArnaldo Carvalho de Melo 	/*
79025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
80025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
81025bf7eaSArnaldo Carvalho de Melo 	 */
82025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
83025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
84025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
85025bf7eaSArnaldo Carvalho de Melo }
86025bf7eaSArnaldo Carvalho de Melo 
87357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
88aca7a94dSNamhyung Kim {
89357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
90357cfff1SArnaldo Carvalho de Melo 
91aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
92357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
93357cfff1SArnaldo Carvalho de Melo 	/*
94357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
95357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
96357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
97357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
98357cfff1SArnaldo Carvalho de Melo  	 */
99357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
100025bf7eaSArnaldo Carvalho de Melo 	hist_browser__update_rows(hb);
101aca7a94dSNamhyung Kim }
102aca7a94dSNamhyung Kim 
103ca3ff33bSArnaldo Carvalho de Melo static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
104ca3ff33bSArnaldo Carvalho de Melo {
105f8e6710dSJiri Olsa 	struct hists *hists = browser->hists;
106f8e6710dSJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
107f8e6710dSJiri Olsa 	u16 header_offset;
108025bf7eaSArnaldo Carvalho de Melo 
109f8e6710dSJiri Olsa 	header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
110025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row + header_offset, column);
111ca3ff33bSArnaldo Carvalho de Melo }
112ca3ff33bSArnaldo Carvalho de Melo 
11305e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
114aca7a94dSNamhyung Kim {
115c3b78952SNamhyung Kim 	/*
116c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
117c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
118c3b78952SNamhyung Kim 	 */
119c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
120c3b78952SNamhyung Kim 
121268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
122c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
123357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
12405e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
125aca7a94dSNamhyung Kim }
126aca7a94dSNamhyung Kim 
127aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
128aca7a94dSNamhyung Kim {
129aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
130aca7a94dSNamhyung Kim }
131aca7a94dSNamhyung Kim 
13205e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
133aca7a94dSNamhyung Kim {
1343698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
135aca7a94dSNamhyung Kim }
136aca7a94dSNamhyung Kim 
13705e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
138aca7a94dSNamhyung Kim {
1393698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
140aca7a94dSNamhyung Kim }
141aca7a94dSNamhyung Kim 
1423698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
143aca7a94dSNamhyung Kim {
1443698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
145aca7a94dSNamhyung Kim }
146aca7a94dSNamhyung Kim 
14705e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
148aca7a94dSNamhyung Kim {
149aca7a94dSNamhyung Kim 	int n = 0;
150aca7a94dSNamhyung Kim 	struct rb_node *nd;
151aca7a94dSNamhyung Kim 
15205e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
153aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
154aca7a94dSNamhyung Kim 		struct callchain_list *chain;
155aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
156aca7a94dSNamhyung Kim 
157aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
158aca7a94dSNamhyung Kim 			++n;
159aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
160aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
161aca7a94dSNamhyung Kim 			if (folded_sign == '+')
162aca7a94dSNamhyung Kim 				break;
163aca7a94dSNamhyung Kim 		}
164aca7a94dSNamhyung Kim 
165aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
166aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
167aca7a94dSNamhyung Kim 	}
168aca7a94dSNamhyung Kim 
169aca7a94dSNamhyung Kim 	return n;
170aca7a94dSNamhyung Kim }
171aca7a94dSNamhyung Kim 
1724b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
1734b3a3212SNamhyung Kim {
1744b3a3212SNamhyung Kim 	struct callchain_list *chain;
1754b3a3212SNamhyung Kim 	char folded_sign = 0;
1764b3a3212SNamhyung Kim 	int n = 0;
1774b3a3212SNamhyung Kim 
1784b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
1794b3a3212SNamhyung Kim 		if (!folded_sign) {
1804b3a3212SNamhyung Kim 			/* only check first chain list entry */
1814b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1824b3a3212SNamhyung Kim 			if (folded_sign == '+')
1834b3a3212SNamhyung Kim 				return 1;
1844b3a3212SNamhyung Kim 		}
1854b3a3212SNamhyung Kim 		n++;
1864b3a3212SNamhyung Kim 	}
1874b3a3212SNamhyung Kim 
1884b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
1894b3a3212SNamhyung Kim 		if (!folded_sign) {
1904b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
1914b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1924b3a3212SNamhyung Kim 			if (folded_sign == '+')
1934b3a3212SNamhyung Kim 				return 1;
1944b3a3212SNamhyung Kim 		}
1954b3a3212SNamhyung Kim 		n++;
1964b3a3212SNamhyung Kim 	}
1974b3a3212SNamhyung Kim 
1984b3a3212SNamhyung Kim 	return n;
1994b3a3212SNamhyung Kim }
2004b3a3212SNamhyung Kim 
2018c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
2028c430a34SNamhyung Kim {
2038c430a34SNamhyung Kim 	return 1;
2048c430a34SNamhyung Kim }
2058c430a34SNamhyung Kim 
206aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
207aca7a94dSNamhyung Kim {
208aca7a94dSNamhyung Kim 	struct callchain_list *chain;
209aca7a94dSNamhyung Kim 	bool unfolded = false;
210aca7a94dSNamhyung Kim 	int n = 0;
211aca7a94dSNamhyung Kim 
2124b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2134b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2148c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2158c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2164b3a3212SNamhyung Kim 
217aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
218aca7a94dSNamhyung Kim 		++n;
2193698dab1SNamhyung Kim 		unfolded = chain->unfolded;
220aca7a94dSNamhyung Kim 	}
221aca7a94dSNamhyung Kim 
222aca7a94dSNamhyung Kim 	if (unfolded)
223aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
224aca7a94dSNamhyung Kim 
225aca7a94dSNamhyung Kim 	return n;
226aca7a94dSNamhyung Kim }
227aca7a94dSNamhyung Kim 
228aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
229aca7a94dSNamhyung Kim {
230aca7a94dSNamhyung Kim 	struct rb_node *nd;
231aca7a94dSNamhyung Kim 	int n = 0;
232aca7a94dSNamhyung Kim 
233aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
234aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
235aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
236aca7a94dSNamhyung Kim 	}
237aca7a94dSNamhyung Kim 
238aca7a94dSNamhyung Kim 	return n;
239aca7a94dSNamhyung Kim }
240aca7a94dSNamhyung Kim 
241f5b763feSNamhyung Kim static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
242f5b763feSNamhyung Kim 				bool include_children)
243f5b763feSNamhyung Kim {
244f5b763feSNamhyung Kim 	int count = 0;
245f5b763feSNamhyung Kim 	struct rb_node *node;
246f5b763feSNamhyung Kim 	struct hist_entry *child;
247f5b763feSNamhyung Kim 
248f5b763feSNamhyung Kim 	if (he->leaf)
249f5b763feSNamhyung Kim 		return callchain__count_rows(&he->sorted_chain);
250f5b763feSNamhyung Kim 
25179dded87SNamhyung Kim 	if (he->has_no_entry)
25279dded87SNamhyung Kim 		return 1;
25379dded87SNamhyung Kim 
254f5b763feSNamhyung Kim 	node = rb_first(&he->hroot_out);
255f5b763feSNamhyung Kim 	while (node) {
256f5b763feSNamhyung Kim 		float percent;
257f5b763feSNamhyung Kim 
258f5b763feSNamhyung Kim 		child = rb_entry(node, struct hist_entry, rb_node);
259f5b763feSNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
260f5b763feSNamhyung Kim 
261f5b763feSNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt) {
262f5b763feSNamhyung Kim 			count++;
263f5b763feSNamhyung Kim 
264f5b763feSNamhyung Kim 			if (include_children && child->unfolded)
265f5b763feSNamhyung Kim 				count += hierarchy_count_rows(hb, child, true);
266f5b763feSNamhyung Kim 		}
267f5b763feSNamhyung Kim 
268f5b763feSNamhyung Kim 		node = rb_next(node);
269f5b763feSNamhyung Kim 	}
270f5b763feSNamhyung Kim 	return count;
271f5b763feSNamhyung Kim }
272f5b763feSNamhyung Kim 
2733698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
274aca7a94dSNamhyung Kim {
2753698dab1SNamhyung Kim 	if (!he)
276aca7a94dSNamhyung Kim 		return false;
277aca7a94dSNamhyung Kim 
2783698dab1SNamhyung Kim 	if (!he->has_children)
279aca7a94dSNamhyung Kim 		return false;
280aca7a94dSNamhyung Kim 
2813698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
2823698dab1SNamhyung Kim 	return true;
2833698dab1SNamhyung Kim }
2843698dab1SNamhyung Kim 
2853698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
2863698dab1SNamhyung Kim {
2873698dab1SNamhyung Kim 	if (!cl)
2883698dab1SNamhyung Kim 		return false;
2893698dab1SNamhyung Kim 
2903698dab1SNamhyung Kim 	if (!cl->has_children)
2913698dab1SNamhyung Kim 		return false;
2923698dab1SNamhyung Kim 
2933698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
294aca7a94dSNamhyung Kim 	return true;
295aca7a94dSNamhyung Kim }
296aca7a94dSNamhyung Kim 
29705e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
298aca7a94dSNamhyung Kim {
29905e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
300aca7a94dSNamhyung Kim 
30105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
302aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
303aca7a94dSNamhyung Kim 		struct callchain_list *chain;
304aca7a94dSNamhyung Kim 		bool first = true;
305aca7a94dSNamhyung Kim 
306aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
307aca7a94dSNamhyung Kim 			if (first) {
308aca7a94dSNamhyung Kim 				first = false;
3093698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
310aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
311aca7a94dSNamhyung Kim 			} else
3123698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
313aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
314aca7a94dSNamhyung Kim 		}
315aca7a94dSNamhyung Kim 
316aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
317aca7a94dSNamhyung Kim 	}
318aca7a94dSNamhyung Kim }
319aca7a94dSNamhyung Kim 
320a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
321a7444af6SNamhyung Kim 					       bool has_sibling)
322aca7a94dSNamhyung Kim {
323aca7a94dSNamhyung Kim 	struct callchain_list *chain;
324aca7a94dSNamhyung Kim 
325a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3263698dab1SNamhyung Kim 	chain->has_children = has_sibling;
327a7444af6SNamhyung Kim 
32890989035SAndres Freund 	if (!list_empty(&node->val)) {
32982162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3303698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
33182162b5aSNamhyung Kim 	}
332aca7a94dSNamhyung Kim 
33305e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
334aca7a94dSNamhyung Kim }
335aca7a94dSNamhyung Kim 
33605e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
337aca7a94dSNamhyung Kim {
338a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
339a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
340aca7a94dSNamhyung Kim 
34105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
342aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
343a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3448c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3458c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3464b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
347aca7a94dSNamhyung Kim 	}
348aca7a94dSNamhyung Kim }
349aca7a94dSNamhyung Kim 
35005e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
351aca7a94dSNamhyung Kim {
352f5b763feSNamhyung Kim 	if (he->init_have_children)
353f5b763feSNamhyung Kim 		return;
354f5b763feSNamhyung Kim 
355f5b763feSNamhyung Kim 	if (he->leaf) {
3563698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
35705e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
358f5b763feSNamhyung Kim 	} else {
359f5b763feSNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
360aca7a94dSNamhyung Kim 	}
361f5b763feSNamhyung Kim 
362f5b763feSNamhyung Kim 	he->init_have_children = true;
363aca7a94dSNamhyung Kim }
364aca7a94dSNamhyung Kim 
36505e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
366aca7a94dSNamhyung Kim {
36705e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
3683698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
3693698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
3703698dab1SNamhyung Kim 	bool has_children;
371aca7a94dSNamhyung Kim 
3724938cf0cSWang Nan 	if (!he || !ms)
3734938cf0cSWang Nan 		return false;
3744938cf0cSWang Nan 
3753698dab1SNamhyung Kim 	if (ms == &he->ms)
3763698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
3773698dab1SNamhyung Kim 	else
3783698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
3793698dab1SNamhyung Kim 
3803698dab1SNamhyung Kim 	if (has_children) {
381f5b763feSNamhyung Kim 		int child_rows = 0;
382f5b763feSNamhyung Kim 
383aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
384c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
385aca7a94dSNamhyung Kim 
386f5b763feSNamhyung Kim 		if (he->leaf)
387f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
388f5b763feSNamhyung Kim 		else
389f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
390f5b763feSNamhyung Kim 
391f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
392f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
393f5b763feSNamhyung Kim 
394f5b763feSNamhyung Kim 		if (he->unfolded) {
395f5b763feSNamhyung Kim 			if (he->leaf)
396aca7a94dSNamhyung Kim 				he->nr_rows = callchain__count_rows(&he->sorted_chain);
397aca7a94dSNamhyung Kim 			else
398f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
399f5b763feSNamhyung Kim 
400f5b763feSNamhyung Kim 			/* account grand children */
401f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
402f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
40379dded87SNamhyung Kim 
40479dded87SNamhyung Kim 			if (!he->leaf && he->nr_rows == 0) {
40579dded87SNamhyung Kim 				he->has_no_entry = true;
40679dded87SNamhyung Kim 				he->nr_rows = 1;
40779dded87SNamhyung Kim 			}
408f5b763feSNamhyung Kim 		} else {
409f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
410f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
411f5b763feSNamhyung Kim 
41279dded87SNamhyung Kim 			if (he->has_no_entry)
41379dded87SNamhyung Kim 				he->has_no_entry = false;
41479dded87SNamhyung Kim 
415aca7a94dSNamhyung Kim 			he->nr_rows = 0;
416f5b763feSNamhyung Kim 		}
417c3b78952SNamhyung Kim 
418c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
419f5b763feSNamhyung Kim 
420f5b763feSNamhyung Kim 		if (he->leaf)
421c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
422f5b763feSNamhyung Kim 		else
423f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
424aca7a94dSNamhyung Kim 
425aca7a94dSNamhyung Kim 		return true;
426aca7a94dSNamhyung Kim 	}
427aca7a94dSNamhyung Kim 
428aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
429aca7a94dSNamhyung Kim 	return false;
430aca7a94dSNamhyung Kim }
431aca7a94dSNamhyung Kim 
43205e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
433aca7a94dSNamhyung Kim {
434aca7a94dSNamhyung Kim 	int n = 0;
435aca7a94dSNamhyung Kim 	struct rb_node *nd;
436aca7a94dSNamhyung Kim 
43705e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
438aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
439aca7a94dSNamhyung Kim 		struct callchain_list *chain;
440aca7a94dSNamhyung Kim 		bool has_children = false;
441aca7a94dSNamhyung Kim 
442aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
443aca7a94dSNamhyung Kim 			++n;
4443698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
4453698dab1SNamhyung Kim 			has_children = chain->has_children;
446aca7a94dSNamhyung Kim 		}
447aca7a94dSNamhyung Kim 
448aca7a94dSNamhyung Kim 		if (has_children)
449aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
450aca7a94dSNamhyung Kim 	}
451aca7a94dSNamhyung Kim 
452aca7a94dSNamhyung Kim 	return n;
453aca7a94dSNamhyung Kim }
454aca7a94dSNamhyung Kim 
455aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
456aca7a94dSNamhyung Kim {
457aca7a94dSNamhyung Kim 	struct callchain_list *chain;
458aca7a94dSNamhyung Kim 	bool has_children = false;
459aca7a94dSNamhyung Kim 	int n = 0;
460aca7a94dSNamhyung Kim 
461aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
462aca7a94dSNamhyung Kim 		++n;
4633698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
4643698dab1SNamhyung Kim 		has_children = chain->has_children;
465aca7a94dSNamhyung Kim 	}
466aca7a94dSNamhyung Kim 
467aca7a94dSNamhyung Kim 	if (has_children)
468aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
469aca7a94dSNamhyung Kim 
470aca7a94dSNamhyung Kim 	return n;
471aca7a94dSNamhyung Kim }
472aca7a94dSNamhyung Kim 
473aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
474aca7a94dSNamhyung Kim {
475aca7a94dSNamhyung Kim 	struct rb_node *nd;
476aca7a94dSNamhyung Kim 	int n = 0;
477aca7a94dSNamhyung Kim 
478aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
479aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
480aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
481aca7a94dSNamhyung Kim 	}
482aca7a94dSNamhyung Kim 
483aca7a94dSNamhyung Kim 	return n;
484aca7a94dSNamhyung Kim }
485aca7a94dSNamhyung Kim 
486492b1010SNamhyung Kim static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
487492b1010SNamhyung Kim 				 bool unfold __maybe_unused)
488492b1010SNamhyung Kim {
489492b1010SNamhyung Kim 	float percent;
490492b1010SNamhyung Kim 	struct rb_node *nd;
491492b1010SNamhyung Kim 	struct hist_entry *child;
492492b1010SNamhyung Kim 	int n = 0;
493492b1010SNamhyung Kim 
494492b1010SNamhyung Kim 	for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
495492b1010SNamhyung Kim 		child = rb_entry(nd, struct hist_entry, rb_node);
496492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
497492b1010SNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt)
498492b1010SNamhyung Kim 			n++;
499492b1010SNamhyung Kim 	}
500492b1010SNamhyung Kim 
501492b1010SNamhyung Kim 	return n;
502492b1010SNamhyung Kim }
503492b1010SNamhyung Kim 
504492b1010SNamhyung Kim static void hist_entry__set_folding(struct hist_entry *he,
505492b1010SNamhyung Kim 				    struct hist_browser *hb, bool unfold)
506aca7a94dSNamhyung Kim {
50705e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
5083698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
509aca7a94dSNamhyung Kim 
5103698dab1SNamhyung Kim 	if (he->has_children) {
511492b1010SNamhyung Kim 		int n;
512492b1010SNamhyung Kim 
513492b1010SNamhyung Kim 		if (he->leaf)
514492b1010SNamhyung Kim 			n = callchain__set_folding(&he->sorted_chain, unfold);
515492b1010SNamhyung Kim 		else
516492b1010SNamhyung Kim 			n = hierarchy_set_folding(hb, he, unfold);
517492b1010SNamhyung Kim 
51805e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
519aca7a94dSNamhyung Kim 	} else
52005e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
521aca7a94dSNamhyung Kim }
522aca7a94dSNamhyung Kim 
523c3b78952SNamhyung Kim static void
524c3b78952SNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
525aca7a94dSNamhyung Kim {
526aca7a94dSNamhyung Kim 	struct rb_node *nd;
527492b1010SNamhyung Kim 	struct hist_entry *he;
528492b1010SNamhyung Kim 	double percent;
529aca7a94dSNamhyung Kim 
530492b1010SNamhyung Kim 	nd = rb_first(&browser->hists->entries);
531492b1010SNamhyung Kim 	while (nd) {
532492b1010SNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
533492b1010SNamhyung Kim 
534492b1010SNamhyung Kim 		/* set folding state even if it's currently folded */
535492b1010SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
536492b1010SNamhyung Kim 
537492b1010SNamhyung Kim 		hist_entry__set_folding(he, browser, unfold);
538492b1010SNamhyung Kim 
539492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(he);
540492b1010SNamhyung Kim 		if (he->filtered || percent < browser->min_pcnt)
541492b1010SNamhyung Kim 			continue;
542492b1010SNamhyung Kim 
543492b1010SNamhyung Kim 		if (!he->depth || unfold)
544492b1010SNamhyung Kim 			browser->nr_hierarchy_entries++;
545492b1010SNamhyung Kim 		if (he->leaf)
546c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
54779dded87SNamhyung Kim 		else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
54879dded87SNamhyung Kim 			browser->nr_hierarchy_entries++;
54979dded87SNamhyung Kim 			he->has_no_entry = true;
55079dded87SNamhyung Kim 			he->nr_rows = 1;
55179dded87SNamhyung Kim 		} else
55279dded87SNamhyung Kim 			he->has_no_entry = false;
553aca7a94dSNamhyung Kim 	}
554aca7a94dSNamhyung Kim }
555aca7a94dSNamhyung Kim 
55605e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
557aca7a94dSNamhyung Kim {
558492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
559c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
560c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
561c3b78952SNamhyung Kim 
562c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
563aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
56405e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
565aca7a94dSNamhyung Kim }
566aca7a94dSNamhyung Kim 
567aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
568aca7a94dSNamhyung Kim {
569aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
570aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
571aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
572aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
573aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
574aca7a94dSNamhyung Kim }
575aca7a94dSNamhyung Kim 
5765b91a86fSJiri Olsa static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
5775b91a86fSJiri Olsa {
5785b91a86fSJiri Olsa 	return browser->title ? browser->title(browser, bf, size) : 0;
5795b91a86fSJiri Olsa }
5805b91a86fSJiri Olsa 
581dabd2012SJiri Olsa int hist_browser__run(struct hist_browser *browser, const char *help)
582aca7a94dSNamhyung Kim {
583aca7a94dSNamhyung Kim 	int key;
584aca7a94dSNamhyung Kim 	char title[160];
585c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
5869783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
587aca7a94dSNamhyung Kim 
58805e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
589c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
590aca7a94dSNamhyung Kim 
5915b91a86fSJiri Olsa 	hist_browser__title(browser, title, sizeof(title));
592aca7a94dSNamhyung Kim 
593090cff3eSNamhyung Kim 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
594aca7a94dSNamhyung Kim 		return -1;
595aca7a94dSNamhyung Kim 
596aca7a94dSNamhyung Kim 	while (1) {
59705e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
598aca7a94dSNamhyung Kim 
599aca7a94dSNamhyung Kim 		switch (key) {
600fa5df943SNamhyung Kim 		case K_TIMER: {
601fa5df943SNamhyung Kim 			u64 nr_entries;
6029783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
603fa5df943SNamhyung Kim 
604*c6111523SNamhyung Kim 			if (hist_browser__has_filter(browser) ||
605*c6111523SNamhyung Kim 			    symbol_conf.report_hierarchy)
606112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
607fa5df943SNamhyung Kim 
608c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
609fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
610aca7a94dSNamhyung Kim 
61105e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
61205e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
61305e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
61405e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
61505e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
616aca7a94dSNamhyung Kim 			}
617aca7a94dSNamhyung Kim 
6185b91a86fSJiri Olsa 			hist_browser__title(browser, title, sizeof(title));
61905e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
620aca7a94dSNamhyung Kim 			continue;
621fa5df943SNamhyung Kim 		}
622aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
623aca7a94dSNamhyung Kim 			static int seq;
62405e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
625aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
626aca7a94dSNamhyung Kim 			ui_helpline__pop();
62762c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
62805e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
62905e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
63062c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
63105e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
63205e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
633aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
634aca7a94dSNamhyung Kim 		}
635aca7a94dSNamhyung Kim 			break;
636aca7a94dSNamhyung Kim 		case 'C':
637aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
63805e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
639aca7a94dSNamhyung Kim 			break;
640aca7a94dSNamhyung Kim 		case 'E':
641aca7a94dSNamhyung Kim 			/* Expand the whole world. */
64205e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
643aca7a94dSNamhyung Kim 			break;
644025bf7eaSArnaldo Carvalho de Melo 		case 'H':
645025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
646025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
647025bf7eaSArnaldo Carvalho de Melo 			break;
648aca7a94dSNamhyung Kim 		case K_ENTER:
64905e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
650aca7a94dSNamhyung Kim 				break;
651aca7a94dSNamhyung Kim 			/* fall thru */
652aca7a94dSNamhyung Kim 		default:
653aca7a94dSNamhyung Kim 			goto out;
654aca7a94dSNamhyung Kim 		}
655aca7a94dSNamhyung Kim 	}
656aca7a94dSNamhyung Kim out:
65705e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
658aca7a94dSNamhyung Kim 	return key;
659aca7a94dSNamhyung Kim }
660aca7a94dSNamhyung Kim 
66139ee533fSNamhyung Kim struct callchain_print_arg {
66239ee533fSNamhyung Kim 	/* for hists browser */
66339ee533fSNamhyung Kim 	off_t	row_offset;
66439ee533fSNamhyung Kim 	bool	is_current_entry;
66539ee533fSNamhyung Kim 
66639ee533fSNamhyung Kim 	/* for file dump */
66739ee533fSNamhyung Kim 	FILE	*fp;
66839ee533fSNamhyung Kim 	int	printed;
66939ee533fSNamhyung Kim };
67039ee533fSNamhyung Kim 
67139ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
67239ee533fSNamhyung Kim 					 struct callchain_list *chain,
67339ee533fSNamhyung Kim 					 const char *str, int offset,
67439ee533fSNamhyung Kim 					 unsigned short row,
67539ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
67639ee533fSNamhyung Kim 
677f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
678f4536dddSNamhyung Kim 					       struct callchain_list *chain,
67939ee533fSNamhyung Kim 					       const char *str, int offset,
68039ee533fSNamhyung Kim 					       unsigned short row,
68139ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
682f4536dddSNamhyung Kim {
683f4536dddSNamhyung Kim 	int color, width;
68439ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
68570e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
686f4536dddSNamhyung Kim 
687f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
688f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
689f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
690f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
691f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
69239ee533fSNamhyung Kim 		arg->is_current_entry = true;
693f4536dddSNamhyung Kim 	}
694f4536dddSNamhyung Kim 
695f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
696f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
69726270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
698517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
69970e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
70026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
701f4536dddSNamhyung Kim }
702f4536dddSNamhyung Kim 
70339ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
70439ee533fSNamhyung Kim 						  struct callchain_list *chain,
70539ee533fSNamhyung Kim 						  const char *str, int offset,
70639ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
70739ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
70839ee533fSNamhyung Kim {
70939ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
71039ee533fSNamhyung Kim 
71139ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
71239ee533fSNamhyung Kim 				folded_sign, str);
71339ee533fSNamhyung Kim }
71439ee533fSNamhyung Kim 
71539ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
71639ee533fSNamhyung Kim 				     unsigned short row);
71739ee533fSNamhyung Kim 
71839ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
71939ee533fSNamhyung Kim 					    unsigned short row)
72039ee533fSNamhyung Kim {
72139ee533fSNamhyung Kim 	return browser->b.rows == row;
72239ee533fSNamhyung Kim }
72339ee533fSNamhyung Kim 
72439ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
72539ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
72639ee533fSNamhyung Kim {
72739ee533fSNamhyung Kim 	return false;
72839ee533fSNamhyung Kim }
72939ee533fSNamhyung Kim 
730aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
731aca7a94dSNamhyung Kim 
73218bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
73318bb8381SNamhyung Kim 					     struct callchain_node *node,
73418bb8381SNamhyung Kim 					     struct callchain_list *chain,
73518bb8381SNamhyung Kim 					     unsigned short row, u64 total,
73618bb8381SNamhyung Kim 					     bool need_percent, int offset,
73718bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
73818bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
73918bb8381SNamhyung Kim {
74018bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
74118bb8381SNamhyung Kim 	const char *str;
74218bb8381SNamhyung Kim 
74318bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
74418bb8381SNamhyung Kim 		arg->row_offset--;
74518bb8381SNamhyung Kim 		return 0;
74618bb8381SNamhyung Kim 	}
74718bb8381SNamhyung Kim 
74818bb8381SNamhyung Kim 	alloc_str = NULL;
74918bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
75018bb8381SNamhyung Kim 				       browser->show_dso);
75118bb8381SNamhyung Kim 
75218bb8381SNamhyung Kim 	if (need_percent) {
75318bb8381SNamhyung Kim 		char buf[64];
75418bb8381SNamhyung Kim 
75518bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
75618bb8381SNamhyung Kim 						total);
75718bb8381SNamhyung Kim 
75818bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
75918bb8381SNamhyung Kim 			str = "Not enough memory!";
76018bb8381SNamhyung Kim 		else
76118bb8381SNamhyung Kim 			str = alloc_str;
76218bb8381SNamhyung Kim 	}
76318bb8381SNamhyung Kim 
76418bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
76518bb8381SNamhyung Kim 
76618bb8381SNamhyung Kim 	free(alloc_str);
76718bb8381SNamhyung Kim 	return 1;
76818bb8381SNamhyung Kim }
76918bb8381SNamhyung Kim 
77059c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
77159c624e2SNamhyung Kim {
77259c624e2SNamhyung Kim 	struct callchain_node *child;
77359c624e2SNamhyung Kim 
77459c624e2SNamhyung Kim 	if (node == NULL)
77559c624e2SNamhyung Kim 		return false;
77659c624e2SNamhyung Kim 
77759c624e2SNamhyung Kim 	if (rb_next(node))
77859c624e2SNamhyung Kim 		return true;
77959c624e2SNamhyung Kim 
78059c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
78159c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
78259c624e2SNamhyung Kim }
78359c624e2SNamhyung Kim 
7844b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
7854b3a3212SNamhyung Kim 					     struct rb_root *root,
7864b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
78759c624e2SNamhyung Kim 					     u64 parent_total,
7884b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
7894b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
7904b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
7914b3a3212SNamhyung Kim {
7924b3a3212SNamhyung Kim 	struct rb_node *node;
7934b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
7944b3a3212SNamhyung Kim 	bool need_percent;
7954b3a3212SNamhyung Kim 
7964b3a3212SNamhyung Kim 	node = rb_first(root);
79759c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
7984b3a3212SNamhyung Kim 
7994b3a3212SNamhyung Kim 	while (node) {
8004b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
8014b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
8024b3a3212SNamhyung Kim 		struct callchain_list *chain;
8034b3a3212SNamhyung Kim 		char folded_sign = ' ';
8044b3a3212SNamhyung Kim 		int first = true;
8054b3a3212SNamhyung Kim 		int extra_offset = 0;
8064b3a3212SNamhyung Kim 
8074b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
8084b3a3212SNamhyung Kim 			bool was_first = first;
8094b3a3212SNamhyung Kim 
8104b3a3212SNamhyung Kim 			if (first)
8114b3a3212SNamhyung Kim 				first = false;
8124b3a3212SNamhyung Kim 			else if (need_percent)
8134b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8144b3a3212SNamhyung Kim 
8154b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8164b3a3212SNamhyung Kim 
8174b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8184b3a3212SNamhyung Kim 							chain, row, total,
8194b3a3212SNamhyung Kim 							was_first && need_percent,
8204b3a3212SNamhyung Kim 							offset + extra_offset,
8214b3a3212SNamhyung Kim 							print, arg);
8224b3a3212SNamhyung Kim 
8234b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8244b3a3212SNamhyung Kim 				goto out;
8254b3a3212SNamhyung Kim 
8264b3a3212SNamhyung Kim 			if (folded_sign == '+')
8274b3a3212SNamhyung Kim 				goto next;
8284b3a3212SNamhyung Kim 		}
8294b3a3212SNamhyung Kim 
8304b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
8314b3a3212SNamhyung Kim 			bool was_first = first;
8324b3a3212SNamhyung Kim 
8334b3a3212SNamhyung Kim 			if (first)
8344b3a3212SNamhyung Kim 				first = false;
8354b3a3212SNamhyung Kim 			else if (need_percent)
8364b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8374b3a3212SNamhyung Kim 
8384b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8394b3a3212SNamhyung Kim 
8404b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8414b3a3212SNamhyung Kim 							chain, row, total,
8424b3a3212SNamhyung Kim 							was_first && need_percent,
8434b3a3212SNamhyung Kim 							offset + extra_offset,
8444b3a3212SNamhyung Kim 							print, arg);
8454b3a3212SNamhyung Kim 
8464b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8474b3a3212SNamhyung Kim 				goto out;
8484b3a3212SNamhyung Kim 
8494b3a3212SNamhyung Kim 			if (folded_sign == '+')
8504b3a3212SNamhyung Kim 				break;
8514b3a3212SNamhyung Kim 		}
8524b3a3212SNamhyung Kim 
8534b3a3212SNamhyung Kim next:
8544b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
8554b3a3212SNamhyung Kim 			break;
8564b3a3212SNamhyung Kim 		node = next;
8574b3a3212SNamhyung Kim 	}
8584b3a3212SNamhyung Kim out:
8594b3a3212SNamhyung Kim 	return row - first_row;
8604b3a3212SNamhyung Kim }
8614b3a3212SNamhyung Kim 
8628c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
8638c430a34SNamhyung Kim 						struct callchain_list *chain,
8648c430a34SNamhyung Kim 						char *value_str, char *old_str)
8658c430a34SNamhyung Kim {
8668c430a34SNamhyung Kim 	char bf[1024];
8678c430a34SNamhyung Kim 	const char *str;
8688c430a34SNamhyung Kim 	char *new;
8698c430a34SNamhyung Kim 
8708c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
8718c430a34SNamhyung Kim 				       browser->show_dso);
8728c430a34SNamhyung Kim 	if (old_str) {
8738c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
8748c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
8758c430a34SNamhyung Kim 			new = NULL;
8768c430a34SNamhyung Kim 	} else {
8778c430a34SNamhyung Kim 		if (value_str) {
8788c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
8798c430a34SNamhyung Kim 				new = NULL;
8808c430a34SNamhyung Kim 		} else {
8818c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
8828c430a34SNamhyung Kim 				new = NULL;
8838c430a34SNamhyung Kim 		}
8848c430a34SNamhyung Kim 	}
8858c430a34SNamhyung Kim 	return new;
8868c430a34SNamhyung Kim }
8878c430a34SNamhyung Kim 
8888c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
8898c430a34SNamhyung Kim 					       struct rb_root *root,
8908c430a34SNamhyung Kim 					       unsigned short row, u64 total,
89159c624e2SNamhyung Kim 					       u64 parent_total,
8928c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
8938c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
8948c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
8958c430a34SNamhyung Kim {
8968c430a34SNamhyung Kim 	struct rb_node *node;
8978c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
8988c430a34SNamhyung Kim 	bool need_percent;
8998c430a34SNamhyung Kim 
9008c430a34SNamhyung Kim 	node = rb_first(root);
90159c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9028c430a34SNamhyung Kim 
9038c430a34SNamhyung Kim 	while (node) {
9048c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
9058c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
9068c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
9078c430a34SNamhyung Kim 		int first = true;
9088c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
9098c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
9108c430a34SNamhyung Kim 
9118c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
9128c430a34SNamhyung Kim 			arg->row_offset--;
9138c430a34SNamhyung Kim 			goto next;
9148c430a34SNamhyung Kim 		}
9158c430a34SNamhyung Kim 
9168c430a34SNamhyung Kim 		if (need_percent) {
9178c430a34SNamhyung Kim 			char buf[64];
9188c430a34SNamhyung Kim 
9198c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
9208c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
9218c430a34SNamhyung Kim 				value_str = (char *)"<...>";
9228c430a34SNamhyung Kim 				goto do_print;
9238c430a34SNamhyung Kim 			}
9248c430a34SNamhyung Kim 			value_str_alloc = value_str;
9258c430a34SNamhyung Kim 		}
9268c430a34SNamhyung Kim 
9278c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9288c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9298c430a34SNamhyung Kim 						chain, value_str, chain_str);
9308c430a34SNamhyung Kim 			if (first) {
9318c430a34SNamhyung Kim 				first = false;
9328c430a34SNamhyung Kim 				first_chain = chain;
9338c430a34SNamhyung Kim 			}
9348c430a34SNamhyung Kim 
9358c430a34SNamhyung Kim 			if (chain_str == NULL) {
9368c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9378c430a34SNamhyung Kim 				goto do_print;
9388c430a34SNamhyung Kim 			}
9398c430a34SNamhyung Kim 
9408c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9418c430a34SNamhyung Kim 		}
9428c430a34SNamhyung Kim 
9438c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
9448c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9458c430a34SNamhyung Kim 						chain, value_str, chain_str);
9468c430a34SNamhyung Kim 			if (first) {
9478c430a34SNamhyung Kim 				first = false;
9488c430a34SNamhyung Kim 				first_chain = chain;
9498c430a34SNamhyung Kim 			}
9508c430a34SNamhyung Kim 
9518c430a34SNamhyung Kim 			if (chain_str == NULL) {
9528c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9538c430a34SNamhyung Kim 				goto do_print;
9548c430a34SNamhyung Kim 			}
9558c430a34SNamhyung Kim 
9568c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9578c430a34SNamhyung Kim 		}
9588c430a34SNamhyung Kim 
9598c430a34SNamhyung Kim do_print:
9608c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
9618c430a34SNamhyung Kim 		free(value_str_alloc);
9628c430a34SNamhyung Kim 		free(chain_str_alloc);
9638c430a34SNamhyung Kim 
9648c430a34SNamhyung Kim next:
9658c430a34SNamhyung Kim 		if (is_output_full(browser, row))
9668c430a34SNamhyung Kim 			break;
9678c430a34SNamhyung Kim 		node = next;
9688c430a34SNamhyung Kim 	}
9698c430a34SNamhyung Kim 
9708c430a34SNamhyung Kim 	return row - first_row;
9718c430a34SNamhyung Kim }
9728c430a34SNamhyung Kim 
9730c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
974c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
97539ee533fSNamhyung Kim 					unsigned short row, u64 total,
9765eca104eSNamhyung Kim 					u64 parent_total,
97739ee533fSNamhyung Kim 					print_callchain_entry_fn print,
97839ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
97939ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
980aca7a94dSNamhyung Kim {
981aca7a94dSNamhyung Kim 	struct rb_node *node;
982f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
9834087d11cSNamhyung Kim 	bool need_percent;
9845eca104eSNamhyung Kim 	u64 percent_total = total;
9855eca104eSNamhyung Kim 
9865eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
9875eca104eSNamhyung Kim 		percent_total = parent_total;
988aca7a94dSNamhyung Kim 
989c09a7e75SNamhyung Kim 	node = rb_first(root);
99059c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9914087d11cSNamhyung Kim 
992aca7a94dSNamhyung Kim 	while (node) {
993aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
994aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
995aca7a94dSNamhyung Kim 		struct callchain_list *chain;
996aca7a94dSNamhyung Kim 		char folded_sign = ' ';
997aca7a94dSNamhyung Kim 		int first = true;
998aca7a94dSNamhyung Kim 		int extra_offset = 0;
999aca7a94dSNamhyung Kim 
1000aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
1001aca7a94dSNamhyung Kim 			bool was_first = first;
1002aca7a94dSNamhyung Kim 
1003aca7a94dSNamhyung Kim 			if (first)
1004aca7a94dSNamhyung Kim 				first = false;
10054087d11cSNamhyung Kim 			else if (need_percent)
1006aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1007aca7a94dSNamhyung Kim 
1008aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1009aca7a94dSNamhyung Kim 
101018bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
10115eca104eSNamhyung Kim 							chain, row, percent_total,
101218bb8381SNamhyung Kim 							was_first && need_percent,
101318bb8381SNamhyung Kim 							offset + extra_offset,
101418bb8381SNamhyung Kim 							print, arg);
1015c09a7e75SNamhyung Kim 
101618bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1017aca7a94dSNamhyung Kim 				goto out;
101818bb8381SNamhyung Kim 
1019aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1020aca7a94dSNamhyung Kim 				break;
1021aca7a94dSNamhyung Kim 		}
1022aca7a94dSNamhyung Kim 
1023aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1024aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1025c09a7e75SNamhyung Kim 
10260c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
10275eca104eSNamhyung Kim 							    new_level, row, total,
10285eca104eSNamhyung Kim 							    child->children_hit,
102939ee533fSNamhyung Kim 							    print, arg, is_output_full);
1030aca7a94dSNamhyung Kim 		}
103139ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1032c09a7e75SNamhyung Kim 			break;
1033aca7a94dSNamhyung Kim 		node = next;
1034aca7a94dSNamhyung Kim 	}
1035aca7a94dSNamhyung Kim out:
1036aca7a94dSNamhyung Kim 	return row - first_row;
1037aca7a94dSNamhyung Kim }
1038aca7a94dSNamhyung Kim 
10390c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
10400c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
10410c841c6cSNamhyung Kim 					unsigned short row,
10420c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
10430c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
10440c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
10450c841c6cSNamhyung Kim {
10460c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
10475eca104eSNamhyung Kim 	u64 parent_total;
10480c841c6cSNamhyung Kim 	int printed;
10490c841c6cSNamhyung Kim 
10500c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
10515eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
10520c841c6cSNamhyung Kim 	else
10535eca104eSNamhyung Kim 		parent_total = entry->stat.period;
10540c841c6cSNamhyung Kim 
10550c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
10560c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
10575eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10585eca104eSNamhyung Kim 						total, parent_total, print, arg,
10595eca104eSNamhyung Kim 						is_output_full);
10600c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
10610c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
10625eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10635eca104eSNamhyung Kim 						total, parent_total, print, arg,
10645eca104eSNamhyung Kim 						is_output_full);
10650c841c6cSNamhyung Kim 	} else {
10660c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
10675eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
10685eca104eSNamhyung Kim 						total, parent_total, print, arg,
10695eca104eSNamhyung Kim 						is_output_full);
10700c841c6cSNamhyung Kim 	}
10710c841c6cSNamhyung Kim 
10720c841c6cSNamhyung Kim 	if (arg->is_current_entry)
10730c841c6cSNamhyung Kim 		browser->he_selection = entry;
10740c841c6cSNamhyung Kim 
10750c841c6cSNamhyung Kim 	return printed;
10760c841c6cSNamhyung Kim }
10770c841c6cSNamhyung Kim 
107889701460SNamhyung Kim struct hpp_arg {
107989701460SNamhyung Kim 	struct ui_browser *b;
108089701460SNamhyung Kim 	char folded_sign;
108189701460SNamhyung Kim 	bool current_entry;
108289701460SNamhyung Kim };
108389701460SNamhyung Kim 
108498ba1609SJiri Olsa int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
10852f6d9009SNamhyung Kim {
10862f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1087d675107cSNamhyung Kim 	int ret, len;
10882f6d9009SNamhyung Kim 	va_list args;
10892f6d9009SNamhyung Kim 	double percent;
10902f6d9009SNamhyung Kim 
10912f6d9009SNamhyung Kim 	va_start(args, fmt);
1092d675107cSNamhyung Kim 	len = va_arg(args, int);
10932f6d9009SNamhyung Kim 	percent = va_arg(args, double);
10942f6d9009SNamhyung Kim 	va_end(args);
10955aed9d24SNamhyung Kim 
109689701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
10975aed9d24SNamhyung Kim 
1098d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1099517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
110089701460SNamhyung Kim 
11015aed9d24SNamhyung Kim 	return ret;
1102f5951d56SNamhyung Kim }
1103f5951d56SNamhyung Kim 
1104fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
11055aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
11065aed9d24SNamhyung Kim {									\
11075aed9d24SNamhyung Kim 	return he->stat._field;						\
11085aed9d24SNamhyung Kim }									\
11095aed9d24SNamhyung Kim 									\
11102c5d4b4aSJiri Olsa static int								\
11115b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11122c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
11135aed9d24SNamhyung Kim 				struct hist_entry *he)			\
11145aed9d24SNamhyung Kim {									\
11155b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
11162f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
11175aed9d24SNamhyung Kim }
1118f5951d56SNamhyung Kim 
11190434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
11200434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
11210434ddd2SNamhyung Kim {									\
11220434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
11230434ddd2SNamhyung Kim }									\
11240434ddd2SNamhyung Kim 									\
11250434ddd2SNamhyung Kim static int								\
11265b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11270434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
11280434ddd2SNamhyung Kim 				struct hist_entry *he)			\
11290434ddd2SNamhyung Kim {									\
11300434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1131517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
11325b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1133d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
11345b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1135517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
11360434ddd2SNamhyung Kim 									\
11370434ddd2SNamhyung Kim 		return ret;						\
11380434ddd2SNamhyung Kim 	}								\
11395b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
11405b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
11410434ddd2SNamhyung Kim }
11420434ddd2SNamhyung Kim 
1143fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1144fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1145fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1146fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1147fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
11480434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
11495aed9d24SNamhyung Kim 
11505aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
11510434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1152f5951d56SNamhyung Kim 
1153f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1154f5951d56SNamhyung Kim {
1155f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1156f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1157f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1158f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1159f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1160f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1161f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1162f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1163f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1164f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
11650434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
11660434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
1167f5951d56SNamhyung Kim }
1168f5951d56SNamhyung Kim 
116905e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1170aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1171aca7a94dSNamhyung Kim 				    unsigned short row)
1172aca7a94dSNamhyung Kim {
11731240005eSJiri Olsa 	int printed = 0;
117467d25916SNamhyung Kim 	int width = browser->b.width;
1175aca7a94dSNamhyung Kim 	char folded_sign = ' ';
117605e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1177aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
117863a1a3d8SNamhyung Kim 	bool first = true;
11791240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1180aca7a94dSNamhyung Kim 
1181aca7a94dSNamhyung Kim 	if (current_entry) {
118205e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
118305e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1184aca7a94dSNamhyung Kim 	}
1185aca7a94dSNamhyung Kim 
1186aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
1187aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1188aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1189aca7a94dSNamhyung Kim 	}
1190aca7a94dSNamhyung Kim 
1191aca7a94dSNamhyung Kim 	if (row_offset == 0) {
119289701460SNamhyung Kim 		struct hpp_arg arg = {
119389701460SNamhyung Kim 			.b		= &browser->b,
119489701460SNamhyung Kim 			.folded_sign	= folded_sign,
119589701460SNamhyung Kim 			.current_entry	= current_entry,
119689701460SNamhyung Kim 		};
1197c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1198f5951d56SNamhyung Kim 
1199ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
1200f5951d56SNamhyung Kim 
1201f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
120289fee709SArnaldo Carvalho de Melo 			char s[2048];
120389fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
120489fee709SArnaldo Carvalho de Melo 				.buf	= s,
120589fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
120689fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
120789fee709SArnaldo Carvalho de Melo 			};
120889fee709SArnaldo Carvalho de Melo 
1209361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1210361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1211e67d49a7SNamhyung Kim 				continue;
1212e67d49a7SNamhyung Kim 
1213fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1214fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1215fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1216fb821c9eSNamhyung Kim 			} else {
1217fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1218fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1219fb821c9eSNamhyung Kim 			}
1220fb821c9eSNamhyung Kim 
1221fb821c9eSNamhyung Kim 			if (first) {
1222fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
1223517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1224f5951d56SNamhyung Kim 					width -= 2;
1225f5951d56SNamhyung Kim 				}
122663a1a3d8SNamhyung Kim 				first = false;
1227fb821c9eSNamhyung Kim 			} else {
1228517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1229fb821c9eSNamhyung Kim 				width -= 2;
1230fb821c9eSNamhyung Kim 			}
1231f5951d56SNamhyung Kim 
12321240005eSJiri Olsa 			if (fmt->color) {
123389fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
123489fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
123589fee709SArnaldo Carvalho de Melo 				/*
123689fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
123789fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
123889fee709SArnaldo Carvalho de Melo 				 */
123989fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1240f5951d56SNamhyung Kim 			} else {
124189fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1242517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1243f5951d56SNamhyung Kim 			}
124489fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1245f5951d56SNamhyung Kim 		}
1246aca7a94dSNamhyung Kim 
1247aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
124805e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1249aca7a94dSNamhyung Kim 			width += 1;
1250aca7a94dSNamhyung Kim 
125126270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
125226d8b338SNamhyung Kim 
1253aca7a94dSNamhyung Kim 		++row;
1254aca7a94dSNamhyung Kim 		++printed;
1255aca7a94dSNamhyung Kim 	} else
1256aca7a94dSNamhyung Kim 		--row_offset;
1257aca7a94dSNamhyung Kim 
125862c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
125939ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
126039ee533fSNamhyung Kim 			.row_offset = row_offset,
126139ee533fSNamhyung Kim 			.is_current_entry = current_entry,
126239ee533fSNamhyung Kim 		};
1263c09a7e75SNamhyung Kim 
12640c841c6cSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry, 1, row,
12654b3a3212SNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
12664b3a3212SNamhyung Kim 					hist_browser__check_output_full);
1267aca7a94dSNamhyung Kim 	}
1268aca7a94dSNamhyung Kim 
1269aca7a94dSNamhyung Kim 	return printed;
1270aca7a94dSNamhyung Kim }
1271aca7a94dSNamhyung Kim 
1272d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1273d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1274d0506edbSNamhyung Kim 					      unsigned short row,
12752dbbe9f2SNamhyung Kim 					      int level)
1276d0506edbSNamhyung Kim {
1277d0506edbSNamhyung Kim 	int printed = 0;
1278d0506edbSNamhyung Kim 	int width = browser->b.width;
1279d0506edbSNamhyung Kim 	char folded_sign = ' ';
1280d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1281d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1282d0506edbSNamhyung Kim 	bool first = true;
1283d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1284a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1285d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1286d0506edbSNamhyung Kim 		.b		= &browser->b,
1287d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1288d0506edbSNamhyung Kim 	};
1289d0506edbSNamhyung Kim 	int column = 0;
12902dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1291d0506edbSNamhyung Kim 
1292d0506edbSNamhyung Kim 	if (current_entry) {
1293d0506edbSNamhyung Kim 		browser->he_selection = entry;
1294d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1295d0506edbSNamhyung Kim 	}
1296d0506edbSNamhyung Kim 
1297d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1298d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1299d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1300d0506edbSNamhyung Kim 
1301d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1302d0506edbSNamhyung Kim 		row_offset--;
1303d0506edbSNamhyung Kim 		goto show_callchain;
1304d0506edbSNamhyung Kim 	}
1305d0506edbSNamhyung Kim 
1306d0506edbSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
1307d0506edbSNamhyung Kim 
1308d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1309d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1310d0506edbSNamhyung Kim 	else
1311d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1312d0506edbSNamhyung Kim 
1313d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1314d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1315d0506edbSNamhyung Kim 
1316a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1317a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1318a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1319a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1320d0506edbSNamhyung Kim 		char s[2048];
1321d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1322d0506edbSNamhyung Kim 			.buf		= s,
1323d0506edbSNamhyung Kim 			.size		= sizeof(s),
1324d0506edbSNamhyung Kim 			.ptr		= &arg,
1325d0506edbSNamhyung Kim 		};
1326d0506edbSNamhyung Kim 
1327d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1328d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1329d0506edbSNamhyung Kim 			continue;
1330d0506edbSNamhyung Kim 
1331d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1332d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1333d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1334d0506edbSNamhyung Kim 		} else {
1335d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1336d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1337d0506edbSNamhyung Kim 		}
1338d0506edbSNamhyung Kim 
1339d0506edbSNamhyung Kim 		if (first) {
1340d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c", folded_sign);
1341d0506edbSNamhyung Kim 			width--;
1342d0506edbSNamhyung Kim 			first = false;
1343d0506edbSNamhyung Kim 		} else {
1344d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1345d0506edbSNamhyung Kim 			width -= 2;
1346d0506edbSNamhyung Kim 		}
1347d0506edbSNamhyung Kim 
1348d0506edbSNamhyung Kim 		if (fmt->color) {
1349d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1350d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1351d0506edbSNamhyung Kim 			/*
1352d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1353d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1354d0506edbSNamhyung Kim 			 */
1355d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1356d0506edbSNamhyung Kim 		} else {
1357d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1358d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1359d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1360d0506edbSNamhyung Kim 		}
1361d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1362d0506edbSNamhyung Kim 	}
1363d0506edbSNamhyung Kim 
1364d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1365d0506edbSNamhyung Kim 	width -= hierarchy_indent;
1366d0506edbSNamhyung Kim 
1367d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1368d0506edbSNamhyung Kim 		char s[2048];
1369d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1370d0506edbSNamhyung Kim 			.buf		= s,
1371d0506edbSNamhyung Kim 			.size		= sizeof(s),
1372d0506edbSNamhyung Kim 			.ptr		= &arg,
1373d0506edbSNamhyung Kim 		};
1374d0506edbSNamhyung Kim 
1375d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1376d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1377d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1378d0506edbSNamhyung Kim 		} else {
1379d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1380d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1381d0506edbSNamhyung Kim 		}
1382d0506edbSNamhyung Kim 
13831b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1384d0506edbSNamhyung Kim 			ui_browser__write_nstring(&browser->b, "", 2);
1385d0506edbSNamhyung Kim 			width -= 2;
1386d0506edbSNamhyung Kim 
1387d0506edbSNamhyung Kim 			/*
1388d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1389d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1390d0506edbSNamhyung Kim 			 * hierarchy mode.
1391d0506edbSNamhyung Kim 			 */
1392d0506edbSNamhyung Kim 			if (fmt->color) {
1393d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1394d0506edbSNamhyung Kim 			} else {
1395cb1fab91SNamhyung Kim 				int i = 0;
1396cb1fab91SNamhyung Kim 
1397d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
1398cb1fab91SNamhyung Kim 				ui_browser__printf(&browser->b, "%s", ltrim(s));
1399cb1fab91SNamhyung Kim 
1400cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1401cb1fab91SNamhyung Kim 					width++;
1402d0506edbSNamhyung Kim 			}
1403d0506edbSNamhyung Kim 		}
14041b2dbbf4SNamhyung Kim 	}
1405d0506edbSNamhyung Kim 
1406d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1407d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1408d0506edbSNamhyung Kim 		width += 1;
1409d0506edbSNamhyung Kim 
1410d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1411d0506edbSNamhyung Kim 
1412d0506edbSNamhyung Kim 	++row;
1413d0506edbSNamhyung Kim 	++printed;
1414d0506edbSNamhyung Kim 
1415d0506edbSNamhyung Kim show_callchain:
1416d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1417d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1418d0506edbSNamhyung Kim 			.row_offset = row_offset,
1419d0506edbSNamhyung Kim 		};
1420d0506edbSNamhyung Kim 
1421d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1422d0506edbSNamhyung Kim 					level + 1, row,
1423d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1424d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1425d0506edbSNamhyung Kim 	}
1426d0506edbSNamhyung Kim 
1427d0506edbSNamhyung Kim 	return printed;
1428d0506edbSNamhyung Kim }
1429d0506edbSNamhyung Kim 
143079dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
14312dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
143279dded87SNamhyung Kim {
143379dded87SNamhyung Kim 	int width = browser->b.width;
143479dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
143579dded87SNamhyung Kim 	bool first = true;
143679dded87SNamhyung Kim 	int column = 0;
143779dded87SNamhyung Kim 	int ret;
143879dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1439a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
14402dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
144179dded87SNamhyung Kim 
144279dded87SNamhyung Kim 	if (current_entry) {
144379dded87SNamhyung Kim 		browser->he_selection = NULL;
144479dded87SNamhyung Kim 		browser->selection = NULL;
144579dded87SNamhyung Kim 	}
144679dded87SNamhyung Kim 
144779dded87SNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
144879dded87SNamhyung Kim 
144979dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
145079dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
145179dded87SNamhyung Kim 	else
145279dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
145379dded87SNamhyung Kim 
145479dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
145579dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
145679dded87SNamhyung Kim 
1457a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1458a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1459a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1460a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
146179dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
146279dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
146379dded87SNamhyung Kim 			continue;
146479dded87SNamhyung Kim 
1465da1b0407SJiri Olsa 		ret = fmt->width(fmt, NULL, browser->hists);
146679dded87SNamhyung Kim 
146779dded87SNamhyung Kim 		if (first) {
146879dded87SNamhyung Kim 			/* for folded sign */
146979dded87SNamhyung Kim 			first = false;
147079dded87SNamhyung Kim 			ret++;
147179dded87SNamhyung Kim 		} else {
147279dded87SNamhyung Kim 			/* space between columns */
147379dded87SNamhyung Kim 			ret += 2;
147479dded87SNamhyung Kim 		}
147579dded87SNamhyung Kim 
147679dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
147779dded87SNamhyung Kim 		width -= ret;
147879dded87SNamhyung Kim 	}
147979dded87SNamhyung Kim 
14802dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
14812dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
148279dded87SNamhyung Kim 
148379dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
148479dded87SNamhyung Kim 		char buf[32];
148579dded87SNamhyung Kim 
148679dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
148779dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
148879dded87SNamhyung Kim 		width -= ret + 2;
148979dded87SNamhyung Kim 	}
149079dded87SNamhyung Kim 
149179dded87SNamhyung Kim 	/* The scroll bar isn't being used */
149279dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
149379dded87SNamhyung Kim 		width += 1;
149479dded87SNamhyung Kim 
149579dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
149679dded87SNamhyung Kim 	return 1;
149779dded87SNamhyung Kim }
149879dded87SNamhyung Kim 
149981a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
150081a888feSJiri Olsa {
150181a888feSJiri Olsa 	advance_hpp(hpp, inc);
150281a888feSJiri Olsa 	return hpp->size <= 0;
150381a888feSJiri Olsa }
150481a888feSJiri Olsa 
150569705b35SJiri Olsa static int
150669705b35SJiri Olsa hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
150769705b35SJiri Olsa 				 size_t size, int line)
150881a888feSJiri Olsa {
1509c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
151081a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
151181a888feSJiri Olsa 		.buf    = buf,
151281a888feSJiri Olsa 		.size   = size,
151381a888feSJiri Olsa 	};
151481a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
151581a888feSJiri Olsa 	size_t ret = 0;
1516c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
151729659ab4SJiri Olsa 	int span = 0;
151881a888feSJiri Olsa 
151981a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
152081a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
152181a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
152281a888feSJiri Olsa 			return ret;
152381a888feSJiri Olsa 	}
152481a888feSJiri Olsa 
1525f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1526361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
152781a888feSJiri Olsa 			continue;
152881a888feSJiri Olsa 
152929659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
153081a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
153181a888feSJiri Olsa 			break;
153281a888feSJiri Olsa 
153329659ab4SJiri Olsa 		if (span)
153429659ab4SJiri Olsa 			continue;
153529659ab4SJiri Olsa 
153681a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
153781a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
153881a888feSJiri Olsa 			break;
153981a888feSJiri Olsa 	}
154081a888feSJiri Olsa 
154181a888feSJiri Olsa 	return ret;
154281a888feSJiri Olsa }
154381a888feSJiri Olsa 
1544d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1545d8b92400SNamhyung Kim {
1546d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1547d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1548d8b92400SNamhyung Kim 		.buf    = buf,
1549d8b92400SNamhyung Kim 		.size   = size,
1550d8b92400SNamhyung Kim 	};
1551d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1552a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1553d8b92400SNamhyung Kim 	size_t ret = 0;
1554d8b92400SNamhyung Kim 	int column = 0;
15552dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1556a61a22f6SNamhyung Kim 	bool first_node, first_col;
1557d8b92400SNamhyung Kim 
1558d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, " ");
1559d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1560d8b92400SNamhyung Kim 		return ret;
1561d8b92400SNamhyung Kim 
1562a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1563a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1564a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1565a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1566d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1567d8b92400SNamhyung Kim 			continue;
1568d8b92400SNamhyung Kim 
156929659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1570d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1571d8b92400SNamhyung Kim 			break;
1572d8b92400SNamhyung Kim 
1573d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1574d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1575d8b92400SNamhyung Kim 			break;
1576d8b92400SNamhyung Kim 	}
1577d8b92400SNamhyung Kim 
1578d8b92400SNamhyung Kim 	ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
15792dbbe9f2SNamhyung Kim 			indent * HIERARCHY_INDENT, "");
1580d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1581d8b92400SNamhyung Kim 		return ret;
1582d8b92400SNamhyung Kim 
1583a61a22f6SNamhyung Kim 	first_node = true;
1584a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1585a61a22f6SNamhyung Kim 		if (!first_node) {
1586d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1587d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1588d8b92400SNamhyung Kim 				break;
1589d8b92400SNamhyung Kim 		}
1590a61a22f6SNamhyung Kim 		first_node = false;
1591a61a22f6SNamhyung Kim 
1592a61a22f6SNamhyung Kim 		first_col = true;
1593a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1594a61a22f6SNamhyung Kim 			char *start;
1595a61a22f6SNamhyung Kim 
1596a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1597a61a22f6SNamhyung Kim 				continue;
1598a61a22f6SNamhyung Kim 
1599a61a22f6SNamhyung Kim 			if (!first_col) {
1600a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1601a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1602a61a22f6SNamhyung Kim 					break;
1603a61a22f6SNamhyung Kim 			}
1604a61a22f6SNamhyung Kim 			first_col = false;
1605d8b92400SNamhyung Kim 
160629659ab4SJiri Olsa 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1607d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1608d8b92400SNamhyung Kim 
16097d6a7e78SJiri Olsa 			start = trim(dummy_hpp.buf);
1610cb1fab91SNamhyung Kim 			ret = strlen(start);
1611cb1fab91SNamhyung Kim 
1612cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1613cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1614cb1fab91SNamhyung Kim 
1615d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1616d8b92400SNamhyung Kim 				break;
1617d8b92400SNamhyung Kim 		}
1618a61a22f6SNamhyung Kim 	}
1619d8b92400SNamhyung Kim 
1620d8b92400SNamhyung Kim 	return ret;
1621d8b92400SNamhyung Kim }
1622d8b92400SNamhyung Kim 
162301b4770dSJiri Olsa static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1624025bf7eaSArnaldo Carvalho de Melo {
162581a888feSJiri Olsa 	char headers[1024];
162681a888feSJiri Olsa 
1627d8b92400SNamhyung Kim 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1628d8b92400SNamhyung Kim 						   sizeof(headers));
162901b4770dSJiri Olsa 
1630025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1631025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
163226270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1633025bf7eaSArnaldo Carvalho de Melo }
1634025bf7eaSArnaldo Carvalho de Melo 
163501b4770dSJiri Olsa static void hists_browser__headers(struct hist_browser *browser)
163601b4770dSJiri Olsa {
163769705b35SJiri Olsa 	struct hists *hists = browser->hists;
163869705b35SJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
163969705b35SJiri Olsa 
164069705b35SJiri Olsa 	int line;
164169705b35SJiri Olsa 
164269705b35SJiri Olsa 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
164301b4770dSJiri Olsa 		char headers[1024];
164401b4770dSJiri Olsa 
164501b4770dSJiri Olsa 		hists_browser__scnprintf_headers(browser, headers,
164669705b35SJiri Olsa 						 sizeof(headers), line);
164701b4770dSJiri Olsa 
164869705b35SJiri Olsa 		ui_browser__gotorc(&browser->b, line, 0);
164901b4770dSJiri Olsa 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
165001b4770dSJiri Olsa 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
165101b4770dSJiri Olsa 	}
165269705b35SJiri Olsa }
165301b4770dSJiri Olsa 
165401b4770dSJiri Olsa static void hist_browser__show_headers(struct hist_browser *browser)
165501b4770dSJiri Olsa {
165601b4770dSJiri Olsa 	if (symbol_conf.report_hierarchy)
165701b4770dSJiri Olsa 		hists_browser__hierarchy_headers(browser);
165801b4770dSJiri Olsa 	else
165901b4770dSJiri Olsa 		hists_browser__headers(browser);
166001b4770dSJiri Olsa }
166101b4770dSJiri Olsa 
1662aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1663aca7a94dSNamhyung Kim {
1664aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1665aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1666aca7a94dSNamhyung Kim 
1667aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
1668aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
1669aca7a94dSNamhyung Kim 	}
1670aca7a94dSNamhyung Kim }
1671aca7a94dSNamhyung Kim 
167205e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1673aca7a94dSNamhyung Kim {
1674aca7a94dSNamhyung Kim 	unsigned row = 0;
1675025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
1676aca7a94dSNamhyung Kim 	struct rb_node *nd;
167705e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1678f8e6710dSJiri Olsa 	struct hists *hists = hb->hists;
1679aca7a94dSNamhyung Kim 
1680025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
1681f8e6710dSJiri Olsa 		struct perf_hpp_list *hpp_list = hists->hpp_list;
1682f8e6710dSJiri Olsa 
1683025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1684f8e6710dSJiri Olsa 		header_offset = hpp_list->nr_header_lines;
1685025bf7eaSArnaldo Carvalho de Melo 	}
1686025bf7eaSArnaldo Carvalho de Melo 
168705e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1688979d2cacSWang Nan 	hb->he_selection = NULL;
1689979d2cacSWang Nan 	hb->selection = NULL;
1690aca7a94dSNamhyung Kim 
1691d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1692aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
169314135663SNamhyung Kim 		float percent;
1694aca7a94dSNamhyung Kim 
1695d0506edbSNamhyung Kim 		if (h->filtered) {
1696d0506edbSNamhyung Kim 			/* let it move to sibling */
1697d0506edbSNamhyung Kim 			h->unfolded = false;
1698aca7a94dSNamhyung Kim 			continue;
1699d0506edbSNamhyung Kim 		}
1700aca7a94dSNamhyung Kim 
170114135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
1702064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1703064f1981SNamhyung Kim 			continue;
1704064f1981SNamhyung Kim 
1705d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1706d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
17072dbbe9f2SNamhyung Kim 								  h->depth);
170879dded87SNamhyung Kim 			if (row == browser->rows)
170979dded87SNamhyung Kim 				break;
171079dded87SNamhyung Kim 
171179dded87SNamhyung Kim 			if (h->has_no_entry) {
1712a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
171379dded87SNamhyung Kim 				row++;
171479dded87SNamhyung Kim 			}
1715d0506edbSNamhyung Kim 		} else {
1716aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1717d0506edbSNamhyung Kim 		}
1718d0506edbSNamhyung Kim 
171962c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1720aca7a94dSNamhyung Kim 			break;
1721aca7a94dSNamhyung Kim 	}
1722aca7a94dSNamhyung Kim 
1723025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
1724aca7a94dSNamhyung Kim }
1725aca7a94dSNamhyung Kim 
1726064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1727064f1981SNamhyung Kim 					     float min_pcnt)
1728aca7a94dSNamhyung Kim {
1729aca7a94dSNamhyung Kim 	while (nd != NULL) {
1730aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
173114135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1732064f1981SNamhyung Kim 
1733c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1734aca7a94dSNamhyung Kim 			return nd;
1735aca7a94dSNamhyung Kim 
1736d0506edbSNamhyung Kim 		/*
1737d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1738d0506edbSNamhyung Kim 		 * So move to sibling node.
1739d0506edbSNamhyung Kim 		 */
1740d0506edbSNamhyung Kim 		if (rb_next(nd))
1741aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1742d0506edbSNamhyung Kim 		else
1743d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1744aca7a94dSNamhyung Kim 	}
1745aca7a94dSNamhyung Kim 
1746aca7a94dSNamhyung Kim 	return NULL;
1747aca7a94dSNamhyung Kim }
1748aca7a94dSNamhyung Kim 
1749064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1750064f1981SNamhyung Kim 						  float min_pcnt)
1751aca7a94dSNamhyung Kim {
1752aca7a94dSNamhyung Kim 	while (nd != NULL) {
1753aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
175414135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1755064f1981SNamhyung Kim 
1756064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1757aca7a94dSNamhyung Kim 			return nd;
1758aca7a94dSNamhyung Kim 
1759d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1760aca7a94dSNamhyung Kim 	}
1761aca7a94dSNamhyung Kim 
1762aca7a94dSNamhyung Kim 	return NULL;
1763aca7a94dSNamhyung Kim }
1764aca7a94dSNamhyung Kim 
176505e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1766aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1767aca7a94dSNamhyung Kim {
1768aca7a94dSNamhyung Kim 	struct hist_entry *h;
1769aca7a94dSNamhyung Kim 	struct rb_node *nd;
1770aca7a94dSNamhyung Kim 	bool first = true;
1771064f1981SNamhyung Kim 	struct hist_browser *hb;
1772064f1981SNamhyung Kim 
1773064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1774aca7a94dSNamhyung Kim 
177505e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1776aca7a94dSNamhyung Kim 		return;
1777aca7a94dSNamhyung Kim 
177805e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1779aca7a94dSNamhyung Kim 
1780aca7a94dSNamhyung Kim 	switch (whence) {
1781aca7a94dSNamhyung Kim 	case SEEK_SET:
1782064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
178314135663SNamhyung Kim 					   hb->min_pcnt);
1784aca7a94dSNamhyung Kim 		break;
1785aca7a94dSNamhyung Kim 	case SEEK_CUR:
178605e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1787aca7a94dSNamhyung Kim 		goto do_offset;
1788aca7a94dSNamhyung Kim 	case SEEK_END:
1789d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1790d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1791aca7a94dSNamhyung Kim 		first = false;
1792aca7a94dSNamhyung Kim 		break;
1793aca7a94dSNamhyung Kim 	default:
1794aca7a94dSNamhyung Kim 		return;
1795aca7a94dSNamhyung Kim 	}
1796aca7a94dSNamhyung Kim 
1797aca7a94dSNamhyung Kim 	/*
1798aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1799aca7a94dSNamhyung Kim 	 * row_offset:
1800aca7a94dSNamhyung Kim 	 */
180105e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1802aca7a94dSNamhyung Kim 	h->row_offset = 0;
1803aca7a94dSNamhyung Kim 
1804aca7a94dSNamhyung Kim 	/*
1805aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1806aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1807aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1808aca7a94dSNamhyung Kim 	 *
1809aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1810aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1811aca7a94dSNamhyung Kim 	 *
1812aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1813aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1814aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1815aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1816aca7a94dSNamhyung Kim 	 */
1817aca7a94dSNamhyung Kim do_offset:
1818837eeb75SWang Nan 	if (!nd)
1819837eeb75SWang Nan 		return;
1820837eeb75SWang Nan 
1821aca7a94dSNamhyung Kim 	if (offset > 0) {
1822aca7a94dSNamhyung Kim 		do {
1823aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1824d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1825aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1826aca7a94dSNamhyung Kim 				if (offset > remaining) {
1827aca7a94dSNamhyung Kim 					offset -= remaining;
1828aca7a94dSNamhyung Kim 					h->row_offset = 0;
1829aca7a94dSNamhyung Kim 				} else {
1830aca7a94dSNamhyung Kim 					h->row_offset += offset;
1831aca7a94dSNamhyung Kim 					offset = 0;
183205e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1833aca7a94dSNamhyung Kim 					break;
1834aca7a94dSNamhyung Kim 				}
1835aca7a94dSNamhyung Kim 			}
1836d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1837d0506edbSNamhyung Kim 						   hb->min_pcnt);
1838aca7a94dSNamhyung Kim 			if (nd == NULL)
1839aca7a94dSNamhyung Kim 				break;
1840aca7a94dSNamhyung Kim 			--offset;
184105e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1842aca7a94dSNamhyung Kim 		} while (offset != 0);
1843aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1844aca7a94dSNamhyung Kim 		while (1) {
1845aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1846d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1847aca7a94dSNamhyung Kim 				if (first) {
1848aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1849aca7a94dSNamhyung Kim 						offset += h->row_offset;
1850aca7a94dSNamhyung Kim 						h->row_offset = 0;
1851aca7a94dSNamhyung Kim 					} else {
1852aca7a94dSNamhyung Kim 						h->row_offset += offset;
1853aca7a94dSNamhyung Kim 						offset = 0;
185405e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1855aca7a94dSNamhyung Kim 						break;
1856aca7a94dSNamhyung Kim 					}
1857aca7a94dSNamhyung Kim 				} else {
1858aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1859aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1860aca7a94dSNamhyung Kim 						h->row_offset = 0;
1861aca7a94dSNamhyung Kim 					} else {
1862aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1863aca7a94dSNamhyung Kim 						offset = 0;
186405e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1865aca7a94dSNamhyung Kim 						break;
1866aca7a94dSNamhyung Kim 					}
1867aca7a94dSNamhyung Kim 				}
1868aca7a94dSNamhyung Kim 			}
1869aca7a94dSNamhyung Kim 
1870d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1871064f1981SNamhyung Kim 							hb->min_pcnt);
1872aca7a94dSNamhyung Kim 			if (nd == NULL)
1873aca7a94dSNamhyung Kim 				break;
1874aca7a94dSNamhyung Kim 			++offset;
187505e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1876aca7a94dSNamhyung Kim 			if (offset == 0) {
1877aca7a94dSNamhyung Kim 				/*
1878aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1879aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1880aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1881aca7a94dSNamhyung Kim 				 */
1882aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
1883d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
1884aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1885aca7a94dSNamhyung Kim 				break;
1886aca7a94dSNamhyung Kim 			}
1887aca7a94dSNamhyung Kim 			first = false;
1888aca7a94dSNamhyung Kim 		}
1889aca7a94dSNamhyung Kim 	} else {
189005e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1891aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1892aca7a94dSNamhyung Kim 		h->row_offset = 0;
1893aca7a94dSNamhyung Kim 	}
1894aca7a94dSNamhyung Kim }
1895aca7a94dSNamhyung Kim 
1896aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1897d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
1898d0506edbSNamhyung Kim 					   int level)
1899aff3f3f6SArnaldo Carvalho de Melo {
190039ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
190139ee533fSNamhyung Kim 		.fp = fp,
190239ee533fSNamhyung Kim 	};
1903aff3f3f6SArnaldo Carvalho de Melo 
1904d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
190539ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
190639ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
190739ee533fSNamhyung Kim 	return arg.printed;
1908aff3f3f6SArnaldo Carvalho de Melo }
1909aff3f3f6SArnaldo Carvalho de Melo 
1910aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1911aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1912aff3f3f6SArnaldo Carvalho de Melo {
1913aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1914aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1915aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
191626d8b338SNamhyung Kim 	struct perf_hpp hpp = {
191726d8b338SNamhyung Kim 		.buf = s,
191826d8b338SNamhyung Kim 		.size = sizeof(s),
191926d8b338SNamhyung Kim 	};
192026d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
192126d8b338SNamhyung Kim 	bool first = true;
192226d8b338SNamhyung Kim 	int ret;
1923aff3f3f6SArnaldo Carvalho de Melo 
19241b6b678eSArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain) {
1925aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1926aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
19271b6b678eSArnaldo Carvalho de Melo 	}
1928aff3f3f6SArnaldo Carvalho de Melo 
1929f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1930361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
1931e67d49a7SNamhyung Kim 			continue;
1932e67d49a7SNamhyung Kim 
193326d8b338SNamhyung Kim 		if (!first) {
193426d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
193526d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
193626d8b338SNamhyung Kim 		} else
193726d8b338SNamhyung Kim 			first = false;
1938aff3f3f6SArnaldo Carvalho de Melo 
193926d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
194089fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
194126d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
194226d8b338SNamhyung Kim 	}
194389fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
1944aff3f3f6SArnaldo Carvalho de Melo 
1945aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1946d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1947d0506edbSNamhyung Kim 
1948d0506edbSNamhyung Kim 	return printed;
1949d0506edbSNamhyung Kim }
1950d0506edbSNamhyung Kim 
1951d0506edbSNamhyung Kim 
1952d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1953d0506edbSNamhyung Kim 						 struct hist_entry *he,
1954325a6283SNamhyung Kim 						 FILE *fp, int level)
1955d0506edbSNamhyung Kim {
1956d0506edbSNamhyung Kim 	char s[8192];
1957d0506edbSNamhyung Kim 	int printed = 0;
1958d0506edbSNamhyung Kim 	char folded_sign = ' ';
1959d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
1960d0506edbSNamhyung Kim 		.buf = s,
1961d0506edbSNamhyung Kim 		.size = sizeof(s),
1962d0506edbSNamhyung Kim 	};
1963d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1964325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1965d0506edbSNamhyung Kim 	bool first = true;
1966d0506edbSNamhyung Kim 	int ret;
1967325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1968d0506edbSNamhyung Kim 
1969d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1970d0506edbSNamhyung Kim 
1971d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
1972d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
1973d0506edbSNamhyung Kim 
1974325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1975325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
1976325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
1977325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1978d0506edbSNamhyung Kim 		if (!first) {
1979d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1980d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
1981d0506edbSNamhyung Kim 		} else
1982d0506edbSNamhyung Kim 			first = false;
1983d0506edbSNamhyung Kim 
1984d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
1985d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
1986d0506edbSNamhyung Kim 	}
1987d0506edbSNamhyung Kim 
1988d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1989d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
1990d0506edbSNamhyung Kim 
19911b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
19921b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
19931b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
19941b2dbbf4SNamhyung Kim 
1995d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
1996d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
19971b2dbbf4SNamhyung Kim 	}
1998d0506edbSNamhyung Kim 
1999d0506edbSNamhyung Kim 	printed += fprintf(fp, "%s\n", rtrim(s));
2000d0506edbSNamhyung Kim 
2001d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
2002d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2003d0506edbSNamhyung Kim 							   he->depth + 1);
2004d0506edbSNamhyung Kim 	}
2005aff3f3f6SArnaldo Carvalho de Melo 
2006aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2007aff3f3f6SArnaldo Carvalho de Melo }
2008aff3f3f6SArnaldo Carvalho de Melo 
2009aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2010aff3f3f6SArnaldo Carvalho de Melo {
2011064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2012064f1981SNamhyung Kim 						   browser->min_pcnt);
2013aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2014aff3f3f6SArnaldo Carvalho de Melo 
2015aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
2016aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2017aff3f3f6SArnaldo Carvalho de Melo 
2018d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
2019d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2020d0506edbSNamhyung Kim 									 h, fp,
2021325a6283SNamhyung Kim 									 h->depth);
2022d0506edbSNamhyung Kim 		} else {
2023aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
2024d0506edbSNamhyung Kim 		}
2025d0506edbSNamhyung Kim 
2026d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2027d0506edbSNamhyung Kim 					   browser->min_pcnt);
2028aff3f3f6SArnaldo Carvalho de Melo 	}
2029aff3f3f6SArnaldo Carvalho de Melo 
2030aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2031aff3f3f6SArnaldo Carvalho de Melo }
2032aff3f3f6SArnaldo Carvalho de Melo 
2033aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2034aff3f3f6SArnaldo Carvalho de Melo {
2035aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2036aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2037aff3f3f6SArnaldo Carvalho de Melo 
2038aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2039aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2040aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2041aff3f3f6SArnaldo Carvalho de Melo 			break;
2042aff3f3f6SArnaldo Carvalho de Melo 		/*
2043aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2044aff3f3f6SArnaldo Carvalho de Melo  		 */
2045aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2046aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2047aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2048aff3f3f6SArnaldo Carvalho de Melo 		}
2049aff3f3f6SArnaldo Carvalho de Melo 	}
2050aff3f3f6SArnaldo Carvalho de Melo 
2051aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2052aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2053aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
2054c8b5f2c9SArnaldo Carvalho de Melo 		const char *err = str_error_r(errno, bf, sizeof(bf));
20554cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2056aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2057aff3f3f6SArnaldo Carvalho de Melo 	}
2058aff3f3f6SArnaldo Carvalho de Melo 
2059aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2060aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2061aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2062aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2063aff3f3f6SArnaldo Carvalho de Melo 
2064aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2065aff3f3f6SArnaldo Carvalho de Melo }
2066aff3f3f6SArnaldo Carvalho de Melo 
2067fcd86426SJiri Olsa void hist_browser__init(struct hist_browser *browser,
2068fcd86426SJiri Olsa 			struct hists *hists)
2069aca7a94dSNamhyung Kim {
2070b1c7a8f7SJiri Olsa 	struct perf_hpp_fmt *fmt;
2071b1c7a8f7SJiri Olsa 
207205e8b080SArnaldo Carvalho de Melo 	browser->hists			= hists;
207305e8b080SArnaldo Carvalho de Melo 	browser->b.refresh		= hist_browser__refresh;
2074357cfff1SArnaldo Carvalho de Melo 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
207505e8b080SArnaldo Carvalho de Melo 	browser->b.seek			= ui_browser__hists_seek;
207605e8b080SArnaldo Carvalho de Melo 	browser->b.use_navkeypressed	= true;
2077c8302367SJiri Olsa 	browser->show_headers		= symbol_conf.show_hist_headers;
2078b1c7a8f7SJiri Olsa 
2079e3b60bc9SNamhyung Kim 	hists__for_each_format(hists, fmt)
2080b1c7a8f7SJiri Olsa 		++browser->b.columns;
2081e3b60bc9SNamhyung Kim 
2082e3b60bc9SNamhyung Kim 	hists__reset_column_width(hists);
2083aca7a94dSNamhyung Kim }
2084aca7a94dSNamhyung Kim 
2085fcd86426SJiri Olsa struct hist_browser *hist_browser__new(struct hists *hists)
2086fcd86426SJiri Olsa {
2087fcd86426SJiri Olsa 	struct hist_browser *browser = zalloc(sizeof(*browser));
2088fcd86426SJiri Olsa 
2089fcd86426SJiri Olsa 	if (browser)
2090fcd86426SJiri Olsa 		hist_browser__init(browser, hists);
2091fcd86426SJiri Olsa 
209205e8b080SArnaldo Carvalho de Melo 	return browser;
2093aca7a94dSNamhyung Kim }
2094aca7a94dSNamhyung Kim 
2095a6ec894dSJiri Olsa static struct hist_browser *
2096a6ec894dSJiri Olsa perf_evsel_browser__new(struct perf_evsel *evsel,
2097a6ec894dSJiri Olsa 			struct hist_browser_timer *hbt,
2098a6ec894dSJiri Olsa 			struct perf_env *env)
2099a6ec894dSJiri Olsa {
2100a6ec894dSJiri Olsa 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2101a6ec894dSJiri Olsa 
2102a6ec894dSJiri Olsa 	if (browser) {
2103a6ec894dSJiri Olsa 		browser->hbt   = hbt;
2104a6ec894dSJiri Olsa 		browser->env   = env;
2105a6ec894dSJiri Olsa 		browser->title = perf_evsel_browser_title;
2106a6ec894dSJiri Olsa 	}
2107a6ec894dSJiri Olsa 	return browser;
2108a6ec894dSJiri Olsa }
2109a6ec894dSJiri Olsa 
2110dabd2012SJiri Olsa void hist_browser__delete(struct hist_browser *browser)
2111aca7a94dSNamhyung Kim {
211205e8b080SArnaldo Carvalho de Melo 	free(browser);
2113aca7a94dSNamhyung Kim }
2114aca7a94dSNamhyung Kim 
211505e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2116aca7a94dSNamhyung Kim {
211705e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2118aca7a94dSNamhyung Kim }
2119aca7a94dSNamhyung Kim 
212005e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2121aca7a94dSNamhyung Kim {
212205e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2123aca7a94dSNamhyung Kim }
2124aca7a94dSNamhyung Kim 
21251e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
21261e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
21271e378ebdSTaeung Song {
21281e378ebdSTaeung Song 	return timer == NULL;
21291e378ebdSTaeung Song }
21301e378ebdSTaeung Song 
21315b91a86fSJiri Olsa static int perf_evsel_browser_title(struct hist_browser *browser,
21321e378ebdSTaeung Song 				char *bf, size_t size)
2133aca7a94dSNamhyung Kim {
21345b91a86fSJiri Olsa 	struct hist_browser_timer *hbt = browser->hbt;
21355b91a86fSJiri Olsa 	struct hists *hists = browser->hists;
2136aca7a94dSNamhyung Kim 	char unit;
2137aca7a94dSNamhyung Kim 	int printed;
213805e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
213905e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
214084734b06SKan Liang 	int socket_id = hists->socket_filter;
214105e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
214205e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
2143717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
2144dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
2145717e263fSNamhyung Kim 	char buf[512];
2146717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
21479e207ddfSKan Liang 	char ref[30] = " show reference callgraph, ";
21489e207ddfSKan Liang 	bool enable_ref = false;
2149717e263fSNamhyung Kim 
2150f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
2151f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
2152f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
2153f2148330SNamhyung Kim 	}
2154f2148330SNamhyung Kim 
2155759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
2156717e263fSNamhyung Kim 		struct perf_evsel *pos;
2157717e263fSNamhyung Kim 
2158717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
2159717e263fSNamhyung Kim 		ev_name = buf;
2160717e263fSNamhyung Kim 
2161717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
21624ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
21634ea062edSArnaldo Carvalho de Melo 
2164f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
21654ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
21664ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
2167f2148330SNamhyung Kim 			} else {
21684ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
21694ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
2170717e263fSNamhyung Kim 			}
2171717e263fSNamhyung Kim 		}
2172f2148330SNamhyung Kim 	}
2173aca7a94dSNamhyung Kim 
21749e207ddfSKan Liang 	if (symbol_conf.show_ref_callgraph &&
21759e207ddfSKan Liang 	    strstr(ev_name, "call-graph=no"))
21769e207ddfSKan Liang 		enable_ref = true;
2177aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
2178aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
21799e207ddfSKan Liang 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
21809e207ddfSKan Liang 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2181aca7a94dSNamhyung Kim 
2182aca7a94dSNamhyung Kim 
218305e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
2184aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
218505e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
21866962ccb3SNamhyung Kim 	if (thread) {
2187fa82911aSJiri Olsa 		if (hists__has(hists, thread)) {
2188aca7a94dSNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
2189aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
2190b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
219138051234SAdrian Hunter 				    thread->tid);
21926962ccb3SNamhyung Kim 		} else {
21936962ccb3SNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
21946962ccb3SNamhyung Kim 				    ", Thread: %s",
21956962ccb3SNamhyung Kim 				     (thread->comm_set ? thread__comm_str(thread) : ""));
21966962ccb3SNamhyung Kim 		}
21976962ccb3SNamhyung Kim 	}
2198aca7a94dSNamhyung Kim 	if (dso)
2199aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
2200aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
220184734b06SKan Liang 	if (socket_id > -1)
220221394d94SKan Liang 		printed += scnprintf(bf + printed, size - printed,
220384734b06SKan Liang 				    ", Processor Socket: %d", socket_id);
22041e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
22051e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
22061e378ebdSTaeung Song 
22071e378ebdSTaeung Song 		if (top->zero)
22081e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
22091e378ebdSTaeung Song 	}
22101e378ebdSTaeung Song 
2211aca7a94dSNamhyung Kim 	return printed;
2212aca7a94dSNamhyung Kim }
2213aca7a94dSNamhyung Kim 
2214aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2215aca7a94dSNamhyung Kim {
2216aca7a94dSNamhyung Kim 	int i;
2217aca7a94dSNamhyung Kim 
221804662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
221904662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2220aca7a94dSNamhyung Kim }
2221aca7a94dSNamhyung Kim 
2222341487abSFeng Tang /*
2223341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2224341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2225341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2226341487abSFeng Tang  */
2227341487abSFeng Tang static bool is_input_name_malloced = false;
2228341487abSFeng Tang 
2229341487abSFeng Tang static int switch_data_file(void)
2230341487abSFeng Tang {
2231341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2232341487abSFeng Tang 	DIR *pwd_dir;
2233341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2234341487abSFeng Tang 	struct dirent *dent;
2235341487abSFeng Tang 
2236341487abSFeng Tang 	pwd = getenv("PWD");
2237341487abSFeng Tang 	if (!pwd)
2238341487abSFeng Tang 		return ret;
2239341487abSFeng Tang 
2240341487abSFeng Tang 	pwd_dir = opendir(pwd);
2241341487abSFeng Tang 	if (!pwd_dir)
2242341487abSFeng Tang 		return ret;
2243341487abSFeng Tang 
2244341487abSFeng Tang 	memset(options, 0, sizeof(options));
2245341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
2246341487abSFeng Tang 
2247341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2248341487abSFeng Tang 		char path[PATH_MAX];
2249341487abSFeng Tang 		u64 magic;
2250341487abSFeng Tang 		char *name = dent->d_name;
2251341487abSFeng Tang 		FILE *file;
2252341487abSFeng Tang 
2253341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2254341487abSFeng Tang 			continue;
2255341487abSFeng Tang 
2256341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2257341487abSFeng Tang 
2258341487abSFeng Tang 		file = fopen(path, "r");
2259341487abSFeng Tang 		if (!file)
2260341487abSFeng Tang 			continue;
2261341487abSFeng Tang 
2262341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2263341487abSFeng Tang 			goto close_file_and_continue;
2264341487abSFeng Tang 
2265341487abSFeng Tang 		if (is_perf_magic(magic)) {
2266341487abSFeng Tang 			options[nr_options] = strdup(name);
2267341487abSFeng Tang 			if (!options[nr_options])
2268341487abSFeng Tang 				goto close_file_and_continue;
2269341487abSFeng Tang 
2270341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2271341487abSFeng Tang 			if (!abs_path[nr_options]) {
227274cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2273341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2274341487abSFeng Tang 				fclose(file);
2275341487abSFeng Tang 				break;
2276341487abSFeng Tang 			}
2277341487abSFeng Tang 
2278341487abSFeng Tang 			nr_options++;
2279341487abSFeng Tang 		}
2280341487abSFeng Tang 
2281341487abSFeng Tang close_file_and_continue:
2282341487abSFeng Tang 		fclose(file);
2283341487abSFeng Tang 		if (nr_options >= 32) {
2284341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2285341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2286341487abSFeng Tang 			break;
2287341487abSFeng Tang 		}
2288341487abSFeng Tang 	}
2289341487abSFeng Tang 	closedir(pwd_dir);
2290341487abSFeng Tang 
2291341487abSFeng Tang 	if (nr_options) {
2292341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
2293341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2294341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2295341487abSFeng Tang 			if (tmp) {
2296341487abSFeng Tang 				if (is_input_name_malloced)
2297341487abSFeng Tang 					free((void *)input_name);
2298341487abSFeng Tang 				input_name = tmp;
2299341487abSFeng Tang 				is_input_name_malloced = true;
2300341487abSFeng Tang 				ret = 0;
2301341487abSFeng Tang 			} else
2302341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2303341487abSFeng Tang 		}
2304341487abSFeng Tang 	}
2305341487abSFeng Tang 
2306341487abSFeng Tang 	free_popup_options(options, nr_options);
2307341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2308341487abSFeng Tang 	return ret;
2309341487abSFeng Tang }
2310341487abSFeng Tang 
2311ea7cd592SNamhyung Kim struct popup_action {
2312ea7cd592SNamhyung Kim 	struct thread 		*thread;
2313ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
231484734b06SKan Liang 	int			socket;
2315ea7cd592SNamhyung Kim 
2316ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2317ea7cd592SNamhyung Kim };
2318ea7cd592SNamhyung Kim 
2319bc7cad42SNamhyung Kim static int
2320ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2321bc7cad42SNamhyung Kim {
2322bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
2323bc7cad42SNamhyung Kim 	struct annotation *notes;
2324bc7cad42SNamhyung Kim 	struct hist_entry *he;
2325bc7cad42SNamhyung Kim 	int err;
2326bc7cad42SNamhyung Kim 
2327eebd0bfcSArnaldo Carvalho de Melo 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
2328bc7cad42SNamhyung Kim 		return 0;
2329bc7cad42SNamhyung Kim 
2330ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2331bc7cad42SNamhyung Kim 	if (!notes->src)
2332bc7cad42SNamhyung Kim 		return 0;
2333bc7cad42SNamhyung Kim 
2334bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
2335ea7cd592SNamhyung Kim 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2336bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2337bc7cad42SNamhyung Kim 	/*
2338bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2339bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2340bc7cad42SNamhyung Kim 	 */
2341bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2342bc7cad42SNamhyung Kim 		return 1;
2343bc7cad42SNamhyung Kim 
2344bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2345bc7cad42SNamhyung Kim 	if (err)
2346bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2347bc7cad42SNamhyung Kim 	return 0;
2348bc7cad42SNamhyung Kim }
2349bc7cad42SNamhyung Kim 
2350bc7cad42SNamhyung Kim static int
2351ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2352ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
2353ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
2354bc7cad42SNamhyung Kim {
2355ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
2356ea7cd592SNamhyung Kim 		return 0;
2357ea7cd592SNamhyung Kim 
2358ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2359ea7cd592SNamhyung Kim 		return 0;
2360ea7cd592SNamhyung Kim 
2361ea7cd592SNamhyung Kim 	act->ms.map = map;
2362ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2363ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2364ea7cd592SNamhyung Kim 	return 1;
2365ea7cd592SNamhyung Kim }
2366ea7cd592SNamhyung Kim 
2367ea7cd592SNamhyung Kim static int
2368ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2369ea7cd592SNamhyung Kim {
2370ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2371ea7cd592SNamhyung Kim 
23727cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
23737cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2374599a2f38SNamhyung Kim 		return 0;
2375599a2f38SNamhyung Kim 
2376bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2377bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2378bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2379bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2380bc7cad42SNamhyung Kim 		ui_helpline__pop();
2381bc7cad42SNamhyung Kim 	} else {
2382fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
23837727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2384bc7cad42SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "",
2385bc7cad42SNamhyung Kim 					   thread->tid);
23866962ccb3SNamhyung Kim 		} else {
23876962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
23886962ccb3SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "");
23896962ccb3SNamhyung Kim 		}
23906962ccb3SNamhyung Kim 
2391bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2392bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2393bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2394bc7cad42SNamhyung Kim 	}
2395bc7cad42SNamhyung Kim 
2396bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2397bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2398bc7cad42SNamhyung Kim 	return 0;
2399bc7cad42SNamhyung Kim }
2400bc7cad42SNamhyung Kim 
2401bc7cad42SNamhyung Kim static int
2402ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2403ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2404bc7cad42SNamhyung Kim {
24056962ccb3SNamhyung Kim 	int ret;
24066962ccb3SNamhyung Kim 
24077cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
24087cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2409ea7cd592SNamhyung Kim 		return 0;
2410ea7cd592SNamhyung Kim 
2411fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
24126962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2413ea7cd592SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
2414ea7cd592SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "",
24156962ccb3SNamhyung Kim 			       thread->tid);
24166962ccb3SNamhyung Kim 	} else {
24176962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s thread",
24186962ccb3SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
24196962ccb3SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "");
24206962ccb3SNamhyung Kim 	}
24216962ccb3SNamhyung Kim 	if (ret < 0)
2422ea7cd592SNamhyung Kim 		return 0;
2423ea7cd592SNamhyung Kim 
2424ea7cd592SNamhyung Kim 	act->thread = thread;
2425ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2426ea7cd592SNamhyung Kim 	return 1;
2427ea7cd592SNamhyung Kim }
2428ea7cd592SNamhyung Kim 
2429ea7cd592SNamhyung Kim static int
2430ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2431ea7cd592SNamhyung Kim {
2432045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
2433ea7cd592SNamhyung Kim 
243469849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2435599a2f38SNamhyung Kim 		return 0;
2436599a2f38SNamhyung Kim 
2437bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2438bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2439bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2440bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2441bc7cad42SNamhyung Kim 		ui_helpline__pop();
2442bc7cad42SNamhyung Kim 	} else {
24437727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2444045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2445045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
2446bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2447bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2448bc7cad42SNamhyung Kim 	}
2449bc7cad42SNamhyung Kim 
2450bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2451bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2452bc7cad42SNamhyung Kim 	return 0;
2453bc7cad42SNamhyung Kim }
2454bc7cad42SNamhyung Kim 
2455bc7cad42SNamhyung Kim static int
2456ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2457045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2458bc7cad42SNamhyung Kim {
245969849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2460ea7cd592SNamhyung Kim 		return 0;
2461ea7cd592SNamhyung Kim 
2462ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
2463ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
2464045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2465ea7cd592SNamhyung Kim 		return 0;
2466ea7cd592SNamhyung Kim 
2467045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2468ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2469ea7cd592SNamhyung Kim 	return 1;
2470ea7cd592SNamhyung Kim }
2471ea7cd592SNamhyung Kim 
2472ea7cd592SNamhyung Kim static int
2473ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2474ea7cd592SNamhyung Kim 	      struct popup_action *act)
2475ea7cd592SNamhyung Kim {
2476ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2477bc7cad42SNamhyung Kim 	return 0;
2478bc7cad42SNamhyung Kim }
2479bc7cad42SNamhyung Kim 
2480bc7cad42SNamhyung Kim static int
248169849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2482ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2483ea7cd592SNamhyung Kim {
248469849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2485ea7cd592SNamhyung Kim 		return 0;
2486ea7cd592SNamhyung Kim 
2487ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2488ea7cd592SNamhyung Kim 		return 0;
2489ea7cd592SNamhyung Kim 
2490ea7cd592SNamhyung Kim 	act->ms.map = map;
2491ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2492ea7cd592SNamhyung Kim 	return 1;
2493ea7cd592SNamhyung Kim }
2494ea7cd592SNamhyung Kim 
2495ea7cd592SNamhyung Kim static int
2496bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2497ea7cd592SNamhyung Kim 	      struct popup_action *act)
2498bc7cad42SNamhyung Kim {
2499bc7cad42SNamhyung Kim 	char script_opt[64];
2500bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
2501bc7cad42SNamhyung Kim 
2502ea7cd592SNamhyung Kim 	if (act->thread) {
2503bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2504ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2505ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
2506bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2507ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2508bc7cad42SNamhyung Kim 	}
2509bc7cad42SNamhyung Kim 
2510bc7cad42SNamhyung Kim 	script_browse(script_opt);
2511bc7cad42SNamhyung Kim 	return 0;
2512bc7cad42SNamhyung Kim }
2513bc7cad42SNamhyung Kim 
2514bc7cad42SNamhyung Kim static int
2515ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
2516ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
2517ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
2518ea7cd592SNamhyung Kim {
2519ea7cd592SNamhyung Kim 	if (thread) {
2520ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2521ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
2522ea7cd592SNamhyung Kim 			return 0;
2523ea7cd592SNamhyung Kim 	} else if (sym) {
2524ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2525ea7cd592SNamhyung Kim 			     sym->name) < 0)
2526ea7cd592SNamhyung Kim 			return 0;
2527ea7cd592SNamhyung Kim 	} else {
2528ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2529ea7cd592SNamhyung Kim 			return 0;
2530ea7cd592SNamhyung Kim 	}
2531ea7cd592SNamhyung Kim 
2532ea7cd592SNamhyung Kim 	act->thread = thread;
2533ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2534ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2535ea7cd592SNamhyung Kim 	return 1;
2536ea7cd592SNamhyung Kim }
2537ea7cd592SNamhyung Kim 
2538ea7cd592SNamhyung Kim static int
2539ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2540ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2541bc7cad42SNamhyung Kim {
2542bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2543bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2544bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2545ea7cd592SNamhyung Kim 		return 0;
2546bc7cad42SNamhyung Kim 	}
2547bc7cad42SNamhyung Kim 
2548bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2549bc7cad42SNamhyung Kim }
2550bc7cad42SNamhyung Kim 
2551ea7cd592SNamhyung Kim static int
2552ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2553ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2554ea7cd592SNamhyung Kim {
2555ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2556ea7cd592SNamhyung Kim 		return 0;
2557ea7cd592SNamhyung Kim 
2558ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2559ea7cd592SNamhyung Kim 		return 0;
2560ea7cd592SNamhyung Kim 
2561ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2562ea7cd592SNamhyung Kim 	return 1;
2563ea7cd592SNamhyung Kim }
2564ea7cd592SNamhyung Kim 
2565ea7cd592SNamhyung Kim static int
2566ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2567ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2568ea7cd592SNamhyung Kim {
2569ea7cd592SNamhyung Kim 	return 0;
2570ea7cd592SNamhyung Kim }
2571ea7cd592SNamhyung Kim 
2572ea7cd592SNamhyung Kim static int
2573ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2574ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2575ea7cd592SNamhyung Kim {
2576ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2577ea7cd592SNamhyung Kim 		return 0;
2578ea7cd592SNamhyung Kim 
2579ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2580ea7cd592SNamhyung Kim 	return 1;
2581ea7cd592SNamhyung Kim }
2582ea7cd592SNamhyung Kim 
258384734b06SKan Liang static int
258484734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
258584734b06SKan Liang {
258635a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2587599a2f38SNamhyung Kim 		return 0;
2588599a2f38SNamhyung Kim 
258984734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
259084734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
259184734b06SKan Liang 		browser->hists->socket_filter = -1;
259284734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
259384734b06SKan Liang 	} else {
259484734b06SKan Liang 		browser->hists->socket_filter = act->socket;
259584734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
259684734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
259784734b06SKan Liang 	}
259884734b06SKan Liang 
259984734b06SKan Liang 	hists__filter_by_socket(browser->hists);
260084734b06SKan Liang 	hist_browser__reset(browser);
260184734b06SKan Liang 	return 0;
260284734b06SKan Liang }
260384734b06SKan Liang 
260484734b06SKan Liang static int
260584734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
260684734b06SKan Liang 	       char **optstr, int socket_id)
260784734b06SKan Liang {
260835a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
260984734b06SKan Liang 		return 0;
261084734b06SKan Liang 
261184734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
261284734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
261384734b06SKan Liang 		     socket_id) < 0)
261484734b06SKan Liang 		return 0;
261584734b06SKan Liang 
261684734b06SKan Liang 	act->socket = socket_id;
261784734b06SKan Liang 	act->fn = do_zoom_socket;
261884734b06SKan Liang 	return 1;
261984734b06SKan Liang }
262084734b06SKan Liang 
2621112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2622064f1981SNamhyung Kim {
2623064f1981SNamhyung Kim 	u64 nr_entries = 0;
2624064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2625064f1981SNamhyung Kim 
2626f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2627268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2628268397cbSNamhyung Kim 		return;
2629268397cbSNamhyung Kim 	}
2630268397cbSNamhyung Kim 
263114135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2632064f1981SNamhyung Kim 		nr_entries++;
2633f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2634064f1981SNamhyung Kim 	}
2635064f1981SNamhyung Kim 
2636112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2637f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2638064f1981SNamhyung Kim }
2639341487abSFeng Tang 
2640b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2641b62e8dfcSNamhyung Kim 					       double percent)
2642b62e8dfcSNamhyung Kim {
2643b62e8dfcSNamhyung Kim 	struct hist_entry *he;
2644b62e8dfcSNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2645b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2646b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2647b62e8dfcSNamhyung Kim 
2648b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2649b62e8dfcSNamhyung Kim 
2650b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2651b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2652b62e8dfcSNamhyung Kim 
265379dded87SNamhyung Kim 		if (he->has_no_entry) {
265479dded87SNamhyung Kim 			he->has_no_entry = false;
265579dded87SNamhyung Kim 			he->nr_rows = 0;
265679dded87SNamhyung Kim 		}
265779dded87SNamhyung Kim 
2658d0506edbSNamhyung Kim 		if (!he->leaf || !symbol_conf.use_callchain)
2659d0506edbSNamhyung Kim 			goto next;
2660d0506edbSNamhyung Kim 
2661b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2662b62e8dfcSNamhyung Kim 			total = he->stat.period;
2663b62e8dfcSNamhyung Kim 
2664b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2665b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2666b62e8dfcSNamhyung Kim 
2667b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2668b62e8dfcSNamhyung Kim 		}
2669b62e8dfcSNamhyung Kim 
2670b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2671b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2672b62e8dfcSNamhyung Kim 
2673d0506edbSNamhyung Kim next:
2674201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2675d0506edbSNamhyung Kim 
2676b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2677b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2678492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2679b62e8dfcSNamhyung Kim 	}
2680b62e8dfcSNamhyung Kim }
2681b62e8dfcSNamhyung Kim 
2682aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2683dd00d486SJiri Olsa 				    const char *helpline,
2684aca7a94dSNamhyung Kim 				    bool left_exits,
268568d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2686064f1981SNamhyung Kim 				    float min_pcnt,
2687ce80d3beSKan Liang 				    struct perf_env *env)
2688aca7a94dSNamhyung Kim {
26894ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2690a6ec894dSJiri Olsa 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2691aca7a94dSNamhyung Kim 	struct branch_info *bi;
2692f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2693f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2694ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2695aca7a94dSNamhyung Kim 	int nr_options = 0;
2696aca7a94dSNamhyung Kim 	int key = -1;
2697aca7a94dSNamhyung Kim 	char buf[64];
26989783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2699aca7a94dSNamhyung Kim 
2700e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2701e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2702e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2703e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
2704e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2705e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2706e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2707e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
27087727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
27097727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
2710e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2711e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2712e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2713e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2714105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2715025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2716b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
271731eb4360SNamhyung Kim 	"m             Display context menu\n"				\
271884734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2719e8e684a5SNamhyung Kim 
2720e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
2721e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
27226dd60135SNamhyung Kim 	"i             Show header information\n"
2723e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2724e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2725e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2726e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2727e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2728e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2729e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2730e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2731e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2732e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
273342337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2734fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2735e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2736e8e684a5SNamhyung Kim 
2737aca7a94dSNamhyung Kim 	if (browser == NULL)
2738aca7a94dSNamhyung Kim 		return -1;
2739aca7a94dSNamhyung Kim 
2740ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2741ed426915SNamhyung Kim 	SLang_reset_tty();
2742ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2743ed426915SNamhyung Kim 
274403905048SNamhyung Kim 	if (min_pcnt)
2745064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2746112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2747064f1981SNamhyung Kim 
274884734b06SKan Liang 	browser->pstack = pstack__new(3);
274901f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2750aca7a94dSNamhyung Kim 		goto out;
2751aca7a94dSNamhyung Kim 
2752aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2753aca7a94dSNamhyung Kim 
2754aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2755ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2756aca7a94dSNamhyung Kim 
27575b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
27585b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
27595b591669SNamhyung Kim 
2760aca7a94dSNamhyung Kim 	while (1) {
2761f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2762045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
2763ea7cd592SNamhyung Kim 		int choice = 0;
276484734b06SKan Liang 		int socked_id = -1;
2765aca7a94dSNamhyung Kim 
2766aca7a94dSNamhyung Kim 		nr_options = 0;
2767aca7a94dSNamhyung Kim 
27685f00b0f4SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline);
2769aca7a94dSNamhyung Kim 
2770aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
2771aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
2772045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
277384734b06SKan Liang 			socked_id = browser->he_selection->socket;
2774aca7a94dSNamhyung Kim 		}
2775aca7a94dSNamhyung Kim 		switch (key) {
2776aca7a94dSNamhyung Kim 		case K_TAB:
2777aca7a94dSNamhyung Kim 		case K_UNTAB:
2778aca7a94dSNamhyung Kim 			if (nr_events == 1)
2779aca7a94dSNamhyung Kim 				continue;
2780aca7a94dSNamhyung Kim 			/*
2781aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
2782aca7a94dSNamhyung Kim 			 * go to the next or previous
2783aca7a94dSNamhyung Kim 			 */
2784aca7a94dSNamhyung Kim 			goto out_free_stack;
2785aca7a94dSNamhyung Kim 		case 'a':
27862e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
2787aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
2788aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
2789aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
2790aca7a94dSNamhyung Kim 				continue;
2791aca7a94dSNamhyung Kim 			}
2792aca7a94dSNamhyung Kim 
2793aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
2794aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
2795aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
2796aca7a94dSNamhyung Kim 				continue;
2797bc7cad42SNamhyung Kim 
2798ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
2799ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
2800ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
2801bc7cad42SNamhyung Kim 			continue;
2802aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
2803aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
2804aff3f3f6SArnaldo Carvalho de Melo 			continue;
2805aca7a94dSNamhyung Kim 		case 'd':
2806fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
2807ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
2808bc7cad42SNamhyung Kim 			continue;
2809a7cb8863SArnaldo Carvalho de Melo 		case 'V':
2810a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
2811a7cb8863SArnaldo Carvalho de Melo 			continue;
2812aca7a94dSNamhyung Kim 		case 't':
2813ea7cd592SNamhyung Kim 			actions->thread = thread;
2814ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
2815bc7cad42SNamhyung Kim 			continue;
281684734b06SKan Liang 		case 'S':
281784734b06SKan Liang 			actions->socket = socked_id;
281884734b06SKan Liang 			do_zoom_socket(browser, actions);
281984734b06SKan Liang 			continue;
28205a5626b1SArnaldo Carvalho de Melo 		case '/':
2821aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
28224aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
28234aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
2824aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2825aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
282605e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
282705e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
2828aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
2829aca7a94dSNamhyung Kim 			}
2830aca7a94dSNamhyung Kim 			continue;
2831cdbab7c2SFeng Tang 		case 'r':
2832ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
2833ea7cd592SNamhyung Kim 				actions->thread = NULL;
2834ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
2835ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
2836ea7cd592SNamhyung Kim 			}
2837c77d8d70SFeng Tang 			continue;
2838341487abSFeng Tang 		case 's':
2839bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
2840ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
2841bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
2842bc7cad42SNamhyung Kim 					goto out_free_stack;
2843bc7cad42SNamhyung Kim 			}
2844341487abSFeng Tang 			continue;
28456dd60135SNamhyung Kim 		case 'i':
28466dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
28476dd60135SNamhyung Kim 			if (env->arch)
28486dd60135SNamhyung Kim 				tui__header_window(env);
28496dd60135SNamhyung Kim 			continue;
2850105eb30fSNamhyung Kim 		case 'F':
2851105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
2852105eb30fSNamhyung Kim 			continue;
285342337a22SNamhyung Kim 		case 'z':
285442337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
285542337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
285642337a22SNamhyung Kim 
285742337a22SNamhyung Kim 				top->zero = !top->zero;
285842337a22SNamhyung Kim 			}
285942337a22SNamhyung Kim 			continue;
2860b62e8dfcSNamhyung Kim 		case 'L':
2861b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
2862b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
2863b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2864b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
2865b62e8dfcSNamhyung Kim 				char *end;
2866b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
2867b62e8dfcSNamhyung Kim 
2868b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
2869b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
2870b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
2871b62e8dfcSNamhyung Kim 					continue;
2872b62e8dfcSNamhyung Kim 				}
2873b62e8dfcSNamhyung Kim 
2874b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
2875b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
2876b62e8dfcSNamhyung Kim 			}
2877b62e8dfcSNamhyung Kim 			continue;
2878aca7a94dSNamhyung Kim 		case K_F1:
2879aca7a94dSNamhyung Kim 		case 'h':
2880aca7a94dSNamhyung Kim 		case '?':
2881aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
2882e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
2883aca7a94dSNamhyung Kim 			continue;
2884aca7a94dSNamhyung Kim 		case K_ENTER:
2885aca7a94dSNamhyung Kim 		case K_RIGHT:
288631eb4360SNamhyung Kim 		case 'm':
2887aca7a94dSNamhyung Kim 			/* menu */
2888aca7a94dSNamhyung Kim 			break;
288963ab1749SArnaldo Carvalho de Melo 		case K_ESC:
2890aca7a94dSNamhyung Kim 		case K_LEFT: {
2891aca7a94dSNamhyung Kim 			const void *top;
2892aca7a94dSNamhyung Kim 
289301f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
2894aca7a94dSNamhyung Kim 				/*
2895aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
2896aca7a94dSNamhyung Kim 				 */
2897aca7a94dSNamhyung Kim 				if (left_exits)
2898aca7a94dSNamhyung Kim 					goto out_free_stack;
289963ab1749SArnaldo Carvalho de Melo 
290063ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
290163ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
290263ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
290363ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
290463ab1749SArnaldo Carvalho de Melo 
2905aca7a94dSNamhyung Kim 				continue;
2906aca7a94dSNamhyung Kim 			}
29076422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
2908bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
29096422184bSNamhyung Kim 				/*
29106422184bSNamhyung Kim 				 * No need to set actions->dso here since
29116422184bSNamhyung Kim 				 * it's just to remove the current filter.
29126422184bSNamhyung Kim 				 * Ditto for thread below.
29136422184bSNamhyung Kim 				 */
29146422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
291584734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
29166422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
291784734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
291884734b06SKan Liang 				do_zoom_socket(browser, actions);
291984734b06SKan Liang 			}
2920aca7a94dSNamhyung Kim 			continue;
2921aca7a94dSNamhyung Kim 		}
2922aca7a94dSNamhyung Kim 		case 'q':
2923aca7a94dSNamhyung Kim 		case CTRL('c'):
2924516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
2925fbb7997eSArnaldo Carvalho de Melo 		case 'f':
292613d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
292713d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
292813d1e536SNamhyung Kim 
292913d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
293013d1e536SNamhyung Kim 				/*
293113d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
293213d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
293313d1e536SNamhyung Kim 				 */
293413d1e536SNamhyung Kim 				if (top->evlist->enabled) {
293513d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
293613d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
293713d1e536SNamhyung Kim 				} else {
293813d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
293913d1e536SNamhyung Kim 					hbt->refresh = 0;
294013d1e536SNamhyung Kim 				}
294113d1e536SNamhyung Kim 				continue;
294213d1e536SNamhyung Kim 			}
29433e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
2944aca7a94dSNamhyung Kim 		default:
29453e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
2946aca7a94dSNamhyung Kim 			continue;
2947aca7a94dSNamhyung Kim 		}
2948aca7a94dSNamhyung Kim 
29492e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
29500ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
29510ba332f7SArnaldo Carvalho de Melo 
295255369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
2953aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
29540ba332f7SArnaldo Carvalho de Melo 
29550ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
29560ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
29570ba332f7SArnaldo Carvalho de Melo 
2958ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2959ea7cd592SNamhyung Kim 						       &actions[nr_options],
2960ea7cd592SNamhyung Kim 						       &options[nr_options],
2961ea7cd592SNamhyung Kim 						       bi->from.map,
2962ea7cd592SNamhyung Kim 						       bi->from.sym);
2963ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
2964ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
2965ea7cd592SNamhyung Kim 							&actions[nr_options],
2966ea7cd592SNamhyung Kim 							&options[nr_options],
2967ea7cd592SNamhyung Kim 							bi->to.map,
2968ea7cd592SNamhyung Kim 							bi->to.sym);
2969aca7a94dSNamhyung Kim 		} else {
2970ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2971ea7cd592SNamhyung Kim 						       &actions[nr_options],
2972ea7cd592SNamhyung Kim 						       &options[nr_options],
2973ea7cd592SNamhyung Kim 						       browser->selection->map,
2974ea7cd592SNamhyung Kim 						       browser->selection->sym);
2975446fb96cSArnaldo Carvalho de Melo 		}
29760ba332f7SArnaldo Carvalho de Melo skip_annotation:
2977ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
2978ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
2979ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
2980045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
2981ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
2982ea7cd592SNamhyung Kim 					  &options[nr_options],
2983bd315aabSWang Nan 					  browser->selection ?
2984bd315aabSWang Nan 						browser->selection->map : NULL);
298584734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
298684734b06SKan Liang 					     &options[nr_options],
298784734b06SKan Liang 					     socked_id);
2988cdbab7c2SFeng Tang 		/* perf script support */
2989b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
2990b1baae89SNamhyung Kim 			goto skip_scripting;
2991b1baae89SNamhyung Kim 
2992cdbab7c2SFeng Tang 		if (browser->he_selection) {
2993fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
2994ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
2995ea7cd592SNamhyung Kim 							     &actions[nr_options],
2996ea7cd592SNamhyung Kim 							     &options[nr_options],
2997ea7cd592SNamhyung Kim 							     thread, NULL);
29982eafd410SNamhyung Kim 			}
2999bd315aabSWang Nan 			/*
3000bd315aabSWang Nan 			 * Note that browser->selection != NULL
3001bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
3002bd315aabSWang Nan 			 * so we don't need to check browser->selection
3003bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
3004bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
3005bd315aabSWang Nan 			 *
3006bd315aabSWang Nan 			 * See hist_browser__show_entry.
3007bd315aabSWang Nan 			 */
30082e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
3009ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3010ea7cd592SNamhyung Kim 							     &actions[nr_options],
3011ea7cd592SNamhyung Kim 							     &options[nr_options],
3012ea7cd592SNamhyung Kim 							     NULL, browser->selection->sym);
3013cdbab7c2SFeng Tang 			}
3014c221acb0SNamhyung Kim 		}
3015ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
3016ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
3017ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
3018ea7cd592SNamhyung Kim 					     &options[nr_options]);
3019b1baae89SNamhyung Kim skip_scripting:
3020ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
3021ea7cd592SNamhyung Kim 					   &options[nr_options]);
3022aca7a94dSNamhyung Kim 
3023ea7cd592SNamhyung Kim 		do {
3024ea7cd592SNamhyung Kim 			struct popup_action *act;
3025ea7cd592SNamhyung Kim 
3026ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
3027ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
3028aca7a94dSNamhyung Kim 				break;
3029aca7a94dSNamhyung Kim 
3030ea7cd592SNamhyung Kim 			act = &actions[choice];
3031ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
3032ea7cd592SNamhyung Kim 		} while (key == 1);
3033aca7a94dSNamhyung Kim 
3034bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3035341487abSFeng Tang 			break;
3036341487abSFeng Tang 	}
3037aca7a94dSNamhyung Kim out_free_stack:
303801f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3039aca7a94dSNamhyung Kim out:
3040aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3041f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3042aca7a94dSNamhyung Kim 	return key;
3043aca7a94dSNamhyung Kim }
3044aca7a94dSNamhyung Kim 
3045aca7a94dSNamhyung Kim struct perf_evsel_menu {
3046aca7a94dSNamhyung Kim 	struct ui_browser b;
3047aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
3048aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3049064f1981SNamhyung Kim 	float min_pcnt;
3050ce80d3beSKan Liang 	struct perf_env *env;
3051aca7a94dSNamhyung Kim };
3052aca7a94dSNamhyung Kim 
3053aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3054aca7a94dSNamhyung Kim 				   void *entry, int row)
3055aca7a94dSNamhyung Kim {
3056aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
3057aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
3058aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
30594ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3060aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
30614ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
30627289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
3063aca7a94dSNamhyung Kim 	char bf[256], unit;
3064aca7a94dSNamhyung Kim 	const char *warn = " ";
3065aca7a94dSNamhyung Kim 	size_t printed;
3066aca7a94dSNamhyung Kim 
3067aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3068aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3069aca7a94dSNamhyung Kim 
3070759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
3071717e263fSNamhyung Kim 		struct perf_evsel *pos;
3072717e263fSNamhyung Kim 
3073717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
3074717e263fSNamhyung Kim 
3075717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
30764ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
30774ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3078717e263fSNamhyung Kim 		}
3079717e263fSNamhyung Kim 	}
3080717e263fSNamhyung Kim 
3081aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3082aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3083aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3084517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3085aca7a94dSNamhyung Kim 
30864ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3087aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3088aca7a94dSNamhyung Kim 		menu->lost_events = true;
3089aca7a94dSNamhyung Kim 		if (!current_entry)
3090aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3091aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3092aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3093aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3094aca7a94dSNamhyung Kim 		warn = bf;
3095aca7a94dSNamhyung Kim 	}
3096aca7a94dSNamhyung Kim 
309726270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3098aca7a94dSNamhyung Kim 
3099aca7a94dSNamhyung Kim 	if (current_entry)
3100aca7a94dSNamhyung Kim 		menu->selection = evsel;
3101aca7a94dSNamhyung Kim }
3102aca7a94dSNamhyung Kim 
3103aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3104aca7a94dSNamhyung Kim 				int nr_events, const char *help,
31059783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
3106aca7a94dSNamhyung Kim {
3107aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
3108aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3109dd00d486SJiri Olsa 	const char *title = "Available samples";
31109783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3111aca7a94dSNamhyung Kim 	int key;
3112aca7a94dSNamhyung Kim 
3113aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3114aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3115aca7a94dSNamhyung Kim 		return -1;
3116aca7a94dSNamhyung Kim 
3117aca7a94dSNamhyung Kim 	while (1) {
3118aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3119aca7a94dSNamhyung Kim 
3120aca7a94dSNamhyung Kim 		switch (key) {
3121aca7a94dSNamhyung Kim 		case K_TIMER:
31229783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
3123aca7a94dSNamhyung Kim 
3124aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
3125aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3126aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3127aca7a94dSNamhyung Kim 			}
3128aca7a94dSNamhyung Kim 			continue;
3129aca7a94dSNamhyung Kim 		case K_RIGHT:
3130aca7a94dSNamhyung Kim 		case K_ENTER:
3131aca7a94dSNamhyung Kim 			if (!menu->selection)
3132aca7a94dSNamhyung Kim 				continue;
3133aca7a94dSNamhyung Kim 			pos = menu->selection;
3134aca7a94dSNamhyung Kim browse_hists:
3135aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
3136aca7a94dSNamhyung Kim 			/*
3137aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3138aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3139aca7a94dSNamhyung Kim 			 */
31409783adf7SNamhyung Kim 			if (hbt)
31419783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3142aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
3143dd00d486SJiri Olsa 						       true, hbt,
3144064f1981SNamhyung Kim 						       menu->min_pcnt,
314568d80758SNamhyung Kim 						       menu->env);
3146aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3147aca7a94dSNamhyung Kim 			switch (key) {
3148aca7a94dSNamhyung Kim 			case K_TAB:
3149aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
31509a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
3151aca7a94dSNamhyung Kim 				else
31529a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
3153aca7a94dSNamhyung Kim 				goto browse_hists;
3154aca7a94dSNamhyung Kim 			case K_UNTAB:
3155aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
31569a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
3157aca7a94dSNamhyung Kim 				else
3158d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
3159aca7a94dSNamhyung Kim 				goto browse_hists;
3160341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
3161aca7a94dSNamhyung Kim 			case 'q':
3162aca7a94dSNamhyung Kim 			case CTRL('c'):
3163aca7a94dSNamhyung Kim 				goto out;
316463ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3165aca7a94dSNamhyung Kim 			default:
3166aca7a94dSNamhyung Kim 				continue;
3167aca7a94dSNamhyung Kim 			}
3168aca7a94dSNamhyung Kim 		case K_LEFT:
3169aca7a94dSNamhyung Kim 			continue;
3170aca7a94dSNamhyung Kim 		case K_ESC:
3171aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3172aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3173aca7a94dSNamhyung Kim 				continue;
3174aca7a94dSNamhyung Kim 			/* Fall thru */
3175aca7a94dSNamhyung Kim 		case 'q':
3176aca7a94dSNamhyung Kim 		case CTRL('c'):
3177aca7a94dSNamhyung Kim 			goto out;
3178aca7a94dSNamhyung Kim 		default:
3179aca7a94dSNamhyung Kim 			continue;
3180aca7a94dSNamhyung Kim 		}
3181aca7a94dSNamhyung Kim 	}
3182aca7a94dSNamhyung Kim 
3183aca7a94dSNamhyung Kim out:
3184aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3185aca7a94dSNamhyung Kim 	return key;
3186aca7a94dSNamhyung Kim }
3187aca7a94dSNamhyung Kim 
3188316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3189fc24d7c2SNamhyung Kim 				 void *entry)
3190fc24d7c2SNamhyung Kim {
3191fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3192fc24d7c2SNamhyung Kim 
3193fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3194fc24d7c2SNamhyung Kim 		return true;
3195fc24d7c2SNamhyung Kim 
3196fc24d7c2SNamhyung Kim 	return false;
3197fc24d7c2SNamhyung Kim }
3198fc24d7c2SNamhyung Kim 
3199aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3200fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
320168d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
3202064f1981SNamhyung Kim 					   float min_pcnt,
3203ce80d3beSKan Liang 					   struct perf_env *env)
3204aca7a94dSNamhyung Kim {
3205aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3206aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
3207aca7a94dSNamhyung Kim 		.b = {
3208aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
3209aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3210aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3211aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3212fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3213fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3214aca7a94dSNamhyung Kim 			.priv	    = evlist,
3215aca7a94dSNamhyung Kim 		},
3216064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
321768d80758SNamhyung Kim 		.env = env,
3218aca7a94dSNamhyung Kim 	};
3219aca7a94dSNamhyung Kim 
3220aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3221aca7a94dSNamhyung Kim 
3222e5cadb93SArnaldo Carvalho de Melo 	evlist__for_each_entry(evlist, pos) {
32237289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
3224aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3225aca7a94dSNamhyung Kim 
3226aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3227aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3228aca7a94dSNamhyung Kim 	}
3229aca7a94dSNamhyung Kim 
3230fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3231aca7a94dSNamhyung Kim }
3232aca7a94dSNamhyung Kim 
3233aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
323468d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
3235064f1981SNamhyung Kim 				  float min_pcnt,
3236ce80d3beSKan Liang 				  struct perf_env *env)
3237aca7a94dSNamhyung Kim {
3238fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
3239fc24d7c2SNamhyung Kim 
3240fc24d7c2SNamhyung Kim single_entry:
3241fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
32429a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
3243fc24d7c2SNamhyung Kim 
3244fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
3245dd00d486SJiri Olsa 						false, hbt, min_pcnt,
3246064f1981SNamhyung Kim 						env);
3247aca7a94dSNamhyung Kim 	}
3248aca7a94dSNamhyung Kim 
3249fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
3250fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
3251fc24d7c2SNamhyung Kim 
3252fc24d7c2SNamhyung Kim 		nr_entries = 0;
3253e5cadb93SArnaldo Carvalho de Melo 		evlist__for_each_entry(evlist, pos) {
3254fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
3255fc24d7c2SNamhyung Kim 				nr_entries++;
32560050f7aaSArnaldo Carvalho de Melo 		}
3257fc24d7c2SNamhyung Kim 
3258fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3259fc24d7c2SNamhyung Kim 			goto single_entry;
3260fc24d7c2SNamhyung Kim 	}
3261fc24d7c2SNamhyung Kim 
3262fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3263064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
3264aca7a94dSNamhyung Kim }
3265