xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 979d2cac)
1aca7a94dSNamhyung Kim #include <stdio.h>
2aca7a94dSNamhyung Kim #include <stdlib.h>
3aca7a94dSNamhyung Kim #include <string.h>
4aca7a94dSNamhyung Kim #include <linux/rbtree.h>
5aca7a94dSNamhyung Kim 
6aca7a94dSNamhyung Kim #include "../../util/evsel.h"
7aca7a94dSNamhyung Kim #include "../../util/evlist.h"
8aca7a94dSNamhyung Kim #include "../../util/hist.h"
9aca7a94dSNamhyung Kim #include "../../util/pstack.h"
10aca7a94dSNamhyung Kim #include "../../util/sort.h"
11aca7a94dSNamhyung Kim #include "../../util/util.h"
1242337a22SNamhyung Kim #include "../../util/top.h"
1368d80758SNamhyung Kim #include "../../arch/common.h"
14aca7a94dSNamhyung Kim 
15aca7a94dSNamhyung Kim #include "../browser.h"
16aca7a94dSNamhyung Kim #include "../helpline.h"
17aca7a94dSNamhyung Kim #include "../util.h"
18aca7a94dSNamhyung Kim #include "../ui.h"
19aca7a94dSNamhyung Kim #include "map.h"
20d755330cSJiri Olsa #include "annotate.h"
21aca7a94dSNamhyung Kim 
22aca7a94dSNamhyung Kim struct hist_browser {
23aca7a94dSNamhyung Kim 	struct ui_browser   b;
24aca7a94dSNamhyung Kim 	struct hists	    *hists;
25aca7a94dSNamhyung Kim 	struct hist_entry   *he_selection;
26aca7a94dSNamhyung Kim 	struct map_symbol   *selection;
27c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt;
2801f00a1cSNamhyung Kim 	struct pstack	    *pstack;
29ce80d3beSKan Liang 	struct perf_env *env;
30aff3f3f6SArnaldo Carvalho de Melo 	int		     print_seq;
31a7cb8863SArnaldo Carvalho de Melo 	bool		     show_dso;
32025bf7eaSArnaldo Carvalho de Melo 	bool		     show_headers;
33064f1981SNamhyung Kim 	float		     min_pcnt;
34112f761fSNamhyung Kim 	u64		     nr_non_filtered_entries;
35c3b78952SNamhyung Kim 	u64		     nr_callchain_rows;
36aca7a94dSNamhyung Kim };
37aca7a94dSNamhyung Kim 
38f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
39f5951d56SNamhyung Kim 
401e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
411e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
421e378ebdSTaeung Song 				char *bf, size_t size);
43112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
44aca7a94dSNamhyung Kim 
45c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
46c3b78952SNamhyung Kim 					     float min_pcnt);
47c3b78952SNamhyung Kim 
48268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
49268397cbSNamhyung Kim {
509c0fa8ddSArnaldo Carvalho de Melo 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
51268397cbSNamhyung Kim }
52268397cbSNamhyung Kim 
534fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
544fabf3d1SHe Kuang {
554fabf3d1SHe Kuang 	struct rb_node *nd;
564fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
574fabf3d1SHe Kuang 	int unfolded_rows = 0;
584fabf3d1SHe Kuang 
594fabf3d1SHe Kuang 	for (nd = rb_first(&hists->entries);
604fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
614fabf3d1SHe Kuang 	     nd = rb_next(nd)) {
624fabf3d1SHe Kuang 		struct hist_entry *he =
634fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
644fabf3d1SHe Kuang 
653698dab1SNamhyung Kim 		if (he->unfolded)
664fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
674fabf3d1SHe Kuang 	}
684fabf3d1SHe Kuang 	return unfolded_rows;
694fabf3d1SHe Kuang }
704fabf3d1SHe Kuang 
71c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
72c3b78952SNamhyung Kim {
73c3b78952SNamhyung Kim 	u32 nr_entries;
74c3b78952SNamhyung Kim 
75c3b78952SNamhyung Kim 	if (hist_browser__has_filter(hb))
76c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
77c3b78952SNamhyung Kim 	else
78c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
79c3b78952SNamhyung Kim 
804fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
81c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
82c3b78952SNamhyung Kim }
83c3b78952SNamhyung Kim 
84025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
85025bf7eaSArnaldo Carvalho de Melo {
86025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
87025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
88025bf7eaSArnaldo Carvalho de Melo 
89025bf7eaSArnaldo Carvalho de Melo 	browser->rows = browser->height - header_offset;
90025bf7eaSArnaldo Carvalho de Melo 	/*
91025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
92025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
93025bf7eaSArnaldo Carvalho de Melo 	 */
94025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
95025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
96025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
97025bf7eaSArnaldo Carvalho de Melo }
98025bf7eaSArnaldo Carvalho de Melo 
99357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
100aca7a94dSNamhyung Kim {
101357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
102357cfff1SArnaldo Carvalho de Melo 
103aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
104357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
105357cfff1SArnaldo Carvalho de Melo 	/*
106357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
107357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
108357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
109357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
110357cfff1SArnaldo Carvalho de Melo  	 */
111357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
112025bf7eaSArnaldo Carvalho de Melo 	hist_browser__update_rows(hb);
113aca7a94dSNamhyung Kim }
114aca7a94dSNamhyung Kim 
115ca3ff33bSArnaldo Carvalho de Melo static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
116ca3ff33bSArnaldo Carvalho de Melo {
117025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = browser->show_headers ? 1 : 0;
118025bf7eaSArnaldo Carvalho de Melo 
119025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row + header_offset, column);
120ca3ff33bSArnaldo Carvalho de Melo }
121ca3ff33bSArnaldo Carvalho de Melo 
12205e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
123aca7a94dSNamhyung Kim {
124c3b78952SNamhyung Kim 	/*
125c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
126c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
127c3b78952SNamhyung Kim 	 */
128c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
129c3b78952SNamhyung Kim 
130268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
131c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
132357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
13305e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
134aca7a94dSNamhyung Kim }
135aca7a94dSNamhyung Kim 
136aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
137aca7a94dSNamhyung Kim {
138aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
139aca7a94dSNamhyung Kim }
140aca7a94dSNamhyung Kim 
14105e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
142aca7a94dSNamhyung Kim {
1433698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
144aca7a94dSNamhyung Kim }
145aca7a94dSNamhyung Kim 
14605e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
147aca7a94dSNamhyung Kim {
1483698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
149aca7a94dSNamhyung Kim }
150aca7a94dSNamhyung Kim 
1513698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
152aca7a94dSNamhyung Kim {
1533698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
154aca7a94dSNamhyung Kim }
155aca7a94dSNamhyung Kim 
15605e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
157aca7a94dSNamhyung Kim {
158aca7a94dSNamhyung Kim 	int n = 0;
159aca7a94dSNamhyung Kim 	struct rb_node *nd;
160aca7a94dSNamhyung Kim 
16105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
162aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
163aca7a94dSNamhyung Kim 		struct callchain_list *chain;
164aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
165aca7a94dSNamhyung Kim 
166aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
167aca7a94dSNamhyung Kim 			++n;
168aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
169aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
170aca7a94dSNamhyung Kim 			if (folded_sign == '+')
171aca7a94dSNamhyung Kim 				break;
172aca7a94dSNamhyung Kim 		}
173aca7a94dSNamhyung Kim 
174aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
175aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
176aca7a94dSNamhyung Kim 	}
177aca7a94dSNamhyung Kim 
178aca7a94dSNamhyung Kim 	return n;
179aca7a94dSNamhyung Kim }
180aca7a94dSNamhyung Kim 
181aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
182aca7a94dSNamhyung Kim {
183aca7a94dSNamhyung Kim 	struct callchain_list *chain;
184aca7a94dSNamhyung Kim 	bool unfolded = false;
185aca7a94dSNamhyung Kim 	int n = 0;
186aca7a94dSNamhyung Kim 
187aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
188aca7a94dSNamhyung Kim 		++n;
1893698dab1SNamhyung Kim 		unfolded = chain->unfolded;
190aca7a94dSNamhyung Kim 	}
191aca7a94dSNamhyung Kim 
192aca7a94dSNamhyung Kim 	if (unfolded)
193aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
194aca7a94dSNamhyung Kim 
195aca7a94dSNamhyung Kim 	return n;
196aca7a94dSNamhyung Kim }
197aca7a94dSNamhyung Kim 
198aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
199aca7a94dSNamhyung Kim {
200aca7a94dSNamhyung Kim 	struct rb_node *nd;
201aca7a94dSNamhyung Kim 	int n = 0;
202aca7a94dSNamhyung Kim 
203aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
204aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
205aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
206aca7a94dSNamhyung Kim 	}
207aca7a94dSNamhyung Kim 
208aca7a94dSNamhyung Kim 	return n;
209aca7a94dSNamhyung Kim }
210aca7a94dSNamhyung Kim 
2113698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
212aca7a94dSNamhyung Kim {
2133698dab1SNamhyung Kim 	if (!he)
214aca7a94dSNamhyung Kim 		return false;
215aca7a94dSNamhyung Kim 
2163698dab1SNamhyung Kim 	if (!he->has_children)
217aca7a94dSNamhyung Kim 		return false;
218aca7a94dSNamhyung Kim 
2193698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
2203698dab1SNamhyung Kim 	return true;
2213698dab1SNamhyung Kim }
2223698dab1SNamhyung Kim 
2233698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
2243698dab1SNamhyung Kim {
2253698dab1SNamhyung Kim 	if (!cl)
2263698dab1SNamhyung Kim 		return false;
2273698dab1SNamhyung Kim 
2283698dab1SNamhyung Kim 	if (!cl->has_children)
2293698dab1SNamhyung Kim 		return false;
2303698dab1SNamhyung Kim 
2313698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
232aca7a94dSNamhyung Kim 	return true;
233aca7a94dSNamhyung Kim }
234aca7a94dSNamhyung Kim 
23505e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
236aca7a94dSNamhyung Kim {
23705e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
238aca7a94dSNamhyung Kim 
23905e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
240aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
241aca7a94dSNamhyung Kim 		struct callchain_list *chain;
242aca7a94dSNamhyung Kim 		bool first = true;
243aca7a94dSNamhyung Kim 
244aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
245aca7a94dSNamhyung Kim 			if (first) {
246aca7a94dSNamhyung Kim 				first = false;
2473698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
248aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
249aca7a94dSNamhyung Kim 			} else
2503698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
251aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
252aca7a94dSNamhyung Kim 		}
253aca7a94dSNamhyung Kim 
254aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
255aca7a94dSNamhyung Kim 	}
256aca7a94dSNamhyung Kim }
257aca7a94dSNamhyung Kim 
258a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
259a7444af6SNamhyung Kim 					       bool has_sibling)
260aca7a94dSNamhyung Kim {
261aca7a94dSNamhyung Kim 	struct callchain_list *chain;
262aca7a94dSNamhyung Kim 
263a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
2643698dab1SNamhyung Kim 	chain->has_children = has_sibling;
265a7444af6SNamhyung Kim 
26682162b5aSNamhyung Kim 	if (!list_empty(&node->val)) {
26782162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
2683698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
26982162b5aSNamhyung Kim 	}
270aca7a94dSNamhyung Kim 
27105e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
272aca7a94dSNamhyung Kim }
273aca7a94dSNamhyung Kim 
27405e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
275aca7a94dSNamhyung Kim {
276a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
277a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
278aca7a94dSNamhyung Kim 
27905e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
280aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
281a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
282aca7a94dSNamhyung Kim 	}
283aca7a94dSNamhyung Kim }
284aca7a94dSNamhyung Kim 
28505e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
286aca7a94dSNamhyung Kim {
28705e8b080SArnaldo Carvalho de Melo 	if (!he->init_have_children) {
2883698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
28905e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
29005e8b080SArnaldo Carvalho de Melo 		he->init_have_children = true;
291aca7a94dSNamhyung Kim 	}
292aca7a94dSNamhyung Kim }
293aca7a94dSNamhyung Kim 
29405e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
295aca7a94dSNamhyung Kim {
29605e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
2973698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
2983698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
2993698dab1SNamhyung Kim 	bool has_children;
300aca7a94dSNamhyung Kim 
3013698dab1SNamhyung Kim 	if (ms == &he->ms)
3023698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
3033698dab1SNamhyung Kim 	else
3043698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
3053698dab1SNamhyung Kim 
3063698dab1SNamhyung Kim 	if (has_children) {
307aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
308c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
309c3b78952SNamhyung Kim 		browser->nr_callchain_rows -= he->nr_rows;
310aca7a94dSNamhyung Kim 
3113698dab1SNamhyung Kim 		if (he->unfolded)
312aca7a94dSNamhyung Kim 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
313aca7a94dSNamhyung Kim 		else
314aca7a94dSNamhyung Kim 			he->nr_rows = 0;
315c3b78952SNamhyung Kim 
316c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
317c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
318aca7a94dSNamhyung Kim 
319aca7a94dSNamhyung Kim 		return true;
320aca7a94dSNamhyung Kim 	}
321aca7a94dSNamhyung Kim 
322aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
323aca7a94dSNamhyung Kim 	return false;
324aca7a94dSNamhyung Kim }
325aca7a94dSNamhyung Kim 
32605e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
327aca7a94dSNamhyung Kim {
328aca7a94dSNamhyung Kim 	int n = 0;
329aca7a94dSNamhyung Kim 	struct rb_node *nd;
330aca7a94dSNamhyung Kim 
33105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
332aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
333aca7a94dSNamhyung Kim 		struct callchain_list *chain;
334aca7a94dSNamhyung Kim 		bool has_children = false;
335aca7a94dSNamhyung Kim 
336aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
337aca7a94dSNamhyung Kim 			++n;
3383698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
3393698dab1SNamhyung Kim 			has_children = chain->has_children;
340aca7a94dSNamhyung Kim 		}
341aca7a94dSNamhyung Kim 
342aca7a94dSNamhyung Kim 		if (has_children)
343aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
344aca7a94dSNamhyung Kim 	}
345aca7a94dSNamhyung Kim 
346aca7a94dSNamhyung Kim 	return n;
347aca7a94dSNamhyung Kim }
348aca7a94dSNamhyung Kim 
349aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
350aca7a94dSNamhyung Kim {
351aca7a94dSNamhyung Kim 	struct callchain_list *chain;
352aca7a94dSNamhyung Kim 	bool has_children = false;
353aca7a94dSNamhyung Kim 	int n = 0;
354aca7a94dSNamhyung Kim 
355aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
356aca7a94dSNamhyung Kim 		++n;
3573698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
3583698dab1SNamhyung Kim 		has_children = chain->has_children;
359aca7a94dSNamhyung Kim 	}
360aca7a94dSNamhyung Kim 
361aca7a94dSNamhyung Kim 	if (has_children)
362aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
363aca7a94dSNamhyung Kim 
364aca7a94dSNamhyung Kim 	return n;
365aca7a94dSNamhyung Kim }
366aca7a94dSNamhyung Kim 
367aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
368aca7a94dSNamhyung Kim {
369aca7a94dSNamhyung Kim 	struct rb_node *nd;
370aca7a94dSNamhyung Kim 	int n = 0;
371aca7a94dSNamhyung Kim 
372aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
373aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
375aca7a94dSNamhyung Kim 	}
376aca7a94dSNamhyung Kim 
377aca7a94dSNamhyung Kim 	return n;
378aca7a94dSNamhyung Kim }
379aca7a94dSNamhyung Kim 
38005e8b080SArnaldo Carvalho de Melo static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
381aca7a94dSNamhyung Kim {
38205e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
3833698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
384aca7a94dSNamhyung Kim 
3853698dab1SNamhyung Kim 	if (he->has_children) {
38605e8b080SArnaldo Carvalho de Melo 		int n = callchain__set_folding(&he->sorted_chain, unfold);
38705e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
388aca7a94dSNamhyung Kim 	} else
38905e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
390aca7a94dSNamhyung Kim }
391aca7a94dSNamhyung Kim 
392c3b78952SNamhyung Kim static void
393c3b78952SNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
394aca7a94dSNamhyung Kim {
395aca7a94dSNamhyung Kim 	struct rb_node *nd;
396c3b78952SNamhyung Kim 	struct hists *hists = browser->hists;
397aca7a94dSNamhyung Kim 
398c3b78952SNamhyung Kim 	for (nd = rb_first(&hists->entries);
39914135663SNamhyung Kim 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
400c3b78952SNamhyung Kim 	     nd = rb_next(nd)) {
401aca7a94dSNamhyung Kim 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
402aca7a94dSNamhyung Kim 		hist_entry__set_folding(he, unfold);
403c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
404aca7a94dSNamhyung Kim 	}
405aca7a94dSNamhyung Kim }
406aca7a94dSNamhyung Kim 
40705e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
408aca7a94dSNamhyung Kim {
409c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
410c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
411c3b78952SNamhyung Kim 
412c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
413aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
41405e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
415aca7a94dSNamhyung Kim }
416aca7a94dSNamhyung Kim 
417aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
418aca7a94dSNamhyung Kim {
419aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
420aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
421aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
422aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
423aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
424aca7a94dSNamhyung Kim }
425aca7a94dSNamhyung Kim 
4265f00b0f4SArnaldo Carvalho de Melo static int hist_browser__run(struct hist_browser *browser, const char *help)
427aca7a94dSNamhyung Kim {
428aca7a94dSNamhyung Kim 	int key;
429aca7a94dSNamhyung Kim 	char title[160];
430c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
4319783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
432aca7a94dSNamhyung Kim 
43305e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
434c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
435aca7a94dSNamhyung Kim 
4361e378ebdSTaeung Song 	hists__browser_title(browser->hists, hbt, title, sizeof(title));
437aca7a94dSNamhyung Kim 
4385f00b0f4SArnaldo Carvalho de Melo 	if (ui_browser__show(&browser->b, title, help) < 0)
439aca7a94dSNamhyung Kim 		return -1;
440aca7a94dSNamhyung Kim 
441aca7a94dSNamhyung Kim 	while (1) {
44205e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
443aca7a94dSNamhyung Kim 
444aca7a94dSNamhyung Kim 		switch (key) {
445fa5df943SNamhyung Kim 		case K_TIMER: {
446fa5df943SNamhyung Kim 			u64 nr_entries;
4479783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
448fa5df943SNamhyung Kim 
449c3b78952SNamhyung Kim 			if (hist_browser__has_filter(browser))
450112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
451fa5df943SNamhyung Kim 
452c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
453fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
454aca7a94dSNamhyung Kim 
45505e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
45605e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
45705e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
45805e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
45905e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
460aca7a94dSNamhyung Kim 			}
461aca7a94dSNamhyung Kim 
4621e378ebdSTaeung Song 			hists__browser_title(browser->hists,
4631e378ebdSTaeung Song 					     hbt, title, sizeof(title));
46405e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
465aca7a94dSNamhyung Kim 			continue;
466fa5df943SNamhyung Kim 		}
467aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
468aca7a94dSNamhyung Kim 			static int seq;
46905e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
470aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
471aca7a94dSNamhyung Kim 			ui_helpline__pop();
47262c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
47305e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
47405e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
47562c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
47605e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
47705e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
478aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
479aca7a94dSNamhyung Kim 		}
480aca7a94dSNamhyung Kim 			break;
481aca7a94dSNamhyung Kim 		case 'C':
482aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
48305e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
484aca7a94dSNamhyung Kim 			break;
485aca7a94dSNamhyung Kim 		case 'E':
486aca7a94dSNamhyung Kim 			/* Expand the whole world. */
48705e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
488aca7a94dSNamhyung Kim 			break;
489025bf7eaSArnaldo Carvalho de Melo 		case 'H':
490025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
491025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
492025bf7eaSArnaldo Carvalho de Melo 			break;
493aca7a94dSNamhyung Kim 		case K_ENTER:
49405e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
495aca7a94dSNamhyung Kim 				break;
496aca7a94dSNamhyung Kim 			/* fall thru */
497aca7a94dSNamhyung Kim 		default:
498aca7a94dSNamhyung Kim 			goto out;
499aca7a94dSNamhyung Kim 		}
500aca7a94dSNamhyung Kim 	}
501aca7a94dSNamhyung Kim out:
50205e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
503aca7a94dSNamhyung Kim 	return key;
504aca7a94dSNamhyung Kim }
505aca7a94dSNamhyung Kim 
50639ee533fSNamhyung Kim struct callchain_print_arg {
50739ee533fSNamhyung Kim 	/* for hists browser */
50839ee533fSNamhyung Kim 	off_t	row_offset;
50939ee533fSNamhyung Kim 	bool	is_current_entry;
51039ee533fSNamhyung Kim 
51139ee533fSNamhyung Kim 	/* for file dump */
51239ee533fSNamhyung Kim 	FILE	*fp;
51339ee533fSNamhyung Kim 	int	printed;
51439ee533fSNamhyung Kim };
51539ee533fSNamhyung Kim 
51639ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
51739ee533fSNamhyung Kim 					 struct callchain_list *chain,
51839ee533fSNamhyung Kim 					 const char *str, int offset,
51939ee533fSNamhyung Kim 					 unsigned short row,
52039ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
52139ee533fSNamhyung Kim 
522f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
523f4536dddSNamhyung Kim 					       struct callchain_list *chain,
52439ee533fSNamhyung Kim 					       const char *str, int offset,
52539ee533fSNamhyung Kim 					       unsigned short row,
52639ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
527f4536dddSNamhyung Kim {
528f4536dddSNamhyung Kim 	int color, width;
52939ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
53070e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
531f4536dddSNamhyung Kim 
532f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
533f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
534f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
535f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
536f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
53739ee533fSNamhyung Kim 		arg->is_current_entry = true;
538f4536dddSNamhyung Kim 	}
539f4536dddSNamhyung Kim 
540f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
541f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
54226270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
543517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
54470e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
54526270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
546f4536dddSNamhyung Kim }
547f4536dddSNamhyung Kim 
54839ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
54939ee533fSNamhyung Kim 						  struct callchain_list *chain,
55039ee533fSNamhyung Kim 						  const char *str, int offset,
55139ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
55239ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
55339ee533fSNamhyung Kim {
55439ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
55539ee533fSNamhyung Kim 
55639ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
55739ee533fSNamhyung Kim 				folded_sign, str);
55839ee533fSNamhyung Kim }
55939ee533fSNamhyung Kim 
56039ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
56139ee533fSNamhyung Kim 				     unsigned short row);
56239ee533fSNamhyung Kim 
56339ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
56439ee533fSNamhyung Kim 					    unsigned short row)
56539ee533fSNamhyung Kim {
56639ee533fSNamhyung Kim 	return browser->b.rows == row;
56739ee533fSNamhyung Kim }
56839ee533fSNamhyung Kim 
56939ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
57039ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
57139ee533fSNamhyung Kim {
57239ee533fSNamhyung Kim 	return false;
57339ee533fSNamhyung Kim }
57439ee533fSNamhyung Kim 
575aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
576aca7a94dSNamhyung Kim 
577c09a7e75SNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
578c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
57939ee533fSNamhyung Kim 					unsigned short row, u64 total,
58039ee533fSNamhyung Kim 					print_callchain_entry_fn print,
58139ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
58239ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
583aca7a94dSNamhyung Kim {
584aca7a94dSNamhyung Kim 	struct rb_node *node;
585f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
58636e15dd4SNamhyung Kim 	u64 new_total;
5874087d11cSNamhyung Kim 	bool need_percent;
588aca7a94dSNamhyung Kim 
589c09a7e75SNamhyung Kim 	node = rb_first(root);
590c09e31ccSNamhyung Kim 	need_percent = node && rb_next(node);
5914087d11cSNamhyung Kim 
592aca7a94dSNamhyung Kim 	while (node) {
593aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
594aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
595aca7a94dSNamhyung Kim 		u64 cumul = callchain_cumul_hits(child);
596aca7a94dSNamhyung Kim 		struct callchain_list *chain;
597aca7a94dSNamhyung Kim 		char folded_sign = ' ';
598aca7a94dSNamhyung Kim 		int first = true;
599aca7a94dSNamhyung Kim 		int extra_offset = 0;
600aca7a94dSNamhyung Kim 
601aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
602a7cb8863SArnaldo Carvalho de Melo 			char bf[1024], *alloc_str;
603aca7a94dSNamhyung Kim 			const char *str;
604aca7a94dSNamhyung Kim 			bool was_first = first;
605aca7a94dSNamhyung Kim 
606aca7a94dSNamhyung Kim 			if (first)
607aca7a94dSNamhyung Kim 				first = false;
6084087d11cSNamhyung Kim 			else if (need_percent)
609aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
610aca7a94dSNamhyung Kim 
611aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
61239ee533fSNamhyung Kim 			if (arg->row_offset != 0) {
61339ee533fSNamhyung Kim 				arg->row_offset--;
614aca7a94dSNamhyung Kim 				goto do_next;
615aca7a94dSNamhyung Kim 			}
616aca7a94dSNamhyung Kim 
617aca7a94dSNamhyung Kim 			alloc_str = NULL;
618a7cb8863SArnaldo Carvalho de Melo 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
619a7cb8863SArnaldo Carvalho de Melo 						       browser->show_dso);
620c09a7e75SNamhyung Kim 
6214087d11cSNamhyung Kim 			if (was_first && need_percent) {
622c09a7e75SNamhyung Kim 				double percent = cumul * 100.0 / total;
623aca7a94dSNamhyung Kim 
624aca7a94dSNamhyung Kim 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
625aca7a94dSNamhyung Kim 					str = "Not enough memory!";
626aca7a94dSNamhyung Kim 				else
627aca7a94dSNamhyung Kim 					str = alloc_str;
628aca7a94dSNamhyung Kim 			}
629aca7a94dSNamhyung Kim 
63039ee533fSNamhyung Kim 			print(browser, chain, str, offset + extra_offset, row, arg);
63139ee533fSNamhyung Kim 
632aca7a94dSNamhyung Kim 			free(alloc_str);
633aca7a94dSNamhyung Kim 
63439ee533fSNamhyung Kim 			if (is_output_full(browser, ++row))
635aca7a94dSNamhyung Kim 				goto out;
636aca7a94dSNamhyung Kim do_next:
637aca7a94dSNamhyung Kim 			if (folded_sign == '+')
638aca7a94dSNamhyung Kim 				break;
639aca7a94dSNamhyung Kim 		}
640aca7a94dSNamhyung Kim 
641aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
642aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
643c09a7e75SNamhyung Kim 
644c09a7e75SNamhyung Kim 			if (callchain_param.mode == CHAIN_GRAPH_REL)
645c09a7e75SNamhyung Kim 				new_total = child->children_hit;
646c09a7e75SNamhyung Kim 			else
647c09a7e75SNamhyung Kim 				new_total = total;
648c09a7e75SNamhyung Kim 
649c09a7e75SNamhyung Kim 			row += hist_browser__show_callchain(browser, &child->rb_root,
65039ee533fSNamhyung Kim 							    new_level, row, new_total,
65139ee533fSNamhyung Kim 							    print, arg, is_output_full);
652aca7a94dSNamhyung Kim 		}
65339ee533fSNamhyung Kim 		if (is_output_full(browser, row))
654c09a7e75SNamhyung Kim 			break;
655aca7a94dSNamhyung Kim 		node = next;
656aca7a94dSNamhyung Kim 	}
657aca7a94dSNamhyung Kim out:
658aca7a94dSNamhyung Kim 	return row - first_row;
659aca7a94dSNamhyung Kim }
660aca7a94dSNamhyung Kim 
66189701460SNamhyung Kim struct hpp_arg {
66289701460SNamhyung Kim 	struct ui_browser *b;
66389701460SNamhyung Kim 	char folded_sign;
66489701460SNamhyung Kim 	bool current_entry;
66589701460SNamhyung Kim };
66689701460SNamhyung Kim 
6672f6d9009SNamhyung Kim static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
6682f6d9009SNamhyung Kim {
6692f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
670d675107cSNamhyung Kim 	int ret, len;
6712f6d9009SNamhyung Kim 	va_list args;
6722f6d9009SNamhyung Kim 	double percent;
6732f6d9009SNamhyung Kim 
6742f6d9009SNamhyung Kim 	va_start(args, fmt);
675d675107cSNamhyung Kim 	len = va_arg(args, int);
6762f6d9009SNamhyung Kim 	percent = va_arg(args, double);
6772f6d9009SNamhyung Kim 	va_end(args);
6785aed9d24SNamhyung Kim 
67989701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
6805aed9d24SNamhyung Kim 
681d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
682517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
68389701460SNamhyung Kim 
6842f6d9009SNamhyung Kim 	advance_hpp(hpp, ret);
6855aed9d24SNamhyung Kim 	return ret;
686f5951d56SNamhyung Kim }
687f5951d56SNamhyung Kim 
688fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
6895aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
6905aed9d24SNamhyung Kim {									\
6915aed9d24SNamhyung Kim 	return he->stat._field;						\
6925aed9d24SNamhyung Kim }									\
6935aed9d24SNamhyung Kim 									\
6942c5d4b4aSJiri Olsa static int								\
6955b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
6962c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
6975aed9d24SNamhyung Kim 				struct hist_entry *he)			\
6985aed9d24SNamhyung Kim {									\
6995b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
7002f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
7015aed9d24SNamhyung Kim }
702f5951d56SNamhyung Kim 
7030434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
7040434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
7050434ddd2SNamhyung Kim {									\
7060434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
7070434ddd2SNamhyung Kim }									\
7080434ddd2SNamhyung Kim 									\
7090434ddd2SNamhyung Kim static int								\
7105b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
7110434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
7120434ddd2SNamhyung Kim 				struct hist_entry *he)			\
7130434ddd2SNamhyung Kim {									\
7140434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
715517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
7165b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
717d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
7185b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
719517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
7200434ddd2SNamhyung Kim 									\
7210434ddd2SNamhyung Kim 		return ret;						\
7220434ddd2SNamhyung Kim 	}								\
7235b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
7245b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
7250434ddd2SNamhyung Kim }
7260434ddd2SNamhyung Kim 
727fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
728fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
729fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
730fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
731fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
7320434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
7335aed9d24SNamhyung Kim 
7345aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
7350434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
736f5951d56SNamhyung Kim 
737f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
738f5951d56SNamhyung Kim {
739f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
740f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
741f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
742f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
743f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
744f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
745f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
746f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
747f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
748f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
7490434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
7500434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
751f5951d56SNamhyung Kim }
752f5951d56SNamhyung Kim 
75305e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
754aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
755aca7a94dSNamhyung Kim 				    unsigned short row)
756aca7a94dSNamhyung Kim {
757aca7a94dSNamhyung Kim 	char s[256];
7581240005eSJiri Olsa 	int printed = 0;
75967d25916SNamhyung Kim 	int width = browser->b.width;
760aca7a94dSNamhyung Kim 	char folded_sign = ' ';
76105e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
762aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
76363a1a3d8SNamhyung Kim 	bool first = true;
7641240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
765aca7a94dSNamhyung Kim 
766aca7a94dSNamhyung Kim 	if (current_entry) {
76705e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
76805e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
769aca7a94dSNamhyung Kim 	}
770aca7a94dSNamhyung Kim 
771aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
772aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
773aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
774aca7a94dSNamhyung Kim 	}
775aca7a94dSNamhyung Kim 
776aca7a94dSNamhyung Kim 	if (row_offset == 0) {
77789701460SNamhyung Kim 		struct hpp_arg arg = {
77889701460SNamhyung Kim 			.b		= &browser->b,
77989701460SNamhyung Kim 			.folded_sign	= folded_sign,
78089701460SNamhyung Kim 			.current_entry	= current_entry,
78189701460SNamhyung Kim 		};
782f5951d56SNamhyung Kim 		struct perf_hpp hpp = {
783f5951d56SNamhyung Kim 			.buf		= s,
784f5951d56SNamhyung Kim 			.size		= sizeof(s),
78589701460SNamhyung Kim 			.ptr		= &arg,
786f5951d56SNamhyung Kim 		};
787c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
788f5951d56SNamhyung Kim 
789ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
790f5951d56SNamhyung Kim 
7911240005eSJiri Olsa 		perf_hpp__for_each_format(fmt) {
792c6c3c02dSArnaldo Carvalho de Melo 			if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
793e67d49a7SNamhyung Kim 				continue;
794e67d49a7SNamhyung Kim 
795fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
796fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
797fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
798fb821c9eSNamhyung Kim 			} else {
799fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
800fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
801fb821c9eSNamhyung Kim 			}
802fb821c9eSNamhyung Kim 
803fb821c9eSNamhyung Kim 			if (first) {
804fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
805517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
806f5951d56SNamhyung Kim 					width -= 2;
807f5951d56SNamhyung Kim 				}
80863a1a3d8SNamhyung Kim 				first = false;
809fb821c9eSNamhyung Kim 			} else {
810517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
811fb821c9eSNamhyung Kim 				width -= 2;
812fb821c9eSNamhyung Kim 			}
813f5951d56SNamhyung Kim 
8141240005eSJiri Olsa 			if (fmt->color) {
8152c5d4b4aSJiri Olsa 				width -= fmt->color(fmt, &hpp, entry);
816f5951d56SNamhyung Kim 			} else {
8172c5d4b4aSJiri Olsa 				width -= fmt->entry(fmt, &hpp, entry);
818517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
819f5951d56SNamhyung Kim 			}
820f5951d56SNamhyung Kim 		}
821aca7a94dSNamhyung Kim 
822aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
82305e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
824aca7a94dSNamhyung Kim 			width += 1;
825aca7a94dSNamhyung Kim 
82626270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
82726d8b338SNamhyung Kim 
828aca7a94dSNamhyung Kim 		++row;
829aca7a94dSNamhyung Kim 		++printed;
830aca7a94dSNamhyung Kim 	} else
831aca7a94dSNamhyung Kim 		--row_offset;
832aca7a94dSNamhyung Kim 
83362c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
834c09a7e75SNamhyung Kim 		u64 total = hists__total_period(entry->hists);
83539ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
83639ee533fSNamhyung Kim 			.row_offset = row_offset,
83739ee533fSNamhyung Kim 			.is_current_entry = current_entry,
83839ee533fSNamhyung Kim 		};
839c09a7e75SNamhyung Kim 
8404087d11cSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
8414087d11cSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
8424087d11cSNamhyung Kim 				total = entry->stat_acc->period;
8434087d11cSNamhyung Kim 			else
8444087d11cSNamhyung Kim 				total = entry->stat.period;
8454087d11cSNamhyung Kim 		}
8464087d11cSNamhyung Kim 
847c09a7e75SNamhyung Kim 		printed += hist_browser__show_callchain(browser,
84839ee533fSNamhyung Kim 					&entry->sorted_chain, 1, row, total,
84939ee533fSNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
85039ee533fSNamhyung Kim 					hist_browser__check_output_full);
851c09a7e75SNamhyung Kim 
85239ee533fSNamhyung Kim 		if (arg.is_current_entry)
85305e8b080SArnaldo Carvalho de Melo 			browser->he_selection = entry;
854aca7a94dSNamhyung Kim 	}
855aca7a94dSNamhyung Kim 
856aca7a94dSNamhyung Kim 	return printed;
857aca7a94dSNamhyung Kim }
858aca7a94dSNamhyung Kim 
85981a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
86081a888feSJiri Olsa {
86181a888feSJiri Olsa 	advance_hpp(hpp, inc);
86281a888feSJiri Olsa 	return hpp->size <= 0;
86381a888feSJiri Olsa }
86481a888feSJiri Olsa 
865c6c3c02dSArnaldo Carvalho de Melo static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
86681a888feSJiri Olsa {
867c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
86881a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
86981a888feSJiri Olsa 		.buf    = buf,
87081a888feSJiri Olsa 		.size   = size,
87181a888feSJiri Olsa 	};
87281a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
87381a888feSJiri Olsa 	size_t ret = 0;
874c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
87581a888feSJiri Olsa 
87681a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
87781a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
87881a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
87981a888feSJiri Olsa 			return ret;
88081a888feSJiri Olsa 	}
88181a888feSJiri Olsa 
88281a888feSJiri Olsa 	perf_hpp__for_each_format(fmt) {
883c6c3c02dSArnaldo Carvalho de Melo 		if (perf_hpp__should_skip(fmt)  || column++ < browser->b.horiz_scroll)
88481a888feSJiri Olsa 			continue;
88581a888feSJiri Olsa 
88681a888feSJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
88781a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
88881a888feSJiri Olsa 			break;
88981a888feSJiri Olsa 
89081a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
89181a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
89281a888feSJiri Olsa 			break;
89381a888feSJiri Olsa 	}
89481a888feSJiri Olsa 
89581a888feSJiri Olsa 	return ret;
89681a888feSJiri Olsa }
89781a888feSJiri Olsa 
898025bf7eaSArnaldo Carvalho de Melo static void hist_browser__show_headers(struct hist_browser *browser)
899025bf7eaSArnaldo Carvalho de Melo {
90081a888feSJiri Olsa 	char headers[1024];
90181a888feSJiri Olsa 
902c6c3c02dSArnaldo Carvalho de Melo 	hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
903025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
904025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
90526270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
906025bf7eaSArnaldo Carvalho de Melo }
907025bf7eaSArnaldo Carvalho de Melo 
908aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
909aca7a94dSNamhyung Kim {
910aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
911aca7a94dSNamhyung Kim 		struct hist_browser *hb;
912aca7a94dSNamhyung Kim 
913aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
914aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
915aca7a94dSNamhyung Kim 	}
916aca7a94dSNamhyung Kim }
917aca7a94dSNamhyung Kim 
91805e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
919aca7a94dSNamhyung Kim {
920aca7a94dSNamhyung Kim 	unsigned row = 0;
921025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
922aca7a94dSNamhyung Kim 	struct rb_node *nd;
92305e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
924aca7a94dSNamhyung Kim 
925025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
926025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
927025bf7eaSArnaldo Carvalho de Melo 		header_offset = 1;
928025bf7eaSArnaldo Carvalho de Melo 	}
929025bf7eaSArnaldo Carvalho de Melo 
93005e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
931*979d2cacSWang Nan 	hb->he_selection = NULL;
932*979d2cacSWang Nan 	hb->selection = NULL;
933aca7a94dSNamhyung Kim 
93405e8b080SArnaldo Carvalho de Melo 	for (nd = browser->top; nd; nd = rb_next(nd)) {
935aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
93614135663SNamhyung Kim 		float percent;
937aca7a94dSNamhyung Kim 
938aca7a94dSNamhyung Kim 		if (h->filtered)
939aca7a94dSNamhyung Kim 			continue;
940aca7a94dSNamhyung Kim 
94114135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
942064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
943064f1981SNamhyung Kim 			continue;
944064f1981SNamhyung Kim 
945aca7a94dSNamhyung Kim 		row += hist_browser__show_entry(hb, h, row);
94662c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
947aca7a94dSNamhyung Kim 			break;
948aca7a94dSNamhyung Kim 	}
949aca7a94dSNamhyung Kim 
950025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
951aca7a94dSNamhyung Kim }
952aca7a94dSNamhyung Kim 
953064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
954064f1981SNamhyung Kim 					     float min_pcnt)
955aca7a94dSNamhyung Kim {
956aca7a94dSNamhyung Kim 	while (nd != NULL) {
957aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
95814135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
959064f1981SNamhyung Kim 
960c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
961aca7a94dSNamhyung Kim 			return nd;
962aca7a94dSNamhyung Kim 
963aca7a94dSNamhyung Kim 		nd = rb_next(nd);
964aca7a94dSNamhyung Kim 	}
965aca7a94dSNamhyung Kim 
966aca7a94dSNamhyung Kim 	return NULL;
967aca7a94dSNamhyung Kim }
968aca7a94dSNamhyung Kim 
969064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
970064f1981SNamhyung Kim 						  float min_pcnt)
971aca7a94dSNamhyung Kim {
972aca7a94dSNamhyung Kim 	while (nd != NULL) {
973aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
97414135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
975064f1981SNamhyung Kim 
976064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
977aca7a94dSNamhyung Kim 			return nd;
978aca7a94dSNamhyung Kim 
979aca7a94dSNamhyung Kim 		nd = rb_prev(nd);
980aca7a94dSNamhyung Kim 	}
981aca7a94dSNamhyung Kim 
982aca7a94dSNamhyung Kim 	return NULL;
983aca7a94dSNamhyung Kim }
984aca7a94dSNamhyung Kim 
98505e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
986aca7a94dSNamhyung Kim 				   off_t offset, int whence)
987aca7a94dSNamhyung Kim {
988aca7a94dSNamhyung Kim 	struct hist_entry *h;
989aca7a94dSNamhyung Kim 	struct rb_node *nd;
990aca7a94dSNamhyung Kim 	bool first = true;
991064f1981SNamhyung Kim 	struct hist_browser *hb;
992064f1981SNamhyung Kim 
993064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
994aca7a94dSNamhyung Kim 
99505e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
996aca7a94dSNamhyung Kim 		return;
997aca7a94dSNamhyung Kim 
99805e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
999aca7a94dSNamhyung Kim 
1000aca7a94dSNamhyung Kim 	switch (whence) {
1001aca7a94dSNamhyung Kim 	case SEEK_SET:
1002064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
100314135663SNamhyung Kim 					   hb->min_pcnt);
1004aca7a94dSNamhyung Kim 		break;
1005aca7a94dSNamhyung Kim 	case SEEK_CUR:
100605e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1007aca7a94dSNamhyung Kim 		goto do_offset;
1008aca7a94dSNamhyung Kim 	case SEEK_END:
1009064f1981SNamhyung Kim 		nd = hists__filter_prev_entries(rb_last(browser->entries),
101014135663SNamhyung Kim 						hb->min_pcnt);
1011aca7a94dSNamhyung Kim 		first = false;
1012aca7a94dSNamhyung Kim 		break;
1013aca7a94dSNamhyung Kim 	default:
1014aca7a94dSNamhyung Kim 		return;
1015aca7a94dSNamhyung Kim 	}
1016aca7a94dSNamhyung Kim 
1017aca7a94dSNamhyung Kim 	/*
1018aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1019aca7a94dSNamhyung Kim 	 * row_offset:
1020aca7a94dSNamhyung Kim 	 */
102105e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1022aca7a94dSNamhyung Kim 	h->row_offset = 0;
1023aca7a94dSNamhyung Kim 
1024aca7a94dSNamhyung Kim 	/*
1025aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1026aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1027aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1028aca7a94dSNamhyung Kim 	 *
1029aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1030aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1031aca7a94dSNamhyung Kim 	 *
1032aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1033aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1034aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1035aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1036aca7a94dSNamhyung Kim 	 */
1037aca7a94dSNamhyung Kim do_offset:
1038837eeb75SWang Nan 	if (!nd)
1039837eeb75SWang Nan 		return;
1040837eeb75SWang Nan 
1041aca7a94dSNamhyung Kim 	if (offset > 0) {
1042aca7a94dSNamhyung Kim 		do {
1043aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
10443698dab1SNamhyung Kim 			if (h->unfolded) {
1045aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1046aca7a94dSNamhyung Kim 				if (offset > remaining) {
1047aca7a94dSNamhyung Kim 					offset -= remaining;
1048aca7a94dSNamhyung Kim 					h->row_offset = 0;
1049aca7a94dSNamhyung Kim 				} else {
1050aca7a94dSNamhyung Kim 					h->row_offset += offset;
1051aca7a94dSNamhyung Kim 					offset = 0;
105205e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1053aca7a94dSNamhyung Kim 					break;
1054aca7a94dSNamhyung Kim 				}
1055aca7a94dSNamhyung Kim 			}
105614135663SNamhyung Kim 			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1057aca7a94dSNamhyung Kim 			if (nd == NULL)
1058aca7a94dSNamhyung Kim 				break;
1059aca7a94dSNamhyung Kim 			--offset;
106005e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1061aca7a94dSNamhyung Kim 		} while (offset != 0);
1062aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1063aca7a94dSNamhyung Kim 		while (1) {
1064aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
10653698dab1SNamhyung Kim 			if (h->unfolded) {
1066aca7a94dSNamhyung Kim 				if (first) {
1067aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1068aca7a94dSNamhyung Kim 						offset += h->row_offset;
1069aca7a94dSNamhyung Kim 						h->row_offset = 0;
1070aca7a94dSNamhyung Kim 					} else {
1071aca7a94dSNamhyung Kim 						h->row_offset += offset;
1072aca7a94dSNamhyung Kim 						offset = 0;
107305e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1074aca7a94dSNamhyung Kim 						break;
1075aca7a94dSNamhyung Kim 					}
1076aca7a94dSNamhyung Kim 				} else {
1077aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1078aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1079aca7a94dSNamhyung Kim 						h->row_offset = 0;
1080aca7a94dSNamhyung Kim 					} else {
1081aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1082aca7a94dSNamhyung Kim 						offset = 0;
108305e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1084aca7a94dSNamhyung Kim 						break;
1085aca7a94dSNamhyung Kim 					}
1086aca7a94dSNamhyung Kim 				}
1087aca7a94dSNamhyung Kim 			}
1088aca7a94dSNamhyung Kim 
108914135663SNamhyung Kim 			nd = hists__filter_prev_entries(rb_prev(nd),
1090064f1981SNamhyung Kim 							hb->min_pcnt);
1091aca7a94dSNamhyung Kim 			if (nd == NULL)
1092aca7a94dSNamhyung Kim 				break;
1093aca7a94dSNamhyung Kim 			++offset;
109405e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1095aca7a94dSNamhyung Kim 			if (offset == 0) {
1096aca7a94dSNamhyung Kim 				/*
1097aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1098aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1099aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1100aca7a94dSNamhyung Kim 				 */
1101aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
11023698dab1SNamhyung Kim 				if (h->unfolded)
1103aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1104aca7a94dSNamhyung Kim 				break;
1105aca7a94dSNamhyung Kim 			}
1106aca7a94dSNamhyung Kim 			first = false;
1107aca7a94dSNamhyung Kim 		}
1108aca7a94dSNamhyung Kim 	} else {
110905e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1110aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1111aca7a94dSNamhyung Kim 		h->row_offset = 0;
1112aca7a94dSNamhyung Kim 	}
1113aca7a94dSNamhyung Kim }
1114aca7a94dSNamhyung Kim 
1115aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
111639ee533fSNamhyung Kim 					   struct hist_entry *he, FILE *fp)
1117aff3f3f6SArnaldo Carvalho de Melo {
111839ee533fSNamhyung Kim 	u64 total = hists__total_period(he->hists);
111939ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
112039ee533fSNamhyung Kim 		.fp = fp,
112139ee533fSNamhyung Kim 	};
1122aff3f3f6SArnaldo Carvalho de Melo 
112339ee533fSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
112439ee533fSNamhyung Kim 		total = he->stat_acc->period;
1125aff3f3f6SArnaldo Carvalho de Melo 
112639ee533fSNamhyung Kim 	hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
112739ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
112839ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
112939ee533fSNamhyung Kim 	return arg.printed;
1130aff3f3f6SArnaldo Carvalho de Melo }
1131aff3f3f6SArnaldo Carvalho de Melo 
1132aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1133aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1134aff3f3f6SArnaldo Carvalho de Melo {
1135aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1136aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1137aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
113826d8b338SNamhyung Kim 	struct perf_hpp hpp = {
113926d8b338SNamhyung Kim 		.buf = s,
114026d8b338SNamhyung Kim 		.size = sizeof(s),
114126d8b338SNamhyung Kim 	};
114226d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
114326d8b338SNamhyung Kim 	bool first = true;
114426d8b338SNamhyung Kim 	int ret;
1145aff3f3f6SArnaldo Carvalho de Melo 
1146aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1147aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1148aff3f3f6SArnaldo Carvalho de Melo 
1149aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1150aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
1151aff3f3f6SArnaldo Carvalho de Melo 
115226d8b338SNamhyung Kim 	perf_hpp__for_each_format(fmt) {
1153e67d49a7SNamhyung Kim 		if (perf_hpp__should_skip(fmt))
1154e67d49a7SNamhyung Kim 			continue;
1155e67d49a7SNamhyung Kim 
115626d8b338SNamhyung Kim 		if (!first) {
115726d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
115826d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
115926d8b338SNamhyung Kim 		} else
116026d8b338SNamhyung Kim 			first = false;
1161aff3f3f6SArnaldo Carvalho de Melo 
116226d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
116326d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
116426d8b338SNamhyung Kim 	}
1165aff3f3f6SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", rtrim(s));
1166aff3f3f6SArnaldo Carvalho de Melo 
1167aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
116839ee533fSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp);
1169aff3f3f6SArnaldo Carvalho de Melo 
1170aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1171aff3f3f6SArnaldo Carvalho de Melo }
1172aff3f3f6SArnaldo Carvalho de Melo 
1173aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1174aff3f3f6SArnaldo Carvalho de Melo {
1175064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1176064f1981SNamhyung Kim 						   browser->min_pcnt);
1177aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1178aff3f3f6SArnaldo Carvalho de Melo 
1179aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1180aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1181aff3f3f6SArnaldo Carvalho de Melo 
1182aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_entry(browser, h, fp);
118314135663SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1184aff3f3f6SArnaldo Carvalho de Melo 	}
1185aff3f3f6SArnaldo Carvalho de Melo 
1186aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1187aff3f3f6SArnaldo Carvalho de Melo }
1188aff3f3f6SArnaldo Carvalho de Melo 
1189aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
1190aff3f3f6SArnaldo Carvalho de Melo {
1191aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
1192aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
1193aff3f3f6SArnaldo Carvalho de Melo 
1194aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
1195aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1196aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
1197aff3f3f6SArnaldo Carvalho de Melo 			break;
1198aff3f3f6SArnaldo Carvalho de Melo 		/*
1199aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
1200aff3f3f6SArnaldo Carvalho de Melo  		 */
1201aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
1202aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1203aff3f3f6SArnaldo Carvalho de Melo 			return -1;
1204aff3f3f6SArnaldo Carvalho de Melo 		}
1205aff3f3f6SArnaldo Carvalho de Melo 	}
1206aff3f3f6SArnaldo Carvalho de Melo 
1207aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
1208aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
1209aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
12104cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
12114cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1212aff3f3f6SArnaldo Carvalho de Melo 		return -1;
1213aff3f3f6SArnaldo Carvalho de Melo 	}
1214aff3f3f6SArnaldo Carvalho de Melo 
1215aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
1216aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
1217aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
1218aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
1219aff3f3f6SArnaldo Carvalho de Melo 
1220aff3f3f6SArnaldo Carvalho de Melo 	return 0;
1221aff3f3f6SArnaldo Carvalho de Melo }
1222aff3f3f6SArnaldo Carvalho de Melo 
1223c2a51ab8SNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists,
1224b1a9ceefSNamhyung Kim 					      struct hist_browser_timer *hbt,
1225ce80d3beSKan Liang 					      struct perf_env *env)
1226aca7a94dSNamhyung Kim {
122705e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
1228aca7a94dSNamhyung Kim 
122905e8b080SArnaldo Carvalho de Melo 	if (browser) {
123005e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
123105e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
1232357cfff1SArnaldo Carvalho de Melo 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
123305e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
123405e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
1235c8302367SJiri Olsa 		browser->show_headers = symbol_conf.show_hist_headers;
1236c2a51ab8SNamhyung Kim 		browser->hbt = hbt;
1237b1a9ceefSNamhyung Kim 		browser->env = env;
1238aca7a94dSNamhyung Kim 	}
1239aca7a94dSNamhyung Kim 
124005e8b080SArnaldo Carvalho de Melo 	return browser;
1241aca7a94dSNamhyung Kim }
1242aca7a94dSNamhyung Kim 
124305e8b080SArnaldo Carvalho de Melo static void hist_browser__delete(struct hist_browser *browser)
1244aca7a94dSNamhyung Kim {
124505e8b080SArnaldo Carvalho de Melo 	free(browser);
1246aca7a94dSNamhyung Kim }
1247aca7a94dSNamhyung Kim 
124805e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1249aca7a94dSNamhyung Kim {
125005e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
1251aca7a94dSNamhyung Kim }
1252aca7a94dSNamhyung Kim 
125305e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1254aca7a94dSNamhyung Kim {
125505e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
1256aca7a94dSNamhyung Kim }
1257aca7a94dSNamhyung Kim 
12581e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
12591e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
12601e378ebdSTaeung Song {
12611e378ebdSTaeung Song 	return timer == NULL;
12621e378ebdSTaeung Song }
12631e378ebdSTaeung Song 
12641e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
12651e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
12661e378ebdSTaeung Song 				char *bf, size_t size)
1267aca7a94dSNamhyung Kim {
1268aca7a94dSNamhyung Kim 	char unit;
1269aca7a94dSNamhyung Kim 	int printed;
127005e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
127105e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
127284734b06SKan Liang 	int socket_id = hists->socket_filter;
127305e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
127405e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
1275717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
1276dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
1277717e263fSNamhyung Kim 	char buf[512];
1278717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
12799e207ddfSKan Liang 	char ref[30] = " show reference callgraph, ";
12809e207ddfSKan Liang 	bool enable_ref = false;
1281717e263fSNamhyung Kim 
1282f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
1283f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
1284f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
1285f2148330SNamhyung Kim 	}
1286f2148330SNamhyung Kim 
1287759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1288717e263fSNamhyung Kim 		struct perf_evsel *pos;
1289717e263fSNamhyung Kim 
1290717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
1291717e263fSNamhyung Kim 		ev_name = buf;
1292717e263fSNamhyung Kim 
1293717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
12944ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
12954ea062edSArnaldo Carvalho de Melo 
1296f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
12974ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
12984ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
1299f2148330SNamhyung Kim 			} else {
13004ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
13014ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
1302717e263fSNamhyung Kim 			}
1303717e263fSNamhyung Kim 		}
1304f2148330SNamhyung Kim 	}
1305aca7a94dSNamhyung Kim 
13069e207ddfSKan Liang 	if (symbol_conf.show_ref_callgraph &&
13079e207ddfSKan Liang 	    strstr(ev_name, "call-graph=no"))
13089e207ddfSKan Liang 		enable_ref = true;
1309aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
1310aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
13119e207ddfSKan Liang 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
13129e207ddfSKan Liang 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1313aca7a94dSNamhyung Kim 
1314aca7a94dSNamhyung Kim 
131505e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
1316aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
131705e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
1318aca7a94dSNamhyung Kim 	if (thread)
1319aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1320aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
1321b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
132238051234SAdrian Hunter 				    thread->tid);
1323aca7a94dSNamhyung Kim 	if (dso)
1324aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1325aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
132684734b06SKan Liang 	if (socket_id > -1)
132721394d94SKan Liang 		printed += scnprintf(bf + printed, size - printed,
132884734b06SKan Liang 				    ", Processor Socket: %d", socket_id);
13291e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
13301e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
13311e378ebdSTaeung Song 
13321e378ebdSTaeung Song 		if (top->zero)
13331e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
13341e378ebdSTaeung Song 	}
13351e378ebdSTaeung Song 
1336aca7a94dSNamhyung Kim 	return printed;
1337aca7a94dSNamhyung Kim }
1338aca7a94dSNamhyung Kim 
1339aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
1340aca7a94dSNamhyung Kim {
1341aca7a94dSNamhyung Kim 	int i;
1342aca7a94dSNamhyung Kim 
134304662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
134404662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
1345aca7a94dSNamhyung Kim }
1346aca7a94dSNamhyung Kim 
1347341487abSFeng Tang /*
1348341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
1349341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1350341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
1351341487abSFeng Tang  */
1352341487abSFeng Tang static bool is_input_name_malloced = false;
1353341487abSFeng Tang 
1354341487abSFeng Tang static int switch_data_file(void)
1355341487abSFeng Tang {
1356341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
1357341487abSFeng Tang 	DIR *pwd_dir;
1358341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
1359341487abSFeng Tang 	struct dirent *dent;
1360341487abSFeng Tang 
1361341487abSFeng Tang 	pwd = getenv("PWD");
1362341487abSFeng Tang 	if (!pwd)
1363341487abSFeng Tang 		return ret;
1364341487abSFeng Tang 
1365341487abSFeng Tang 	pwd_dir = opendir(pwd);
1366341487abSFeng Tang 	if (!pwd_dir)
1367341487abSFeng Tang 		return ret;
1368341487abSFeng Tang 
1369341487abSFeng Tang 	memset(options, 0, sizeof(options));
1370341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
1371341487abSFeng Tang 
1372341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
1373341487abSFeng Tang 		char path[PATH_MAX];
1374341487abSFeng Tang 		u64 magic;
1375341487abSFeng Tang 		char *name = dent->d_name;
1376341487abSFeng Tang 		FILE *file;
1377341487abSFeng Tang 
1378341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
1379341487abSFeng Tang 			continue;
1380341487abSFeng Tang 
1381341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1382341487abSFeng Tang 
1383341487abSFeng Tang 		file = fopen(path, "r");
1384341487abSFeng Tang 		if (!file)
1385341487abSFeng Tang 			continue;
1386341487abSFeng Tang 
1387341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
1388341487abSFeng Tang 			goto close_file_and_continue;
1389341487abSFeng Tang 
1390341487abSFeng Tang 		if (is_perf_magic(magic)) {
1391341487abSFeng Tang 			options[nr_options] = strdup(name);
1392341487abSFeng Tang 			if (!options[nr_options])
1393341487abSFeng Tang 				goto close_file_and_continue;
1394341487abSFeng Tang 
1395341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
1396341487abSFeng Tang 			if (!abs_path[nr_options]) {
139774cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
1398341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
1399341487abSFeng Tang 				fclose(file);
1400341487abSFeng Tang 				break;
1401341487abSFeng Tang 			}
1402341487abSFeng Tang 
1403341487abSFeng Tang 			nr_options++;
1404341487abSFeng Tang 		}
1405341487abSFeng Tang 
1406341487abSFeng Tang close_file_and_continue:
1407341487abSFeng Tang 		fclose(file);
1408341487abSFeng Tang 		if (nr_options >= 32) {
1409341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
1410341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
1411341487abSFeng Tang 			break;
1412341487abSFeng Tang 		}
1413341487abSFeng Tang 	}
1414341487abSFeng Tang 	closedir(pwd_dir);
1415341487abSFeng Tang 
1416341487abSFeng Tang 	if (nr_options) {
1417341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
1418341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
1419341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
1420341487abSFeng Tang 			if (tmp) {
1421341487abSFeng Tang 				if (is_input_name_malloced)
1422341487abSFeng Tang 					free((void *)input_name);
1423341487abSFeng Tang 				input_name = tmp;
1424341487abSFeng Tang 				is_input_name_malloced = true;
1425341487abSFeng Tang 				ret = 0;
1426341487abSFeng Tang 			} else
1427341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
1428341487abSFeng Tang 		}
1429341487abSFeng Tang 	}
1430341487abSFeng Tang 
1431341487abSFeng Tang 	free_popup_options(options, nr_options);
1432341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
1433341487abSFeng Tang 	return ret;
1434341487abSFeng Tang }
1435341487abSFeng Tang 
1436ea7cd592SNamhyung Kim struct popup_action {
1437ea7cd592SNamhyung Kim 	struct thread 		*thread;
1438ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
143984734b06SKan Liang 	int			socket;
1440ea7cd592SNamhyung Kim 
1441ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
1442ea7cd592SNamhyung Kim };
1443ea7cd592SNamhyung Kim 
1444bc7cad42SNamhyung Kim static int
1445ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
1446bc7cad42SNamhyung Kim {
1447bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
1448bc7cad42SNamhyung Kim 	struct annotation *notes;
1449bc7cad42SNamhyung Kim 	struct hist_entry *he;
1450bc7cad42SNamhyung Kim 	int err;
1451bc7cad42SNamhyung Kim 
1452eebd0bfcSArnaldo Carvalho de Melo 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
1453bc7cad42SNamhyung Kim 		return 0;
1454bc7cad42SNamhyung Kim 
1455ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
1456bc7cad42SNamhyung Kim 	if (!notes->src)
1457bc7cad42SNamhyung Kim 		return 0;
1458bc7cad42SNamhyung Kim 
1459bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
1460ea7cd592SNamhyung Kim 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1461bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
1462bc7cad42SNamhyung Kim 	/*
1463bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
1464bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
1465bc7cad42SNamhyung Kim 	 */
1466bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1467bc7cad42SNamhyung Kim 		return 1;
1468bc7cad42SNamhyung Kim 
1469bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1470bc7cad42SNamhyung Kim 	if (err)
1471bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
1472bc7cad42SNamhyung Kim 	return 0;
1473bc7cad42SNamhyung Kim }
1474bc7cad42SNamhyung Kim 
1475bc7cad42SNamhyung Kim static int
1476ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
1477ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
1478ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
1479bc7cad42SNamhyung Kim {
1480ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
1481ea7cd592SNamhyung Kim 		return 0;
1482ea7cd592SNamhyung Kim 
1483ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1484ea7cd592SNamhyung Kim 		return 0;
1485ea7cd592SNamhyung Kim 
1486ea7cd592SNamhyung Kim 	act->ms.map = map;
1487ea7cd592SNamhyung Kim 	act->ms.sym = sym;
1488ea7cd592SNamhyung Kim 	act->fn = do_annotate;
1489ea7cd592SNamhyung Kim 	return 1;
1490ea7cd592SNamhyung Kim }
1491ea7cd592SNamhyung Kim 
1492ea7cd592SNamhyung Kim static int
1493ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1494ea7cd592SNamhyung Kim {
1495ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
1496ea7cd592SNamhyung Kim 
1497bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
1498bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
1499bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
1500bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
1501bc7cad42SNamhyung Kim 		ui_helpline__pop();
1502bc7cad42SNamhyung Kim 	} else {
15037727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1504bc7cad42SNamhyung Kim 				   thread->comm_set ? thread__comm_str(thread) : "",
1505bc7cad42SNamhyung Kim 				   thread->tid);
1506bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
1507bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
1508bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
1509bc7cad42SNamhyung Kim 	}
1510bc7cad42SNamhyung Kim 
1511bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
1512bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
1513bc7cad42SNamhyung Kim 	return 0;
1514bc7cad42SNamhyung Kim }
1515bc7cad42SNamhyung Kim 
1516bc7cad42SNamhyung Kim static int
1517ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1518ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
1519bc7cad42SNamhyung Kim {
1520ea7cd592SNamhyung Kim 	if (thread == NULL)
1521ea7cd592SNamhyung Kim 		return 0;
1522ea7cd592SNamhyung Kim 
1523ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s(%d) thread",
1524ea7cd592SNamhyung Kim 		     browser->hists->thread_filter ? "out of" : "into",
1525ea7cd592SNamhyung Kim 		     thread->comm_set ? thread__comm_str(thread) : "",
1526ea7cd592SNamhyung Kim 		     thread->tid) < 0)
1527ea7cd592SNamhyung Kim 		return 0;
1528ea7cd592SNamhyung Kim 
1529ea7cd592SNamhyung Kim 	act->thread = thread;
1530ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
1531ea7cd592SNamhyung Kim 	return 1;
1532ea7cd592SNamhyung Kim }
1533ea7cd592SNamhyung Kim 
1534ea7cd592SNamhyung Kim static int
1535ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1536ea7cd592SNamhyung Kim {
1537045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
1538ea7cd592SNamhyung Kim 
1539bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
1540bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
1541bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
1542bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
1543bc7cad42SNamhyung Kim 		ui_helpline__pop();
1544bc7cad42SNamhyung Kim 	} else {
1545045b80ddSArnaldo Carvalho de Melo 		if (map == NULL)
1546bc7cad42SNamhyung Kim 			return 0;
15477727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1548045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1549045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
1550bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
1551bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
1552bc7cad42SNamhyung Kim 	}
1553bc7cad42SNamhyung Kim 
1554bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
1555bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
1556bc7cad42SNamhyung Kim 	return 0;
1557bc7cad42SNamhyung Kim }
1558bc7cad42SNamhyung Kim 
1559bc7cad42SNamhyung Kim static int
1560ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1561045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
1562bc7cad42SNamhyung Kim {
1563045b80ddSArnaldo Carvalho de Melo 	if (map == NULL)
1564ea7cd592SNamhyung Kim 		return 0;
1565ea7cd592SNamhyung Kim 
1566ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
1567ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
1568045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1569ea7cd592SNamhyung Kim 		return 0;
1570ea7cd592SNamhyung Kim 
1571045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
1572ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
1573ea7cd592SNamhyung Kim 	return 1;
1574ea7cd592SNamhyung Kim }
1575ea7cd592SNamhyung Kim 
1576ea7cd592SNamhyung Kim static int
1577ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
1578ea7cd592SNamhyung Kim 	      struct popup_action *act)
1579ea7cd592SNamhyung Kim {
1580ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
1581bc7cad42SNamhyung Kim 	return 0;
1582bc7cad42SNamhyung Kim }
1583bc7cad42SNamhyung Kim 
1584bc7cad42SNamhyung Kim static int
1585ea7cd592SNamhyung Kim add_map_opt(struct hist_browser *browser __maybe_unused,
1586ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
1587ea7cd592SNamhyung Kim {
1588ea7cd592SNamhyung Kim 	if (map == NULL)
1589ea7cd592SNamhyung Kim 		return 0;
1590ea7cd592SNamhyung Kim 
1591ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
1592ea7cd592SNamhyung Kim 		return 0;
1593ea7cd592SNamhyung Kim 
1594ea7cd592SNamhyung Kim 	act->ms.map = map;
1595ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
1596ea7cd592SNamhyung Kim 	return 1;
1597ea7cd592SNamhyung Kim }
1598ea7cd592SNamhyung Kim 
1599ea7cd592SNamhyung Kim static int
1600bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
1601ea7cd592SNamhyung Kim 	      struct popup_action *act)
1602bc7cad42SNamhyung Kim {
1603bc7cad42SNamhyung Kim 	char script_opt[64];
1604bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
1605bc7cad42SNamhyung Kim 
1606ea7cd592SNamhyung Kim 	if (act->thread) {
1607bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1608ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
1609ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
1610bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1611ea7cd592SNamhyung Kim 			  act->ms.sym->name);
1612bc7cad42SNamhyung Kim 	}
1613bc7cad42SNamhyung Kim 
1614bc7cad42SNamhyung Kim 	script_browse(script_opt);
1615bc7cad42SNamhyung Kim 	return 0;
1616bc7cad42SNamhyung Kim }
1617bc7cad42SNamhyung Kim 
1618bc7cad42SNamhyung Kim static int
1619ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
1620ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
1621ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
1622ea7cd592SNamhyung Kim {
1623ea7cd592SNamhyung Kim 	if (thread) {
1624ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1625ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
1626ea7cd592SNamhyung Kim 			return 0;
1627ea7cd592SNamhyung Kim 	} else if (sym) {
1628ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1629ea7cd592SNamhyung Kim 			     sym->name) < 0)
1630ea7cd592SNamhyung Kim 			return 0;
1631ea7cd592SNamhyung Kim 	} else {
1632ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
1633ea7cd592SNamhyung Kim 			return 0;
1634ea7cd592SNamhyung Kim 	}
1635ea7cd592SNamhyung Kim 
1636ea7cd592SNamhyung Kim 	act->thread = thread;
1637ea7cd592SNamhyung Kim 	act->ms.sym = sym;
1638ea7cd592SNamhyung Kim 	act->fn = do_run_script;
1639ea7cd592SNamhyung Kim 	return 1;
1640ea7cd592SNamhyung Kim }
1641ea7cd592SNamhyung Kim 
1642ea7cd592SNamhyung Kim static int
1643ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
1644ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
1645bc7cad42SNamhyung Kim {
1646bc7cad42SNamhyung Kim 	if (switch_data_file()) {
1647bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
1648bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
1649ea7cd592SNamhyung Kim 		return 0;
1650bc7cad42SNamhyung Kim 	}
1651bc7cad42SNamhyung Kim 
1652bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
1653bc7cad42SNamhyung Kim }
1654bc7cad42SNamhyung Kim 
1655ea7cd592SNamhyung Kim static int
1656ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
1657ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
1658ea7cd592SNamhyung Kim {
1659ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
1660ea7cd592SNamhyung Kim 		return 0;
1661ea7cd592SNamhyung Kim 
1662ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1663ea7cd592SNamhyung Kim 		return 0;
1664ea7cd592SNamhyung Kim 
1665ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
1666ea7cd592SNamhyung Kim 	return 1;
1667ea7cd592SNamhyung Kim }
1668ea7cd592SNamhyung Kim 
1669ea7cd592SNamhyung Kim static int
1670ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
1671ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
1672ea7cd592SNamhyung Kim {
1673ea7cd592SNamhyung Kim 	return 0;
1674ea7cd592SNamhyung Kim }
1675ea7cd592SNamhyung Kim 
1676ea7cd592SNamhyung Kim static int
1677ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
1678ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
1679ea7cd592SNamhyung Kim {
1680ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
1681ea7cd592SNamhyung Kim 		return 0;
1682ea7cd592SNamhyung Kim 
1683ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
1684ea7cd592SNamhyung Kim 	return 1;
1685ea7cd592SNamhyung Kim }
1686ea7cd592SNamhyung Kim 
168784734b06SKan Liang static int
168884734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
168984734b06SKan Liang {
169084734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
169184734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
169284734b06SKan Liang 		browser->hists->socket_filter = -1;
169384734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
169484734b06SKan Liang 	} else {
169584734b06SKan Liang 		browser->hists->socket_filter = act->socket;
169684734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
169784734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
169884734b06SKan Liang 	}
169984734b06SKan Liang 
170084734b06SKan Liang 	hists__filter_by_socket(browser->hists);
170184734b06SKan Liang 	hist_browser__reset(browser);
170284734b06SKan Liang 	return 0;
170384734b06SKan Liang }
170484734b06SKan Liang 
170584734b06SKan Liang static int
170684734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
170784734b06SKan Liang 	       char **optstr, int socket_id)
170884734b06SKan Liang {
170984734b06SKan Liang 	if (socket_id < 0)
171084734b06SKan Liang 		return 0;
171184734b06SKan Liang 
171284734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
171384734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
171484734b06SKan Liang 		     socket_id) < 0)
171584734b06SKan Liang 		return 0;
171684734b06SKan Liang 
171784734b06SKan Liang 	act->socket = socket_id;
171884734b06SKan Liang 	act->fn = do_zoom_socket;
171984734b06SKan Liang 	return 1;
172084734b06SKan Liang }
172184734b06SKan Liang 
1722112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
1723064f1981SNamhyung Kim {
1724064f1981SNamhyung Kim 	u64 nr_entries = 0;
1725064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
1726064f1981SNamhyung Kim 
1727268397cbSNamhyung Kim 	if (hb->min_pcnt == 0) {
1728268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1729268397cbSNamhyung Kim 		return;
1730268397cbSNamhyung Kim 	}
1731268397cbSNamhyung Kim 
173214135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1733064f1981SNamhyung Kim 		nr_entries++;
1734c481f930SNamhyung Kim 		nd = rb_next(nd);
1735064f1981SNamhyung Kim 	}
1736064f1981SNamhyung Kim 
1737112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
1738064f1981SNamhyung Kim }
1739341487abSFeng Tang 
1740aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1741dd00d486SJiri Olsa 				    const char *helpline,
1742aca7a94dSNamhyung Kim 				    bool left_exits,
174368d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
1744064f1981SNamhyung Kim 				    float min_pcnt,
1745ce80d3beSKan Liang 				    struct perf_env *env)
1746aca7a94dSNamhyung Kim {
17474ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
1748b1a9ceefSNamhyung Kim 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1749aca7a94dSNamhyung Kim 	struct branch_info *bi;
1750f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
1751f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
1752ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
1753aca7a94dSNamhyung Kim 	int nr_options = 0;
1754aca7a94dSNamhyung Kim 	int key = -1;
1755aca7a94dSNamhyung Kim 	char buf[64];
17569783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
175759dc9f25SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1758aca7a94dSNamhyung Kim 
1759e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
1760e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
1761e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
1762e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
1763e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1764e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
1765e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
1766e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
17677727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
17687727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
1769e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
1770e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
1771e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
1772e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
1773105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
1774025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
177531eb4360SNamhyung Kim 	"m             Display context menu\n"				\
177684734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
1777e8e684a5SNamhyung Kim 
1778e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
1779e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
17806dd60135SNamhyung Kim 	"i             Show header information\n"
1781e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1782e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
1783e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
1784e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1785e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
1786e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1787e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1788e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
1789e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
1790e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
179142337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
1792fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
1793e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
1794e8e684a5SNamhyung Kim 
1795aca7a94dSNamhyung Kim 	if (browser == NULL)
1796aca7a94dSNamhyung Kim 		return -1;
1797aca7a94dSNamhyung Kim 
1798ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
1799ed426915SNamhyung Kim 	SLang_reset_tty();
1800ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
1801ed426915SNamhyung Kim 
1802064f1981SNamhyung Kim 	if (min_pcnt) {
1803064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
1804112f761fSNamhyung Kim 		hist_browser__update_nr_entries(browser);
1805064f1981SNamhyung Kim 	}
1806064f1981SNamhyung Kim 
180784734b06SKan Liang 	browser->pstack = pstack__new(3);
180801f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
1809aca7a94dSNamhyung Kim 		goto out;
1810aca7a94dSNamhyung Kim 
1811aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
1812aca7a94dSNamhyung Kim 
1813aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
1814ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
1815aca7a94dSNamhyung Kim 
1816c6c3c02dSArnaldo Carvalho de Melo 	perf_hpp__for_each_format(fmt) {
181759dc9f25SNamhyung Kim 		perf_hpp__reset_width(fmt, hists);
1818c6c3c02dSArnaldo Carvalho de Melo 		/*
1819c6c3c02dSArnaldo Carvalho de Melo 		 * This is done just once, and activates the horizontal scrolling
1820c6c3c02dSArnaldo Carvalho de Melo 		 * code in the ui_browser code, it would be better to have a the
1821c6c3c02dSArnaldo Carvalho de Melo 		 * counter in the perf_hpp code, but I couldn't find doing it here
1822c6c3c02dSArnaldo Carvalho de Melo 		 * works, FIXME by setting this in hist_browser__new, for now, be
1823c6c3c02dSArnaldo Carvalho de Melo 		 * clever 8-)
1824c6c3c02dSArnaldo Carvalho de Melo 		 */
1825c6c3c02dSArnaldo Carvalho de Melo 		++browser->b.columns;
1826c6c3c02dSArnaldo Carvalho de Melo 	}
182759dc9f25SNamhyung Kim 
18285b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
18295b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
18305b591669SNamhyung Kim 
1831aca7a94dSNamhyung Kim 	while (1) {
1832f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
1833045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
1834ea7cd592SNamhyung Kim 		int choice = 0;
183584734b06SKan Liang 		int socked_id = -1;
1836aca7a94dSNamhyung Kim 
1837aca7a94dSNamhyung Kim 		nr_options = 0;
1838aca7a94dSNamhyung Kim 
18395f00b0f4SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline);
1840aca7a94dSNamhyung Kim 
1841aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
1842aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
1843045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
184484734b06SKan Liang 			socked_id = browser->he_selection->socket;
1845aca7a94dSNamhyung Kim 		}
1846aca7a94dSNamhyung Kim 		switch (key) {
1847aca7a94dSNamhyung Kim 		case K_TAB:
1848aca7a94dSNamhyung Kim 		case K_UNTAB:
1849aca7a94dSNamhyung Kim 			if (nr_events == 1)
1850aca7a94dSNamhyung Kim 				continue;
1851aca7a94dSNamhyung Kim 			/*
1852aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
1853aca7a94dSNamhyung Kim 			 * go to the next or previous
1854aca7a94dSNamhyung Kim 			 */
1855aca7a94dSNamhyung Kim 			goto out_free_stack;
1856aca7a94dSNamhyung Kim 		case 'a':
18579c796ec8SArnaldo Carvalho de Melo 			if (!sort__has_sym) {
1858aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
1859aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
1860aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
1861aca7a94dSNamhyung Kim 				continue;
1862aca7a94dSNamhyung Kim 			}
1863aca7a94dSNamhyung Kim 
1864aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
1865aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
1866aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
1867aca7a94dSNamhyung Kim 				continue;
1868bc7cad42SNamhyung Kim 
1869ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
1870ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
1871ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
1872bc7cad42SNamhyung Kim 			continue;
1873aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
1874aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
1875aff3f3f6SArnaldo Carvalho de Melo 			continue;
1876aca7a94dSNamhyung Kim 		case 'd':
1877fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
1878ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
1879bc7cad42SNamhyung Kim 			continue;
1880a7cb8863SArnaldo Carvalho de Melo 		case 'V':
1881a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
1882a7cb8863SArnaldo Carvalho de Melo 			continue;
1883aca7a94dSNamhyung Kim 		case 't':
1884ea7cd592SNamhyung Kim 			actions->thread = thread;
1885ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
1886bc7cad42SNamhyung Kim 			continue;
188784734b06SKan Liang 		case 'S':
188884734b06SKan Liang 			actions->socket = socked_id;
188984734b06SKan Liang 			do_zoom_socket(browser, actions);
189084734b06SKan Liang 			continue;
18915a5626b1SArnaldo Carvalho de Melo 		case '/':
1892aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
18934aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
18944aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
1895aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
1896aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
189705e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
189805e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
1899aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
1900aca7a94dSNamhyung Kim 			}
1901aca7a94dSNamhyung Kim 			continue;
1902cdbab7c2SFeng Tang 		case 'r':
1903ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
1904ea7cd592SNamhyung Kim 				actions->thread = NULL;
1905ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
1906ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
1907ea7cd592SNamhyung Kim 			}
1908c77d8d70SFeng Tang 			continue;
1909341487abSFeng Tang 		case 's':
1910bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
1911ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
1912bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
1913bc7cad42SNamhyung Kim 					goto out_free_stack;
1914bc7cad42SNamhyung Kim 			}
1915341487abSFeng Tang 			continue;
19166dd60135SNamhyung Kim 		case 'i':
19176dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
19186dd60135SNamhyung Kim 			if (env->arch)
19196dd60135SNamhyung Kim 				tui__header_window(env);
19206dd60135SNamhyung Kim 			continue;
1921105eb30fSNamhyung Kim 		case 'F':
1922105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
1923105eb30fSNamhyung Kim 			continue;
192442337a22SNamhyung Kim 		case 'z':
192542337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
192642337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
192742337a22SNamhyung Kim 
192842337a22SNamhyung Kim 				top->zero = !top->zero;
192942337a22SNamhyung Kim 			}
193042337a22SNamhyung Kim 			continue;
1931aca7a94dSNamhyung Kim 		case K_F1:
1932aca7a94dSNamhyung Kim 		case 'h':
1933aca7a94dSNamhyung Kim 		case '?':
1934aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
1935e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
1936aca7a94dSNamhyung Kim 			continue;
1937aca7a94dSNamhyung Kim 		case K_ENTER:
1938aca7a94dSNamhyung Kim 		case K_RIGHT:
193931eb4360SNamhyung Kim 		case 'm':
1940aca7a94dSNamhyung Kim 			/* menu */
1941aca7a94dSNamhyung Kim 			break;
194263ab1749SArnaldo Carvalho de Melo 		case K_ESC:
1943aca7a94dSNamhyung Kim 		case K_LEFT: {
1944aca7a94dSNamhyung Kim 			const void *top;
1945aca7a94dSNamhyung Kim 
194601f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
1947aca7a94dSNamhyung Kim 				/*
1948aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
1949aca7a94dSNamhyung Kim 				 */
1950aca7a94dSNamhyung Kim 				if (left_exits)
1951aca7a94dSNamhyung Kim 					goto out_free_stack;
195263ab1749SArnaldo Carvalho de Melo 
195363ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
195463ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
195563ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
195663ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
195763ab1749SArnaldo Carvalho de Melo 
1958aca7a94dSNamhyung Kim 				continue;
1959aca7a94dSNamhyung Kim 			}
19606422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
1961bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
19626422184bSNamhyung Kim 				/*
19636422184bSNamhyung Kim 				 * No need to set actions->dso here since
19646422184bSNamhyung Kim 				 * it's just to remove the current filter.
19656422184bSNamhyung Kim 				 * Ditto for thread below.
19666422184bSNamhyung Kim 				 */
19676422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
196884734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
19696422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
197084734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
197184734b06SKan Liang 				do_zoom_socket(browser, actions);
197284734b06SKan Liang 			}
1973aca7a94dSNamhyung Kim 			continue;
1974aca7a94dSNamhyung Kim 		}
1975aca7a94dSNamhyung Kim 		case 'q':
1976aca7a94dSNamhyung Kim 		case CTRL('c'):
1977516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
1978fbb7997eSArnaldo Carvalho de Melo 		case 'f':
197913d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
198013d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
198113d1e536SNamhyung Kim 
198213d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
198313d1e536SNamhyung Kim 				/*
198413d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
198513d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
198613d1e536SNamhyung Kim 				 */
198713d1e536SNamhyung Kim 				if (top->evlist->enabled) {
198813d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
198913d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
199013d1e536SNamhyung Kim 				} else {
199113d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
199213d1e536SNamhyung Kim 					hbt->refresh = 0;
199313d1e536SNamhyung Kim 				}
199413d1e536SNamhyung Kim 				continue;
199513d1e536SNamhyung Kim 			}
19963e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
1997aca7a94dSNamhyung Kim 		default:
19983e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
1999aca7a94dSNamhyung Kim 			continue;
2000aca7a94dSNamhyung Kim 		}
2001aca7a94dSNamhyung Kim 
20029c796ec8SArnaldo Carvalho de Melo 		if (!sort__has_sym)
2003aca7a94dSNamhyung Kim 			goto add_exit_option;
2004aca7a94dSNamhyung Kim 
20050ba332f7SArnaldo Carvalho de Melo 		if (browser->selection == NULL)
20060ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
20070ba332f7SArnaldo Carvalho de Melo 
200855369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
2009aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
20100ba332f7SArnaldo Carvalho de Melo 
20110ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
20120ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
20130ba332f7SArnaldo Carvalho de Melo 
2014ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2015ea7cd592SNamhyung Kim 						       &actions[nr_options],
2016ea7cd592SNamhyung Kim 						       &options[nr_options],
2017ea7cd592SNamhyung Kim 						       bi->from.map,
2018ea7cd592SNamhyung Kim 						       bi->from.sym);
2019ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
2020ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
2021ea7cd592SNamhyung Kim 							&actions[nr_options],
2022ea7cd592SNamhyung Kim 							&options[nr_options],
2023ea7cd592SNamhyung Kim 							bi->to.map,
2024ea7cd592SNamhyung Kim 							bi->to.sym);
2025aca7a94dSNamhyung Kim 		} else {
2026ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2027ea7cd592SNamhyung Kim 						       &actions[nr_options],
2028ea7cd592SNamhyung Kim 						       &options[nr_options],
2029ea7cd592SNamhyung Kim 						       browser->selection->map,
2030ea7cd592SNamhyung Kim 						       browser->selection->sym);
2031446fb96cSArnaldo Carvalho de Melo 		}
20320ba332f7SArnaldo Carvalho de Melo skip_annotation:
2033ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
2034ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
2035ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
2036045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
2037ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
2038ea7cd592SNamhyung Kim 					  &options[nr_options],
2039bd315aabSWang Nan 					  browser->selection ?
2040bd315aabSWang Nan 						browser->selection->map : NULL);
204184734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
204284734b06SKan Liang 					     &options[nr_options],
204384734b06SKan Liang 					     socked_id);
2044cdbab7c2SFeng Tang 		/* perf script support */
2045cdbab7c2SFeng Tang 		if (browser->he_selection) {
2046ea7cd592SNamhyung Kim 			nr_options += add_script_opt(browser,
2047ea7cd592SNamhyung Kim 						     &actions[nr_options],
2048ea7cd592SNamhyung Kim 						     &options[nr_options],
2049ea7cd592SNamhyung Kim 						     thread, NULL);
2050bd315aabSWang Nan 			/*
2051bd315aabSWang Nan 			 * Note that browser->selection != NULL
2052bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
2053bd315aabSWang Nan 			 * so we don't need to check browser->selection
2054bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
2055bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
2056bd315aabSWang Nan 			 *
2057bd315aabSWang Nan 			 * See hist_browser__show_entry.
2058bd315aabSWang Nan 			 */
2059ea7cd592SNamhyung Kim 			nr_options += add_script_opt(browser,
2060ea7cd592SNamhyung Kim 						     &actions[nr_options],
2061ea7cd592SNamhyung Kim 						     &options[nr_options],
2062ea7cd592SNamhyung Kim 						     NULL, browser->selection->sym);
2063cdbab7c2SFeng Tang 		}
2064ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
2065ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
2066ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
2067ea7cd592SNamhyung Kim 					     &options[nr_options]);
2068aca7a94dSNamhyung Kim add_exit_option:
2069ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
2070ea7cd592SNamhyung Kim 					   &options[nr_options]);
2071aca7a94dSNamhyung Kim 
2072ea7cd592SNamhyung Kim 		do {
2073ea7cd592SNamhyung Kim 			struct popup_action *act;
2074ea7cd592SNamhyung Kim 
2075ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
2076ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
2077aca7a94dSNamhyung Kim 				break;
2078aca7a94dSNamhyung Kim 
2079ea7cd592SNamhyung Kim 			act = &actions[choice];
2080ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
2081ea7cd592SNamhyung Kim 		} while (key == 1);
2082aca7a94dSNamhyung Kim 
2083bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
2084341487abSFeng Tang 			break;
2085341487abSFeng Tang 	}
2086aca7a94dSNamhyung Kim out_free_stack:
208701f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
2088aca7a94dSNamhyung Kim out:
2089aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
2090f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
2091aca7a94dSNamhyung Kim 	return key;
2092aca7a94dSNamhyung Kim }
2093aca7a94dSNamhyung Kim 
2094aca7a94dSNamhyung Kim struct perf_evsel_menu {
2095aca7a94dSNamhyung Kim 	struct ui_browser b;
2096aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
2097aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
2098064f1981SNamhyung Kim 	float min_pcnt;
2099ce80d3beSKan Liang 	struct perf_env *env;
2100aca7a94dSNamhyung Kim };
2101aca7a94dSNamhyung Kim 
2102aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
2103aca7a94dSNamhyung Kim 				   void *entry, int row)
2104aca7a94dSNamhyung Kim {
2105aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
2106aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
2107aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
21084ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2109aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
21104ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
21117289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
2112aca7a94dSNamhyung Kim 	char bf[256], unit;
2113aca7a94dSNamhyung Kim 	const char *warn = " ";
2114aca7a94dSNamhyung Kim 	size_t printed;
2115aca7a94dSNamhyung Kim 
2116aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2117aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
2118aca7a94dSNamhyung Kim 
2119759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
2120717e263fSNamhyung Kim 		struct perf_evsel *pos;
2121717e263fSNamhyung Kim 
2122717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
2123717e263fSNamhyung Kim 
2124717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
21254ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
21264ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2127717e263fSNamhyung Kim 		}
2128717e263fSNamhyung Kim 	}
2129717e263fSNamhyung Kim 
2130aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
2131aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2132aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
2133517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
2134aca7a94dSNamhyung Kim 
21354ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2136aca7a94dSNamhyung Kim 	if (nr_events != 0) {
2137aca7a94dSNamhyung Kim 		menu->lost_events = true;
2138aca7a94dSNamhyung Kim 		if (!current_entry)
2139aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
2140aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
2141aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2142aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
2143aca7a94dSNamhyung Kim 		warn = bf;
2144aca7a94dSNamhyung Kim 	}
2145aca7a94dSNamhyung Kim 
214626270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
2147aca7a94dSNamhyung Kim 
2148aca7a94dSNamhyung Kim 	if (current_entry)
2149aca7a94dSNamhyung Kim 		menu->selection = evsel;
2150aca7a94dSNamhyung Kim }
2151aca7a94dSNamhyung Kim 
2152aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2153aca7a94dSNamhyung Kim 				int nr_events, const char *help,
21549783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
2155aca7a94dSNamhyung Kim {
2156aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
2157aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
2158dd00d486SJiri Olsa 	const char *title = "Available samples";
21599783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2160aca7a94dSNamhyung Kim 	int key;
2161aca7a94dSNamhyung Kim 
2162aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
2163aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
2164aca7a94dSNamhyung Kim 		return -1;
2165aca7a94dSNamhyung Kim 
2166aca7a94dSNamhyung Kim 	while (1) {
2167aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
2168aca7a94dSNamhyung Kim 
2169aca7a94dSNamhyung Kim 		switch (key) {
2170aca7a94dSNamhyung Kim 		case K_TIMER:
21719783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
2172aca7a94dSNamhyung Kim 
2173aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
2174aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
2175aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
2176aca7a94dSNamhyung Kim 			}
2177aca7a94dSNamhyung Kim 			continue;
2178aca7a94dSNamhyung Kim 		case K_RIGHT:
2179aca7a94dSNamhyung Kim 		case K_ENTER:
2180aca7a94dSNamhyung Kim 			if (!menu->selection)
2181aca7a94dSNamhyung Kim 				continue;
2182aca7a94dSNamhyung Kim 			pos = menu->selection;
2183aca7a94dSNamhyung Kim browse_hists:
2184aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
2185aca7a94dSNamhyung Kim 			/*
2186aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
2187aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
2188aca7a94dSNamhyung Kim 			 */
21899783adf7SNamhyung Kim 			if (hbt)
21909783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
2191aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
2192dd00d486SJiri Olsa 						       true, hbt,
2193064f1981SNamhyung Kim 						       menu->min_pcnt,
219468d80758SNamhyung Kim 						       menu->env);
2195aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
2196aca7a94dSNamhyung Kim 			switch (key) {
2197aca7a94dSNamhyung Kim 			case K_TAB:
2198aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
21999a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
2200aca7a94dSNamhyung Kim 				else
22019a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
2202aca7a94dSNamhyung Kim 				goto browse_hists;
2203aca7a94dSNamhyung Kim 			case K_UNTAB:
2204aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
22059a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
2206aca7a94dSNamhyung Kim 				else
2207d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
2208aca7a94dSNamhyung Kim 				goto browse_hists;
2209341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
2210aca7a94dSNamhyung Kim 			case 'q':
2211aca7a94dSNamhyung Kim 			case CTRL('c'):
2212aca7a94dSNamhyung Kim 				goto out;
221363ab1749SArnaldo Carvalho de Melo 			case K_ESC:
2214aca7a94dSNamhyung Kim 			default:
2215aca7a94dSNamhyung Kim 				continue;
2216aca7a94dSNamhyung Kim 			}
2217aca7a94dSNamhyung Kim 		case K_LEFT:
2218aca7a94dSNamhyung Kim 			continue;
2219aca7a94dSNamhyung Kim 		case K_ESC:
2220aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
2221aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
2222aca7a94dSNamhyung Kim 				continue;
2223aca7a94dSNamhyung Kim 			/* Fall thru */
2224aca7a94dSNamhyung Kim 		case 'q':
2225aca7a94dSNamhyung Kim 		case CTRL('c'):
2226aca7a94dSNamhyung Kim 			goto out;
2227aca7a94dSNamhyung Kim 		default:
2228aca7a94dSNamhyung Kim 			continue;
2229aca7a94dSNamhyung Kim 		}
2230aca7a94dSNamhyung Kim 	}
2231aca7a94dSNamhyung Kim 
2232aca7a94dSNamhyung Kim out:
2233aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
2234aca7a94dSNamhyung Kim 	return key;
2235aca7a94dSNamhyung Kim }
2236aca7a94dSNamhyung Kim 
2237316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2238fc24d7c2SNamhyung Kim 				 void *entry)
2239fc24d7c2SNamhyung Kim {
2240fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2241fc24d7c2SNamhyung Kim 
2242fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2243fc24d7c2SNamhyung Kim 		return true;
2244fc24d7c2SNamhyung Kim 
2245fc24d7c2SNamhyung Kim 	return false;
2246fc24d7c2SNamhyung Kim }
2247fc24d7c2SNamhyung Kim 
2248aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2249fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
225068d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
2251064f1981SNamhyung Kim 					   float min_pcnt,
2252ce80d3beSKan Liang 					   struct perf_env *env)
2253aca7a94dSNamhyung Kim {
2254aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
2255aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
2256aca7a94dSNamhyung Kim 		.b = {
2257aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
2258aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
2259aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
2260aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
2261fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
2262fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
2263aca7a94dSNamhyung Kim 			.priv	    = evlist,
2264aca7a94dSNamhyung Kim 		},
2265064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
226668d80758SNamhyung Kim 		.env = env,
2267aca7a94dSNamhyung Kim 	};
2268aca7a94dSNamhyung Kim 
2269aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
2270aca7a94dSNamhyung Kim 
22710050f7aaSArnaldo Carvalho de Melo 	evlist__for_each(evlist, pos) {
22727289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
2273aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
2274aca7a94dSNamhyung Kim 
2275aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
2276aca7a94dSNamhyung Kim 			menu.b.width = line_len;
2277aca7a94dSNamhyung Kim 	}
2278aca7a94dSNamhyung Kim 
2279fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2280aca7a94dSNamhyung Kim }
2281aca7a94dSNamhyung Kim 
2282aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
228368d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
2284064f1981SNamhyung Kim 				  float min_pcnt,
2285ce80d3beSKan Liang 				  struct perf_env *env)
2286aca7a94dSNamhyung Kim {
2287fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
2288fc24d7c2SNamhyung Kim 
2289fc24d7c2SNamhyung Kim single_entry:
2290fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
22919a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
2292fc24d7c2SNamhyung Kim 
2293fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
2294dd00d486SJiri Olsa 						false, hbt, min_pcnt,
2295064f1981SNamhyung Kim 						env);
2296aca7a94dSNamhyung Kim 	}
2297aca7a94dSNamhyung Kim 
2298fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
2299fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
2300fc24d7c2SNamhyung Kim 
2301fc24d7c2SNamhyung Kim 		nr_entries = 0;
23020050f7aaSArnaldo Carvalho de Melo 		evlist__for_each(evlist, pos) {
2303fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
2304fc24d7c2SNamhyung Kim 				nr_entries++;
23050050f7aaSArnaldo Carvalho de Melo 		}
2306fc24d7c2SNamhyung Kim 
2307fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
2308fc24d7c2SNamhyung Kim 			goto single_entry;
2309fc24d7c2SNamhyung Kim 	}
2310fc24d7c2SNamhyung Kim 
2311fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2312064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
2313aca7a94dSNamhyung Kim }
2314