xref: /linux/tools/perf/ui/browsers/hists.c (revision c2a51ab802d17c572cd0a940fd97538b75aa7889)
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"
1342337a22SNamhyung Kim #include "../../util/top.h"
1468d80758SNamhyung Kim #include "../../arch/common.h"
15aca7a94dSNamhyung Kim 
16aca7a94dSNamhyung Kim #include "../browser.h"
17aca7a94dSNamhyung Kim #include "../helpline.h"
18aca7a94dSNamhyung Kim #include "../util.h"
19aca7a94dSNamhyung Kim #include "../ui.h"
20aca7a94dSNamhyung Kim #include "map.h"
21d755330cSJiri Olsa #include "annotate.h"
22aca7a94dSNamhyung Kim 
23aca7a94dSNamhyung Kim struct hist_browser {
24aca7a94dSNamhyung Kim 	struct ui_browser   b;
25aca7a94dSNamhyung Kim 	struct hists	    *hists;
26aca7a94dSNamhyung Kim 	struct hist_entry   *he_selection;
27aca7a94dSNamhyung Kim 	struct map_symbol   *selection;
28*c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt;
29aff3f3f6SArnaldo Carvalho de Melo 	int		     print_seq;
30a7cb8863SArnaldo Carvalho de Melo 	bool		     show_dso;
31025bf7eaSArnaldo Carvalho de Melo 	bool		     show_headers;
32064f1981SNamhyung Kim 	float		     min_pcnt;
33112f761fSNamhyung Kim 	u64		     nr_non_filtered_entries;
34c3b78952SNamhyung Kim 	u64		     nr_callchain_rows;
35aca7a94dSNamhyung Kim };
36aca7a94dSNamhyung Kim 
37f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
38f5951d56SNamhyung Kim 
391e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
401e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
411e378ebdSTaeung Song 				char *bf, size_t size);
42112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
43aca7a94dSNamhyung Kim 
44c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
45c3b78952SNamhyung Kim 					     float min_pcnt);
46c3b78952SNamhyung Kim 
47268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
48268397cbSNamhyung Kim {
49268397cbSNamhyung Kim 	return hists__has_filter(hb->hists) || hb->min_pcnt;
50268397cbSNamhyung Kim }
51268397cbSNamhyung Kim 
524fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
534fabf3d1SHe Kuang {
544fabf3d1SHe Kuang 	struct rb_node *nd;
554fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
564fabf3d1SHe Kuang 	int unfolded_rows = 0;
574fabf3d1SHe Kuang 
584fabf3d1SHe Kuang 	for (nd = rb_first(&hists->entries);
594fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
604fabf3d1SHe Kuang 	     nd = rb_next(nd)) {
614fabf3d1SHe Kuang 		struct hist_entry *he =
624fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
634fabf3d1SHe Kuang 
644fabf3d1SHe Kuang 		if (he->ms.unfolded)
654fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
664fabf3d1SHe Kuang 	}
674fabf3d1SHe Kuang 	return unfolded_rows;
684fabf3d1SHe Kuang }
694fabf3d1SHe Kuang 
70c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
71c3b78952SNamhyung Kim {
72c3b78952SNamhyung Kim 	u32 nr_entries;
73c3b78952SNamhyung Kim 
74c3b78952SNamhyung Kim 	if (hist_browser__has_filter(hb))
75c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
76c3b78952SNamhyung Kim 	else
77c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
78c3b78952SNamhyung Kim 
794fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
80c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
81c3b78952SNamhyung Kim }
82c3b78952SNamhyung Kim 
83025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
84025bf7eaSArnaldo Carvalho de Melo {
85025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
86025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
87025bf7eaSArnaldo Carvalho de Melo 
88025bf7eaSArnaldo Carvalho de Melo 	browser->rows = browser->height - header_offset;
89025bf7eaSArnaldo Carvalho de Melo 	/*
90025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
91025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
92025bf7eaSArnaldo Carvalho de Melo 	 */
93025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
94025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
95025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
96025bf7eaSArnaldo Carvalho de Melo }
97025bf7eaSArnaldo Carvalho de Melo 
98357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
99aca7a94dSNamhyung Kim {
100357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
101357cfff1SArnaldo Carvalho de Melo 
102aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
103357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
104357cfff1SArnaldo Carvalho de Melo 	/*
105357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
106357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
107357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
108357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
109357cfff1SArnaldo Carvalho de Melo  	 */
110357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
111025bf7eaSArnaldo Carvalho de Melo 	hist_browser__update_rows(hb);
112aca7a94dSNamhyung Kim }
113aca7a94dSNamhyung Kim 
114ca3ff33bSArnaldo Carvalho de Melo static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
115ca3ff33bSArnaldo Carvalho de Melo {
116025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = browser->show_headers ? 1 : 0;
117025bf7eaSArnaldo Carvalho de Melo 
118025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row + header_offset, column);
119ca3ff33bSArnaldo Carvalho de Melo }
120ca3ff33bSArnaldo Carvalho de Melo 
12105e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
122aca7a94dSNamhyung Kim {
123c3b78952SNamhyung Kim 	/*
124c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
125c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
126c3b78952SNamhyung Kim 	 */
127c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
128c3b78952SNamhyung Kim 
129268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
130c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
131357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
13205e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
133aca7a94dSNamhyung Kim }
134aca7a94dSNamhyung Kim 
135aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
136aca7a94dSNamhyung Kim {
137aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
138aca7a94dSNamhyung Kim }
139aca7a94dSNamhyung Kim 
14005e8b080SArnaldo Carvalho de Melo static char map_symbol__folded(const struct map_symbol *ms)
141aca7a94dSNamhyung Kim {
14205e8b080SArnaldo Carvalho de Melo 	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
143aca7a94dSNamhyung Kim }
144aca7a94dSNamhyung Kim 
14505e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
146aca7a94dSNamhyung Kim {
14705e8b080SArnaldo Carvalho de Melo 	return map_symbol__folded(&he->ms);
148aca7a94dSNamhyung Kim }
149aca7a94dSNamhyung Kim 
15005e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
151aca7a94dSNamhyung Kim {
15205e8b080SArnaldo Carvalho de Melo 	return map_symbol__folded(&cl->ms);
153aca7a94dSNamhyung Kim }
154aca7a94dSNamhyung Kim 
15505e8b080SArnaldo Carvalho de Melo static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
156aca7a94dSNamhyung Kim {
15705e8b080SArnaldo Carvalho de Melo 	ms->unfolded = unfold ? ms->has_children : false;
158aca7a94dSNamhyung Kim }
159aca7a94dSNamhyung Kim 
16005e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
161aca7a94dSNamhyung Kim {
162aca7a94dSNamhyung Kim 	int n = 0;
163aca7a94dSNamhyung Kim 	struct rb_node *nd;
164aca7a94dSNamhyung Kim 
16505e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
166aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
167aca7a94dSNamhyung Kim 		struct callchain_list *chain;
168aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
169aca7a94dSNamhyung Kim 
170aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
171aca7a94dSNamhyung Kim 			++n;
172aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
173aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
174aca7a94dSNamhyung Kim 			if (folded_sign == '+')
175aca7a94dSNamhyung Kim 				break;
176aca7a94dSNamhyung Kim 		}
177aca7a94dSNamhyung Kim 
178aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
179aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
180aca7a94dSNamhyung Kim 	}
181aca7a94dSNamhyung Kim 
182aca7a94dSNamhyung Kim 	return n;
183aca7a94dSNamhyung Kim }
184aca7a94dSNamhyung Kim 
185aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
186aca7a94dSNamhyung Kim {
187aca7a94dSNamhyung Kim 	struct callchain_list *chain;
188aca7a94dSNamhyung Kim 	bool unfolded = false;
189aca7a94dSNamhyung Kim 	int n = 0;
190aca7a94dSNamhyung Kim 
191aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
192aca7a94dSNamhyung Kim 		++n;
193aca7a94dSNamhyung Kim 		unfolded = chain->ms.unfolded;
194aca7a94dSNamhyung Kim 	}
195aca7a94dSNamhyung Kim 
196aca7a94dSNamhyung Kim 	if (unfolded)
197aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
198aca7a94dSNamhyung Kim 
199aca7a94dSNamhyung Kim 	return n;
200aca7a94dSNamhyung Kim }
201aca7a94dSNamhyung Kim 
202aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
203aca7a94dSNamhyung Kim {
204aca7a94dSNamhyung Kim 	struct rb_node *nd;
205aca7a94dSNamhyung Kim 	int n = 0;
206aca7a94dSNamhyung Kim 
207aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
208aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
209aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
210aca7a94dSNamhyung Kim 	}
211aca7a94dSNamhyung Kim 
212aca7a94dSNamhyung Kim 	return n;
213aca7a94dSNamhyung Kim }
214aca7a94dSNamhyung Kim 
21505e8b080SArnaldo Carvalho de Melo static bool map_symbol__toggle_fold(struct map_symbol *ms)
216aca7a94dSNamhyung Kim {
21705e8b080SArnaldo Carvalho de Melo 	if (!ms)
218aca7a94dSNamhyung Kim 		return false;
219aca7a94dSNamhyung Kim 
22005e8b080SArnaldo Carvalho de Melo 	if (!ms->has_children)
221aca7a94dSNamhyung Kim 		return false;
222aca7a94dSNamhyung Kim 
22305e8b080SArnaldo Carvalho de Melo 	ms->unfolded = !ms->unfolded;
224aca7a94dSNamhyung Kim 	return true;
225aca7a94dSNamhyung Kim }
226aca7a94dSNamhyung Kim 
22705e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
228aca7a94dSNamhyung Kim {
22905e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
230aca7a94dSNamhyung Kim 
23105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
232aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
233aca7a94dSNamhyung Kim 		struct callchain_list *chain;
234aca7a94dSNamhyung Kim 		bool first = true;
235aca7a94dSNamhyung Kim 
236aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
237aca7a94dSNamhyung Kim 			if (first) {
238aca7a94dSNamhyung Kim 				first = false;
239aca7a94dSNamhyung Kim 				chain->ms.has_children = chain->list.next != &child->val ||
240aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
241aca7a94dSNamhyung Kim 			} else
242aca7a94dSNamhyung Kim 				chain->ms.has_children = chain->list.next == &child->val &&
243aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
244aca7a94dSNamhyung Kim 		}
245aca7a94dSNamhyung Kim 
246aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
247aca7a94dSNamhyung Kim 	}
248aca7a94dSNamhyung Kim }
249aca7a94dSNamhyung Kim 
250a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
251a7444af6SNamhyung Kim 					       bool has_sibling)
252aca7a94dSNamhyung Kim {
253aca7a94dSNamhyung Kim 	struct callchain_list *chain;
254aca7a94dSNamhyung Kim 
255a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
256a7444af6SNamhyung Kim 	chain->ms.has_children = has_sibling;
257a7444af6SNamhyung Kim 
25882162b5aSNamhyung Kim 	if (!list_empty(&node->val)) {
25982162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
26005e8b080SArnaldo Carvalho de Melo 		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
26182162b5aSNamhyung Kim 	}
262aca7a94dSNamhyung Kim 
26305e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
264aca7a94dSNamhyung Kim }
265aca7a94dSNamhyung Kim 
26605e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
267aca7a94dSNamhyung Kim {
268a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
269a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
270aca7a94dSNamhyung Kim 
27105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
272aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
273a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
274aca7a94dSNamhyung Kim 	}
275aca7a94dSNamhyung Kim }
276aca7a94dSNamhyung Kim 
27705e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
278aca7a94dSNamhyung Kim {
27905e8b080SArnaldo Carvalho de Melo 	if (!he->init_have_children) {
28005e8b080SArnaldo Carvalho de Melo 		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
28105e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
28205e8b080SArnaldo Carvalho de Melo 		he->init_have_children = true;
283aca7a94dSNamhyung Kim 	}
284aca7a94dSNamhyung Kim }
285aca7a94dSNamhyung Kim 
28605e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
287aca7a94dSNamhyung Kim {
28805e8b080SArnaldo Carvalho de Melo 	if (map_symbol__toggle_fold(browser->selection)) {
28905e8b080SArnaldo Carvalho de Melo 		struct hist_entry *he = browser->he_selection;
290aca7a94dSNamhyung Kim 
291aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
292c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
293c3b78952SNamhyung Kim 		browser->nr_callchain_rows -= he->nr_rows;
294aca7a94dSNamhyung Kim 
295aca7a94dSNamhyung Kim 		if (he->ms.unfolded)
296aca7a94dSNamhyung Kim 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
297aca7a94dSNamhyung Kim 		else
298aca7a94dSNamhyung Kim 			he->nr_rows = 0;
299c3b78952SNamhyung Kim 
300c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
301c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
302aca7a94dSNamhyung Kim 
303aca7a94dSNamhyung Kim 		return true;
304aca7a94dSNamhyung Kim 	}
305aca7a94dSNamhyung Kim 
306aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
307aca7a94dSNamhyung Kim 	return false;
308aca7a94dSNamhyung Kim }
309aca7a94dSNamhyung Kim 
31005e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
311aca7a94dSNamhyung Kim {
312aca7a94dSNamhyung Kim 	int n = 0;
313aca7a94dSNamhyung Kim 	struct rb_node *nd;
314aca7a94dSNamhyung Kim 
31505e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
316aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
317aca7a94dSNamhyung Kim 		struct callchain_list *chain;
318aca7a94dSNamhyung Kim 		bool has_children = false;
319aca7a94dSNamhyung Kim 
320aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
321aca7a94dSNamhyung Kim 			++n;
322aca7a94dSNamhyung Kim 			map_symbol__set_folding(&chain->ms, unfold);
323aca7a94dSNamhyung Kim 			has_children = chain->ms.has_children;
324aca7a94dSNamhyung Kim 		}
325aca7a94dSNamhyung Kim 
326aca7a94dSNamhyung Kim 		if (has_children)
327aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
328aca7a94dSNamhyung Kim 	}
329aca7a94dSNamhyung Kim 
330aca7a94dSNamhyung Kim 	return n;
331aca7a94dSNamhyung Kim }
332aca7a94dSNamhyung Kim 
333aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
334aca7a94dSNamhyung Kim {
335aca7a94dSNamhyung Kim 	struct callchain_list *chain;
336aca7a94dSNamhyung Kim 	bool has_children = false;
337aca7a94dSNamhyung Kim 	int n = 0;
338aca7a94dSNamhyung Kim 
339aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
340aca7a94dSNamhyung Kim 		++n;
341aca7a94dSNamhyung Kim 		map_symbol__set_folding(&chain->ms, unfold);
342aca7a94dSNamhyung Kim 		has_children = chain->ms.has_children;
343aca7a94dSNamhyung Kim 	}
344aca7a94dSNamhyung Kim 
345aca7a94dSNamhyung Kim 	if (has_children)
346aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
347aca7a94dSNamhyung Kim 
348aca7a94dSNamhyung Kim 	return n;
349aca7a94dSNamhyung Kim }
350aca7a94dSNamhyung Kim 
351aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
352aca7a94dSNamhyung Kim {
353aca7a94dSNamhyung Kim 	struct rb_node *nd;
354aca7a94dSNamhyung Kim 	int n = 0;
355aca7a94dSNamhyung Kim 
356aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
357aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
358aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
359aca7a94dSNamhyung Kim 	}
360aca7a94dSNamhyung Kim 
361aca7a94dSNamhyung Kim 	return n;
362aca7a94dSNamhyung Kim }
363aca7a94dSNamhyung Kim 
36405e8b080SArnaldo Carvalho de Melo static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
365aca7a94dSNamhyung Kim {
36605e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
36705e8b080SArnaldo Carvalho de Melo 	map_symbol__set_folding(&he->ms, unfold);
368aca7a94dSNamhyung Kim 
36905e8b080SArnaldo Carvalho de Melo 	if (he->ms.has_children) {
37005e8b080SArnaldo Carvalho de Melo 		int n = callchain__set_folding(&he->sorted_chain, unfold);
37105e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
372aca7a94dSNamhyung Kim 	} else
37305e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
374aca7a94dSNamhyung Kim }
375aca7a94dSNamhyung Kim 
376c3b78952SNamhyung Kim static void
377c3b78952SNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
378aca7a94dSNamhyung Kim {
379aca7a94dSNamhyung Kim 	struct rb_node *nd;
380c3b78952SNamhyung Kim 	struct hists *hists = browser->hists;
381aca7a94dSNamhyung Kim 
382c3b78952SNamhyung Kim 	for (nd = rb_first(&hists->entries);
38314135663SNamhyung Kim 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
384c3b78952SNamhyung Kim 	     nd = rb_next(nd)) {
385aca7a94dSNamhyung Kim 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
386aca7a94dSNamhyung Kim 		hist_entry__set_folding(he, unfold);
387c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
388aca7a94dSNamhyung Kim 	}
389aca7a94dSNamhyung Kim }
390aca7a94dSNamhyung Kim 
39105e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
392aca7a94dSNamhyung Kim {
393c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
394c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
395c3b78952SNamhyung Kim 
396c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
397aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
39805e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
399aca7a94dSNamhyung Kim }
400aca7a94dSNamhyung Kim 
401aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
402aca7a94dSNamhyung Kim {
403aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
404aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
405aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
406aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
407aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
408aca7a94dSNamhyung Kim }
409aca7a94dSNamhyung Kim 
410*c2a51ab8SNamhyung Kim static int hist_browser__run(struct hist_browser *browser)
411aca7a94dSNamhyung Kim {
412aca7a94dSNamhyung Kim 	int key;
413aca7a94dSNamhyung Kim 	char title[160];
414*c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
4159783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
416aca7a94dSNamhyung Kim 
41705e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
418c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
419aca7a94dSNamhyung Kim 
4201e378ebdSTaeung Song 	hists__browser_title(browser->hists, hbt, title, sizeof(title));
421aca7a94dSNamhyung Kim 
42205e8b080SArnaldo Carvalho de Melo 	if (ui_browser__show(&browser->b, title,
423aca7a94dSNamhyung Kim 			     "Press '?' for help on key bindings") < 0)
424aca7a94dSNamhyung Kim 		return -1;
425aca7a94dSNamhyung Kim 
426aca7a94dSNamhyung Kim 	while (1) {
42705e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
428aca7a94dSNamhyung Kim 
429aca7a94dSNamhyung Kim 		switch (key) {
430fa5df943SNamhyung Kim 		case K_TIMER: {
431fa5df943SNamhyung Kim 			u64 nr_entries;
4329783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
433fa5df943SNamhyung Kim 
434c3b78952SNamhyung Kim 			if (hist_browser__has_filter(browser))
435112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
436fa5df943SNamhyung Kim 
437c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
438fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
439aca7a94dSNamhyung Kim 
44005e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
44105e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
44205e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
44305e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
44405e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
445aca7a94dSNamhyung Kim 			}
446aca7a94dSNamhyung Kim 
4471e378ebdSTaeung Song 			hists__browser_title(browser->hists,
4481e378ebdSTaeung Song 					     hbt, title, sizeof(title));
44905e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
450aca7a94dSNamhyung Kim 			continue;
451fa5df943SNamhyung Kim 		}
452aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
453aca7a94dSNamhyung Kim 			static int seq;
45405e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
455aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
456aca7a94dSNamhyung Kim 			ui_helpline__pop();
45762c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
45805e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
45905e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
46062c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
46105e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
46205e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
463aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
464aca7a94dSNamhyung Kim 		}
465aca7a94dSNamhyung Kim 			break;
466aca7a94dSNamhyung Kim 		case 'C':
467aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
46805e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
469aca7a94dSNamhyung Kim 			break;
470aca7a94dSNamhyung Kim 		case 'E':
471aca7a94dSNamhyung Kim 			/* Expand the whole world. */
47205e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
473aca7a94dSNamhyung Kim 			break;
474025bf7eaSArnaldo Carvalho de Melo 		case 'H':
475025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
476025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
477025bf7eaSArnaldo Carvalho de Melo 			break;
478aca7a94dSNamhyung Kim 		case K_ENTER:
47905e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
480aca7a94dSNamhyung Kim 				break;
481aca7a94dSNamhyung Kim 			/* fall thru */
482aca7a94dSNamhyung Kim 		default:
483aca7a94dSNamhyung Kim 			goto out;
484aca7a94dSNamhyung Kim 		}
485aca7a94dSNamhyung Kim 	}
486aca7a94dSNamhyung Kim out:
48705e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
488aca7a94dSNamhyung Kim 	return key;
489aca7a94dSNamhyung Kim }
490aca7a94dSNamhyung Kim 
49139ee533fSNamhyung Kim struct callchain_print_arg {
49239ee533fSNamhyung Kim 	/* for hists browser */
49339ee533fSNamhyung Kim 	off_t	row_offset;
49439ee533fSNamhyung Kim 	bool	is_current_entry;
49539ee533fSNamhyung Kim 
49639ee533fSNamhyung Kim 	/* for file dump */
49739ee533fSNamhyung Kim 	FILE	*fp;
49839ee533fSNamhyung Kim 	int	printed;
49939ee533fSNamhyung Kim };
50039ee533fSNamhyung Kim 
50139ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
50239ee533fSNamhyung Kim 					 struct callchain_list *chain,
50339ee533fSNamhyung Kim 					 const char *str, int offset,
50439ee533fSNamhyung Kim 					 unsigned short row,
50539ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
50639ee533fSNamhyung Kim 
507f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
508f4536dddSNamhyung Kim 					       struct callchain_list *chain,
50939ee533fSNamhyung Kim 					       const char *str, int offset,
51039ee533fSNamhyung Kim 					       unsigned short row,
51139ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
512f4536dddSNamhyung Kim {
513f4536dddSNamhyung Kim 	int color, width;
51439ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
51570e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
516f4536dddSNamhyung Kim 
517f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
518f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
519f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
520f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
521f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
52239ee533fSNamhyung Kim 		arg->is_current_entry = true;
523f4536dddSNamhyung Kim 	}
524f4536dddSNamhyung Kim 
525f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
526f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
527f4536dddSNamhyung Kim 	slsmg_write_nstring(" ", offset);
528f4536dddSNamhyung Kim 	slsmg_printf("%c", folded_sign);
52970e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
530f4536dddSNamhyung Kim 	slsmg_write_nstring(str, width);
531f4536dddSNamhyung Kim }
532f4536dddSNamhyung Kim 
53339ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
53439ee533fSNamhyung Kim 						  struct callchain_list *chain,
53539ee533fSNamhyung Kim 						  const char *str, int offset,
53639ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
53739ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
53839ee533fSNamhyung Kim {
53939ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
54039ee533fSNamhyung Kim 
54139ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
54239ee533fSNamhyung Kim 				folded_sign, str);
54339ee533fSNamhyung Kim }
54439ee533fSNamhyung Kim 
54539ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
54639ee533fSNamhyung Kim 				     unsigned short row);
54739ee533fSNamhyung Kim 
54839ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
54939ee533fSNamhyung Kim 					    unsigned short row)
55039ee533fSNamhyung Kim {
55139ee533fSNamhyung Kim 	return browser->b.rows == row;
55239ee533fSNamhyung Kim }
55339ee533fSNamhyung Kim 
55439ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
55539ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
55639ee533fSNamhyung Kim {
55739ee533fSNamhyung Kim 	return false;
55839ee533fSNamhyung Kim }
55939ee533fSNamhyung Kim 
560aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
561aca7a94dSNamhyung Kim 
562c09a7e75SNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
563c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
56439ee533fSNamhyung Kim 					unsigned short row, u64 total,
56539ee533fSNamhyung Kim 					print_callchain_entry_fn print,
56639ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
56739ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
568aca7a94dSNamhyung Kim {
569aca7a94dSNamhyung Kim 	struct rb_node *node;
570f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
57136e15dd4SNamhyung Kim 	u64 new_total;
5724087d11cSNamhyung Kim 	bool need_percent;
573aca7a94dSNamhyung Kim 
574c09a7e75SNamhyung Kim 	node = rb_first(root);
575c09e31ccSNamhyung Kim 	need_percent = node && rb_next(node);
5764087d11cSNamhyung Kim 
577aca7a94dSNamhyung Kim 	while (node) {
578aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
579aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
580aca7a94dSNamhyung Kim 		u64 cumul = callchain_cumul_hits(child);
581aca7a94dSNamhyung Kim 		struct callchain_list *chain;
582aca7a94dSNamhyung Kim 		char folded_sign = ' ';
583aca7a94dSNamhyung Kim 		int first = true;
584aca7a94dSNamhyung Kim 		int extra_offset = 0;
585aca7a94dSNamhyung Kim 
586aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
587a7cb8863SArnaldo Carvalho de Melo 			char bf[1024], *alloc_str;
588aca7a94dSNamhyung Kim 			const char *str;
589aca7a94dSNamhyung Kim 			bool was_first = first;
590aca7a94dSNamhyung Kim 
591aca7a94dSNamhyung Kim 			if (first)
592aca7a94dSNamhyung Kim 				first = false;
5934087d11cSNamhyung Kim 			else if (need_percent)
594aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
595aca7a94dSNamhyung Kim 
596aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
59739ee533fSNamhyung Kim 			if (arg->row_offset != 0) {
59839ee533fSNamhyung Kim 				arg->row_offset--;
599aca7a94dSNamhyung Kim 				goto do_next;
600aca7a94dSNamhyung Kim 			}
601aca7a94dSNamhyung Kim 
602aca7a94dSNamhyung Kim 			alloc_str = NULL;
603a7cb8863SArnaldo Carvalho de Melo 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
604a7cb8863SArnaldo Carvalho de Melo 						       browser->show_dso);
605c09a7e75SNamhyung Kim 
6064087d11cSNamhyung Kim 			if (was_first && need_percent) {
607c09a7e75SNamhyung Kim 				double percent = cumul * 100.0 / total;
608aca7a94dSNamhyung Kim 
609aca7a94dSNamhyung Kim 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
610aca7a94dSNamhyung Kim 					str = "Not enough memory!";
611aca7a94dSNamhyung Kim 				else
612aca7a94dSNamhyung Kim 					str = alloc_str;
613aca7a94dSNamhyung Kim 			}
614aca7a94dSNamhyung Kim 
61539ee533fSNamhyung Kim 			print(browser, chain, str, offset + extra_offset, row, arg);
61639ee533fSNamhyung Kim 
617aca7a94dSNamhyung Kim 			free(alloc_str);
618aca7a94dSNamhyung Kim 
61939ee533fSNamhyung Kim 			if (is_output_full(browser, ++row))
620aca7a94dSNamhyung Kim 				goto out;
621aca7a94dSNamhyung Kim do_next:
622aca7a94dSNamhyung Kim 			if (folded_sign == '+')
623aca7a94dSNamhyung Kim 				break;
624aca7a94dSNamhyung Kim 		}
625aca7a94dSNamhyung Kim 
626aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
627aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
628c09a7e75SNamhyung Kim 
629c09a7e75SNamhyung Kim 			if (callchain_param.mode == CHAIN_GRAPH_REL)
630c09a7e75SNamhyung Kim 				new_total = child->children_hit;
631c09a7e75SNamhyung Kim 			else
632c09a7e75SNamhyung Kim 				new_total = total;
633c09a7e75SNamhyung Kim 
634c09a7e75SNamhyung Kim 			row += hist_browser__show_callchain(browser, &child->rb_root,
63539ee533fSNamhyung Kim 							    new_level, row, new_total,
63639ee533fSNamhyung Kim 							    print, arg, is_output_full);
637aca7a94dSNamhyung Kim 		}
63839ee533fSNamhyung Kim 		if (is_output_full(browser, row))
639c09a7e75SNamhyung Kim 			break;
640aca7a94dSNamhyung Kim 		node = next;
641aca7a94dSNamhyung Kim 	}
642aca7a94dSNamhyung Kim out:
643aca7a94dSNamhyung Kim 	return row - first_row;
644aca7a94dSNamhyung Kim }
645aca7a94dSNamhyung Kim 
64689701460SNamhyung Kim struct hpp_arg {
64789701460SNamhyung Kim 	struct ui_browser *b;
64889701460SNamhyung Kim 	char folded_sign;
64989701460SNamhyung Kim 	bool current_entry;
65089701460SNamhyung Kim };
65189701460SNamhyung Kim 
6522f6d9009SNamhyung Kim static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
6532f6d9009SNamhyung Kim {
6542f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
655d675107cSNamhyung Kim 	int ret, len;
6562f6d9009SNamhyung Kim 	va_list args;
6572f6d9009SNamhyung Kim 	double percent;
6582f6d9009SNamhyung Kim 
6592f6d9009SNamhyung Kim 	va_start(args, fmt);
660d675107cSNamhyung Kim 	len = va_arg(args, int);
6612f6d9009SNamhyung Kim 	percent = va_arg(args, double);
6622f6d9009SNamhyung Kim 	va_end(args);
6635aed9d24SNamhyung Kim 
66489701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
6655aed9d24SNamhyung Kim 
666d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
66789701460SNamhyung Kim 	slsmg_printf("%s", hpp->buf);
66889701460SNamhyung Kim 
6692f6d9009SNamhyung Kim 	advance_hpp(hpp, ret);
6705aed9d24SNamhyung Kim 	return ret;
671f5951d56SNamhyung Kim }
672f5951d56SNamhyung Kim 
673fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
6745aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
6755aed9d24SNamhyung Kim {									\
6765aed9d24SNamhyung Kim 	return he->stat._field;						\
6775aed9d24SNamhyung Kim }									\
6785aed9d24SNamhyung Kim 									\
6792c5d4b4aSJiri Olsa static int								\
6805b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
6812c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
6825aed9d24SNamhyung Kim 				struct hist_entry *he)			\
6835aed9d24SNamhyung Kim {									\
6845b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
6852f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
6865aed9d24SNamhyung Kim }
687f5951d56SNamhyung Kim 
6880434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
6890434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
6900434ddd2SNamhyung Kim {									\
6910434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
6920434ddd2SNamhyung Kim }									\
6930434ddd2SNamhyung Kim 									\
6940434ddd2SNamhyung Kim static int								\
6955b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
6960434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
6970434ddd2SNamhyung Kim 				struct hist_entry *he)			\
6980434ddd2SNamhyung Kim {									\
6990434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
7005b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
701d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
7025b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
7030434ddd2SNamhyung Kim 		slsmg_printf("%s", hpp->buf);				\
7040434ddd2SNamhyung Kim 									\
7050434ddd2SNamhyung Kim 		return ret;						\
7060434ddd2SNamhyung Kim 	}								\
7075b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
7085b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
7090434ddd2SNamhyung Kim }
7100434ddd2SNamhyung Kim 
711fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
712fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
713fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
714fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
715fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
7160434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
7175aed9d24SNamhyung Kim 
7185aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
7190434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
720f5951d56SNamhyung Kim 
721f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
722f5951d56SNamhyung Kim {
723f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
724f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
725f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
726f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
727f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
728f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
729f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
730f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
731f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
732f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
7330434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
7340434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
735f5951d56SNamhyung Kim }
736f5951d56SNamhyung Kim 
73705e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
738aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
739aca7a94dSNamhyung Kim 				    unsigned short row)
740aca7a94dSNamhyung Kim {
741aca7a94dSNamhyung Kim 	char s[256];
7421240005eSJiri Olsa 	int printed = 0;
74367d25916SNamhyung Kim 	int width = browser->b.width;
744aca7a94dSNamhyung Kim 	char folded_sign = ' ';
74505e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
746aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
74763a1a3d8SNamhyung Kim 	bool first = true;
7481240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
749aca7a94dSNamhyung Kim 
750aca7a94dSNamhyung Kim 	if (current_entry) {
75105e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
75205e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
753aca7a94dSNamhyung Kim 	}
754aca7a94dSNamhyung Kim 
755aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
756aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
757aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
758aca7a94dSNamhyung Kim 	}
759aca7a94dSNamhyung Kim 
760aca7a94dSNamhyung Kim 	if (row_offset == 0) {
76189701460SNamhyung Kim 		struct hpp_arg arg = {
76289701460SNamhyung Kim 			.b		= &browser->b,
76389701460SNamhyung Kim 			.folded_sign	= folded_sign,
76489701460SNamhyung Kim 			.current_entry	= current_entry,
76589701460SNamhyung Kim 		};
766f5951d56SNamhyung Kim 		struct perf_hpp hpp = {
767f5951d56SNamhyung Kim 			.buf		= s,
768f5951d56SNamhyung Kim 			.size		= sizeof(s),
76989701460SNamhyung Kim 			.ptr		= &arg,
770f5951d56SNamhyung Kim 		};
771f5951d56SNamhyung Kim 
772ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
773f5951d56SNamhyung Kim 
7741240005eSJiri Olsa 		perf_hpp__for_each_format(fmt) {
775e67d49a7SNamhyung Kim 			if (perf_hpp__should_skip(fmt))
776e67d49a7SNamhyung Kim 				continue;
777e67d49a7SNamhyung Kim 
778fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
779fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
780fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
781fb821c9eSNamhyung Kim 			} else {
782fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
783fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
784fb821c9eSNamhyung Kim 			}
785fb821c9eSNamhyung Kim 
786fb821c9eSNamhyung Kim 			if (first) {
787fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
788fb821c9eSNamhyung Kim 					slsmg_printf("%c ", folded_sign);
789f5951d56SNamhyung Kim 					width -= 2;
790f5951d56SNamhyung Kim 				}
79163a1a3d8SNamhyung Kim 				first = false;
792fb821c9eSNamhyung Kim 			} else {
793fb821c9eSNamhyung Kim 				slsmg_printf("  ");
794fb821c9eSNamhyung Kim 				width -= 2;
795fb821c9eSNamhyung Kim 			}
796f5951d56SNamhyung Kim 
7971240005eSJiri Olsa 			if (fmt->color) {
7982c5d4b4aSJiri Olsa 				width -= fmt->color(fmt, &hpp, entry);
799f5951d56SNamhyung Kim 			} else {
8002c5d4b4aSJiri Olsa 				width -= fmt->entry(fmt, &hpp, entry);
801f5951d56SNamhyung Kim 				slsmg_printf("%s", s);
802f5951d56SNamhyung Kim 			}
803f5951d56SNamhyung Kim 		}
804aca7a94dSNamhyung Kim 
805aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
80605e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
807aca7a94dSNamhyung Kim 			width += 1;
808aca7a94dSNamhyung Kim 
80926d8b338SNamhyung Kim 		slsmg_write_nstring("", width);
81026d8b338SNamhyung Kim 
811aca7a94dSNamhyung Kim 		++row;
812aca7a94dSNamhyung Kim 		++printed;
813aca7a94dSNamhyung Kim 	} else
814aca7a94dSNamhyung Kim 		--row_offset;
815aca7a94dSNamhyung Kim 
81662c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
817c09a7e75SNamhyung Kim 		u64 total = hists__total_period(entry->hists);
81839ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
81939ee533fSNamhyung Kim 			.row_offset = row_offset,
82039ee533fSNamhyung Kim 			.is_current_entry = current_entry,
82139ee533fSNamhyung Kim 		};
822c09a7e75SNamhyung Kim 
8234087d11cSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
8244087d11cSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
8254087d11cSNamhyung Kim 				total = entry->stat_acc->period;
8264087d11cSNamhyung Kim 			else
8274087d11cSNamhyung Kim 				total = entry->stat.period;
8284087d11cSNamhyung Kim 		}
8294087d11cSNamhyung Kim 
830c09a7e75SNamhyung Kim 		printed += hist_browser__show_callchain(browser,
83139ee533fSNamhyung Kim 					&entry->sorted_chain, 1, row, total,
83239ee533fSNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
83339ee533fSNamhyung Kim 					hist_browser__check_output_full);
834c09a7e75SNamhyung Kim 
83539ee533fSNamhyung Kim 		if (arg.is_current_entry)
83605e8b080SArnaldo Carvalho de Melo 			browser->he_selection = entry;
837aca7a94dSNamhyung Kim 	}
838aca7a94dSNamhyung Kim 
839aca7a94dSNamhyung Kim 	return printed;
840aca7a94dSNamhyung Kim }
841aca7a94dSNamhyung Kim 
84281a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
84381a888feSJiri Olsa {
84481a888feSJiri Olsa 	advance_hpp(hpp, inc);
84581a888feSJiri Olsa 	return hpp->size <= 0;
84681a888feSJiri Olsa }
84781a888feSJiri Olsa 
84881a888feSJiri Olsa static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
84981a888feSJiri Olsa {
85081a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
85181a888feSJiri Olsa 		.buf    = buf,
85281a888feSJiri Olsa 		.size   = size,
85381a888feSJiri Olsa 	};
85481a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
85581a888feSJiri Olsa 	size_t ret = 0;
85681a888feSJiri Olsa 
85781a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
85881a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
85981a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
86081a888feSJiri Olsa 			return ret;
86181a888feSJiri Olsa 	}
86281a888feSJiri Olsa 
86381a888feSJiri Olsa 	perf_hpp__for_each_format(fmt) {
86481a888feSJiri Olsa 		if (perf_hpp__should_skip(fmt))
86581a888feSJiri Olsa 			continue;
86681a888feSJiri Olsa 
86781a888feSJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
86881a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
86981a888feSJiri Olsa 			break;
87081a888feSJiri Olsa 
87181a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
87281a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
87381a888feSJiri Olsa 			break;
87481a888feSJiri Olsa 	}
87581a888feSJiri Olsa 
87681a888feSJiri Olsa 	return ret;
87781a888feSJiri Olsa }
87881a888feSJiri Olsa 
879025bf7eaSArnaldo Carvalho de Melo static void hist_browser__show_headers(struct hist_browser *browser)
880025bf7eaSArnaldo Carvalho de Melo {
88181a888feSJiri Olsa 	char headers[1024];
88281a888feSJiri Olsa 
88381a888feSJiri Olsa 	hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
884025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
885025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
88681a888feSJiri Olsa 	slsmg_write_nstring(headers, browser->b.width + 1);
887025bf7eaSArnaldo Carvalho de Melo }
888025bf7eaSArnaldo Carvalho de Melo 
889aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
890aca7a94dSNamhyung Kim {
891aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
892aca7a94dSNamhyung Kim 		struct hist_browser *hb;
893aca7a94dSNamhyung Kim 
894aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
895aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
896aca7a94dSNamhyung Kim 	}
897aca7a94dSNamhyung Kim }
898aca7a94dSNamhyung Kim 
89905e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
900aca7a94dSNamhyung Kim {
901aca7a94dSNamhyung Kim 	unsigned row = 0;
902025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
903aca7a94dSNamhyung Kim 	struct rb_node *nd;
90405e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
905aca7a94dSNamhyung Kim 
906025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
907025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
908025bf7eaSArnaldo Carvalho de Melo 		header_offset = 1;
909025bf7eaSArnaldo Carvalho de Melo 	}
910025bf7eaSArnaldo Carvalho de Melo 
91105e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
912aca7a94dSNamhyung Kim 
91305e8b080SArnaldo Carvalho de Melo 	for (nd = browser->top; nd; nd = rb_next(nd)) {
914aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
91514135663SNamhyung Kim 		float percent;
916aca7a94dSNamhyung Kim 
917aca7a94dSNamhyung Kim 		if (h->filtered)
918aca7a94dSNamhyung Kim 			continue;
919aca7a94dSNamhyung Kim 
92014135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
921064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
922064f1981SNamhyung Kim 			continue;
923064f1981SNamhyung Kim 
924aca7a94dSNamhyung Kim 		row += hist_browser__show_entry(hb, h, row);
92562c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
926aca7a94dSNamhyung Kim 			break;
927aca7a94dSNamhyung Kim 	}
928aca7a94dSNamhyung Kim 
929025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
930aca7a94dSNamhyung Kim }
931aca7a94dSNamhyung Kim 
932064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
933064f1981SNamhyung Kim 					     float min_pcnt)
934aca7a94dSNamhyung Kim {
935aca7a94dSNamhyung Kim 	while (nd != NULL) {
936aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
93714135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
938064f1981SNamhyung Kim 
939c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
940aca7a94dSNamhyung Kim 			return nd;
941aca7a94dSNamhyung Kim 
942aca7a94dSNamhyung Kim 		nd = rb_next(nd);
943aca7a94dSNamhyung Kim 	}
944aca7a94dSNamhyung Kim 
945aca7a94dSNamhyung Kim 	return NULL;
946aca7a94dSNamhyung Kim }
947aca7a94dSNamhyung Kim 
948064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
949064f1981SNamhyung Kim 						  float min_pcnt)
950aca7a94dSNamhyung Kim {
951aca7a94dSNamhyung Kim 	while (nd != NULL) {
952aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
95314135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
954064f1981SNamhyung Kim 
955064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
956aca7a94dSNamhyung Kim 			return nd;
957aca7a94dSNamhyung Kim 
958aca7a94dSNamhyung Kim 		nd = rb_prev(nd);
959aca7a94dSNamhyung Kim 	}
960aca7a94dSNamhyung Kim 
961aca7a94dSNamhyung Kim 	return NULL;
962aca7a94dSNamhyung Kim }
963aca7a94dSNamhyung Kim 
96405e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
965aca7a94dSNamhyung Kim 				   off_t offset, int whence)
966aca7a94dSNamhyung Kim {
967aca7a94dSNamhyung Kim 	struct hist_entry *h;
968aca7a94dSNamhyung Kim 	struct rb_node *nd;
969aca7a94dSNamhyung Kim 	bool first = true;
970064f1981SNamhyung Kim 	struct hist_browser *hb;
971064f1981SNamhyung Kim 
972064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
973aca7a94dSNamhyung Kim 
97405e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
975aca7a94dSNamhyung Kim 		return;
976aca7a94dSNamhyung Kim 
97705e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
978aca7a94dSNamhyung Kim 
979aca7a94dSNamhyung Kim 	switch (whence) {
980aca7a94dSNamhyung Kim 	case SEEK_SET:
981064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
98214135663SNamhyung Kim 					   hb->min_pcnt);
983aca7a94dSNamhyung Kim 		break;
984aca7a94dSNamhyung Kim 	case SEEK_CUR:
98505e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
986aca7a94dSNamhyung Kim 		goto do_offset;
987aca7a94dSNamhyung Kim 	case SEEK_END:
988064f1981SNamhyung Kim 		nd = hists__filter_prev_entries(rb_last(browser->entries),
98914135663SNamhyung Kim 						hb->min_pcnt);
990aca7a94dSNamhyung Kim 		first = false;
991aca7a94dSNamhyung Kim 		break;
992aca7a94dSNamhyung Kim 	default:
993aca7a94dSNamhyung Kim 		return;
994aca7a94dSNamhyung Kim 	}
995aca7a94dSNamhyung Kim 
996aca7a94dSNamhyung Kim 	/*
997aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
998aca7a94dSNamhyung Kim 	 * row_offset:
999aca7a94dSNamhyung Kim 	 */
100005e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1001aca7a94dSNamhyung Kim 	h->row_offset = 0;
1002aca7a94dSNamhyung Kim 
1003aca7a94dSNamhyung Kim 	/*
1004aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1005aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1006aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1007aca7a94dSNamhyung Kim 	 *
1008aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1009aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1010aca7a94dSNamhyung Kim 	 *
1011aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1012aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1013aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1014aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1015aca7a94dSNamhyung Kim 	 */
1016aca7a94dSNamhyung Kim do_offset:
1017aca7a94dSNamhyung Kim 	if (offset > 0) {
1018aca7a94dSNamhyung Kim 		do {
1019aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1020aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
1021aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1022aca7a94dSNamhyung Kim 				if (offset > remaining) {
1023aca7a94dSNamhyung Kim 					offset -= remaining;
1024aca7a94dSNamhyung Kim 					h->row_offset = 0;
1025aca7a94dSNamhyung Kim 				} else {
1026aca7a94dSNamhyung Kim 					h->row_offset += offset;
1027aca7a94dSNamhyung Kim 					offset = 0;
102805e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1029aca7a94dSNamhyung Kim 					break;
1030aca7a94dSNamhyung Kim 				}
1031aca7a94dSNamhyung Kim 			}
103214135663SNamhyung Kim 			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1033aca7a94dSNamhyung Kim 			if (nd == NULL)
1034aca7a94dSNamhyung Kim 				break;
1035aca7a94dSNamhyung Kim 			--offset;
103605e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1037aca7a94dSNamhyung Kim 		} while (offset != 0);
1038aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1039aca7a94dSNamhyung Kim 		while (1) {
1040aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1041aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
1042aca7a94dSNamhyung Kim 				if (first) {
1043aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1044aca7a94dSNamhyung Kim 						offset += h->row_offset;
1045aca7a94dSNamhyung Kim 						h->row_offset = 0;
1046aca7a94dSNamhyung Kim 					} else {
1047aca7a94dSNamhyung Kim 						h->row_offset += offset;
1048aca7a94dSNamhyung Kim 						offset = 0;
104905e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1050aca7a94dSNamhyung Kim 						break;
1051aca7a94dSNamhyung Kim 					}
1052aca7a94dSNamhyung Kim 				} else {
1053aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1054aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1055aca7a94dSNamhyung Kim 						h->row_offset = 0;
1056aca7a94dSNamhyung Kim 					} else {
1057aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1058aca7a94dSNamhyung Kim 						offset = 0;
105905e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1060aca7a94dSNamhyung Kim 						break;
1061aca7a94dSNamhyung Kim 					}
1062aca7a94dSNamhyung Kim 				}
1063aca7a94dSNamhyung Kim 			}
1064aca7a94dSNamhyung Kim 
106514135663SNamhyung Kim 			nd = hists__filter_prev_entries(rb_prev(nd),
1066064f1981SNamhyung Kim 							hb->min_pcnt);
1067aca7a94dSNamhyung Kim 			if (nd == NULL)
1068aca7a94dSNamhyung Kim 				break;
1069aca7a94dSNamhyung Kim 			++offset;
107005e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1071aca7a94dSNamhyung Kim 			if (offset == 0) {
1072aca7a94dSNamhyung Kim 				/*
1073aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1074aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1075aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1076aca7a94dSNamhyung Kim 				 */
1077aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
1078aca7a94dSNamhyung Kim 				if (h->ms.unfolded)
1079aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1080aca7a94dSNamhyung Kim 				break;
1081aca7a94dSNamhyung Kim 			}
1082aca7a94dSNamhyung Kim 			first = false;
1083aca7a94dSNamhyung Kim 		}
1084aca7a94dSNamhyung Kim 	} else {
108505e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1086aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1087aca7a94dSNamhyung Kim 		h->row_offset = 0;
1088aca7a94dSNamhyung Kim 	}
1089aca7a94dSNamhyung Kim }
1090aca7a94dSNamhyung Kim 
1091aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
109239ee533fSNamhyung Kim 					   struct hist_entry *he, FILE *fp)
1093aff3f3f6SArnaldo Carvalho de Melo {
109439ee533fSNamhyung Kim 	u64 total = hists__total_period(he->hists);
109539ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
109639ee533fSNamhyung Kim 		.fp = fp,
109739ee533fSNamhyung Kim 	};
1098aff3f3f6SArnaldo Carvalho de Melo 
109939ee533fSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
110039ee533fSNamhyung Kim 		total = he->stat_acc->period;
1101aff3f3f6SArnaldo Carvalho de Melo 
110239ee533fSNamhyung Kim 	hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
110339ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
110439ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
110539ee533fSNamhyung Kim 	return arg.printed;
1106aff3f3f6SArnaldo Carvalho de Melo }
1107aff3f3f6SArnaldo Carvalho de Melo 
1108aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1109aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1110aff3f3f6SArnaldo Carvalho de Melo {
1111aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1112aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1113aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
111426d8b338SNamhyung Kim 	struct perf_hpp hpp = {
111526d8b338SNamhyung Kim 		.buf = s,
111626d8b338SNamhyung Kim 		.size = sizeof(s),
111726d8b338SNamhyung Kim 	};
111826d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
111926d8b338SNamhyung Kim 	bool first = true;
112026d8b338SNamhyung Kim 	int ret;
1121aff3f3f6SArnaldo Carvalho de Melo 
1122aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1123aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1124aff3f3f6SArnaldo Carvalho de Melo 
1125aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1126aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
1127aff3f3f6SArnaldo Carvalho de Melo 
112826d8b338SNamhyung Kim 	perf_hpp__for_each_format(fmt) {
1129e67d49a7SNamhyung Kim 		if (perf_hpp__should_skip(fmt))
1130e67d49a7SNamhyung Kim 			continue;
1131e67d49a7SNamhyung Kim 
113226d8b338SNamhyung Kim 		if (!first) {
113326d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
113426d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
113526d8b338SNamhyung Kim 		} else
113626d8b338SNamhyung Kim 			first = false;
1137aff3f3f6SArnaldo Carvalho de Melo 
113826d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
113926d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
114026d8b338SNamhyung Kim 	}
1141aff3f3f6SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", rtrim(s));
1142aff3f3f6SArnaldo Carvalho de Melo 
1143aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
114439ee533fSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp);
1145aff3f3f6SArnaldo Carvalho de Melo 
1146aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1147aff3f3f6SArnaldo Carvalho de Melo }
1148aff3f3f6SArnaldo Carvalho de Melo 
1149aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1150aff3f3f6SArnaldo Carvalho de Melo {
1151064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1152064f1981SNamhyung Kim 						   browser->min_pcnt);
1153aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1154aff3f3f6SArnaldo Carvalho de Melo 
1155aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1156aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1157aff3f3f6SArnaldo Carvalho de Melo 
1158aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_entry(browser, h, fp);
115914135663SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1160aff3f3f6SArnaldo Carvalho de Melo 	}
1161aff3f3f6SArnaldo Carvalho de Melo 
1162aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1163aff3f3f6SArnaldo Carvalho de Melo }
1164aff3f3f6SArnaldo Carvalho de Melo 
1165aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
1166aff3f3f6SArnaldo Carvalho de Melo {
1167aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
1168aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
1169aff3f3f6SArnaldo Carvalho de Melo 
1170aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
1171aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1172aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
1173aff3f3f6SArnaldo Carvalho de Melo 			break;
1174aff3f3f6SArnaldo Carvalho de Melo 		/*
1175aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
1176aff3f3f6SArnaldo Carvalho de Melo  		 */
1177aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
1178aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1179aff3f3f6SArnaldo Carvalho de Melo 			return -1;
1180aff3f3f6SArnaldo Carvalho de Melo 		}
1181aff3f3f6SArnaldo Carvalho de Melo 	}
1182aff3f3f6SArnaldo Carvalho de Melo 
1183aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
1184aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
1185aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
11864cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
11874cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1188aff3f3f6SArnaldo Carvalho de Melo 		return -1;
1189aff3f3f6SArnaldo Carvalho de Melo 	}
1190aff3f3f6SArnaldo Carvalho de Melo 
1191aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
1192aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
1193aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
1194aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
1195aff3f3f6SArnaldo Carvalho de Melo 
1196aff3f3f6SArnaldo Carvalho de Melo 	return 0;
1197aff3f3f6SArnaldo Carvalho de Melo }
1198aff3f3f6SArnaldo Carvalho de Melo 
1199*c2a51ab8SNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists,
1200*c2a51ab8SNamhyung Kim 					      struct hist_browser_timer *hbt)
1201aca7a94dSNamhyung Kim {
120205e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
1203aca7a94dSNamhyung Kim 
120405e8b080SArnaldo Carvalho de Melo 	if (browser) {
120505e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
120605e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
1207357cfff1SArnaldo Carvalho de Melo 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
120805e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
120905e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
1210c8302367SJiri Olsa 		browser->show_headers = symbol_conf.show_hist_headers;
1211*c2a51ab8SNamhyung Kim 		browser->hbt = hbt;
1212aca7a94dSNamhyung Kim 	}
1213aca7a94dSNamhyung Kim 
121405e8b080SArnaldo Carvalho de Melo 	return browser;
1215aca7a94dSNamhyung Kim }
1216aca7a94dSNamhyung Kim 
121705e8b080SArnaldo Carvalho de Melo static void hist_browser__delete(struct hist_browser *browser)
1218aca7a94dSNamhyung Kim {
121905e8b080SArnaldo Carvalho de Melo 	free(browser);
1220aca7a94dSNamhyung Kim }
1221aca7a94dSNamhyung Kim 
122205e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1223aca7a94dSNamhyung Kim {
122405e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
1225aca7a94dSNamhyung Kim }
1226aca7a94dSNamhyung Kim 
122705e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1228aca7a94dSNamhyung Kim {
122905e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
1230aca7a94dSNamhyung Kim }
1231aca7a94dSNamhyung Kim 
12321e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
12331e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
12341e378ebdSTaeung Song {
12351e378ebdSTaeung Song 	return timer == NULL;
12361e378ebdSTaeung Song }
12371e378ebdSTaeung Song 
12381e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
12391e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
12401e378ebdSTaeung Song 				char *bf, size_t size)
1241aca7a94dSNamhyung Kim {
1242aca7a94dSNamhyung Kim 	char unit;
1243aca7a94dSNamhyung Kim 	int printed;
124405e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
124505e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
124605e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
124705e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
1248717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
1249dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
1250717e263fSNamhyung Kim 	char buf[512];
1251717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
1252717e263fSNamhyung Kim 
1253f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
1254f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
1255f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
1256f2148330SNamhyung Kim 	}
1257f2148330SNamhyung Kim 
1258759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1259717e263fSNamhyung Kim 		struct perf_evsel *pos;
1260717e263fSNamhyung Kim 
1261717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
1262717e263fSNamhyung Kim 		ev_name = buf;
1263717e263fSNamhyung Kim 
1264717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
12654ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
12664ea062edSArnaldo Carvalho de Melo 
1267f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
12684ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
12694ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
1270f2148330SNamhyung Kim 			} else {
12714ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
12724ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
1273717e263fSNamhyung Kim 			}
1274717e263fSNamhyung Kim 		}
1275f2148330SNamhyung Kim 	}
1276aca7a94dSNamhyung Kim 
1277aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
1278aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
1279e641f696STom Huynh 			   "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64,
1280aca7a94dSNamhyung Kim 			   nr_samples, unit, ev_name, nr_events);
1281aca7a94dSNamhyung Kim 
1282aca7a94dSNamhyung Kim 
128305e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
1284aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
128505e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
1286aca7a94dSNamhyung Kim 	if (thread)
1287aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1288aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
1289b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
129038051234SAdrian Hunter 				    thread->tid);
1291aca7a94dSNamhyung Kim 	if (dso)
1292aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1293aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
12941e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
12951e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
12961e378ebdSTaeung Song 
12971e378ebdSTaeung Song 		if (top->zero)
12981e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
12991e378ebdSTaeung Song 	}
13001e378ebdSTaeung Song 
1301aca7a94dSNamhyung Kim 	return printed;
1302aca7a94dSNamhyung Kim }
1303aca7a94dSNamhyung Kim 
1304aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
1305aca7a94dSNamhyung Kim {
1306aca7a94dSNamhyung Kim 	int i;
1307aca7a94dSNamhyung Kim 
130804662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
130904662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
1310aca7a94dSNamhyung Kim }
1311aca7a94dSNamhyung Kim 
1312341487abSFeng Tang /*
1313341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
1314341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1315341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
1316341487abSFeng Tang  */
1317341487abSFeng Tang static bool is_input_name_malloced = false;
1318341487abSFeng Tang 
1319341487abSFeng Tang static int switch_data_file(void)
1320341487abSFeng Tang {
1321341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
1322341487abSFeng Tang 	DIR *pwd_dir;
1323341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
1324341487abSFeng Tang 	struct dirent *dent;
1325341487abSFeng Tang 
1326341487abSFeng Tang 	pwd = getenv("PWD");
1327341487abSFeng Tang 	if (!pwd)
1328341487abSFeng Tang 		return ret;
1329341487abSFeng Tang 
1330341487abSFeng Tang 	pwd_dir = opendir(pwd);
1331341487abSFeng Tang 	if (!pwd_dir)
1332341487abSFeng Tang 		return ret;
1333341487abSFeng Tang 
1334341487abSFeng Tang 	memset(options, 0, sizeof(options));
1335341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
1336341487abSFeng Tang 
1337341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
1338341487abSFeng Tang 		char path[PATH_MAX];
1339341487abSFeng Tang 		u64 magic;
1340341487abSFeng Tang 		char *name = dent->d_name;
1341341487abSFeng Tang 		FILE *file;
1342341487abSFeng Tang 
1343341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
1344341487abSFeng Tang 			continue;
1345341487abSFeng Tang 
1346341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1347341487abSFeng Tang 
1348341487abSFeng Tang 		file = fopen(path, "r");
1349341487abSFeng Tang 		if (!file)
1350341487abSFeng Tang 			continue;
1351341487abSFeng Tang 
1352341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
1353341487abSFeng Tang 			goto close_file_and_continue;
1354341487abSFeng Tang 
1355341487abSFeng Tang 		if (is_perf_magic(magic)) {
1356341487abSFeng Tang 			options[nr_options] = strdup(name);
1357341487abSFeng Tang 			if (!options[nr_options])
1358341487abSFeng Tang 				goto close_file_and_continue;
1359341487abSFeng Tang 
1360341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
1361341487abSFeng Tang 			if (!abs_path[nr_options]) {
136274cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
1363341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
1364341487abSFeng Tang 				fclose(file);
1365341487abSFeng Tang 				break;
1366341487abSFeng Tang 			}
1367341487abSFeng Tang 
1368341487abSFeng Tang 			nr_options++;
1369341487abSFeng Tang 		}
1370341487abSFeng Tang 
1371341487abSFeng Tang close_file_and_continue:
1372341487abSFeng Tang 		fclose(file);
1373341487abSFeng Tang 		if (nr_options >= 32) {
1374341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
1375341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
1376341487abSFeng Tang 			break;
1377341487abSFeng Tang 		}
1378341487abSFeng Tang 	}
1379341487abSFeng Tang 	closedir(pwd_dir);
1380341487abSFeng Tang 
1381341487abSFeng Tang 	if (nr_options) {
1382341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
1383341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
1384341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
1385341487abSFeng Tang 			if (tmp) {
1386341487abSFeng Tang 				if (is_input_name_malloced)
1387341487abSFeng Tang 					free((void *)input_name);
1388341487abSFeng Tang 				input_name = tmp;
1389341487abSFeng Tang 				is_input_name_malloced = true;
1390341487abSFeng Tang 				ret = 0;
1391341487abSFeng Tang 			} else
1392341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
1393341487abSFeng Tang 		}
1394341487abSFeng Tang 	}
1395341487abSFeng Tang 
1396341487abSFeng Tang 	free_popup_options(options, nr_options);
1397341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
1398341487abSFeng Tang 	return ret;
1399341487abSFeng Tang }
1400341487abSFeng Tang 
1401112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
1402064f1981SNamhyung Kim {
1403064f1981SNamhyung Kim 	u64 nr_entries = 0;
1404064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
1405064f1981SNamhyung Kim 
1406268397cbSNamhyung Kim 	if (hb->min_pcnt == 0) {
1407268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1408268397cbSNamhyung Kim 		return;
1409268397cbSNamhyung Kim 	}
1410268397cbSNamhyung Kim 
141114135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1412064f1981SNamhyung Kim 		nr_entries++;
1413c481f930SNamhyung Kim 		nd = rb_next(nd);
1414064f1981SNamhyung Kim 	}
1415064f1981SNamhyung Kim 
1416112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
1417064f1981SNamhyung Kim }
1418341487abSFeng Tang 
1419aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1420dd00d486SJiri Olsa 				    const char *helpline,
1421aca7a94dSNamhyung Kim 				    bool left_exits,
142268d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
1423064f1981SNamhyung Kim 				    float min_pcnt,
142468d80758SNamhyung Kim 				    struct perf_session_env *env)
1425aca7a94dSNamhyung Kim {
14264ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
1427*c2a51ab8SNamhyung Kim 	struct hist_browser *browser = hist_browser__new(hists, hbt);
1428aca7a94dSNamhyung Kim 	struct branch_info *bi;
1429aca7a94dSNamhyung Kim 	struct pstack *fstack;
1430f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
1431f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
1432aca7a94dSNamhyung Kim 	int nr_options = 0;
1433aca7a94dSNamhyung Kim 	int key = -1;
1434aca7a94dSNamhyung Kim 	char buf[64];
1435cdbab7c2SFeng Tang 	char script_opt[64];
14369783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
143759dc9f25SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1438aca7a94dSNamhyung Kim 
1439e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
1440e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
1441e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
1442e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
1443e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1444e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
1445e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
1446e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
1447e8e684a5SNamhyung Kim 	"->            Zoom into DSO/Threads & Annotate current symbol\n" \
1448e8e684a5SNamhyung Kim 	"<-            Zoom out\n"					\
1449e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
1450e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
1451e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
1452e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
1453105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
1454025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
1455e8e684a5SNamhyung Kim 
1456e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
1457e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
14586dd60135SNamhyung Kim 	"i             Show header information\n"
1459e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1460e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
1461e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
1462e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1463e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
1464e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1465e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1466e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1467e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1468e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
146942337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
1470e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1471e8e684a5SNamhyung Kim 
1472aca7a94dSNamhyung Kim 	if (browser == NULL)
1473aca7a94dSNamhyung Kim 		return -1;
1474aca7a94dSNamhyung Kim 
1475064f1981SNamhyung Kim 	if (min_pcnt) {
1476064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
1477112f761fSNamhyung Kim 		hist_browser__update_nr_entries(browser);
1478064f1981SNamhyung Kim 	}
1479064f1981SNamhyung Kim 
1480aca7a94dSNamhyung Kim 	fstack = pstack__new(2);
1481aca7a94dSNamhyung Kim 	if (fstack == NULL)
1482aca7a94dSNamhyung Kim 		goto out;
1483aca7a94dSNamhyung Kim 
1484aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
1485aca7a94dSNamhyung Kim 
1486aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
1487aca7a94dSNamhyung Kim 
148859dc9f25SNamhyung Kim 	perf_hpp__for_each_format(fmt)
148959dc9f25SNamhyung Kim 		perf_hpp__reset_width(fmt, hists);
149059dc9f25SNamhyung Kim 
14915b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
14925b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
14935b591669SNamhyung Kim 
1494aca7a94dSNamhyung Kim 	while (1) {
1495f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
1496aca7a94dSNamhyung Kim 		const struct dso *dso = NULL;
1497aca7a94dSNamhyung Kim 		int choice = 0,
1498aca7a94dSNamhyung Kim 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1499aca7a94dSNamhyung Kim 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1500341487abSFeng Tang 		int scripts_comm = -2, scripts_symbol = -2,
1501341487abSFeng Tang 		    scripts_all = -2, switch_data = -2;
1502aca7a94dSNamhyung Kim 
1503aca7a94dSNamhyung Kim 		nr_options = 0;
1504aca7a94dSNamhyung Kim 
1505*c2a51ab8SNamhyung Kim 		key = hist_browser__run(browser);
1506aca7a94dSNamhyung Kim 
1507aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
1508aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
1509aca7a94dSNamhyung Kim 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1510aca7a94dSNamhyung Kim 		}
1511aca7a94dSNamhyung Kim 		switch (key) {
1512aca7a94dSNamhyung Kim 		case K_TAB:
1513aca7a94dSNamhyung Kim 		case K_UNTAB:
1514aca7a94dSNamhyung Kim 			if (nr_events == 1)
1515aca7a94dSNamhyung Kim 				continue;
1516aca7a94dSNamhyung Kim 			/*
1517aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
1518aca7a94dSNamhyung Kim 			 * go to the next or previous
1519aca7a94dSNamhyung Kim 			 */
1520aca7a94dSNamhyung Kim 			goto out_free_stack;
1521aca7a94dSNamhyung Kim 		case 'a':
15229c796ec8SArnaldo Carvalho de Melo 			if (!sort__has_sym) {
1523aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
1524aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
1525aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
1526aca7a94dSNamhyung Kim 				continue;
1527aca7a94dSNamhyung Kim 			}
1528aca7a94dSNamhyung Kim 
1529aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
1530aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
1531aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
1532aca7a94dSNamhyung Kim 				continue;
1533aca7a94dSNamhyung Kim 			goto do_annotate;
1534aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
1535aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
1536aff3f3f6SArnaldo Carvalho de Melo 			continue;
1537aca7a94dSNamhyung Kim 		case 'd':
1538aca7a94dSNamhyung Kim 			goto zoom_dso;
1539a7cb8863SArnaldo Carvalho de Melo 		case 'V':
1540a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
1541a7cb8863SArnaldo Carvalho de Melo 			continue;
1542aca7a94dSNamhyung Kim 		case 't':
1543aca7a94dSNamhyung Kim 			goto zoom_thread;
15445a5626b1SArnaldo Carvalho de Melo 		case '/':
1545aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
1546aca7a94dSNamhyung Kim 					"Please enter the name of symbol you want to see",
1547aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
1548aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
154905e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
155005e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
1551aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
1552aca7a94dSNamhyung Kim 			}
1553aca7a94dSNamhyung Kim 			continue;
1554cdbab7c2SFeng Tang 		case 'r':
15559783adf7SNamhyung Kim 			if (is_report_browser(hbt))
1556cdbab7c2SFeng Tang 				goto do_scripts;
1557c77d8d70SFeng Tang 			continue;
1558341487abSFeng Tang 		case 's':
1559341487abSFeng Tang 			if (is_report_browser(hbt))
1560341487abSFeng Tang 				goto do_data_switch;
1561341487abSFeng Tang 			continue;
15626dd60135SNamhyung Kim 		case 'i':
15636dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
15646dd60135SNamhyung Kim 			if (env->arch)
15656dd60135SNamhyung Kim 				tui__header_window(env);
15666dd60135SNamhyung Kim 			continue;
1567105eb30fSNamhyung Kim 		case 'F':
1568105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
1569105eb30fSNamhyung Kim 			continue;
157042337a22SNamhyung Kim 		case 'z':
157142337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
157242337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
157342337a22SNamhyung Kim 
157442337a22SNamhyung Kim 				top->zero = !top->zero;
157542337a22SNamhyung Kim 			}
157642337a22SNamhyung Kim 			continue;
1577aca7a94dSNamhyung Kim 		case K_F1:
1578aca7a94dSNamhyung Kim 		case 'h':
1579aca7a94dSNamhyung Kim 		case '?':
1580aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
1581e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
1582aca7a94dSNamhyung Kim 			continue;
1583aca7a94dSNamhyung Kim 		case K_ENTER:
1584aca7a94dSNamhyung Kim 		case K_RIGHT:
1585aca7a94dSNamhyung Kim 			/* menu */
1586aca7a94dSNamhyung Kim 			break;
1587aca7a94dSNamhyung Kim 		case K_LEFT: {
1588aca7a94dSNamhyung Kim 			const void *top;
1589aca7a94dSNamhyung Kim 
1590aca7a94dSNamhyung Kim 			if (pstack__empty(fstack)) {
1591aca7a94dSNamhyung Kim 				/*
1592aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
1593aca7a94dSNamhyung Kim 				 */
1594aca7a94dSNamhyung Kim 				if (left_exits)
1595aca7a94dSNamhyung Kim 					goto out_free_stack;
1596aca7a94dSNamhyung Kim 				continue;
1597aca7a94dSNamhyung Kim 			}
1598aca7a94dSNamhyung Kim 			top = pstack__pop(fstack);
1599aca7a94dSNamhyung Kim 			if (top == &browser->hists->dso_filter)
1600aca7a94dSNamhyung Kim 				goto zoom_out_dso;
1601aca7a94dSNamhyung Kim 			if (top == &browser->hists->thread_filter)
1602aca7a94dSNamhyung Kim 				goto zoom_out_thread;
1603aca7a94dSNamhyung Kim 			continue;
1604aca7a94dSNamhyung Kim 		}
1605aca7a94dSNamhyung Kim 		case K_ESC:
1606aca7a94dSNamhyung Kim 			if (!left_exits &&
1607aca7a94dSNamhyung Kim 			    !ui_browser__dialog_yesno(&browser->b,
1608aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1609aca7a94dSNamhyung Kim 				continue;
1610aca7a94dSNamhyung Kim 			/* Fall thru */
1611aca7a94dSNamhyung Kim 		case 'q':
1612aca7a94dSNamhyung Kim 		case CTRL('c'):
1613aca7a94dSNamhyung Kim 			goto out_free_stack;
1614aca7a94dSNamhyung Kim 		default:
1615aca7a94dSNamhyung Kim 			continue;
1616aca7a94dSNamhyung Kim 		}
1617aca7a94dSNamhyung Kim 
16189c796ec8SArnaldo Carvalho de Melo 		if (!sort__has_sym)
1619aca7a94dSNamhyung Kim 			goto add_exit_option;
1620aca7a94dSNamhyung Kim 
16210ba332f7SArnaldo Carvalho de Melo 		if (browser->selection == NULL)
16220ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
16230ba332f7SArnaldo Carvalho de Melo 
162455369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
1625aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
16260ba332f7SArnaldo Carvalho de Melo 
16270ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
16280ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
16290ba332f7SArnaldo Carvalho de Melo 
16300ba332f7SArnaldo Carvalho de Melo 			if (bi->from.sym != NULL &&
1631aca7a94dSNamhyung Kim 			    !bi->from.map->dso->annotate_warned &&
1632446fb96cSArnaldo Carvalho de Melo 			    asprintf(&options[nr_options], "Annotate %s", bi->from.sym->name) > 0) {
1633aca7a94dSNamhyung Kim 				annotate_f = nr_options++;
1634446fb96cSArnaldo Carvalho de Melo 			}
1635aca7a94dSNamhyung Kim 
16360ba332f7SArnaldo Carvalho de Melo 			if (bi->to.sym != NULL &&
1637aca7a94dSNamhyung Kim 			    !bi->to.map->dso->annotate_warned &&
1638aca7a94dSNamhyung Kim 			    (bi->to.sym != bi->from.sym ||
1639aca7a94dSNamhyung Kim 			     bi->to.map->dso != bi->from.map->dso) &&
1640446fb96cSArnaldo Carvalho de Melo 			    asprintf(&options[nr_options], "Annotate %s", bi->to.sym->name) > 0) {
1641aca7a94dSNamhyung Kim 				annotate_t = nr_options++;
1642446fb96cSArnaldo Carvalho de Melo 			}
1643aca7a94dSNamhyung Kim 		} else {
16440ba332f7SArnaldo Carvalho de Melo 			if (browser->selection->sym != NULL &&
1645d755330cSJiri Olsa 			    !browser->selection->map->dso->annotate_warned) {
1646d755330cSJiri Olsa 				struct annotation *notes;
1647d755330cSJiri Olsa 
1648d755330cSJiri Olsa 				notes = symbol__annotation(browser->selection->sym);
1649d755330cSJiri Olsa 
1650d755330cSJiri Olsa 				if (notes->src &&
1651aca7a94dSNamhyung Kim 				    asprintf(&options[nr_options], "Annotate %s",
1652446fb96cSArnaldo Carvalho de Melo 						 browser->selection->sym->name) > 0) {
1653aca7a94dSNamhyung Kim 					annotate = nr_options++;
1654aca7a94dSNamhyung Kim 				}
1655d755330cSJiri Olsa 			}
1656446fb96cSArnaldo Carvalho de Melo 		}
16570ba332f7SArnaldo Carvalho de Melo skip_annotation:
1658aca7a94dSNamhyung Kim 		if (thread != NULL &&
1659aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1660aca7a94dSNamhyung Kim 			     (browser->hists->thread_filter ? "out of" : "into"),
1661b9c5143aSFrederic Weisbecker 			     (thread->comm_set ? thread__comm_str(thread) : ""),
166238051234SAdrian Hunter 			     thread->tid) > 0)
1663aca7a94dSNamhyung Kim 			zoom_thread = nr_options++;
1664aca7a94dSNamhyung Kim 
1665aca7a94dSNamhyung Kim 		if (dso != NULL &&
1666aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1667aca7a94dSNamhyung Kim 			     (browser->hists->dso_filter ? "out of" : "into"),
1668aca7a94dSNamhyung Kim 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1669aca7a94dSNamhyung Kim 			zoom_dso = nr_options++;
1670aca7a94dSNamhyung Kim 
1671aca7a94dSNamhyung Kim 		if (browser->selection != NULL &&
1672aca7a94dSNamhyung Kim 		    browser->selection->map != NULL &&
1673aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Browse map details") > 0)
1674aca7a94dSNamhyung Kim 			browse_map = nr_options++;
1675cdbab7c2SFeng Tang 
1676cdbab7c2SFeng Tang 		/* perf script support */
1677cdbab7c2SFeng Tang 		if (browser->he_selection) {
1678cdbab7c2SFeng Tang 			struct symbol *sym;
1679cdbab7c2SFeng Tang 
1680cdbab7c2SFeng Tang 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1681b9c5143aSFrederic Weisbecker 				     thread__comm_str(browser->he_selection->thread)) > 0)
1682cdbab7c2SFeng Tang 				scripts_comm = nr_options++;
1683cdbab7c2SFeng Tang 
1684cdbab7c2SFeng Tang 			sym = browser->he_selection->ms.sym;
1685cdbab7c2SFeng Tang 			if (sym && sym->namelen &&
1686cdbab7c2SFeng Tang 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1687cdbab7c2SFeng Tang 						sym->name) > 0)
1688cdbab7c2SFeng Tang 				scripts_symbol = nr_options++;
1689cdbab7c2SFeng Tang 		}
1690cdbab7c2SFeng Tang 
1691cdbab7c2SFeng Tang 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1692cdbab7c2SFeng Tang 			scripts_all = nr_options++;
1693cdbab7c2SFeng Tang 
1694341487abSFeng Tang 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1695341487abSFeng Tang 				"Switch to another data file in PWD") > 0)
1696341487abSFeng Tang 			switch_data = nr_options++;
1697aca7a94dSNamhyung Kim add_exit_option:
1698f2b487dbSNamhyung Kim 		if (asprintf(&options[nr_options], "Exit") > 0)
1699f2b487dbSNamhyung Kim 			nr_options++;
1700aca7a94dSNamhyung Kim retry_popup_menu:
1701aca7a94dSNamhyung Kim 		choice = ui__popup_menu(nr_options, options);
1702aca7a94dSNamhyung Kim 
1703aca7a94dSNamhyung Kim 		if (choice == nr_options - 1)
1704aca7a94dSNamhyung Kim 			break;
1705aca7a94dSNamhyung Kim 
1706aca7a94dSNamhyung Kim 		if (choice == -1) {
1707aca7a94dSNamhyung Kim 			free_popup_options(options, nr_options - 1);
1708aca7a94dSNamhyung Kim 			continue;
1709aca7a94dSNamhyung Kim 		}
1710aca7a94dSNamhyung Kim 
1711aca7a94dSNamhyung Kim 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1712aca7a94dSNamhyung Kim 			struct hist_entry *he;
1713d755330cSJiri Olsa 			struct annotation *notes;
1714d5dbc518SArnaldo Carvalho de Melo 			struct map_symbol ms;
1715aca7a94dSNamhyung Kim 			int err;
1716aca7a94dSNamhyung Kim do_annotate:
171768d80758SNamhyung Kim 			if (!objdump_path && perf_session_env__lookup_objdump(env))
171868d80758SNamhyung Kim 				continue;
171968d80758SNamhyung Kim 
1720aca7a94dSNamhyung Kim 			he = hist_browser__selected_entry(browser);
1721aca7a94dSNamhyung Kim 			if (he == NULL)
1722aca7a94dSNamhyung Kim 				continue;
1723aca7a94dSNamhyung Kim 
1724aca7a94dSNamhyung Kim 			if (choice == annotate_f) {
1725d5dbc518SArnaldo Carvalho de Melo 				ms.map = he->branch_info->from.map;
1726d5dbc518SArnaldo Carvalho de Melo 				ms.sym = he->branch_info->from.sym;
1727aca7a94dSNamhyung Kim 			} else if (choice == annotate_t) {
1728d5dbc518SArnaldo Carvalho de Melo 				ms.map = he->branch_info->to.map;
1729d5dbc518SArnaldo Carvalho de Melo 				ms.sym = he->branch_info->to.sym;
1730d5dbc518SArnaldo Carvalho de Melo 			} else {
1731d5dbc518SArnaldo Carvalho de Melo 				ms = *browser->selection;
1732aca7a94dSNamhyung Kim 			}
1733aca7a94dSNamhyung Kim 
1734d5dbc518SArnaldo Carvalho de Melo 			notes = symbol__annotation(ms.sym);
1735d755330cSJiri Olsa 			if (!notes->src)
1736d755330cSJiri Olsa 				continue;
1737d755330cSJiri Olsa 
1738d5dbc518SArnaldo Carvalho de Melo 			err = map_symbol__tui_annotate(&ms, evsel, hbt);
1739aca7a94dSNamhyung Kim 			/*
1740aca7a94dSNamhyung Kim 			 * offer option to annotate the other branch source or target
1741aca7a94dSNamhyung Kim 			 * (if they exists) when returning from annotate
1742aca7a94dSNamhyung Kim 			 */
1743aca7a94dSNamhyung Kim 			if ((err == 'q' || err == CTRL('c'))
1744aca7a94dSNamhyung Kim 			    && annotate_t != -2 && annotate_f != -2)
1745aca7a94dSNamhyung Kim 				goto retry_popup_menu;
1746aca7a94dSNamhyung Kim 
1747aca7a94dSNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1748aca7a94dSNamhyung Kim 			if (err)
1749aca7a94dSNamhyung Kim 				ui_browser__handle_resize(&browser->b);
1750aca7a94dSNamhyung Kim 
1751aca7a94dSNamhyung Kim 		} else if (choice == browse_map)
1752aca7a94dSNamhyung Kim 			map__browse(browser->selection->map);
1753aca7a94dSNamhyung Kim 		else if (choice == zoom_dso) {
1754aca7a94dSNamhyung Kim zoom_dso:
1755aca7a94dSNamhyung Kim 			if (browser->hists->dso_filter) {
1756aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->dso_filter);
1757aca7a94dSNamhyung Kim zoom_out_dso:
1758aca7a94dSNamhyung Kim 				ui_helpline__pop();
1759aca7a94dSNamhyung Kim 				browser->hists->dso_filter = NULL;
1760f2998422SJiri Olsa 				perf_hpp__set_elide(HISTC_DSO, false);
1761aca7a94dSNamhyung Kim 			} else {
1762aca7a94dSNamhyung Kim 				if (dso == NULL)
1763aca7a94dSNamhyung Kim 					continue;
1764aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1765aca7a94dSNamhyung Kim 						   dso->kernel ? "the Kernel" : dso->short_name);
1766aca7a94dSNamhyung Kim 				browser->hists->dso_filter = dso;
1767f2998422SJiri Olsa 				perf_hpp__set_elide(HISTC_DSO, true);
1768aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->dso_filter);
1769aca7a94dSNamhyung Kim 			}
177005e8b080SArnaldo Carvalho de Melo 			hists__filter_by_dso(hists);
1771aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1772aca7a94dSNamhyung Kim 		} else if (choice == zoom_thread) {
1773aca7a94dSNamhyung Kim zoom_thread:
1774aca7a94dSNamhyung Kim 			if (browser->hists->thread_filter) {
1775aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->thread_filter);
1776aca7a94dSNamhyung Kim zoom_out_thread:
1777aca7a94dSNamhyung Kim 				ui_helpline__pop();
1778f3b623b8SArnaldo Carvalho de Melo 				thread__zput(browser->hists->thread_filter);
1779f2998422SJiri Olsa 				perf_hpp__set_elide(HISTC_THREAD, false);
1780aca7a94dSNamhyung Kim 			} else {
1781aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1782b9c5143aSFrederic Weisbecker 						   thread->comm_set ? thread__comm_str(thread) : "",
178338051234SAdrian Hunter 						   thread->tid);
1784f3b623b8SArnaldo Carvalho de Melo 				browser->hists->thread_filter = thread__get(thread);
1785f2998422SJiri Olsa 				perf_hpp__set_elide(HISTC_THREAD, false);
1786aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->thread_filter);
1787aca7a94dSNamhyung Kim 			}
178805e8b080SArnaldo Carvalho de Melo 			hists__filter_by_thread(hists);
1789aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1790aca7a94dSNamhyung Kim 		}
1791cdbab7c2SFeng Tang 		/* perf scripts support */
1792cdbab7c2SFeng Tang 		else if (choice == scripts_all || choice == scripts_comm ||
1793cdbab7c2SFeng Tang 				choice == scripts_symbol) {
1794cdbab7c2SFeng Tang do_scripts:
1795cdbab7c2SFeng Tang 			memset(script_opt, 0, 64);
1796cdbab7c2SFeng Tang 
1797cdbab7c2SFeng Tang 			if (choice == scripts_comm)
1798b9c5143aSFrederic Weisbecker 				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1799cdbab7c2SFeng Tang 
1800cdbab7c2SFeng Tang 			if (choice == scripts_symbol)
1801cdbab7c2SFeng Tang 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1802cdbab7c2SFeng Tang 
1803cdbab7c2SFeng Tang 			script_browse(script_opt);
1804cdbab7c2SFeng Tang 		}
1805341487abSFeng Tang 		/* Switch to another data file */
1806341487abSFeng Tang 		else if (choice == switch_data) {
1807341487abSFeng Tang do_data_switch:
1808341487abSFeng Tang 			if (!switch_data_file()) {
1809341487abSFeng Tang 				key = K_SWITCH_INPUT_DATA;
1810341487abSFeng Tang 				break;
1811341487abSFeng Tang 			} else
1812341487abSFeng Tang 				ui__warning("Won't switch the data files due to\n"
1813341487abSFeng Tang 					"no valid data file get selected!\n");
1814341487abSFeng Tang 		}
1815aca7a94dSNamhyung Kim 	}
1816aca7a94dSNamhyung Kim out_free_stack:
1817aca7a94dSNamhyung Kim 	pstack__delete(fstack);
1818aca7a94dSNamhyung Kim out:
1819aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
1820f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
1821aca7a94dSNamhyung Kim 	return key;
1822aca7a94dSNamhyung Kim }
1823aca7a94dSNamhyung Kim 
1824aca7a94dSNamhyung Kim struct perf_evsel_menu {
1825aca7a94dSNamhyung Kim 	struct ui_browser b;
1826aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
1827aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
1828064f1981SNamhyung Kim 	float min_pcnt;
182968d80758SNamhyung Kim 	struct perf_session_env *env;
1830aca7a94dSNamhyung Kim };
1831aca7a94dSNamhyung Kim 
1832aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
1833aca7a94dSNamhyung Kim 				   void *entry, int row)
1834aca7a94dSNamhyung Kim {
1835aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
1836aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
1837aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
18384ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
1839aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
18404ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
18417289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
1842aca7a94dSNamhyung Kim 	char bf[256], unit;
1843aca7a94dSNamhyung Kim 	const char *warn = " ";
1844aca7a94dSNamhyung Kim 	size_t printed;
1845aca7a94dSNamhyung Kim 
1846aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1847aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
1848aca7a94dSNamhyung Kim 
1849759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1850717e263fSNamhyung Kim 		struct perf_evsel *pos;
1851717e263fSNamhyung Kim 
1852717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
1853717e263fSNamhyung Kim 
1854717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
18554ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
18564ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1857717e263fSNamhyung Kim 		}
1858717e263fSNamhyung Kim 	}
1859717e263fSNamhyung Kim 
1860aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
1861aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1862aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
1863aca7a94dSNamhyung Kim 	slsmg_printf("%s", bf);
1864aca7a94dSNamhyung Kim 
18654ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
1866aca7a94dSNamhyung Kim 	if (nr_events != 0) {
1867aca7a94dSNamhyung Kim 		menu->lost_events = true;
1868aca7a94dSNamhyung Kim 		if (!current_entry)
1869aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1870aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
1871aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1872aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
1873aca7a94dSNamhyung Kim 		warn = bf;
1874aca7a94dSNamhyung Kim 	}
1875aca7a94dSNamhyung Kim 
1876aca7a94dSNamhyung Kim 	slsmg_write_nstring(warn, browser->width - printed);
1877aca7a94dSNamhyung Kim 
1878aca7a94dSNamhyung Kim 	if (current_entry)
1879aca7a94dSNamhyung Kim 		menu->selection = evsel;
1880aca7a94dSNamhyung Kim }
1881aca7a94dSNamhyung Kim 
1882aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1883aca7a94dSNamhyung Kim 				int nr_events, const char *help,
18849783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
1885aca7a94dSNamhyung Kim {
1886aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
1887aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1888dd00d486SJiri Olsa 	const char *title = "Available samples";
18899783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
1890aca7a94dSNamhyung Kim 	int key;
1891aca7a94dSNamhyung Kim 
1892aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
1893aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1894aca7a94dSNamhyung Kim 		return -1;
1895aca7a94dSNamhyung Kim 
1896aca7a94dSNamhyung Kim 	while (1) {
1897aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
1898aca7a94dSNamhyung Kim 
1899aca7a94dSNamhyung Kim 		switch (key) {
1900aca7a94dSNamhyung Kim 		case K_TIMER:
19019783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
1902aca7a94dSNamhyung Kim 
1903aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
1904aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
1905aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
1906aca7a94dSNamhyung Kim 			}
1907aca7a94dSNamhyung Kim 			continue;
1908aca7a94dSNamhyung Kim 		case K_RIGHT:
1909aca7a94dSNamhyung Kim 		case K_ENTER:
1910aca7a94dSNamhyung Kim 			if (!menu->selection)
1911aca7a94dSNamhyung Kim 				continue;
1912aca7a94dSNamhyung Kim 			pos = menu->selection;
1913aca7a94dSNamhyung Kim browse_hists:
1914aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
1915aca7a94dSNamhyung Kim 			/*
1916aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
1917aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
1918aca7a94dSNamhyung Kim 			 */
19199783adf7SNamhyung Kim 			if (hbt)
19209783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
1921aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
1922dd00d486SJiri Olsa 						       true, hbt,
1923064f1981SNamhyung Kim 						       menu->min_pcnt,
192468d80758SNamhyung Kim 						       menu->env);
1925aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
1926aca7a94dSNamhyung Kim 			switch (key) {
1927aca7a94dSNamhyung Kim 			case K_TAB:
1928aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
19299a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
1930aca7a94dSNamhyung Kim 				else
19319a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
1932aca7a94dSNamhyung Kim 				goto browse_hists;
1933aca7a94dSNamhyung Kim 			case K_UNTAB:
1934aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
19359a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
1936aca7a94dSNamhyung Kim 				else
1937d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
1938aca7a94dSNamhyung Kim 				goto browse_hists;
1939aca7a94dSNamhyung Kim 			case K_ESC:
1940aca7a94dSNamhyung Kim 				if (!ui_browser__dialog_yesno(&menu->b,
1941aca7a94dSNamhyung Kim 						"Do you really want to exit?"))
1942aca7a94dSNamhyung Kim 					continue;
1943aca7a94dSNamhyung Kim 				/* Fall thru */
1944341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
1945aca7a94dSNamhyung Kim 			case 'q':
1946aca7a94dSNamhyung Kim 			case CTRL('c'):
1947aca7a94dSNamhyung Kim 				goto out;
1948aca7a94dSNamhyung Kim 			default:
1949aca7a94dSNamhyung Kim 				continue;
1950aca7a94dSNamhyung Kim 			}
1951aca7a94dSNamhyung Kim 		case K_LEFT:
1952aca7a94dSNamhyung Kim 			continue;
1953aca7a94dSNamhyung Kim 		case K_ESC:
1954aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
1955aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1956aca7a94dSNamhyung Kim 				continue;
1957aca7a94dSNamhyung Kim 			/* Fall thru */
1958aca7a94dSNamhyung Kim 		case 'q':
1959aca7a94dSNamhyung Kim 		case CTRL('c'):
1960aca7a94dSNamhyung Kim 			goto out;
1961aca7a94dSNamhyung Kim 		default:
1962aca7a94dSNamhyung Kim 			continue;
1963aca7a94dSNamhyung Kim 		}
1964aca7a94dSNamhyung Kim 	}
1965aca7a94dSNamhyung Kim 
1966aca7a94dSNamhyung Kim out:
1967aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
1968aca7a94dSNamhyung Kim 	return key;
1969aca7a94dSNamhyung Kim }
1970aca7a94dSNamhyung Kim 
1971316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1972fc24d7c2SNamhyung Kim 				 void *entry)
1973fc24d7c2SNamhyung Kim {
1974fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1975fc24d7c2SNamhyung Kim 
1976fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1977fc24d7c2SNamhyung Kim 		return true;
1978fc24d7c2SNamhyung Kim 
1979fc24d7c2SNamhyung Kim 	return false;
1980fc24d7c2SNamhyung Kim }
1981fc24d7c2SNamhyung Kim 
1982aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1983fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
198468d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
1985064f1981SNamhyung Kim 					   float min_pcnt,
198668d80758SNamhyung Kim 					   struct perf_session_env *env)
1987aca7a94dSNamhyung Kim {
1988aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1989aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
1990aca7a94dSNamhyung Kim 		.b = {
1991aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
1992aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
1993aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
1994aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
1995fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
1996fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
1997aca7a94dSNamhyung Kim 			.priv	    = evlist,
1998aca7a94dSNamhyung Kim 		},
1999064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
200068d80758SNamhyung Kim 		.env = env,
2001aca7a94dSNamhyung Kim 	};
2002aca7a94dSNamhyung Kim 
2003aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
2004aca7a94dSNamhyung Kim 
20050050f7aaSArnaldo Carvalho de Melo 	evlist__for_each(evlist, pos) {
20067289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
2007aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
2008aca7a94dSNamhyung Kim 
2009aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
2010aca7a94dSNamhyung Kim 			menu.b.width = line_len;
2011aca7a94dSNamhyung Kim 	}
2012aca7a94dSNamhyung Kim 
2013fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2014aca7a94dSNamhyung Kim }
2015aca7a94dSNamhyung Kim 
2016aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
201768d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
2018064f1981SNamhyung Kim 				  float min_pcnt,
201968d80758SNamhyung Kim 				  struct perf_session_env *env)
2020aca7a94dSNamhyung Kim {
2021fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
2022fc24d7c2SNamhyung Kim 
2023fc24d7c2SNamhyung Kim single_entry:
2024fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
20259a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
2026fc24d7c2SNamhyung Kim 
2027fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
2028dd00d486SJiri Olsa 						false, hbt, min_pcnt,
2029064f1981SNamhyung Kim 						env);
2030aca7a94dSNamhyung Kim 	}
2031aca7a94dSNamhyung Kim 
2032fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
2033fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
2034fc24d7c2SNamhyung Kim 
2035fc24d7c2SNamhyung Kim 		nr_entries = 0;
20360050f7aaSArnaldo Carvalho de Melo 		evlist__for_each(evlist, pos) {
2037fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
2038fc24d7c2SNamhyung Kim 				nr_entries++;
20390050f7aaSArnaldo Carvalho de Melo 		}
2040fc24d7c2SNamhyung Kim 
2041fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
2042fc24d7c2SNamhyung Kim 			goto single_entry;
2043fc24d7c2SNamhyung Kim 	}
2044fc24d7c2SNamhyung Kim 
2045fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2046064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
2047aca7a94dSNamhyung Kim }
2048