xref: /linux/tools/perf/ui/browsers/hists.c (revision 2f6d9009af1df0f7cba1fdfe012a089babd8c747)
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"
20aca7a94dSNamhyung Kim 
21aca7a94dSNamhyung Kim struct hist_browser {
22aca7a94dSNamhyung Kim 	struct ui_browser   b;
23aca7a94dSNamhyung Kim 	struct hists	    *hists;
24aca7a94dSNamhyung Kim 	struct hist_entry   *he_selection;
25aca7a94dSNamhyung Kim 	struct map_symbol   *selection;
26aff3f3f6SArnaldo Carvalho de Melo 	int		     print_seq;
27a7cb8863SArnaldo Carvalho de Melo 	bool		     show_dso;
28064f1981SNamhyung Kim 	float		     min_pcnt;
29064f1981SNamhyung Kim 	u64		     nr_pcnt_entries;
30aca7a94dSNamhyung Kim };
31aca7a94dSNamhyung Kim 
32f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
33f5951d56SNamhyung Kim 
3405e8b080SArnaldo Carvalho de Melo static int hists__browser_title(struct hists *hists, char *bf, size_t size,
35aca7a94dSNamhyung Kim 				const char *ev_name);
36aca7a94dSNamhyung Kim 
3705e8b080SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct hist_browser *browser)
38aca7a94dSNamhyung Kim {
39aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
4005e8b080SArnaldo Carvalho de Melo 	browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
41aca7a94dSNamhyung Kim 			     sizeof("[k]"));
42aca7a94dSNamhyung Kim }
43aca7a94dSNamhyung Kim 
4405e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
45aca7a94dSNamhyung Kim {
4605e8b080SArnaldo Carvalho de Melo 	browser->b.nr_entries = browser->hists->nr_entries;
4705e8b080SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(browser);
4805e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
49aca7a94dSNamhyung Kim }
50aca7a94dSNamhyung Kim 
51aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
52aca7a94dSNamhyung Kim {
53aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
54aca7a94dSNamhyung Kim }
55aca7a94dSNamhyung Kim 
5605e8b080SArnaldo Carvalho de Melo static char map_symbol__folded(const struct map_symbol *ms)
57aca7a94dSNamhyung Kim {
5805e8b080SArnaldo Carvalho de Melo 	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
59aca7a94dSNamhyung Kim }
60aca7a94dSNamhyung Kim 
6105e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
62aca7a94dSNamhyung Kim {
6305e8b080SArnaldo Carvalho de Melo 	return map_symbol__folded(&he->ms);
64aca7a94dSNamhyung Kim }
65aca7a94dSNamhyung Kim 
6605e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
67aca7a94dSNamhyung Kim {
6805e8b080SArnaldo Carvalho de Melo 	return map_symbol__folded(&cl->ms);
69aca7a94dSNamhyung Kim }
70aca7a94dSNamhyung Kim 
7105e8b080SArnaldo Carvalho de Melo static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
72aca7a94dSNamhyung Kim {
7305e8b080SArnaldo Carvalho de Melo 	ms->unfolded = unfold ? ms->has_children : false;
74aca7a94dSNamhyung Kim }
75aca7a94dSNamhyung Kim 
7605e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
77aca7a94dSNamhyung Kim {
78aca7a94dSNamhyung Kim 	int n = 0;
79aca7a94dSNamhyung Kim 	struct rb_node *nd;
80aca7a94dSNamhyung Kim 
8105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
82aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
83aca7a94dSNamhyung Kim 		struct callchain_list *chain;
84aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
85aca7a94dSNamhyung Kim 
86aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
87aca7a94dSNamhyung Kim 			++n;
88aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
89aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
90aca7a94dSNamhyung Kim 			if (folded_sign == '+')
91aca7a94dSNamhyung Kim 				break;
92aca7a94dSNamhyung Kim 		}
93aca7a94dSNamhyung Kim 
94aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
95aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
96aca7a94dSNamhyung Kim 	}
97aca7a94dSNamhyung Kim 
98aca7a94dSNamhyung Kim 	return n;
99aca7a94dSNamhyung Kim }
100aca7a94dSNamhyung Kim 
101aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
102aca7a94dSNamhyung Kim {
103aca7a94dSNamhyung Kim 	struct callchain_list *chain;
104aca7a94dSNamhyung Kim 	bool unfolded = false;
105aca7a94dSNamhyung Kim 	int n = 0;
106aca7a94dSNamhyung Kim 
107aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
108aca7a94dSNamhyung Kim 		++n;
109aca7a94dSNamhyung Kim 		unfolded = chain->ms.unfolded;
110aca7a94dSNamhyung Kim 	}
111aca7a94dSNamhyung Kim 
112aca7a94dSNamhyung Kim 	if (unfolded)
113aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
114aca7a94dSNamhyung Kim 
115aca7a94dSNamhyung Kim 	return n;
116aca7a94dSNamhyung Kim }
117aca7a94dSNamhyung Kim 
118aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
119aca7a94dSNamhyung Kim {
120aca7a94dSNamhyung Kim 	struct rb_node *nd;
121aca7a94dSNamhyung Kim 	int n = 0;
122aca7a94dSNamhyung Kim 
123aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
124aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
125aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
126aca7a94dSNamhyung Kim 	}
127aca7a94dSNamhyung Kim 
128aca7a94dSNamhyung Kim 	return n;
129aca7a94dSNamhyung Kim }
130aca7a94dSNamhyung Kim 
13105e8b080SArnaldo Carvalho de Melo static bool map_symbol__toggle_fold(struct map_symbol *ms)
132aca7a94dSNamhyung Kim {
13305e8b080SArnaldo Carvalho de Melo 	if (!ms)
134aca7a94dSNamhyung Kim 		return false;
135aca7a94dSNamhyung Kim 
13605e8b080SArnaldo Carvalho de Melo 	if (!ms->has_children)
137aca7a94dSNamhyung Kim 		return false;
138aca7a94dSNamhyung Kim 
13905e8b080SArnaldo Carvalho de Melo 	ms->unfolded = !ms->unfolded;
140aca7a94dSNamhyung Kim 	return true;
141aca7a94dSNamhyung Kim }
142aca7a94dSNamhyung Kim 
14305e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
144aca7a94dSNamhyung Kim {
14505e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
146aca7a94dSNamhyung Kim 
14705e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
148aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149aca7a94dSNamhyung Kim 		struct callchain_list *chain;
150aca7a94dSNamhyung Kim 		bool first = true;
151aca7a94dSNamhyung Kim 
152aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
153aca7a94dSNamhyung Kim 			if (first) {
154aca7a94dSNamhyung Kim 				first = false;
155aca7a94dSNamhyung Kim 				chain->ms.has_children = chain->list.next != &child->val ||
156aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
157aca7a94dSNamhyung Kim 			} else
158aca7a94dSNamhyung Kim 				chain->ms.has_children = chain->list.next == &child->val &&
159aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
160aca7a94dSNamhyung Kim 		}
161aca7a94dSNamhyung Kim 
162aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
163aca7a94dSNamhyung Kim 	}
164aca7a94dSNamhyung Kim }
165aca7a94dSNamhyung Kim 
16605e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children(struct callchain_node *node)
167aca7a94dSNamhyung Kim {
168aca7a94dSNamhyung Kim 	struct callchain_list *chain;
169aca7a94dSNamhyung Kim 
17005e8b080SArnaldo Carvalho de Melo 	list_for_each_entry(chain, &node->val, list)
17105e8b080SArnaldo Carvalho de Melo 		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
172aca7a94dSNamhyung Kim 
17305e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
174aca7a94dSNamhyung Kim }
175aca7a94dSNamhyung Kim 
17605e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
177aca7a94dSNamhyung Kim {
178aca7a94dSNamhyung Kim 	struct rb_node *nd;
179aca7a94dSNamhyung Kim 
18005e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
181aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
182aca7a94dSNamhyung Kim 		callchain_node__init_have_children(node);
183aca7a94dSNamhyung Kim 	}
184aca7a94dSNamhyung Kim }
185aca7a94dSNamhyung Kim 
18605e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
187aca7a94dSNamhyung Kim {
18805e8b080SArnaldo Carvalho de Melo 	if (!he->init_have_children) {
18905e8b080SArnaldo Carvalho de Melo 		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
19005e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
19105e8b080SArnaldo Carvalho de Melo 		he->init_have_children = true;
192aca7a94dSNamhyung Kim 	}
193aca7a94dSNamhyung Kim }
194aca7a94dSNamhyung Kim 
19505e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
196aca7a94dSNamhyung Kim {
19705e8b080SArnaldo Carvalho de Melo 	if (map_symbol__toggle_fold(browser->selection)) {
19805e8b080SArnaldo Carvalho de Melo 		struct hist_entry *he = browser->he_selection;
199aca7a94dSNamhyung Kim 
200aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
20105e8b080SArnaldo Carvalho de Melo 		browser->hists->nr_entries -= he->nr_rows;
202aca7a94dSNamhyung Kim 
203aca7a94dSNamhyung Kim 		if (he->ms.unfolded)
204aca7a94dSNamhyung Kim 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
205aca7a94dSNamhyung Kim 		else
206aca7a94dSNamhyung Kim 			he->nr_rows = 0;
20705e8b080SArnaldo Carvalho de Melo 		browser->hists->nr_entries += he->nr_rows;
20805e8b080SArnaldo Carvalho de Melo 		browser->b.nr_entries = browser->hists->nr_entries;
209aca7a94dSNamhyung Kim 
210aca7a94dSNamhyung Kim 		return true;
211aca7a94dSNamhyung Kim 	}
212aca7a94dSNamhyung Kim 
213aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
214aca7a94dSNamhyung Kim 	return false;
215aca7a94dSNamhyung Kim }
216aca7a94dSNamhyung Kim 
21705e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
218aca7a94dSNamhyung Kim {
219aca7a94dSNamhyung Kim 	int n = 0;
220aca7a94dSNamhyung Kim 	struct rb_node *nd;
221aca7a94dSNamhyung Kim 
22205e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
223aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
224aca7a94dSNamhyung Kim 		struct callchain_list *chain;
225aca7a94dSNamhyung Kim 		bool has_children = false;
226aca7a94dSNamhyung Kim 
227aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
228aca7a94dSNamhyung Kim 			++n;
229aca7a94dSNamhyung Kim 			map_symbol__set_folding(&chain->ms, unfold);
230aca7a94dSNamhyung Kim 			has_children = chain->ms.has_children;
231aca7a94dSNamhyung Kim 		}
232aca7a94dSNamhyung Kim 
233aca7a94dSNamhyung Kim 		if (has_children)
234aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
235aca7a94dSNamhyung Kim 	}
236aca7a94dSNamhyung Kim 
237aca7a94dSNamhyung Kim 	return n;
238aca7a94dSNamhyung Kim }
239aca7a94dSNamhyung Kim 
240aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
241aca7a94dSNamhyung Kim {
242aca7a94dSNamhyung Kim 	struct callchain_list *chain;
243aca7a94dSNamhyung Kim 	bool has_children = false;
244aca7a94dSNamhyung Kim 	int n = 0;
245aca7a94dSNamhyung Kim 
246aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
247aca7a94dSNamhyung Kim 		++n;
248aca7a94dSNamhyung Kim 		map_symbol__set_folding(&chain->ms, unfold);
249aca7a94dSNamhyung Kim 		has_children = chain->ms.has_children;
250aca7a94dSNamhyung Kim 	}
251aca7a94dSNamhyung Kim 
252aca7a94dSNamhyung Kim 	if (has_children)
253aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
254aca7a94dSNamhyung Kim 
255aca7a94dSNamhyung Kim 	return n;
256aca7a94dSNamhyung Kim }
257aca7a94dSNamhyung Kim 
258aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
259aca7a94dSNamhyung Kim {
260aca7a94dSNamhyung Kim 	struct rb_node *nd;
261aca7a94dSNamhyung Kim 	int n = 0;
262aca7a94dSNamhyung Kim 
263aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
264aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
265aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
266aca7a94dSNamhyung Kim 	}
267aca7a94dSNamhyung Kim 
268aca7a94dSNamhyung Kim 	return n;
269aca7a94dSNamhyung Kim }
270aca7a94dSNamhyung Kim 
27105e8b080SArnaldo Carvalho de Melo static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
272aca7a94dSNamhyung Kim {
27305e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
27405e8b080SArnaldo Carvalho de Melo 	map_symbol__set_folding(&he->ms, unfold);
275aca7a94dSNamhyung Kim 
27605e8b080SArnaldo Carvalho de Melo 	if (he->ms.has_children) {
27705e8b080SArnaldo Carvalho de Melo 		int n = callchain__set_folding(&he->sorted_chain, unfold);
27805e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
279aca7a94dSNamhyung Kim 	} else
28005e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
281aca7a94dSNamhyung Kim }
282aca7a94dSNamhyung Kim 
28305e8b080SArnaldo Carvalho de Melo static void hists__set_folding(struct hists *hists, bool unfold)
284aca7a94dSNamhyung Kim {
285aca7a94dSNamhyung Kim 	struct rb_node *nd;
286aca7a94dSNamhyung Kim 
28705e8b080SArnaldo Carvalho de Melo 	hists->nr_entries = 0;
288aca7a94dSNamhyung Kim 
28905e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
290aca7a94dSNamhyung Kim 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291aca7a94dSNamhyung Kim 		hist_entry__set_folding(he, unfold);
29205e8b080SArnaldo Carvalho de Melo 		hists->nr_entries += 1 + he->nr_rows;
293aca7a94dSNamhyung Kim 	}
294aca7a94dSNamhyung Kim }
295aca7a94dSNamhyung Kim 
29605e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
297aca7a94dSNamhyung Kim {
29805e8b080SArnaldo Carvalho de Melo 	hists__set_folding(browser->hists, unfold);
29905e8b080SArnaldo Carvalho de Melo 	browser->b.nr_entries = browser->hists->nr_entries;
300aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
30105e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
302aca7a94dSNamhyung Kim }
303aca7a94dSNamhyung Kim 
304aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
305aca7a94dSNamhyung Kim {
306aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
307aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
308aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
309aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
310aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
311aca7a94dSNamhyung Kim }
312aca7a94dSNamhyung Kim 
313fa5df943SNamhyung Kim static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
314fa5df943SNamhyung Kim 
31505e8b080SArnaldo Carvalho de Melo static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
3169783adf7SNamhyung Kim 			     struct hist_browser_timer *hbt)
317aca7a94dSNamhyung Kim {
318aca7a94dSNamhyung Kim 	int key;
319aca7a94dSNamhyung Kim 	char title[160];
3209783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
321aca7a94dSNamhyung Kim 
32205e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
32305e8b080SArnaldo Carvalho de Melo 	browser->b.nr_entries = browser->hists->nr_entries;
324064f1981SNamhyung Kim 	if (browser->min_pcnt)
325064f1981SNamhyung Kim 		browser->b.nr_entries = browser->nr_pcnt_entries;
326aca7a94dSNamhyung Kim 
32705e8b080SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(browser);
32805e8b080SArnaldo Carvalho de Melo 	hists__browser_title(browser->hists, title, sizeof(title), ev_name);
329aca7a94dSNamhyung Kim 
33005e8b080SArnaldo Carvalho de Melo 	if (ui_browser__show(&browser->b, title,
331aca7a94dSNamhyung Kim 			     "Press '?' for help on key bindings") < 0)
332aca7a94dSNamhyung Kim 		return -1;
333aca7a94dSNamhyung Kim 
334aca7a94dSNamhyung Kim 	while (1) {
33505e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
336aca7a94dSNamhyung Kim 
337aca7a94dSNamhyung Kim 		switch (key) {
338fa5df943SNamhyung Kim 		case K_TIMER: {
339fa5df943SNamhyung Kim 			u64 nr_entries;
3409783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
341fa5df943SNamhyung Kim 
342fa5df943SNamhyung Kim 			if (browser->min_pcnt) {
343fa5df943SNamhyung Kim 				hist_browser__update_pcnt_entries(browser);
344fa5df943SNamhyung Kim 				nr_entries = browser->nr_pcnt_entries;
345fa5df943SNamhyung Kim 			} else {
346fa5df943SNamhyung Kim 				nr_entries = browser->hists->nr_entries;
347fa5df943SNamhyung Kim 			}
348fa5df943SNamhyung Kim 
349fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
350aca7a94dSNamhyung Kim 
35105e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
35205e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
35305e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
35405e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
35505e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
356aca7a94dSNamhyung Kim 			}
357aca7a94dSNamhyung Kim 
35805e8b080SArnaldo Carvalho de Melo 			hists__browser_title(browser->hists, title, sizeof(title), ev_name);
35905e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
360aca7a94dSNamhyung Kim 			continue;
361fa5df943SNamhyung Kim 		}
362aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
363aca7a94dSNamhyung Kim 			static int seq;
36405e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
365aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
366aca7a94dSNamhyung Kim 			ui_helpline__pop();
367aca7a94dSNamhyung Kim 			ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
36805e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
36905e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
37005e8b080SArnaldo Carvalho de Melo 					   browser->b.height,
37105e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
37205e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
373aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
374aca7a94dSNamhyung Kim 		}
375aca7a94dSNamhyung Kim 			break;
376aca7a94dSNamhyung Kim 		case 'C':
377aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
37805e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
379aca7a94dSNamhyung Kim 			break;
380aca7a94dSNamhyung Kim 		case 'E':
381aca7a94dSNamhyung Kim 			/* Expand the whole world. */
38205e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
383aca7a94dSNamhyung Kim 			break;
384aca7a94dSNamhyung Kim 		case K_ENTER:
38505e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
386aca7a94dSNamhyung Kim 				break;
387aca7a94dSNamhyung Kim 			/* fall thru */
388aca7a94dSNamhyung Kim 		default:
389aca7a94dSNamhyung Kim 			goto out;
390aca7a94dSNamhyung Kim 		}
391aca7a94dSNamhyung Kim 	}
392aca7a94dSNamhyung Kim out:
39305e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
394aca7a94dSNamhyung Kim 	return key;
395aca7a94dSNamhyung Kim }
396aca7a94dSNamhyung Kim 
39705e8b080SArnaldo Carvalho de Melo static char *callchain_list__sym_name(struct callchain_list *cl,
398a7cb8863SArnaldo Carvalho de Melo 				      char *bf, size_t bfsize, bool show_dso)
399aca7a94dSNamhyung Kim {
400a7cb8863SArnaldo Carvalho de Melo 	int printed;
401aca7a94dSNamhyung Kim 
402a7cb8863SArnaldo Carvalho de Melo 	if (cl->ms.sym)
403a7cb8863SArnaldo Carvalho de Melo 		printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
404a7cb8863SArnaldo Carvalho de Melo 	else
405a7cb8863SArnaldo Carvalho de Melo 		printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
406a7cb8863SArnaldo Carvalho de Melo 
407a7cb8863SArnaldo Carvalho de Melo 	if (show_dso)
408a7cb8863SArnaldo Carvalho de Melo 		scnprintf(bf + printed, bfsize - printed, " %s",
409a7cb8863SArnaldo Carvalho de Melo 			  cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
410a7cb8863SArnaldo Carvalho de Melo 
411aca7a94dSNamhyung Kim 	return bf;
412aca7a94dSNamhyung Kim }
413aca7a94dSNamhyung Kim 
414aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
415aca7a94dSNamhyung Kim 
41605e8b080SArnaldo Carvalho de Melo static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
417aca7a94dSNamhyung Kim 						     struct callchain_node *chain_node,
418aca7a94dSNamhyung Kim 						     u64 total, int level,
419aca7a94dSNamhyung Kim 						     unsigned short row,
420aca7a94dSNamhyung Kim 						     off_t *row_offset,
421aca7a94dSNamhyung Kim 						     bool *is_current_entry)
422aca7a94dSNamhyung Kim {
423aca7a94dSNamhyung Kim 	struct rb_node *node;
424aca7a94dSNamhyung Kim 	int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
425aca7a94dSNamhyung Kim 	u64 new_total, remaining;
426aca7a94dSNamhyung Kim 
427aca7a94dSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
428aca7a94dSNamhyung Kim 		new_total = chain_node->children_hit;
429aca7a94dSNamhyung Kim 	else
430aca7a94dSNamhyung Kim 		new_total = total;
431aca7a94dSNamhyung Kim 
432aca7a94dSNamhyung Kim 	remaining = new_total;
433aca7a94dSNamhyung Kim 	node = rb_first(&chain_node->rb_root);
434aca7a94dSNamhyung Kim 	while (node) {
435aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
436aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
437aca7a94dSNamhyung Kim 		u64 cumul = callchain_cumul_hits(child);
438aca7a94dSNamhyung Kim 		struct callchain_list *chain;
439aca7a94dSNamhyung Kim 		char folded_sign = ' ';
440aca7a94dSNamhyung Kim 		int first = true;
441aca7a94dSNamhyung Kim 		int extra_offset = 0;
442aca7a94dSNamhyung Kim 
443aca7a94dSNamhyung Kim 		remaining -= cumul;
444aca7a94dSNamhyung Kim 
445aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
446a7cb8863SArnaldo Carvalho de Melo 			char bf[1024], *alloc_str;
447aca7a94dSNamhyung Kim 			const char *str;
448aca7a94dSNamhyung Kim 			int color;
449aca7a94dSNamhyung Kim 			bool was_first = first;
450aca7a94dSNamhyung Kim 
451aca7a94dSNamhyung Kim 			if (first)
452aca7a94dSNamhyung Kim 				first = false;
453aca7a94dSNamhyung Kim 			else
454aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
455aca7a94dSNamhyung Kim 
456aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
457aca7a94dSNamhyung Kim 			if (*row_offset != 0) {
458aca7a94dSNamhyung Kim 				--*row_offset;
459aca7a94dSNamhyung Kim 				goto do_next;
460aca7a94dSNamhyung Kim 			}
461aca7a94dSNamhyung Kim 
462aca7a94dSNamhyung Kim 			alloc_str = NULL;
463a7cb8863SArnaldo Carvalho de Melo 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
464a7cb8863SArnaldo Carvalho de Melo 						       browser->show_dso);
465aca7a94dSNamhyung Kim 			if (was_first) {
466aca7a94dSNamhyung Kim 				double percent = cumul * 100.0 / new_total;
467aca7a94dSNamhyung Kim 
468aca7a94dSNamhyung Kim 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
469aca7a94dSNamhyung Kim 					str = "Not enough memory!";
470aca7a94dSNamhyung Kim 				else
471aca7a94dSNamhyung Kim 					str = alloc_str;
472aca7a94dSNamhyung Kim 			}
473aca7a94dSNamhyung Kim 
474aca7a94dSNamhyung Kim 			color = HE_COLORSET_NORMAL;
47505e8b080SArnaldo Carvalho de Melo 			width = browser->b.width - (offset + extra_offset + 2);
47605e8b080SArnaldo Carvalho de Melo 			if (ui_browser__is_current_entry(&browser->b, row)) {
47705e8b080SArnaldo Carvalho de Melo 				browser->selection = &chain->ms;
478aca7a94dSNamhyung Kim 				color = HE_COLORSET_SELECTED;
479aca7a94dSNamhyung Kim 				*is_current_entry = true;
480aca7a94dSNamhyung Kim 			}
481aca7a94dSNamhyung Kim 
48205e8b080SArnaldo Carvalho de Melo 			ui_browser__set_color(&browser->b, color);
48305e8b080SArnaldo Carvalho de Melo 			ui_browser__gotorc(&browser->b, row, 0);
484aca7a94dSNamhyung Kim 			slsmg_write_nstring(" ", offset + extra_offset);
485aca7a94dSNamhyung Kim 			slsmg_printf("%c ", folded_sign);
486aca7a94dSNamhyung Kim 			slsmg_write_nstring(str, width);
487aca7a94dSNamhyung Kim 			free(alloc_str);
488aca7a94dSNamhyung Kim 
48905e8b080SArnaldo Carvalho de Melo 			if (++row == browser->b.height)
490aca7a94dSNamhyung Kim 				goto out;
491aca7a94dSNamhyung Kim do_next:
492aca7a94dSNamhyung Kim 			if (folded_sign == '+')
493aca7a94dSNamhyung Kim 				break;
494aca7a94dSNamhyung Kim 		}
495aca7a94dSNamhyung Kim 
496aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
497aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
49805e8b080SArnaldo Carvalho de Melo 			row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
499aca7a94dSNamhyung Kim 									 new_level, row, row_offset,
500aca7a94dSNamhyung Kim 									 is_current_entry);
501aca7a94dSNamhyung Kim 		}
50205e8b080SArnaldo Carvalho de Melo 		if (row == browser->b.height)
503aca7a94dSNamhyung Kim 			goto out;
504aca7a94dSNamhyung Kim 		node = next;
505aca7a94dSNamhyung Kim 	}
506aca7a94dSNamhyung Kim out:
507aca7a94dSNamhyung Kim 	return row - first_row;
508aca7a94dSNamhyung Kim }
509aca7a94dSNamhyung Kim 
51005e8b080SArnaldo Carvalho de Melo static int hist_browser__show_callchain_node(struct hist_browser *browser,
511aca7a94dSNamhyung Kim 					     struct callchain_node *node,
512aca7a94dSNamhyung Kim 					     int level, unsigned short row,
513aca7a94dSNamhyung Kim 					     off_t *row_offset,
514aca7a94dSNamhyung Kim 					     bool *is_current_entry)
515aca7a94dSNamhyung Kim {
516aca7a94dSNamhyung Kim 	struct callchain_list *chain;
517aca7a94dSNamhyung Kim 	int first_row = row,
518aca7a94dSNamhyung Kim 	     offset = level * LEVEL_OFFSET_STEP,
51905e8b080SArnaldo Carvalho de Melo 	     width = browser->b.width - offset;
520aca7a94dSNamhyung Kim 	char folded_sign = ' ';
521aca7a94dSNamhyung Kim 
522aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
523a7cb8863SArnaldo Carvalho de Melo 		char bf[1024], *s;
524aca7a94dSNamhyung Kim 		int color;
525aca7a94dSNamhyung Kim 
526aca7a94dSNamhyung Kim 		folded_sign = callchain_list__folded(chain);
527aca7a94dSNamhyung Kim 
528aca7a94dSNamhyung Kim 		if (*row_offset != 0) {
529aca7a94dSNamhyung Kim 			--*row_offset;
530aca7a94dSNamhyung Kim 			continue;
531aca7a94dSNamhyung Kim 		}
532aca7a94dSNamhyung Kim 
533aca7a94dSNamhyung Kim 		color = HE_COLORSET_NORMAL;
53405e8b080SArnaldo Carvalho de Melo 		if (ui_browser__is_current_entry(&browser->b, row)) {
53505e8b080SArnaldo Carvalho de Melo 			browser->selection = &chain->ms;
536aca7a94dSNamhyung Kim 			color = HE_COLORSET_SELECTED;
537aca7a94dSNamhyung Kim 			*is_current_entry = true;
538aca7a94dSNamhyung Kim 		}
539aca7a94dSNamhyung Kim 
540a7cb8863SArnaldo Carvalho de Melo 		s = callchain_list__sym_name(chain, bf, sizeof(bf),
541a7cb8863SArnaldo Carvalho de Melo 					     browser->show_dso);
54205e8b080SArnaldo Carvalho de Melo 		ui_browser__gotorc(&browser->b, row, 0);
54305e8b080SArnaldo Carvalho de Melo 		ui_browser__set_color(&browser->b, color);
544aca7a94dSNamhyung Kim 		slsmg_write_nstring(" ", offset);
545aca7a94dSNamhyung Kim 		slsmg_printf("%c ", folded_sign);
546aca7a94dSNamhyung Kim 		slsmg_write_nstring(s, width - 2);
547aca7a94dSNamhyung Kim 
54805e8b080SArnaldo Carvalho de Melo 		if (++row == browser->b.height)
549aca7a94dSNamhyung Kim 			goto out;
550aca7a94dSNamhyung Kim 	}
551aca7a94dSNamhyung Kim 
552aca7a94dSNamhyung Kim 	if (folded_sign == '-')
55305e8b080SArnaldo Carvalho de Melo 		row += hist_browser__show_callchain_node_rb_tree(browser, node,
55405e8b080SArnaldo Carvalho de Melo 								 browser->hists->stats.total_period,
555aca7a94dSNamhyung Kim 								 level + 1, row,
556aca7a94dSNamhyung Kim 								 row_offset,
557aca7a94dSNamhyung Kim 								 is_current_entry);
558aca7a94dSNamhyung Kim out:
559aca7a94dSNamhyung Kim 	return row - first_row;
560aca7a94dSNamhyung Kim }
561aca7a94dSNamhyung Kim 
56205e8b080SArnaldo Carvalho de Melo static int hist_browser__show_callchain(struct hist_browser *browser,
563aca7a94dSNamhyung Kim 					struct rb_root *chain,
564aca7a94dSNamhyung Kim 					int level, unsigned short row,
565aca7a94dSNamhyung Kim 					off_t *row_offset,
566aca7a94dSNamhyung Kim 					bool *is_current_entry)
567aca7a94dSNamhyung Kim {
568aca7a94dSNamhyung Kim 	struct rb_node *nd;
569aca7a94dSNamhyung Kim 	int first_row = row;
570aca7a94dSNamhyung Kim 
571aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
572aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
573aca7a94dSNamhyung Kim 
57405e8b080SArnaldo Carvalho de Melo 		row += hist_browser__show_callchain_node(browser, node, level,
575aca7a94dSNamhyung Kim 							 row, row_offset,
576aca7a94dSNamhyung Kim 							 is_current_entry);
57705e8b080SArnaldo Carvalho de Melo 		if (row == browser->b.height)
578aca7a94dSNamhyung Kim 			break;
579aca7a94dSNamhyung Kim 	}
580aca7a94dSNamhyung Kim 
581aca7a94dSNamhyung Kim 	return row - first_row;
582aca7a94dSNamhyung Kim }
583aca7a94dSNamhyung Kim 
58489701460SNamhyung Kim struct hpp_arg {
58589701460SNamhyung Kim 	struct ui_browser *b;
58689701460SNamhyung Kim 	char folded_sign;
58789701460SNamhyung Kim 	bool current_entry;
58889701460SNamhyung Kim };
58989701460SNamhyung Kim 
590*2f6d9009SNamhyung Kim static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
5915aed9d24SNamhyung Kim {
592*2f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
593*2f6d9009SNamhyung Kim 
594*2f6d9009SNamhyung Kim 	if (arg->current_entry && arg->b->navkeypressed)
595*2f6d9009SNamhyung Kim 		ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
596*2f6d9009SNamhyung Kim 	else
597*2f6d9009SNamhyung Kim 		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
598*2f6d9009SNamhyung Kim 
599*2f6d9009SNamhyung Kim 	if (front) {
60089701460SNamhyung Kim 		if (!symbol_conf.use_callchain)
60189701460SNamhyung Kim 			return 0;
60289701460SNamhyung Kim 
60389701460SNamhyung Kim 		slsmg_printf("%c ", arg->folded_sign);
60489701460SNamhyung Kim 		return 2;
60589701460SNamhyung Kim 	}
60689701460SNamhyung Kim 
607*2f6d9009SNamhyung Kim 	return 0;
608*2f6d9009SNamhyung Kim }
609*2f6d9009SNamhyung Kim 
610*2f6d9009SNamhyung Kim static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
61189701460SNamhyung Kim {
61289701460SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
6135aed9d24SNamhyung Kim 
614*2f6d9009SNamhyung Kim 	if (!arg->current_entry || !arg->b->navkeypressed)
615*2f6d9009SNamhyung Kim 		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
616*2f6d9009SNamhyung Kim 	return 0;
617*2f6d9009SNamhyung Kim }
618*2f6d9009SNamhyung Kim 
619*2f6d9009SNamhyung Kim static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
620*2f6d9009SNamhyung Kim {
621*2f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
622*2f6d9009SNamhyung Kim 	int ret;
623*2f6d9009SNamhyung Kim 	va_list args;
624*2f6d9009SNamhyung Kim 	double percent;
625*2f6d9009SNamhyung Kim 
626*2f6d9009SNamhyung Kim 	va_start(args, fmt);
627*2f6d9009SNamhyung Kim 	percent = va_arg(args, double);
628*2f6d9009SNamhyung Kim 	va_end(args);
6295aed9d24SNamhyung Kim 
63089701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
6315aed9d24SNamhyung Kim 
632*2f6d9009SNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
63389701460SNamhyung Kim 	slsmg_printf("%s", hpp->buf);
63489701460SNamhyung Kim 
635*2f6d9009SNamhyung Kim 	advance_hpp(hpp, ret);
6365aed9d24SNamhyung Kim 	return ret;
637f5951d56SNamhyung Kim }
638f5951d56SNamhyung Kim 
63989701460SNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)			\
6405aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
6415aed9d24SNamhyung Kim {									\
6425aed9d24SNamhyung Kim 	return he->stat._field;						\
6435aed9d24SNamhyung Kim }									\
6445aed9d24SNamhyung Kim 									\
6452c5d4b4aSJiri Olsa static int								\
6462c5d4b4aSJiri Olsa hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
6472c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
6485aed9d24SNamhyung Kim 				struct hist_entry *he)			\
6495aed9d24SNamhyung Kim {									\
650*2f6d9009SNamhyung Kim 	return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%",	\
651*2f6d9009SNamhyung Kim 			  __hpp__slsmg_color_printf, true);		\
6525aed9d24SNamhyung Kim }
653f5951d56SNamhyung Kim 
654*2f6d9009SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback)
655*2f6d9009SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback)
656*2f6d9009SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback)
657*2f6d9009SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback)
658*2f6d9009SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback)
6595aed9d24SNamhyung Kim 
6605aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
661f5951d56SNamhyung Kim 
662f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
663f5951d56SNamhyung Kim {
6641d77822eSJiri Olsa 	perf_hpp__init();
665f5951d56SNamhyung Kim 
666f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
667f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
668f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
669f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
670f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
671f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
672f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
673f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
674f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
675f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
676f5951d56SNamhyung Kim }
677f5951d56SNamhyung Kim 
67805e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
679aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
680aca7a94dSNamhyung Kim 				    unsigned short row)
681aca7a94dSNamhyung Kim {
682aca7a94dSNamhyung Kim 	char s[256];
6831240005eSJiri Olsa 	int printed = 0;
68467d25916SNamhyung Kim 	int width = browser->b.width;
685aca7a94dSNamhyung Kim 	char folded_sign = ' ';
68605e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
687aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
68863a1a3d8SNamhyung Kim 	bool first = true;
6891240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
690aca7a94dSNamhyung Kim 
691aca7a94dSNamhyung Kim 	if (current_entry) {
69205e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
69305e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
694aca7a94dSNamhyung Kim 	}
695aca7a94dSNamhyung Kim 
696aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
697aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
698aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
699aca7a94dSNamhyung Kim 	}
700aca7a94dSNamhyung Kim 
701aca7a94dSNamhyung Kim 	if (row_offset == 0) {
70289701460SNamhyung Kim 		struct hpp_arg arg = {
70389701460SNamhyung Kim 			.b 		= &browser->b,
70489701460SNamhyung Kim 			.folded_sign	= folded_sign,
70589701460SNamhyung Kim 			.current_entry	= current_entry,
70689701460SNamhyung Kim 		};
707f5951d56SNamhyung Kim 		struct perf_hpp hpp = {
708f5951d56SNamhyung Kim 			.buf		= s,
709f5951d56SNamhyung Kim 			.size		= sizeof(s),
71089701460SNamhyung Kim 			.ptr		= &arg,
711f5951d56SNamhyung Kim 		};
712f5951d56SNamhyung Kim 
71367d25916SNamhyung Kim 		ui_browser__gotorc(&browser->b, row, 0);
714f5951d56SNamhyung Kim 
7151240005eSJiri Olsa 		perf_hpp__for_each_format(fmt) {
71663a1a3d8SNamhyung Kim 			if (!first) {
717f5951d56SNamhyung Kim 				slsmg_printf("  ");
718f5951d56SNamhyung Kim 				width -= 2;
719f5951d56SNamhyung Kim 			}
72063a1a3d8SNamhyung Kim 			first = false;
721f5951d56SNamhyung Kim 
7221240005eSJiri Olsa 			if (fmt->color) {
7232c5d4b4aSJiri Olsa 				width -= fmt->color(fmt, &hpp, entry);
724f5951d56SNamhyung Kim 			} else {
7252c5d4b4aSJiri Olsa 				width -= fmt->entry(fmt, &hpp, entry);
726f5951d56SNamhyung Kim 				slsmg_printf("%s", s);
727f5951d56SNamhyung Kim 			}
728f5951d56SNamhyung Kim 		}
729aca7a94dSNamhyung Kim 
730aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
73105e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
732aca7a94dSNamhyung Kim 			width += 1;
733aca7a94dSNamhyung Kim 
734f5951d56SNamhyung Kim 		hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
735aca7a94dSNamhyung Kim 		slsmg_write_nstring(s, width);
736aca7a94dSNamhyung Kim 		++row;
737aca7a94dSNamhyung Kim 		++printed;
738aca7a94dSNamhyung Kim 	} else
739aca7a94dSNamhyung Kim 		--row_offset;
740aca7a94dSNamhyung Kim 
74105e8b080SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.height) {
74205e8b080SArnaldo Carvalho de Melo 		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
743aca7a94dSNamhyung Kim 							1, row, &row_offset,
744aca7a94dSNamhyung Kim 							&current_entry);
745aca7a94dSNamhyung Kim 		if (current_entry)
74605e8b080SArnaldo Carvalho de Melo 			browser->he_selection = entry;
747aca7a94dSNamhyung Kim 	}
748aca7a94dSNamhyung Kim 
749aca7a94dSNamhyung Kim 	return printed;
750aca7a94dSNamhyung Kim }
751aca7a94dSNamhyung Kim 
752aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
753aca7a94dSNamhyung Kim {
754aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
755aca7a94dSNamhyung Kim 		struct hist_browser *hb;
756aca7a94dSNamhyung Kim 
757aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
758aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
759aca7a94dSNamhyung Kim 	}
760aca7a94dSNamhyung Kim }
761aca7a94dSNamhyung Kim 
76205e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
763aca7a94dSNamhyung Kim {
764aca7a94dSNamhyung Kim 	unsigned row = 0;
765aca7a94dSNamhyung Kim 	struct rb_node *nd;
76605e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
767aca7a94dSNamhyung Kim 
76805e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
769aca7a94dSNamhyung Kim 
77005e8b080SArnaldo Carvalho de Melo 	for (nd = browser->top; nd; nd = rb_next(nd)) {
771aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
772064f1981SNamhyung Kim 		float percent = h->stat.period * 100.0 /
773064f1981SNamhyung Kim 					hb->hists->stats.total_period;
774aca7a94dSNamhyung Kim 
775aca7a94dSNamhyung Kim 		if (h->filtered)
776aca7a94dSNamhyung Kim 			continue;
777aca7a94dSNamhyung Kim 
778064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
779064f1981SNamhyung Kim 			continue;
780064f1981SNamhyung Kim 
781aca7a94dSNamhyung Kim 		row += hist_browser__show_entry(hb, h, row);
78205e8b080SArnaldo Carvalho de Melo 		if (row == browser->height)
783aca7a94dSNamhyung Kim 			break;
784aca7a94dSNamhyung Kim 	}
785aca7a94dSNamhyung Kim 
786aca7a94dSNamhyung Kim 	return row;
787aca7a94dSNamhyung Kim }
788aca7a94dSNamhyung Kim 
789064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
790064f1981SNamhyung Kim 					     struct hists *hists,
791064f1981SNamhyung Kim 					     float min_pcnt)
792aca7a94dSNamhyung Kim {
793aca7a94dSNamhyung Kim 	while (nd != NULL) {
794aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
795064f1981SNamhyung Kim 		float percent = h->stat.period * 100.0 /
796064f1981SNamhyung Kim 					hists->stats.total_period;
797064f1981SNamhyung Kim 
798064f1981SNamhyung Kim 		if (percent < min_pcnt)
799064f1981SNamhyung Kim 			return NULL;
800064f1981SNamhyung Kim 
801aca7a94dSNamhyung Kim 		if (!h->filtered)
802aca7a94dSNamhyung Kim 			return nd;
803aca7a94dSNamhyung Kim 
804aca7a94dSNamhyung Kim 		nd = rb_next(nd);
805aca7a94dSNamhyung Kim 	}
806aca7a94dSNamhyung Kim 
807aca7a94dSNamhyung Kim 	return NULL;
808aca7a94dSNamhyung Kim }
809aca7a94dSNamhyung Kim 
810064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
811064f1981SNamhyung Kim 						  struct hists *hists,
812064f1981SNamhyung Kim 						  float min_pcnt)
813aca7a94dSNamhyung Kim {
814aca7a94dSNamhyung Kim 	while (nd != NULL) {
815aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
816064f1981SNamhyung Kim 		float percent = h->stat.period * 100.0 /
817064f1981SNamhyung Kim 					hists->stats.total_period;
818064f1981SNamhyung Kim 
819064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
820aca7a94dSNamhyung Kim 			return nd;
821aca7a94dSNamhyung Kim 
822aca7a94dSNamhyung Kim 		nd = rb_prev(nd);
823aca7a94dSNamhyung Kim 	}
824aca7a94dSNamhyung Kim 
825aca7a94dSNamhyung Kim 	return NULL;
826aca7a94dSNamhyung Kim }
827aca7a94dSNamhyung Kim 
82805e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
829aca7a94dSNamhyung Kim 				   off_t offset, int whence)
830aca7a94dSNamhyung Kim {
831aca7a94dSNamhyung Kim 	struct hist_entry *h;
832aca7a94dSNamhyung Kim 	struct rb_node *nd;
833aca7a94dSNamhyung Kim 	bool first = true;
834064f1981SNamhyung Kim 	struct hist_browser *hb;
835064f1981SNamhyung Kim 
836064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
837aca7a94dSNamhyung Kim 
83805e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
839aca7a94dSNamhyung Kim 		return;
840aca7a94dSNamhyung Kim 
84105e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
842aca7a94dSNamhyung Kim 
843aca7a94dSNamhyung Kim 	switch (whence) {
844aca7a94dSNamhyung Kim 	case SEEK_SET:
845064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
846064f1981SNamhyung Kim 					   hb->hists, hb->min_pcnt);
847aca7a94dSNamhyung Kim 		break;
848aca7a94dSNamhyung Kim 	case SEEK_CUR:
84905e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
850aca7a94dSNamhyung Kim 		goto do_offset;
851aca7a94dSNamhyung Kim 	case SEEK_END:
852064f1981SNamhyung Kim 		nd = hists__filter_prev_entries(rb_last(browser->entries),
853064f1981SNamhyung Kim 						hb->hists, hb->min_pcnt);
854aca7a94dSNamhyung Kim 		first = false;
855aca7a94dSNamhyung Kim 		break;
856aca7a94dSNamhyung Kim 	default:
857aca7a94dSNamhyung Kim 		return;
858aca7a94dSNamhyung Kim 	}
859aca7a94dSNamhyung Kim 
860aca7a94dSNamhyung Kim 	/*
861aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
862aca7a94dSNamhyung Kim 	 * row_offset:
863aca7a94dSNamhyung Kim 	 */
86405e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
865aca7a94dSNamhyung Kim 	h->row_offset = 0;
866aca7a94dSNamhyung Kim 
867aca7a94dSNamhyung Kim 	/*
868aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
869aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
870aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
871aca7a94dSNamhyung Kim 	 *
872aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
873aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
874aca7a94dSNamhyung Kim 	 *
875aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
876aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
877aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
878aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
879aca7a94dSNamhyung Kim 	 */
880aca7a94dSNamhyung Kim do_offset:
881aca7a94dSNamhyung Kim 	if (offset > 0) {
882aca7a94dSNamhyung Kim 		do {
883aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
884aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
885aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
886aca7a94dSNamhyung Kim 				if (offset > remaining) {
887aca7a94dSNamhyung Kim 					offset -= remaining;
888aca7a94dSNamhyung Kim 					h->row_offset = 0;
889aca7a94dSNamhyung Kim 				} else {
890aca7a94dSNamhyung Kim 					h->row_offset += offset;
891aca7a94dSNamhyung Kim 					offset = 0;
89205e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
893aca7a94dSNamhyung Kim 					break;
894aca7a94dSNamhyung Kim 				}
895aca7a94dSNamhyung Kim 			}
896064f1981SNamhyung Kim 			nd = hists__filter_entries(rb_next(nd), hb->hists,
897064f1981SNamhyung Kim 						   hb->min_pcnt);
898aca7a94dSNamhyung Kim 			if (nd == NULL)
899aca7a94dSNamhyung Kim 				break;
900aca7a94dSNamhyung Kim 			--offset;
90105e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
902aca7a94dSNamhyung Kim 		} while (offset != 0);
903aca7a94dSNamhyung Kim 	} else if (offset < 0) {
904aca7a94dSNamhyung Kim 		while (1) {
905aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
906aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
907aca7a94dSNamhyung Kim 				if (first) {
908aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
909aca7a94dSNamhyung Kim 						offset += h->row_offset;
910aca7a94dSNamhyung Kim 						h->row_offset = 0;
911aca7a94dSNamhyung Kim 					} else {
912aca7a94dSNamhyung Kim 						h->row_offset += offset;
913aca7a94dSNamhyung Kim 						offset = 0;
91405e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
915aca7a94dSNamhyung Kim 						break;
916aca7a94dSNamhyung Kim 					}
917aca7a94dSNamhyung Kim 				} else {
918aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
919aca7a94dSNamhyung Kim 						offset += h->nr_rows;
920aca7a94dSNamhyung Kim 						h->row_offset = 0;
921aca7a94dSNamhyung Kim 					} else {
922aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
923aca7a94dSNamhyung Kim 						offset = 0;
92405e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
925aca7a94dSNamhyung Kim 						break;
926aca7a94dSNamhyung Kim 					}
927aca7a94dSNamhyung Kim 				}
928aca7a94dSNamhyung Kim 			}
929aca7a94dSNamhyung Kim 
930064f1981SNamhyung Kim 			nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
931064f1981SNamhyung Kim 							hb->min_pcnt);
932aca7a94dSNamhyung Kim 			if (nd == NULL)
933aca7a94dSNamhyung Kim 				break;
934aca7a94dSNamhyung Kim 			++offset;
93505e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
936aca7a94dSNamhyung Kim 			if (offset == 0) {
937aca7a94dSNamhyung Kim 				/*
938aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
939aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
940aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
941aca7a94dSNamhyung Kim 				 */
942aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
943aca7a94dSNamhyung Kim 				if (h->ms.unfolded)
944aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
945aca7a94dSNamhyung Kim 				break;
946aca7a94dSNamhyung Kim 			}
947aca7a94dSNamhyung Kim 			first = false;
948aca7a94dSNamhyung Kim 		}
949aca7a94dSNamhyung Kim 	} else {
95005e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
951aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
952aca7a94dSNamhyung Kim 		h->row_offset = 0;
953aca7a94dSNamhyung Kim 	}
954aca7a94dSNamhyung Kim }
955aca7a94dSNamhyung Kim 
956aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
957aff3f3f6SArnaldo Carvalho de Melo 							struct callchain_node *chain_node,
958aff3f3f6SArnaldo Carvalho de Melo 							u64 total, int level,
959aff3f3f6SArnaldo Carvalho de Melo 							FILE *fp)
960aff3f3f6SArnaldo Carvalho de Melo {
961aff3f3f6SArnaldo Carvalho de Melo 	struct rb_node *node;
962aff3f3f6SArnaldo Carvalho de Melo 	int offset = level * LEVEL_OFFSET_STEP;
963aff3f3f6SArnaldo Carvalho de Melo 	u64 new_total, remaining;
964aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
965aff3f3f6SArnaldo Carvalho de Melo 
966aff3f3f6SArnaldo Carvalho de Melo 	if (callchain_param.mode == CHAIN_GRAPH_REL)
967aff3f3f6SArnaldo Carvalho de Melo 		new_total = chain_node->children_hit;
968aff3f3f6SArnaldo Carvalho de Melo 	else
969aff3f3f6SArnaldo Carvalho de Melo 		new_total = total;
970aff3f3f6SArnaldo Carvalho de Melo 
971aff3f3f6SArnaldo Carvalho de Melo 	remaining = new_total;
972aff3f3f6SArnaldo Carvalho de Melo 	node = rb_first(&chain_node->rb_root);
973aff3f3f6SArnaldo Carvalho de Melo 	while (node) {
974aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
975aff3f3f6SArnaldo Carvalho de Melo 		struct rb_node *next = rb_next(node);
976aff3f3f6SArnaldo Carvalho de Melo 		u64 cumul = callchain_cumul_hits(child);
977aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_list *chain;
978aff3f3f6SArnaldo Carvalho de Melo 		char folded_sign = ' ';
979aff3f3f6SArnaldo Carvalho de Melo 		int first = true;
980aff3f3f6SArnaldo Carvalho de Melo 		int extra_offset = 0;
981aff3f3f6SArnaldo Carvalho de Melo 
982aff3f3f6SArnaldo Carvalho de Melo 		remaining -= cumul;
983aff3f3f6SArnaldo Carvalho de Melo 
984aff3f3f6SArnaldo Carvalho de Melo 		list_for_each_entry(chain, &child->val, list) {
985a7cb8863SArnaldo Carvalho de Melo 			char bf[1024], *alloc_str;
986aff3f3f6SArnaldo Carvalho de Melo 			const char *str;
987aff3f3f6SArnaldo Carvalho de Melo 			bool was_first = first;
988aff3f3f6SArnaldo Carvalho de Melo 
989aff3f3f6SArnaldo Carvalho de Melo 			if (first)
990aff3f3f6SArnaldo Carvalho de Melo 				first = false;
991aff3f3f6SArnaldo Carvalho de Melo 			else
992aff3f3f6SArnaldo Carvalho de Melo 				extra_offset = LEVEL_OFFSET_STEP;
993aff3f3f6SArnaldo Carvalho de Melo 
994aff3f3f6SArnaldo Carvalho de Melo 			folded_sign = callchain_list__folded(chain);
995aff3f3f6SArnaldo Carvalho de Melo 
996aff3f3f6SArnaldo Carvalho de Melo 			alloc_str = NULL;
997a7cb8863SArnaldo Carvalho de Melo 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
998a7cb8863SArnaldo Carvalho de Melo 						       browser->show_dso);
999aff3f3f6SArnaldo Carvalho de Melo 			if (was_first) {
1000aff3f3f6SArnaldo Carvalho de Melo 				double percent = cumul * 100.0 / new_total;
1001aff3f3f6SArnaldo Carvalho de Melo 
1002aff3f3f6SArnaldo Carvalho de Melo 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1003aff3f3f6SArnaldo Carvalho de Melo 					str = "Not enough memory!";
1004aff3f3f6SArnaldo Carvalho de Melo 				else
1005aff3f3f6SArnaldo Carvalho de Melo 					str = alloc_str;
1006aff3f3f6SArnaldo Carvalho de Melo 			}
1007aff3f3f6SArnaldo Carvalho de Melo 
1008aff3f3f6SArnaldo Carvalho de Melo 			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1009aff3f3f6SArnaldo Carvalho de Melo 			free(alloc_str);
1010aff3f3f6SArnaldo Carvalho de Melo 			if (folded_sign == '+')
1011aff3f3f6SArnaldo Carvalho de Melo 				break;
1012aff3f3f6SArnaldo Carvalho de Melo 		}
1013aff3f3f6SArnaldo Carvalho de Melo 
1014aff3f3f6SArnaldo Carvalho de Melo 		if (folded_sign == '-') {
1015aff3f3f6SArnaldo Carvalho de Melo 			const int new_level = level + (extra_offset ? 2 : 1);
1016aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1017aff3f3f6SArnaldo Carvalho de Melo 										new_level, fp);
1018aff3f3f6SArnaldo Carvalho de Melo 		}
1019aff3f3f6SArnaldo Carvalho de Melo 
1020aff3f3f6SArnaldo Carvalho de Melo 		node = next;
1021aff3f3f6SArnaldo Carvalho de Melo 	}
1022aff3f3f6SArnaldo Carvalho de Melo 
1023aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1024aff3f3f6SArnaldo Carvalho de Melo }
1025aff3f3f6SArnaldo Carvalho de Melo 
1026aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1027aff3f3f6SArnaldo Carvalho de Melo 						struct callchain_node *node,
1028aff3f3f6SArnaldo Carvalho de Melo 						int level, FILE *fp)
1029aff3f3f6SArnaldo Carvalho de Melo {
1030aff3f3f6SArnaldo Carvalho de Melo 	struct callchain_list *chain;
1031aff3f3f6SArnaldo Carvalho de Melo 	int offset = level * LEVEL_OFFSET_STEP;
1032aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
1033aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1034aff3f3f6SArnaldo Carvalho de Melo 
1035aff3f3f6SArnaldo Carvalho de Melo 	list_for_each_entry(chain, &node->val, list) {
1036a7cb8863SArnaldo Carvalho de Melo 		char bf[1024], *s;
1037aff3f3f6SArnaldo Carvalho de Melo 
1038aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = callchain_list__folded(chain);
1039a7cb8863SArnaldo Carvalho de Melo 		s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1040aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1041aff3f3f6SArnaldo Carvalho de Melo 	}
1042aff3f3f6SArnaldo Carvalho de Melo 
1043aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1044aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1045aff3f3f6SArnaldo Carvalho de Melo 									browser->hists->stats.total_period,
1046aff3f3f6SArnaldo Carvalho de Melo 									level + 1,  fp);
1047aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1048aff3f3f6SArnaldo Carvalho de Melo }
1049aff3f3f6SArnaldo Carvalho de Melo 
1050aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1051aff3f3f6SArnaldo Carvalho de Melo 					   struct rb_root *chain, int level, FILE *fp)
1052aff3f3f6SArnaldo Carvalho de Melo {
1053aff3f3f6SArnaldo Carvalho de Melo 	struct rb_node *nd;
1054aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1055aff3f3f6SArnaldo Carvalho de Melo 
1056aff3f3f6SArnaldo Carvalho de Melo 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1057aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1058aff3f3f6SArnaldo Carvalho de Melo 
1059aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1060aff3f3f6SArnaldo Carvalho de Melo 	}
1061aff3f3f6SArnaldo Carvalho de Melo 
1062aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1063aff3f3f6SArnaldo Carvalho de Melo }
1064aff3f3f6SArnaldo Carvalho de Melo 
1065aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1066aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1067aff3f3f6SArnaldo Carvalho de Melo {
1068aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1069aff3f3f6SArnaldo Carvalho de Melo 	double percent;
1070aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1071aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
1072aff3f3f6SArnaldo Carvalho de Melo 
1073aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1074aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1075aff3f3f6SArnaldo Carvalho de Melo 
1076000078bcSNamhyung Kim 	hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1077b24c28f7SNamhyung Kim 	percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1078aff3f3f6SArnaldo Carvalho de Melo 
1079aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1080aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
1081aff3f3f6SArnaldo Carvalho de Melo 
1082aff3f3f6SArnaldo Carvalho de Melo 	printed += fprintf(fp, " %5.2f%%", percent);
1083aff3f3f6SArnaldo Carvalho de Melo 
1084aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.show_nr_samples)
1085b24c28f7SNamhyung Kim 		printed += fprintf(fp, " %11u", he->stat.nr_events);
1086aff3f3f6SArnaldo Carvalho de Melo 
1087aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.show_total_period)
1088b24c28f7SNamhyung Kim 		printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1089aff3f3f6SArnaldo Carvalho de Melo 
1090aff3f3f6SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", rtrim(s));
1091aff3f3f6SArnaldo Carvalho de Melo 
1092aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1093aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1094aff3f3f6SArnaldo Carvalho de Melo 
1095aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1096aff3f3f6SArnaldo Carvalho de Melo }
1097aff3f3f6SArnaldo Carvalho de Melo 
1098aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1099aff3f3f6SArnaldo Carvalho de Melo {
1100064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1101064f1981SNamhyung Kim 						   browser->hists,
1102064f1981SNamhyung Kim 						   browser->min_pcnt);
1103aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1104aff3f3f6SArnaldo Carvalho de Melo 
1105aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1106aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1107aff3f3f6SArnaldo Carvalho de Melo 
1108aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_entry(browser, h, fp);
1109064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), browser->hists,
1110064f1981SNamhyung Kim 					   browser->min_pcnt);
1111aff3f3f6SArnaldo Carvalho de Melo 	}
1112aff3f3f6SArnaldo Carvalho de Melo 
1113aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1114aff3f3f6SArnaldo Carvalho de Melo }
1115aff3f3f6SArnaldo Carvalho de Melo 
1116aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
1117aff3f3f6SArnaldo Carvalho de Melo {
1118aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
1119aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
1120aff3f3f6SArnaldo Carvalho de Melo 
1121aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
1122aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1123aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
1124aff3f3f6SArnaldo Carvalho de Melo 			break;
1125aff3f3f6SArnaldo Carvalho de Melo 		/*
1126aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
1127aff3f3f6SArnaldo Carvalho de Melo  		 */
1128aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
1129aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1130aff3f3f6SArnaldo Carvalho de Melo 			return -1;
1131aff3f3f6SArnaldo Carvalho de Melo 		}
1132aff3f3f6SArnaldo Carvalho de Melo 	}
1133aff3f3f6SArnaldo Carvalho de Melo 
1134aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
1135aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
1136aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
11374cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
11384cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1139aff3f3f6SArnaldo Carvalho de Melo 		return -1;
1140aff3f3f6SArnaldo Carvalho de Melo 	}
1141aff3f3f6SArnaldo Carvalho de Melo 
1142aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
1143aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
1144aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
1145aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
1146aff3f3f6SArnaldo Carvalho de Melo 
1147aff3f3f6SArnaldo Carvalho de Melo 	return 0;
1148aff3f3f6SArnaldo Carvalho de Melo }
1149aff3f3f6SArnaldo Carvalho de Melo 
1150aca7a94dSNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists)
1151aca7a94dSNamhyung Kim {
115205e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
1153aca7a94dSNamhyung Kim 
115405e8b080SArnaldo Carvalho de Melo 	if (browser) {
115505e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
115605e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
115705e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
115805e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
1159aca7a94dSNamhyung Kim 	}
1160aca7a94dSNamhyung Kim 
116105e8b080SArnaldo Carvalho de Melo 	return browser;
1162aca7a94dSNamhyung Kim }
1163aca7a94dSNamhyung Kim 
116405e8b080SArnaldo Carvalho de Melo static void hist_browser__delete(struct hist_browser *browser)
1165aca7a94dSNamhyung Kim {
116605e8b080SArnaldo Carvalho de Melo 	free(browser);
1167aca7a94dSNamhyung Kim }
1168aca7a94dSNamhyung Kim 
116905e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1170aca7a94dSNamhyung Kim {
117105e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
1172aca7a94dSNamhyung Kim }
1173aca7a94dSNamhyung Kim 
117405e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1175aca7a94dSNamhyung Kim {
117605e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
1177aca7a94dSNamhyung Kim }
1178aca7a94dSNamhyung Kim 
117905e8b080SArnaldo Carvalho de Melo static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1180aca7a94dSNamhyung Kim 				const char *ev_name)
1181aca7a94dSNamhyung Kim {
1182aca7a94dSNamhyung Kim 	char unit;
1183aca7a94dSNamhyung Kim 	int printed;
118405e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
118505e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
118605e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
118705e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
1188717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
1189717e263fSNamhyung Kim 	char buf[512];
1190717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
1191717e263fSNamhyung Kim 
1192759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1193717e263fSNamhyung Kim 		struct perf_evsel *pos;
1194717e263fSNamhyung Kim 
1195717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
1196717e263fSNamhyung Kim 		ev_name = buf;
1197717e263fSNamhyung Kim 
1198717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
1199717e263fSNamhyung Kim 			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1200717e263fSNamhyung Kim 			nr_events += pos->hists.stats.total_period;
1201717e263fSNamhyung Kim 		}
1202717e263fSNamhyung Kim 	}
1203aca7a94dSNamhyung Kim 
1204aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
1205aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
1206aca7a94dSNamhyung Kim 			   "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1207aca7a94dSNamhyung Kim 			   nr_samples, unit, ev_name, nr_events);
1208aca7a94dSNamhyung Kim 
1209aca7a94dSNamhyung Kim 
121005e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
1211aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
121205e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
1213aca7a94dSNamhyung Kim 	if (thread)
1214aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1215aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
1216b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
121738051234SAdrian Hunter 				    thread->tid);
1218aca7a94dSNamhyung Kim 	if (dso)
1219aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1220aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
1221aca7a94dSNamhyung Kim 	return printed;
1222aca7a94dSNamhyung Kim }
1223aca7a94dSNamhyung Kim 
1224aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
1225aca7a94dSNamhyung Kim {
1226aca7a94dSNamhyung Kim 	int i;
1227aca7a94dSNamhyung Kim 
122804662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
122904662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
1230aca7a94dSNamhyung Kim }
1231aca7a94dSNamhyung Kim 
1232c77d8d70SFeng Tang /* Check whether the browser is for 'top' or 'report' */
1233c77d8d70SFeng Tang static inline bool is_report_browser(void *timer)
1234c77d8d70SFeng Tang {
1235c77d8d70SFeng Tang 	return timer == NULL;
1236c77d8d70SFeng Tang }
1237c77d8d70SFeng Tang 
1238341487abSFeng Tang /*
1239341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
1240341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1241341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
1242341487abSFeng Tang  */
1243341487abSFeng Tang static bool is_input_name_malloced = false;
1244341487abSFeng Tang 
1245341487abSFeng Tang static int switch_data_file(void)
1246341487abSFeng Tang {
1247341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
1248341487abSFeng Tang 	DIR *pwd_dir;
1249341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
1250341487abSFeng Tang 	struct dirent *dent;
1251341487abSFeng Tang 
1252341487abSFeng Tang 	pwd = getenv("PWD");
1253341487abSFeng Tang 	if (!pwd)
1254341487abSFeng Tang 		return ret;
1255341487abSFeng Tang 
1256341487abSFeng Tang 	pwd_dir = opendir(pwd);
1257341487abSFeng Tang 	if (!pwd_dir)
1258341487abSFeng Tang 		return ret;
1259341487abSFeng Tang 
1260341487abSFeng Tang 	memset(options, 0, sizeof(options));
1261341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
1262341487abSFeng Tang 
1263341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
1264341487abSFeng Tang 		char path[PATH_MAX];
1265341487abSFeng Tang 		u64 magic;
1266341487abSFeng Tang 		char *name = dent->d_name;
1267341487abSFeng Tang 		FILE *file;
1268341487abSFeng Tang 
1269341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
1270341487abSFeng Tang 			continue;
1271341487abSFeng Tang 
1272341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1273341487abSFeng Tang 
1274341487abSFeng Tang 		file = fopen(path, "r");
1275341487abSFeng Tang 		if (!file)
1276341487abSFeng Tang 			continue;
1277341487abSFeng Tang 
1278341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
1279341487abSFeng Tang 			goto close_file_and_continue;
1280341487abSFeng Tang 
1281341487abSFeng Tang 		if (is_perf_magic(magic)) {
1282341487abSFeng Tang 			options[nr_options] = strdup(name);
1283341487abSFeng Tang 			if (!options[nr_options])
1284341487abSFeng Tang 				goto close_file_and_continue;
1285341487abSFeng Tang 
1286341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
1287341487abSFeng Tang 			if (!abs_path[nr_options]) {
128874cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
1289341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
1290341487abSFeng Tang 				fclose(file);
1291341487abSFeng Tang 				break;
1292341487abSFeng Tang 			}
1293341487abSFeng Tang 
1294341487abSFeng Tang 			nr_options++;
1295341487abSFeng Tang 		}
1296341487abSFeng Tang 
1297341487abSFeng Tang close_file_and_continue:
1298341487abSFeng Tang 		fclose(file);
1299341487abSFeng Tang 		if (nr_options >= 32) {
1300341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
1301341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
1302341487abSFeng Tang 			break;
1303341487abSFeng Tang 		}
1304341487abSFeng Tang 	}
1305341487abSFeng Tang 	closedir(pwd_dir);
1306341487abSFeng Tang 
1307341487abSFeng Tang 	if (nr_options) {
1308341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
1309341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
1310341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
1311341487abSFeng Tang 			if (tmp) {
1312341487abSFeng Tang 				if (is_input_name_malloced)
1313341487abSFeng Tang 					free((void *)input_name);
1314341487abSFeng Tang 				input_name = tmp;
1315341487abSFeng Tang 				is_input_name_malloced = true;
1316341487abSFeng Tang 				ret = 0;
1317341487abSFeng Tang 			} else
1318341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
1319341487abSFeng Tang 		}
1320341487abSFeng Tang 	}
1321341487abSFeng Tang 
1322341487abSFeng Tang 	free_popup_options(options, nr_options);
1323341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
1324341487abSFeng Tang 	return ret;
1325341487abSFeng Tang }
1326341487abSFeng Tang 
1327064f1981SNamhyung Kim static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1328064f1981SNamhyung Kim {
1329064f1981SNamhyung Kim 	u64 nr_entries = 0;
1330064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
1331064f1981SNamhyung Kim 
1332064f1981SNamhyung Kim 	while (nd) {
1333064f1981SNamhyung Kim 		nr_entries++;
1334064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), hb->hists,
1335064f1981SNamhyung Kim 					   hb->min_pcnt);
1336064f1981SNamhyung Kim 	}
1337064f1981SNamhyung Kim 
1338064f1981SNamhyung Kim 	hb->nr_pcnt_entries = nr_entries;
1339064f1981SNamhyung Kim }
1340341487abSFeng Tang 
1341aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1342aca7a94dSNamhyung Kim 				    const char *helpline, const char *ev_name,
1343aca7a94dSNamhyung Kim 				    bool left_exits,
134468d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
1345064f1981SNamhyung Kim 				    float min_pcnt,
134668d80758SNamhyung Kim 				    struct perf_session_env *env)
1347aca7a94dSNamhyung Kim {
134805e8b080SArnaldo Carvalho de Melo 	struct hists *hists = &evsel->hists;
134905e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = hist_browser__new(hists);
1350aca7a94dSNamhyung Kim 	struct branch_info *bi;
1351aca7a94dSNamhyung Kim 	struct pstack *fstack;
1352aca7a94dSNamhyung Kim 	char *options[16];
1353aca7a94dSNamhyung Kim 	int nr_options = 0;
1354aca7a94dSNamhyung Kim 	int key = -1;
1355aca7a94dSNamhyung Kim 	char buf[64];
1356cdbab7c2SFeng Tang 	char script_opt[64];
13579783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
1358aca7a94dSNamhyung Kim 
1359e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
1360e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
1361e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
1362e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
1363e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1364e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
1365e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
1366e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
1367e8e684a5SNamhyung Kim 	"->            Zoom into DSO/Threads & Annotate current symbol\n" \
1368e8e684a5SNamhyung Kim 	"<-            Zoom out\n"					\
1369e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
1370e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
1371e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
1372e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
1373e8e684a5SNamhyung Kim 
1374e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
1375e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
13766dd60135SNamhyung Kim 	"i             Show header information\n"
1377e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1378e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
1379e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
1380e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1381e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
1382e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1383e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1384e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1385e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1386e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
1387e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1388e8e684a5SNamhyung Kim 
1389aca7a94dSNamhyung Kim 	if (browser == NULL)
1390aca7a94dSNamhyung Kim 		return -1;
1391aca7a94dSNamhyung Kim 
1392064f1981SNamhyung Kim 	if (min_pcnt) {
1393064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
1394064f1981SNamhyung Kim 		hist_browser__update_pcnt_entries(browser);
1395064f1981SNamhyung Kim 	}
1396064f1981SNamhyung Kim 
1397aca7a94dSNamhyung Kim 	fstack = pstack__new(2);
1398aca7a94dSNamhyung Kim 	if (fstack == NULL)
1399aca7a94dSNamhyung Kim 		goto out;
1400aca7a94dSNamhyung Kim 
1401aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
1402aca7a94dSNamhyung Kim 
1403aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
1404aca7a94dSNamhyung Kim 
1405aca7a94dSNamhyung Kim 	while (1) {
1406aca7a94dSNamhyung Kim 		const struct thread *thread = NULL;
1407aca7a94dSNamhyung Kim 		const struct dso *dso = NULL;
1408aca7a94dSNamhyung Kim 		int choice = 0,
1409aca7a94dSNamhyung Kim 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1410aca7a94dSNamhyung Kim 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1411341487abSFeng Tang 		int scripts_comm = -2, scripts_symbol = -2,
1412341487abSFeng Tang 		    scripts_all = -2, switch_data = -2;
1413aca7a94dSNamhyung Kim 
1414aca7a94dSNamhyung Kim 		nr_options = 0;
1415aca7a94dSNamhyung Kim 
14169783adf7SNamhyung Kim 		key = hist_browser__run(browser, ev_name, hbt);
1417aca7a94dSNamhyung Kim 
1418aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
1419aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
1420aca7a94dSNamhyung Kim 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1421aca7a94dSNamhyung Kim 		}
1422aca7a94dSNamhyung Kim 		switch (key) {
1423aca7a94dSNamhyung Kim 		case K_TAB:
1424aca7a94dSNamhyung Kim 		case K_UNTAB:
1425aca7a94dSNamhyung Kim 			if (nr_events == 1)
1426aca7a94dSNamhyung Kim 				continue;
1427aca7a94dSNamhyung Kim 			/*
1428aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
1429aca7a94dSNamhyung Kim 			 * go to the next or previous
1430aca7a94dSNamhyung Kim 			 */
1431aca7a94dSNamhyung Kim 			goto out_free_stack;
1432aca7a94dSNamhyung Kim 		case 'a':
14339c796ec8SArnaldo Carvalho de Melo 			if (!sort__has_sym) {
1434aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
1435aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
1436aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
1437aca7a94dSNamhyung Kim 				continue;
1438aca7a94dSNamhyung Kim 			}
1439aca7a94dSNamhyung Kim 
1440aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
1441aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
1442aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
1443aca7a94dSNamhyung Kim 				continue;
1444aca7a94dSNamhyung Kim 			goto do_annotate;
1445aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
1446aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
1447aff3f3f6SArnaldo Carvalho de Melo 			continue;
1448aca7a94dSNamhyung Kim 		case 'd':
1449aca7a94dSNamhyung Kim 			goto zoom_dso;
1450a7cb8863SArnaldo Carvalho de Melo 		case 'V':
1451a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
1452a7cb8863SArnaldo Carvalho de Melo 			continue;
1453aca7a94dSNamhyung Kim 		case 't':
1454aca7a94dSNamhyung Kim 			goto zoom_thread;
14555a5626b1SArnaldo Carvalho de Melo 		case '/':
1456aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
1457aca7a94dSNamhyung Kim 					"Please enter the name of symbol you want to see",
1458aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
1459aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
146005e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
146105e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
1462aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
1463aca7a94dSNamhyung Kim 			}
1464aca7a94dSNamhyung Kim 			continue;
1465cdbab7c2SFeng Tang 		case 'r':
14669783adf7SNamhyung Kim 			if (is_report_browser(hbt))
1467cdbab7c2SFeng Tang 				goto do_scripts;
1468c77d8d70SFeng Tang 			continue;
1469341487abSFeng Tang 		case 's':
1470341487abSFeng Tang 			if (is_report_browser(hbt))
1471341487abSFeng Tang 				goto do_data_switch;
1472341487abSFeng Tang 			continue;
14736dd60135SNamhyung Kim 		case 'i':
14746dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
14756dd60135SNamhyung Kim 			if (env->arch)
14766dd60135SNamhyung Kim 				tui__header_window(env);
14776dd60135SNamhyung Kim 			continue;
1478aca7a94dSNamhyung Kim 		case K_F1:
1479aca7a94dSNamhyung Kim 		case 'h':
1480aca7a94dSNamhyung Kim 		case '?':
1481aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
1482e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
1483aca7a94dSNamhyung Kim 			continue;
1484aca7a94dSNamhyung Kim 		case K_ENTER:
1485aca7a94dSNamhyung Kim 		case K_RIGHT:
1486aca7a94dSNamhyung Kim 			/* menu */
1487aca7a94dSNamhyung Kim 			break;
1488aca7a94dSNamhyung Kim 		case K_LEFT: {
1489aca7a94dSNamhyung Kim 			const void *top;
1490aca7a94dSNamhyung Kim 
1491aca7a94dSNamhyung Kim 			if (pstack__empty(fstack)) {
1492aca7a94dSNamhyung Kim 				/*
1493aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
1494aca7a94dSNamhyung Kim 				 */
1495aca7a94dSNamhyung Kim 				if (left_exits)
1496aca7a94dSNamhyung Kim 					goto out_free_stack;
1497aca7a94dSNamhyung Kim 				continue;
1498aca7a94dSNamhyung Kim 			}
1499aca7a94dSNamhyung Kim 			top = pstack__pop(fstack);
1500aca7a94dSNamhyung Kim 			if (top == &browser->hists->dso_filter)
1501aca7a94dSNamhyung Kim 				goto zoom_out_dso;
1502aca7a94dSNamhyung Kim 			if (top == &browser->hists->thread_filter)
1503aca7a94dSNamhyung Kim 				goto zoom_out_thread;
1504aca7a94dSNamhyung Kim 			continue;
1505aca7a94dSNamhyung Kim 		}
1506aca7a94dSNamhyung Kim 		case K_ESC:
1507aca7a94dSNamhyung Kim 			if (!left_exits &&
1508aca7a94dSNamhyung Kim 			    !ui_browser__dialog_yesno(&browser->b,
1509aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1510aca7a94dSNamhyung Kim 				continue;
1511aca7a94dSNamhyung Kim 			/* Fall thru */
1512aca7a94dSNamhyung Kim 		case 'q':
1513aca7a94dSNamhyung Kim 		case CTRL('c'):
1514aca7a94dSNamhyung Kim 			goto out_free_stack;
1515aca7a94dSNamhyung Kim 		default:
1516aca7a94dSNamhyung Kim 			continue;
1517aca7a94dSNamhyung Kim 		}
1518aca7a94dSNamhyung Kim 
15199c796ec8SArnaldo Carvalho de Melo 		if (!sort__has_sym)
1520aca7a94dSNamhyung Kim 			goto add_exit_option;
1521aca7a94dSNamhyung Kim 
152255369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
1523aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
1524aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1525aca7a94dSNamhyung Kim 			    bi &&
1526aca7a94dSNamhyung Kim 			    bi->from.sym != NULL &&
1527aca7a94dSNamhyung Kim 			    !bi->from.map->dso->annotate_warned &&
1528aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1529aca7a94dSNamhyung Kim 					 bi->from.sym->name) > 0)
1530aca7a94dSNamhyung Kim 				annotate_f = nr_options++;
1531aca7a94dSNamhyung Kim 
1532aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1533aca7a94dSNamhyung Kim 			    bi &&
1534aca7a94dSNamhyung Kim 			    bi->to.sym != NULL &&
1535aca7a94dSNamhyung Kim 			    !bi->to.map->dso->annotate_warned &&
1536aca7a94dSNamhyung Kim 			    (bi->to.sym != bi->from.sym ||
1537aca7a94dSNamhyung Kim 			     bi->to.map->dso != bi->from.map->dso) &&
1538aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1539aca7a94dSNamhyung Kim 					 bi->to.sym->name) > 0)
1540aca7a94dSNamhyung Kim 				annotate_t = nr_options++;
1541aca7a94dSNamhyung Kim 		} else {
1542aca7a94dSNamhyung Kim 
1543aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1544aca7a94dSNamhyung Kim 			    browser->selection->sym != NULL &&
1545aca7a94dSNamhyung Kim 			    !browser->selection->map->dso->annotate_warned &&
1546aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1547aca7a94dSNamhyung Kim 					 browser->selection->sym->name) > 0)
1548aca7a94dSNamhyung Kim 				annotate = nr_options++;
1549aca7a94dSNamhyung Kim 		}
1550aca7a94dSNamhyung Kim 
1551aca7a94dSNamhyung Kim 		if (thread != NULL &&
1552aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1553aca7a94dSNamhyung Kim 			     (browser->hists->thread_filter ? "out of" : "into"),
1554b9c5143aSFrederic Weisbecker 			     (thread->comm_set ? thread__comm_str(thread) : ""),
155538051234SAdrian Hunter 			     thread->tid) > 0)
1556aca7a94dSNamhyung Kim 			zoom_thread = nr_options++;
1557aca7a94dSNamhyung Kim 
1558aca7a94dSNamhyung Kim 		if (dso != NULL &&
1559aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1560aca7a94dSNamhyung Kim 			     (browser->hists->dso_filter ? "out of" : "into"),
1561aca7a94dSNamhyung Kim 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1562aca7a94dSNamhyung Kim 			zoom_dso = nr_options++;
1563aca7a94dSNamhyung Kim 
1564aca7a94dSNamhyung Kim 		if (browser->selection != NULL &&
1565aca7a94dSNamhyung Kim 		    browser->selection->map != NULL &&
1566aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Browse map details") > 0)
1567aca7a94dSNamhyung Kim 			browse_map = nr_options++;
1568cdbab7c2SFeng Tang 
1569cdbab7c2SFeng Tang 		/* perf script support */
1570cdbab7c2SFeng Tang 		if (browser->he_selection) {
1571cdbab7c2SFeng Tang 			struct symbol *sym;
1572cdbab7c2SFeng Tang 
1573cdbab7c2SFeng Tang 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1574b9c5143aSFrederic Weisbecker 				     thread__comm_str(browser->he_selection->thread)) > 0)
1575cdbab7c2SFeng Tang 				scripts_comm = nr_options++;
1576cdbab7c2SFeng Tang 
1577cdbab7c2SFeng Tang 			sym = browser->he_selection->ms.sym;
1578cdbab7c2SFeng Tang 			if (sym && sym->namelen &&
1579cdbab7c2SFeng Tang 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1580cdbab7c2SFeng Tang 						sym->name) > 0)
1581cdbab7c2SFeng Tang 				scripts_symbol = nr_options++;
1582cdbab7c2SFeng Tang 		}
1583cdbab7c2SFeng Tang 
1584cdbab7c2SFeng Tang 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1585cdbab7c2SFeng Tang 			scripts_all = nr_options++;
1586cdbab7c2SFeng Tang 
1587341487abSFeng Tang 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1588341487abSFeng Tang 				"Switch to another data file in PWD") > 0)
1589341487abSFeng Tang 			switch_data = nr_options++;
1590aca7a94dSNamhyung Kim add_exit_option:
1591aca7a94dSNamhyung Kim 		options[nr_options++] = (char *)"Exit";
1592aca7a94dSNamhyung Kim retry_popup_menu:
1593aca7a94dSNamhyung Kim 		choice = ui__popup_menu(nr_options, options);
1594aca7a94dSNamhyung Kim 
1595aca7a94dSNamhyung Kim 		if (choice == nr_options - 1)
1596aca7a94dSNamhyung Kim 			break;
1597aca7a94dSNamhyung Kim 
1598aca7a94dSNamhyung Kim 		if (choice == -1) {
1599aca7a94dSNamhyung Kim 			free_popup_options(options, nr_options - 1);
1600aca7a94dSNamhyung Kim 			continue;
1601aca7a94dSNamhyung Kim 		}
1602aca7a94dSNamhyung Kim 
1603aca7a94dSNamhyung Kim 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1604aca7a94dSNamhyung Kim 			struct hist_entry *he;
1605aca7a94dSNamhyung Kim 			int err;
1606aca7a94dSNamhyung Kim do_annotate:
160768d80758SNamhyung Kim 			if (!objdump_path && perf_session_env__lookup_objdump(env))
160868d80758SNamhyung Kim 				continue;
160968d80758SNamhyung Kim 
1610aca7a94dSNamhyung Kim 			he = hist_browser__selected_entry(browser);
1611aca7a94dSNamhyung Kim 			if (he == NULL)
1612aca7a94dSNamhyung Kim 				continue;
1613aca7a94dSNamhyung Kim 
1614aca7a94dSNamhyung Kim 			/*
1615aca7a94dSNamhyung Kim 			 * we stash the branch_info symbol + map into the
1616aca7a94dSNamhyung Kim 			 * the ms so we don't have to rewrite all the annotation
1617aca7a94dSNamhyung Kim 			 * code to use branch_info.
1618aca7a94dSNamhyung Kim 			 * in branch mode, the ms struct is not used
1619aca7a94dSNamhyung Kim 			 */
1620aca7a94dSNamhyung Kim 			if (choice == annotate_f) {
1621aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->from.sym;
1622aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->from.map;
1623aca7a94dSNamhyung Kim 			}  else if (choice == annotate_t) {
1624aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->to.sym;
1625aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->to.map;
1626aca7a94dSNamhyung Kim 			}
1627aca7a94dSNamhyung Kim 
1628aca7a94dSNamhyung Kim 			/*
1629aca7a94dSNamhyung Kim 			 * Don't let this be freed, say, by hists__decay_entry.
1630aca7a94dSNamhyung Kim 			 */
1631aca7a94dSNamhyung Kim 			he->used = true;
1632db8fd07aSNamhyung Kim 			err = hist_entry__tui_annotate(he, evsel, hbt);
1633aca7a94dSNamhyung Kim 			he->used = false;
1634aca7a94dSNamhyung Kim 			/*
1635aca7a94dSNamhyung Kim 			 * offer option to annotate the other branch source or target
1636aca7a94dSNamhyung Kim 			 * (if they exists) when returning from annotate
1637aca7a94dSNamhyung Kim 			 */
1638aca7a94dSNamhyung Kim 			if ((err == 'q' || err == CTRL('c'))
1639aca7a94dSNamhyung Kim 			    && annotate_t != -2 && annotate_f != -2)
1640aca7a94dSNamhyung Kim 				goto retry_popup_menu;
1641aca7a94dSNamhyung Kim 
1642aca7a94dSNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1643aca7a94dSNamhyung Kim 			if (err)
1644aca7a94dSNamhyung Kim 				ui_browser__handle_resize(&browser->b);
1645aca7a94dSNamhyung Kim 
1646aca7a94dSNamhyung Kim 		} else if (choice == browse_map)
1647aca7a94dSNamhyung Kim 			map__browse(browser->selection->map);
1648aca7a94dSNamhyung Kim 		else if (choice == zoom_dso) {
1649aca7a94dSNamhyung Kim zoom_dso:
1650aca7a94dSNamhyung Kim 			if (browser->hists->dso_filter) {
1651aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->dso_filter);
1652aca7a94dSNamhyung Kim zoom_out_dso:
1653aca7a94dSNamhyung Kim 				ui_helpline__pop();
1654aca7a94dSNamhyung Kim 				browser->hists->dso_filter = NULL;
1655aca7a94dSNamhyung Kim 				sort_dso.elide = false;
1656aca7a94dSNamhyung Kim 			} else {
1657aca7a94dSNamhyung Kim 				if (dso == NULL)
1658aca7a94dSNamhyung Kim 					continue;
1659aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1660aca7a94dSNamhyung Kim 						   dso->kernel ? "the Kernel" : dso->short_name);
1661aca7a94dSNamhyung Kim 				browser->hists->dso_filter = dso;
1662aca7a94dSNamhyung Kim 				sort_dso.elide = true;
1663aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->dso_filter);
1664aca7a94dSNamhyung Kim 			}
166505e8b080SArnaldo Carvalho de Melo 			hists__filter_by_dso(hists);
1666aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1667aca7a94dSNamhyung Kim 		} else if (choice == zoom_thread) {
1668aca7a94dSNamhyung Kim zoom_thread:
1669aca7a94dSNamhyung Kim 			if (browser->hists->thread_filter) {
1670aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->thread_filter);
1671aca7a94dSNamhyung Kim zoom_out_thread:
1672aca7a94dSNamhyung Kim 				ui_helpline__pop();
1673aca7a94dSNamhyung Kim 				browser->hists->thread_filter = NULL;
1674aca7a94dSNamhyung Kim 				sort_thread.elide = false;
1675aca7a94dSNamhyung Kim 			} else {
1676aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1677b9c5143aSFrederic Weisbecker 						   thread->comm_set ? thread__comm_str(thread) : "",
167838051234SAdrian Hunter 						   thread->tid);
1679aca7a94dSNamhyung Kim 				browser->hists->thread_filter = thread;
1680aca7a94dSNamhyung Kim 				sort_thread.elide = true;
1681aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->thread_filter);
1682aca7a94dSNamhyung Kim 			}
168305e8b080SArnaldo Carvalho de Melo 			hists__filter_by_thread(hists);
1684aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1685aca7a94dSNamhyung Kim 		}
1686cdbab7c2SFeng Tang 		/* perf scripts support */
1687cdbab7c2SFeng Tang 		else if (choice == scripts_all || choice == scripts_comm ||
1688cdbab7c2SFeng Tang 				choice == scripts_symbol) {
1689cdbab7c2SFeng Tang do_scripts:
1690cdbab7c2SFeng Tang 			memset(script_opt, 0, 64);
1691cdbab7c2SFeng Tang 
1692cdbab7c2SFeng Tang 			if (choice == scripts_comm)
1693b9c5143aSFrederic Weisbecker 				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1694cdbab7c2SFeng Tang 
1695cdbab7c2SFeng Tang 			if (choice == scripts_symbol)
1696cdbab7c2SFeng Tang 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1697cdbab7c2SFeng Tang 
1698cdbab7c2SFeng Tang 			script_browse(script_opt);
1699cdbab7c2SFeng Tang 		}
1700341487abSFeng Tang 		/* Switch to another data file */
1701341487abSFeng Tang 		else if (choice == switch_data) {
1702341487abSFeng Tang do_data_switch:
1703341487abSFeng Tang 			if (!switch_data_file()) {
1704341487abSFeng Tang 				key = K_SWITCH_INPUT_DATA;
1705341487abSFeng Tang 				break;
1706341487abSFeng Tang 			} else
1707341487abSFeng Tang 				ui__warning("Won't switch the data files due to\n"
1708341487abSFeng Tang 					"no valid data file get selected!\n");
1709341487abSFeng Tang 		}
1710aca7a94dSNamhyung Kim 	}
1711aca7a94dSNamhyung Kim out_free_stack:
1712aca7a94dSNamhyung Kim 	pstack__delete(fstack);
1713aca7a94dSNamhyung Kim out:
1714aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
1715aca7a94dSNamhyung Kim 	free_popup_options(options, nr_options - 1);
1716aca7a94dSNamhyung Kim 	return key;
1717aca7a94dSNamhyung Kim }
1718aca7a94dSNamhyung Kim 
1719aca7a94dSNamhyung Kim struct perf_evsel_menu {
1720aca7a94dSNamhyung Kim 	struct ui_browser b;
1721aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
1722aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
1723064f1981SNamhyung Kim 	float min_pcnt;
172468d80758SNamhyung Kim 	struct perf_session_env *env;
1725aca7a94dSNamhyung Kim };
1726aca7a94dSNamhyung Kim 
1727aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
1728aca7a94dSNamhyung Kim 				   void *entry, int row)
1729aca7a94dSNamhyung Kim {
1730aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
1731aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
1732aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1733aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
1734aca7a94dSNamhyung Kim 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
17357289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
1736aca7a94dSNamhyung Kim 	char bf[256], unit;
1737aca7a94dSNamhyung Kim 	const char *warn = " ";
1738aca7a94dSNamhyung Kim 	size_t printed;
1739aca7a94dSNamhyung Kim 
1740aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1741aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
1742aca7a94dSNamhyung Kim 
1743759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1744717e263fSNamhyung Kim 		struct perf_evsel *pos;
1745717e263fSNamhyung Kim 
1746717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
1747717e263fSNamhyung Kim 
1748717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
1749717e263fSNamhyung Kim 			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1750717e263fSNamhyung Kim 		}
1751717e263fSNamhyung Kim 	}
1752717e263fSNamhyung Kim 
1753aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
1754aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1755aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
1756aca7a94dSNamhyung Kim 	slsmg_printf("%s", bf);
1757aca7a94dSNamhyung Kim 
1758aca7a94dSNamhyung Kim 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1759aca7a94dSNamhyung Kim 	if (nr_events != 0) {
1760aca7a94dSNamhyung Kim 		menu->lost_events = true;
1761aca7a94dSNamhyung Kim 		if (!current_entry)
1762aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1763aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
1764aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1765aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
1766aca7a94dSNamhyung Kim 		warn = bf;
1767aca7a94dSNamhyung Kim 	}
1768aca7a94dSNamhyung Kim 
1769aca7a94dSNamhyung Kim 	slsmg_write_nstring(warn, browser->width - printed);
1770aca7a94dSNamhyung Kim 
1771aca7a94dSNamhyung Kim 	if (current_entry)
1772aca7a94dSNamhyung Kim 		menu->selection = evsel;
1773aca7a94dSNamhyung Kim }
1774aca7a94dSNamhyung Kim 
1775aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1776aca7a94dSNamhyung Kim 				int nr_events, const char *help,
17779783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
1778aca7a94dSNamhyung Kim {
1779aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
1780aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1781aca7a94dSNamhyung Kim 	const char *ev_name, *title = "Available samples";
17829783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
1783aca7a94dSNamhyung Kim 	int key;
1784aca7a94dSNamhyung Kim 
1785aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
1786aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1787aca7a94dSNamhyung Kim 		return -1;
1788aca7a94dSNamhyung Kim 
1789aca7a94dSNamhyung Kim 	while (1) {
1790aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
1791aca7a94dSNamhyung Kim 
1792aca7a94dSNamhyung Kim 		switch (key) {
1793aca7a94dSNamhyung Kim 		case K_TIMER:
17949783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
1795aca7a94dSNamhyung Kim 
1796aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
1797aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
1798aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
1799aca7a94dSNamhyung Kim 			}
1800aca7a94dSNamhyung Kim 			continue;
1801aca7a94dSNamhyung Kim 		case K_RIGHT:
1802aca7a94dSNamhyung Kim 		case K_ENTER:
1803aca7a94dSNamhyung Kim 			if (!menu->selection)
1804aca7a94dSNamhyung Kim 				continue;
1805aca7a94dSNamhyung Kim 			pos = menu->selection;
1806aca7a94dSNamhyung Kim browse_hists:
1807aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
1808aca7a94dSNamhyung Kim 			/*
1809aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
1810aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
1811aca7a94dSNamhyung Kim 			 */
18129783adf7SNamhyung Kim 			if (hbt)
18139783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
18147289f83cSArnaldo Carvalho de Melo 			ev_name = perf_evsel__name(pos);
1815aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
181668d80758SNamhyung Kim 						       ev_name, true, hbt,
1817064f1981SNamhyung Kim 						       menu->min_pcnt,
181868d80758SNamhyung Kim 						       menu->env);
1819aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
1820aca7a94dSNamhyung Kim 			switch (key) {
1821aca7a94dSNamhyung Kim 			case K_TAB:
1822aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
18239a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
1824aca7a94dSNamhyung Kim 				else
18259a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
1826aca7a94dSNamhyung Kim 				goto browse_hists;
1827aca7a94dSNamhyung Kim 			case K_UNTAB:
1828aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
18299a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
1830aca7a94dSNamhyung Kim 				else
1831d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
1832aca7a94dSNamhyung Kim 				goto browse_hists;
1833aca7a94dSNamhyung Kim 			case K_ESC:
1834aca7a94dSNamhyung Kim 				if (!ui_browser__dialog_yesno(&menu->b,
1835aca7a94dSNamhyung Kim 						"Do you really want to exit?"))
1836aca7a94dSNamhyung Kim 					continue;
1837aca7a94dSNamhyung Kim 				/* Fall thru */
1838341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
1839aca7a94dSNamhyung Kim 			case 'q':
1840aca7a94dSNamhyung Kim 			case CTRL('c'):
1841aca7a94dSNamhyung Kim 				goto out;
1842aca7a94dSNamhyung Kim 			default:
1843aca7a94dSNamhyung Kim 				continue;
1844aca7a94dSNamhyung Kim 			}
1845aca7a94dSNamhyung Kim 		case K_LEFT:
1846aca7a94dSNamhyung Kim 			continue;
1847aca7a94dSNamhyung Kim 		case K_ESC:
1848aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
1849aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1850aca7a94dSNamhyung Kim 				continue;
1851aca7a94dSNamhyung Kim 			/* Fall thru */
1852aca7a94dSNamhyung Kim 		case 'q':
1853aca7a94dSNamhyung Kim 		case CTRL('c'):
1854aca7a94dSNamhyung Kim 			goto out;
1855aca7a94dSNamhyung Kim 		default:
1856aca7a94dSNamhyung Kim 			continue;
1857aca7a94dSNamhyung Kim 		}
1858aca7a94dSNamhyung Kim 	}
1859aca7a94dSNamhyung Kim 
1860aca7a94dSNamhyung Kim out:
1861aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
1862aca7a94dSNamhyung Kim 	return key;
1863aca7a94dSNamhyung Kim }
1864aca7a94dSNamhyung Kim 
1865316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1866fc24d7c2SNamhyung Kim 				 void *entry)
1867fc24d7c2SNamhyung Kim {
1868fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1869fc24d7c2SNamhyung Kim 
1870fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1871fc24d7c2SNamhyung Kim 		return true;
1872fc24d7c2SNamhyung Kim 
1873fc24d7c2SNamhyung Kim 	return false;
1874fc24d7c2SNamhyung Kim }
1875fc24d7c2SNamhyung Kim 
1876aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1877fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
187868d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
1879064f1981SNamhyung Kim 					   float min_pcnt,
188068d80758SNamhyung Kim 					   struct perf_session_env *env)
1881aca7a94dSNamhyung Kim {
1882aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1883aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
1884aca7a94dSNamhyung Kim 		.b = {
1885aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
1886aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
1887aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
1888aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
1889fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
1890fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
1891aca7a94dSNamhyung Kim 			.priv	    = evlist,
1892aca7a94dSNamhyung Kim 		},
1893064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
189468d80758SNamhyung Kim 		.env = env,
1895aca7a94dSNamhyung Kim 	};
1896aca7a94dSNamhyung Kim 
1897aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
1898aca7a94dSNamhyung Kim 
18990050f7aaSArnaldo Carvalho de Melo 	evlist__for_each(evlist, pos) {
19007289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
1901aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
1902aca7a94dSNamhyung Kim 
1903aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
1904aca7a94dSNamhyung Kim 			menu.b.width = line_len;
1905aca7a94dSNamhyung Kim 	}
1906aca7a94dSNamhyung Kim 
1907fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1908aca7a94dSNamhyung Kim }
1909aca7a94dSNamhyung Kim 
1910aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
191168d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
1912064f1981SNamhyung Kim 				  float min_pcnt,
191368d80758SNamhyung Kim 				  struct perf_session_env *env)
1914aca7a94dSNamhyung Kim {
1915fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
1916fc24d7c2SNamhyung Kim 
1917fc24d7c2SNamhyung Kim single_entry:
1918fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
19199a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
19207289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(first);
1921fc24d7c2SNamhyung Kim 
1922fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
1923064f1981SNamhyung Kim 						ev_name, false, hbt, min_pcnt,
1924064f1981SNamhyung Kim 						env);
1925aca7a94dSNamhyung Kim 	}
1926aca7a94dSNamhyung Kim 
1927fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
1928fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
1929fc24d7c2SNamhyung Kim 
1930fc24d7c2SNamhyung Kim 		nr_entries = 0;
19310050f7aaSArnaldo Carvalho de Melo 		evlist__for_each(evlist, pos) {
1932fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
1933fc24d7c2SNamhyung Kim 				nr_entries++;
19340050f7aaSArnaldo Carvalho de Melo 		}
1935fc24d7c2SNamhyung Kim 
1936fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
1937fc24d7c2SNamhyung Kim 			goto single_entry;
1938fc24d7c2SNamhyung Kim 	}
1939fc24d7c2SNamhyung Kim 
1940fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1941064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
1942aca7a94dSNamhyung Kim }
1943