xref: /linux/tools/perf/ui/browsers/hists.c (revision d675107ce6fa988102851e0b0ef06e46c8aa7ac6)
1aca7a94dSNamhyung Kim #include <stdio.h>
2aca7a94dSNamhyung Kim #include "../libslang.h"
3aca7a94dSNamhyung Kim #include <stdlib.h>
4aca7a94dSNamhyung Kim #include <string.h>
5aca7a94dSNamhyung Kim #include <linux/rbtree.h>
6aca7a94dSNamhyung Kim 
7aca7a94dSNamhyung Kim #include "../../util/evsel.h"
8aca7a94dSNamhyung Kim #include "../../util/evlist.h"
9aca7a94dSNamhyung Kim #include "../../util/hist.h"
10aca7a94dSNamhyung Kim #include "../../util/pstack.h"
11aca7a94dSNamhyung Kim #include "../../util/sort.h"
12aca7a94dSNamhyung Kim #include "../../util/util.h"
1368d80758SNamhyung Kim #include "../../arch/common.h"
14aca7a94dSNamhyung Kim 
15aca7a94dSNamhyung Kim #include "../browser.h"
16aca7a94dSNamhyung Kim #include "../helpline.h"
17aca7a94dSNamhyung Kim #include "../util.h"
18aca7a94dSNamhyung Kim #include "../ui.h"
19aca7a94dSNamhyung Kim #include "map.h"
20d755330cSJiri Olsa #include "annotate.h"
21aca7a94dSNamhyung Kim 
22aca7a94dSNamhyung Kim struct hist_browser {
23aca7a94dSNamhyung Kim 	struct ui_browser   b;
24aca7a94dSNamhyung Kim 	struct hists	    *hists;
25aca7a94dSNamhyung Kim 	struct hist_entry   *he_selection;
26aca7a94dSNamhyung Kim 	struct map_symbol   *selection;
27aff3f3f6SArnaldo Carvalho de Melo 	int		     print_seq;
28a7cb8863SArnaldo Carvalho de Melo 	bool		     show_dso;
29025bf7eaSArnaldo Carvalho de Melo 	bool		     show_headers;
30064f1981SNamhyung Kim 	float		     min_pcnt;
31112f761fSNamhyung Kim 	u64		     nr_non_filtered_entries;
32c3b78952SNamhyung Kim 	u64		     nr_callchain_rows;
33aca7a94dSNamhyung Kim };
34aca7a94dSNamhyung Kim 
35f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
36f5951d56SNamhyung Kim 
37dd00d486SJiri Olsa static int hists__browser_title(struct hists *hists, char *bf, size_t size);
38112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
39aca7a94dSNamhyung Kim 
40c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
41c3b78952SNamhyung Kim 					     float min_pcnt);
42c3b78952SNamhyung Kim 
43268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
44268397cbSNamhyung Kim {
45268397cbSNamhyung Kim 	return hists__has_filter(hb->hists) || hb->min_pcnt;
46268397cbSNamhyung Kim }
47268397cbSNamhyung Kim 
48c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
49c3b78952SNamhyung Kim {
50c3b78952SNamhyung Kim 	u32 nr_entries;
51c3b78952SNamhyung Kim 
52c3b78952SNamhyung Kim 	if (hist_browser__has_filter(hb))
53c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
54c3b78952SNamhyung Kim 	else
55c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
56c3b78952SNamhyung Kim 
57c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
58c3b78952SNamhyung Kim }
59c3b78952SNamhyung Kim 
60025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
61025bf7eaSArnaldo Carvalho de Melo {
62025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
63025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
64025bf7eaSArnaldo Carvalho de Melo 
65025bf7eaSArnaldo Carvalho de Melo 	browser->rows = browser->height - header_offset;
66025bf7eaSArnaldo Carvalho de Melo 	/*
67025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
68025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
69025bf7eaSArnaldo Carvalho de Melo 	 */
70025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
71025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
72025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
73025bf7eaSArnaldo Carvalho de Melo }
74025bf7eaSArnaldo Carvalho de Melo 
75357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
76aca7a94dSNamhyung Kim {
77357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
78357cfff1SArnaldo Carvalho de Melo 
79aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
80357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
81357cfff1SArnaldo Carvalho de Melo 	/*
82357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
83357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
84357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
85357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
86357cfff1SArnaldo Carvalho de Melo  	 */
87357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
88025bf7eaSArnaldo Carvalho de Melo 	hist_browser__update_rows(hb);
89aca7a94dSNamhyung Kim }
90aca7a94dSNamhyung Kim 
91ca3ff33bSArnaldo Carvalho de Melo static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
92ca3ff33bSArnaldo Carvalho de Melo {
93025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = browser->show_headers ? 1 : 0;
94025bf7eaSArnaldo Carvalho de Melo 
95025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row + header_offset, column);
96ca3ff33bSArnaldo Carvalho de Melo }
97ca3ff33bSArnaldo Carvalho de Melo 
9805e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
99aca7a94dSNamhyung Kim {
100c3b78952SNamhyung Kim 	/*
101c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
102c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
103c3b78952SNamhyung Kim 	 */
104c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
105c3b78952SNamhyung Kim 
106268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
107c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
108357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
10905e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
110aca7a94dSNamhyung Kim }
111aca7a94dSNamhyung Kim 
112aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
113aca7a94dSNamhyung Kim {
114aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
115aca7a94dSNamhyung Kim }
116aca7a94dSNamhyung Kim 
11705e8b080SArnaldo Carvalho de Melo static char map_symbol__folded(const struct map_symbol *ms)
118aca7a94dSNamhyung Kim {
11905e8b080SArnaldo Carvalho de Melo 	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
120aca7a94dSNamhyung Kim }
121aca7a94dSNamhyung Kim 
12205e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
123aca7a94dSNamhyung Kim {
12405e8b080SArnaldo Carvalho de Melo 	return map_symbol__folded(&he->ms);
125aca7a94dSNamhyung Kim }
126aca7a94dSNamhyung Kim 
12705e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
128aca7a94dSNamhyung Kim {
12905e8b080SArnaldo Carvalho de Melo 	return map_symbol__folded(&cl->ms);
130aca7a94dSNamhyung Kim }
131aca7a94dSNamhyung Kim 
13205e8b080SArnaldo Carvalho de Melo static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
133aca7a94dSNamhyung Kim {
13405e8b080SArnaldo Carvalho de Melo 	ms->unfolded = unfold ? ms->has_children : false;
135aca7a94dSNamhyung Kim }
136aca7a94dSNamhyung Kim 
13705e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
138aca7a94dSNamhyung Kim {
139aca7a94dSNamhyung Kim 	int n = 0;
140aca7a94dSNamhyung Kim 	struct rb_node *nd;
141aca7a94dSNamhyung Kim 
14205e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
143aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
144aca7a94dSNamhyung Kim 		struct callchain_list *chain;
145aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
146aca7a94dSNamhyung Kim 
147aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
148aca7a94dSNamhyung Kim 			++n;
149aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
150aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
151aca7a94dSNamhyung Kim 			if (folded_sign == '+')
152aca7a94dSNamhyung Kim 				break;
153aca7a94dSNamhyung Kim 		}
154aca7a94dSNamhyung Kim 
155aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
156aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
157aca7a94dSNamhyung Kim 	}
158aca7a94dSNamhyung Kim 
159aca7a94dSNamhyung Kim 	return n;
160aca7a94dSNamhyung Kim }
161aca7a94dSNamhyung Kim 
162aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
163aca7a94dSNamhyung Kim {
164aca7a94dSNamhyung Kim 	struct callchain_list *chain;
165aca7a94dSNamhyung Kim 	bool unfolded = false;
166aca7a94dSNamhyung Kim 	int n = 0;
167aca7a94dSNamhyung Kim 
168aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
169aca7a94dSNamhyung Kim 		++n;
170aca7a94dSNamhyung Kim 		unfolded = chain->ms.unfolded;
171aca7a94dSNamhyung Kim 	}
172aca7a94dSNamhyung Kim 
173aca7a94dSNamhyung Kim 	if (unfolded)
174aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
175aca7a94dSNamhyung Kim 
176aca7a94dSNamhyung Kim 	return n;
177aca7a94dSNamhyung Kim }
178aca7a94dSNamhyung Kim 
179aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
180aca7a94dSNamhyung Kim {
181aca7a94dSNamhyung Kim 	struct rb_node *nd;
182aca7a94dSNamhyung Kim 	int n = 0;
183aca7a94dSNamhyung Kim 
184aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
185aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
186aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
187aca7a94dSNamhyung Kim 	}
188aca7a94dSNamhyung Kim 
189aca7a94dSNamhyung Kim 	return n;
190aca7a94dSNamhyung Kim }
191aca7a94dSNamhyung Kim 
19205e8b080SArnaldo Carvalho de Melo static bool map_symbol__toggle_fold(struct map_symbol *ms)
193aca7a94dSNamhyung Kim {
19405e8b080SArnaldo Carvalho de Melo 	if (!ms)
195aca7a94dSNamhyung Kim 		return false;
196aca7a94dSNamhyung Kim 
19705e8b080SArnaldo Carvalho de Melo 	if (!ms->has_children)
198aca7a94dSNamhyung Kim 		return false;
199aca7a94dSNamhyung Kim 
20005e8b080SArnaldo Carvalho de Melo 	ms->unfolded = !ms->unfolded;
201aca7a94dSNamhyung Kim 	return true;
202aca7a94dSNamhyung Kim }
203aca7a94dSNamhyung Kim 
20405e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
205aca7a94dSNamhyung Kim {
20605e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
207aca7a94dSNamhyung Kim 
20805e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
209aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
210aca7a94dSNamhyung Kim 		struct callchain_list *chain;
211aca7a94dSNamhyung Kim 		bool first = true;
212aca7a94dSNamhyung Kim 
213aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
214aca7a94dSNamhyung Kim 			if (first) {
215aca7a94dSNamhyung Kim 				first = false;
216aca7a94dSNamhyung Kim 				chain->ms.has_children = chain->list.next != &child->val ||
217aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
218aca7a94dSNamhyung Kim 			} else
219aca7a94dSNamhyung Kim 				chain->ms.has_children = chain->list.next == &child->val &&
220aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
221aca7a94dSNamhyung Kim 		}
222aca7a94dSNamhyung Kim 
223aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
224aca7a94dSNamhyung Kim 	}
225aca7a94dSNamhyung Kim }
226aca7a94dSNamhyung Kim 
22705e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children(struct callchain_node *node)
228aca7a94dSNamhyung Kim {
229aca7a94dSNamhyung Kim 	struct callchain_list *chain;
230aca7a94dSNamhyung Kim 
23105e8b080SArnaldo Carvalho de Melo 	list_for_each_entry(chain, &node->val, list)
23205e8b080SArnaldo Carvalho de Melo 		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
233aca7a94dSNamhyung Kim 
23405e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
235aca7a94dSNamhyung Kim }
236aca7a94dSNamhyung Kim 
23705e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
238aca7a94dSNamhyung Kim {
239aca7a94dSNamhyung Kim 	struct rb_node *nd;
240aca7a94dSNamhyung Kim 
24105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
242aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
243aca7a94dSNamhyung Kim 		callchain_node__init_have_children(node);
244aca7a94dSNamhyung Kim 	}
245aca7a94dSNamhyung Kim }
246aca7a94dSNamhyung Kim 
24705e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
248aca7a94dSNamhyung Kim {
24905e8b080SArnaldo Carvalho de Melo 	if (!he->init_have_children) {
25005e8b080SArnaldo Carvalho de Melo 		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
25105e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
25205e8b080SArnaldo Carvalho de Melo 		he->init_have_children = true;
253aca7a94dSNamhyung Kim 	}
254aca7a94dSNamhyung Kim }
255aca7a94dSNamhyung Kim 
25605e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
257aca7a94dSNamhyung Kim {
25805e8b080SArnaldo Carvalho de Melo 	if (map_symbol__toggle_fold(browser->selection)) {
25905e8b080SArnaldo Carvalho de Melo 		struct hist_entry *he = browser->he_selection;
260aca7a94dSNamhyung Kim 
261aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
262c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
263c3b78952SNamhyung Kim 		browser->nr_callchain_rows -= he->nr_rows;
264aca7a94dSNamhyung Kim 
265aca7a94dSNamhyung Kim 		if (he->ms.unfolded)
266aca7a94dSNamhyung Kim 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
267aca7a94dSNamhyung Kim 		else
268aca7a94dSNamhyung Kim 			he->nr_rows = 0;
269c3b78952SNamhyung Kim 
270c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
271c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
272aca7a94dSNamhyung Kim 
273aca7a94dSNamhyung Kim 		return true;
274aca7a94dSNamhyung Kim 	}
275aca7a94dSNamhyung Kim 
276aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
277aca7a94dSNamhyung Kim 	return false;
278aca7a94dSNamhyung Kim }
279aca7a94dSNamhyung Kim 
28005e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
281aca7a94dSNamhyung Kim {
282aca7a94dSNamhyung Kim 	int n = 0;
283aca7a94dSNamhyung Kim 	struct rb_node *nd;
284aca7a94dSNamhyung Kim 
28505e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
286aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
287aca7a94dSNamhyung Kim 		struct callchain_list *chain;
288aca7a94dSNamhyung Kim 		bool has_children = false;
289aca7a94dSNamhyung Kim 
290aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
291aca7a94dSNamhyung Kim 			++n;
292aca7a94dSNamhyung Kim 			map_symbol__set_folding(&chain->ms, unfold);
293aca7a94dSNamhyung Kim 			has_children = chain->ms.has_children;
294aca7a94dSNamhyung Kim 		}
295aca7a94dSNamhyung Kim 
296aca7a94dSNamhyung Kim 		if (has_children)
297aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
298aca7a94dSNamhyung Kim 	}
299aca7a94dSNamhyung Kim 
300aca7a94dSNamhyung Kim 	return n;
301aca7a94dSNamhyung Kim }
302aca7a94dSNamhyung Kim 
303aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
304aca7a94dSNamhyung Kim {
305aca7a94dSNamhyung Kim 	struct callchain_list *chain;
306aca7a94dSNamhyung Kim 	bool has_children = false;
307aca7a94dSNamhyung Kim 	int n = 0;
308aca7a94dSNamhyung Kim 
309aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
310aca7a94dSNamhyung Kim 		++n;
311aca7a94dSNamhyung Kim 		map_symbol__set_folding(&chain->ms, unfold);
312aca7a94dSNamhyung Kim 		has_children = chain->ms.has_children;
313aca7a94dSNamhyung Kim 	}
314aca7a94dSNamhyung Kim 
315aca7a94dSNamhyung Kim 	if (has_children)
316aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
317aca7a94dSNamhyung Kim 
318aca7a94dSNamhyung Kim 	return n;
319aca7a94dSNamhyung Kim }
320aca7a94dSNamhyung Kim 
321aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
322aca7a94dSNamhyung Kim {
323aca7a94dSNamhyung Kim 	struct rb_node *nd;
324aca7a94dSNamhyung Kim 	int n = 0;
325aca7a94dSNamhyung Kim 
326aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
327aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
328aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
329aca7a94dSNamhyung Kim 	}
330aca7a94dSNamhyung Kim 
331aca7a94dSNamhyung Kim 	return n;
332aca7a94dSNamhyung Kim }
333aca7a94dSNamhyung Kim 
33405e8b080SArnaldo Carvalho de Melo static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
335aca7a94dSNamhyung Kim {
33605e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
33705e8b080SArnaldo Carvalho de Melo 	map_symbol__set_folding(&he->ms, unfold);
338aca7a94dSNamhyung Kim 
33905e8b080SArnaldo Carvalho de Melo 	if (he->ms.has_children) {
34005e8b080SArnaldo Carvalho de Melo 		int n = callchain__set_folding(&he->sorted_chain, unfold);
34105e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
342aca7a94dSNamhyung Kim 	} else
34305e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
344aca7a94dSNamhyung Kim }
345aca7a94dSNamhyung Kim 
346c3b78952SNamhyung Kim static void
347c3b78952SNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
348aca7a94dSNamhyung Kim {
349aca7a94dSNamhyung Kim 	struct rb_node *nd;
350c3b78952SNamhyung Kim 	struct hists *hists = browser->hists;
351aca7a94dSNamhyung Kim 
352c3b78952SNamhyung Kim 	for (nd = rb_first(&hists->entries);
35314135663SNamhyung Kim 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
354c3b78952SNamhyung Kim 	     nd = rb_next(nd)) {
355aca7a94dSNamhyung Kim 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
356aca7a94dSNamhyung Kim 		hist_entry__set_folding(he, unfold);
357c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
358aca7a94dSNamhyung Kim 	}
359aca7a94dSNamhyung Kim }
360aca7a94dSNamhyung Kim 
36105e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
362aca7a94dSNamhyung Kim {
363c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
364c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
365c3b78952SNamhyung Kim 
366c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
367aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
36805e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
369aca7a94dSNamhyung Kim }
370aca7a94dSNamhyung Kim 
371aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
372aca7a94dSNamhyung Kim {
373aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
374aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
375aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
376aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
377aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
378aca7a94dSNamhyung Kim }
379aca7a94dSNamhyung Kim 
380dd00d486SJiri Olsa static int hist_browser__run(struct hist_browser *browser,
3819783adf7SNamhyung Kim 			     struct hist_browser_timer *hbt)
382aca7a94dSNamhyung Kim {
383aca7a94dSNamhyung Kim 	int key;
384aca7a94dSNamhyung Kim 	char title[160];
3859783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
386aca7a94dSNamhyung Kim 
38705e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
388c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
389aca7a94dSNamhyung Kim 
390dd00d486SJiri Olsa 	hists__browser_title(browser->hists, title, sizeof(title));
391aca7a94dSNamhyung Kim 
39205e8b080SArnaldo Carvalho de Melo 	if (ui_browser__show(&browser->b, title,
393aca7a94dSNamhyung Kim 			     "Press '?' for help on key bindings") < 0)
394aca7a94dSNamhyung Kim 		return -1;
395aca7a94dSNamhyung Kim 
396aca7a94dSNamhyung Kim 	while (1) {
39705e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
398aca7a94dSNamhyung Kim 
399aca7a94dSNamhyung Kim 		switch (key) {
400fa5df943SNamhyung Kim 		case K_TIMER: {
401fa5df943SNamhyung Kim 			u64 nr_entries;
4029783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
403fa5df943SNamhyung Kim 
404c3b78952SNamhyung Kim 			if (hist_browser__has_filter(browser))
405112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
406fa5df943SNamhyung Kim 
407c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
408fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
409aca7a94dSNamhyung Kim 
41005e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
41105e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
41205e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
41305e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
41405e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
415aca7a94dSNamhyung Kim 			}
416aca7a94dSNamhyung Kim 
417dd00d486SJiri Olsa 			hists__browser_title(browser->hists, title, sizeof(title));
41805e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
419aca7a94dSNamhyung Kim 			continue;
420fa5df943SNamhyung Kim 		}
421aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
422aca7a94dSNamhyung Kim 			static int seq;
42305e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
424aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
425aca7a94dSNamhyung Kim 			ui_helpline__pop();
42662c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
42705e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
42805e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
42962c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
43005e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
43105e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
432aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
433aca7a94dSNamhyung Kim 		}
434aca7a94dSNamhyung Kim 			break;
435aca7a94dSNamhyung Kim 		case 'C':
436aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
43705e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
438aca7a94dSNamhyung Kim 			break;
439aca7a94dSNamhyung Kim 		case 'E':
440aca7a94dSNamhyung Kim 			/* Expand the whole world. */
44105e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
442aca7a94dSNamhyung Kim 			break;
443025bf7eaSArnaldo Carvalho de Melo 		case 'H':
444025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
445025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
446025bf7eaSArnaldo Carvalho de Melo 			break;
447aca7a94dSNamhyung Kim 		case K_ENTER:
44805e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
449aca7a94dSNamhyung Kim 				break;
450aca7a94dSNamhyung Kim 			/* fall thru */
451aca7a94dSNamhyung Kim 		default:
452aca7a94dSNamhyung Kim 			goto out;
453aca7a94dSNamhyung Kim 		}
454aca7a94dSNamhyung Kim 	}
455aca7a94dSNamhyung Kim out:
45605e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
457aca7a94dSNamhyung Kim 	return key;
458aca7a94dSNamhyung Kim }
459aca7a94dSNamhyung Kim 
46005e8b080SArnaldo Carvalho de Melo static char *callchain_list__sym_name(struct callchain_list *cl,
461a7cb8863SArnaldo Carvalho de Melo 				      char *bf, size_t bfsize, bool show_dso)
462aca7a94dSNamhyung Kim {
463a7cb8863SArnaldo Carvalho de Melo 	int printed;
464aca7a94dSNamhyung Kim 
465a7cb8863SArnaldo Carvalho de Melo 	if (cl->ms.sym)
466a7cb8863SArnaldo Carvalho de Melo 		printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
467a7cb8863SArnaldo Carvalho de Melo 	else
468a7cb8863SArnaldo Carvalho de Melo 		printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
469a7cb8863SArnaldo Carvalho de Melo 
470a7cb8863SArnaldo Carvalho de Melo 	if (show_dso)
471a7cb8863SArnaldo Carvalho de Melo 		scnprintf(bf + printed, bfsize - printed, " %s",
472a7cb8863SArnaldo Carvalho de Melo 			  cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
473a7cb8863SArnaldo Carvalho de Melo 
474aca7a94dSNamhyung Kim 	return bf;
475aca7a94dSNamhyung Kim }
476aca7a94dSNamhyung Kim 
477aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
478aca7a94dSNamhyung Kim 
47905e8b080SArnaldo Carvalho de Melo static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
480aca7a94dSNamhyung Kim 						     struct callchain_node *chain_node,
481aca7a94dSNamhyung Kim 						     u64 total, int level,
482aca7a94dSNamhyung Kim 						     unsigned short row,
483aca7a94dSNamhyung Kim 						     off_t *row_offset,
484aca7a94dSNamhyung Kim 						     bool *is_current_entry)
485aca7a94dSNamhyung Kim {
486aca7a94dSNamhyung Kim 	struct rb_node *node;
487aca7a94dSNamhyung Kim 	int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
488aca7a94dSNamhyung Kim 	u64 new_total, remaining;
489aca7a94dSNamhyung Kim 
490aca7a94dSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
491aca7a94dSNamhyung Kim 		new_total = chain_node->children_hit;
492aca7a94dSNamhyung Kim 	else
493aca7a94dSNamhyung Kim 		new_total = total;
494aca7a94dSNamhyung Kim 
495aca7a94dSNamhyung Kim 	remaining = new_total;
496aca7a94dSNamhyung Kim 	node = rb_first(&chain_node->rb_root);
497aca7a94dSNamhyung Kim 	while (node) {
498aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
499aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
500aca7a94dSNamhyung Kim 		u64 cumul = callchain_cumul_hits(child);
501aca7a94dSNamhyung Kim 		struct callchain_list *chain;
502aca7a94dSNamhyung Kim 		char folded_sign = ' ';
503aca7a94dSNamhyung Kim 		int first = true;
504aca7a94dSNamhyung Kim 		int extra_offset = 0;
505aca7a94dSNamhyung Kim 
506aca7a94dSNamhyung Kim 		remaining -= cumul;
507aca7a94dSNamhyung Kim 
508aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
509a7cb8863SArnaldo Carvalho de Melo 			char bf[1024], *alloc_str;
510aca7a94dSNamhyung Kim 			const char *str;
511aca7a94dSNamhyung Kim 			int color;
512aca7a94dSNamhyung Kim 			bool was_first = first;
513aca7a94dSNamhyung Kim 
514aca7a94dSNamhyung Kim 			if (first)
515aca7a94dSNamhyung Kim 				first = false;
516aca7a94dSNamhyung Kim 			else
517aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
518aca7a94dSNamhyung Kim 
519aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
520aca7a94dSNamhyung Kim 			if (*row_offset != 0) {
521aca7a94dSNamhyung Kim 				--*row_offset;
522aca7a94dSNamhyung Kim 				goto do_next;
523aca7a94dSNamhyung Kim 			}
524aca7a94dSNamhyung Kim 
525aca7a94dSNamhyung Kim 			alloc_str = NULL;
526a7cb8863SArnaldo Carvalho de Melo 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
527a7cb8863SArnaldo Carvalho de Melo 						       browser->show_dso);
528aca7a94dSNamhyung Kim 			if (was_first) {
529aca7a94dSNamhyung Kim 				double percent = cumul * 100.0 / new_total;
530aca7a94dSNamhyung Kim 
531aca7a94dSNamhyung Kim 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
532aca7a94dSNamhyung Kim 					str = "Not enough memory!";
533aca7a94dSNamhyung Kim 				else
534aca7a94dSNamhyung Kim 					str = alloc_str;
535aca7a94dSNamhyung Kim 			}
536aca7a94dSNamhyung Kim 
537aca7a94dSNamhyung Kim 			color = HE_COLORSET_NORMAL;
53805e8b080SArnaldo Carvalho de Melo 			width = browser->b.width - (offset + extra_offset + 2);
53905e8b080SArnaldo Carvalho de Melo 			if (ui_browser__is_current_entry(&browser->b, row)) {
54005e8b080SArnaldo Carvalho de Melo 				browser->selection = &chain->ms;
541aca7a94dSNamhyung Kim 				color = HE_COLORSET_SELECTED;
542aca7a94dSNamhyung Kim 				*is_current_entry = true;
543aca7a94dSNamhyung Kim 			}
544aca7a94dSNamhyung Kim 
54505e8b080SArnaldo Carvalho de Melo 			ui_browser__set_color(&browser->b, color);
546ca3ff33bSArnaldo Carvalho de Melo 			hist_browser__gotorc(browser, row, 0);
547aca7a94dSNamhyung Kim 			slsmg_write_nstring(" ", offset + extra_offset);
548aca7a94dSNamhyung Kim 			slsmg_printf("%c ", folded_sign);
549aca7a94dSNamhyung Kim 			slsmg_write_nstring(str, width);
550aca7a94dSNamhyung Kim 			free(alloc_str);
551aca7a94dSNamhyung Kim 
55262c95ae3SArnaldo Carvalho de Melo 			if (++row == browser->b.rows)
553aca7a94dSNamhyung Kim 				goto out;
554aca7a94dSNamhyung Kim do_next:
555aca7a94dSNamhyung Kim 			if (folded_sign == '+')
556aca7a94dSNamhyung Kim 				break;
557aca7a94dSNamhyung Kim 		}
558aca7a94dSNamhyung Kim 
559aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
560aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
56105e8b080SArnaldo Carvalho de Melo 			row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
562aca7a94dSNamhyung Kim 									 new_level, row, row_offset,
563aca7a94dSNamhyung Kim 									 is_current_entry);
564aca7a94dSNamhyung Kim 		}
56562c95ae3SArnaldo Carvalho de Melo 		if (row == browser->b.rows)
566aca7a94dSNamhyung Kim 			goto out;
567aca7a94dSNamhyung Kim 		node = next;
568aca7a94dSNamhyung Kim 	}
569aca7a94dSNamhyung Kim out:
570aca7a94dSNamhyung Kim 	return row - first_row;
571aca7a94dSNamhyung Kim }
572aca7a94dSNamhyung Kim 
57305e8b080SArnaldo Carvalho de Melo static int hist_browser__show_callchain_node(struct hist_browser *browser,
574aca7a94dSNamhyung Kim 					     struct callchain_node *node,
575aca7a94dSNamhyung Kim 					     int level, unsigned short row,
576aca7a94dSNamhyung Kim 					     off_t *row_offset,
577aca7a94dSNamhyung Kim 					     bool *is_current_entry)
578aca7a94dSNamhyung Kim {
579aca7a94dSNamhyung Kim 	struct callchain_list *chain;
580aca7a94dSNamhyung Kim 	int first_row = row,
581aca7a94dSNamhyung Kim 	     offset = level * LEVEL_OFFSET_STEP,
58205e8b080SArnaldo Carvalho de Melo 	     width = browser->b.width - offset;
583aca7a94dSNamhyung Kim 	char folded_sign = ' ';
584aca7a94dSNamhyung Kim 
585aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
586a7cb8863SArnaldo Carvalho de Melo 		char bf[1024], *s;
587aca7a94dSNamhyung Kim 		int color;
588aca7a94dSNamhyung Kim 
589aca7a94dSNamhyung Kim 		folded_sign = callchain_list__folded(chain);
590aca7a94dSNamhyung Kim 
591aca7a94dSNamhyung Kim 		if (*row_offset != 0) {
592aca7a94dSNamhyung Kim 			--*row_offset;
593aca7a94dSNamhyung Kim 			continue;
594aca7a94dSNamhyung Kim 		}
595aca7a94dSNamhyung Kim 
596aca7a94dSNamhyung Kim 		color = HE_COLORSET_NORMAL;
59705e8b080SArnaldo Carvalho de Melo 		if (ui_browser__is_current_entry(&browser->b, row)) {
59805e8b080SArnaldo Carvalho de Melo 			browser->selection = &chain->ms;
599aca7a94dSNamhyung Kim 			color = HE_COLORSET_SELECTED;
600aca7a94dSNamhyung Kim 			*is_current_entry = true;
601aca7a94dSNamhyung Kim 		}
602aca7a94dSNamhyung Kim 
603a7cb8863SArnaldo Carvalho de Melo 		s = callchain_list__sym_name(chain, bf, sizeof(bf),
604a7cb8863SArnaldo Carvalho de Melo 					     browser->show_dso);
605ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
60605e8b080SArnaldo Carvalho de Melo 		ui_browser__set_color(&browser->b, color);
607aca7a94dSNamhyung Kim 		slsmg_write_nstring(" ", offset);
608aca7a94dSNamhyung Kim 		slsmg_printf("%c ", folded_sign);
609aca7a94dSNamhyung Kim 		slsmg_write_nstring(s, width - 2);
610aca7a94dSNamhyung Kim 
61162c95ae3SArnaldo Carvalho de Melo 		if (++row == browser->b.rows)
612aca7a94dSNamhyung Kim 			goto out;
613aca7a94dSNamhyung Kim 	}
614aca7a94dSNamhyung Kim 
615aca7a94dSNamhyung Kim 	if (folded_sign == '-')
61605e8b080SArnaldo Carvalho de Melo 		row += hist_browser__show_callchain_node_rb_tree(browser, node,
61705e8b080SArnaldo Carvalho de Melo 								 browser->hists->stats.total_period,
618aca7a94dSNamhyung Kim 								 level + 1, row,
619aca7a94dSNamhyung Kim 								 row_offset,
620aca7a94dSNamhyung Kim 								 is_current_entry);
621aca7a94dSNamhyung Kim out:
622aca7a94dSNamhyung Kim 	return row - first_row;
623aca7a94dSNamhyung Kim }
624aca7a94dSNamhyung Kim 
62505e8b080SArnaldo Carvalho de Melo static int hist_browser__show_callchain(struct hist_browser *browser,
626aca7a94dSNamhyung Kim 					struct rb_root *chain,
627aca7a94dSNamhyung Kim 					int level, unsigned short row,
628aca7a94dSNamhyung Kim 					off_t *row_offset,
629aca7a94dSNamhyung Kim 					bool *is_current_entry)
630aca7a94dSNamhyung Kim {
631aca7a94dSNamhyung Kim 	struct rb_node *nd;
632aca7a94dSNamhyung Kim 	int first_row = row;
633aca7a94dSNamhyung Kim 
634aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
635aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
636aca7a94dSNamhyung Kim 
63705e8b080SArnaldo Carvalho de Melo 		row += hist_browser__show_callchain_node(browser, node, level,
638aca7a94dSNamhyung Kim 							 row, row_offset,
639aca7a94dSNamhyung Kim 							 is_current_entry);
64062c95ae3SArnaldo Carvalho de Melo 		if (row == browser->b.rows)
641aca7a94dSNamhyung Kim 			break;
642aca7a94dSNamhyung Kim 	}
643aca7a94dSNamhyung Kim 
644aca7a94dSNamhyung Kim 	return row - first_row;
645aca7a94dSNamhyung Kim }
646aca7a94dSNamhyung Kim 
64789701460SNamhyung Kim struct hpp_arg {
64889701460SNamhyung Kim 	struct ui_browser *b;
64989701460SNamhyung Kim 	char folded_sign;
65089701460SNamhyung Kim 	bool current_entry;
65189701460SNamhyung Kim };
65289701460SNamhyung Kim 
6532f6d9009SNamhyung Kim static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
6542f6d9009SNamhyung Kim {
6552f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
656*d675107cSNamhyung Kim 	int ret, len;
6572f6d9009SNamhyung Kim 	va_list args;
6582f6d9009SNamhyung Kim 	double percent;
6592f6d9009SNamhyung Kim 
6602f6d9009SNamhyung Kim 	va_start(args, fmt);
661*d675107cSNamhyung Kim 	len = va_arg(args, int);
6622f6d9009SNamhyung Kim 	percent = va_arg(args, double);
6632f6d9009SNamhyung Kim 	va_end(args);
6645aed9d24SNamhyung Kim 
66589701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
6665aed9d24SNamhyung Kim 
667*d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
66889701460SNamhyung Kim 	slsmg_printf("%s", hpp->buf);
66989701460SNamhyung Kim 
6702f6d9009SNamhyung Kim 	advance_hpp(hpp, ret);
6715aed9d24SNamhyung Kim 	return ret;
672f5951d56SNamhyung Kim }
673f5951d56SNamhyung Kim 
674fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
6755aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
6765aed9d24SNamhyung Kim {									\
6775aed9d24SNamhyung Kim 	return he->stat._field;						\
6785aed9d24SNamhyung Kim }									\
6795aed9d24SNamhyung Kim 									\
6802c5d4b4aSJiri Olsa static int								\
6812c5d4b4aSJiri Olsa hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
6822c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
6835aed9d24SNamhyung Kim 				struct hist_entry *he)			\
6845aed9d24SNamhyung Kim {									\
685*d675107cSNamhyung Kim 	return __hpp__fmt(hpp, he, __hpp_get_##_field, " %*.2f%%", 6,	\
6862f6d9009SNamhyung Kim 			  __hpp__slsmg_color_printf, true);		\
6875aed9d24SNamhyung Kim }
688f5951d56SNamhyung Kim 
6890434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
6900434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
6910434ddd2SNamhyung Kim {									\
6920434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
6930434ddd2SNamhyung Kim }									\
6940434ddd2SNamhyung Kim 									\
6950434ddd2SNamhyung Kim static int								\
6960434ddd2SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
6970434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
6980434ddd2SNamhyung Kim 				struct hist_entry *he)			\
6990434ddd2SNamhyung Kim {									\
7000434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
701*d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
702*d675107cSNamhyung Kim 				    "%*s", 8, "N/A");			\
7030434ddd2SNamhyung Kim 		slsmg_printf("%s", hpp->buf);				\
7040434ddd2SNamhyung Kim 									\
7050434ddd2SNamhyung Kim 		return ret;						\
7060434ddd2SNamhyung Kim 	}								\
707*d675107cSNamhyung Kim 	return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %*.2f%%",	\
708*d675107cSNamhyung Kim 			  6, __hpp__slsmg_color_printf, true);	\
7090434ddd2SNamhyung Kim }
7100434ddd2SNamhyung Kim 
711fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
712fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
713fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
714fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
715fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
7160434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
7175aed9d24SNamhyung Kim 
7185aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
7190434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
720f5951d56SNamhyung Kim 
721f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
722f5951d56SNamhyung Kim {
723f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
724f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
725f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
726f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
727f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
728f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
729f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
730f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
731f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
732f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
7330434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
7340434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
735f5951d56SNamhyung Kim }
736f5951d56SNamhyung Kim 
73705e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
738aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
739aca7a94dSNamhyung Kim 				    unsigned short row)
740aca7a94dSNamhyung Kim {
741aca7a94dSNamhyung Kim 	char s[256];
7421240005eSJiri Olsa 	int printed = 0;
74367d25916SNamhyung Kim 	int width = browser->b.width;
744aca7a94dSNamhyung Kim 	char folded_sign = ' ';
74505e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
746aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
74763a1a3d8SNamhyung Kim 	bool first = true;
7481240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
749aca7a94dSNamhyung Kim 
750aca7a94dSNamhyung Kim 	if (current_entry) {
75105e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
75205e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
753aca7a94dSNamhyung Kim 	}
754aca7a94dSNamhyung Kim 
755aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
756aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
757aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
758aca7a94dSNamhyung Kim 	}
759aca7a94dSNamhyung Kim 
760aca7a94dSNamhyung Kim 	if (row_offset == 0) {
76189701460SNamhyung Kim 		struct hpp_arg arg = {
76289701460SNamhyung Kim 			.b		= &browser->b,
76389701460SNamhyung Kim 			.folded_sign	= folded_sign,
76489701460SNamhyung Kim 			.current_entry	= current_entry,
76589701460SNamhyung Kim 		};
766f5951d56SNamhyung Kim 		struct perf_hpp hpp = {
767f5951d56SNamhyung Kim 			.buf		= s,
768f5951d56SNamhyung Kim 			.size		= sizeof(s),
76989701460SNamhyung Kim 			.ptr		= &arg,
770f5951d56SNamhyung Kim 		};
771f5951d56SNamhyung Kim 
772ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
773f5951d56SNamhyung Kim 
7741240005eSJiri Olsa 		perf_hpp__for_each_format(fmt) {
775e67d49a7SNamhyung Kim 			if (perf_hpp__should_skip(fmt))
776e67d49a7SNamhyung Kim 				continue;
777e67d49a7SNamhyung Kim 
778fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
779fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
780fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
781fb821c9eSNamhyung Kim 			} else {
782fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
783fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
784fb821c9eSNamhyung Kim 			}
785fb821c9eSNamhyung Kim 
786fb821c9eSNamhyung Kim 			if (first) {
787fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
788fb821c9eSNamhyung Kim 					slsmg_printf("%c ", folded_sign);
789f5951d56SNamhyung Kim 					width -= 2;
790f5951d56SNamhyung Kim 				}
79163a1a3d8SNamhyung Kim 				first = false;
792fb821c9eSNamhyung Kim 			} else {
793fb821c9eSNamhyung Kim 				slsmg_printf("  ");
794fb821c9eSNamhyung Kim 				width -= 2;
795fb821c9eSNamhyung Kim 			}
796f5951d56SNamhyung Kim 
7971240005eSJiri Olsa 			if (fmt->color) {
7982c5d4b4aSJiri Olsa 				width -= fmt->color(fmt, &hpp, entry);
799f5951d56SNamhyung Kim 			} else {
8002c5d4b4aSJiri Olsa 				width -= fmt->entry(fmt, &hpp, entry);
801f5951d56SNamhyung Kim 				slsmg_printf("%s", s);
802f5951d56SNamhyung Kim 			}
803f5951d56SNamhyung Kim 		}
804aca7a94dSNamhyung Kim 
805aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
80605e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
807aca7a94dSNamhyung Kim 			width += 1;
808aca7a94dSNamhyung Kim 
80926d8b338SNamhyung Kim 		slsmg_write_nstring("", width);
81026d8b338SNamhyung Kim 
811aca7a94dSNamhyung Kim 		++row;
812aca7a94dSNamhyung Kim 		++printed;
813aca7a94dSNamhyung Kim 	} else
814aca7a94dSNamhyung Kim 		--row_offset;
815aca7a94dSNamhyung Kim 
81662c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
81705e8b080SArnaldo Carvalho de Melo 		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
818aca7a94dSNamhyung Kim 							1, row, &row_offset,
819aca7a94dSNamhyung Kim 							&current_entry);
820aca7a94dSNamhyung Kim 		if (current_entry)
82105e8b080SArnaldo Carvalho de Melo 			browser->he_selection = entry;
822aca7a94dSNamhyung Kim 	}
823aca7a94dSNamhyung Kim 
824aca7a94dSNamhyung Kim 	return printed;
825aca7a94dSNamhyung Kim }
826aca7a94dSNamhyung Kim 
82781a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
82881a888feSJiri Olsa {
82981a888feSJiri Olsa 	advance_hpp(hpp, inc);
83081a888feSJiri Olsa 	return hpp->size <= 0;
83181a888feSJiri Olsa }
83281a888feSJiri Olsa 
83381a888feSJiri Olsa static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
83481a888feSJiri Olsa {
83581a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
83681a888feSJiri Olsa 		.buf    = buf,
83781a888feSJiri Olsa 		.size   = size,
83881a888feSJiri Olsa 	};
83981a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
84081a888feSJiri Olsa 	size_t ret = 0;
84181a888feSJiri Olsa 
84281a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
84381a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
84481a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
84581a888feSJiri Olsa 			return ret;
84681a888feSJiri Olsa 	}
84781a888feSJiri Olsa 
84881a888feSJiri Olsa 	perf_hpp__for_each_format(fmt) {
84981a888feSJiri Olsa 		if (perf_hpp__should_skip(fmt))
85081a888feSJiri Olsa 			continue;
85181a888feSJiri Olsa 
85281a888feSJiri Olsa 		/* We need to add the length of the columns header. */
85381a888feSJiri Olsa 		perf_hpp__reset_width(fmt, hists);
85481a888feSJiri Olsa 
85581a888feSJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
85681a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
85781a888feSJiri Olsa 			break;
85881a888feSJiri Olsa 
85981a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
86081a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
86181a888feSJiri Olsa 			break;
86281a888feSJiri Olsa 	}
86381a888feSJiri Olsa 
86481a888feSJiri Olsa 	return ret;
86581a888feSJiri Olsa }
86681a888feSJiri Olsa 
867025bf7eaSArnaldo Carvalho de Melo static void hist_browser__show_headers(struct hist_browser *browser)
868025bf7eaSArnaldo Carvalho de Melo {
86981a888feSJiri Olsa 	char headers[1024];
87081a888feSJiri Olsa 
87181a888feSJiri Olsa 	hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
872025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
873025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
87481a888feSJiri Olsa 	slsmg_write_nstring(headers, browser->b.width + 1);
875025bf7eaSArnaldo Carvalho de Melo }
876025bf7eaSArnaldo Carvalho de Melo 
877aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
878aca7a94dSNamhyung Kim {
879aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
880aca7a94dSNamhyung Kim 		struct hist_browser *hb;
881aca7a94dSNamhyung Kim 
882aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
883aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
884aca7a94dSNamhyung Kim 	}
885aca7a94dSNamhyung Kim }
886aca7a94dSNamhyung Kim 
88705e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
888aca7a94dSNamhyung Kim {
889aca7a94dSNamhyung Kim 	unsigned row = 0;
890025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
891aca7a94dSNamhyung Kim 	struct rb_node *nd;
89205e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
893aca7a94dSNamhyung Kim 
894025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
895025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
896025bf7eaSArnaldo Carvalho de Melo 		header_offset = 1;
897025bf7eaSArnaldo Carvalho de Melo 	}
898025bf7eaSArnaldo Carvalho de Melo 
89905e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
900aca7a94dSNamhyung Kim 
90105e8b080SArnaldo Carvalho de Melo 	for (nd = browser->top; nd; nd = rb_next(nd)) {
902aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
90314135663SNamhyung Kim 		float percent;
904aca7a94dSNamhyung Kim 
905aca7a94dSNamhyung Kim 		if (h->filtered)
906aca7a94dSNamhyung Kim 			continue;
907aca7a94dSNamhyung Kim 
90814135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
909064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
910064f1981SNamhyung Kim 			continue;
911064f1981SNamhyung Kim 
912aca7a94dSNamhyung Kim 		row += hist_browser__show_entry(hb, h, row);
91362c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
914aca7a94dSNamhyung Kim 			break;
915aca7a94dSNamhyung Kim 	}
916aca7a94dSNamhyung Kim 
917025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
918aca7a94dSNamhyung Kim }
919aca7a94dSNamhyung Kim 
920064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
921064f1981SNamhyung Kim 					     float min_pcnt)
922aca7a94dSNamhyung Kim {
923aca7a94dSNamhyung Kim 	while (nd != NULL) {
924aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
92514135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
926064f1981SNamhyung Kim 
927c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
928aca7a94dSNamhyung Kim 			return nd;
929aca7a94dSNamhyung Kim 
930aca7a94dSNamhyung Kim 		nd = rb_next(nd);
931aca7a94dSNamhyung Kim 	}
932aca7a94dSNamhyung Kim 
933aca7a94dSNamhyung Kim 	return NULL;
934aca7a94dSNamhyung Kim }
935aca7a94dSNamhyung Kim 
936064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
937064f1981SNamhyung Kim 						  float min_pcnt)
938aca7a94dSNamhyung Kim {
939aca7a94dSNamhyung Kim 	while (nd != NULL) {
940aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
94114135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
942064f1981SNamhyung Kim 
943064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
944aca7a94dSNamhyung Kim 			return nd;
945aca7a94dSNamhyung Kim 
946aca7a94dSNamhyung Kim 		nd = rb_prev(nd);
947aca7a94dSNamhyung Kim 	}
948aca7a94dSNamhyung Kim 
949aca7a94dSNamhyung Kim 	return NULL;
950aca7a94dSNamhyung Kim }
951aca7a94dSNamhyung Kim 
95205e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
953aca7a94dSNamhyung Kim 				   off_t offset, int whence)
954aca7a94dSNamhyung Kim {
955aca7a94dSNamhyung Kim 	struct hist_entry *h;
956aca7a94dSNamhyung Kim 	struct rb_node *nd;
957aca7a94dSNamhyung Kim 	bool first = true;
958064f1981SNamhyung Kim 	struct hist_browser *hb;
959064f1981SNamhyung Kim 
960064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
961aca7a94dSNamhyung Kim 
96205e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
963aca7a94dSNamhyung Kim 		return;
964aca7a94dSNamhyung Kim 
96505e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
966aca7a94dSNamhyung Kim 
967aca7a94dSNamhyung Kim 	switch (whence) {
968aca7a94dSNamhyung Kim 	case SEEK_SET:
969064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
97014135663SNamhyung Kim 					   hb->min_pcnt);
971aca7a94dSNamhyung Kim 		break;
972aca7a94dSNamhyung Kim 	case SEEK_CUR:
97305e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
974aca7a94dSNamhyung Kim 		goto do_offset;
975aca7a94dSNamhyung Kim 	case SEEK_END:
976064f1981SNamhyung Kim 		nd = hists__filter_prev_entries(rb_last(browser->entries),
97714135663SNamhyung Kim 						hb->min_pcnt);
978aca7a94dSNamhyung Kim 		first = false;
979aca7a94dSNamhyung Kim 		break;
980aca7a94dSNamhyung Kim 	default:
981aca7a94dSNamhyung Kim 		return;
982aca7a94dSNamhyung Kim 	}
983aca7a94dSNamhyung Kim 
984aca7a94dSNamhyung Kim 	/*
985aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
986aca7a94dSNamhyung Kim 	 * row_offset:
987aca7a94dSNamhyung Kim 	 */
98805e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
989aca7a94dSNamhyung Kim 	h->row_offset = 0;
990aca7a94dSNamhyung Kim 
991aca7a94dSNamhyung Kim 	/*
992aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
993aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
994aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
995aca7a94dSNamhyung Kim 	 *
996aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
997aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
998aca7a94dSNamhyung Kim 	 *
999aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1000aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1001aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1002aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1003aca7a94dSNamhyung Kim 	 */
1004aca7a94dSNamhyung Kim do_offset:
1005aca7a94dSNamhyung Kim 	if (offset > 0) {
1006aca7a94dSNamhyung Kim 		do {
1007aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1008aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
1009aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1010aca7a94dSNamhyung Kim 				if (offset > remaining) {
1011aca7a94dSNamhyung Kim 					offset -= remaining;
1012aca7a94dSNamhyung Kim 					h->row_offset = 0;
1013aca7a94dSNamhyung Kim 				} else {
1014aca7a94dSNamhyung Kim 					h->row_offset += offset;
1015aca7a94dSNamhyung Kim 					offset = 0;
101605e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1017aca7a94dSNamhyung Kim 					break;
1018aca7a94dSNamhyung Kim 				}
1019aca7a94dSNamhyung Kim 			}
102014135663SNamhyung Kim 			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1021aca7a94dSNamhyung Kim 			if (nd == NULL)
1022aca7a94dSNamhyung Kim 				break;
1023aca7a94dSNamhyung Kim 			--offset;
102405e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1025aca7a94dSNamhyung Kim 		} while (offset != 0);
1026aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1027aca7a94dSNamhyung Kim 		while (1) {
1028aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1029aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
1030aca7a94dSNamhyung Kim 				if (first) {
1031aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1032aca7a94dSNamhyung Kim 						offset += h->row_offset;
1033aca7a94dSNamhyung Kim 						h->row_offset = 0;
1034aca7a94dSNamhyung Kim 					} else {
1035aca7a94dSNamhyung Kim 						h->row_offset += offset;
1036aca7a94dSNamhyung Kim 						offset = 0;
103705e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1038aca7a94dSNamhyung Kim 						break;
1039aca7a94dSNamhyung Kim 					}
1040aca7a94dSNamhyung Kim 				} else {
1041aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1042aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1043aca7a94dSNamhyung Kim 						h->row_offset = 0;
1044aca7a94dSNamhyung Kim 					} else {
1045aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1046aca7a94dSNamhyung Kim 						offset = 0;
104705e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1048aca7a94dSNamhyung Kim 						break;
1049aca7a94dSNamhyung Kim 					}
1050aca7a94dSNamhyung Kim 				}
1051aca7a94dSNamhyung Kim 			}
1052aca7a94dSNamhyung Kim 
105314135663SNamhyung Kim 			nd = hists__filter_prev_entries(rb_prev(nd),
1054064f1981SNamhyung Kim 							hb->min_pcnt);
1055aca7a94dSNamhyung Kim 			if (nd == NULL)
1056aca7a94dSNamhyung Kim 				break;
1057aca7a94dSNamhyung Kim 			++offset;
105805e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1059aca7a94dSNamhyung Kim 			if (offset == 0) {
1060aca7a94dSNamhyung Kim 				/*
1061aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1062aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1063aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1064aca7a94dSNamhyung Kim 				 */
1065aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
1066aca7a94dSNamhyung Kim 				if (h->ms.unfolded)
1067aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1068aca7a94dSNamhyung Kim 				break;
1069aca7a94dSNamhyung Kim 			}
1070aca7a94dSNamhyung Kim 			first = false;
1071aca7a94dSNamhyung Kim 		}
1072aca7a94dSNamhyung Kim 	} else {
107305e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1074aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1075aca7a94dSNamhyung Kim 		h->row_offset = 0;
1076aca7a94dSNamhyung Kim 	}
1077aca7a94dSNamhyung Kim }
1078aca7a94dSNamhyung Kim 
1079aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
1080aff3f3f6SArnaldo Carvalho de Melo 							struct callchain_node *chain_node,
1081aff3f3f6SArnaldo Carvalho de Melo 							u64 total, int level,
1082aff3f3f6SArnaldo Carvalho de Melo 							FILE *fp)
1083aff3f3f6SArnaldo Carvalho de Melo {
1084aff3f3f6SArnaldo Carvalho de Melo 	struct rb_node *node;
1085aff3f3f6SArnaldo Carvalho de Melo 	int offset = level * LEVEL_OFFSET_STEP;
1086aff3f3f6SArnaldo Carvalho de Melo 	u64 new_total, remaining;
1087aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1088aff3f3f6SArnaldo Carvalho de Melo 
1089aff3f3f6SArnaldo Carvalho de Melo 	if (callchain_param.mode == CHAIN_GRAPH_REL)
1090aff3f3f6SArnaldo Carvalho de Melo 		new_total = chain_node->children_hit;
1091aff3f3f6SArnaldo Carvalho de Melo 	else
1092aff3f3f6SArnaldo Carvalho de Melo 		new_total = total;
1093aff3f3f6SArnaldo Carvalho de Melo 
1094aff3f3f6SArnaldo Carvalho de Melo 	remaining = new_total;
1095aff3f3f6SArnaldo Carvalho de Melo 	node = rb_first(&chain_node->rb_root);
1096aff3f3f6SArnaldo Carvalho de Melo 	while (node) {
1097aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1098aff3f3f6SArnaldo Carvalho de Melo 		struct rb_node *next = rb_next(node);
1099aff3f3f6SArnaldo Carvalho de Melo 		u64 cumul = callchain_cumul_hits(child);
1100aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_list *chain;
1101aff3f3f6SArnaldo Carvalho de Melo 		char folded_sign = ' ';
1102aff3f3f6SArnaldo Carvalho de Melo 		int first = true;
1103aff3f3f6SArnaldo Carvalho de Melo 		int extra_offset = 0;
1104aff3f3f6SArnaldo Carvalho de Melo 
1105aff3f3f6SArnaldo Carvalho de Melo 		remaining -= cumul;
1106aff3f3f6SArnaldo Carvalho de Melo 
1107aff3f3f6SArnaldo Carvalho de Melo 		list_for_each_entry(chain, &child->val, list) {
1108a7cb8863SArnaldo Carvalho de Melo 			char bf[1024], *alloc_str;
1109aff3f3f6SArnaldo Carvalho de Melo 			const char *str;
1110aff3f3f6SArnaldo Carvalho de Melo 			bool was_first = first;
1111aff3f3f6SArnaldo Carvalho de Melo 
1112aff3f3f6SArnaldo Carvalho de Melo 			if (first)
1113aff3f3f6SArnaldo Carvalho de Melo 				first = false;
1114aff3f3f6SArnaldo Carvalho de Melo 			else
1115aff3f3f6SArnaldo Carvalho de Melo 				extra_offset = LEVEL_OFFSET_STEP;
1116aff3f3f6SArnaldo Carvalho de Melo 
1117aff3f3f6SArnaldo Carvalho de Melo 			folded_sign = callchain_list__folded(chain);
1118aff3f3f6SArnaldo Carvalho de Melo 
1119aff3f3f6SArnaldo Carvalho de Melo 			alloc_str = NULL;
1120a7cb8863SArnaldo Carvalho de Melo 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
1121a7cb8863SArnaldo Carvalho de Melo 						       browser->show_dso);
1122aff3f3f6SArnaldo Carvalho de Melo 			if (was_first) {
1123aff3f3f6SArnaldo Carvalho de Melo 				double percent = cumul * 100.0 / new_total;
1124aff3f3f6SArnaldo Carvalho de Melo 
1125aff3f3f6SArnaldo Carvalho de Melo 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1126aff3f3f6SArnaldo Carvalho de Melo 					str = "Not enough memory!";
1127aff3f3f6SArnaldo Carvalho de Melo 				else
1128aff3f3f6SArnaldo Carvalho de Melo 					str = alloc_str;
1129aff3f3f6SArnaldo Carvalho de Melo 			}
1130aff3f3f6SArnaldo Carvalho de Melo 
1131aff3f3f6SArnaldo Carvalho de Melo 			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1132aff3f3f6SArnaldo Carvalho de Melo 			free(alloc_str);
1133aff3f3f6SArnaldo Carvalho de Melo 			if (folded_sign == '+')
1134aff3f3f6SArnaldo Carvalho de Melo 				break;
1135aff3f3f6SArnaldo Carvalho de Melo 		}
1136aff3f3f6SArnaldo Carvalho de Melo 
1137aff3f3f6SArnaldo Carvalho de Melo 		if (folded_sign == '-') {
1138aff3f3f6SArnaldo Carvalho de Melo 			const int new_level = level + (extra_offset ? 2 : 1);
1139aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1140aff3f3f6SArnaldo Carvalho de Melo 										new_level, fp);
1141aff3f3f6SArnaldo Carvalho de Melo 		}
1142aff3f3f6SArnaldo Carvalho de Melo 
1143aff3f3f6SArnaldo Carvalho de Melo 		node = next;
1144aff3f3f6SArnaldo Carvalho de Melo 	}
1145aff3f3f6SArnaldo Carvalho de Melo 
1146aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1147aff3f3f6SArnaldo Carvalho de Melo }
1148aff3f3f6SArnaldo Carvalho de Melo 
1149aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1150aff3f3f6SArnaldo Carvalho de Melo 						struct callchain_node *node,
1151aff3f3f6SArnaldo Carvalho de Melo 						int level, FILE *fp)
1152aff3f3f6SArnaldo Carvalho de Melo {
1153aff3f3f6SArnaldo Carvalho de Melo 	struct callchain_list *chain;
1154aff3f3f6SArnaldo Carvalho de Melo 	int offset = level * LEVEL_OFFSET_STEP;
1155aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
1156aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1157aff3f3f6SArnaldo Carvalho de Melo 
1158aff3f3f6SArnaldo Carvalho de Melo 	list_for_each_entry(chain, &node->val, list) {
1159a7cb8863SArnaldo Carvalho de Melo 		char bf[1024], *s;
1160aff3f3f6SArnaldo Carvalho de Melo 
1161aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = callchain_list__folded(chain);
1162a7cb8863SArnaldo Carvalho de Melo 		s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1163aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1164aff3f3f6SArnaldo Carvalho de Melo 	}
1165aff3f3f6SArnaldo Carvalho de Melo 
1166aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1167aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1168aff3f3f6SArnaldo Carvalho de Melo 									browser->hists->stats.total_period,
1169aff3f3f6SArnaldo Carvalho de Melo 									level + 1,  fp);
1170aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1171aff3f3f6SArnaldo Carvalho de Melo }
1172aff3f3f6SArnaldo Carvalho de Melo 
1173aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1174aff3f3f6SArnaldo Carvalho de Melo 					   struct rb_root *chain, int level, FILE *fp)
1175aff3f3f6SArnaldo Carvalho de Melo {
1176aff3f3f6SArnaldo Carvalho de Melo 	struct rb_node *nd;
1177aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1178aff3f3f6SArnaldo Carvalho de Melo 
1179aff3f3f6SArnaldo Carvalho de Melo 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1180aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1181aff3f3f6SArnaldo Carvalho de Melo 
1182aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1183aff3f3f6SArnaldo Carvalho de Melo 	}
1184aff3f3f6SArnaldo Carvalho de Melo 
1185aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1186aff3f3f6SArnaldo Carvalho de Melo }
1187aff3f3f6SArnaldo Carvalho de Melo 
1188aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1189aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1190aff3f3f6SArnaldo Carvalho de Melo {
1191aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1192aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1193aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
119426d8b338SNamhyung Kim 	struct perf_hpp hpp = {
119526d8b338SNamhyung Kim 		.buf = s,
119626d8b338SNamhyung Kim 		.size = sizeof(s),
119726d8b338SNamhyung Kim 	};
119826d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
119926d8b338SNamhyung Kim 	bool first = true;
120026d8b338SNamhyung Kim 	int ret;
1201aff3f3f6SArnaldo Carvalho de Melo 
1202aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1203aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1204aff3f3f6SArnaldo Carvalho de Melo 
1205aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1206aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
1207aff3f3f6SArnaldo Carvalho de Melo 
120826d8b338SNamhyung Kim 	perf_hpp__for_each_format(fmt) {
1209e67d49a7SNamhyung Kim 		if (perf_hpp__should_skip(fmt))
1210e67d49a7SNamhyung Kim 			continue;
1211e67d49a7SNamhyung Kim 
121226d8b338SNamhyung Kim 		if (!first) {
121326d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
121426d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
121526d8b338SNamhyung Kim 		} else
121626d8b338SNamhyung Kim 			first = false;
1217aff3f3f6SArnaldo Carvalho de Melo 
121826d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
121926d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
122026d8b338SNamhyung Kim 	}
1221aff3f3f6SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", rtrim(s));
1222aff3f3f6SArnaldo Carvalho de Melo 
1223aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1224aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1225aff3f3f6SArnaldo Carvalho de Melo 
1226aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1227aff3f3f6SArnaldo Carvalho de Melo }
1228aff3f3f6SArnaldo Carvalho de Melo 
1229aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1230aff3f3f6SArnaldo Carvalho de Melo {
1231064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1232064f1981SNamhyung Kim 						   browser->min_pcnt);
1233aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1234aff3f3f6SArnaldo Carvalho de Melo 
1235aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1236aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1237aff3f3f6SArnaldo Carvalho de Melo 
1238aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_entry(browser, h, fp);
123914135663SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1240aff3f3f6SArnaldo Carvalho de Melo 	}
1241aff3f3f6SArnaldo Carvalho de Melo 
1242aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1243aff3f3f6SArnaldo Carvalho de Melo }
1244aff3f3f6SArnaldo Carvalho de Melo 
1245aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
1246aff3f3f6SArnaldo Carvalho de Melo {
1247aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
1248aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
1249aff3f3f6SArnaldo Carvalho de Melo 
1250aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
1251aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1252aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
1253aff3f3f6SArnaldo Carvalho de Melo 			break;
1254aff3f3f6SArnaldo Carvalho de Melo 		/*
1255aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
1256aff3f3f6SArnaldo Carvalho de Melo  		 */
1257aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
1258aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1259aff3f3f6SArnaldo Carvalho de Melo 			return -1;
1260aff3f3f6SArnaldo Carvalho de Melo 		}
1261aff3f3f6SArnaldo Carvalho de Melo 	}
1262aff3f3f6SArnaldo Carvalho de Melo 
1263aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
1264aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
1265aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
12664cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
12674cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1268aff3f3f6SArnaldo Carvalho de Melo 		return -1;
1269aff3f3f6SArnaldo Carvalho de Melo 	}
1270aff3f3f6SArnaldo Carvalho de Melo 
1271aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
1272aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
1273aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
1274aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
1275aff3f3f6SArnaldo Carvalho de Melo 
1276aff3f3f6SArnaldo Carvalho de Melo 	return 0;
1277aff3f3f6SArnaldo Carvalho de Melo }
1278aff3f3f6SArnaldo Carvalho de Melo 
1279aca7a94dSNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists)
1280aca7a94dSNamhyung Kim {
128105e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
1282aca7a94dSNamhyung Kim 
128305e8b080SArnaldo Carvalho de Melo 	if (browser) {
128405e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
128505e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
1286357cfff1SArnaldo Carvalho de Melo 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
128705e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
128805e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
1289c8302367SJiri Olsa 		browser->show_headers = symbol_conf.show_hist_headers;
1290aca7a94dSNamhyung Kim 	}
1291aca7a94dSNamhyung Kim 
129205e8b080SArnaldo Carvalho de Melo 	return browser;
1293aca7a94dSNamhyung Kim }
1294aca7a94dSNamhyung Kim 
129505e8b080SArnaldo Carvalho de Melo static void hist_browser__delete(struct hist_browser *browser)
1296aca7a94dSNamhyung Kim {
129705e8b080SArnaldo Carvalho de Melo 	free(browser);
1298aca7a94dSNamhyung Kim }
1299aca7a94dSNamhyung Kim 
130005e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1301aca7a94dSNamhyung Kim {
130205e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
1303aca7a94dSNamhyung Kim }
1304aca7a94dSNamhyung Kim 
130505e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1306aca7a94dSNamhyung Kim {
130705e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
1308aca7a94dSNamhyung Kim }
1309aca7a94dSNamhyung Kim 
1310dd00d486SJiri Olsa static int hists__browser_title(struct hists *hists, char *bf, size_t size)
1311aca7a94dSNamhyung Kim {
1312aca7a94dSNamhyung Kim 	char unit;
1313aca7a94dSNamhyung Kim 	int printed;
131405e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
131505e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
131605e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
131705e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
1318717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
1319dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
1320717e263fSNamhyung Kim 	char buf[512];
1321717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
1322717e263fSNamhyung Kim 
1323f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
1324f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
1325f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
1326f2148330SNamhyung Kim 	}
1327f2148330SNamhyung Kim 
1328759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1329717e263fSNamhyung Kim 		struct perf_evsel *pos;
1330717e263fSNamhyung Kim 
1331717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
1332717e263fSNamhyung Kim 		ev_name = buf;
1333717e263fSNamhyung Kim 
1334717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
1335f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
1336f2148330SNamhyung Kim 				nr_samples += pos->hists.stats.nr_non_filtered_samples;
1337f2148330SNamhyung Kim 				nr_events += pos->hists.stats.total_non_filtered_period;
1338f2148330SNamhyung Kim 			} else {
1339717e263fSNamhyung Kim 				nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1340717e263fSNamhyung Kim 				nr_events += pos->hists.stats.total_period;
1341717e263fSNamhyung Kim 			}
1342717e263fSNamhyung Kim 		}
1343f2148330SNamhyung Kim 	}
1344aca7a94dSNamhyung Kim 
1345aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
1346aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
1347aca7a94dSNamhyung Kim 			   "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1348aca7a94dSNamhyung Kim 			   nr_samples, unit, ev_name, nr_events);
1349aca7a94dSNamhyung Kim 
1350aca7a94dSNamhyung Kim 
135105e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
1352aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
135305e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
1354aca7a94dSNamhyung Kim 	if (thread)
1355aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1356aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
1357b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
135838051234SAdrian Hunter 				    thread->tid);
1359aca7a94dSNamhyung Kim 	if (dso)
1360aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1361aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
1362aca7a94dSNamhyung Kim 	return printed;
1363aca7a94dSNamhyung Kim }
1364aca7a94dSNamhyung Kim 
1365aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
1366aca7a94dSNamhyung Kim {
1367aca7a94dSNamhyung Kim 	int i;
1368aca7a94dSNamhyung Kim 
136904662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
137004662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
1371aca7a94dSNamhyung Kim }
1372aca7a94dSNamhyung Kim 
1373c77d8d70SFeng Tang /* Check whether the browser is for 'top' or 'report' */
1374c77d8d70SFeng Tang static inline bool is_report_browser(void *timer)
1375c77d8d70SFeng Tang {
1376c77d8d70SFeng Tang 	return timer == NULL;
1377c77d8d70SFeng Tang }
1378c77d8d70SFeng Tang 
1379341487abSFeng Tang /*
1380341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
1381341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1382341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
1383341487abSFeng Tang  */
1384341487abSFeng Tang static bool is_input_name_malloced = false;
1385341487abSFeng Tang 
1386341487abSFeng Tang static int switch_data_file(void)
1387341487abSFeng Tang {
1388341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
1389341487abSFeng Tang 	DIR *pwd_dir;
1390341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
1391341487abSFeng Tang 	struct dirent *dent;
1392341487abSFeng Tang 
1393341487abSFeng Tang 	pwd = getenv("PWD");
1394341487abSFeng Tang 	if (!pwd)
1395341487abSFeng Tang 		return ret;
1396341487abSFeng Tang 
1397341487abSFeng Tang 	pwd_dir = opendir(pwd);
1398341487abSFeng Tang 	if (!pwd_dir)
1399341487abSFeng Tang 		return ret;
1400341487abSFeng Tang 
1401341487abSFeng Tang 	memset(options, 0, sizeof(options));
1402341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
1403341487abSFeng Tang 
1404341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
1405341487abSFeng Tang 		char path[PATH_MAX];
1406341487abSFeng Tang 		u64 magic;
1407341487abSFeng Tang 		char *name = dent->d_name;
1408341487abSFeng Tang 		FILE *file;
1409341487abSFeng Tang 
1410341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
1411341487abSFeng Tang 			continue;
1412341487abSFeng Tang 
1413341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1414341487abSFeng Tang 
1415341487abSFeng Tang 		file = fopen(path, "r");
1416341487abSFeng Tang 		if (!file)
1417341487abSFeng Tang 			continue;
1418341487abSFeng Tang 
1419341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
1420341487abSFeng Tang 			goto close_file_and_continue;
1421341487abSFeng Tang 
1422341487abSFeng Tang 		if (is_perf_magic(magic)) {
1423341487abSFeng Tang 			options[nr_options] = strdup(name);
1424341487abSFeng Tang 			if (!options[nr_options])
1425341487abSFeng Tang 				goto close_file_and_continue;
1426341487abSFeng Tang 
1427341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
1428341487abSFeng Tang 			if (!abs_path[nr_options]) {
142974cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
1430341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
1431341487abSFeng Tang 				fclose(file);
1432341487abSFeng Tang 				break;
1433341487abSFeng Tang 			}
1434341487abSFeng Tang 
1435341487abSFeng Tang 			nr_options++;
1436341487abSFeng Tang 		}
1437341487abSFeng Tang 
1438341487abSFeng Tang close_file_and_continue:
1439341487abSFeng Tang 		fclose(file);
1440341487abSFeng Tang 		if (nr_options >= 32) {
1441341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
1442341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
1443341487abSFeng Tang 			break;
1444341487abSFeng Tang 		}
1445341487abSFeng Tang 	}
1446341487abSFeng Tang 	closedir(pwd_dir);
1447341487abSFeng Tang 
1448341487abSFeng Tang 	if (nr_options) {
1449341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
1450341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
1451341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
1452341487abSFeng Tang 			if (tmp) {
1453341487abSFeng Tang 				if (is_input_name_malloced)
1454341487abSFeng Tang 					free((void *)input_name);
1455341487abSFeng Tang 				input_name = tmp;
1456341487abSFeng Tang 				is_input_name_malloced = true;
1457341487abSFeng Tang 				ret = 0;
1458341487abSFeng Tang 			} else
1459341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
1460341487abSFeng Tang 		}
1461341487abSFeng Tang 	}
1462341487abSFeng Tang 
1463341487abSFeng Tang 	free_popup_options(options, nr_options);
1464341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
1465341487abSFeng Tang 	return ret;
1466341487abSFeng Tang }
1467341487abSFeng Tang 
1468112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
1469064f1981SNamhyung Kim {
1470064f1981SNamhyung Kim 	u64 nr_entries = 0;
1471064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
1472064f1981SNamhyung Kim 
1473268397cbSNamhyung Kim 	if (hb->min_pcnt == 0) {
1474268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1475268397cbSNamhyung Kim 		return;
1476268397cbSNamhyung Kim 	}
1477268397cbSNamhyung Kim 
147814135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1479064f1981SNamhyung Kim 		nr_entries++;
1480c481f930SNamhyung Kim 		nd = rb_next(nd);
1481064f1981SNamhyung Kim 	}
1482064f1981SNamhyung Kim 
1483112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
1484064f1981SNamhyung Kim }
1485341487abSFeng Tang 
1486aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1487dd00d486SJiri Olsa 				    const char *helpline,
1488aca7a94dSNamhyung Kim 				    bool left_exits,
148968d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
1490064f1981SNamhyung Kim 				    float min_pcnt,
149168d80758SNamhyung Kim 				    struct perf_session_env *env)
1492aca7a94dSNamhyung Kim {
149305e8b080SArnaldo Carvalho de Melo 	struct hists *hists = &evsel->hists;
149405e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = hist_browser__new(hists);
1495aca7a94dSNamhyung Kim 	struct branch_info *bi;
1496aca7a94dSNamhyung Kim 	struct pstack *fstack;
1497aca7a94dSNamhyung Kim 	char *options[16];
1498aca7a94dSNamhyung Kim 	int nr_options = 0;
1499aca7a94dSNamhyung Kim 	int key = -1;
1500aca7a94dSNamhyung Kim 	char buf[64];
1501cdbab7c2SFeng Tang 	char script_opt[64];
15029783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
1503aca7a94dSNamhyung Kim 
1504e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
1505e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
1506e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
1507e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
1508e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1509e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
1510e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
1511e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
1512e8e684a5SNamhyung Kim 	"->            Zoom into DSO/Threads & Annotate current symbol\n" \
1513e8e684a5SNamhyung Kim 	"<-            Zoom out\n"					\
1514e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
1515e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
1516e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
1517e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
1518105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
1519025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
1520e8e684a5SNamhyung Kim 
1521e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
1522e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
15236dd60135SNamhyung Kim 	"i             Show header information\n"
1524e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1525e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
1526e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
1527e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1528e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
1529e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1530e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1531e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1532e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1533e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
1534e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1535e8e684a5SNamhyung Kim 
1536aca7a94dSNamhyung Kim 	if (browser == NULL)
1537aca7a94dSNamhyung Kim 		return -1;
1538aca7a94dSNamhyung Kim 
1539064f1981SNamhyung Kim 	if (min_pcnt) {
1540064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
1541112f761fSNamhyung Kim 		hist_browser__update_nr_entries(browser);
1542064f1981SNamhyung Kim 	}
1543064f1981SNamhyung Kim 
1544aca7a94dSNamhyung Kim 	fstack = pstack__new(2);
1545aca7a94dSNamhyung Kim 	if (fstack == NULL)
1546aca7a94dSNamhyung Kim 		goto out;
1547aca7a94dSNamhyung Kim 
1548aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
1549aca7a94dSNamhyung Kim 
1550aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
1551aca7a94dSNamhyung Kim 
1552aca7a94dSNamhyung Kim 	while (1) {
1553aca7a94dSNamhyung Kim 		const struct thread *thread = NULL;
1554aca7a94dSNamhyung Kim 		const struct dso *dso = NULL;
1555aca7a94dSNamhyung Kim 		int choice = 0,
1556aca7a94dSNamhyung Kim 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1557aca7a94dSNamhyung Kim 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1558341487abSFeng Tang 		int scripts_comm = -2, scripts_symbol = -2,
1559341487abSFeng Tang 		    scripts_all = -2, switch_data = -2;
1560aca7a94dSNamhyung Kim 
1561aca7a94dSNamhyung Kim 		nr_options = 0;
1562aca7a94dSNamhyung Kim 
1563dd00d486SJiri Olsa 		key = hist_browser__run(browser, hbt);
1564aca7a94dSNamhyung Kim 
1565aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
1566aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
1567aca7a94dSNamhyung Kim 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1568aca7a94dSNamhyung Kim 		}
1569aca7a94dSNamhyung Kim 		switch (key) {
1570aca7a94dSNamhyung Kim 		case K_TAB:
1571aca7a94dSNamhyung Kim 		case K_UNTAB:
1572aca7a94dSNamhyung Kim 			if (nr_events == 1)
1573aca7a94dSNamhyung Kim 				continue;
1574aca7a94dSNamhyung Kim 			/*
1575aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
1576aca7a94dSNamhyung Kim 			 * go to the next or previous
1577aca7a94dSNamhyung Kim 			 */
1578aca7a94dSNamhyung Kim 			goto out_free_stack;
1579aca7a94dSNamhyung Kim 		case 'a':
15809c796ec8SArnaldo Carvalho de Melo 			if (!sort__has_sym) {
1581aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
1582aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
1583aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
1584aca7a94dSNamhyung Kim 				continue;
1585aca7a94dSNamhyung Kim 			}
1586aca7a94dSNamhyung Kim 
1587aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
1588aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
1589aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
1590aca7a94dSNamhyung Kim 				continue;
1591aca7a94dSNamhyung Kim 			goto do_annotate;
1592aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
1593aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
1594aff3f3f6SArnaldo Carvalho de Melo 			continue;
1595aca7a94dSNamhyung Kim 		case 'd':
1596aca7a94dSNamhyung Kim 			goto zoom_dso;
1597a7cb8863SArnaldo Carvalho de Melo 		case 'V':
1598a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
1599a7cb8863SArnaldo Carvalho de Melo 			continue;
1600aca7a94dSNamhyung Kim 		case 't':
1601aca7a94dSNamhyung Kim 			goto zoom_thread;
16025a5626b1SArnaldo Carvalho de Melo 		case '/':
1603aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
1604aca7a94dSNamhyung Kim 					"Please enter the name of symbol you want to see",
1605aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
1606aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
160705e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
160805e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
1609aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
1610aca7a94dSNamhyung Kim 			}
1611aca7a94dSNamhyung Kim 			continue;
1612cdbab7c2SFeng Tang 		case 'r':
16139783adf7SNamhyung Kim 			if (is_report_browser(hbt))
1614cdbab7c2SFeng Tang 				goto do_scripts;
1615c77d8d70SFeng Tang 			continue;
1616341487abSFeng Tang 		case 's':
1617341487abSFeng Tang 			if (is_report_browser(hbt))
1618341487abSFeng Tang 				goto do_data_switch;
1619341487abSFeng Tang 			continue;
16206dd60135SNamhyung Kim 		case 'i':
16216dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
16226dd60135SNamhyung Kim 			if (env->arch)
16236dd60135SNamhyung Kim 				tui__header_window(env);
16246dd60135SNamhyung Kim 			continue;
1625105eb30fSNamhyung Kim 		case 'F':
1626105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
1627105eb30fSNamhyung Kim 			continue;
1628aca7a94dSNamhyung Kim 		case K_F1:
1629aca7a94dSNamhyung Kim 		case 'h':
1630aca7a94dSNamhyung Kim 		case '?':
1631aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
1632e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
1633aca7a94dSNamhyung Kim 			continue;
1634aca7a94dSNamhyung Kim 		case K_ENTER:
1635aca7a94dSNamhyung Kim 		case K_RIGHT:
1636aca7a94dSNamhyung Kim 			/* menu */
1637aca7a94dSNamhyung Kim 			break;
1638aca7a94dSNamhyung Kim 		case K_LEFT: {
1639aca7a94dSNamhyung Kim 			const void *top;
1640aca7a94dSNamhyung Kim 
1641aca7a94dSNamhyung Kim 			if (pstack__empty(fstack)) {
1642aca7a94dSNamhyung Kim 				/*
1643aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
1644aca7a94dSNamhyung Kim 				 */
1645aca7a94dSNamhyung Kim 				if (left_exits)
1646aca7a94dSNamhyung Kim 					goto out_free_stack;
1647aca7a94dSNamhyung Kim 				continue;
1648aca7a94dSNamhyung Kim 			}
1649aca7a94dSNamhyung Kim 			top = pstack__pop(fstack);
1650aca7a94dSNamhyung Kim 			if (top == &browser->hists->dso_filter)
1651aca7a94dSNamhyung Kim 				goto zoom_out_dso;
1652aca7a94dSNamhyung Kim 			if (top == &browser->hists->thread_filter)
1653aca7a94dSNamhyung Kim 				goto zoom_out_thread;
1654aca7a94dSNamhyung Kim 			continue;
1655aca7a94dSNamhyung Kim 		}
1656aca7a94dSNamhyung Kim 		case K_ESC:
1657aca7a94dSNamhyung Kim 			if (!left_exits &&
1658aca7a94dSNamhyung Kim 			    !ui_browser__dialog_yesno(&browser->b,
1659aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1660aca7a94dSNamhyung Kim 				continue;
1661aca7a94dSNamhyung Kim 			/* Fall thru */
1662aca7a94dSNamhyung Kim 		case 'q':
1663aca7a94dSNamhyung Kim 		case CTRL('c'):
1664aca7a94dSNamhyung Kim 			goto out_free_stack;
1665aca7a94dSNamhyung Kim 		default:
1666aca7a94dSNamhyung Kim 			continue;
1667aca7a94dSNamhyung Kim 		}
1668aca7a94dSNamhyung Kim 
16699c796ec8SArnaldo Carvalho de Melo 		if (!sort__has_sym)
1670aca7a94dSNamhyung Kim 			goto add_exit_option;
1671aca7a94dSNamhyung Kim 
167255369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
1673aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
1674aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1675aca7a94dSNamhyung Kim 			    bi &&
1676aca7a94dSNamhyung Kim 			    bi->from.sym != NULL &&
1677aca7a94dSNamhyung Kim 			    !bi->from.map->dso->annotate_warned &&
1678aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1679aca7a94dSNamhyung Kim 					 bi->from.sym->name) > 0)
1680aca7a94dSNamhyung Kim 				annotate_f = nr_options++;
1681aca7a94dSNamhyung Kim 
1682aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1683aca7a94dSNamhyung Kim 			    bi &&
1684aca7a94dSNamhyung Kim 			    bi->to.sym != NULL &&
1685aca7a94dSNamhyung Kim 			    !bi->to.map->dso->annotate_warned &&
1686aca7a94dSNamhyung Kim 			    (bi->to.sym != bi->from.sym ||
1687aca7a94dSNamhyung Kim 			     bi->to.map->dso != bi->from.map->dso) &&
1688aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1689aca7a94dSNamhyung Kim 					 bi->to.sym->name) > 0)
1690aca7a94dSNamhyung Kim 				annotate_t = nr_options++;
1691aca7a94dSNamhyung Kim 		} else {
1692aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1693aca7a94dSNamhyung Kim 			    browser->selection->sym != NULL &&
1694d755330cSJiri Olsa 			    !browser->selection->map->dso->annotate_warned) {
1695d755330cSJiri Olsa 				struct annotation *notes;
1696d755330cSJiri Olsa 
1697d755330cSJiri Olsa 				notes = symbol__annotation(browser->selection->sym);
1698d755330cSJiri Olsa 
1699d755330cSJiri Olsa 				if (notes->src &&
1700aca7a94dSNamhyung Kim 				    asprintf(&options[nr_options], "Annotate %s",
1701aca7a94dSNamhyung Kim 						 browser->selection->sym->name) > 0)
1702aca7a94dSNamhyung Kim 					annotate = nr_options++;
1703aca7a94dSNamhyung Kim 			}
1704d755330cSJiri Olsa 		}
1705aca7a94dSNamhyung Kim 
1706aca7a94dSNamhyung Kim 		if (thread != NULL &&
1707aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1708aca7a94dSNamhyung Kim 			     (browser->hists->thread_filter ? "out of" : "into"),
1709b9c5143aSFrederic Weisbecker 			     (thread->comm_set ? thread__comm_str(thread) : ""),
171038051234SAdrian Hunter 			     thread->tid) > 0)
1711aca7a94dSNamhyung Kim 			zoom_thread = nr_options++;
1712aca7a94dSNamhyung Kim 
1713aca7a94dSNamhyung Kim 		if (dso != NULL &&
1714aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1715aca7a94dSNamhyung Kim 			     (browser->hists->dso_filter ? "out of" : "into"),
1716aca7a94dSNamhyung Kim 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1717aca7a94dSNamhyung Kim 			zoom_dso = nr_options++;
1718aca7a94dSNamhyung Kim 
1719aca7a94dSNamhyung Kim 		if (browser->selection != NULL &&
1720aca7a94dSNamhyung Kim 		    browser->selection->map != NULL &&
1721aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Browse map details") > 0)
1722aca7a94dSNamhyung Kim 			browse_map = nr_options++;
1723cdbab7c2SFeng Tang 
1724cdbab7c2SFeng Tang 		/* perf script support */
1725cdbab7c2SFeng Tang 		if (browser->he_selection) {
1726cdbab7c2SFeng Tang 			struct symbol *sym;
1727cdbab7c2SFeng Tang 
1728cdbab7c2SFeng Tang 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1729b9c5143aSFrederic Weisbecker 				     thread__comm_str(browser->he_selection->thread)) > 0)
1730cdbab7c2SFeng Tang 				scripts_comm = nr_options++;
1731cdbab7c2SFeng Tang 
1732cdbab7c2SFeng Tang 			sym = browser->he_selection->ms.sym;
1733cdbab7c2SFeng Tang 			if (sym && sym->namelen &&
1734cdbab7c2SFeng Tang 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1735cdbab7c2SFeng Tang 						sym->name) > 0)
1736cdbab7c2SFeng Tang 				scripts_symbol = nr_options++;
1737cdbab7c2SFeng Tang 		}
1738cdbab7c2SFeng Tang 
1739cdbab7c2SFeng Tang 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1740cdbab7c2SFeng Tang 			scripts_all = nr_options++;
1741cdbab7c2SFeng Tang 
1742341487abSFeng Tang 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1743341487abSFeng Tang 				"Switch to another data file in PWD") > 0)
1744341487abSFeng Tang 			switch_data = nr_options++;
1745aca7a94dSNamhyung Kim add_exit_option:
1746aca7a94dSNamhyung Kim 		options[nr_options++] = (char *)"Exit";
1747aca7a94dSNamhyung Kim retry_popup_menu:
1748aca7a94dSNamhyung Kim 		choice = ui__popup_menu(nr_options, options);
1749aca7a94dSNamhyung Kim 
1750aca7a94dSNamhyung Kim 		if (choice == nr_options - 1)
1751aca7a94dSNamhyung Kim 			break;
1752aca7a94dSNamhyung Kim 
1753aca7a94dSNamhyung Kim 		if (choice == -1) {
1754aca7a94dSNamhyung Kim 			free_popup_options(options, nr_options - 1);
1755aca7a94dSNamhyung Kim 			continue;
1756aca7a94dSNamhyung Kim 		}
1757aca7a94dSNamhyung Kim 
1758aca7a94dSNamhyung Kim 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1759aca7a94dSNamhyung Kim 			struct hist_entry *he;
1760d755330cSJiri Olsa 			struct annotation *notes;
1761aca7a94dSNamhyung Kim 			int err;
1762aca7a94dSNamhyung Kim do_annotate:
176368d80758SNamhyung Kim 			if (!objdump_path && perf_session_env__lookup_objdump(env))
176468d80758SNamhyung Kim 				continue;
176568d80758SNamhyung Kim 
1766aca7a94dSNamhyung Kim 			he = hist_browser__selected_entry(browser);
1767aca7a94dSNamhyung Kim 			if (he == NULL)
1768aca7a94dSNamhyung Kim 				continue;
1769aca7a94dSNamhyung Kim 
1770aca7a94dSNamhyung Kim 			/*
1771aca7a94dSNamhyung Kim 			 * we stash the branch_info symbol + map into the
1772aca7a94dSNamhyung Kim 			 * the ms so we don't have to rewrite all the annotation
1773aca7a94dSNamhyung Kim 			 * code to use branch_info.
1774aca7a94dSNamhyung Kim 			 * in branch mode, the ms struct is not used
1775aca7a94dSNamhyung Kim 			 */
1776aca7a94dSNamhyung Kim 			if (choice == annotate_f) {
1777aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->from.sym;
1778aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->from.map;
1779aca7a94dSNamhyung Kim 			}  else if (choice == annotate_t) {
1780aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->to.sym;
1781aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->to.map;
1782aca7a94dSNamhyung Kim 			}
1783aca7a94dSNamhyung Kim 
1784d755330cSJiri Olsa 			notes = symbol__annotation(he->ms.sym);
1785d755330cSJiri Olsa 			if (!notes->src)
1786d755330cSJiri Olsa 				continue;
1787d755330cSJiri Olsa 
1788aca7a94dSNamhyung Kim 			/*
1789aca7a94dSNamhyung Kim 			 * Don't let this be freed, say, by hists__decay_entry.
1790aca7a94dSNamhyung Kim 			 */
1791aca7a94dSNamhyung Kim 			he->used = true;
1792db8fd07aSNamhyung Kim 			err = hist_entry__tui_annotate(he, evsel, hbt);
1793aca7a94dSNamhyung Kim 			he->used = false;
1794aca7a94dSNamhyung Kim 			/*
1795aca7a94dSNamhyung Kim 			 * offer option to annotate the other branch source or target
1796aca7a94dSNamhyung Kim 			 * (if they exists) when returning from annotate
1797aca7a94dSNamhyung Kim 			 */
1798aca7a94dSNamhyung Kim 			if ((err == 'q' || err == CTRL('c'))
1799aca7a94dSNamhyung Kim 			    && annotate_t != -2 && annotate_f != -2)
1800aca7a94dSNamhyung Kim 				goto retry_popup_menu;
1801aca7a94dSNamhyung Kim 
1802aca7a94dSNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1803aca7a94dSNamhyung Kim 			if (err)
1804aca7a94dSNamhyung Kim 				ui_browser__handle_resize(&browser->b);
1805aca7a94dSNamhyung Kim 
1806aca7a94dSNamhyung Kim 		} else if (choice == browse_map)
1807aca7a94dSNamhyung Kim 			map__browse(browser->selection->map);
1808aca7a94dSNamhyung Kim 		else if (choice == zoom_dso) {
1809aca7a94dSNamhyung Kim zoom_dso:
1810aca7a94dSNamhyung Kim 			if (browser->hists->dso_filter) {
1811aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->dso_filter);
1812aca7a94dSNamhyung Kim zoom_out_dso:
1813aca7a94dSNamhyung Kim 				ui_helpline__pop();
1814aca7a94dSNamhyung Kim 				browser->hists->dso_filter = NULL;
1815f2998422SJiri Olsa 				perf_hpp__set_elide(HISTC_DSO, false);
1816aca7a94dSNamhyung Kim 			} else {
1817aca7a94dSNamhyung Kim 				if (dso == NULL)
1818aca7a94dSNamhyung Kim 					continue;
1819aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1820aca7a94dSNamhyung Kim 						   dso->kernel ? "the Kernel" : dso->short_name);
1821aca7a94dSNamhyung Kim 				browser->hists->dso_filter = dso;
1822f2998422SJiri Olsa 				perf_hpp__set_elide(HISTC_DSO, true);
1823aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->dso_filter);
1824aca7a94dSNamhyung Kim 			}
182505e8b080SArnaldo Carvalho de Melo 			hists__filter_by_dso(hists);
1826aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1827aca7a94dSNamhyung Kim 		} else if (choice == zoom_thread) {
1828aca7a94dSNamhyung Kim zoom_thread:
1829aca7a94dSNamhyung Kim 			if (browser->hists->thread_filter) {
1830aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->thread_filter);
1831aca7a94dSNamhyung Kim zoom_out_thread:
1832aca7a94dSNamhyung Kim 				ui_helpline__pop();
1833aca7a94dSNamhyung Kim 				browser->hists->thread_filter = NULL;
1834f2998422SJiri Olsa 				perf_hpp__set_elide(HISTC_THREAD, false);
1835aca7a94dSNamhyung Kim 			} else {
1836aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1837b9c5143aSFrederic Weisbecker 						   thread->comm_set ? thread__comm_str(thread) : "",
183838051234SAdrian Hunter 						   thread->tid);
1839aca7a94dSNamhyung Kim 				browser->hists->thread_filter = thread;
1840f2998422SJiri Olsa 				perf_hpp__set_elide(HISTC_THREAD, false);
1841aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->thread_filter);
1842aca7a94dSNamhyung Kim 			}
184305e8b080SArnaldo Carvalho de Melo 			hists__filter_by_thread(hists);
1844aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1845aca7a94dSNamhyung Kim 		}
1846cdbab7c2SFeng Tang 		/* perf scripts support */
1847cdbab7c2SFeng Tang 		else if (choice == scripts_all || choice == scripts_comm ||
1848cdbab7c2SFeng Tang 				choice == scripts_symbol) {
1849cdbab7c2SFeng Tang do_scripts:
1850cdbab7c2SFeng Tang 			memset(script_opt, 0, 64);
1851cdbab7c2SFeng Tang 
1852cdbab7c2SFeng Tang 			if (choice == scripts_comm)
1853b9c5143aSFrederic Weisbecker 				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1854cdbab7c2SFeng Tang 
1855cdbab7c2SFeng Tang 			if (choice == scripts_symbol)
1856cdbab7c2SFeng Tang 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1857cdbab7c2SFeng Tang 
1858cdbab7c2SFeng Tang 			script_browse(script_opt);
1859cdbab7c2SFeng Tang 		}
1860341487abSFeng Tang 		/* Switch to another data file */
1861341487abSFeng Tang 		else if (choice == switch_data) {
1862341487abSFeng Tang do_data_switch:
1863341487abSFeng Tang 			if (!switch_data_file()) {
1864341487abSFeng Tang 				key = K_SWITCH_INPUT_DATA;
1865341487abSFeng Tang 				break;
1866341487abSFeng Tang 			} else
1867341487abSFeng Tang 				ui__warning("Won't switch the data files due to\n"
1868341487abSFeng Tang 					"no valid data file get selected!\n");
1869341487abSFeng Tang 		}
1870aca7a94dSNamhyung Kim 	}
1871aca7a94dSNamhyung Kim out_free_stack:
1872aca7a94dSNamhyung Kim 	pstack__delete(fstack);
1873aca7a94dSNamhyung Kim out:
1874aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
1875aca7a94dSNamhyung Kim 	free_popup_options(options, nr_options - 1);
1876aca7a94dSNamhyung Kim 	return key;
1877aca7a94dSNamhyung Kim }
1878aca7a94dSNamhyung Kim 
1879aca7a94dSNamhyung Kim struct perf_evsel_menu {
1880aca7a94dSNamhyung Kim 	struct ui_browser b;
1881aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
1882aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
1883064f1981SNamhyung Kim 	float min_pcnt;
188468d80758SNamhyung Kim 	struct perf_session_env *env;
1885aca7a94dSNamhyung Kim };
1886aca7a94dSNamhyung Kim 
1887aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
1888aca7a94dSNamhyung Kim 				   void *entry, int row)
1889aca7a94dSNamhyung Kim {
1890aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
1891aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
1892aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1893aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
1894aca7a94dSNamhyung Kim 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
18957289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
1896aca7a94dSNamhyung Kim 	char bf[256], unit;
1897aca7a94dSNamhyung Kim 	const char *warn = " ";
1898aca7a94dSNamhyung Kim 	size_t printed;
1899aca7a94dSNamhyung Kim 
1900aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1901aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
1902aca7a94dSNamhyung Kim 
1903759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1904717e263fSNamhyung Kim 		struct perf_evsel *pos;
1905717e263fSNamhyung Kim 
1906717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
1907717e263fSNamhyung Kim 
1908717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
1909717e263fSNamhyung Kim 			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1910717e263fSNamhyung Kim 		}
1911717e263fSNamhyung Kim 	}
1912717e263fSNamhyung Kim 
1913aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
1914aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1915aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
1916aca7a94dSNamhyung Kim 	slsmg_printf("%s", bf);
1917aca7a94dSNamhyung Kim 
1918aca7a94dSNamhyung Kim 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1919aca7a94dSNamhyung Kim 	if (nr_events != 0) {
1920aca7a94dSNamhyung Kim 		menu->lost_events = true;
1921aca7a94dSNamhyung Kim 		if (!current_entry)
1922aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1923aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
1924aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1925aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
1926aca7a94dSNamhyung Kim 		warn = bf;
1927aca7a94dSNamhyung Kim 	}
1928aca7a94dSNamhyung Kim 
1929aca7a94dSNamhyung Kim 	slsmg_write_nstring(warn, browser->width - printed);
1930aca7a94dSNamhyung Kim 
1931aca7a94dSNamhyung Kim 	if (current_entry)
1932aca7a94dSNamhyung Kim 		menu->selection = evsel;
1933aca7a94dSNamhyung Kim }
1934aca7a94dSNamhyung Kim 
1935aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1936aca7a94dSNamhyung Kim 				int nr_events, const char *help,
19379783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
1938aca7a94dSNamhyung Kim {
1939aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
1940aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1941dd00d486SJiri Olsa 	const char *title = "Available samples";
19429783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
1943aca7a94dSNamhyung Kim 	int key;
1944aca7a94dSNamhyung Kim 
1945aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
1946aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1947aca7a94dSNamhyung Kim 		return -1;
1948aca7a94dSNamhyung Kim 
1949aca7a94dSNamhyung Kim 	while (1) {
1950aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
1951aca7a94dSNamhyung Kim 
1952aca7a94dSNamhyung Kim 		switch (key) {
1953aca7a94dSNamhyung Kim 		case K_TIMER:
19549783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
1955aca7a94dSNamhyung Kim 
1956aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
1957aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
1958aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
1959aca7a94dSNamhyung Kim 			}
1960aca7a94dSNamhyung Kim 			continue;
1961aca7a94dSNamhyung Kim 		case K_RIGHT:
1962aca7a94dSNamhyung Kim 		case K_ENTER:
1963aca7a94dSNamhyung Kim 			if (!menu->selection)
1964aca7a94dSNamhyung Kim 				continue;
1965aca7a94dSNamhyung Kim 			pos = menu->selection;
1966aca7a94dSNamhyung Kim browse_hists:
1967aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
1968aca7a94dSNamhyung Kim 			/*
1969aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
1970aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
1971aca7a94dSNamhyung Kim 			 */
19729783adf7SNamhyung Kim 			if (hbt)
19739783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
1974aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
1975dd00d486SJiri Olsa 						       true, hbt,
1976064f1981SNamhyung Kim 						       menu->min_pcnt,
197768d80758SNamhyung Kim 						       menu->env);
1978aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
1979aca7a94dSNamhyung Kim 			switch (key) {
1980aca7a94dSNamhyung Kim 			case K_TAB:
1981aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
19829a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
1983aca7a94dSNamhyung Kim 				else
19849a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
1985aca7a94dSNamhyung Kim 				goto browse_hists;
1986aca7a94dSNamhyung Kim 			case K_UNTAB:
1987aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
19889a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
1989aca7a94dSNamhyung Kim 				else
1990d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
1991aca7a94dSNamhyung Kim 				goto browse_hists;
1992aca7a94dSNamhyung Kim 			case K_ESC:
1993aca7a94dSNamhyung Kim 				if (!ui_browser__dialog_yesno(&menu->b,
1994aca7a94dSNamhyung Kim 						"Do you really want to exit?"))
1995aca7a94dSNamhyung Kim 					continue;
1996aca7a94dSNamhyung Kim 				/* Fall thru */
1997341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
1998aca7a94dSNamhyung Kim 			case 'q':
1999aca7a94dSNamhyung Kim 			case CTRL('c'):
2000aca7a94dSNamhyung Kim 				goto out;
2001aca7a94dSNamhyung Kim 			default:
2002aca7a94dSNamhyung Kim 				continue;
2003aca7a94dSNamhyung Kim 			}
2004aca7a94dSNamhyung Kim 		case K_LEFT:
2005aca7a94dSNamhyung Kim 			continue;
2006aca7a94dSNamhyung Kim 		case K_ESC:
2007aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
2008aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
2009aca7a94dSNamhyung Kim 				continue;
2010aca7a94dSNamhyung Kim 			/* Fall thru */
2011aca7a94dSNamhyung Kim 		case 'q':
2012aca7a94dSNamhyung Kim 		case CTRL('c'):
2013aca7a94dSNamhyung Kim 			goto out;
2014aca7a94dSNamhyung Kim 		default:
2015aca7a94dSNamhyung Kim 			continue;
2016aca7a94dSNamhyung Kim 		}
2017aca7a94dSNamhyung Kim 	}
2018aca7a94dSNamhyung Kim 
2019aca7a94dSNamhyung Kim out:
2020aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
2021aca7a94dSNamhyung Kim 	return key;
2022aca7a94dSNamhyung Kim }
2023aca7a94dSNamhyung Kim 
2024316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2025fc24d7c2SNamhyung Kim 				 void *entry)
2026fc24d7c2SNamhyung Kim {
2027fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2028fc24d7c2SNamhyung Kim 
2029fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2030fc24d7c2SNamhyung Kim 		return true;
2031fc24d7c2SNamhyung Kim 
2032fc24d7c2SNamhyung Kim 	return false;
2033fc24d7c2SNamhyung Kim }
2034fc24d7c2SNamhyung Kim 
2035aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2036fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
203768d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
2038064f1981SNamhyung Kim 					   float min_pcnt,
203968d80758SNamhyung Kim 					   struct perf_session_env *env)
2040aca7a94dSNamhyung Kim {
2041aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
2042aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
2043aca7a94dSNamhyung Kim 		.b = {
2044aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
2045aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
2046aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
2047aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
2048fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
2049fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
2050aca7a94dSNamhyung Kim 			.priv	    = evlist,
2051aca7a94dSNamhyung Kim 		},
2052064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
205368d80758SNamhyung Kim 		.env = env,
2054aca7a94dSNamhyung Kim 	};
2055aca7a94dSNamhyung Kim 
2056aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
2057aca7a94dSNamhyung Kim 
20580050f7aaSArnaldo Carvalho de Melo 	evlist__for_each(evlist, pos) {
20597289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
2060aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
2061aca7a94dSNamhyung Kim 
2062aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
2063aca7a94dSNamhyung Kim 			menu.b.width = line_len;
2064aca7a94dSNamhyung Kim 	}
2065aca7a94dSNamhyung Kim 
2066fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2067aca7a94dSNamhyung Kim }
2068aca7a94dSNamhyung Kim 
2069aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
207068d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
2071064f1981SNamhyung Kim 				  float min_pcnt,
207268d80758SNamhyung Kim 				  struct perf_session_env *env)
2073aca7a94dSNamhyung Kim {
2074fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
2075fc24d7c2SNamhyung Kim 
2076fc24d7c2SNamhyung Kim single_entry:
2077fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
20789a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
2079fc24d7c2SNamhyung Kim 
2080fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
2081dd00d486SJiri Olsa 						false, hbt, min_pcnt,
2082064f1981SNamhyung Kim 						env);
2083aca7a94dSNamhyung Kim 	}
2084aca7a94dSNamhyung Kim 
2085fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
2086fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
2087fc24d7c2SNamhyung Kim 
2088fc24d7c2SNamhyung Kim 		nr_entries = 0;
20890050f7aaSArnaldo Carvalho de Melo 		evlist__for_each(evlist, pos) {
2090fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
2091fc24d7c2SNamhyung Kim 				nr_entries++;
20920050f7aaSArnaldo Carvalho de Melo 		}
2093fc24d7c2SNamhyung Kim 
2094fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
2095fc24d7c2SNamhyung Kim 			goto single_entry;
2096fc24d7c2SNamhyung Kim 	}
2097fc24d7c2SNamhyung Kim 
2098fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2099064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
2100aca7a94dSNamhyung Kim }
2101