xref: /linux/tools/perf/ui/browsers/hists.c (revision e8e684a58b9bddde3fdb1a65cd26eb7a3e1e746e)
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 
59089701460SNamhyung Kim static int __hpp__color_callchain(struct hpp_arg *arg)
5915aed9d24SNamhyung Kim {
59289701460SNamhyung Kim 	if (!symbol_conf.use_callchain)
59389701460SNamhyung Kim 		return 0;
59489701460SNamhyung Kim 
59589701460SNamhyung Kim 	slsmg_printf("%c ", arg->folded_sign);
59689701460SNamhyung Kim 	return 2;
59789701460SNamhyung Kim }
59889701460SNamhyung Kim 
59989701460SNamhyung Kim static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
60089701460SNamhyung Kim 			    u64 (*get_field)(struct hist_entry *),
60189701460SNamhyung Kim 			    int (*callchain_cb)(struct hpp_arg *))
60289701460SNamhyung Kim {
60389701460SNamhyung Kim 	int ret = 0;
6045aed9d24SNamhyung Kim 	double percent = 0.0;
6055aed9d24SNamhyung Kim 	struct hists *hists = he->hists;
60689701460SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
6075aed9d24SNamhyung Kim 
6085aed9d24SNamhyung Kim 	if (hists->stats.total_period)
6095aed9d24SNamhyung Kim 		percent = 100.0 * get_field(he) / hists->stats.total_period;
6105aed9d24SNamhyung Kim 
61189701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
6125aed9d24SNamhyung Kim 
61389701460SNamhyung Kim 	if (callchain_cb)
61489701460SNamhyung Kim 		ret += callchain_cb(arg);
61589701460SNamhyung Kim 
61689701460SNamhyung Kim 	ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
61789701460SNamhyung Kim 	slsmg_printf("%s", hpp->buf);
61889701460SNamhyung Kim 
619371d8c40SNamhyung Kim 	if (symbol_conf.event_group) {
620371d8c40SNamhyung Kim 		int prev_idx, idx_delta;
621371d8c40SNamhyung Kim 		struct perf_evsel *evsel = hists_to_evsel(hists);
622371d8c40SNamhyung Kim 		struct hist_entry *pair;
623371d8c40SNamhyung Kim 		int nr_members = evsel->nr_members;
624371d8c40SNamhyung Kim 
625371d8c40SNamhyung Kim 		if (nr_members <= 1)
626371d8c40SNamhyung Kim 			goto out;
627371d8c40SNamhyung Kim 
628371d8c40SNamhyung Kim 		prev_idx = perf_evsel__group_idx(evsel);
629371d8c40SNamhyung Kim 
630371d8c40SNamhyung Kim 		list_for_each_entry(pair, &he->pairs.head, pairs.node) {
631371d8c40SNamhyung Kim 			u64 period = get_field(pair);
632371d8c40SNamhyung Kim 			u64 total = pair->hists->stats.total_period;
633371d8c40SNamhyung Kim 
634371d8c40SNamhyung Kim 			if (!total)
635371d8c40SNamhyung Kim 				continue;
636371d8c40SNamhyung Kim 
637371d8c40SNamhyung Kim 			evsel = hists_to_evsel(pair->hists);
638371d8c40SNamhyung Kim 			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
639371d8c40SNamhyung Kim 
640371d8c40SNamhyung Kim 			while (idx_delta--) {
641371d8c40SNamhyung Kim 				/*
642371d8c40SNamhyung Kim 				 * zero-fill group members in the middle which
643371d8c40SNamhyung Kim 				 * have no sample
644371d8c40SNamhyung Kim 				 */
645371d8c40SNamhyung Kim 				ui_browser__set_percent_color(arg->b, 0.0,
646371d8c40SNamhyung Kim 							arg->current_entry);
647371d8c40SNamhyung Kim 				ret += scnprintf(hpp->buf, hpp->size,
648371d8c40SNamhyung Kim 						 " %6.2f%%", 0.0);
649371d8c40SNamhyung Kim 				slsmg_printf("%s", hpp->buf);
650371d8c40SNamhyung Kim 			}
651371d8c40SNamhyung Kim 
652371d8c40SNamhyung Kim 			percent = 100.0 * period / total;
653371d8c40SNamhyung Kim 			ui_browser__set_percent_color(arg->b, percent,
654371d8c40SNamhyung Kim 						      arg->current_entry);
655371d8c40SNamhyung Kim 			ret += scnprintf(hpp->buf, hpp->size,
656371d8c40SNamhyung Kim 					 " %6.2f%%", percent);
657371d8c40SNamhyung Kim 			slsmg_printf("%s", hpp->buf);
658371d8c40SNamhyung Kim 
659371d8c40SNamhyung Kim 			prev_idx = perf_evsel__group_idx(evsel);
660371d8c40SNamhyung Kim 		}
661371d8c40SNamhyung Kim 
662371d8c40SNamhyung Kim 		idx_delta = nr_members - prev_idx - 1;
663371d8c40SNamhyung Kim 
664371d8c40SNamhyung Kim 		while (idx_delta--) {
665371d8c40SNamhyung Kim 			/*
666371d8c40SNamhyung Kim 			 * zero-fill group members at last which have no sample
667371d8c40SNamhyung Kim 			 */
668371d8c40SNamhyung Kim 			ui_browser__set_percent_color(arg->b, 0.0,
669371d8c40SNamhyung Kim 						      arg->current_entry);
670371d8c40SNamhyung Kim 			ret += scnprintf(hpp->buf, hpp->size,
671371d8c40SNamhyung Kim 					 " %6.2f%%", 0.0);
672371d8c40SNamhyung Kim 			slsmg_printf("%s", hpp->buf);
673371d8c40SNamhyung Kim 		}
674371d8c40SNamhyung Kim 	}
675371d8c40SNamhyung Kim out:
67689701460SNamhyung Kim 	if (!arg->current_entry || !arg->b->navkeypressed)
67789701460SNamhyung Kim 		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
67889701460SNamhyung Kim 
6795aed9d24SNamhyung Kim 	return ret;
680f5951d56SNamhyung Kim }
681f5951d56SNamhyung Kim 
68289701460SNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)			\
6835aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
6845aed9d24SNamhyung Kim {									\
6855aed9d24SNamhyung Kim 	return he->stat._field;						\
6865aed9d24SNamhyung Kim }									\
6875aed9d24SNamhyung Kim 									\
6882c5d4b4aSJiri Olsa static int								\
6892c5d4b4aSJiri Olsa hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
6902c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
6915aed9d24SNamhyung Kim 				struct hist_entry *he)			\
6925aed9d24SNamhyung Kim {									\
69389701460SNamhyung Kim 	return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);	\
6945aed9d24SNamhyung Kim }
695f5951d56SNamhyung Kim 
69689701460SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
69789701460SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
69889701460SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
69989701460SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
70089701460SNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
7015aed9d24SNamhyung Kim 
7025aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
703f5951d56SNamhyung Kim 
704f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
705f5951d56SNamhyung Kim {
7061d77822eSJiri Olsa 	perf_hpp__init();
707f5951d56SNamhyung Kim 
708f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
709f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
710f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
711f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
712f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
713f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
714f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
715f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
716f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
717f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
718f5951d56SNamhyung Kim }
719f5951d56SNamhyung Kim 
72005e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
721aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
722aca7a94dSNamhyung Kim 				    unsigned short row)
723aca7a94dSNamhyung Kim {
724aca7a94dSNamhyung Kim 	char s[256];
7251240005eSJiri Olsa 	int printed = 0;
72667d25916SNamhyung Kim 	int width = browser->b.width;
727aca7a94dSNamhyung Kim 	char folded_sign = ' ';
72805e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
729aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
73063a1a3d8SNamhyung Kim 	bool first = true;
7311240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
732aca7a94dSNamhyung Kim 
733aca7a94dSNamhyung Kim 	if (current_entry) {
73405e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
73505e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
736aca7a94dSNamhyung Kim 	}
737aca7a94dSNamhyung Kim 
738aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
739aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
740aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
741aca7a94dSNamhyung Kim 	}
742aca7a94dSNamhyung Kim 
743aca7a94dSNamhyung Kim 	if (row_offset == 0) {
74489701460SNamhyung Kim 		struct hpp_arg arg = {
74589701460SNamhyung Kim 			.b 		= &browser->b,
74689701460SNamhyung Kim 			.folded_sign	= folded_sign,
74789701460SNamhyung Kim 			.current_entry	= current_entry,
74889701460SNamhyung Kim 		};
749f5951d56SNamhyung Kim 		struct perf_hpp hpp = {
750f5951d56SNamhyung Kim 			.buf		= s,
751f5951d56SNamhyung Kim 			.size		= sizeof(s),
75289701460SNamhyung Kim 			.ptr		= &arg,
753f5951d56SNamhyung Kim 		};
754f5951d56SNamhyung Kim 
75567d25916SNamhyung Kim 		ui_browser__gotorc(&browser->b, row, 0);
756f5951d56SNamhyung Kim 
7571240005eSJiri Olsa 		perf_hpp__for_each_format(fmt) {
75863a1a3d8SNamhyung Kim 			if (!first) {
759f5951d56SNamhyung Kim 				slsmg_printf("  ");
760f5951d56SNamhyung Kim 				width -= 2;
761f5951d56SNamhyung Kim 			}
76263a1a3d8SNamhyung Kim 			first = false;
763f5951d56SNamhyung Kim 
7641240005eSJiri Olsa 			if (fmt->color) {
7652c5d4b4aSJiri Olsa 				width -= fmt->color(fmt, &hpp, entry);
766f5951d56SNamhyung Kim 			} else {
7672c5d4b4aSJiri Olsa 				width -= fmt->entry(fmt, &hpp, entry);
768f5951d56SNamhyung Kim 				slsmg_printf("%s", s);
769f5951d56SNamhyung Kim 			}
770f5951d56SNamhyung Kim 		}
771aca7a94dSNamhyung Kim 
772aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
77305e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
774aca7a94dSNamhyung Kim 			width += 1;
775aca7a94dSNamhyung Kim 
776f5951d56SNamhyung Kim 		hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
777aca7a94dSNamhyung Kim 		slsmg_write_nstring(s, width);
778aca7a94dSNamhyung Kim 		++row;
779aca7a94dSNamhyung Kim 		++printed;
780aca7a94dSNamhyung Kim 	} else
781aca7a94dSNamhyung Kim 		--row_offset;
782aca7a94dSNamhyung Kim 
78305e8b080SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.height) {
78405e8b080SArnaldo Carvalho de Melo 		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
785aca7a94dSNamhyung Kim 							1, row, &row_offset,
786aca7a94dSNamhyung Kim 							&current_entry);
787aca7a94dSNamhyung Kim 		if (current_entry)
78805e8b080SArnaldo Carvalho de Melo 			browser->he_selection = entry;
789aca7a94dSNamhyung Kim 	}
790aca7a94dSNamhyung Kim 
791aca7a94dSNamhyung Kim 	return printed;
792aca7a94dSNamhyung Kim }
793aca7a94dSNamhyung Kim 
794aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
795aca7a94dSNamhyung Kim {
796aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
797aca7a94dSNamhyung Kim 		struct hist_browser *hb;
798aca7a94dSNamhyung Kim 
799aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
800aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
801aca7a94dSNamhyung Kim 	}
802aca7a94dSNamhyung Kim }
803aca7a94dSNamhyung Kim 
80405e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
805aca7a94dSNamhyung Kim {
806aca7a94dSNamhyung Kim 	unsigned row = 0;
807aca7a94dSNamhyung Kim 	struct rb_node *nd;
80805e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
809aca7a94dSNamhyung Kim 
81005e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
811aca7a94dSNamhyung Kim 
81205e8b080SArnaldo Carvalho de Melo 	for (nd = browser->top; nd; nd = rb_next(nd)) {
813aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
814064f1981SNamhyung Kim 		float percent = h->stat.period * 100.0 /
815064f1981SNamhyung Kim 					hb->hists->stats.total_period;
816aca7a94dSNamhyung Kim 
817aca7a94dSNamhyung Kim 		if (h->filtered)
818aca7a94dSNamhyung Kim 			continue;
819aca7a94dSNamhyung Kim 
820064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
821064f1981SNamhyung Kim 			continue;
822064f1981SNamhyung Kim 
823aca7a94dSNamhyung Kim 		row += hist_browser__show_entry(hb, h, row);
82405e8b080SArnaldo Carvalho de Melo 		if (row == browser->height)
825aca7a94dSNamhyung Kim 			break;
826aca7a94dSNamhyung Kim 	}
827aca7a94dSNamhyung Kim 
828aca7a94dSNamhyung Kim 	return row;
829aca7a94dSNamhyung Kim }
830aca7a94dSNamhyung Kim 
831064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
832064f1981SNamhyung Kim 					     struct hists *hists,
833064f1981SNamhyung Kim 					     float min_pcnt)
834aca7a94dSNamhyung Kim {
835aca7a94dSNamhyung Kim 	while (nd != NULL) {
836aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
837064f1981SNamhyung Kim 		float percent = h->stat.period * 100.0 /
838064f1981SNamhyung Kim 					hists->stats.total_period;
839064f1981SNamhyung Kim 
840064f1981SNamhyung Kim 		if (percent < min_pcnt)
841064f1981SNamhyung Kim 			return NULL;
842064f1981SNamhyung Kim 
843aca7a94dSNamhyung Kim 		if (!h->filtered)
844aca7a94dSNamhyung Kim 			return nd;
845aca7a94dSNamhyung Kim 
846aca7a94dSNamhyung Kim 		nd = rb_next(nd);
847aca7a94dSNamhyung Kim 	}
848aca7a94dSNamhyung Kim 
849aca7a94dSNamhyung Kim 	return NULL;
850aca7a94dSNamhyung Kim }
851aca7a94dSNamhyung Kim 
852064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
853064f1981SNamhyung Kim 						  struct hists *hists,
854064f1981SNamhyung Kim 						  float min_pcnt)
855aca7a94dSNamhyung Kim {
856aca7a94dSNamhyung Kim 	while (nd != NULL) {
857aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
858064f1981SNamhyung Kim 		float percent = h->stat.period * 100.0 /
859064f1981SNamhyung Kim 					hists->stats.total_period;
860064f1981SNamhyung Kim 
861064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
862aca7a94dSNamhyung Kim 			return nd;
863aca7a94dSNamhyung Kim 
864aca7a94dSNamhyung Kim 		nd = rb_prev(nd);
865aca7a94dSNamhyung Kim 	}
866aca7a94dSNamhyung Kim 
867aca7a94dSNamhyung Kim 	return NULL;
868aca7a94dSNamhyung Kim }
869aca7a94dSNamhyung Kim 
87005e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
871aca7a94dSNamhyung Kim 				   off_t offset, int whence)
872aca7a94dSNamhyung Kim {
873aca7a94dSNamhyung Kim 	struct hist_entry *h;
874aca7a94dSNamhyung Kim 	struct rb_node *nd;
875aca7a94dSNamhyung Kim 	bool first = true;
876064f1981SNamhyung Kim 	struct hist_browser *hb;
877064f1981SNamhyung Kim 
878064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
879aca7a94dSNamhyung Kim 
88005e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
881aca7a94dSNamhyung Kim 		return;
882aca7a94dSNamhyung Kim 
88305e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
884aca7a94dSNamhyung Kim 
885aca7a94dSNamhyung Kim 	switch (whence) {
886aca7a94dSNamhyung Kim 	case SEEK_SET:
887064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
888064f1981SNamhyung Kim 					   hb->hists, hb->min_pcnt);
889aca7a94dSNamhyung Kim 		break;
890aca7a94dSNamhyung Kim 	case SEEK_CUR:
89105e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
892aca7a94dSNamhyung Kim 		goto do_offset;
893aca7a94dSNamhyung Kim 	case SEEK_END:
894064f1981SNamhyung Kim 		nd = hists__filter_prev_entries(rb_last(browser->entries),
895064f1981SNamhyung Kim 						hb->hists, hb->min_pcnt);
896aca7a94dSNamhyung Kim 		first = false;
897aca7a94dSNamhyung Kim 		break;
898aca7a94dSNamhyung Kim 	default:
899aca7a94dSNamhyung Kim 		return;
900aca7a94dSNamhyung Kim 	}
901aca7a94dSNamhyung Kim 
902aca7a94dSNamhyung Kim 	/*
903aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
904aca7a94dSNamhyung Kim 	 * row_offset:
905aca7a94dSNamhyung Kim 	 */
90605e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
907aca7a94dSNamhyung Kim 	h->row_offset = 0;
908aca7a94dSNamhyung Kim 
909aca7a94dSNamhyung Kim 	/*
910aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
911aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
912aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
913aca7a94dSNamhyung Kim 	 *
914aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
915aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
916aca7a94dSNamhyung Kim 	 *
917aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
918aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
919aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
920aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
921aca7a94dSNamhyung Kim 	 */
922aca7a94dSNamhyung Kim do_offset:
923aca7a94dSNamhyung Kim 	if (offset > 0) {
924aca7a94dSNamhyung Kim 		do {
925aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
926aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
927aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
928aca7a94dSNamhyung Kim 				if (offset > remaining) {
929aca7a94dSNamhyung Kim 					offset -= remaining;
930aca7a94dSNamhyung Kim 					h->row_offset = 0;
931aca7a94dSNamhyung Kim 				} else {
932aca7a94dSNamhyung Kim 					h->row_offset += offset;
933aca7a94dSNamhyung Kim 					offset = 0;
93405e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
935aca7a94dSNamhyung Kim 					break;
936aca7a94dSNamhyung Kim 				}
937aca7a94dSNamhyung Kim 			}
938064f1981SNamhyung Kim 			nd = hists__filter_entries(rb_next(nd), hb->hists,
939064f1981SNamhyung Kim 						   hb->min_pcnt);
940aca7a94dSNamhyung Kim 			if (nd == NULL)
941aca7a94dSNamhyung Kim 				break;
942aca7a94dSNamhyung Kim 			--offset;
94305e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
944aca7a94dSNamhyung Kim 		} while (offset != 0);
945aca7a94dSNamhyung Kim 	} else if (offset < 0) {
946aca7a94dSNamhyung Kim 		while (1) {
947aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
948aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
949aca7a94dSNamhyung Kim 				if (first) {
950aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
951aca7a94dSNamhyung Kim 						offset += h->row_offset;
952aca7a94dSNamhyung Kim 						h->row_offset = 0;
953aca7a94dSNamhyung Kim 					} else {
954aca7a94dSNamhyung Kim 						h->row_offset += offset;
955aca7a94dSNamhyung Kim 						offset = 0;
95605e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
957aca7a94dSNamhyung Kim 						break;
958aca7a94dSNamhyung Kim 					}
959aca7a94dSNamhyung Kim 				} else {
960aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
961aca7a94dSNamhyung Kim 						offset += h->nr_rows;
962aca7a94dSNamhyung Kim 						h->row_offset = 0;
963aca7a94dSNamhyung Kim 					} else {
964aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
965aca7a94dSNamhyung Kim 						offset = 0;
96605e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
967aca7a94dSNamhyung Kim 						break;
968aca7a94dSNamhyung Kim 					}
969aca7a94dSNamhyung Kim 				}
970aca7a94dSNamhyung Kim 			}
971aca7a94dSNamhyung Kim 
972064f1981SNamhyung Kim 			nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
973064f1981SNamhyung Kim 							hb->min_pcnt);
974aca7a94dSNamhyung Kim 			if (nd == NULL)
975aca7a94dSNamhyung Kim 				break;
976aca7a94dSNamhyung Kim 			++offset;
97705e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
978aca7a94dSNamhyung Kim 			if (offset == 0) {
979aca7a94dSNamhyung Kim 				/*
980aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
981aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
982aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
983aca7a94dSNamhyung Kim 				 */
984aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
985aca7a94dSNamhyung Kim 				if (h->ms.unfolded)
986aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
987aca7a94dSNamhyung Kim 				break;
988aca7a94dSNamhyung Kim 			}
989aca7a94dSNamhyung Kim 			first = false;
990aca7a94dSNamhyung Kim 		}
991aca7a94dSNamhyung Kim 	} else {
99205e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
993aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
994aca7a94dSNamhyung Kim 		h->row_offset = 0;
995aca7a94dSNamhyung Kim 	}
996aca7a94dSNamhyung Kim }
997aca7a94dSNamhyung Kim 
998aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
999aff3f3f6SArnaldo Carvalho de Melo 							struct callchain_node *chain_node,
1000aff3f3f6SArnaldo Carvalho de Melo 							u64 total, int level,
1001aff3f3f6SArnaldo Carvalho de Melo 							FILE *fp)
1002aff3f3f6SArnaldo Carvalho de Melo {
1003aff3f3f6SArnaldo Carvalho de Melo 	struct rb_node *node;
1004aff3f3f6SArnaldo Carvalho de Melo 	int offset = level * LEVEL_OFFSET_STEP;
1005aff3f3f6SArnaldo Carvalho de Melo 	u64 new_total, remaining;
1006aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1007aff3f3f6SArnaldo Carvalho de Melo 
1008aff3f3f6SArnaldo Carvalho de Melo 	if (callchain_param.mode == CHAIN_GRAPH_REL)
1009aff3f3f6SArnaldo Carvalho de Melo 		new_total = chain_node->children_hit;
1010aff3f3f6SArnaldo Carvalho de Melo 	else
1011aff3f3f6SArnaldo Carvalho de Melo 		new_total = total;
1012aff3f3f6SArnaldo Carvalho de Melo 
1013aff3f3f6SArnaldo Carvalho de Melo 	remaining = new_total;
1014aff3f3f6SArnaldo Carvalho de Melo 	node = rb_first(&chain_node->rb_root);
1015aff3f3f6SArnaldo Carvalho de Melo 	while (node) {
1016aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1017aff3f3f6SArnaldo Carvalho de Melo 		struct rb_node *next = rb_next(node);
1018aff3f3f6SArnaldo Carvalho de Melo 		u64 cumul = callchain_cumul_hits(child);
1019aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_list *chain;
1020aff3f3f6SArnaldo Carvalho de Melo 		char folded_sign = ' ';
1021aff3f3f6SArnaldo Carvalho de Melo 		int first = true;
1022aff3f3f6SArnaldo Carvalho de Melo 		int extra_offset = 0;
1023aff3f3f6SArnaldo Carvalho de Melo 
1024aff3f3f6SArnaldo Carvalho de Melo 		remaining -= cumul;
1025aff3f3f6SArnaldo Carvalho de Melo 
1026aff3f3f6SArnaldo Carvalho de Melo 		list_for_each_entry(chain, &child->val, list) {
1027a7cb8863SArnaldo Carvalho de Melo 			char bf[1024], *alloc_str;
1028aff3f3f6SArnaldo Carvalho de Melo 			const char *str;
1029aff3f3f6SArnaldo Carvalho de Melo 			bool was_first = first;
1030aff3f3f6SArnaldo Carvalho de Melo 
1031aff3f3f6SArnaldo Carvalho de Melo 			if (first)
1032aff3f3f6SArnaldo Carvalho de Melo 				first = false;
1033aff3f3f6SArnaldo Carvalho de Melo 			else
1034aff3f3f6SArnaldo Carvalho de Melo 				extra_offset = LEVEL_OFFSET_STEP;
1035aff3f3f6SArnaldo Carvalho de Melo 
1036aff3f3f6SArnaldo Carvalho de Melo 			folded_sign = callchain_list__folded(chain);
1037aff3f3f6SArnaldo Carvalho de Melo 
1038aff3f3f6SArnaldo Carvalho de Melo 			alloc_str = NULL;
1039a7cb8863SArnaldo Carvalho de Melo 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
1040a7cb8863SArnaldo Carvalho de Melo 						       browser->show_dso);
1041aff3f3f6SArnaldo Carvalho de Melo 			if (was_first) {
1042aff3f3f6SArnaldo Carvalho de Melo 				double percent = cumul * 100.0 / new_total;
1043aff3f3f6SArnaldo Carvalho de Melo 
1044aff3f3f6SArnaldo Carvalho de Melo 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1045aff3f3f6SArnaldo Carvalho de Melo 					str = "Not enough memory!";
1046aff3f3f6SArnaldo Carvalho de Melo 				else
1047aff3f3f6SArnaldo Carvalho de Melo 					str = alloc_str;
1048aff3f3f6SArnaldo Carvalho de Melo 			}
1049aff3f3f6SArnaldo Carvalho de Melo 
1050aff3f3f6SArnaldo Carvalho de Melo 			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1051aff3f3f6SArnaldo Carvalho de Melo 			free(alloc_str);
1052aff3f3f6SArnaldo Carvalho de Melo 			if (folded_sign == '+')
1053aff3f3f6SArnaldo Carvalho de Melo 				break;
1054aff3f3f6SArnaldo Carvalho de Melo 		}
1055aff3f3f6SArnaldo Carvalho de Melo 
1056aff3f3f6SArnaldo Carvalho de Melo 		if (folded_sign == '-') {
1057aff3f3f6SArnaldo Carvalho de Melo 			const int new_level = level + (extra_offset ? 2 : 1);
1058aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1059aff3f3f6SArnaldo Carvalho de Melo 										new_level, fp);
1060aff3f3f6SArnaldo Carvalho de Melo 		}
1061aff3f3f6SArnaldo Carvalho de Melo 
1062aff3f3f6SArnaldo Carvalho de Melo 		node = next;
1063aff3f3f6SArnaldo Carvalho de Melo 	}
1064aff3f3f6SArnaldo Carvalho de Melo 
1065aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1066aff3f3f6SArnaldo Carvalho de Melo }
1067aff3f3f6SArnaldo Carvalho de Melo 
1068aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1069aff3f3f6SArnaldo Carvalho de Melo 						struct callchain_node *node,
1070aff3f3f6SArnaldo Carvalho de Melo 						int level, FILE *fp)
1071aff3f3f6SArnaldo Carvalho de Melo {
1072aff3f3f6SArnaldo Carvalho de Melo 	struct callchain_list *chain;
1073aff3f3f6SArnaldo Carvalho de Melo 	int offset = level * LEVEL_OFFSET_STEP;
1074aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
1075aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1076aff3f3f6SArnaldo Carvalho de Melo 
1077aff3f3f6SArnaldo Carvalho de Melo 	list_for_each_entry(chain, &node->val, list) {
1078a7cb8863SArnaldo Carvalho de Melo 		char bf[1024], *s;
1079aff3f3f6SArnaldo Carvalho de Melo 
1080aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = callchain_list__folded(chain);
1081a7cb8863SArnaldo Carvalho de Melo 		s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1082aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1083aff3f3f6SArnaldo Carvalho de Melo 	}
1084aff3f3f6SArnaldo Carvalho de Melo 
1085aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1086aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1087aff3f3f6SArnaldo Carvalho de Melo 									browser->hists->stats.total_period,
1088aff3f3f6SArnaldo Carvalho de Melo 									level + 1,  fp);
1089aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1090aff3f3f6SArnaldo Carvalho de Melo }
1091aff3f3f6SArnaldo Carvalho de Melo 
1092aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1093aff3f3f6SArnaldo Carvalho de Melo 					   struct rb_root *chain, int level, FILE *fp)
1094aff3f3f6SArnaldo Carvalho de Melo {
1095aff3f3f6SArnaldo Carvalho de Melo 	struct rb_node *nd;
1096aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1097aff3f3f6SArnaldo Carvalho de Melo 
1098aff3f3f6SArnaldo Carvalho de Melo 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1099aff3f3f6SArnaldo Carvalho de Melo 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1100aff3f3f6SArnaldo Carvalho de Melo 
1101aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1102aff3f3f6SArnaldo Carvalho de Melo 	}
1103aff3f3f6SArnaldo Carvalho de Melo 
1104aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1105aff3f3f6SArnaldo Carvalho de Melo }
1106aff3f3f6SArnaldo Carvalho de Melo 
1107aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1108aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1109aff3f3f6SArnaldo Carvalho de Melo {
1110aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1111aff3f3f6SArnaldo Carvalho de Melo 	double percent;
1112aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1113aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
1114aff3f3f6SArnaldo Carvalho de Melo 
1115aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1116aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1117aff3f3f6SArnaldo Carvalho de Melo 
1118000078bcSNamhyung Kim 	hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1119b24c28f7SNamhyung Kim 	percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1120aff3f3f6SArnaldo Carvalho de Melo 
1121aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1122aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
1123aff3f3f6SArnaldo Carvalho de Melo 
1124aff3f3f6SArnaldo Carvalho de Melo 	printed += fprintf(fp, " %5.2f%%", percent);
1125aff3f3f6SArnaldo Carvalho de Melo 
1126aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.show_nr_samples)
1127b24c28f7SNamhyung Kim 		printed += fprintf(fp, " %11u", he->stat.nr_events);
1128aff3f3f6SArnaldo Carvalho de Melo 
1129aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.show_total_period)
1130b24c28f7SNamhyung Kim 		printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1131aff3f3f6SArnaldo Carvalho de Melo 
1132aff3f3f6SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", rtrim(s));
1133aff3f3f6SArnaldo Carvalho de Melo 
1134aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
1135aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1136aff3f3f6SArnaldo Carvalho de Melo 
1137aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1138aff3f3f6SArnaldo Carvalho de Melo }
1139aff3f3f6SArnaldo Carvalho de Melo 
1140aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1141aff3f3f6SArnaldo Carvalho de Melo {
1142064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1143064f1981SNamhyung Kim 						   browser->hists,
1144064f1981SNamhyung Kim 						   browser->min_pcnt);
1145aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1146aff3f3f6SArnaldo Carvalho de Melo 
1147aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1148aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1149aff3f3f6SArnaldo Carvalho de Melo 
1150aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_entry(browser, h, fp);
1151064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), browser->hists,
1152064f1981SNamhyung Kim 					   browser->min_pcnt);
1153aff3f3f6SArnaldo Carvalho de Melo 	}
1154aff3f3f6SArnaldo Carvalho de Melo 
1155aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1156aff3f3f6SArnaldo Carvalho de Melo }
1157aff3f3f6SArnaldo Carvalho de Melo 
1158aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
1159aff3f3f6SArnaldo Carvalho de Melo {
1160aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
1161aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
1162aff3f3f6SArnaldo Carvalho de Melo 
1163aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
1164aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1165aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
1166aff3f3f6SArnaldo Carvalho de Melo 			break;
1167aff3f3f6SArnaldo Carvalho de Melo 		/*
1168aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
1169aff3f3f6SArnaldo Carvalho de Melo  		 */
1170aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
1171aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1172aff3f3f6SArnaldo Carvalho de Melo 			return -1;
1173aff3f3f6SArnaldo Carvalho de Melo 		}
1174aff3f3f6SArnaldo Carvalho de Melo 	}
1175aff3f3f6SArnaldo Carvalho de Melo 
1176aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
1177aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
1178aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
11794cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
11804cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1181aff3f3f6SArnaldo Carvalho de Melo 		return -1;
1182aff3f3f6SArnaldo Carvalho de Melo 	}
1183aff3f3f6SArnaldo Carvalho de Melo 
1184aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
1185aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
1186aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
1187aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
1188aff3f3f6SArnaldo Carvalho de Melo 
1189aff3f3f6SArnaldo Carvalho de Melo 	return 0;
1190aff3f3f6SArnaldo Carvalho de Melo }
1191aff3f3f6SArnaldo Carvalho de Melo 
1192aca7a94dSNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists)
1193aca7a94dSNamhyung Kim {
119405e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
1195aca7a94dSNamhyung Kim 
119605e8b080SArnaldo Carvalho de Melo 	if (browser) {
119705e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
119805e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
119905e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
120005e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
1201aca7a94dSNamhyung Kim 	}
1202aca7a94dSNamhyung Kim 
120305e8b080SArnaldo Carvalho de Melo 	return browser;
1204aca7a94dSNamhyung Kim }
1205aca7a94dSNamhyung Kim 
120605e8b080SArnaldo Carvalho de Melo static void hist_browser__delete(struct hist_browser *browser)
1207aca7a94dSNamhyung Kim {
120805e8b080SArnaldo Carvalho de Melo 	free(browser);
1209aca7a94dSNamhyung Kim }
1210aca7a94dSNamhyung Kim 
121105e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1212aca7a94dSNamhyung Kim {
121305e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
1214aca7a94dSNamhyung Kim }
1215aca7a94dSNamhyung Kim 
121605e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1217aca7a94dSNamhyung Kim {
121805e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
1219aca7a94dSNamhyung Kim }
1220aca7a94dSNamhyung Kim 
122105e8b080SArnaldo Carvalho de Melo static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1222aca7a94dSNamhyung Kim 				const char *ev_name)
1223aca7a94dSNamhyung Kim {
1224aca7a94dSNamhyung Kim 	char unit;
1225aca7a94dSNamhyung Kim 	int printed;
122605e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
122705e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
122805e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
122905e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
1230717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
1231717e263fSNamhyung Kim 	char buf[512];
1232717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
1233717e263fSNamhyung Kim 
1234759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1235717e263fSNamhyung Kim 		struct perf_evsel *pos;
1236717e263fSNamhyung Kim 
1237717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
1238717e263fSNamhyung Kim 		ev_name = buf;
1239717e263fSNamhyung Kim 
1240717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
1241717e263fSNamhyung Kim 			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1242717e263fSNamhyung Kim 			nr_events += pos->hists.stats.total_period;
1243717e263fSNamhyung Kim 		}
1244717e263fSNamhyung Kim 	}
1245aca7a94dSNamhyung Kim 
1246aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
1247aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
1248aca7a94dSNamhyung Kim 			   "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1249aca7a94dSNamhyung Kim 			   nr_samples, unit, ev_name, nr_events);
1250aca7a94dSNamhyung Kim 
1251aca7a94dSNamhyung Kim 
125205e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
1253aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
125405e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
1255aca7a94dSNamhyung Kim 	if (thread)
1256aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1257aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
1258b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
125938051234SAdrian Hunter 				    thread->tid);
1260aca7a94dSNamhyung Kim 	if (dso)
1261aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1262aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
1263aca7a94dSNamhyung Kim 	return printed;
1264aca7a94dSNamhyung Kim }
1265aca7a94dSNamhyung Kim 
1266aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
1267aca7a94dSNamhyung Kim {
1268aca7a94dSNamhyung Kim 	int i;
1269aca7a94dSNamhyung Kim 
1270aca7a94dSNamhyung Kim 	for (i = 0; i < n; ++i) {
1271aca7a94dSNamhyung Kim 		free(options[i]);
1272aca7a94dSNamhyung Kim 		options[i] = NULL;
1273aca7a94dSNamhyung Kim 	}
1274aca7a94dSNamhyung Kim }
1275aca7a94dSNamhyung Kim 
1276c77d8d70SFeng Tang /* Check whether the browser is for 'top' or 'report' */
1277c77d8d70SFeng Tang static inline bool is_report_browser(void *timer)
1278c77d8d70SFeng Tang {
1279c77d8d70SFeng Tang 	return timer == NULL;
1280c77d8d70SFeng Tang }
1281c77d8d70SFeng Tang 
1282341487abSFeng Tang /*
1283341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
1284341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1285341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
1286341487abSFeng Tang  */
1287341487abSFeng Tang static bool is_input_name_malloced = false;
1288341487abSFeng Tang 
1289341487abSFeng Tang static int switch_data_file(void)
1290341487abSFeng Tang {
1291341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
1292341487abSFeng Tang 	DIR *pwd_dir;
1293341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
1294341487abSFeng Tang 	struct dirent *dent;
1295341487abSFeng Tang 
1296341487abSFeng Tang 	pwd = getenv("PWD");
1297341487abSFeng Tang 	if (!pwd)
1298341487abSFeng Tang 		return ret;
1299341487abSFeng Tang 
1300341487abSFeng Tang 	pwd_dir = opendir(pwd);
1301341487abSFeng Tang 	if (!pwd_dir)
1302341487abSFeng Tang 		return ret;
1303341487abSFeng Tang 
1304341487abSFeng Tang 	memset(options, 0, sizeof(options));
1305341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
1306341487abSFeng Tang 
1307341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
1308341487abSFeng Tang 		char path[PATH_MAX];
1309341487abSFeng Tang 		u64 magic;
1310341487abSFeng Tang 		char *name = dent->d_name;
1311341487abSFeng Tang 		FILE *file;
1312341487abSFeng Tang 
1313341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
1314341487abSFeng Tang 			continue;
1315341487abSFeng Tang 
1316341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1317341487abSFeng Tang 
1318341487abSFeng Tang 		file = fopen(path, "r");
1319341487abSFeng Tang 		if (!file)
1320341487abSFeng Tang 			continue;
1321341487abSFeng Tang 
1322341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
1323341487abSFeng Tang 			goto close_file_and_continue;
1324341487abSFeng Tang 
1325341487abSFeng Tang 		if (is_perf_magic(magic)) {
1326341487abSFeng Tang 			options[nr_options] = strdup(name);
1327341487abSFeng Tang 			if (!options[nr_options])
1328341487abSFeng Tang 				goto close_file_and_continue;
1329341487abSFeng Tang 
1330341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
1331341487abSFeng Tang 			if (!abs_path[nr_options]) {
1332341487abSFeng Tang 				free(options[nr_options]);
1333341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
1334341487abSFeng Tang 				fclose(file);
1335341487abSFeng Tang 				break;
1336341487abSFeng Tang 			}
1337341487abSFeng Tang 
1338341487abSFeng Tang 			nr_options++;
1339341487abSFeng Tang 		}
1340341487abSFeng Tang 
1341341487abSFeng Tang close_file_and_continue:
1342341487abSFeng Tang 		fclose(file);
1343341487abSFeng Tang 		if (nr_options >= 32) {
1344341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
1345341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
1346341487abSFeng Tang 			break;
1347341487abSFeng Tang 		}
1348341487abSFeng Tang 	}
1349341487abSFeng Tang 	closedir(pwd_dir);
1350341487abSFeng Tang 
1351341487abSFeng Tang 	if (nr_options) {
1352341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
1353341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
1354341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
1355341487abSFeng Tang 			if (tmp) {
1356341487abSFeng Tang 				if (is_input_name_malloced)
1357341487abSFeng Tang 					free((void *)input_name);
1358341487abSFeng Tang 				input_name = tmp;
1359341487abSFeng Tang 				is_input_name_malloced = true;
1360341487abSFeng Tang 				ret = 0;
1361341487abSFeng Tang 			} else
1362341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
1363341487abSFeng Tang 		}
1364341487abSFeng Tang 	}
1365341487abSFeng Tang 
1366341487abSFeng Tang 	free_popup_options(options, nr_options);
1367341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
1368341487abSFeng Tang 	return ret;
1369341487abSFeng Tang }
1370341487abSFeng Tang 
1371064f1981SNamhyung Kim static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1372064f1981SNamhyung Kim {
1373064f1981SNamhyung Kim 	u64 nr_entries = 0;
1374064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
1375064f1981SNamhyung Kim 
1376064f1981SNamhyung Kim 	while (nd) {
1377064f1981SNamhyung Kim 		nr_entries++;
1378064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), hb->hists,
1379064f1981SNamhyung Kim 					   hb->min_pcnt);
1380064f1981SNamhyung Kim 	}
1381064f1981SNamhyung Kim 
1382064f1981SNamhyung Kim 	hb->nr_pcnt_entries = nr_entries;
1383064f1981SNamhyung Kim }
1384341487abSFeng Tang 
1385aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1386aca7a94dSNamhyung Kim 				    const char *helpline, const char *ev_name,
1387aca7a94dSNamhyung Kim 				    bool left_exits,
138868d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
1389064f1981SNamhyung Kim 				    float min_pcnt,
139068d80758SNamhyung Kim 				    struct perf_session_env *env)
1391aca7a94dSNamhyung Kim {
139205e8b080SArnaldo Carvalho de Melo 	struct hists *hists = &evsel->hists;
139305e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = hist_browser__new(hists);
1394aca7a94dSNamhyung Kim 	struct branch_info *bi;
1395aca7a94dSNamhyung Kim 	struct pstack *fstack;
1396aca7a94dSNamhyung Kim 	char *options[16];
1397aca7a94dSNamhyung Kim 	int nr_options = 0;
1398aca7a94dSNamhyung Kim 	int key = -1;
1399aca7a94dSNamhyung Kim 	char buf[64];
1400cdbab7c2SFeng Tang 	char script_opt[64];
14019783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
1402aca7a94dSNamhyung Kim 
1403*e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
1404*e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
1405*e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
1406*e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
1407*e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1408*e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
1409*e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
1410*e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
1411*e8e684a5SNamhyung Kim 	"->            Zoom into DSO/Threads & Annotate current symbol\n" \
1412*e8e684a5SNamhyung Kim 	"<-            Zoom out\n"					\
1413*e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
1414*e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
1415*e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
1416*e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
1417*e8e684a5SNamhyung Kim 
1418*e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
1419*e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
1420*e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1421*e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
1422*e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
1423*e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1424*e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
1425*e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1426*e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1427*e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1428*e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1429*e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
1430*e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1431*e8e684a5SNamhyung Kim 
1432aca7a94dSNamhyung Kim 	if (browser == NULL)
1433aca7a94dSNamhyung Kim 		return -1;
1434aca7a94dSNamhyung Kim 
1435064f1981SNamhyung Kim 	if (min_pcnt) {
1436064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
1437064f1981SNamhyung Kim 		hist_browser__update_pcnt_entries(browser);
1438064f1981SNamhyung Kim 	}
1439064f1981SNamhyung Kim 
1440aca7a94dSNamhyung Kim 	fstack = pstack__new(2);
1441aca7a94dSNamhyung Kim 	if (fstack == NULL)
1442aca7a94dSNamhyung Kim 		goto out;
1443aca7a94dSNamhyung Kim 
1444aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
1445aca7a94dSNamhyung Kim 
1446aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
1447aca7a94dSNamhyung Kim 
1448aca7a94dSNamhyung Kim 	while (1) {
1449aca7a94dSNamhyung Kim 		const struct thread *thread = NULL;
1450aca7a94dSNamhyung Kim 		const struct dso *dso = NULL;
1451aca7a94dSNamhyung Kim 		int choice = 0,
1452aca7a94dSNamhyung Kim 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1453aca7a94dSNamhyung Kim 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1454341487abSFeng Tang 		int scripts_comm = -2, scripts_symbol = -2,
1455341487abSFeng Tang 		    scripts_all = -2, switch_data = -2;
1456aca7a94dSNamhyung Kim 
1457aca7a94dSNamhyung Kim 		nr_options = 0;
1458aca7a94dSNamhyung Kim 
14599783adf7SNamhyung Kim 		key = hist_browser__run(browser, ev_name, hbt);
1460aca7a94dSNamhyung Kim 
1461aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
1462aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
1463aca7a94dSNamhyung Kim 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1464aca7a94dSNamhyung Kim 		}
1465aca7a94dSNamhyung Kim 		switch (key) {
1466aca7a94dSNamhyung Kim 		case K_TAB:
1467aca7a94dSNamhyung Kim 		case K_UNTAB:
1468aca7a94dSNamhyung Kim 			if (nr_events == 1)
1469aca7a94dSNamhyung Kim 				continue;
1470aca7a94dSNamhyung Kim 			/*
1471aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
1472aca7a94dSNamhyung Kim 			 * go to the next or previous
1473aca7a94dSNamhyung Kim 			 */
1474aca7a94dSNamhyung Kim 			goto out_free_stack;
1475aca7a94dSNamhyung Kim 		case 'a':
14769c796ec8SArnaldo Carvalho de Melo 			if (!sort__has_sym) {
1477aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
1478aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
1479aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
1480aca7a94dSNamhyung Kim 				continue;
1481aca7a94dSNamhyung Kim 			}
1482aca7a94dSNamhyung Kim 
1483aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
1484aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
1485aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
1486aca7a94dSNamhyung Kim 				continue;
1487aca7a94dSNamhyung Kim 			goto do_annotate;
1488aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
1489aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
1490aff3f3f6SArnaldo Carvalho de Melo 			continue;
1491aca7a94dSNamhyung Kim 		case 'd':
1492aca7a94dSNamhyung Kim 			goto zoom_dso;
1493a7cb8863SArnaldo Carvalho de Melo 		case 'V':
1494a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
1495a7cb8863SArnaldo Carvalho de Melo 			continue;
1496aca7a94dSNamhyung Kim 		case 't':
1497aca7a94dSNamhyung Kim 			goto zoom_thread;
14985a5626b1SArnaldo Carvalho de Melo 		case '/':
1499aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
1500aca7a94dSNamhyung Kim 					"Please enter the name of symbol you want to see",
1501aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
1502aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
150305e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
150405e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
1505aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
1506aca7a94dSNamhyung Kim 			}
1507aca7a94dSNamhyung Kim 			continue;
1508cdbab7c2SFeng Tang 		case 'r':
15099783adf7SNamhyung Kim 			if (is_report_browser(hbt))
1510cdbab7c2SFeng Tang 				goto do_scripts;
1511c77d8d70SFeng Tang 			continue;
1512341487abSFeng Tang 		case 's':
1513341487abSFeng Tang 			if (is_report_browser(hbt))
1514341487abSFeng Tang 				goto do_data_switch;
1515341487abSFeng Tang 			continue;
1516aca7a94dSNamhyung Kim 		case K_F1:
1517aca7a94dSNamhyung Kim 		case 'h':
1518aca7a94dSNamhyung Kim 		case '?':
1519aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
1520*e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
1521aca7a94dSNamhyung Kim 			continue;
1522aca7a94dSNamhyung Kim 		case K_ENTER:
1523aca7a94dSNamhyung Kim 		case K_RIGHT:
1524aca7a94dSNamhyung Kim 			/* menu */
1525aca7a94dSNamhyung Kim 			break;
1526aca7a94dSNamhyung Kim 		case K_LEFT: {
1527aca7a94dSNamhyung Kim 			const void *top;
1528aca7a94dSNamhyung Kim 
1529aca7a94dSNamhyung Kim 			if (pstack__empty(fstack)) {
1530aca7a94dSNamhyung Kim 				/*
1531aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
1532aca7a94dSNamhyung Kim 				 */
1533aca7a94dSNamhyung Kim 				if (left_exits)
1534aca7a94dSNamhyung Kim 					goto out_free_stack;
1535aca7a94dSNamhyung Kim 				continue;
1536aca7a94dSNamhyung Kim 			}
1537aca7a94dSNamhyung Kim 			top = pstack__pop(fstack);
1538aca7a94dSNamhyung Kim 			if (top == &browser->hists->dso_filter)
1539aca7a94dSNamhyung Kim 				goto zoom_out_dso;
1540aca7a94dSNamhyung Kim 			if (top == &browser->hists->thread_filter)
1541aca7a94dSNamhyung Kim 				goto zoom_out_thread;
1542aca7a94dSNamhyung Kim 			continue;
1543aca7a94dSNamhyung Kim 		}
1544aca7a94dSNamhyung Kim 		case K_ESC:
1545aca7a94dSNamhyung Kim 			if (!left_exits &&
1546aca7a94dSNamhyung Kim 			    !ui_browser__dialog_yesno(&browser->b,
1547aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1548aca7a94dSNamhyung Kim 				continue;
1549aca7a94dSNamhyung Kim 			/* Fall thru */
1550aca7a94dSNamhyung Kim 		case 'q':
1551aca7a94dSNamhyung Kim 		case CTRL('c'):
1552aca7a94dSNamhyung Kim 			goto out_free_stack;
1553aca7a94dSNamhyung Kim 		default:
1554aca7a94dSNamhyung Kim 			continue;
1555aca7a94dSNamhyung Kim 		}
1556aca7a94dSNamhyung Kim 
15579c796ec8SArnaldo Carvalho de Melo 		if (!sort__has_sym)
1558aca7a94dSNamhyung Kim 			goto add_exit_option;
1559aca7a94dSNamhyung Kim 
156055369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
1561aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
1562aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1563aca7a94dSNamhyung Kim 			    bi &&
1564aca7a94dSNamhyung Kim 			    bi->from.sym != NULL &&
1565aca7a94dSNamhyung Kim 			    !bi->from.map->dso->annotate_warned &&
1566aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1567aca7a94dSNamhyung Kim 					 bi->from.sym->name) > 0)
1568aca7a94dSNamhyung Kim 				annotate_f = nr_options++;
1569aca7a94dSNamhyung Kim 
1570aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1571aca7a94dSNamhyung Kim 			    bi &&
1572aca7a94dSNamhyung Kim 			    bi->to.sym != NULL &&
1573aca7a94dSNamhyung Kim 			    !bi->to.map->dso->annotate_warned &&
1574aca7a94dSNamhyung Kim 			    (bi->to.sym != bi->from.sym ||
1575aca7a94dSNamhyung Kim 			     bi->to.map->dso != bi->from.map->dso) &&
1576aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1577aca7a94dSNamhyung Kim 					 bi->to.sym->name) > 0)
1578aca7a94dSNamhyung Kim 				annotate_t = nr_options++;
1579aca7a94dSNamhyung Kim 		} else {
1580aca7a94dSNamhyung Kim 
1581aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1582aca7a94dSNamhyung Kim 			    browser->selection->sym != NULL &&
1583aca7a94dSNamhyung Kim 			    !browser->selection->map->dso->annotate_warned &&
1584aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1585aca7a94dSNamhyung Kim 					 browser->selection->sym->name) > 0)
1586aca7a94dSNamhyung Kim 				annotate = nr_options++;
1587aca7a94dSNamhyung Kim 		}
1588aca7a94dSNamhyung Kim 
1589aca7a94dSNamhyung Kim 		if (thread != NULL &&
1590aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1591aca7a94dSNamhyung Kim 			     (browser->hists->thread_filter ? "out of" : "into"),
1592b9c5143aSFrederic Weisbecker 			     (thread->comm_set ? thread__comm_str(thread) : ""),
159338051234SAdrian Hunter 			     thread->tid) > 0)
1594aca7a94dSNamhyung Kim 			zoom_thread = nr_options++;
1595aca7a94dSNamhyung Kim 
1596aca7a94dSNamhyung Kim 		if (dso != NULL &&
1597aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1598aca7a94dSNamhyung Kim 			     (browser->hists->dso_filter ? "out of" : "into"),
1599aca7a94dSNamhyung Kim 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1600aca7a94dSNamhyung Kim 			zoom_dso = nr_options++;
1601aca7a94dSNamhyung Kim 
1602aca7a94dSNamhyung Kim 		if (browser->selection != NULL &&
1603aca7a94dSNamhyung Kim 		    browser->selection->map != NULL &&
1604aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Browse map details") > 0)
1605aca7a94dSNamhyung Kim 			browse_map = nr_options++;
1606cdbab7c2SFeng Tang 
1607cdbab7c2SFeng Tang 		/* perf script support */
1608cdbab7c2SFeng Tang 		if (browser->he_selection) {
1609cdbab7c2SFeng Tang 			struct symbol *sym;
1610cdbab7c2SFeng Tang 
1611cdbab7c2SFeng Tang 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1612b9c5143aSFrederic Weisbecker 				     thread__comm_str(browser->he_selection->thread)) > 0)
1613cdbab7c2SFeng Tang 				scripts_comm = nr_options++;
1614cdbab7c2SFeng Tang 
1615cdbab7c2SFeng Tang 			sym = browser->he_selection->ms.sym;
1616cdbab7c2SFeng Tang 			if (sym && sym->namelen &&
1617cdbab7c2SFeng Tang 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1618cdbab7c2SFeng Tang 						sym->name) > 0)
1619cdbab7c2SFeng Tang 				scripts_symbol = nr_options++;
1620cdbab7c2SFeng Tang 		}
1621cdbab7c2SFeng Tang 
1622cdbab7c2SFeng Tang 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1623cdbab7c2SFeng Tang 			scripts_all = nr_options++;
1624cdbab7c2SFeng Tang 
1625341487abSFeng Tang 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1626341487abSFeng Tang 				"Switch to another data file in PWD") > 0)
1627341487abSFeng Tang 			switch_data = nr_options++;
1628aca7a94dSNamhyung Kim add_exit_option:
1629aca7a94dSNamhyung Kim 		options[nr_options++] = (char *)"Exit";
1630aca7a94dSNamhyung Kim retry_popup_menu:
1631aca7a94dSNamhyung Kim 		choice = ui__popup_menu(nr_options, options);
1632aca7a94dSNamhyung Kim 
1633aca7a94dSNamhyung Kim 		if (choice == nr_options - 1)
1634aca7a94dSNamhyung Kim 			break;
1635aca7a94dSNamhyung Kim 
1636aca7a94dSNamhyung Kim 		if (choice == -1) {
1637aca7a94dSNamhyung Kim 			free_popup_options(options, nr_options - 1);
1638aca7a94dSNamhyung Kim 			continue;
1639aca7a94dSNamhyung Kim 		}
1640aca7a94dSNamhyung Kim 
1641aca7a94dSNamhyung Kim 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1642aca7a94dSNamhyung Kim 			struct hist_entry *he;
1643aca7a94dSNamhyung Kim 			int err;
1644aca7a94dSNamhyung Kim do_annotate:
164568d80758SNamhyung Kim 			if (!objdump_path && perf_session_env__lookup_objdump(env))
164668d80758SNamhyung Kim 				continue;
164768d80758SNamhyung Kim 
1648aca7a94dSNamhyung Kim 			he = hist_browser__selected_entry(browser);
1649aca7a94dSNamhyung Kim 			if (he == NULL)
1650aca7a94dSNamhyung Kim 				continue;
1651aca7a94dSNamhyung Kim 
1652aca7a94dSNamhyung Kim 			/*
1653aca7a94dSNamhyung Kim 			 * we stash the branch_info symbol + map into the
1654aca7a94dSNamhyung Kim 			 * the ms so we don't have to rewrite all the annotation
1655aca7a94dSNamhyung Kim 			 * code to use branch_info.
1656aca7a94dSNamhyung Kim 			 * in branch mode, the ms struct is not used
1657aca7a94dSNamhyung Kim 			 */
1658aca7a94dSNamhyung Kim 			if (choice == annotate_f) {
1659aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->from.sym;
1660aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->from.map;
1661aca7a94dSNamhyung Kim 			}  else if (choice == annotate_t) {
1662aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->to.sym;
1663aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->to.map;
1664aca7a94dSNamhyung Kim 			}
1665aca7a94dSNamhyung Kim 
1666aca7a94dSNamhyung Kim 			/*
1667aca7a94dSNamhyung Kim 			 * Don't let this be freed, say, by hists__decay_entry.
1668aca7a94dSNamhyung Kim 			 */
1669aca7a94dSNamhyung Kim 			he->used = true;
1670db8fd07aSNamhyung Kim 			err = hist_entry__tui_annotate(he, evsel, hbt);
1671aca7a94dSNamhyung Kim 			he->used = false;
1672aca7a94dSNamhyung Kim 			/*
1673aca7a94dSNamhyung Kim 			 * offer option to annotate the other branch source or target
1674aca7a94dSNamhyung Kim 			 * (if they exists) when returning from annotate
1675aca7a94dSNamhyung Kim 			 */
1676aca7a94dSNamhyung Kim 			if ((err == 'q' || err == CTRL('c'))
1677aca7a94dSNamhyung Kim 			    && annotate_t != -2 && annotate_f != -2)
1678aca7a94dSNamhyung Kim 				goto retry_popup_menu;
1679aca7a94dSNamhyung Kim 
1680aca7a94dSNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1681aca7a94dSNamhyung Kim 			if (err)
1682aca7a94dSNamhyung Kim 				ui_browser__handle_resize(&browser->b);
1683aca7a94dSNamhyung Kim 
1684aca7a94dSNamhyung Kim 		} else if (choice == browse_map)
1685aca7a94dSNamhyung Kim 			map__browse(browser->selection->map);
1686aca7a94dSNamhyung Kim 		else if (choice == zoom_dso) {
1687aca7a94dSNamhyung Kim zoom_dso:
1688aca7a94dSNamhyung Kim 			if (browser->hists->dso_filter) {
1689aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->dso_filter);
1690aca7a94dSNamhyung Kim zoom_out_dso:
1691aca7a94dSNamhyung Kim 				ui_helpline__pop();
1692aca7a94dSNamhyung Kim 				browser->hists->dso_filter = NULL;
1693aca7a94dSNamhyung Kim 				sort_dso.elide = false;
1694aca7a94dSNamhyung Kim 			} else {
1695aca7a94dSNamhyung Kim 				if (dso == NULL)
1696aca7a94dSNamhyung Kim 					continue;
1697aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1698aca7a94dSNamhyung Kim 						   dso->kernel ? "the Kernel" : dso->short_name);
1699aca7a94dSNamhyung Kim 				browser->hists->dso_filter = dso;
1700aca7a94dSNamhyung Kim 				sort_dso.elide = true;
1701aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->dso_filter);
1702aca7a94dSNamhyung Kim 			}
170305e8b080SArnaldo Carvalho de Melo 			hists__filter_by_dso(hists);
1704aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1705aca7a94dSNamhyung Kim 		} else if (choice == zoom_thread) {
1706aca7a94dSNamhyung Kim zoom_thread:
1707aca7a94dSNamhyung Kim 			if (browser->hists->thread_filter) {
1708aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->thread_filter);
1709aca7a94dSNamhyung Kim zoom_out_thread:
1710aca7a94dSNamhyung Kim 				ui_helpline__pop();
1711aca7a94dSNamhyung Kim 				browser->hists->thread_filter = NULL;
1712aca7a94dSNamhyung Kim 				sort_thread.elide = false;
1713aca7a94dSNamhyung Kim 			} else {
1714aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1715b9c5143aSFrederic Weisbecker 						   thread->comm_set ? thread__comm_str(thread) : "",
171638051234SAdrian Hunter 						   thread->tid);
1717aca7a94dSNamhyung Kim 				browser->hists->thread_filter = thread;
1718aca7a94dSNamhyung Kim 				sort_thread.elide = true;
1719aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->thread_filter);
1720aca7a94dSNamhyung Kim 			}
172105e8b080SArnaldo Carvalho de Melo 			hists__filter_by_thread(hists);
1722aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1723aca7a94dSNamhyung Kim 		}
1724cdbab7c2SFeng Tang 		/* perf scripts support */
1725cdbab7c2SFeng Tang 		else if (choice == scripts_all || choice == scripts_comm ||
1726cdbab7c2SFeng Tang 				choice == scripts_symbol) {
1727cdbab7c2SFeng Tang do_scripts:
1728cdbab7c2SFeng Tang 			memset(script_opt, 0, 64);
1729cdbab7c2SFeng Tang 
1730cdbab7c2SFeng Tang 			if (choice == scripts_comm)
1731b9c5143aSFrederic Weisbecker 				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1732cdbab7c2SFeng Tang 
1733cdbab7c2SFeng Tang 			if (choice == scripts_symbol)
1734cdbab7c2SFeng Tang 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1735cdbab7c2SFeng Tang 
1736cdbab7c2SFeng Tang 			script_browse(script_opt);
1737cdbab7c2SFeng Tang 		}
1738341487abSFeng Tang 		/* Switch to another data file */
1739341487abSFeng Tang 		else if (choice == switch_data) {
1740341487abSFeng Tang do_data_switch:
1741341487abSFeng Tang 			if (!switch_data_file()) {
1742341487abSFeng Tang 				key = K_SWITCH_INPUT_DATA;
1743341487abSFeng Tang 				break;
1744341487abSFeng Tang 			} else
1745341487abSFeng Tang 				ui__warning("Won't switch the data files due to\n"
1746341487abSFeng Tang 					"no valid data file get selected!\n");
1747341487abSFeng Tang 		}
1748aca7a94dSNamhyung Kim 	}
1749aca7a94dSNamhyung Kim out_free_stack:
1750aca7a94dSNamhyung Kim 	pstack__delete(fstack);
1751aca7a94dSNamhyung Kim out:
1752aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
1753aca7a94dSNamhyung Kim 	free_popup_options(options, nr_options - 1);
1754aca7a94dSNamhyung Kim 	return key;
1755aca7a94dSNamhyung Kim }
1756aca7a94dSNamhyung Kim 
1757aca7a94dSNamhyung Kim struct perf_evsel_menu {
1758aca7a94dSNamhyung Kim 	struct ui_browser b;
1759aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
1760aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
1761064f1981SNamhyung Kim 	float min_pcnt;
176268d80758SNamhyung Kim 	struct perf_session_env *env;
1763aca7a94dSNamhyung Kim };
1764aca7a94dSNamhyung Kim 
1765aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
1766aca7a94dSNamhyung Kim 				   void *entry, int row)
1767aca7a94dSNamhyung Kim {
1768aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
1769aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
1770aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1771aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
1772aca7a94dSNamhyung Kim 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
17737289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
1774aca7a94dSNamhyung Kim 	char bf[256], unit;
1775aca7a94dSNamhyung Kim 	const char *warn = " ";
1776aca7a94dSNamhyung Kim 	size_t printed;
1777aca7a94dSNamhyung Kim 
1778aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1779aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
1780aca7a94dSNamhyung Kim 
1781759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1782717e263fSNamhyung Kim 		struct perf_evsel *pos;
1783717e263fSNamhyung Kim 
1784717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
1785717e263fSNamhyung Kim 
1786717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
1787717e263fSNamhyung Kim 			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1788717e263fSNamhyung Kim 		}
1789717e263fSNamhyung Kim 	}
1790717e263fSNamhyung Kim 
1791aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
1792aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1793aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
1794aca7a94dSNamhyung Kim 	slsmg_printf("%s", bf);
1795aca7a94dSNamhyung Kim 
1796aca7a94dSNamhyung Kim 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1797aca7a94dSNamhyung Kim 	if (nr_events != 0) {
1798aca7a94dSNamhyung Kim 		menu->lost_events = true;
1799aca7a94dSNamhyung Kim 		if (!current_entry)
1800aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1801aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
1802aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1803aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
1804aca7a94dSNamhyung Kim 		warn = bf;
1805aca7a94dSNamhyung Kim 	}
1806aca7a94dSNamhyung Kim 
1807aca7a94dSNamhyung Kim 	slsmg_write_nstring(warn, browser->width - printed);
1808aca7a94dSNamhyung Kim 
1809aca7a94dSNamhyung Kim 	if (current_entry)
1810aca7a94dSNamhyung Kim 		menu->selection = evsel;
1811aca7a94dSNamhyung Kim }
1812aca7a94dSNamhyung Kim 
1813aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1814aca7a94dSNamhyung Kim 				int nr_events, const char *help,
18159783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
1816aca7a94dSNamhyung Kim {
1817aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
1818aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1819aca7a94dSNamhyung Kim 	const char *ev_name, *title = "Available samples";
18209783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
1821aca7a94dSNamhyung Kim 	int key;
1822aca7a94dSNamhyung Kim 
1823aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
1824aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1825aca7a94dSNamhyung Kim 		return -1;
1826aca7a94dSNamhyung Kim 
1827aca7a94dSNamhyung Kim 	while (1) {
1828aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
1829aca7a94dSNamhyung Kim 
1830aca7a94dSNamhyung Kim 		switch (key) {
1831aca7a94dSNamhyung Kim 		case K_TIMER:
18329783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
1833aca7a94dSNamhyung Kim 
1834aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
1835aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
1836aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
1837aca7a94dSNamhyung Kim 			}
1838aca7a94dSNamhyung Kim 			continue;
1839aca7a94dSNamhyung Kim 		case K_RIGHT:
1840aca7a94dSNamhyung Kim 		case K_ENTER:
1841aca7a94dSNamhyung Kim 			if (!menu->selection)
1842aca7a94dSNamhyung Kim 				continue;
1843aca7a94dSNamhyung Kim 			pos = menu->selection;
1844aca7a94dSNamhyung Kim browse_hists:
1845aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
1846aca7a94dSNamhyung Kim 			/*
1847aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
1848aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
1849aca7a94dSNamhyung Kim 			 */
18509783adf7SNamhyung Kim 			if (hbt)
18519783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
18527289f83cSArnaldo Carvalho de Melo 			ev_name = perf_evsel__name(pos);
1853aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
185468d80758SNamhyung Kim 						       ev_name, true, hbt,
1855064f1981SNamhyung Kim 						       menu->min_pcnt,
185668d80758SNamhyung Kim 						       menu->env);
1857aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
1858aca7a94dSNamhyung Kim 			switch (key) {
1859aca7a94dSNamhyung Kim 			case K_TAB:
1860aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
18619a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
1862aca7a94dSNamhyung Kim 				else
18639a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
1864aca7a94dSNamhyung Kim 				goto browse_hists;
1865aca7a94dSNamhyung Kim 			case K_UNTAB:
1866aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
18679a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
1868aca7a94dSNamhyung Kim 				else
1869d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
1870aca7a94dSNamhyung Kim 				goto browse_hists;
1871aca7a94dSNamhyung Kim 			case K_ESC:
1872aca7a94dSNamhyung Kim 				if (!ui_browser__dialog_yesno(&menu->b,
1873aca7a94dSNamhyung Kim 						"Do you really want to exit?"))
1874aca7a94dSNamhyung Kim 					continue;
1875aca7a94dSNamhyung Kim 				/* Fall thru */
1876341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
1877aca7a94dSNamhyung Kim 			case 'q':
1878aca7a94dSNamhyung Kim 			case CTRL('c'):
1879aca7a94dSNamhyung Kim 				goto out;
1880aca7a94dSNamhyung Kim 			default:
1881aca7a94dSNamhyung Kim 				continue;
1882aca7a94dSNamhyung Kim 			}
1883aca7a94dSNamhyung Kim 		case K_LEFT:
1884aca7a94dSNamhyung Kim 			continue;
1885aca7a94dSNamhyung Kim 		case K_ESC:
1886aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
1887aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1888aca7a94dSNamhyung Kim 				continue;
1889aca7a94dSNamhyung Kim 			/* Fall thru */
1890aca7a94dSNamhyung Kim 		case 'q':
1891aca7a94dSNamhyung Kim 		case CTRL('c'):
1892aca7a94dSNamhyung Kim 			goto out;
1893aca7a94dSNamhyung Kim 		default:
1894aca7a94dSNamhyung Kim 			continue;
1895aca7a94dSNamhyung Kim 		}
1896aca7a94dSNamhyung Kim 	}
1897aca7a94dSNamhyung Kim 
1898aca7a94dSNamhyung Kim out:
1899aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
1900aca7a94dSNamhyung Kim 	return key;
1901aca7a94dSNamhyung Kim }
1902aca7a94dSNamhyung Kim 
1903316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1904fc24d7c2SNamhyung Kim 				 void *entry)
1905fc24d7c2SNamhyung Kim {
1906fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1907fc24d7c2SNamhyung Kim 
1908fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1909fc24d7c2SNamhyung Kim 		return true;
1910fc24d7c2SNamhyung Kim 
1911fc24d7c2SNamhyung Kim 	return false;
1912fc24d7c2SNamhyung Kim }
1913fc24d7c2SNamhyung Kim 
1914aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1915fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
191668d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
1917064f1981SNamhyung Kim 					   float min_pcnt,
191868d80758SNamhyung Kim 					   struct perf_session_env *env)
1919aca7a94dSNamhyung Kim {
1920aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1921aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
1922aca7a94dSNamhyung Kim 		.b = {
1923aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
1924aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
1925aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
1926aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
1927fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
1928fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
1929aca7a94dSNamhyung Kim 			.priv	    = evlist,
1930aca7a94dSNamhyung Kim 		},
1931064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
193268d80758SNamhyung Kim 		.env = env,
1933aca7a94dSNamhyung Kim 	};
1934aca7a94dSNamhyung Kim 
1935aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
1936aca7a94dSNamhyung Kim 
1937aca7a94dSNamhyung Kim 	list_for_each_entry(pos, &evlist->entries, node) {
19387289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
1939aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
1940aca7a94dSNamhyung Kim 
1941aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
1942aca7a94dSNamhyung Kim 			menu.b.width = line_len;
1943aca7a94dSNamhyung Kim 	}
1944aca7a94dSNamhyung Kim 
1945fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1946aca7a94dSNamhyung Kim }
1947aca7a94dSNamhyung Kim 
1948aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
194968d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
1950064f1981SNamhyung Kim 				  float min_pcnt,
195168d80758SNamhyung Kim 				  struct perf_session_env *env)
1952aca7a94dSNamhyung Kim {
1953fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
1954fc24d7c2SNamhyung Kim 
1955fc24d7c2SNamhyung Kim single_entry:
1956fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
19579a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
19587289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(first);
1959fc24d7c2SNamhyung Kim 
1960fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
1961064f1981SNamhyung Kim 						ev_name, false, hbt, min_pcnt,
1962064f1981SNamhyung Kim 						env);
1963aca7a94dSNamhyung Kim 	}
1964aca7a94dSNamhyung Kim 
1965fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
1966fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
1967fc24d7c2SNamhyung Kim 
1968fc24d7c2SNamhyung Kim 		nr_entries = 0;
1969fc24d7c2SNamhyung Kim 		list_for_each_entry(pos, &evlist->entries, node)
1970fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
1971fc24d7c2SNamhyung Kim 				nr_entries++;
1972fc24d7c2SNamhyung Kim 
1973fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
1974fc24d7c2SNamhyung Kim 			goto single_entry;
1975fc24d7c2SNamhyung Kim 	}
1976fc24d7c2SNamhyung Kim 
1977fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1978064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
1979aca7a94dSNamhyung Kim }
1980