xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 316c7136)
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 
1403aca7a94dSNamhyung Kim 	if (browser == NULL)
1404aca7a94dSNamhyung Kim 		return -1;
1405aca7a94dSNamhyung Kim 
1406064f1981SNamhyung Kim 	if (min_pcnt) {
1407064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
1408064f1981SNamhyung Kim 		hist_browser__update_pcnt_entries(browser);
1409064f1981SNamhyung Kim 	}
1410064f1981SNamhyung Kim 
1411aca7a94dSNamhyung Kim 	fstack = pstack__new(2);
1412aca7a94dSNamhyung Kim 	if (fstack == NULL)
1413aca7a94dSNamhyung Kim 		goto out;
1414aca7a94dSNamhyung Kim 
1415aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
1416aca7a94dSNamhyung Kim 
1417aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
1418aca7a94dSNamhyung Kim 
1419aca7a94dSNamhyung Kim 	while (1) {
1420aca7a94dSNamhyung Kim 		const struct thread *thread = NULL;
1421aca7a94dSNamhyung Kim 		const struct dso *dso = NULL;
1422aca7a94dSNamhyung Kim 		int choice = 0,
1423aca7a94dSNamhyung Kim 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1424aca7a94dSNamhyung Kim 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1425341487abSFeng Tang 		int scripts_comm = -2, scripts_symbol = -2,
1426341487abSFeng Tang 		    scripts_all = -2, switch_data = -2;
1427aca7a94dSNamhyung Kim 
1428aca7a94dSNamhyung Kim 		nr_options = 0;
1429aca7a94dSNamhyung Kim 
14309783adf7SNamhyung Kim 		key = hist_browser__run(browser, ev_name, hbt);
1431aca7a94dSNamhyung Kim 
1432aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
1433aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
1434aca7a94dSNamhyung Kim 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1435aca7a94dSNamhyung Kim 		}
1436aca7a94dSNamhyung Kim 		switch (key) {
1437aca7a94dSNamhyung Kim 		case K_TAB:
1438aca7a94dSNamhyung Kim 		case K_UNTAB:
1439aca7a94dSNamhyung Kim 			if (nr_events == 1)
1440aca7a94dSNamhyung Kim 				continue;
1441aca7a94dSNamhyung Kim 			/*
1442aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
1443aca7a94dSNamhyung Kim 			 * go to the next or previous
1444aca7a94dSNamhyung Kim 			 */
1445aca7a94dSNamhyung Kim 			goto out_free_stack;
1446aca7a94dSNamhyung Kim 		case 'a':
14479c796ec8SArnaldo Carvalho de Melo 			if (!sort__has_sym) {
1448aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
1449aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
1450aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
1451aca7a94dSNamhyung Kim 				continue;
1452aca7a94dSNamhyung Kim 			}
1453aca7a94dSNamhyung Kim 
1454aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
1455aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
1456aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
1457aca7a94dSNamhyung Kim 				continue;
1458aca7a94dSNamhyung Kim 			goto do_annotate;
1459aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
1460aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
1461aff3f3f6SArnaldo Carvalho de Melo 			continue;
1462aca7a94dSNamhyung Kim 		case 'd':
1463aca7a94dSNamhyung Kim 			goto zoom_dso;
1464a7cb8863SArnaldo Carvalho de Melo 		case 'V':
1465a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
1466a7cb8863SArnaldo Carvalho de Melo 			continue;
1467aca7a94dSNamhyung Kim 		case 't':
1468aca7a94dSNamhyung Kim 			goto zoom_thread;
14695a5626b1SArnaldo Carvalho de Melo 		case '/':
1470aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
1471aca7a94dSNamhyung Kim 					"Please enter the name of symbol you want to see",
1472aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
1473aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
147405e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
147505e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
1476aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
1477aca7a94dSNamhyung Kim 			}
1478aca7a94dSNamhyung Kim 			continue;
1479cdbab7c2SFeng Tang 		case 'r':
14809783adf7SNamhyung Kim 			if (is_report_browser(hbt))
1481cdbab7c2SFeng Tang 				goto do_scripts;
1482c77d8d70SFeng Tang 			continue;
1483341487abSFeng Tang 		case 's':
1484341487abSFeng Tang 			if (is_report_browser(hbt))
1485341487abSFeng Tang 				goto do_data_switch;
1486341487abSFeng Tang 			continue;
1487aca7a94dSNamhyung Kim 		case K_F1:
1488aca7a94dSNamhyung Kim 		case 'h':
1489aca7a94dSNamhyung Kim 		case '?':
1490aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
1491aca7a94dSNamhyung Kim 					"h/?/F1        Show this window\n"
1492aca7a94dSNamhyung Kim 					"UP/DOWN/PGUP\n"
1493aca7a94dSNamhyung Kim 					"PGDN/SPACE    Navigate\n"
1494aca7a94dSNamhyung Kim 					"q/ESC/CTRL+C  Exit browser\n\n"
1495aca7a94dSNamhyung Kim 					"For multiple event sessions:\n\n"
1496aca7a94dSNamhyung Kim 					"TAB/UNTAB Switch events\n\n"
1497aca7a94dSNamhyung Kim 					"For symbolic views (--sort has sym):\n\n"
1498aca7a94dSNamhyung Kim 					"->            Zoom into DSO/Threads & Annotate current symbol\n"
1499aca7a94dSNamhyung Kim 					"<-            Zoom out\n"
1500aca7a94dSNamhyung Kim 					"a             Annotate current symbol\n"
1501aca7a94dSNamhyung Kim 					"C             Collapse all callchains\n"
1502aca7a94dSNamhyung Kim 					"E             Expand all callchains\n"
1503aca7a94dSNamhyung Kim 					"d             Zoom into current DSO\n"
1504aca7a94dSNamhyung Kim 					"t             Zoom into current Thread\n"
1505c77d8d70SFeng Tang 					"r             Run available scripts('perf report' only)\n"
1506341487abSFeng Tang 					"s             Switch to another data file in PWD ('perf report' only)\n"
1507aff3f3f6SArnaldo Carvalho de Melo 					"P             Print histograms to perf.hist.N\n"
1508a7cb8863SArnaldo Carvalho de Melo 					"V             Verbose (DSO names in callchains, etc)\n"
15095a5626b1SArnaldo Carvalho de Melo 					"/             Filter symbol by name");
1510aca7a94dSNamhyung Kim 			continue;
1511aca7a94dSNamhyung Kim 		case K_ENTER:
1512aca7a94dSNamhyung Kim 		case K_RIGHT:
1513aca7a94dSNamhyung Kim 			/* menu */
1514aca7a94dSNamhyung Kim 			break;
1515aca7a94dSNamhyung Kim 		case K_LEFT: {
1516aca7a94dSNamhyung Kim 			const void *top;
1517aca7a94dSNamhyung Kim 
1518aca7a94dSNamhyung Kim 			if (pstack__empty(fstack)) {
1519aca7a94dSNamhyung Kim 				/*
1520aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
1521aca7a94dSNamhyung Kim 				 */
1522aca7a94dSNamhyung Kim 				if (left_exits)
1523aca7a94dSNamhyung Kim 					goto out_free_stack;
1524aca7a94dSNamhyung Kim 				continue;
1525aca7a94dSNamhyung Kim 			}
1526aca7a94dSNamhyung Kim 			top = pstack__pop(fstack);
1527aca7a94dSNamhyung Kim 			if (top == &browser->hists->dso_filter)
1528aca7a94dSNamhyung Kim 				goto zoom_out_dso;
1529aca7a94dSNamhyung Kim 			if (top == &browser->hists->thread_filter)
1530aca7a94dSNamhyung Kim 				goto zoom_out_thread;
1531aca7a94dSNamhyung Kim 			continue;
1532aca7a94dSNamhyung Kim 		}
1533aca7a94dSNamhyung Kim 		case K_ESC:
1534aca7a94dSNamhyung Kim 			if (!left_exits &&
1535aca7a94dSNamhyung Kim 			    !ui_browser__dialog_yesno(&browser->b,
1536aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1537aca7a94dSNamhyung Kim 				continue;
1538aca7a94dSNamhyung Kim 			/* Fall thru */
1539aca7a94dSNamhyung Kim 		case 'q':
1540aca7a94dSNamhyung Kim 		case CTRL('c'):
1541aca7a94dSNamhyung Kim 			goto out_free_stack;
1542aca7a94dSNamhyung Kim 		default:
1543aca7a94dSNamhyung Kim 			continue;
1544aca7a94dSNamhyung Kim 		}
1545aca7a94dSNamhyung Kim 
15469c796ec8SArnaldo Carvalho de Melo 		if (!sort__has_sym)
1547aca7a94dSNamhyung Kim 			goto add_exit_option;
1548aca7a94dSNamhyung Kim 
154955369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
1550aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
1551aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1552aca7a94dSNamhyung Kim 			    bi &&
1553aca7a94dSNamhyung Kim 			    bi->from.sym != NULL &&
1554aca7a94dSNamhyung Kim 			    !bi->from.map->dso->annotate_warned &&
1555aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1556aca7a94dSNamhyung Kim 					 bi->from.sym->name) > 0)
1557aca7a94dSNamhyung Kim 				annotate_f = nr_options++;
1558aca7a94dSNamhyung Kim 
1559aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1560aca7a94dSNamhyung Kim 			    bi &&
1561aca7a94dSNamhyung Kim 			    bi->to.sym != NULL &&
1562aca7a94dSNamhyung Kim 			    !bi->to.map->dso->annotate_warned &&
1563aca7a94dSNamhyung Kim 			    (bi->to.sym != bi->from.sym ||
1564aca7a94dSNamhyung Kim 			     bi->to.map->dso != bi->from.map->dso) &&
1565aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1566aca7a94dSNamhyung Kim 					 bi->to.sym->name) > 0)
1567aca7a94dSNamhyung Kim 				annotate_t = nr_options++;
1568aca7a94dSNamhyung Kim 		} else {
1569aca7a94dSNamhyung Kim 
1570aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1571aca7a94dSNamhyung Kim 			    browser->selection->sym != NULL &&
1572aca7a94dSNamhyung Kim 			    !browser->selection->map->dso->annotate_warned &&
1573aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1574aca7a94dSNamhyung Kim 					 browser->selection->sym->name) > 0)
1575aca7a94dSNamhyung Kim 				annotate = nr_options++;
1576aca7a94dSNamhyung Kim 		}
1577aca7a94dSNamhyung Kim 
1578aca7a94dSNamhyung Kim 		if (thread != NULL &&
1579aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1580aca7a94dSNamhyung Kim 			     (browser->hists->thread_filter ? "out of" : "into"),
1581b9c5143aSFrederic Weisbecker 			     (thread->comm_set ? thread__comm_str(thread) : ""),
158238051234SAdrian Hunter 			     thread->tid) > 0)
1583aca7a94dSNamhyung Kim 			zoom_thread = nr_options++;
1584aca7a94dSNamhyung Kim 
1585aca7a94dSNamhyung Kim 		if (dso != NULL &&
1586aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1587aca7a94dSNamhyung Kim 			     (browser->hists->dso_filter ? "out of" : "into"),
1588aca7a94dSNamhyung Kim 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1589aca7a94dSNamhyung Kim 			zoom_dso = nr_options++;
1590aca7a94dSNamhyung Kim 
1591aca7a94dSNamhyung Kim 		if (browser->selection != NULL &&
1592aca7a94dSNamhyung Kim 		    browser->selection->map != NULL &&
1593aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Browse map details") > 0)
1594aca7a94dSNamhyung Kim 			browse_map = nr_options++;
1595cdbab7c2SFeng Tang 
1596cdbab7c2SFeng Tang 		/* perf script support */
1597cdbab7c2SFeng Tang 		if (browser->he_selection) {
1598cdbab7c2SFeng Tang 			struct symbol *sym;
1599cdbab7c2SFeng Tang 
1600cdbab7c2SFeng Tang 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1601b9c5143aSFrederic Weisbecker 				     thread__comm_str(browser->he_selection->thread)) > 0)
1602cdbab7c2SFeng Tang 				scripts_comm = nr_options++;
1603cdbab7c2SFeng Tang 
1604cdbab7c2SFeng Tang 			sym = browser->he_selection->ms.sym;
1605cdbab7c2SFeng Tang 			if (sym && sym->namelen &&
1606cdbab7c2SFeng Tang 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1607cdbab7c2SFeng Tang 						sym->name) > 0)
1608cdbab7c2SFeng Tang 				scripts_symbol = nr_options++;
1609cdbab7c2SFeng Tang 		}
1610cdbab7c2SFeng Tang 
1611cdbab7c2SFeng Tang 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1612cdbab7c2SFeng Tang 			scripts_all = nr_options++;
1613cdbab7c2SFeng Tang 
1614341487abSFeng Tang 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1615341487abSFeng Tang 				"Switch to another data file in PWD") > 0)
1616341487abSFeng Tang 			switch_data = nr_options++;
1617aca7a94dSNamhyung Kim add_exit_option:
1618aca7a94dSNamhyung Kim 		options[nr_options++] = (char *)"Exit";
1619aca7a94dSNamhyung Kim retry_popup_menu:
1620aca7a94dSNamhyung Kim 		choice = ui__popup_menu(nr_options, options);
1621aca7a94dSNamhyung Kim 
1622aca7a94dSNamhyung Kim 		if (choice == nr_options - 1)
1623aca7a94dSNamhyung Kim 			break;
1624aca7a94dSNamhyung Kim 
1625aca7a94dSNamhyung Kim 		if (choice == -1) {
1626aca7a94dSNamhyung Kim 			free_popup_options(options, nr_options - 1);
1627aca7a94dSNamhyung Kim 			continue;
1628aca7a94dSNamhyung Kim 		}
1629aca7a94dSNamhyung Kim 
1630aca7a94dSNamhyung Kim 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1631aca7a94dSNamhyung Kim 			struct hist_entry *he;
1632aca7a94dSNamhyung Kim 			int err;
1633aca7a94dSNamhyung Kim do_annotate:
163468d80758SNamhyung Kim 			if (!objdump_path && perf_session_env__lookup_objdump(env))
163568d80758SNamhyung Kim 				continue;
163668d80758SNamhyung Kim 
1637aca7a94dSNamhyung Kim 			he = hist_browser__selected_entry(browser);
1638aca7a94dSNamhyung Kim 			if (he == NULL)
1639aca7a94dSNamhyung Kim 				continue;
1640aca7a94dSNamhyung Kim 
1641aca7a94dSNamhyung Kim 			/*
1642aca7a94dSNamhyung Kim 			 * we stash the branch_info symbol + map into the
1643aca7a94dSNamhyung Kim 			 * the ms so we don't have to rewrite all the annotation
1644aca7a94dSNamhyung Kim 			 * code to use branch_info.
1645aca7a94dSNamhyung Kim 			 * in branch mode, the ms struct is not used
1646aca7a94dSNamhyung Kim 			 */
1647aca7a94dSNamhyung Kim 			if (choice == annotate_f) {
1648aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->from.sym;
1649aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->from.map;
1650aca7a94dSNamhyung Kim 			}  else if (choice == annotate_t) {
1651aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->to.sym;
1652aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->to.map;
1653aca7a94dSNamhyung Kim 			}
1654aca7a94dSNamhyung Kim 
1655aca7a94dSNamhyung Kim 			/*
1656aca7a94dSNamhyung Kim 			 * Don't let this be freed, say, by hists__decay_entry.
1657aca7a94dSNamhyung Kim 			 */
1658aca7a94dSNamhyung Kim 			he->used = true;
1659db8fd07aSNamhyung Kim 			err = hist_entry__tui_annotate(he, evsel, hbt);
1660aca7a94dSNamhyung Kim 			he->used = false;
1661aca7a94dSNamhyung Kim 			/*
1662aca7a94dSNamhyung Kim 			 * offer option to annotate the other branch source or target
1663aca7a94dSNamhyung Kim 			 * (if they exists) when returning from annotate
1664aca7a94dSNamhyung Kim 			 */
1665aca7a94dSNamhyung Kim 			if ((err == 'q' || err == CTRL('c'))
1666aca7a94dSNamhyung Kim 			    && annotate_t != -2 && annotate_f != -2)
1667aca7a94dSNamhyung Kim 				goto retry_popup_menu;
1668aca7a94dSNamhyung Kim 
1669aca7a94dSNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1670aca7a94dSNamhyung Kim 			if (err)
1671aca7a94dSNamhyung Kim 				ui_browser__handle_resize(&browser->b);
1672aca7a94dSNamhyung Kim 
1673aca7a94dSNamhyung Kim 		} else if (choice == browse_map)
1674aca7a94dSNamhyung Kim 			map__browse(browser->selection->map);
1675aca7a94dSNamhyung Kim 		else if (choice == zoom_dso) {
1676aca7a94dSNamhyung Kim zoom_dso:
1677aca7a94dSNamhyung Kim 			if (browser->hists->dso_filter) {
1678aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->dso_filter);
1679aca7a94dSNamhyung Kim zoom_out_dso:
1680aca7a94dSNamhyung Kim 				ui_helpline__pop();
1681aca7a94dSNamhyung Kim 				browser->hists->dso_filter = NULL;
1682aca7a94dSNamhyung Kim 				sort_dso.elide = false;
1683aca7a94dSNamhyung Kim 			} else {
1684aca7a94dSNamhyung Kim 				if (dso == NULL)
1685aca7a94dSNamhyung Kim 					continue;
1686aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1687aca7a94dSNamhyung Kim 						   dso->kernel ? "the Kernel" : dso->short_name);
1688aca7a94dSNamhyung Kim 				browser->hists->dso_filter = dso;
1689aca7a94dSNamhyung Kim 				sort_dso.elide = true;
1690aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->dso_filter);
1691aca7a94dSNamhyung Kim 			}
169205e8b080SArnaldo Carvalho de Melo 			hists__filter_by_dso(hists);
1693aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1694aca7a94dSNamhyung Kim 		} else if (choice == zoom_thread) {
1695aca7a94dSNamhyung Kim zoom_thread:
1696aca7a94dSNamhyung Kim 			if (browser->hists->thread_filter) {
1697aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->thread_filter);
1698aca7a94dSNamhyung Kim zoom_out_thread:
1699aca7a94dSNamhyung Kim 				ui_helpline__pop();
1700aca7a94dSNamhyung Kim 				browser->hists->thread_filter = NULL;
1701aca7a94dSNamhyung Kim 				sort_thread.elide = false;
1702aca7a94dSNamhyung Kim 			} else {
1703aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1704b9c5143aSFrederic Weisbecker 						   thread->comm_set ? thread__comm_str(thread) : "",
170538051234SAdrian Hunter 						   thread->tid);
1706aca7a94dSNamhyung Kim 				browser->hists->thread_filter = thread;
1707aca7a94dSNamhyung Kim 				sort_thread.elide = true;
1708aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->thread_filter);
1709aca7a94dSNamhyung Kim 			}
171005e8b080SArnaldo Carvalho de Melo 			hists__filter_by_thread(hists);
1711aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1712aca7a94dSNamhyung Kim 		}
1713cdbab7c2SFeng Tang 		/* perf scripts support */
1714cdbab7c2SFeng Tang 		else if (choice == scripts_all || choice == scripts_comm ||
1715cdbab7c2SFeng Tang 				choice == scripts_symbol) {
1716cdbab7c2SFeng Tang do_scripts:
1717cdbab7c2SFeng Tang 			memset(script_opt, 0, 64);
1718cdbab7c2SFeng Tang 
1719cdbab7c2SFeng Tang 			if (choice == scripts_comm)
1720b9c5143aSFrederic Weisbecker 				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1721cdbab7c2SFeng Tang 
1722cdbab7c2SFeng Tang 			if (choice == scripts_symbol)
1723cdbab7c2SFeng Tang 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1724cdbab7c2SFeng Tang 
1725cdbab7c2SFeng Tang 			script_browse(script_opt);
1726cdbab7c2SFeng Tang 		}
1727341487abSFeng Tang 		/* Switch to another data file */
1728341487abSFeng Tang 		else if (choice == switch_data) {
1729341487abSFeng Tang do_data_switch:
1730341487abSFeng Tang 			if (!switch_data_file()) {
1731341487abSFeng Tang 				key = K_SWITCH_INPUT_DATA;
1732341487abSFeng Tang 				break;
1733341487abSFeng Tang 			} else
1734341487abSFeng Tang 				ui__warning("Won't switch the data files due to\n"
1735341487abSFeng Tang 					"no valid data file get selected!\n");
1736341487abSFeng Tang 		}
1737aca7a94dSNamhyung Kim 	}
1738aca7a94dSNamhyung Kim out_free_stack:
1739aca7a94dSNamhyung Kim 	pstack__delete(fstack);
1740aca7a94dSNamhyung Kim out:
1741aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
1742aca7a94dSNamhyung Kim 	free_popup_options(options, nr_options - 1);
1743aca7a94dSNamhyung Kim 	return key;
1744aca7a94dSNamhyung Kim }
1745aca7a94dSNamhyung Kim 
1746aca7a94dSNamhyung Kim struct perf_evsel_menu {
1747aca7a94dSNamhyung Kim 	struct ui_browser b;
1748aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
1749aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
1750064f1981SNamhyung Kim 	float min_pcnt;
175168d80758SNamhyung Kim 	struct perf_session_env *env;
1752aca7a94dSNamhyung Kim };
1753aca7a94dSNamhyung Kim 
1754aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
1755aca7a94dSNamhyung Kim 				   void *entry, int row)
1756aca7a94dSNamhyung Kim {
1757aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
1758aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
1759aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1760aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
1761aca7a94dSNamhyung Kim 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
17627289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
1763aca7a94dSNamhyung Kim 	char bf[256], unit;
1764aca7a94dSNamhyung Kim 	const char *warn = " ";
1765aca7a94dSNamhyung Kim 	size_t printed;
1766aca7a94dSNamhyung Kim 
1767aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1768aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
1769aca7a94dSNamhyung Kim 
1770759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1771717e263fSNamhyung Kim 		struct perf_evsel *pos;
1772717e263fSNamhyung Kim 
1773717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
1774717e263fSNamhyung Kim 
1775717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
1776717e263fSNamhyung Kim 			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1777717e263fSNamhyung Kim 		}
1778717e263fSNamhyung Kim 	}
1779717e263fSNamhyung Kim 
1780aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
1781aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1782aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
1783aca7a94dSNamhyung Kim 	slsmg_printf("%s", bf);
1784aca7a94dSNamhyung Kim 
1785aca7a94dSNamhyung Kim 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1786aca7a94dSNamhyung Kim 	if (nr_events != 0) {
1787aca7a94dSNamhyung Kim 		menu->lost_events = true;
1788aca7a94dSNamhyung Kim 		if (!current_entry)
1789aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1790aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
1791aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1792aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
1793aca7a94dSNamhyung Kim 		warn = bf;
1794aca7a94dSNamhyung Kim 	}
1795aca7a94dSNamhyung Kim 
1796aca7a94dSNamhyung Kim 	slsmg_write_nstring(warn, browser->width - printed);
1797aca7a94dSNamhyung Kim 
1798aca7a94dSNamhyung Kim 	if (current_entry)
1799aca7a94dSNamhyung Kim 		menu->selection = evsel;
1800aca7a94dSNamhyung Kim }
1801aca7a94dSNamhyung Kim 
1802aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1803aca7a94dSNamhyung Kim 				int nr_events, const char *help,
18049783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
1805aca7a94dSNamhyung Kim {
1806aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
1807aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1808aca7a94dSNamhyung Kim 	const char *ev_name, *title = "Available samples";
18099783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
1810aca7a94dSNamhyung Kim 	int key;
1811aca7a94dSNamhyung Kim 
1812aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
1813aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1814aca7a94dSNamhyung Kim 		return -1;
1815aca7a94dSNamhyung Kim 
1816aca7a94dSNamhyung Kim 	while (1) {
1817aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
1818aca7a94dSNamhyung Kim 
1819aca7a94dSNamhyung Kim 		switch (key) {
1820aca7a94dSNamhyung Kim 		case K_TIMER:
18219783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
1822aca7a94dSNamhyung Kim 
1823aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
1824aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
1825aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
1826aca7a94dSNamhyung Kim 			}
1827aca7a94dSNamhyung Kim 			continue;
1828aca7a94dSNamhyung Kim 		case K_RIGHT:
1829aca7a94dSNamhyung Kim 		case K_ENTER:
1830aca7a94dSNamhyung Kim 			if (!menu->selection)
1831aca7a94dSNamhyung Kim 				continue;
1832aca7a94dSNamhyung Kim 			pos = menu->selection;
1833aca7a94dSNamhyung Kim browse_hists:
1834aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
1835aca7a94dSNamhyung Kim 			/*
1836aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
1837aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
1838aca7a94dSNamhyung Kim 			 */
18399783adf7SNamhyung Kim 			if (hbt)
18409783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
18417289f83cSArnaldo Carvalho de Melo 			ev_name = perf_evsel__name(pos);
1842aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
184368d80758SNamhyung Kim 						       ev_name, true, hbt,
1844064f1981SNamhyung Kim 						       menu->min_pcnt,
184568d80758SNamhyung Kim 						       menu->env);
1846aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
1847aca7a94dSNamhyung Kim 			switch (key) {
1848aca7a94dSNamhyung Kim 			case K_TAB:
1849aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
1850aca7a94dSNamhyung Kim 					pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1851aca7a94dSNamhyung Kim 				else
1852aca7a94dSNamhyung Kim 					pos = list_entry(pos->node.next, struct perf_evsel, node);
1853aca7a94dSNamhyung Kim 				goto browse_hists;
1854aca7a94dSNamhyung Kim 			case K_UNTAB:
1855aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
1856aca7a94dSNamhyung Kim 					pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1857aca7a94dSNamhyung Kim 				else
1858aca7a94dSNamhyung Kim 					pos = list_entry(pos->node.prev, struct perf_evsel, node);
1859aca7a94dSNamhyung Kim 				goto browse_hists;
1860aca7a94dSNamhyung Kim 			case K_ESC:
1861aca7a94dSNamhyung Kim 				if (!ui_browser__dialog_yesno(&menu->b,
1862aca7a94dSNamhyung Kim 						"Do you really want to exit?"))
1863aca7a94dSNamhyung Kim 					continue;
1864aca7a94dSNamhyung Kim 				/* Fall thru */
1865341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
1866aca7a94dSNamhyung Kim 			case 'q':
1867aca7a94dSNamhyung Kim 			case CTRL('c'):
1868aca7a94dSNamhyung Kim 				goto out;
1869aca7a94dSNamhyung Kim 			default:
1870aca7a94dSNamhyung Kim 				continue;
1871aca7a94dSNamhyung Kim 			}
1872aca7a94dSNamhyung Kim 		case K_LEFT:
1873aca7a94dSNamhyung Kim 			continue;
1874aca7a94dSNamhyung Kim 		case K_ESC:
1875aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
1876aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1877aca7a94dSNamhyung Kim 				continue;
1878aca7a94dSNamhyung Kim 			/* Fall thru */
1879aca7a94dSNamhyung Kim 		case 'q':
1880aca7a94dSNamhyung Kim 		case CTRL('c'):
1881aca7a94dSNamhyung Kim 			goto out;
1882aca7a94dSNamhyung Kim 		default:
1883aca7a94dSNamhyung Kim 			continue;
1884aca7a94dSNamhyung Kim 		}
1885aca7a94dSNamhyung Kim 	}
1886aca7a94dSNamhyung Kim 
1887aca7a94dSNamhyung Kim out:
1888aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
1889aca7a94dSNamhyung Kim 	return key;
1890aca7a94dSNamhyung Kim }
1891aca7a94dSNamhyung Kim 
1892*316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1893fc24d7c2SNamhyung Kim 				 void *entry)
1894fc24d7c2SNamhyung Kim {
1895fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1896fc24d7c2SNamhyung Kim 
1897fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1898fc24d7c2SNamhyung Kim 		return true;
1899fc24d7c2SNamhyung Kim 
1900fc24d7c2SNamhyung Kim 	return false;
1901fc24d7c2SNamhyung Kim }
1902fc24d7c2SNamhyung Kim 
1903aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1904fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
190568d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
1906064f1981SNamhyung Kim 					   float min_pcnt,
190768d80758SNamhyung Kim 					   struct perf_session_env *env)
1908aca7a94dSNamhyung Kim {
1909aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1910aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
1911aca7a94dSNamhyung Kim 		.b = {
1912aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
1913aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
1914aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
1915aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
1916fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
1917fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
1918aca7a94dSNamhyung Kim 			.priv	    = evlist,
1919aca7a94dSNamhyung Kim 		},
1920064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
192168d80758SNamhyung Kim 		.env = env,
1922aca7a94dSNamhyung Kim 	};
1923aca7a94dSNamhyung Kim 
1924aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
1925aca7a94dSNamhyung Kim 
1926aca7a94dSNamhyung Kim 	list_for_each_entry(pos, &evlist->entries, node) {
19277289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
1928aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
1929aca7a94dSNamhyung Kim 
1930aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
1931aca7a94dSNamhyung Kim 			menu.b.width = line_len;
1932aca7a94dSNamhyung Kim 	}
1933aca7a94dSNamhyung Kim 
1934fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1935aca7a94dSNamhyung Kim }
1936aca7a94dSNamhyung Kim 
1937aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
193868d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
1939064f1981SNamhyung Kim 				  float min_pcnt,
194068d80758SNamhyung Kim 				  struct perf_session_env *env)
1941aca7a94dSNamhyung Kim {
1942fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
1943fc24d7c2SNamhyung Kim 
1944fc24d7c2SNamhyung Kim single_entry:
1945fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
1946aca7a94dSNamhyung Kim 		struct perf_evsel *first = list_entry(evlist->entries.next,
1947aca7a94dSNamhyung Kim 						      struct perf_evsel, node);
19487289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(first);
1949fc24d7c2SNamhyung Kim 
1950fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
1951064f1981SNamhyung Kim 						ev_name, false, hbt, min_pcnt,
1952064f1981SNamhyung Kim 						env);
1953aca7a94dSNamhyung Kim 	}
1954aca7a94dSNamhyung Kim 
1955fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
1956fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
1957fc24d7c2SNamhyung Kim 
1958fc24d7c2SNamhyung Kim 		nr_entries = 0;
1959fc24d7c2SNamhyung Kim 		list_for_each_entry(pos, &evlist->entries, node)
1960fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
1961fc24d7c2SNamhyung Kim 				nr_entries++;
1962fc24d7c2SNamhyung Kim 
1963fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
1964fc24d7c2SNamhyung Kim 			goto single_entry;
1965fc24d7c2SNamhyung Kim 	}
1966fc24d7c2SNamhyung Kim 
1967fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1968064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
1969aca7a94dSNamhyung Kim }
1970