xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 0e3fa7a7)
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 {
335a1a99cdSJiri Olsa 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_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 
504b33f9226SJiri Olsa 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 
523b33f9226SJiri Olsa static void hist_entry__set_folding(struct hist_entry *he,
524b33f9226SJiri Olsa 				    struct hist_browser *browser, bool unfold)
525aca7a94dSNamhyung Kim {
526492b1010SNamhyung Kim 	double percent;
527aca7a94dSNamhyung Kim 
528492b1010SNamhyung Kim 	percent = hist_entry__get_percent_limit(he);
529492b1010SNamhyung Kim 	if (he->filtered || percent < browser->min_pcnt)
530b33f9226SJiri Olsa 		return;
531b33f9226SJiri Olsa 
532b33f9226SJiri Olsa 	__hist_entry__set_folding(he, browser, unfold);
533492b1010SNamhyung Kim 
534492b1010SNamhyung Kim 	if (!he->depth || unfold)
535492b1010SNamhyung Kim 		browser->nr_hierarchy_entries++;
536492b1010SNamhyung Kim 	if (he->leaf)
537c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
53879dded87SNamhyung Kim 	else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
53979dded87SNamhyung Kim 		browser->nr_hierarchy_entries++;
54079dded87SNamhyung Kim 		he->has_no_entry = true;
54179dded87SNamhyung Kim 		he->nr_rows = 1;
54279dded87SNamhyung Kim 	} else
54379dded87SNamhyung Kim 		he->has_no_entry = false;
544aca7a94dSNamhyung Kim }
545b33f9226SJiri Olsa 
546b33f9226SJiri Olsa static void
547b33f9226SJiri Olsa __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
548b33f9226SJiri Olsa {
549b33f9226SJiri Olsa 	struct rb_node *nd;
550b33f9226SJiri Olsa 	struct hist_entry *he;
551b33f9226SJiri Olsa 
552b33f9226SJiri Olsa 	nd = rb_first(&browser->hists->entries);
553b33f9226SJiri Olsa 	while (nd) {
554b33f9226SJiri Olsa 		he = rb_entry(nd, struct hist_entry, rb_node);
555b33f9226SJiri Olsa 
556b33f9226SJiri Olsa 		/* set folding state even if it's currently folded */
557b33f9226SJiri Olsa 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
558b33f9226SJiri Olsa 
559b33f9226SJiri Olsa 		hist_entry__set_folding(he, browser, unfold);
560b33f9226SJiri Olsa 	}
561aca7a94dSNamhyung Kim }
562aca7a94dSNamhyung Kim 
56305e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
564aca7a94dSNamhyung Kim {
565492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
566c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
567c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
568c3b78952SNamhyung Kim 
569c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
570aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
57105e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
572aca7a94dSNamhyung Kim }
573aca7a94dSNamhyung Kim 
574*0e3fa7a7SJiri Olsa static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
575*0e3fa7a7SJiri Olsa {
576*0e3fa7a7SJiri Olsa 	if (!browser->he_selection)
577*0e3fa7a7SJiri Olsa 		return;
578*0e3fa7a7SJiri Olsa 
579*0e3fa7a7SJiri Olsa 	hist_entry__set_folding(browser->he_selection, browser, unfold);
580*0e3fa7a7SJiri Olsa 	browser->b.nr_entries = hist_browser__nr_entries(browser);
581*0e3fa7a7SJiri Olsa }
582*0e3fa7a7SJiri Olsa 
583aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
584aca7a94dSNamhyung Kim {
585aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
586aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
587aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
588aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
589aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
590aca7a94dSNamhyung Kim }
591aca7a94dSNamhyung Kim 
5925b91a86fSJiri Olsa static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
5935b91a86fSJiri Olsa {
5945b91a86fSJiri Olsa 	return browser->title ? browser->title(browser, bf, size) : 0;
5955b91a86fSJiri Olsa }
5965b91a86fSJiri Olsa 
597dabd2012SJiri Olsa int hist_browser__run(struct hist_browser *browser, const char *help)
598aca7a94dSNamhyung Kim {
599aca7a94dSNamhyung Kim 	int key;
600aca7a94dSNamhyung Kim 	char title[160];
601c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
6029783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
603aca7a94dSNamhyung Kim 
60405e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
605c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
606aca7a94dSNamhyung Kim 
6075b91a86fSJiri Olsa 	hist_browser__title(browser, title, sizeof(title));
608aca7a94dSNamhyung Kim 
609090cff3eSNamhyung Kim 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
610aca7a94dSNamhyung Kim 		return -1;
611aca7a94dSNamhyung Kim 
612aca7a94dSNamhyung Kim 	while (1) {
61305e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
614aca7a94dSNamhyung Kim 
615aca7a94dSNamhyung Kim 		switch (key) {
616fa5df943SNamhyung Kim 		case K_TIMER: {
617fa5df943SNamhyung Kim 			u64 nr_entries;
6189783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
619fa5df943SNamhyung Kim 
620c6111523SNamhyung Kim 			if (hist_browser__has_filter(browser) ||
621c6111523SNamhyung Kim 			    symbol_conf.report_hierarchy)
622112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
623fa5df943SNamhyung Kim 
624c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
625fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
626aca7a94dSNamhyung Kim 
62705e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
62805e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
62905e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
63005e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
63105e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
632aca7a94dSNamhyung Kim 			}
633aca7a94dSNamhyung Kim 
6345b91a86fSJiri Olsa 			hist_browser__title(browser, title, sizeof(title));
63505e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
636aca7a94dSNamhyung Kim 			continue;
637fa5df943SNamhyung Kim 		}
638aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
639aca7a94dSNamhyung Kim 			static int seq;
64005e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
641aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
642aca7a94dSNamhyung Kim 			ui_helpline__pop();
64362c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
64405e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
64505e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
64662c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
64705e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
64805e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
649aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
650aca7a94dSNamhyung Kim 		}
651aca7a94dSNamhyung Kim 			break;
652aca7a94dSNamhyung Kim 		case 'C':
653aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
65405e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
655aca7a94dSNamhyung Kim 			break;
656*0e3fa7a7SJiri Olsa 		case 'c':
657*0e3fa7a7SJiri Olsa 			/* Collapse the selected entry. */
658*0e3fa7a7SJiri Olsa 			hist_browser__set_folding_selected(browser, false);
659*0e3fa7a7SJiri Olsa 			break;
660aca7a94dSNamhyung Kim 		case 'E':
661aca7a94dSNamhyung Kim 			/* Expand the whole world. */
66205e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
663aca7a94dSNamhyung Kim 			break;
664*0e3fa7a7SJiri Olsa 		case 'e':
665*0e3fa7a7SJiri Olsa 			/* Expand the selected entry. */
666*0e3fa7a7SJiri Olsa 			hist_browser__set_folding_selected(browser, true);
667*0e3fa7a7SJiri Olsa 			break;
668025bf7eaSArnaldo Carvalho de Melo 		case 'H':
669025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
670025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
671025bf7eaSArnaldo Carvalho de Melo 			break;
672aca7a94dSNamhyung Kim 		case K_ENTER:
67305e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
674aca7a94dSNamhyung Kim 				break;
675aca7a94dSNamhyung Kim 			/* fall thru */
676aca7a94dSNamhyung Kim 		default:
677aca7a94dSNamhyung Kim 			goto out;
678aca7a94dSNamhyung Kim 		}
679aca7a94dSNamhyung Kim 	}
680aca7a94dSNamhyung Kim out:
68105e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
682aca7a94dSNamhyung Kim 	return key;
683aca7a94dSNamhyung Kim }
684aca7a94dSNamhyung Kim 
68539ee533fSNamhyung Kim struct callchain_print_arg {
68639ee533fSNamhyung Kim 	/* for hists browser */
68739ee533fSNamhyung Kim 	off_t	row_offset;
68839ee533fSNamhyung Kim 	bool	is_current_entry;
68939ee533fSNamhyung Kim 
69039ee533fSNamhyung Kim 	/* for file dump */
69139ee533fSNamhyung Kim 	FILE	*fp;
69239ee533fSNamhyung Kim 	int	printed;
69339ee533fSNamhyung Kim };
69439ee533fSNamhyung Kim 
69539ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
69639ee533fSNamhyung Kim 					 struct callchain_list *chain,
69739ee533fSNamhyung Kim 					 const char *str, int offset,
69839ee533fSNamhyung Kim 					 unsigned short row,
69939ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
70039ee533fSNamhyung Kim 
701f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
702f4536dddSNamhyung Kim 					       struct callchain_list *chain,
70339ee533fSNamhyung Kim 					       const char *str, int offset,
70439ee533fSNamhyung Kim 					       unsigned short row,
70539ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
706f4536dddSNamhyung Kim {
707f4536dddSNamhyung Kim 	int color, width;
70839ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
70970e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
710f4536dddSNamhyung Kim 
711f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
712f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
713f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
714f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
715f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
71639ee533fSNamhyung Kim 		arg->is_current_entry = true;
717f4536dddSNamhyung Kim 	}
718f4536dddSNamhyung Kim 
719f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
720f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
72126270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
722517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
72370e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
72426270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
725f4536dddSNamhyung Kim }
726f4536dddSNamhyung Kim 
72739ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
72839ee533fSNamhyung Kim 						  struct callchain_list *chain,
72939ee533fSNamhyung Kim 						  const char *str, int offset,
73039ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
73139ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
73239ee533fSNamhyung Kim {
73339ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
73439ee533fSNamhyung Kim 
73539ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
73639ee533fSNamhyung Kim 				folded_sign, str);
73739ee533fSNamhyung Kim }
73839ee533fSNamhyung Kim 
73939ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
74039ee533fSNamhyung Kim 				     unsigned short row);
74139ee533fSNamhyung Kim 
74239ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
74339ee533fSNamhyung Kim 					    unsigned short row)
74439ee533fSNamhyung Kim {
74539ee533fSNamhyung Kim 	return browser->b.rows == row;
74639ee533fSNamhyung Kim }
74739ee533fSNamhyung Kim 
74839ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
74939ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
75039ee533fSNamhyung Kim {
75139ee533fSNamhyung Kim 	return false;
75239ee533fSNamhyung Kim }
75339ee533fSNamhyung Kim 
754aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
755aca7a94dSNamhyung Kim 
75618bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
75718bb8381SNamhyung Kim 					     struct callchain_node *node,
75818bb8381SNamhyung Kim 					     struct callchain_list *chain,
75918bb8381SNamhyung Kim 					     unsigned short row, u64 total,
76018bb8381SNamhyung Kim 					     bool need_percent, int offset,
76118bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
76218bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
76318bb8381SNamhyung Kim {
76418bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
765fef51ecdSJin Yao 	char buf[64], *alloc_str2;
76618bb8381SNamhyung Kim 	const char *str;
76718bb8381SNamhyung Kim 
76818bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
76918bb8381SNamhyung Kim 		arg->row_offset--;
77018bb8381SNamhyung Kim 		return 0;
77118bb8381SNamhyung Kim 	}
77218bb8381SNamhyung Kim 
77318bb8381SNamhyung Kim 	alloc_str = NULL;
774fef51ecdSJin Yao 	alloc_str2 = NULL;
775fef51ecdSJin Yao 
77618bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
77718bb8381SNamhyung Kim 				       browser->show_dso);
77818bb8381SNamhyung Kim 
779fef51ecdSJin Yao 	if (symbol_conf.show_branchflag_count) {
780fef51ecdSJin Yao 		if (need_percent)
781fef51ecdSJin Yao 			callchain_list_counts__printf_value(node, chain, NULL,
782fef51ecdSJin Yao 							    buf, sizeof(buf));
783fef51ecdSJin Yao 		else
784fef51ecdSJin Yao 			callchain_list_counts__printf_value(NULL, chain, NULL,
785fef51ecdSJin Yao 							    buf, sizeof(buf));
78618bb8381SNamhyung Kim 
787fef51ecdSJin Yao 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
788fef51ecdSJin Yao 			str = "Not enough memory!";
789fef51ecdSJin Yao 		else
790fef51ecdSJin Yao 			str = alloc_str2;
791fef51ecdSJin Yao 	}
792fef51ecdSJin Yao 
793fef51ecdSJin Yao 	if (need_percent) {
79418bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
79518bb8381SNamhyung Kim 						total);
79618bb8381SNamhyung Kim 
79718bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
79818bb8381SNamhyung Kim 			str = "Not enough memory!";
79918bb8381SNamhyung Kim 		else
80018bb8381SNamhyung Kim 			str = alloc_str;
80118bb8381SNamhyung Kim 	}
80218bb8381SNamhyung Kim 
80318bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
80418bb8381SNamhyung Kim 
80518bb8381SNamhyung Kim 	free(alloc_str);
806fef51ecdSJin Yao 	free(alloc_str2);
80718bb8381SNamhyung Kim 	return 1;
80818bb8381SNamhyung Kim }
80918bb8381SNamhyung Kim 
81059c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
81159c624e2SNamhyung Kim {
81259c624e2SNamhyung Kim 	struct callchain_node *child;
81359c624e2SNamhyung Kim 
81459c624e2SNamhyung Kim 	if (node == NULL)
81559c624e2SNamhyung Kim 		return false;
81659c624e2SNamhyung Kim 
81759c624e2SNamhyung Kim 	if (rb_next(node))
81859c624e2SNamhyung Kim 		return true;
81959c624e2SNamhyung Kim 
82059c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
82159c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
82259c624e2SNamhyung Kim }
82359c624e2SNamhyung Kim 
8244b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
8254b3a3212SNamhyung Kim 					     struct rb_root *root,
8264b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
82759c624e2SNamhyung Kim 					     u64 parent_total,
8284b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
8294b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
8304b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
8314b3a3212SNamhyung Kim {
8324b3a3212SNamhyung Kim 	struct rb_node *node;
8334b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
8344b3a3212SNamhyung Kim 	bool need_percent;
8354b3a3212SNamhyung Kim 
8364b3a3212SNamhyung Kim 	node = rb_first(root);
83759c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
8384b3a3212SNamhyung Kim 
8394b3a3212SNamhyung Kim 	while (node) {
8404b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
8414b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
8424b3a3212SNamhyung Kim 		struct callchain_list *chain;
8434b3a3212SNamhyung Kim 		char folded_sign = ' ';
8444b3a3212SNamhyung Kim 		int first = true;
8454b3a3212SNamhyung Kim 		int extra_offset = 0;
8464b3a3212SNamhyung Kim 
8474b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
8484b3a3212SNamhyung Kim 			bool was_first = first;
8494b3a3212SNamhyung Kim 
8504b3a3212SNamhyung Kim 			if (first)
8514b3a3212SNamhyung Kim 				first = false;
8524b3a3212SNamhyung Kim 			else if (need_percent)
8534b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8544b3a3212SNamhyung Kim 
8554b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8564b3a3212SNamhyung Kim 
8574b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8584b3a3212SNamhyung Kim 							chain, row, total,
8594b3a3212SNamhyung Kim 							was_first && need_percent,
8604b3a3212SNamhyung Kim 							offset + extra_offset,
8614b3a3212SNamhyung Kim 							print, arg);
8624b3a3212SNamhyung Kim 
8634b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8644b3a3212SNamhyung Kim 				goto out;
8654b3a3212SNamhyung Kim 
8664b3a3212SNamhyung Kim 			if (folded_sign == '+')
8674b3a3212SNamhyung Kim 				goto next;
8684b3a3212SNamhyung Kim 		}
8694b3a3212SNamhyung Kim 
8704b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
8714b3a3212SNamhyung Kim 			bool was_first = first;
8724b3a3212SNamhyung Kim 
8734b3a3212SNamhyung Kim 			if (first)
8744b3a3212SNamhyung Kim 				first = false;
8754b3a3212SNamhyung Kim 			else if (need_percent)
8764b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8774b3a3212SNamhyung Kim 
8784b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8794b3a3212SNamhyung Kim 
8804b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8814b3a3212SNamhyung Kim 							chain, row, total,
8824b3a3212SNamhyung Kim 							was_first && need_percent,
8834b3a3212SNamhyung Kim 							offset + extra_offset,
8844b3a3212SNamhyung Kim 							print, arg);
8854b3a3212SNamhyung Kim 
8864b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8874b3a3212SNamhyung Kim 				goto out;
8884b3a3212SNamhyung Kim 
8894b3a3212SNamhyung Kim 			if (folded_sign == '+')
8904b3a3212SNamhyung Kim 				break;
8914b3a3212SNamhyung Kim 		}
8924b3a3212SNamhyung Kim 
8934b3a3212SNamhyung Kim next:
8944b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
8954b3a3212SNamhyung Kim 			break;
8964b3a3212SNamhyung Kim 		node = next;
8974b3a3212SNamhyung Kim 	}
8984b3a3212SNamhyung Kim out:
8994b3a3212SNamhyung Kim 	return row - first_row;
9004b3a3212SNamhyung Kim }
9014b3a3212SNamhyung Kim 
9028c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
9038c430a34SNamhyung Kim 						struct callchain_list *chain,
9048c430a34SNamhyung Kim 						char *value_str, char *old_str)
9058c430a34SNamhyung Kim {
9068c430a34SNamhyung Kim 	char bf[1024];
9078c430a34SNamhyung Kim 	const char *str;
9088c430a34SNamhyung Kim 	char *new;
9098c430a34SNamhyung Kim 
9108c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
9118c430a34SNamhyung Kim 				       browser->show_dso);
9128c430a34SNamhyung Kim 	if (old_str) {
9138c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
9148c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
9158c430a34SNamhyung Kim 			new = NULL;
9168c430a34SNamhyung Kim 	} else {
9178c430a34SNamhyung Kim 		if (value_str) {
9188c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
9198c430a34SNamhyung Kim 				new = NULL;
9208c430a34SNamhyung Kim 		} else {
9218c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
9228c430a34SNamhyung Kim 				new = NULL;
9238c430a34SNamhyung Kim 		}
9248c430a34SNamhyung Kim 	}
9258c430a34SNamhyung Kim 	return new;
9268c430a34SNamhyung Kim }
9278c430a34SNamhyung Kim 
9288c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
9298c430a34SNamhyung Kim 					       struct rb_root *root,
9308c430a34SNamhyung Kim 					       unsigned short row, u64 total,
93159c624e2SNamhyung Kim 					       u64 parent_total,
9328c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
9338c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
9348c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
9358c430a34SNamhyung Kim {
9368c430a34SNamhyung Kim 	struct rb_node *node;
9378c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
9388c430a34SNamhyung Kim 	bool need_percent;
9398c430a34SNamhyung Kim 
9408c430a34SNamhyung Kim 	node = rb_first(root);
94159c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9428c430a34SNamhyung Kim 
9438c430a34SNamhyung Kim 	while (node) {
9448c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
9458c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
9468c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
9478c430a34SNamhyung Kim 		int first = true;
9488c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
9498c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
9508c430a34SNamhyung Kim 
9518c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
9528c430a34SNamhyung Kim 			arg->row_offset--;
9538c430a34SNamhyung Kim 			goto next;
9548c430a34SNamhyung Kim 		}
9558c430a34SNamhyung Kim 
9568c430a34SNamhyung Kim 		if (need_percent) {
9578c430a34SNamhyung Kim 			char buf[64];
9588c430a34SNamhyung Kim 
9598c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
9608c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
9618c430a34SNamhyung Kim 				value_str = (char *)"<...>";
9628c430a34SNamhyung Kim 				goto do_print;
9638c430a34SNamhyung Kim 			}
9648c430a34SNamhyung Kim 			value_str_alloc = value_str;
9658c430a34SNamhyung Kim 		}
9668c430a34SNamhyung Kim 
9678c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9688c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9698c430a34SNamhyung Kim 						chain, value_str, chain_str);
9708c430a34SNamhyung Kim 			if (first) {
9718c430a34SNamhyung Kim 				first = false;
9728c430a34SNamhyung Kim 				first_chain = chain;
9738c430a34SNamhyung Kim 			}
9748c430a34SNamhyung Kim 
9758c430a34SNamhyung Kim 			if (chain_str == NULL) {
9768c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9778c430a34SNamhyung Kim 				goto do_print;
9788c430a34SNamhyung Kim 			}
9798c430a34SNamhyung Kim 
9808c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9818c430a34SNamhyung Kim 		}
9828c430a34SNamhyung Kim 
9838c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
9848c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9858c430a34SNamhyung Kim 						chain, value_str, chain_str);
9868c430a34SNamhyung Kim 			if (first) {
9878c430a34SNamhyung Kim 				first = false;
9888c430a34SNamhyung Kim 				first_chain = chain;
9898c430a34SNamhyung Kim 			}
9908c430a34SNamhyung Kim 
9918c430a34SNamhyung Kim 			if (chain_str == NULL) {
9928c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9938c430a34SNamhyung Kim 				goto do_print;
9948c430a34SNamhyung Kim 			}
9958c430a34SNamhyung Kim 
9968c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9978c430a34SNamhyung Kim 		}
9988c430a34SNamhyung Kim 
9998c430a34SNamhyung Kim do_print:
10008c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
10018c430a34SNamhyung Kim 		free(value_str_alloc);
10028c430a34SNamhyung Kim 		free(chain_str_alloc);
10038c430a34SNamhyung Kim 
10048c430a34SNamhyung Kim next:
10058c430a34SNamhyung Kim 		if (is_output_full(browser, row))
10068c430a34SNamhyung Kim 			break;
10078c430a34SNamhyung Kim 		node = next;
10088c430a34SNamhyung Kim 	}
10098c430a34SNamhyung Kim 
10108c430a34SNamhyung Kim 	return row - first_row;
10118c430a34SNamhyung Kim }
10128c430a34SNamhyung Kim 
10130c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1014c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
101539ee533fSNamhyung Kim 					unsigned short row, u64 total,
10165eca104eSNamhyung Kim 					u64 parent_total,
101739ee533fSNamhyung Kim 					print_callchain_entry_fn print,
101839ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
101939ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
1020aca7a94dSNamhyung Kim {
1021aca7a94dSNamhyung Kim 	struct rb_node *node;
1022f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
10234087d11cSNamhyung Kim 	bool need_percent;
10245eca104eSNamhyung Kim 	u64 percent_total = total;
10255eca104eSNamhyung Kim 
10265eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
10275eca104eSNamhyung Kim 		percent_total = parent_total;
1028aca7a94dSNamhyung Kim 
1029c09a7e75SNamhyung Kim 	node = rb_first(root);
103059c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
10314087d11cSNamhyung Kim 
1032aca7a94dSNamhyung Kim 	while (node) {
1033aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1034aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
1035aca7a94dSNamhyung Kim 		struct callchain_list *chain;
1036aca7a94dSNamhyung Kim 		char folded_sign = ' ';
1037aca7a94dSNamhyung Kim 		int first = true;
1038aca7a94dSNamhyung Kim 		int extra_offset = 0;
1039aca7a94dSNamhyung Kim 
1040aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
1041aca7a94dSNamhyung Kim 			bool was_first = first;
1042aca7a94dSNamhyung Kim 
1043aca7a94dSNamhyung Kim 			if (first)
1044aca7a94dSNamhyung Kim 				first = false;
10454087d11cSNamhyung Kim 			else if (need_percent)
1046aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1047aca7a94dSNamhyung Kim 
1048aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1049aca7a94dSNamhyung Kim 
105018bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
10515eca104eSNamhyung Kim 							chain, row, percent_total,
105218bb8381SNamhyung Kim 							was_first && need_percent,
105318bb8381SNamhyung Kim 							offset + extra_offset,
105418bb8381SNamhyung Kim 							print, arg);
1055c09a7e75SNamhyung Kim 
105618bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1057aca7a94dSNamhyung Kim 				goto out;
105818bb8381SNamhyung Kim 
1059aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1060aca7a94dSNamhyung Kim 				break;
1061aca7a94dSNamhyung Kim 		}
1062aca7a94dSNamhyung Kim 
1063aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1064aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1065c09a7e75SNamhyung Kim 
10660c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
10675eca104eSNamhyung Kim 							    new_level, row, total,
10685eca104eSNamhyung Kim 							    child->children_hit,
106939ee533fSNamhyung Kim 							    print, arg, is_output_full);
1070aca7a94dSNamhyung Kim 		}
107139ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1072c09a7e75SNamhyung Kim 			break;
1073aca7a94dSNamhyung Kim 		node = next;
1074aca7a94dSNamhyung Kim 	}
1075aca7a94dSNamhyung Kim out:
1076aca7a94dSNamhyung Kim 	return row - first_row;
1077aca7a94dSNamhyung Kim }
1078aca7a94dSNamhyung Kim 
10790c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
10800c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
10810c841c6cSNamhyung Kim 					unsigned short row,
10820c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
10830c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
10840c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
10850c841c6cSNamhyung Kim {
10860c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
10875eca104eSNamhyung Kim 	u64 parent_total;
10880c841c6cSNamhyung Kim 	int printed;
10890c841c6cSNamhyung Kim 
10900c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
10915eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
10920c841c6cSNamhyung Kim 	else
10935eca104eSNamhyung Kim 		parent_total = entry->stat.period;
10940c841c6cSNamhyung Kim 
10950c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
10960c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
10975eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10985eca104eSNamhyung Kim 						total, parent_total, print, arg,
10995eca104eSNamhyung Kim 						is_output_full);
11000c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
11010c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
11025eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11035eca104eSNamhyung Kim 						total, parent_total, print, arg,
11045eca104eSNamhyung Kim 						is_output_full);
11050c841c6cSNamhyung Kim 	} else {
11060c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
11075eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
11085eca104eSNamhyung Kim 						total, parent_total, print, arg,
11095eca104eSNamhyung Kim 						is_output_full);
11100c841c6cSNamhyung Kim 	}
11110c841c6cSNamhyung Kim 
11120c841c6cSNamhyung Kim 	if (arg->is_current_entry)
11130c841c6cSNamhyung Kim 		browser->he_selection = entry;
11140c841c6cSNamhyung Kim 
11150c841c6cSNamhyung Kim 	return printed;
11160c841c6cSNamhyung Kim }
11170c841c6cSNamhyung Kim 
111889701460SNamhyung Kim struct hpp_arg {
111989701460SNamhyung Kim 	struct ui_browser *b;
112089701460SNamhyung Kim 	char folded_sign;
112189701460SNamhyung Kim 	bool current_entry;
112289701460SNamhyung Kim };
112389701460SNamhyung Kim 
112498ba1609SJiri Olsa int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
11252f6d9009SNamhyung Kim {
11262f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1127d675107cSNamhyung Kim 	int ret, len;
11282f6d9009SNamhyung Kim 	va_list args;
11292f6d9009SNamhyung Kim 	double percent;
11302f6d9009SNamhyung Kim 
11312f6d9009SNamhyung Kim 	va_start(args, fmt);
1132d675107cSNamhyung Kim 	len = va_arg(args, int);
11332f6d9009SNamhyung Kim 	percent = va_arg(args, double);
11342f6d9009SNamhyung Kim 	va_end(args);
11355aed9d24SNamhyung Kim 
113689701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
11375aed9d24SNamhyung Kim 
1138d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1139517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
114089701460SNamhyung Kim 
11415aed9d24SNamhyung Kim 	return ret;
1142f5951d56SNamhyung Kim }
1143f5951d56SNamhyung Kim 
1144fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
11455aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
11465aed9d24SNamhyung Kim {									\
11475aed9d24SNamhyung Kim 	return he->stat._field;						\
11485aed9d24SNamhyung Kim }									\
11495aed9d24SNamhyung Kim 									\
11502c5d4b4aSJiri Olsa static int								\
11515b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11522c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
11535aed9d24SNamhyung Kim 				struct hist_entry *he)			\
11545aed9d24SNamhyung Kim {									\
11555b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
11562f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
11575aed9d24SNamhyung Kim }
1158f5951d56SNamhyung Kim 
11590434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
11600434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
11610434ddd2SNamhyung Kim {									\
11620434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
11630434ddd2SNamhyung Kim }									\
11640434ddd2SNamhyung Kim 									\
11650434ddd2SNamhyung Kim static int								\
11665b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11670434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
11680434ddd2SNamhyung Kim 				struct hist_entry *he)			\
11690434ddd2SNamhyung Kim {									\
11700434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1171517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
11725b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1173d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
11745b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1175517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
11760434ddd2SNamhyung Kim 									\
11770434ddd2SNamhyung Kim 		return ret;						\
11780434ddd2SNamhyung Kim 	}								\
11795b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
11805b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
11810434ddd2SNamhyung Kim }
11820434ddd2SNamhyung Kim 
1183fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1184fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1185fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1186fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1187fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
11880434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
11895aed9d24SNamhyung Kim 
11905aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
11910434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1192f5951d56SNamhyung Kim 
1193f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1194f5951d56SNamhyung Kim {
1195f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1196f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1197f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1198f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1199f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1200f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1201f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1202f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1203f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1204f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
12050434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
12060434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
1207f5951d56SNamhyung Kim }
1208f5951d56SNamhyung Kim 
120905e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1210aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1211aca7a94dSNamhyung Kim 				    unsigned short row)
1212aca7a94dSNamhyung Kim {
12131240005eSJiri Olsa 	int printed = 0;
121467d25916SNamhyung Kim 	int width = browser->b.width;
1215aca7a94dSNamhyung Kim 	char folded_sign = ' ';
121605e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1217aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
121863a1a3d8SNamhyung Kim 	bool first = true;
12191240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1220aca7a94dSNamhyung Kim 
1221aca7a94dSNamhyung Kim 	if (current_entry) {
122205e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
122305e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1224aca7a94dSNamhyung Kim 	}
1225aca7a94dSNamhyung Kim 
1226aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
1227aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1228aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1229aca7a94dSNamhyung Kim 	}
1230aca7a94dSNamhyung Kim 
1231aca7a94dSNamhyung Kim 	if (row_offset == 0) {
123289701460SNamhyung Kim 		struct hpp_arg arg = {
123389701460SNamhyung Kim 			.b		= &browser->b,
123489701460SNamhyung Kim 			.folded_sign	= folded_sign,
123589701460SNamhyung Kim 			.current_entry	= current_entry,
123689701460SNamhyung Kim 		};
1237c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1238f5951d56SNamhyung Kim 
1239ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
1240f5951d56SNamhyung Kim 
1241f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
124289fee709SArnaldo Carvalho de Melo 			char s[2048];
124389fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
124489fee709SArnaldo Carvalho de Melo 				.buf	= s,
124589fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
124689fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
124789fee709SArnaldo Carvalho de Melo 			};
124889fee709SArnaldo Carvalho de Melo 
1249361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1250361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1251e67d49a7SNamhyung Kim 				continue;
1252e67d49a7SNamhyung Kim 
1253fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1254fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1255fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1256fb821c9eSNamhyung Kim 			} else {
1257fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1258fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1259fb821c9eSNamhyung Kim 			}
1260fb821c9eSNamhyung Kim 
1261fb821c9eSNamhyung Kim 			if (first) {
1262fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
1263517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1264f5951d56SNamhyung Kim 					width -= 2;
1265f5951d56SNamhyung Kim 				}
126663a1a3d8SNamhyung Kim 				first = false;
1267fb821c9eSNamhyung Kim 			} else {
1268517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1269fb821c9eSNamhyung Kim 				width -= 2;
1270fb821c9eSNamhyung Kim 			}
1271f5951d56SNamhyung Kim 
12721240005eSJiri Olsa 			if (fmt->color) {
127389fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
127489fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
127589fee709SArnaldo Carvalho de Melo 				/*
127689fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
127789fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
127889fee709SArnaldo Carvalho de Melo 				 */
127989fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1280f5951d56SNamhyung Kim 			} else {
128189fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1282517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1283f5951d56SNamhyung Kim 			}
128489fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1285f5951d56SNamhyung Kim 		}
1286aca7a94dSNamhyung Kim 
1287aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
128805e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1289aca7a94dSNamhyung Kim 			width += 1;
1290aca7a94dSNamhyung Kim 
129126270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
129226d8b338SNamhyung Kim 
1293aca7a94dSNamhyung Kim 		++row;
1294aca7a94dSNamhyung Kim 		++printed;
1295aca7a94dSNamhyung Kim 	} else
1296aca7a94dSNamhyung Kim 		--row_offset;
1297aca7a94dSNamhyung Kim 
129862c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
129939ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
130039ee533fSNamhyung Kim 			.row_offset = row_offset,
130139ee533fSNamhyung Kim 			.is_current_entry = current_entry,
130239ee533fSNamhyung Kim 		};
1303c09a7e75SNamhyung Kim 
13040c841c6cSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry, 1, row,
13054b3a3212SNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
13064b3a3212SNamhyung Kim 					hist_browser__check_output_full);
1307aca7a94dSNamhyung Kim 	}
1308aca7a94dSNamhyung Kim 
1309aca7a94dSNamhyung Kim 	return printed;
1310aca7a94dSNamhyung Kim }
1311aca7a94dSNamhyung Kim 
1312d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1313d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1314d0506edbSNamhyung Kim 					      unsigned short row,
13152dbbe9f2SNamhyung Kim 					      int level)
1316d0506edbSNamhyung Kim {
1317d0506edbSNamhyung Kim 	int printed = 0;
1318d0506edbSNamhyung Kim 	int width = browser->b.width;
1319d0506edbSNamhyung Kim 	char folded_sign = ' ';
1320d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1321d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1322d0506edbSNamhyung Kim 	bool first = true;
1323d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1324a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1325d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1326d0506edbSNamhyung Kim 		.b		= &browser->b,
1327d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1328d0506edbSNamhyung Kim 	};
1329d0506edbSNamhyung Kim 	int column = 0;
13302dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1331d0506edbSNamhyung Kim 
1332d0506edbSNamhyung Kim 	if (current_entry) {
1333d0506edbSNamhyung Kim 		browser->he_selection = entry;
1334d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1335d0506edbSNamhyung Kim 	}
1336d0506edbSNamhyung Kim 
1337d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1338d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1339d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1340d0506edbSNamhyung Kim 
1341d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1342d0506edbSNamhyung Kim 		row_offset--;
1343d0506edbSNamhyung Kim 		goto show_callchain;
1344d0506edbSNamhyung Kim 	}
1345d0506edbSNamhyung Kim 
1346d0506edbSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
1347d0506edbSNamhyung Kim 
1348d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1349d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1350d0506edbSNamhyung Kim 	else
1351d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1352d0506edbSNamhyung Kim 
1353d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1354d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1355d0506edbSNamhyung Kim 
1356a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1357a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1358a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1359a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1360d0506edbSNamhyung Kim 		char s[2048];
1361d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1362d0506edbSNamhyung Kim 			.buf		= s,
1363d0506edbSNamhyung Kim 			.size		= sizeof(s),
1364d0506edbSNamhyung Kim 			.ptr		= &arg,
1365d0506edbSNamhyung Kim 		};
1366d0506edbSNamhyung Kim 
1367d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1368d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1369d0506edbSNamhyung Kim 			continue;
1370d0506edbSNamhyung Kim 
1371d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1372d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1373d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1374d0506edbSNamhyung Kim 		} else {
1375d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1376d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1377d0506edbSNamhyung Kim 		}
1378d0506edbSNamhyung Kim 
1379d0506edbSNamhyung Kim 		if (first) {
1380d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c ", folded_sign);
13813d9f4683SNamhyung Kim 			width -= 2;
1382d0506edbSNamhyung Kim 			first = false;
1383d0506edbSNamhyung Kim 		} else {
1384d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1385d0506edbSNamhyung Kim 			width -= 2;
1386d0506edbSNamhyung Kim 		}
1387d0506edbSNamhyung Kim 
1388d0506edbSNamhyung Kim 		if (fmt->color) {
1389d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1390d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1391d0506edbSNamhyung Kim 			/*
1392d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1393d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1394d0506edbSNamhyung Kim 			 */
1395d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1396d0506edbSNamhyung Kim 		} else {
1397d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1398d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1399d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1400d0506edbSNamhyung Kim 		}
1401d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1402d0506edbSNamhyung Kim 	}
1403d0506edbSNamhyung Kim 
1404b9bf911eSNamhyung Kim 	if (!first) {
1405d0506edbSNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1406d0506edbSNamhyung Kim 		width -= hierarchy_indent;
1407b9bf911eSNamhyung Kim 	}
1408d0506edbSNamhyung Kim 
1409d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1410d0506edbSNamhyung Kim 		char s[2048];
1411d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1412d0506edbSNamhyung Kim 			.buf		= s,
1413d0506edbSNamhyung Kim 			.size		= sizeof(s),
1414d0506edbSNamhyung Kim 			.ptr		= &arg,
1415d0506edbSNamhyung Kim 		};
1416d0506edbSNamhyung Kim 
1417d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1418d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1419d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1420d0506edbSNamhyung Kim 		} else {
1421d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1422d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1423d0506edbSNamhyung Kim 		}
1424d0506edbSNamhyung Kim 
14251b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1426131d51ebSNamhyung Kim 			if (first) {
1427131d51ebSNamhyung Kim 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1428131d51ebSNamhyung Kim 				first = false;
1429131d51ebSNamhyung Kim 			} else {
1430d0506edbSNamhyung Kim 				ui_browser__write_nstring(&browser->b, "", 2);
1431131d51ebSNamhyung Kim 			}
1432131d51ebSNamhyung Kim 
1433d0506edbSNamhyung Kim 			width -= 2;
1434d0506edbSNamhyung Kim 
1435d0506edbSNamhyung Kim 			/*
1436d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1437d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1438d0506edbSNamhyung Kim 			 * hierarchy mode.
1439d0506edbSNamhyung Kim 			 */
1440d0506edbSNamhyung Kim 			if (fmt->color) {
1441d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1442d0506edbSNamhyung Kim 			} else {
1443cb1fab91SNamhyung Kim 				int i = 0;
1444cb1fab91SNamhyung Kim 
1445d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
1446cb1fab91SNamhyung Kim 				ui_browser__printf(&browser->b, "%s", ltrim(s));
1447cb1fab91SNamhyung Kim 
1448cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1449cb1fab91SNamhyung Kim 					width++;
1450d0506edbSNamhyung Kim 			}
1451d0506edbSNamhyung Kim 		}
14521b2dbbf4SNamhyung Kim 	}
1453d0506edbSNamhyung Kim 
1454d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1455d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1456d0506edbSNamhyung Kim 		width += 1;
1457d0506edbSNamhyung Kim 
1458d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1459d0506edbSNamhyung Kim 
1460d0506edbSNamhyung Kim 	++row;
1461d0506edbSNamhyung Kim 	++printed;
1462d0506edbSNamhyung Kim 
1463d0506edbSNamhyung Kim show_callchain:
1464d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1465d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1466d0506edbSNamhyung Kim 			.row_offset = row_offset,
1467d0506edbSNamhyung Kim 		};
1468d0506edbSNamhyung Kim 
1469d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1470d0506edbSNamhyung Kim 					level + 1, row,
1471d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1472d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1473d0506edbSNamhyung Kim 	}
1474d0506edbSNamhyung Kim 
1475d0506edbSNamhyung Kim 	return printed;
1476d0506edbSNamhyung Kim }
1477d0506edbSNamhyung Kim 
147879dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
14792dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
148079dded87SNamhyung Kim {
148179dded87SNamhyung Kim 	int width = browser->b.width;
148279dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
148379dded87SNamhyung Kim 	bool first = true;
148479dded87SNamhyung Kim 	int column = 0;
148579dded87SNamhyung Kim 	int ret;
148679dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1487a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
14882dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
148979dded87SNamhyung Kim 
149079dded87SNamhyung Kim 	if (current_entry) {
149179dded87SNamhyung Kim 		browser->he_selection = NULL;
149279dded87SNamhyung Kim 		browser->selection = NULL;
149379dded87SNamhyung Kim 	}
149479dded87SNamhyung Kim 
149579dded87SNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
149679dded87SNamhyung Kim 
149779dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
149879dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
149979dded87SNamhyung Kim 	else
150079dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
150179dded87SNamhyung Kim 
150279dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
150379dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
150479dded87SNamhyung Kim 
1505a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1506a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1507a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1508a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
150979dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
151079dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
151179dded87SNamhyung Kim 			continue;
151279dded87SNamhyung Kim 
1513da1b0407SJiri Olsa 		ret = fmt->width(fmt, NULL, browser->hists);
151479dded87SNamhyung Kim 
151579dded87SNamhyung Kim 		if (first) {
151679dded87SNamhyung Kim 			/* for folded sign */
151779dded87SNamhyung Kim 			first = false;
151879dded87SNamhyung Kim 			ret++;
151979dded87SNamhyung Kim 		} else {
152079dded87SNamhyung Kim 			/* space between columns */
152179dded87SNamhyung Kim 			ret += 2;
152279dded87SNamhyung Kim 		}
152379dded87SNamhyung Kim 
152479dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
152579dded87SNamhyung Kim 		width -= ret;
152679dded87SNamhyung Kim 	}
152779dded87SNamhyung Kim 
15282dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
15292dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
153079dded87SNamhyung Kim 
153179dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
153279dded87SNamhyung Kim 		char buf[32];
153379dded87SNamhyung Kim 
153479dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
153579dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
153679dded87SNamhyung Kim 		width -= ret + 2;
153779dded87SNamhyung Kim 	}
153879dded87SNamhyung Kim 
153979dded87SNamhyung Kim 	/* The scroll bar isn't being used */
154079dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
154179dded87SNamhyung Kim 		width += 1;
154279dded87SNamhyung Kim 
154379dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
154479dded87SNamhyung Kim 	return 1;
154579dded87SNamhyung Kim }
154679dded87SNamhyung Kim 
154781a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
154881a888feSJiri Olsa {
154981a888feSJiri Olsa 	advance_hpp(hpp, inc);
155081a888feSJiri Olsa 	return hpp->size <= 0;
155181a888feSJiri Olsa }
155281a888feSJiri Olsa 
155369705b35SJiri Olsa static int
155469705b35SJiri Olsa hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
155569705b35SJiri Olsa 				 size_t size, int line)
155681a888feSJiri Olsa {
1557c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
155881a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
155981a888feSJiri Olsa 		.buf    = buf,
156081a888feSJiri Olsa 		.size   = size,
156181a888feSJiri Olsa 	};
156281a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
156381a888feSJiri Olsa 	size_t ret = 0;
1564c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
156529659ab4SJiri Olsa 	int span = 0;
156681a888feSJiri Olsa 
156781a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
156881a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
156981a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
157081a888feSJiri Olsa 			return ret;
157181a888feSJiri Olsa 	}
157281a888feSJiri Olsa 
1573f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1574361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
157581a888feSJiri Olsa 			continue;
157681a888feSJiri Olsa 
157729659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
157881a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
157981a888feSJiri Olsa 			break;
158081a888feSJiri Olsa 
158129659ab4SJiri Olsa 		if (span)
158229659ab4SJiri Olsa 			continue;
158329659ab4SJiri Olsa 
158481a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
158581a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
158681a888feSJiri Olsa 			break;
158781a888feSJiri Olsa 	}
158881a888feSJiri Olsa 
158981a888feSJiri Olsa 	return ret;
159081a888feSJiri Olsa }
159181a888feSJiri Olsa 
1592d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1593d8b92400SNamhyung Kim {
1594d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1595d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1596d8b92400SNamhyung Kim 		.buf    = buf,
1597d8b92400SNamhyung Kim 		.size   = size,
1598d8b92400SNamhyung Kim 	};
1599d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1600a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1601d8b92400SNamhyung Kim 	size_t ret = 0;
1602d8b92400SNamhyung Kim 	int column = 0;
16032dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1604a61a22f6SNamhyung Kim 	bool first_node, first_col;
1605d8b92400SNamhyung Kim 
1606d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, "  ");
1607d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1608d8b92400SNamhyung Kim 		return ret;
1609d8b92400SNamhyung Kim 
1610b9bf911eSNamhyung Kim 	first_node = true;
1611a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1612a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1613a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1614a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1615d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1616d8b92400SNamhyung Kim 			continue;
1617d8b92400SNamhyung Kim 
161829659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1619d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1620d8b92400SNamhyung Kim 			break;
1621d8b92400SNamhyung Kim 
1622d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1623d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1624d8b92400SNamhyung Kim 			break;
1625b9bf911eSNamhyung Kim 
1626b9bf911eSNamhyung Kim 		first_node = false;
1627d8b92400SNamhyung Kim 	}
1628d8b92400SNamhyung Kim 
1629b9bf911eSNamhyung Kim 	if (!first_node) {
1630d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
16312dbbe9f2SNamhyung Kim 				indent * HIERARCHY_INDENT, "");
1632d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1633d8b92400SNamhyung Kim 			return ret;
1634b9bf911eSNamhyung Kim 	}
1635d8b92400SNamhyung Kim 
1636a61a22f6SNamhyung Kim 	first_node = true;
1637a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1638a61a22f6SNamhyung Kim 		if (!first_node) {
1639d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1640d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1641d8b92400SNamhyung Kim 				break;
1642d8b92400SNamhyung Kim 		}
1643a61a22f6SNamhyung Kim 		first_node = false;
1644a61a22f6SNamhyung Kim 
1645a61a22f6SNamhyung Kim 		first_col = true;
1646a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1647a61a22f6SNamhyung Kim 			char *start;
1648a61a22f6SNamhyung Kim 
1649a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1650a61a22f6SNamhyung Kim 				continue;
1651a61a22f6SNamhyung Kim 
1652a61a22f6SNamhyung Kim 			if (!first_col) {
1653a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1654a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1655a61a22f6SNamhyung Kim 					break;
1656a61a22f6SNamhyung Kim 			}
1657a61a22f6SNamhyung Kim 			first_col = false;
1658d8b92400SNamhyung Kim 
165929659ab4SJiri Olsa 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1660d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1661d8b92400SNamhyung Kim 
16627d6a7e78SJiri Olsa 			start = trim(dummy_hpp.buf);
1663cb1fab91SNamhyung Kim 			ret = strlen(start);
1664cb1fab91SNamhyung Kim 
1665cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1666cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1667cb1fab91SNamhyung Kim 
1668d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1669d8b92400SNamhyung Kim 				break;
1670d8b92400SNamhyung Kim 		}
1671a61a22f6SNamhyung Kim 	}
1672d8b92400SNamhyung Kim 
1673d8b92400SNamhyung Kim 	return ret;
1674d8b92400SNamhyung Kim }
1675d8b92400SNamhyung Kim 
167601b4770dSJiri Olsa static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1677025bf7eaSArnaldo Carvalho de Melo {
167881a888feSJiri Olsa 	char headers[1024];
167981a888feSJiri Olsa 
1680d8b92400SNamhyung Kim 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1681d8b92400SNamhyung Kim 						   sizeof(headers));
168201b4770dSJiri Olsa 
1683025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1684025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
168526270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1686025bf7eaSArnaldo Carvalho de Melo }
1687025bf7eaSArnaldo Carvalho de Melo 
168801b4770dSJiri Olsa static void hists_browser__headers(struct hist_browser *browser)
168901b4770dSJiri Olsa {
169069705b35SJiri Olsa 	struct hists *hists = browser->hists;
169169705b35SJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
169269705b35SJiri Olsa 
169369705b35SJiri Olsa 	int line;
169469705b35SJiri Olsa 
169569705b35SJiri Olsa 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
169601b4770dSJiri Olsa 		char headers[1024];
169701b4770dSJiri Olsa 
169801b4770dSJiri Olsa 		hists_browser__scnprintf_headers(browser, headers,
169969705b35SJiri Olsa 						 sizeof(headers), line);
170001b4770dSJiri Olsa 
170169705b35SJiri Olsa 		ui_browser__gotorc(&browser->b, line, 0);
170201b4770dSJiri Olsa 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
170301b4770dSJiri Olsa 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
170401b4770dSJiri Olsa 	}
170569705b35SJiri Olsa }
170601b4770dSJiri Olsa 
170701b4770dSJiri Olsa static void hist_browser__show_headers(struct hist_browser *browser)
170801b4770dSJiri Olsa {
170901b4770dSJiri Olsa 	if (symbol_conf.report_hierarchy)
171001b4770dSJiri Olsa 		hists_browser__hierarchy_headers(browser);
171101b4770dSJiri Olsa 	else
171201b4770dSJiri Olsa 		hists_browser__headers(browser);
171301b4770dSJiri Olsa }
171401b4770dSJiri Olsa 
1715aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1716aca7a94dSNamhyung Kim {
1717aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1718aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1719aca7a94dSNamhyung Kim 
1720aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
1721aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
1722aca7a94dSNamhyung Kim 	}
1723aca7a94dSNamhyung Kim }
1724aca7a94dSNamhyung Kim 
172505e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1726aca7a94dSNamhyung Kim {
1727aca7a94dSNamhyung Kim 	unsigned row = 0;
1728025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
1729aca7a94dSNamhyung Kim 	struct rb_node *nd;
173005e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1731f8e6710dSJiri Olsa 	struct hists *hists = hb->hists;
1732aca7a94dSNamhyung Kim 
1733025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
1734f8e6710dSJiri Olsa 		struct perf_hpp_list *hpp_list = hists->hpp_list;
1735f8e6710dSJiri Olsa 
1736025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1737f8e6710dSJiri Olsa 		header_offset = hpp_list->nr_header_lines;
1738025bf7eaSArnaldo Carvalho de Melo 	}
1739025bf7eaSArnaldo Carvalho de Melo 
174005e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1741979d2cacSWang Nan 	hb->he_selection = NULL;
1742979d2cacSWang Nan 	hb->selection = NULL;
1743aca7a94dSNamhyung Kim 
1744d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1745aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
174614135663SNamhyung Kim 		float percent;
1747aca7a94dSNamhyung Kim 
1748d0506edbSNamhyung Kim 		if (h->filtered) {
1749d0506edbSNamhyung Kim 			/* let it move to sibling */
1750d0506edbSNamhyung Kim 			h->unfolded = false;
1751aca7a94dSNamhyung Kim 			continue;
1752d0506edbSNamhyung Kim 		}
1753aca7a94dSNamhyung Kim 
175414135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
1755064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1756064f1981SNamhyung Kim 			continue;
1757064f1981SNamhyung Kim 
1758d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1759d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
17602dbbe9f2SNamhyung Kim 								  h->depth);
176179dded87SNamhyung Kim 			if (row == browser->rows)
176279dded87SNamhyung Kim 				break;
176379dded87SNamhyung Kim 
176479dded87SNamhyung Kim 			if (h->has_no_entry) {
1765a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
176679dded87SNamhyung Kim 				row++;
176779dded87SNamhyung Kim 			}
1768d0506edbSNamhyung Kim 		} else {
1769aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1770d0506edbSNamhyung Kim 		}
1771d0506edbSNamhyung Kim 
177262c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1773aca7a94dSNamhyung Kim 			break;
1774aca7a94dSNamhyung Kim 	}
1775aca7a94dSNamhyung Kim 
1776025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
1777aca7a94dSNamhyung Kim }
1778aca7a94dSNamhyung Kim 
1779064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1780064f1981SNamhyung Kim 					     float min_pcnt)
1781aca7a94dSNamhyung Kim {
1782aca7a94dSNamhyung Kim 	while (nd != NULL) {
1783aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
178414135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1785064f1981SNamhyung Kim 
1786c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1787aca7a94dSNamhyung Kim 			return nd;
1788aca7a94dSNamhyung Kim 
1789d0506edbSNamhyung Kim 		/*
1790d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1791d0506edbSNamhyung Kim 		 * So move to sibling node.
1792d0506edbSNamhyung Kim 		 */
1793d0506edbSNamhyung Kim 		if (rb_next(nd))
1794aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1795d0506edbSNamhyung Kim 		else
1796d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1797aca7a94dSNamhyung Kim 	}
1798aca7a94dSNamhyung Kim 
1799aca7a94dSNamhyung Kim 	return NULL;
1800aca7a94dSNamhyung Kim }
1801aca7a94dSNamhyung Kim 
1802064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1803064f1981SNamhyung Kim 						  float min_pcnt)
1804aca7a94dSNamhyung Kim {
1805aca7a94dSNamhyung Kim 	while (nd != NULL) {
1806aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
180714135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1808064f1981SNamhyung Kim 
1809064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1810aca7a94dSNamhyung Kim 			return nd;
1811aca7a94dSNamhyung Kim 
1812d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1813aca7a94dSNamhyung Kim 	}
1814aca7a94dSNamhyung Kim 
1815aca7a94dSNamhyung Kim 	return NULL;
1816aca7a94dSNamhyung Kim }
1817aca7a94dSNamhyung Kim 
181805e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1819aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1820aca7a94dSNamhyung Kim {
1821aca7a94dSNamhyung Kim 	struct hist_entry *h;
1822aca7a94dSNamhyung Kim 	struct rb_node *nd;
1823aca7a94dSNamhyung Kim 	bool first = true;
1824064f1981SNamhyung Kim 	struct hist_browser *hb;
1825064f1981SNamhyung Kim 
1826064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1827aca7a94dSNamhyung Kim 
182805e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1829aca7a94dSNamhyung Kim 		return;
1830aca7a94dSNamhyung Kim 
183105e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1832aca7a94dSNamhyung Kim 
1833aca7a94dSNamhyung Kim 	switch (whence) {
1834aca7a94dSNamhyung Kim 	case SEEK_SET:
1835064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
183614135663SNamhyung Kim 					   hb->min_pcnt);
1837aca7a94dSNamhyung Kim 		break;
1838aca7a94dSNamhyung Kim 	case SEEK_CUR:
183905e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1840aca7a94dSNamhyung Kim 		goto do_offset;
1841aca7a94dSNamhyung Kim 	case SEEK_END:
1842d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1843d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1844aca7a94dSNamhyung Kim 		first = false;
1845aca7a94dSNamhyung Kim 		break;
1846aca7a94dSNamhyung Kim 	default:
1847aca7a94dSNamhyung Kim 		return;
1848aca7a94dSNamhyung Kim 	}
1849aca7a94dSNamhyung Kim 
1850aca7a94dSNamhyung Kim 	/*
1851aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1852aca7a94dSNamhyung Kim 	 * row_offset:
1853aca7a94dSNamhyung Kim 	 */
185405e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1855aca7a94dSNamhyung Kim 	h->row_offset = 0;
1856aca7a94dSNamhyung Kim 
1857aca7a94dSNamhyung Kim 	/*
1858aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1859aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1860aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1861aca7a94dSNamhyung Kim 	 *
1862aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1863aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1864aca7a94dSNamhyung Kim 	 *
1865aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1866aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1867aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1868aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1869aca7a94dSNamhyung Kim 	 */
1870aca7a94dSNamhyung Kim do_offset:
1871837eeb75SWang Nan 	if (!nd)
1872837eeb75SWang Nan 		return;
1873837eeb75SWang Nan 
1874aca7a94dSNamhyung Kim 	if (offset > 0) {
1875aca7a94dSNamhyung Kim 		do {
1876aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1877d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1878aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1879aca7a94dSNamhyung Kim 				if (offset > remaining) {
1880aca7a94dSNamhyung Kim 					offset -= remaining;
1881aca7a94dSNamhyung Kim 					h->row_offset = 0;
1882aca7a94dSNamhyung Kim 				} else {
1883aca7a94dSNamhyung Kim 					h->row_offset += offset;
1884aca7a94dSNamhyung Kim 					offset = 0;
188505e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1886aca7a94dSNamhyung Kim 					break;
1887aca7a94dSNamhyung Kim 				}
1888aca7a94dSNamhyung Kim 			}
1889d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1890d0506edbSNamhyung Kim 						   hb->min_pcnt);
1891aca7a94dSNamhyung Kim 			if (nd == NULL)
1892aca7a94dSNamhyung Kim 				break;
1893aca7a94dSNamhyung Kim 			--offset;
189405e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1895aca7a94dSNamhyung Kim 		} while (offset != 0);
1896aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1897aca7a94dSNamhyung Kim 		while (1) {
1898aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1899d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1900aca7a94dSNamhyung Kim 				if (first) {
1901aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1902aca7a94dSNamhyung Kim 						offset += h->row_offset;
1903aca7a94dSNamhyung Kim 						h->row_offset = 0;
1904aca7a94dSNamhyung Kim 					} else {
1905aca7a94dSNamhyung Kim 						h->row_offset += offset;
1906aca7a94dSNamhyung Kim 						offset = 0;
190705e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1908aca7a94dSNamhyung Kim 						break;
1909aca7a94dSNamhyung Kim 					}
1910aca7a94dSNamhyung Kim 				} else {
1911aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1912aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1913aca7a94dSNamhyung Kim 						h->row_offset = 0;
1914aca7a94dSNamhyung Kim 					} else {
1915aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1916aca7a94dSNamhyung Kim 						offset = 0;
191705e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1918aca7a94dSNamhyung Kim 						break;
1919aca7a94dSNamhyung Kim 					}
1920aca7a94dSNamhyung Kim 				}
1921aca7a94dSNamhyung Kim 			}
1922aca7a94dSNamhyung Kim 
1923d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1924064f1981SNamhyung Kim 							hb->min_pcnt);
1925aca7a94dSNamhyung Kim 			if (nd == NULL)
1926aca7a94dSNamhyung Kim 				break;
1927aca7a94dSNamhyung Kim 			++offset;
192805e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1929aca7a94dSNamhyung Kim 			if (offset == 0) {
1930aca7a94dSNamhyung Kim 				/*
1931aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1932aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1933aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1934aca7a94dSNamhyung Kim 				 */
1935aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
1936d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
1937aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1938aca7a94dSNamhyung Kim 				break;
1939aca7a94dSNamhyung Kim 			}
1940aca7a94dSNamhyung Kim 			first = false;
1941aca7a94dSNamhyung Kim 		}
1942aca7a94dSNamhyung Kim 	} else {
194305e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1944aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1945aca7a94dSNamhyung Kim 		h->row_offset = 0;
1946aca7a94dSNamhyung Kim 	}
1947aca7a94dSNamhyung Kim }
1948aca7a94dSNamhyung Kim 
1949aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1950d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
1951d0506edbSNamhyung Kim 					   int level)
1952aff3f3f6SArnaldo Carvalho de Melo {
195339ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
195439ee533fSNamhyung Kim 		.fp = fp,
195539ee533fSNamhyung Kim 	};
1956aff3f3f6SArnaldo Carvalho de Melo 
1957d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
195839ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
195939ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
196039ee533fSNamhyung Kim 	return arg.printed;
1961aff3f3f6SArnaldo Carvalho de Melo }
1962aff3f3f6SArnaldo Carvalho de Melo 
1963aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1964aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1965aff3f3f6SArnaldo Carvalho de Melo {
1966aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1967aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1968aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
196926d8b338SNamhyung Kim 	struct perf_hpp hpp = {
197026d8b338SNamhyung Kim 		.buf = s,
197126d8b338SNamhyung Kim 		.size = sizeof(s),
197226d8b338SNamhyung Kim 	};
197326d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
197426d8b338SNamhyung Kim 	bool first = true;
197526d8b338SNamhyung Kim 	int ret;
1976aff3f3f6SArnaldo Carvalho de Melo 
19771b6b678eSArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain) {
1978aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1979aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
19801b6b678eSArnaldo Carvalho de Melo 	}
1981aff3f3f6SArnaldo Carvalho de Melo 
1982f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1983361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
1984e67d49a7SNamhyung Kim 			continue;
1985e67d49a7SNamhyung Kim 
198626d8b338SNamhyung Kim 		if (!first) {
198726d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
198826d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
198926d8b338SNamhyung Kim 		} else
199026d8b338SNamhyung Kim 			first = false;
1991aff3f3f6SArnaldo Carvalho de Melo 
199226d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
199389fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
199426d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
199526d8b338SNamhyung Kim 	}
199689fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
1997aff3f3f6SArnaldo Carvalho de Melo 
1998aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1999d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2000d0506edbSNamhyung Kim 
2001d0506edbSNamhyung Kim 	return printed;
2002d0506edbSNamhyung Kim }
2003d0506edbSNamhyung Kim 
2004d0506edbSNamhyung Kim 
2005d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2006d0506edbSNamhyung Kim 						 struct hist_entry *he,
2007325a6283SNamhyung Kim 						 FILE *fp, int level)
2008d0506edbSNamhyung Kim {
2009d0506edbSNamhyung Kim 	char s[8192];
2010d0506edbSNamhyung Kim 	int printed = 0;
2011d0506edbSNamhyung Kim 	char folded_sign = ' ';
2012d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
2013d0506edbSNamhyung Kim 		.buf = s,
2014d0506edbSNamhyung Kim 		.size = sizeof(s),
2015d0506edbSNamhyung Kim 	};
2016d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
2017325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
2018d0506edbSNamhyung Kim 	bool first = true;
2019d0506edbSNamhyung Kim 	int ret;
2020325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2021d0506edbSNamhyung Kim 
2022d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2023d0506edbSNamhyung Kim 
2024d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
2025d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
2026d0506edbSNamhyung Kim 
2027325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
2028325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2029325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
2030325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2031d0506edbSNamhyung Kim 		if (!first) {
2032d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2033d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
2034d0506edbSNamhyung Kim 		} else
2035d0506edbSNamhyung Kim 			first = false;
2036d0506edbSNamhyung Kim 
2037d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2038d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
2039d0506edbSNamhyung Kim 	}
2040d0506edbSNamhyung Kim 
2041d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2042d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
2043d0506edbSNamhyung Kim 
20441b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
20451b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
20461b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
20471b2dbbf4SNamhyung Kim 
2048d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2049d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
20501b2dbbf4SNamhyung Kim 	}
2051d0506edbSNamhyung Kim 
2052d0506edbSNamhyung Kim 	printed += fprintf(fp, "%s\n", rtrim(s));
2053d0506edbSNamhyung Kim 
2054d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
2055d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2056d0506edbSNamhyung Kim 							   he->depth + 1);
2057d0506edbSNamhyung Kim 	}
2058aff3f3f6SArnaldo Carvalho de Melo 
2059aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2060aff3f3f6SArnaldo Carvalho de Melo }
2061aff3f3f6SArnaldo Carvalho de Melo 
2062aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2063aff3f3f6SArnaldo Carvalho de Melo {
2064064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2065064f1981SNamhyung Kim 						   browser->min_pcnt);
2066aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2067aff3f3f6SArnaldo Carvalho de Melo 
2068aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
2069aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2070aff3f3f6SArnaldo Carvalho de Melo 
2071d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
2072d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2073d0506edbSNamhyung Kim 									 h, fp,
2074325a6283SNamhyung Kim 									 h->depth);
2075d0506edbSNamhyung Kim 		} else {
2076aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
2077d0506edbSNamhyung Kim 		}
2078d0506edbSNamhyung Kim 
2079d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2080d0506edbSNamhyung Kim 					   browser->min_pcnt);
2081aff3f3f6SArnaldo Carvalho de Melo 	}
2082aff3f3f6SArnaldo Carvalho de Melo 
2083aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2084aff3f3f6SArnaldo Carvalho de Melo }
2085aff3f3f6SArnaldo Carvalho de Melo 
2086aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2087aff3f3f6SArnaldo Carvalho de Melo {
2088aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2089aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2090aff3f3f6SArnaldo Carvalho de Melo 
2091aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2092aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2093aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2094aff3f3f6SArnaldo Carvalho de Melo 			break;
2095aff3f3f6SArnaldo Carvalho de Melo 		/*
2096aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2097aff3f3f6SArnaldo Carvalho de Melo  		 */
2098aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2099aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2100aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2101aff3f3f6SArnaldo Carvalho de Melo 		}
2102aff3f3f6SArnaldo Carvalho de Melo 	}
2103aff3f3f6SArnaldo Carvalho de Melo 
2104aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2105aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2106aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
2107c8b5f2c9SArnaldo Carvalho de Melo 		const char *err = str_error_r(errno, bf, sizeof(bf));
21084cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2109aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2110aff3f3f6SArnaldo Carvalho de Melo 	}
2111aff3f3f6SArnaldo Carvalho de Melo 
2112aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2113aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2114aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2115aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2116aff3f3f6SArnaldo Carvalho de Melo 
2117aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2118aff3f3f6SArnaldo Carvalho de Melo }
2119aff3f3f6SArnaldo Carvalho de Melo 
2120fcd86426SJiri Olsa void hist_browser__init(struct hist_browser *browser,
2121fcd86426SJiri Olsa 			struct hists *hists)
2122aca7a94dSNamhyung Kim {
2123b1c7a8f7SJiri Olsa 	struct perf_hpp_fmt *fmt;
2124b1c7a8f7SJiri Olsa 
212505e8b080SArnaldo Carvalho de Melo 	browser->hists			= hists;
212605e8b080SArnaldo Carvalho de Melo 	browser->b.refresh		= hist_browser__refresh;
2127357cfff1SArnaldo Carvalho de Melo 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
212805e8b080SArnaldo Carvalho de Melo 	browser->b.seek			= ui_browser__hists_seek;
212905e8b080SArnaldo Carvalho de Melo 	browser->b.use_navkeypressed	= true;
2130c8302367SJiri Olsa 	browser->show_headers		= symbol_conf.show_hist_headers;
2131b1c7a8f7SJiri Olsa 
21328a06b0beSNamhyung Kim 	if (symbol_conf.report_hierarchy) {
21338a06b0beSNamhyung Kim 		struct perf_hpp_list_node *fmt_node;
21348a06b0beSNamhyung Kim 
21358a06b0beSNamhyung Kim 		/* count overhead columns (in the first node) */
21368a06b0beSNamhyung Kim 		fmt_node = list_first_entry(&hists->hpp_formats,
21378a06b0beSNamhyung Kim 					    struct perf_hpp_list_node, list);
21388a06b0beSNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
21398a06b0beSNamhyung Kim 			++browser->b.columns;
21408a06b0beSNamhyung Kim 
21418a06b0beSNamhyung Kim 		/* add a single column for whole hierarchy sort keys*/
21428a06b0beSNamhyung Kim 		++browser->b.columns;
21438a06b0beSNamhyung Kim 	} else {
2144e3b60bc9SNamhyung Kim 		hists__for_each_format(hists, fmt)
2145b1c7a8f7SJiri Olsa 			++browser->b.columns;
21468a06b0beSNamhyung Kim 	}
2147e3b60bc9SNamhyung Kim 
2148e3b60bc9SNamhyung Kim 	hists__reset_column_width(hists);
2149aca7a94dSNamhyung Kim }
2150aca7a94dSNamhyung Kim 
2151fcd86426SJiri Olsa struct hist_browser *hist_browser__new(struct hists *hists)
2152fcd86426SJiri Olsa {
2153fcd86426SJiri Olsa 	struct hist_browser *browser = zalloc(sizeof(*browser));
2154fcd86426SJiri Olsa 
2155fcd86426SJiri Olsa 	if (browser)
2156fcd86426SJiri Olsa 		hist_browser__init(browser, hists);
2157fcd86426SJiri Olsa 
215805e8b080SArnaldo Carvalho de Melo 	return browser;
2159aca7a94dSNamhyung Kim }
2160aca7a94dSNamhyung Kim 
2161a6ec894dSJiri Olsa static struct hist_browser *
2162a6ec894dSJiri Olsa perf_evsel_browser__new(struct perf_evsel *evsel,
2163a6ec894dSJiri Olsa 			struct hist_browser_timer *hbt,
2164a6ec894dSJiri Olsa 			struct perf_env *env)
2165a6ec894dSJiri Olsa {
2166a6ec894dSJiri Olsa 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2167a6ec894dSJiri Olsa 
2168a6ec894dSJiri Olsa 	if (browser) {
2169a6ec894dSJiri Olsa 		browser->hbt   = hbt;
2170a6ec894dSJiri Olsa 		browser->env   = env;
2171a6ec894dSJiri Olsa 		browser->title = perf_evsel_browser_title;
2172a6ec894dSJiri Olsa 	}
2173a6ec894dSJiri Olsa 	return browser;
2174a6ec894dSJiri Olsa }
2175a6ec894dSJiri Olsa 
2176dabd2012SJiri Olsa void hist_browser__delete(struct hist_browser *browser)
2177aca7a94dSNamhyung Kim {
217805e8b080SArnaldo Carvalho de Melo 	free(browser);
2179aca7a94dSNamhyung Kim }
2180aca7a94dSNamhyung Kim 
218105e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2182aca7a94dSNamhyung Kim {
218305e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2184aca7a94dSNamhyung Kim }
2185aca7a94dSNamhyung Kim 
218605e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2187aca7a94dSNamhyung Kim {
218805e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2189aca7a94dSNamhyung Kim }
2190aca7a94dSNamhyung Kim 
21911e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
21921e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
21931e378ebdSTaeung Song {
21941e378ebdSTaeung Song 	return timer == NULL;
21951e378ebdSTaeung Song }
21961e378ebdSTaeung Song 
21975b91a86fSJiri Olsa static int perf_evsel_browser_title(struct hist_browser *browser,
21981e378ebdSTaeung Song 				char *bf, size_t size)
2199aca7a94dSNamhyung Kim {
22005b91a86fSJiri Olsa 	struct hist_browser_timer *hbt = browser->hbt;
22015b91a86fSJiri Olsa 	struct hists *hists = browser->hists;
2202aca7a94dSNamhyung Kim 	char unit;
2203aca7a94dSNamhyung Kim 	int printed;
220405e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
220505e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
220684734b06SKan Liang 	int socket_id = hists->socket_filter;
220705e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
220805e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
2209717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
2210dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
2211717e263fSNamhyung Kim 	char buf[512];
2212717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
22139e207ddfSKan Liang 	char ref[30] = " show reference callgraph, ";
22149e207ddfSKan Liang 	bool enable_ref = false;
2215717e263fSNamhyung Kim 
2216f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
2217f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
2218f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
2219f2148330SNamhyung Kim 	}
2220f2148330SNamhyung Kim 
2221759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
2222717e263fSNamhyung Kim 		struct perf_evsel *pos;
2223717e263fSNamhyung Kim 
2224717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
2225717e263fSNamhyung Kim 		ev_name = buf;
2226717e263fSNamhyung Kim 
2227717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
22284ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
22294ea062edSArnaldo Carvalho de Melo 
2230f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
22314ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
22324ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
2233f2148330SNamhyung Kim 			} else {
22344ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
22354ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
2236717e263fSNamhyung Kim 			}
2237717e263fSNamhyung Kim 		}
2238f2148330SNamhyung Kim 	}
2239aca7a94dSNamhyung Kim 
22409e207ddfSKan Liang 	if (symbol_conf.show_ref_callgraph &&
22419e207ddfSKan Liang 	    strstr(ev_name, "call-graph=no"))
22429e207ddfSKan Liang 		enable_ref = true;
2243aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
2244aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
22459e207ddfSKan Liang 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
22469e207ddfSKan Liang 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2247aca7a94dSNamhyung Kim 
2248aca7a94dSNamhyung Kim 
224905e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
2250aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
225105e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
22526962ccb3SNamhyung Kim 	if (thread) {
2253fa82911aSJiri Olsa 		if (hists__has(hists, thread)) {
2254aca7a94dSNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
2255aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
2256b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
225738051234SAdrian Hunter 				    thread->tid);
22586962ccb3SNamhyung Kim 		} else {
22596962ccb3SNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
22606962ccb3SNamhyung Kim 				    ", Thread: %s",
22616962ccb3SNamhyung Kim 				     (thread->comm_set ? thread__comm_str(thread) : ""));
22626962ccb3SNamhyung Kim 		}
22636962ccb3SNamhyung Kim 	}
2264aca7a94dSNamhyung Kim 	if (dso)
2265aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
2266aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
226784734b06SKan Liang 	if (socket_id > -1)
226821394d94SKan Liang 		printed += scnprintf(bf + printed, size - printed,
226984734b06SKan Liang 				    ", Processor Socket: %d", socket_id);
22701e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
22711e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
22721e378ebdSTaeung Song 
22731e378ebdSTaeung Song 		if (top->zero)
22741e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
22751e378ebdSTaeung Song 	}
22761e378ebdSTaeung Song 
2277aca7a94dSNamhyung Kim 	return printed;
2278aca7a94dSNamhyung Kim }
2279aca7a94dSNamhyung Kim 
2280aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2281aca7a94dSNamhyung Kim {
2282aca7a94dSNamhyung Kim 	int i;
2283aca7a94dSNamhyung Kim 
228404662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
228504662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2286aca7a94dSNamhyung Kim }
2287aca7a94dSNamhyung Kim 
2288341487abSFeng Tang /*
2289341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2290341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2291341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2292341487abSFeng Tang  */
2293341487abSFeng Tang static bool is_input_name_malloced = false;
2294341487abSFeng Tang 
2295341487abSFeng Tang static int switch_data_file(void)
2296341487abSFeng Tang {
2297341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2298341487abSFeng Tang 	DIR *pwd_dir;
2299341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2300341487abSFeng Tang 	struct dirent *dent;
2301341487abSFeng Tang 
2302341487abSFeng Tang 	pwd = getenv("PWD");
2303341487abSFeng Tang 	if (!pwd)
2304341487abSFeng Tang 		return ret;
2305341487abSFeng Tang 
2306341487abSFeng Tang 	pwd_dir = opendir(pwd);
2307341487abSFeng Tang 	if (!pwd_dir)
2308341487abSFeng Tang 		return ret;
2309341487abSFeng Tang 
2310341487abSFeng Tang 	memset(options, 0, sizeof(options));
2311341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
2312341487abSFeng Tang 
2313341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2314341487abSFeng Tang 		char path[PATH_MAX];
2315341487abSFeng Tang 		u64 magic;
2316341487abSFeng Tang 		char *name = dent->d_name;
2317341487abSFeng Tang 		FILE *file;
2318341487abSFeng Tang 
2319341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2320341487abSFeng Tang 			continue;
2321341487abSFeng Tang 
2322341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2323341487abSFeng Tang 
2324341487abSFeng Tang 		file = fopen(path, "r");
2325341487abSFeng Tang 		if (!file)
2326341487abSFeng Tang 			continue;
2327341487abSFeng Tang 
2328341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2329341487abSFeng Tang 			goto close_file_and_continue;
2330341487abSFeng Tang 
2331341487abSFeng Tang 		if (is_perf_magic(magic)) {
2332341487abSFeng Tang 			options[nr_options] = strdup(name);
2333341487abSFeng Tang 			if (!options[nr_options])
2334341487abSFeng Tang 				goto close_file_and_continue;
2335341487abSFeng Tang 
2336341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2337341487abSFeng Tang 			if (!abs_path[nr_options]) {
233874cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2339341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2340341487abSFeng Tang 				fclose(file);
2341341487abSFeng Tang 				break;
2342341487abSFeng Tang 			}
2343341487abSFeng Tang 
2344341487abSFeng Tang 			nr_options++;
2345341487abSFeng Tang 		}
2346341487abSFeng Tang 
2347341487abSFeng Tang close_file_and_continue:
2348341487abSFeng Tang 		fclose(file);
2349341487abSFeng Tang 		if (nr_options >= 32) {
2350341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2351341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2352341487abSFeng Tang 			break;
2353341487abSFeng Tang 		}
2354341487abSFeng Tang 	}
2355341487abSFeng Tang 	closedir(pwd_dir);
2356341487abSFeng Tang 
2357341487abSFeng Tang 	if (nr_options) {
2358341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
2359341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2360341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2361341487abSFeng Tang 			if (tmp) {
2362341487abSFeng Tang 				if (is_input_name_malloced)
2363341487abSFeng Tang 					free((void *)input_name);
2364341487abSFeng Tang 				input_name = tmp;
2365341487abSFeng Tang 				is_input_name_malloced = true;
2366341487abSFeng Tang 				ret = 0;
2367341487abSFeng Tang 			} else
2368341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2369341487abSFeng Tang 		}
2370341487abSFeng Tang 	}
2371341487abSFeng Tang 
2372341487abSFeng Tang 	free_popup_options(options, nr_options);
2373341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2374341487abSFeng Tang 	return ret;
2375341487abSFeng Tang }
2376341487abSFeng Tang 
2377ea7cd592SNamhyung Kim struct popup_action {
2378ea7cd592SNamhyung Kim 	struct thread 		*thread;
2379ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
238084734b06SKan Liang 	int			socket;
2381ea7cd592SNamhyung Kim 
2382ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2383ea7cd592SNamhyung Kim };
2384ea7cd592SNamhyung Kim 
2385bc7cad42SNamhyung Kim static int
2386ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2387bc7cad42SNamhyung Kim {
2388bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
2389bc7cad42SNamhyung Kim 	struct annotation *notes;
2390bc7cad42SNamhyung Kim 	struct hist_entry *he;
2391bc7cad42SNamhyung Kim 	int err;
2392bc7cad42SNamhyung Kim 
2393eebd0bfcSArnaldo Carvalho de Melo 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
2394bc7cad42SNamhyung Kim 		return 0;
2395bc7cad42SNamhyung Kim 
2396ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2397bc7cad42SNamhyung Kim 	if (!notes->src)
2398bc7cad42SNamhyung Kim 		return 0;
2399bc7cad42SNamhyung Kim 
2400bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
2401ea7cd592SNamhyung Kim 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2402bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2403bc7cad42SNamhyung Kim 	/*
2404bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2405bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2406bc7cad42SNamhyung Kim 	 */
2407bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2408bc7cad42SNamhyung Kim 		return 1;
2409bc7cad42SNamhyung Kim 
2410bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2411bc7cad42SNamhyung Kim 	if (err)
2412bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2413bc7cad42SNamhyung Kim 	return 0;
2414bc7cad42SNamhyung Kim }
2415bc7cad42SNamhyung Kim 
2416bc7cad42SNamhyung Kim static int
2417ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2418ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
2419ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
2420bc7cad42SNamhyung Kim {
2421ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
2422ea7cd592SNamhyung Kim 		return 0;
2423ea7cd592SNamhyung Kim 
2424ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2425ea7cd592SNamhyung Kim 		return 0;
2426ea7cd592SNamhyung Kim 
2427ea7cd592SNamhyung Kim 	act->ms.map = map;
2428ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2429ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2430ea7cd592SNamhyung Kim 	return 1;
2431ea7cd592SNamhyung Kim }
2432ea7cd592SNamhyung Kim 
2433ea7cd592SNamhyung Kim static int
2434ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2435ea7cd592SNamhyung Kim {
2436ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2437ea7cd592SNamhyung Kim 
24387cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
24397cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2440599a2f38SNamhyung Kim 		return 0;
2441599a2f38SNamhyung Kim 
2442bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2443bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2444bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2445bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2446bc7cad42SNamhyung Kim 		ui_helpline__pop();
2447bc7cad42SNamhyung Kim 	} else {
2448fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
24497727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2450bc7cad42SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "",
2451bc7cad42SNamhyung Kim 					   thread->tid);
24526962ccb3SNamhyung Kim 		} else {
24536962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
24546962ccb3SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "");
24556962ccb3SNamhyung Kim 		}
24566962ccb3SNamhyung Kim 
2457bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2458bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2459bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2460bc7cad42SNamhyung Kim 	}
2461bc7cad42SNamhyung Kim 
2462bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2463bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2464bc7cad42SNamhyung Kim 	return 0;
2465bc7cad42SNamhyung Kim }
2466bc7cad42SNamhyung Kim 
2467bc7cad42SNamhyung Kim static int
2468ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2469ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2470bc7cad42SNamhyung Kim {
24716962ccb3SNamhyung Kim 	int ret;
24726962ccb3SNamhyung Kim 
24737cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
24747cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2475ea7cd592SNamhyung Kim 		return 0;
2476ea7cd592SNamhyung Kim 
2477fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
24786962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2479ea7cd592SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
2480ea7cd592SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "",
24816962ccb3SNamhyung Kim 			       thread->tid);
24826962ccb3SNamhyung Kim 	} else {
24836962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s thread",
24846962ccb3SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
24856962ccb3SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "");
24866962ccb3SNamhyung Kim 	}
24876962ccb3SNamhyung Kim 	if (ret < 0)
2488ea7cd592SNamhyung Kim 		return 0;
2489ea7cd592SNamhyung Kim 
2490ea7cd592SNamhyung Kim 	act->thread = thread;
2491ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2492ea7cd592SNamhyung Kim 	return 1;
2493ea7cd592SNamhyung Kim }
2494ea7cd592SNamhyung Kim 
2495ea7cd592SNamhyung Kim static int
2496ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2497ea7cd592SNamhyung Kim {
2498045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
2499ea7cd592SNamhyung Kim 
250069849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2501599a2f38SNamhyung Kim 		return 0;
2502599a2f38SNamhyung Kim 
2503bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2504bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2505bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2506bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2507bc7cad42SNamhyung Kim 		ui_helpline__pop();
2508bc7cad42SNamhyung Kim 	} else {
25097727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2510045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2511045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
2512bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2513bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2514bc7cad42SNamhyung Kim 	}
2515bc7cad42SNamhyung Kim 
2516bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2517bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2518bc7cad42SNamhyung Kim 	return 0;
2519bc7cad42SNamhyung Kim }
2520bc7cad42SNamhyung Kim 
2521bc7cad42SNamhyung Kim static int
2522ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2523045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2524bc7cad42SNamhyung Kim {
252569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2526ea7cd592SNamhyung Kim 		return 0;
2527ea7cd592SNamhyung Kim 
2528ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
2529ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
2530045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2531ea7cd592SNamhyung Kim 		return 0;
2532ea7cd592SNamhyung Kim 
2533045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2534ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2535ea7cd592SNamhyung Kim 	return 1;
2536ea7cd592SNamhyung Kim }
2537ea7cd592SNamhyung Kim 
2538ea7cd592SNamhyung Kim static int
2539ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2540ea7cd592SNamhyung Kim 	      struct popup_action *act)
2541ea7cd592SNamhyung Kim {
2542ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2543bc7cad42SNamhyung Kim 	return 0;
2544bc7cad42SNamhyung Kim }
2545bc7cad42SNamhyung Kim 
2546bc7cad42SNamhyung Kim static int
254769849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2548ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2549ea7cd592SNamhyung Kim {
255069849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2551ea7cd592SNamhyung Kim 		return 0;
2552ea7cd592SNamhyung Kim 
2553ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2554ea7cd592SNamhyung Kim 		return 0;
2555ea7cd592SNamhyung Kim 
2556ea7cd592SNamhyung Kim 	act->ms.map = map;
2557ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2558ea7cd592SNamhyung Kim 	return 1;
2559ea7cd592SNamhyung Kim }
2560ea7cd592SNamhyung Kim 
2561ea7cd592SNamhyung Kim static int
2562bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2563ea7cd592SNamhyung Kim 	      struct popup_action *act)
2564bc7cad42SNamhyung Kim {
2565bc7cad42SNamhyung Kim 	char script_opt[64];
2566bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
2567bc7cad42SNamhyung Kim 
2568ea7cd592SNamhyung Kim 	if (act->thread) {
2569bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2570ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2571ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
2572bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2573ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2574bc7cad42SNamhyung Kim 	}
2575bc7cad42SNamhyung Kim 
2576bc7cad42SNamhyung Kim 	script_browse(script_opt);
2577bc7cad42SNamhyung Kim 	return 0;
2578bc7cad42SNamhyung Kim }
2579bc7cad42SNamhyung Kim 
2580bc7cad42SNamhyung Kim static int
2581ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
2582ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
2583ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
2584ea7cd592SNamhyung Kim {
2585ea7cd592SNamhyung Kim 	if (thread) {
2586ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2587ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
2588ea7cd592SNamhyung Kim 			return 0;
2589ea7cd592SNamhyung Kim 	} else if (sym) {
2590ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2591ea7cd592SNamhyung Kim 			     sym->name) < 0)
2592ea7cd592SNamhyung Kim 			return 0;
2593ea7cd592SNamhyung Kim 	} else {
2594ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2595ea7cd592SNamhyung Kim 			return 0;
2596ea7cd592SNamhyung Kim 	}
2597ea7cd592SNamhyung Kim 
2598ea7cd592SNamhyung Kim 	act->thread = thread;
2599ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2600ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2601ea7cd592SNamhyung Kim 	return 1;
2602ea7cd592SNamhyung Kim }
2603ea7cd592SNamhyung Kim 
2604ea7cd592SNamhyung Kim static int
2605ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2606ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2607bc7cad42SNamhyung Kim {
2608bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2609bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2610bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2611ea7cd592SNamhyung Kim 		return 0;
2612bc7cad42SNamhyung Kim 	}
2613bc7cad42SNamhyung Kim 
2614bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2615bc7cad42SNamhyung Kim }
2616bc7cad42SNamhyung Kim 
2617ea7cd592SNamhyung Kim static int
2618ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2619ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2620ea7cd592SNamhyung Kim {
2621ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2622ea7cd592SNamhyung Kim 		return 0;
2623ea7cd592SNamhyung Kim 
2624ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2625ea7cd592SNamhyung Kim 		return 0;
2626ea7cd592SNamhyung Kim 
2627ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2628ea7cd592SNamhyung Kim 	return 1;
2629ea7cd592SNamhyung Kim }
2630ea7cd592SNamhyung Kim 
2631ea7cd592SNamhyung Kim static int
2632ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2633ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2634ea7cd592SNamhyung Kim {
2635ea7cd592SNamhyung Kim 	return 0;
2636ea7cd592SNamhyung Kim }
2637ea7cd592SNamhyung Kim 
2638ea7cd592SNamhyung Kim static int
2639ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2640ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2641ea7cd592SNamhyung Kim {
2642ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2643ea7cd592SNamhyung Kim 		return 0;
2644ea7cd592SNamhyung Kim 
2645ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2646ea7cd592SNamhyung Kim 	return 1;
2647ea7cd592SNamhyung Kim }
2648ea7cd592SNamhyung Kim 
264984734b06SKan Liang static int
265084734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
265184734b06SKan Liang {
265235a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2653599a2f38SNamhyung Kim 		return 0;
2654599a2f38SNamhyung Kim 
265584734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
265684734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
265784734b06SKan Liang 		browser->hists->socket_filter = -1;
265884734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
265984734b06SKan Liang 	} else {
266084734b06SKan Liang 		browser->hists->socket_filter = act->socket;
266184734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
266284734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
266384734b06SKan Liang 	}
266484734b06SKan Liang 
266584734b06SKan Liang 	hists__filter_by_socket(browser->hists);
266684734b06SKan Liang 	hist_browser__reset(browser);
266784734b06SKan Liang 	return 0;
266884734b06SKan Liang }
266984734b06SKan Liang 
267084734b06SKan Liang static int
267184734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
267284734b06SKan Liang 	       char **optstr, int socket_id)
267384734b06SKan Liang {
267435a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
267584734b06SKan Liang 		return 0;
267684734b06SKan Liang 
267784734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
267884734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
267984734b06SKan Liang 		     socket_id) < 0)
268084734b06SKan Liang 		return 0;
268184734b06SKan Liang 
268284734b06SKan Liang 	act->socket = socket_id;
268384734b06SKan Liang 	act->fn = do_zoom_socket;
268484734b06SKan Liang 	return 1;
268584734b06SKan Liang }
268684734b06SKan Liang 
2687112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2688064f1981SNamhyung Kim {
2689064f1981SNamhyung Kim 	u64 nr_entries = 0;
2690064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2691064f1981SNamhyung Kim 
2692f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2693268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2694268397cbSNamhyung Kim 		return;
2695268397cbSNamhyung Kim 	}
2696268397cbSNamhyung Kim 
269714135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2698064f1981SNamhyung Kim 		nr_entries++;
2699f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2700064f1981SNamhyung Kim 	}
2701064f1981SNamhyung Kim 
2702112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2703f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2704064f1981SNamhyung Kim }
2705341487abSFeng Tang 
2706b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2707b62e8dfcSNamhyung Kim 					       double percent)
2708b62e8dfcSNamhyung Kim {
2709b62e8dfcSNamhyung Kim 	struct hist_entry *he;
2710b62e8dfcSNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2711b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2712b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2713b62e8dfcSNamhyung Kim 
2714b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2715b62e8dfcSNamhyung Kim 
2716b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2717b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2718b62e8dfcSNamhyung Kim 
271979dded87SNamhyung Kim 		if (he->has_no_entry) {
272079dded87SNamhyung Kim 			he->has_no_entry = false;
272179dded87SNamhyung Kim 			he->nr_rows = 0;
272279dded87SNamhyung Kim 		}
272379dded87SNamhyung Kim 
2724d0506edbSNamhyung Kim 		if (!he->leaf || !symbol_conf.use_callchain)
2725d0506edbSNamhyung Kim 			goto next;
2726d0506edbSNamhyung Kim 
2727b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2728b62e8dfcSNamhyung Kim 			total = he->stat.period;
2729b62e8dfcSNamhyung Kim 
2730b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2731b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2732b62e8dfcSNamhyung Kim 
2733b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2734b62e8dfcSNamhyung Kim 		}
2735b62e8dfcSNamhyung Kim 
2736b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2737b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2738b62e8dfcSNamhyung Kim 
2739d0506edbSNamhyung Kim next:
2740201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2741d0506edbSNamhyung Kim 
2742b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2743b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2744492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2745b62e8dfcSNamhyung Kim 	}
2746b62e8dfcSNamhyung Kim }
2747b62e8dfcSNamhyung Kim 
2748aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2749dd00d486SJiri Olsa 				    const char *helpline,
2750aca7a94dSNamhyung Kim 				    bool left_exits,
275168d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2752064f1981SNamhyung Kim 				    float min_pcnt,
2753ce80d3beSKan Liang 				    struct perf_env *env)
2754aca7a94dSNamhyung Kim {
27554ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2756a6ec894dSJiri Olsa 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2757aca7a94dSNamhyung Kim 	struct branch_info *bi;
2758f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2759f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2760ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2761aca7a94dSNamhyung Kim 	int nr_options = 0;
2762aca7a94dSNamhyung Kim 	int key = -1;
2763aca7a94dSNamhyung Kim 	char buf[64];
27649783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2765aca7a94dSNamhyung Kim 
2766e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2767e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2768e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2769e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
2770e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2771e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2772e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2773e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
27747727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
27757727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
2776e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2777e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2778e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2779e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2780105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2781025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2782b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
278331eb4360SNamhyung Kim 	"m             Display context menu\n"				\
278484734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2785e8e684a5SNamhyung Kim 
2786e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
2787e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
27886dd60135SNamhyung Kim 	"i             Show header information\n"
2789e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2790e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2791e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2792e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2793e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2794e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2795e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2796e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2797e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2798e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
279942337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2800fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2801e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2802e8e684a5SNamhyung Kim 
2803aca7a94dSNamhyung Kim 	if (browser == NULL)
2804aca7a94dSNamhyung Kim 		return -1;
2805aca7a94dSNamhyung Kim 
2806ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2807ed426915SNamhyung Kim 	SLang_reset_tty();
2808ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2809ed426915SNamhyung Kim 
281003905048SNamhyung Kim 	if (min_pcnt)
2811064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2812112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2813064f1981SNamhyung Kim 
281484734b06SKan Liang 	browser->pstack = pstack__new(3);
281501f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2816aca7a94dSNamhyung Kim 		goto out;
2817aca7a94dSNamhyung Kim 
2818aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2819aca7a94dSNamhyung Kim 
2820aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2821ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2822aca7a94dSNamhyung Kim 
28235b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
28245b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
28255b591669SNamhyung Kim 
2826aca7a94dSNamhyung Kim 	while (1) {
2827f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2828045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
2829ea7cd592SNamhyung Kim 		int choice = 0;
283084734b06SKan Liang 		int socked_id = -1;
2831aca7a94dSNamhyung Kim 
2832aca7a94dSNamhyung Kim 		nr_options = 0;
2833aca7a94dSNamhyung Kim 
28345f00b0f4SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline);
2835aca7a94dSNamhyung Kim 
2836aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
2837aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
2838045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
283984734b06SKan Liang 			socked_id = browser->he_selection->socket;
2840aca7a94dSNamhyung Kim 		}
2841aca7a94dSNamhyung Kim 		switch (key) {
2842aca7a94dSNamhyung Kim 		case K_TAB:
2843aca7a94dSNamhyung Kim 		case K_UNTAB:
2844aca7a94dSNamhyung Kim 			if (nr_events == 1)
2845aca7a94dSNamhyung Kim 				continue;
2846aca7a94dSNamhyung Kim 			/*
2847aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
2848aca7a94dSNamhyung Kim 			 * go to the next or previous
2849aca7a94dSNamhyung Kim 			 */
2850aca7a94dSNamhyung Kim 			goto out_free_stack;
2851aca7a94dSNamhyung Kim 		case 'a':
28522e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
2853aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
2854aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
2855aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
2856aca7a94dSNamhyung Kim 				continue;
2857aca7a94dSNamhyung Kim 			}
2858aca7a94dSNamhyung Kim 
2859aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
2860aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
2861aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
2862aca7a94dSNamhyung Kim 				continue;
2863bc7cad42SNamhyung Kim 
2864ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
2865ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
2866ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
2867bc7cad42SNamhyung Kim 			continue;
2868aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
2869aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
2870aff3f3f6SArnaldo Carvalho de Melo 			continue;
2871aca7a94dSNamhyung Kim 		case 'd':
2872fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
2873ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
2874bc7cad42SNamhyung Kim 			continue;
2875a7cb8863SArnaldo Carvalho de Melo 		case 'V':
287621e8c810SAlexis Berlemont 			verbose = (verbose + 1) % 4;
287721e8c810SAlexis Berlemont 			browser->show_dso = verbose > 0;
287821e8c810SAlexis Berlemont 			ui_helpline__fpush("Verbosity level set to %d\n",
287921e8c810SAlexis Berlemont 					   verbose);
2880a7cb8863SArnaldo Carvalho de Melo 			continue;
2881aca7a94dSNamhyung Kim 		case 't':
2882ea7cd592SNamhyung Kim 			actions->thread = thread;
2883ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
2884bc7cad42SNamhyung Kim 			continue;
288584734b06SKan Liang 		case 'S':
288684734b06SKan Liang 			actions->socket = socked_id;
288784734b06SKan Liang 			do_zoom_socket(browser, actions);
288884734b06SKan Liang 			continue;
28895a5626b1SArnaldo Carvalho de Melo 		case '/':
2890aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
28914aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
28924aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
2893aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2894aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
289505e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
289605e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
2897aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
2898aca7a94dSNamhyung Kim 			}
2899aca7a94dSNamhyung Kim 			continue;
2900cdbab7c2SFeng Tang 		case 'r':
2901ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
2902ea7cd592SNamhyung Kim 				actions->thread = NULL;
2903ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
2904ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
2905ea7cd592SNamhyung Kim 			}
2906c77d8d70SFeng Tang 			continue;
2907341487abSFeng Tang 		case 's':
2908bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
2909ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
2910bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
2911bc7cad42SNamhyung Kim 					goto out_free_stack;
2912bc7cad42SNamhyung Kim 			}
2913341487abSFeng Tang 			continue;
29146dd60135SNamhyung Kim 		case 'i':
29156dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
29166dd60135SNamhyung Kim 			if (env->arch)
29176dd60135SNamhyung Kim 				tui__header_window(env);
29186dd60135SNamhyung Kim 			continue;
2919105eb30fSNamhyung Kim 		case 'F':
2920105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
2921105eb30fSNamhyung Kim 			continue;
292242337a22SNamhyung Kim 		case 'z':
292342337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
292442337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
292542337a22SNamhyung Kim 
292642337a22SNamhyung Kim 				top->zero = !top->zero;
292742337a22SNamhyung Kim 			}
292842337a22SNamhyung Kim 			continue;
2929b62e8dfcSNamhyung Kim 		case 'L':
2930b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
2931b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
2932b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2933b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
2934b62e8dfcSNamhyung Kim 				char *end;
2935b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
2936b62e8dfcSNamhyung Kim 
2937b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
2938b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
2939b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
2940b62e8dfcSNamhyung Kim 					continue;
2941b62e8dfcSNamhyung Kim 				}
2942b62e8dfcSNamhyung Kim 
2943b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
2944b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
2945b62e8dfcSNamhyung Kim 			}
2946b62e8dfcSNamhyung Kim 			continue;
2947aca7a94dSNamhyung Kim 		case K_F1:
2948aca7a94dSNamhyung Kim 		case 'h':
2949aca7a94dSNamhyung Kim 		case '?':
2950aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
2951e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
2952aca7a94dSNamhyung Kim 			continue;
2953aca7a94dSNamhyung Kim 		case K_ENTER:
2954aca7a94dSNamhyung Kim 		case K_RIGHT:
295531eb4360SNamhyung Kim 		case 'm':
2956aca7a94dSNamhyung Kim 			/* menu */
2957aca7a94dSNamhyung Kim 			break;
295863ab1749SArnaldo Carvalho de Melo 		case K_ESC:
2959aca7a94dSNamhyung Kim 		case K_LEFT: {
2960aca7a94dSNamhyung Kim 			const void *top;
2961aca7a94dSNamhyung Kim 
296201f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
2963aca7a94dSNamhyung Kim 				/*
2964aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
2965aca7a94dSNamhyung Kim 				 */
2966aca7a94dSNamhyung Kim 				if (left_exits)
2967aca7a94dSNamhyung Kim 					goto out_free_stack;
296863ab1749SArnaldo Carvalho de Melo 
296963ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
297063ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
297163ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
297263ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
297363ab1749SArnaldo Carvalho de Melo 
2974aca7a94dSNamhyung Kim 				continue;
2975aca7a94dSNamhyung Kim 			}
29766422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
2977bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
29786422184bSNamhyung Kim 				/*
29796422184bSNamhyung Kim 				 * No need to set actions->dso here since
29806422184bSNamhyung Kim 				 * it's just to remove the current filter.
29816422184bSNamhyung Kim 				 * Ditto for thread below.
29826422184bSNamhyung Kim 				 */
29836422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
298484734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
29856422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
298684734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
298784734b06SKan Liang 				do_zoom_socket(browser, actions);
298884734b06SKan Liang 			}
2989aca7a94dSNamhyung Kim 			continue;
2990aca7a94dSNamhyung Kim 		}
2991aca7a94dSNamhyung Kim 		case 'q':
2992aca7a94dSNamhyung Kim 		case CTRL('c'):
2993516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
2994fbb7997eSArnaldo Carvalho de Melo 		case 'f':
299513d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
299613d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
299713d1e536SNamhyung Kim 
299813d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
299913d1e536SNamhyung Kim 				/*
300013d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
300113d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
300213d1e536SNamhyung Kim 				 */
300313d1e536SNamhyung Kim 				if (top->evlist->enabled) {
300413d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
300513d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
300613d1e536SNamhyung Kim 				} else {
300713d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
300813d1e536SNamhyung Kim 					hbt->refresh = 0;
300913d1e536SNamhyung Kim 				}
301013d1e536SNamhyung Kim 				continue;
301113d1e536SNamhyung Kim 			}
30123e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
3013aca7a94dSNamhyung Kim 		default:
30143e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
3015aca7a94dSNamhyung Kim 			continue;
3016aca7a94dSNamhyung Kim 		}
3017aca7a94dSNamhyung Kim 
30182e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
30190ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
30200ba332f7SArnaldo Carvalho de Melo 
302155369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
3022aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
30230ba332f7SArnaldo Carvalho de Melo 
30240ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
30250ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
30260ba332f7SArnaldo Carvalho de Melo 
3027ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3028ea7cd592SNamhyung Kim 						       &actions[nr_options],
3029ea7cd592SNamhyung Kim 						       &options[nr_options],
3030ea7cd592SNamhyung Kim 						       bi->from.map,
3031ea7cd592SNamhyung Kim 						       bi->from.sym);
3032ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
3033ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
3034ea7cd592SNamhyung Kim 							&actions[nr_options],
3035ea7cd592SNamhyung Kim 							&options[nr_options],
3036ea7cd592SNamhyung Kim 							bi->to.map,
3037ea7cd592SNamhyung Kim 							bi->to.sym);
3038aca7a94dSNamhyung Kim 		} else {
3039ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3040ea7cd592SNamhyung Kim 						       &actions[nr_options],
3041ea7cd592SNamhyung Kim 						       &options[nr_options],
3042ea7cd592SNamhyung Kim 						       browser->selection->map,
3043ea7cd592SNamhyung Kim 						       browser->selection->sym);
3044446fb96cSArnaldo Carvalho de Melo 		}
30450ba332f7SArnaldo Carvalho de Melo skip_annotation:
3046ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
3047ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
3048ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
3049045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
3050ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
3051ea7cd592SNamhyung Kim 					  &options[nr_options],
3052bd315aabSWang Nan 					  browser->selection ?
3053bd315aabSWang Nan 						browser->selection->map : NULL);
305484734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
305584734b06SKan Liang 					     &options[nr_options],
305684734b06SKan Liang 					     socked_id);
3057cdbab7c2SFeng Tang 		/* perf script support */
3058b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
3059b1baae89SNamhyung Kim 			goto skip_scripting;
3060b1baae89SNamhyung Kim 
3061cdbab7c2SFeng Tang 		if (browser->he_selection) {
3062fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
3063ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3064ea7cd592SNamhyung Kim 							     &actions[nr_options],
3065ea7cd592SNamhyung Kim 							     &options[nr_options],
3066ea7cd592SNamhyung Kim 							     thread, NULL);
30672eafd410SNamhyung Kim 			}
3068bd315aabSWang Nan 			/*
3069bd315aabSWang Nan 			 * Note that browser->selection != NULL
3070bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
3071bd315aabSWang Nan 			 * so we don't need to check browser->selection
3072bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
3073bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
3074bd315aabSWang Nan 			 *
3075bd315aabSWang Nan 			 * See hist_browser__show_entry.
3076bd315aabSWang Nan 			 */
30772e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
3078ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3079ea7cd592SNamhyung Kim 							     &actions[nr_options],
3080ea7cd592SNamhyung Kim 							     &options[nr_options],
3081ea7cd592SNamhyung Kim 							     NULL, browser->selection->sym);
3082cdbab7c2SFeng Tang 			}
3083c221acb0SNamhyung Kim 		}
3084ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
3085ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
3086ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
3087ea7cd592SNamhyung Kim 					     &options[nr_options]);
3088b1baae89SNamhyung Kim skip_scripting:
3089ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
3090ea7cd592SNamhyung Kim 					   &options[nr_options]);
3091aca7a94dSNamhyung Kim 
3092ea7cd592SNamhyung Kim 		do {
3093ea7cd592SNamhyung Kim 			struct popup_action *act;
3094ea7cd592SNamhyung Kim 
3095ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
3096ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
3097aca7a94dSNamhyung Kim 				break;
3098aca7a94dSNamhyung Kim 
3099ea7cd592SNamhyung Kim 			act = &actions[choice];
3100ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
3101ea7cd592SNamhyung Kim 		} while (key == 1);
3102aca7a94dSNamhyung Kim 
3103bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3104341487abSFeng Tang 			break;
3105341487abSFeng Tang 	}
3106aca7a94dSNamhyung Kim out_free_stack:
310701f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3108aca7a94dSNamhyung Kim out:
3109aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3110f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3111aca7a94dSNamhyung Kim 	return key;
3112aca7a94dSNamhyung Kim }
3113aca7a94dSNamhyung Kim 
3114aca7a94dSNamhyung Kim struct perf_evsel_menu {
3115aca7a94dSNamhyung Kim 	struct ui_browser b;
3116aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
3117aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3118064f1981SNamhyung Kim 	float min_pcnt;
3119ce80d3beSKan Liang 	struct perf_env *env;
3120aca7a94dSNamhyung Kim };
3121aca7a94dSNamhyung Kim 
3122aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3123aca7a94dSNamhyung Kim 				   void *entry, int row)
3124aca7a94dSNamhyung Kim {
3125aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
3126aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
3127aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
31284ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3129aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
31304ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
31317289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
3132aca7a94dSNamhyung Kim 	char bf[256], unit;
3133aca7a94dSNamhyung Kim 	const char *warn = " ";
3134aca7a94dSNamhyung Kim 	size_t printed;
3135aca7a94dSNamhyung Kim 
3136aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3137aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3138aca7a94dSNamhyung Kim 
3139759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
3140717e263fSNamhyung Kim 		struct perf_evsel *pos;
3141717e263fSNamhyung Kim 
3142717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
3143717e263fSNamhyung Kim 
3144717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
31454ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
31464ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3147717e263fSNamhyung Kim 		}
3148717e263fSNamhyung Kim 	}
3149717e263fSNamhyung Kim 
3150aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3151aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3152aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3153517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3154aca7a94dSNamhyung Kim 
31554ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3156aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3157aca7a94dSNamhyung Kim 		menu->lost_events = true;
3158aca7a94dSNamhyung Kim 		if (!current_entry)
3159aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3160aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3161aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3162aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3163aca7a94dSNamhyung Kim 		warn = bf;
3164aca7a94dSNamhyung Kim 	}
3165aca7a94dSNamhyung Kim 
316626270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3167aca7a94dSNamhyung Kim 
3168aca7a94dSNamhyung Kim 	if (current_entry)
3169aca7a94dSNamhyung Kim 		menu->selection = evsel;
3170aca7a94dSNamhyung Kim }
3171aca7a94dSNamhyung Kim 
3172aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3173aca7a94dSNamhyung Kim 				int nr_events, const char *help,
31749783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
3175aca7a94dSNamhyung Kim {
3176aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
3177aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3178dd00d486SJiri Olsa 	const char *title = "Available samples";
31799783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3180aca7a94dSNamhyung Kim 	int key;
3181aca7a94dSNamhyung Kim 
3182aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3183aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3184aca7a94dSNamhyung Kim 		return -1;
3185aca7a94dSNamhyung Kim 
3186aca7a94dSNamhyung Kim 	while (1) {
3187aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3188aca7a94dSNamhyung Kim 
3189aca7a94dSNamhyung Kim 		switch (key) {
3190aca7a94dSNamhyung Kim 		case K_TIMER:
31919783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
3192aca7a94dSNamhyung Kim 
3193aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
3194aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3195aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3196aca7a94dSNamhyung Kim 			}
3197aca7a94dSNamhyung Kim 			continue;
3198aca7a94dSNamhyung Kim 		case K_RIGHT:
3199aca7a94dSNamhyung Kim 		case K_ENTER:
3200aca7a94dSNamhyung Kim 			if (!menu->selection)
3201aca7a94dSNamhyung Kim 				continue;
3202aca7a94dSNamhyung Kim 			pos = menu->selection;
3203aca7a94dSNamhyung Kim browse_hists:
3204aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
3205aca7a94dSNamhyung Kim 			/*
3206aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3207aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3208aca7a94dSNamhyung Kim 			 */
32099783adf7SNamhyung Kim 			if (hbt)
32109783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3211aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
3212dd00d486SJiri Olsa 						       true, hbt,
3213064f1981SNamhyung Kim 						       menu->min_pcnt,
321468d80758SNamhyung Kim 						       menu->env);
3215aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3216aca7a94dSNamhyung Kim 			switch (key) {
3217aca7a94dSNamhyung Kim 			case K_TAB:
3218aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
32199a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
3220aca7a94dSNamhyung Kim 				else
32219a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
3222aca7a94dSNamhyung Kim 				goto browse_hists;
3223aca7a94dSNamhyung Kim 			case K_UNTAB:
3224aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
32259a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
3226aca7a94dSNamhyung Kim 				else
3227d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
3228aca7a94dSNamhyung Kim 				goto browse_hists;
3229341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
3230aca7a94dSNamhyung Kim 			case 'q':
3231aca7a94dSNamhyung Kim 			case CTRL('c'):
3232aca7a94dSNamhyung Kim 				goto out;
323363ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3234aca7a94dSNamhyung Kim 			default:
3235aca7a94dSNamhyung Kim 				continue;
3236aca7a94dSNamhyung Kim 			}
3237aca7a94dSNamhyung Kim 		case K_LEFT:
3238aca7a94dSNamhyung Kim 			continue;
3239aca7a94dSNamhyung Kim 		case K_ESC:
3240aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3241aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3242aca7a94dSNamhyung Kim 				continue;
3243aca7a94dSNamhyung Kim 			/* Fall thru */
3244aca7a94dSNamhyung Kim 		case 'q':
3245aca7a94dSNamhyung Kim 		case CTRL('c'):
3246aca7a94dSNamhyung Kim 			goto out;
3247aca7a94dSNamhyung Kim 		default:
3248aca7a94dSNamhyung Kim 			continue;
3249aca7a94dSNamhyung Kim 		}
3250aca7a94dSNamhyung Kim 	}
3251aca7a94dSNamhyung Kim 
3252aca7a94dSNamhyung Kim out:
3253aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3254aca7a94dSNamhyung Kim 	return key;
3255aca7a94dSNamhyung Kim }
3256aca7a94dSNamhyung Kim 
3257316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3258fc24d7c2SNamhyung Kim 				 void *entry)
3259fc24d7c2SNamhyung Kim {
3260fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3261fc24d7c2SNamhyung Kim 
3262fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3263fc24d7c2SNamhyung Kim 		return true;
3264fc24d7c2SNamhyung Kim 
3265fc24d7c2SNamhyung Kim 	return false;
3266fc24d7c2SNamhyung Kim }
3267fc24d7c2SNamhyung Kim 
3268aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3269fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
327068d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
3271064f1981SNamhyung Kim 					   float min_pcnt,
3272ce80d3beSKan Liang 					   struct perf_env *env)
3273aca7a94dSNamhyung Kim {
3274aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3275aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
3276aca7a94dSNamhyung Kim 		.b = {
3277aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
3278aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3279aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3280aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3281fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3282fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3283aca7a94dSNamhyung Kim 			.priv	    = evlist,
3284aca7a94dSNamhyung Kim 		},
3285064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
328668d80758SNamhyung Kim 		.env = env,
3287aca7a94dSNamhyung Kim 	};
3288aca7a94dSNamhyung Kim 
3289aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3290aca7a94dSNamhyung Kim 
3291e5cadb93SArnaldo Carvalho de Melo 	evlist__for_each_entry(evlist, pos) {
32927289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
3293aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3294aca7a94dSNamhyung Kim 
3295aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3296aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3297aca7a94dSNamhyung Kim 	}
3298aca7a94dSNamhyung Kim 
3299fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3300aca7a94dSNamhyung Kim }
3301aca7a94dSNamhyung Kim 
3302aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
330368d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
3304064f1981SNamhyung Kim 				  float min_pcnt,
3305ce80d3beSKan Liang 				  struct perf_env *env)
3306aca7a94dSNamhyung Kim {
3307fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
3308fc24d7c2SNamhyung Kim 
3309fc24d7c2SNamhyung Kim single_entry:
3310fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
33119a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
3312fc24d7c2SNamhyung Kim 
3313fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
3314dd00d486SJiri Olsa 						false, hbt, min_pcnt,
3315064f1981SNamhyung Kim 						env);
3316aca7a94dSNamhyung Kim 	}
3317aca7a94dSNamhyung Kim 
3318fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
3319fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
3320fc24d7c2SNamhyung Kim 
3321fc24d7c2SNamhyung Kim 		nr_entries = 0;
3322e5cadb93SArnaldo Carvalho de Melo 		evlist__for_each_entry(evlist, pos) {
3323fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
3324fc24d7c2SNamhyung Kim 				nr_entries++;
33250050f7aaSArnaldo Carvalho de Melo 		}
3326fc24d7c2SNamhyung Kim 
3327fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3328fc24d7c2SNamhyung Kim 			goto single_entry;
3329fc24d7c2SNamhyung Kim 	}
3330fc24d7c2SNamhyung Kim 
3331fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3332064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
3333aca7a94dSNamhyung Kim }
3334