xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 361459f1)
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 
1814b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
1824b3a3212SNamhyung Kim {
1834b3a3212SNamhyung Kim 	struct callchain_list *chain;
1844b3a3212SNamhyung Kim 	char folded_sign = 0;
1854b3a3212SNamhyung Kim 	int n = 0;
1864b3a3212SNamhyung Kim 
1874b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
1884b3a3212SNamhyung Kim 		if (!folded_sign) {
1894b3a3212SNamhyung Kim 			/* only check first chain list entry */
1904b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1914b3a3212SNamhyung Kim 			if (folded_sign == '+')
1924b3a3212SNamhyung Kim 				return 1;
1934b3a3212SNamhyung Kim 		}
1944b3a3212SNamhyung Kim 		n++;
1954b3a3212SNamhyung Kim 	}
1964b3a3212SNamhyung Kim 
1974b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
1984b3a3212SNamhyung Kim 		if (!folded_sign) {
1994b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
2004b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2014b3a3212SNamhyung Kim 			if (folded_sign == '+')
2024b3a3212SNamhyung Kim 				return 1;
2034b3a3212SNamhyung Kim 		}
2044b3a3212SNamhyung Kim 		n++;
2054b3a3212SNamhyung Kim 	}
2064b3a3212SNamhyung Kim 
2074b3a3212SNamhyung Kim 	return n;
2084b3a3212SNamhyung Kim }
2094b3a3212SNamhyung Kim 
2108c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
2118c430a34SNamhyung Kim {
2128c430a34SNamhyung Kim 	return 1;
2138c430a34SNamhyung Kim }
2148c430a34SNamhyung Kim 
215aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
216aca7a94dSNamhyung Kim {
217aca7a94dSNamhyung Kim 	struct callchain_list *chain;
218aca7a94dSNamhyung Kim 	bool unfolded = false;
219aca7a94dSNamhyung Kim 	int n = 0;
220aca7a94dSNamhyung Kim 
2214b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2224b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2238c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2248c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2254b3a3212SNamhyung Kim 
226aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
227aca7a94dSNamhyung Kim 		++n;
2283698dab1SNamhyung Kim 		unfolded = chain->unfolded;
229aca7a94dSNamhyung Kim 	}
230aca7a94dSNamhyung Kim 
231aca7a94dSNamhyung Kim 	if (unfolded)
232aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
233aca7a94dSNamhyung Kim 
234aca7a94dSNamhyung Kim 	return n;
235aca7a94dSNamhyung Kim }
236aca7a94dSNamhyung Kim 
237aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
238aca7a94dSNamhyung Kim {
239aca7a94dSNamhyung Kim 	struct rb_node *nd;
240aca7a94dSNamhyung Kim 	int n = 0;
241aca7a94dSNamhyung Kim 
242aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
243aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
244aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
245aca7a94dSNamhyung Kim 	}
246aca7a94dSNamhyung Kim 
247aca7a94dSNamhyung Kim 	return n;
248aca7a94dSNamhyung Kim }
249aca7a94dSNamhyung Kim 
2503698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
251aca7a94dSNamhyung Kim {
2523698dab1SNamhyung Kim 	if (!he)
253aca7a94dSNamhyung Kim 		return false;
254aca7a94dSNamhyung Kim 
2553698dab1SNamhyung Kim 	if (!he->has_children)
256aca7a94dSNamhyung Kim 		return false;
257aca7a94dSNamhyung Kim 
2583698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
2593698dab1SNamhyung Kim 	return true;
2603698dab1SNamhyung Kim }
2613698dab1SNamhyung Kim 
2623698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
2633698dab1SNamhyung Kim {
2643698dab1SNamhyung Kim 	if (!cl)
2653698dab1SNamhyung Kim 		return false;
2663698dab1SNamhyung Kim 
2673698dab1SNamhyung Kim 	if (!cl->has_children)
2683698dab1SNamhyung Kim 		return false;
2693698dab1SNamhyung Kim 
2703698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
271aca7a94dSNamhyung Kim 	return true;
272aca7a94dSNamhyung Kim }
273aca7a94dSNamhyung Kim 
27405e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
275aca7a94dSNamhyung Kim {
27605e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
277aca7a94dSNamhyung Kim 
27805e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
279aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
280aca7a94dSNamhyung Kim 		struct callchain_list *chain;
281aca7a94dSNamhyung Kim 		bool first = true;
282aca7a94dSNamhyung Kim 
283aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
284aca7a94dSNamhyung Kim 			if (first) {
285aca7a94dSNamhyung Kim 				first = false;
2863698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
287aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
288aca7a94dSNamhyung Kim 			} else
2893698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
290aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
291aca7a94dSNamhyung Kim 		}
292aca7a94dSNamhyung Kim 
293aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
294aca7a94dSNamhyung Kim 	}
295aca7a94dSNamhyung Kim }
296aca7a94dSNamhyung Kim 
297a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
298a7444af6SNamhyung Kim 					       bool has_sibling)
299aca7a94dSNamhyung Kim {
300aca7a94dSNamhyung Kim 	struct callchain_list *chain;
301aca7a94dSNamhyung Kim 
302a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3033698dab1SNamhyung Kim 	chain->has_children = has_sibling;
304a7444af6SNamhyung Kim 
3054b3a3212SNamhyung Kim 	if (node->val.next != node->val.prev) {
30682162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3073698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
30882162b5aSNamhyung Kim 	}
309aca7a94dSNamhyung Kim 
31005e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
311aca7a94dSNamhyung Kim }
312aca7a94dSNamhyung Kim 
31305e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
314aca7a94dSNamhyung Kim {
315a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
316a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
317aca7a94dSNamhyung Kim 
31805e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
319aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
320a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3218c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3228c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3234b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
324aca7a94dSNamhyung Kim 	}
325aca7a94dSNamhyung Kim }
326aca7a94dSNamhyung Kim 
32705e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
328aca7a94dSNamhyung Kim {
32905e8b080SArnaldo Carvalho de Melo 	if (!he->init_have_children) {
3303698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
33105e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
33205e8b080SArnaldo Carvalho de Melo 		he->init_have_children = true;
333aca7a94dSNamhyung Kim 	}
334aca7a94dSNamhyung Kim }
335aca7a94dSNamhyung Kim 
33605e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
337aca7a94dSNamhyung Kim {
33805e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
3393698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
3403698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
3413698dab1SNamhyung Kim 	bool has_children;
342aca7a94dSNamhyung Kim 
3434938cf0cSWang Nan 	if (!he || !ms)
3444938cf0cSWang Nan 		return false;
3454938cf0cSWang Nan 
3463698dab1SNamhyung Kim 	if (ms == &he->ms)
3473698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
3483698dab1SNamhyung Kim 	else
3493698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
3503698dab1SNamhyung Kim 
3513698dab1SNamhyung Kim 	if (has_children) {
352aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
353c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
354c3b78952SNamhyung Kim 		browser->nr_callchain_rows -= he->nr_rows;
355aca7a94dSNamhyung Kim 
3563698dab1SNamhyung Kim 		if (he->unfolded)
357aca7a94dSNamhyung Kim 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
358aca7a94dSNamhyung Kim 		else
359aca7a94dSNamhyung Kim 			he->nr_rows = 0;
360c3b78952SNamhyung Kim 
361c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
362c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
363aca7a94dSNamhyung Kim 
364aca7a94dSNamhyung Kim 		return true;
365aca7a94dSNamhyung Kim 	}
366aca7a94dSNamhyung Kim 
367aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
368aca7a94dSNamhyung Kim 	return false;
369aca7a94dSNamhyung Kim }
370aca7a94dSNamhyung Kim 
37105e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
372aca7a94dSNamhyung Kim {
373aca7a94dSNamhyung Kim 	int n = 0;
374aca7a94dSNamhyung Kim 	struct rb_node *nd;
375aca7a94dSNamhyung Kim 
37605e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
377aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
378aca7a94dSNamhyung Kim 		struct callchain_list *chain;
379aca7a94dSNamhyung Kim 		bool has_children = false;
380aca7a94dSNamhyung Kim 
381aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
382aca7a94dSNamhyung Kim 			++n;
3833698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
3843698dab1SNamhyung Kim 			has_children = chain->has_children;
385aca7a94dSNamhyung Kim 		}
386aca7a94dSNamhyung Kim 
387aca7a94dSNamhyung Kim 		if (has_children)
388aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
389aca7a94dSNamhyung Kim 	}
390aca7a94dSNamhyung Kim 
391aca7a94dSNamhyung Kim 	return n;
392aca7a94dSNamhyung Kim }
393aca7a94dSNamhyung Kim 
394aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
395aca7a94dSNamhyung Kim {
396aca7a94dSNamhyung Kim 	struct callchain_list *chain;
397aca7a94dSNamhyung Kim 	bool has_children = false;
398aca7a94dSNamhyung Kim 	int n = 0;
399aca7a94dSNamhyung Kim 
400aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
401aca7a94dSNamhyung Kim 		++n;
4023698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
4033698dab1SNamhyung Kim 		has_children = chain->has_children;
404aca7a94dSNamhyung Kim 	}
405aca7a94dSNamhyung Kim 
406aca7a94dSNamhyung Kim 	if (has_children)
407aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
408aca7a94dSNamhyung Kim 
409aca7a94dSNamhyung Kim 	return n;
410aca7a94dSNamhyung Kim }
411aca7a94dSNamhyung Kim 
412aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
413aca7a94dSNamhyung Kim {
414aca7a94dSNamhyung Kim 	struct rb_node *nd;
415aca7a94dSNamhyung Kim 	int n = 0;
416aca7a94dSNamhyung Kim 
417aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
418aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
419aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
420aca7a94dSNamhyung Kim 	}
421aca7a94dSNamhyung Kim 
422aca7a94dSNamhyung Kim 	return n;
423aca7a94dSNamhyung Kim }
424aca7a94dSNamhyung Kim 
42505e8b080SArnaldo Carvalho de Melo static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
426aca7a94dSNamhyung Kim {
42705e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
4283698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
429aca7a94dSNamhyung Kim 
4303698dab1SNamhyung Kim 	if (he->has_children) {
43105e8b080SArnaldo Carvalho de Melo 		int n = callchain__set_folding(&he->sorted_chain, unfold);
43205e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
433aca7a94dSNamhyung Kim 	} else
43405e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
435aca7a94dSNamhyung Kim }
436aca7a94dSNamhyung Kim 
437c3b78952SNamhyung Kim static void
438c3b78952SNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
439aca7a94dSNamhyung Kim {
440aca7a94dSNamhyung Kim 	struct rb_node *nd;
441c3b78952SNamhyung Kim 	struct hists *hists = browser->hists;
442aca7a94dSNamhyung Kim 
443c3b78952SNamhyung Kim 	for (nd = rb_first(&hists->entries);
44414135663SNamhyung Kim 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
445c3b78952SNamhyung Kim 	     nd = rb_next(nd)) {
446aca7a94dSNamhyung Kim 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
447aca7a94dSNamhyung Kim 		hist_entry__set_folding(he, unfold);
448c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
449aca7a94dSNamhyung Kim 	}
450aca7a94dSNamhyung Kim }
451aca7a94dSNamhyung Kim 
45205e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
453aca7a94dSNamhyung Kim {
454c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
455c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
456c3b78952SNamhyung Kim 
457c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
458aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
45905e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
460aca7a94dSNamhyung Kim }
461aca7a94dSNamhyung Kim 
462aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
463aca7a94dSNamhyung Kim {
464aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
465aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
466aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
467aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
468aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
469aca7a94dSNamhyung Kim }
470aca7a94dSNamhyung Kim 
4715f00b0f4SArnaldo Carvalho de Melo static int hist_browser__run(struct hist_browser *browser, const char *help)
472aca7a94dSNamhyung Kim {
473aca7a94dSNamhyung Kim 	int key;
474aca7a94dSNamhyung Kim 	char title[160];
475c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
4769783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
477aca7a94dSNamhyung Kim 
47805e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
479c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
480aca7a94dSNamhyung Kim 
4811e378ebdSTaeung Song 	hists__browser_title(browser->hists, hbt, title, sizeof(title));
482aca7a94dSNamhyung Kim 
4835f00b0f4SArnaldo Carvalho de Melo 	if (ui_browser__show(&browser->b, title, help) < 0)
484aca7a94dSNamhyung Kim 		return -1;
485aca7a94dSNamhyung Kim 
486aca7a94dSNamhyung Kim 	while (1) {
48705e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
488aca7a94dSNamhyung Kim 
489aca7a94dSNamhyung Kim 		switch (key) {
490fa5df943SNamhyung Kim 		case K_TIMER: {
491fa5df943SNamhyung Kim 			u64 nr_entries;
4929783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
493fa5df943SNamhyung Kim 
494c3b78952SNamhyung Kim 			if (hist_browser__has_filter(browser))
495112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
496fa5df943SNamhyung Kim 
497c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
498fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
499aca7a94dSNamhyung Kim 
50005e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
50105e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
50205e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
50305e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
50405e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
505aca7a94dSNamhyung Kim 			}
506aca7a94dSNamhyung Kim 
5071e378ebdSTaeung Song 			hists__browser_title(browser->hists,
5081e378ebdSTaeung Song 					     hbt, title, sizeof(title));
50905e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
510aca7a94dSNamhyung Kim 			continue;
511fa5df943SNamhyung Kim 		}
512aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
513aca7a94dSNamhyung Kim 			static int seq;
51405e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
515aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
516aca7a94dSNamhyung Kim 			ui_helpline__pop();
51762c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
51805e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
51905e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
52062c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
52105e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
52205e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
523aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
524aca7a94dSNamhyung Kim 		}
525aca7a94dSNamhyung Kim 			break;
526aca7a94dSNamhyung Kim 		case 'C':
527aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
52805e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
529aca7a94dSNamhyung Kim 			break;
530aca7a94dSNamhyung Kim 		case 'E':
531aca7a94dSNamhyung Kim 			/* Expand the whole world. */
53205e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
533aca7a94dSNamhyung Kim 			break;
534025bf7eaSArnaldo Carvalho de Melo 		case 'H':
535025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
536025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
537025bf7eaSArnaldo Carvalho de Melo 			break;
538aca7a94dSNamhyung Kim 		case K_ENTER:
53905e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
540aca7a94dSNamhyung Kim 				break;
541aca7a94dSNamhyung Kim 			/* fall thru */
542aca7a94dSNamhyung Kim 		default:
543aca7a94dSNamhyung Kim 			goto out;
544aca7a94dSNamhyung Kim 		}
545aca7a94dSNamhyung Kim 	}
546aca7a94dSNamhyung Kim out:
54705e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
548aca7a94dSNamhyung Kim 	return key;
549aca7a94dSNamhyung Kim }
550aca7a94dSNamhyung Kim 
55139ee533fSNamhyung Kim struct callchain_print_arg {
55239ee533fSNamhyung Kim 	/* for hists browser */
55339ee533fSNamhyung Kim 	off_t	row_offset;
55439ee533fSNamhyung Kim 	bool	is_current_entry;
55539ee533fSNamhyung Kim 
55639ee533fSNamhyung Kim 	/* for file dump */
55739ee533fSNamhyung Kim 	FILE	*fp;
55839ee533fSNamhyung Kim 	int	printed;
55939ee533fSNamhyung Kim };
56039ee533fSNamhyung Kim 
56139ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
56239ee533fSNamhyung Kim 					 struct callchain_list *chain,
56339ee533fSNamhyung Kim 					 const char *str, int offset,
56439ee533fSNamhyung Kim 					 unsigned short row,
56539ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
56639ee533fSNamhyung Kim 
567f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
568f4536dddSNamhyung Kim 					       struct callchain_list *chain,
56939ee533fSNamhyung Kim 					       const char *str, int offset,
57039ee533fSNamhyung Kim 					       unsigned short row,
57139ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
572f4536dddSNamhyung Kim {
573f4536dddSNamhyung Kim 	int color, width;
57439ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
57570e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
576f4536dddSNamhyung Kim 
577f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
578f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
579f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
580f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
581f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
58239ee533fSNamhyung Kim 		arg->is_current_entry = true;
583f4536dddSNamhyung Kim 	}
584f4536dddSNamhyung Kim 
585f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
586f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
58726270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
588517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
58970e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
59026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
591f4536dddSNamhyung Kim }
592f4536dddSNamhyung Kim 
59339ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
59439ee533fSNamhyung Kim 						  struct callchain_list *chain,
59539ee533fSNamhyung Kim 						  const char *str, int offset,
59639ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
59739ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
59839ee533fSNamhyung Kim {
59939ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
60039ee533fSNamhyung Kim 
60139ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
60239ee533fSNamhyung Kim 				folded_sign, str);
60339ee533fSNamhyung Kim }
60439ee533fSNamhyung Kim 
60539ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
60639ee533fSNamhyung Kim 				     unsigned short row);
60739ee533fSNamhyung Kim 
60839ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
60939ee533fSNamhyung Kim 					    unsigned short row)
61039ee533fSNamhyung Kim {
61139ee533fSNamhyung Kim 	return browser->b.rows == row;
61239ee533fSNamhyung Kim }
61339ee533fSNamhyung Kim 
61439ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
61539ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
61639ee533fSNamhyung Kim {
61739ee533fSNamhyung Kim 	return false;
61839ee533fSNamhyung Kim }
61939ee533fSNamhyung Kim 
620aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
621aca7a94dSNamhyung Kim 
62218bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
62318bb8381SNamhyung Kim 					     struct callchain_node *node,
62418bb8381SNamhyung Kim 					     struct callchain_list *chain,
62518bb8381SNamhyung Kim 					     unsigned short row, u64 total,
62618bb8381SNamhyung Kim 					     bool need_percent, int offset,
62718bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
62818bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
62918bb8381SNamhyung Kim {
63018bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
63118bb8381SNamhyung Kim 	const char *str;
63218bb8381SNamhyung Kim 
63318bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
63418bb8381SNamhyung Kim 		arg->row_offset--;
63518bb8381SNamhyung Kim 		return 0;
63618bb8381SNamhyung Kim 	}
63718bb8381SNamhyung Kim 
63818bb8381SNamhyung Kim 	alloc_str = NULL;
63918bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
64018bb8381SNamhyung Kim 				       browser->show_dso);
64118bb8381SNamhyung Kim 
64218bb8381SNamhyung Kim 	if (need_percent) {
64318bb8381SNamhyung Kim 		char buf[64];
64418bb8381SNamhyung Kim 
64518bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
64618bb8381SNamhyung Kim 						total);
64718bb8381SNamhyung Kim 
64818bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
64918bb8381SNamhyung Kim 			str = "Not enough memory!";
65018bb8381SNamhyung Kim 		else
65118bb8381SNamhyung Kim 			str = alloc_str;
65218bb8381SNamhyung Kim 	}
65318bb8381SNamhyung Kim 
65418bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
65518bb8381SNamhyung Kim 
65618bb8381SNamhyung Kim 	free(alloc_str);
65718bb8381SNamhyung Kim 	return 1;
65818bb8381SNamhyung Kim }
65918bb8381SNamhyung Kim 
6604b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
6614b3a3212SNamhyung Kim 					     struct rb_root *root,
6624b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
6634b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
6644b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
6654b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
6664b3a3212SNamhyung Kim {
6674b3a3212SNamhyung Kim 	struct rb_node *node;
6684b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
6694b3a3212SNamhyung Kim 	bool need_percent;
6704b3a3212SNamhyung Kim 
6714b3a3212SNamhyung Kim 	node = rb_first(root);
6724b3a3212SNamhyung Kim 	need_percent = node && rb_next(node);
6734b3a3212SNamhyung Kim 
6744b3a3212SNamhyung Kim 	while (node) {
6754b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
6764b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
6774b3a3212SNamhyung Kim 		struct callchain_list *chain;
6784b3a3212SNamhyung Kim 		char folded_sign = ' ';
6794b3a3212SNamhyung Kim 		int first = true;
6804b3a3212SNamhyung Kim 		int extra_offset = 0;
6814b3a3212SNamhyung Kim 
6824b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
6834b3a3212SNamhyung Kim 			bool was_first = first;
6844b3a3212SNamhyung Kim 
6854b3a3212SNamhyung Kim 			if (first)
6864b3a3212SNamhyung Kim 				first = false;
6874b3a3212SNamhyung Kim 			else if (need_percent)
6884b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
6894b3a3212SNamhyung Kim 
6904b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
6914b3a3212SNamhyung Kim 
6924b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
6934b3a3212SNamhyung Kim 							chain, row, total,
6944b3a3212SNamhyung Kim 							was_first && need_percent,
6954b3a3212SNamhyung Kim 							offset + extra_offset,
6964b3a3212SNamhyung Kim 							print, arg);
6974b3a3212SNamhyung Kim 
6984b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
6994b3a3212SNamhyung Kim 				goto out;
7004b3a3212SNamhyung Kim 
7014b3a3212SNamhyung Kim 			if (folded_sign == '+')
7024b3a3212SNamhyung Kim 				goto next;
7034b3a3212SNamhyung Kim 		}
7044b3a3212SNamhyung Kim 
7054b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
7064b3a3212SNamhyung Kim 			bool was_first = first;
7074b3a3212SNamhyung Kim 
7084b3a3212SNamhyung Kim 			if (first)
7094b3a3212SNamhyung Kim 				first = false;
7104b3a3212SNamhyung Kim 			else if (need_percent)
7114b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
7124b3a3212SNamhyung Kim 
7134b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
7144b3a3212SNamhyung Kim 
7154b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
7164b3a3212SNamhyung Kim 							chain, row, total,
7174b3a3212SNamhyung Kim 							was_first && need_percent,
7184b3a3212SNamhyung Kim 							offset + extra_offset,
7194b3a3212SNamhyung Kim 							print, arg);
7204b3a3212SNamhyung Kim 
7214b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
7224b3a3212SNamhyung Kim 				goto out;
7234b3a3212SNamhyung Kim 
7244b3a3212SNamhyung Kim 			if (folded_sign == '+')
7254b3a3212SNamhyung Kim 				break;
7264b3a3212SNamhyung Kim 		}
7274b3a3212SNamhyung Kim 
7284b3a3212SNamhyung Kim next:
7294b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
7304b3a3212SNamhyung Kim 			break;
7314b3a3212SNamhyung Kim 		node = next;
7324b3a3212SNamhyung Kim 	}
7334b3a3212SNamhyung Kim out:
7344b3a3212SNamhyung Kim 	return row - first_row;
7354b3a3212SNamhyung Kim }
7364b3a3212SNamhyung Kim 
7378c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
7388c430a34SNamhyung Kim 						struct callchain_list *chain,
7398c430a34SNamhyung Kim 						char *value_str, char *old_str)
7408c430a34SNamhyung Kim {
7418c430a34SNamhyung Kim 	char bf[1024];
7428c430a34SNamhyung Kim 	const char *str;
7438c430a34SNamhyung Kim 	char *new;
7448c430a34SNamhyung Kim 
7458c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
7468c430a34SNamhyung Kim 				       browser->show_dso);
7478c430a34SNamhyung Kim 	if (old_str) {
7488c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
7498c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
7508c430a34SNamhyung Kim 			new = NULL;
7518c430a34SNamhyung Kim 	} else {
7528c430a34SNamhyung Kim 		if (value_str) {
7538c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
7548c430a34SNamhyung Kim 				new = NULL;
7558c430a34SNamhyung Kim 		} else {
7568c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
7578c430a34SNamhyung Kim 				new = NULL;
7588c430a34SNamhyung Kim 		}
7598c430a34SNamhyung Kim 	}
7608c430a34SNamhyung Kim 	return new;
7618c430a34SNamhyung Kim }
7628c430a34SNamhyung Kim 
7638c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
7648c430a34SNamhyung Kim 					       struct rb_root *root,
7658c430a34SNamhyung Kim 					       unsigned short row, u64 total,
7668c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
7678c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
7688c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
7698c430a34SNamhyung Kim {
7708c430a34SNamhyung Kim 	struct rb_node *node;
7718c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
7728c430a34SNamhyung Kim 	bool need_percent;
7738c430a34SNamhyung Kim 
7748c430a34SNamhyung Kim 	node = rb_first(root);
7758c430a34SNamhyung Kim 	need_percent = node && rb_next(node);
7768c430a34SNamhyung Kim 
7778c430a34SNamhyung Kim 	while (node) {
7788c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
7798c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
7808c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
7818c430a34SNamhyung Kim 		int first = true;
7828c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
7838c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
7848c430a34SNamhyung Kim 
7858c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
7868c430a34SNamhyung Kim 			arg->row_offset--;
7878c430a34SNamhyung Kim 			goto next;
7888c430a34SNamhyung Kim 		}
7898c430a34SNamhyung Kim 
7908c430a34SNamhyung Kim 		if (need_percent) {
7918c430a34SNamhyung Kim 			char buf[64];
7928c430a34SNamhyung Kim 
7938c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
7948c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
7958c430a34SNamhyung Kim 				value_str = (char *)"<...>";
7968c430a34SNamhyung Kim 				goto do_print;
7978c430a34SNamhyung Kim 			}
7988c430a34SNamhyung Kim 			value_str_alloc = value_str;
7998c430a34SNamhyung Kim 		}
8008c430a34SNamhyung Kim 
8018c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
8028c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
8038c430a34SNamhyung Kim 						chain, value_str, chain_str);
8048c430a34SNamhyung Kim 			if (first) {
8058c430a34SNamhyung Kim 				first = false;
8068c430a34SNamhyung Kim 				first_chain = chain;
8078c430a34SNamhyung Kim 			}
8088c430a34SNamhyung Kim 
8098c430a34SNamhyung Kim 			if (chain_str == NULL) {
8108c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
8118c430a34SNamhyung Kim 				goto do_print;
8128c430a34SNamhyung Kim 			}
8138c430a34SNamhyung Kim 
8148c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
8158c430a34SNamhyung Kim 		}
8168c430a34SNamhyung Kim 
8178c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
8188c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
8198c430a34SNamhyung Kim 						chain, value_str, chain_str);
8208c430a34SNamhyung Kim 			if (first) {
8218c430a34SNamhyung Kim 				first = false;
8228c430a34SNamhyung Kim 				first_chain = chain;
8238c430a34SNamhyung Kim 			}
8248c430a34SNamhyung Kim 
8258c430a34SNamhyung Kim 			if (chain_str == NULL) {
8268c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
8278c430a34SNamhyung Kim 				goto do_print;
8288c430a34SNamhyung Kim 			}
8298c430a34SNamhyung Kim 
8308c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
8318c430a34SNamhyung Kim 		}
8328c430a34SNamhyung Kim 
8338c430a34SNamhyung Kim do_print:
8348c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
8358c430a34SNamhyung Kim 		free(value_str_alloc);
8368c430a34SNamhyung Kim 		free(chain_str_alloc);
8378c430a34SNamhyung Kim 
8388c430a34SNamhyung Kim next:
8398c430a34SNamhyung Kim 		if (is_output_full(browser, row))
8408c430a34SNamhyung Kim 			break;
8418c430a34SNamhyung Kim 		node = next;
8428c430a34SNamhyung Kim 	}
8438c430a34SNamhyung Kim 
8448c430a34SNamhyung Kim 	return row - first_row;
8458c430a34SNamhyung Kim }
8468c430a34SNamhyung Kim 
847c09a7e75SNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
848c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
84939ee533fSNamhyung Kim 					unsigned short row, u64 total,
85039ee533fSNamhyung Kim 					print_callchain_entry_fn print,
85139ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
85239ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
853aca7a94dSNamhyung Kim {
854aca7a94dSNamhyung Kim 	struct rb_node *node;
855f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
85636e15dd4SNamhyung Kim 	u64 new_total;
8574087d11cSNamhyung Kim 	bool need_percent;
858aca7a94dSNamhyung Kim 
859c09a7e75SNamhyung Kim 	node = rb_first(root);
860c09e31ccSNamhyung Kim 	need_percent = node && rb_next(node);
8614087d11cSNamhyung Kim 
862aca7a94dSNamhyung Kim 	while (node) {
863aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
864aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
865aca7a94dSNamhyung Kim 		struct callchain_list *chain;
866aca7a94dSNamhyung Kim 		char folded_sign = ' ';
867aca7a94dSNamhyung Kim 		int first = true;
868aca7a94dSNamhyung Kim 		int extra_offset = 0;
869aca7a94dSNamhyung Kim 
870aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
871aca7a94dSNamhyung Kim 			bool was_first = first;
872aca7a94dSNamhyung Kim 
873aca7a94dSNamhyung Kim 			if (first)
874aca7a94dSNamhyung Kim 				first = false;
8754087d11cSNamhyung Kim 			else if (need_percent)
876aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
877aca7a94dSNamhyung Kim 
878aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
879aca7a94dSNamhyung Kim 
88018bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
88118bb8381SNamhyung Kim 							chain, row, total,
88218bb8381SNamhyung Kim 							was_first && need_percent,
88318bb8381SNamhyung Kim 							offset + extra_offset,
88418bb8381SNamhyung Kim 							print, arg);
885c09a7e75SNamhyung Kim 
88618bb8381SNamhyung Kim 			if (is_output_full(browser, row))
887aca7a94dSNamhyung Kim 				goto out;
88818bb8381SNamhyung Kim 
889aca7a94dSNamhyung Kim 			if (folded_sign == '+')
890aca7a94dSNamhyung Kim 				break;
891aca7a94dSNamhyung Kim 		}
892aca7a94dSNamhyung Kim 
893aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
894aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
895c09a7e75SNamhyung Kim 
896c09a7e75SNamhyung Kim 			if (callchain_param.mode == CHAIN_GRAPH_REL)
897c09a7e75SNamhyung Kim 				new_total = child->children_hit;
898c09a7e75SNamhyung Kim 			else
899c09a7e75SNamhyung Kim 				new_total = total;
900c09a7e75SNamhyung Kim 
901c09a7e75SNamhyung Kim 			row += hist_browser__show_callchain(browser, &child->rb_root,
90239ee533fSNamhyung Kim 							    new_level, row, new_total,
90339ee533fSNamhyung Kim 							    print, arg, is_output_full);
904aca7a94dSNamhyung Kim 		}
90539ee533fSNamhyung Kim 		if (is_output_full(browser, row))
906c09a7e75SNamhyung Kim 			break;
907aca7a94dSNamhyung Kim 		node = next;
908aca7a94dSNamhyung Kim 	}
909aca7a94dSNamhyung Kim out:
910aca7a94dSNamhyung Kim 	return row - first_row;
911aca7a94dSNamhyung Kim }
912aca7a94dSNamhyung Kim 
91389701460SNamhyung Kim struct hpp_arg {
91489701460SNamhyung Kim 	struct ui_browser *b;
91589701460SNamhyung Kim 	char folded_sign;
91689701460SNamhyung Kim 	bool current_entry;
91789701460SNamhyung Kim };
91889701460SNamhyung Kim 
9192f6d9009SNamhyung Kim static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
9202f6d9009SNamhyung Kim {
9212f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
922d675107cSNamhyung Kim 	int ret, len;
9232f6d9009SNamhyung Kim 	va_list args;
9242f6d9009SNamhyung Kim 	double percent;
9252f6d9009SNamhyung Kim 
9262f6d9009SNamhyung Kim 	va_start(args, fmt);
927d675107cSNamhyung Kim 	len = va_arg(args, int);
9282f6d9009SNamhyung Kim 	percent = va_arg(args, double);
9292f6d9009SNamhyung Kim 	va_end(args);
9305aed9d24SNamhyung Kim 
93189701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
9325aed9d24SNamhyung Kim 
933d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
934517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
93589701460SNamhyung Kim 
9362f6d9009SNamhyung Kim 	advance_hpp(hpp, ret);
9375aed9d24SNamhyung Kim 	return ret;
938f5951d56SNamhyung Kim }
939f5951d56SNamhyung Kim 
940fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
9415aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
9425aed9d24SNamhyung Kim {									\
9435aed9d24SNamhyung Kim 	return he->stat._field;						\
9445aed9d24SNamhyung Kim }									\
9455aed9d24SNamhyung Kim 									\
9462c5d4b4aSJiri Olsa static int								\
9475b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
9482c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
9495aed9d24SNamhyung Kim 				struct hist_entry *he)			\
9505aed9d24SNamhyung Kim {									\
9515b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
9522f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
9535aed9d24SNamhyung Kim }
954f5951d56SNamhyung Kim 
9550434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
9560434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
9570434ddd2SNamhyung Kim {									\
9580434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
9590434ddd2SNamhyung Kim }									\
9600434ddd2SNamhyung Kim 									\
9610434ddd2SNamhyung Kim static int								\
9625b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
9630434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
9640434ddd2SNamhyung Kim 				struct hist_entry *he)			\
9650434ddd2SNamhyung Kim {									\
9660434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
967517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
9685b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
969d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
9705b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
971517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
9720434ddd2SNamhyung Kim 									\
9730434ddd2SNamhyung Kim 		return ret;						\
9740434ddd2SNamhyung Kim 	}								\
9755b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
9765b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
9770434ddd2SNamhyung Kim }
9780434ddd2SNamhyung Kim 
979fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
980fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
981fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
982fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
983fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
9840434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
9855aed9d24SNamhyung Kim 
9865aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
9870434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
988f5951d56SNamhyung Kim 
989f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
990f5951d56SNamhyung Kim {
991f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
992f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
993f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
994f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
995f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
996f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
997f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
998f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
999f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1000f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
10010434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
10020434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
1003f5951d56SNamhyung Kim }
1004f5951d56SNamhyung Kim 
100505e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1006aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1007aca7a94dSNamhyung Kim 				    unsigned short row)
1008aca7a94dSNamhyung Kim {
1009aca7a94dSNamhyung Kim 	char s[256];
10101240005eSJiri Olsa 	int printed = 0;
101167d25916SNamhyung Kim 	int width = browser->b.width;
1012aca7a94dSNamhyung Kim 	char folded_sign = ' ';
101305e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1014aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
101563a1a3d8SNamhyung Kim 	bool first = true;
10161240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1017aca7a94dSNamhyung Kim 
1018aca7a94dSNamhyung Kim 	if (current_entry) {
101905e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
102005e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1021aca7a94dSNamhyung Kim 	}
1022aca7a94dSNamhyung Kim 
1023aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
1024aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1025aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1026aca7a94dSNamhyung Kim 	}
1027aca7a94dSNamhyung Kim 
1028aca7a94dSNamhyung Kim 	if (row_offset == 0) {
102989701460SNamhyung Kim 		struct hpp_arg arg = {
103089701460SNamhyung Kim 			.b		= &browser->b,
103189701460SNamhyung Kim 			.folded_sign	= folded_sign,
103289701460SNamhyung Kim 			.current_entry	= current_entry,
103389701460SNamhyung Kim 		};
1034f5951d56SNamhyung Kim 		struct perf_hpp hpp = {
1035f5951d56SNamhyung Kim 			.buf		= s,
1036f5951d56SNamhyung Kim 			.size		= sizeof(s),
103789701460SNamhyung Kim 			.ptr		= &arg,
1038f5951d56SNamhyung Kim 		};
1039c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1040f5951d56SNamhyung Kim 
1041ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
1042f5951d56SNamhyung Kim 
10431240005eSJiri Olsa 		perf_hpp__for_each_format(fmt) {
1044*361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1045*361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1046e67d49a7SNamhyung Kim 				continue;
1047e67d49a7SNamhyung Kim 
1048fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1049fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1050fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1051fb821c9eSNamhyung Kim 			} else {
1052fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1053fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1054fb821c9eSNamhyung Kim 			}
1055fb821c9eSNamhyung Kim 
1056fb821c9eSNamhyung Kim 			if (first) {
1057fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
1058517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1059f5951d56SNamhyung Kim 					width -= 2;
1060f5951d56SNamhyung Kim 				}
106163a1a3d8SNamhyung Kim 				first = false;
1062fb821c9eSNamhyung Kim 			} else {
1063517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1064fb821c9eSNamhyung Kim 				width -= 2;
1065fb821c9eSNamhyung Kim 			}
1066f5951d56SNamhyung Kim 
10671240005eSJiri Olsa 			if (fmt->color) {
10682c5d4b4aSJiri Olsa 				width -= fmt->color(fmt, &hpp, entry);
1069f5951d56SNamhyung Kim 			} else {
10702c5d4b4aSJiri Olsa 				width -= fmt->entry(fmt, &hpp, entry);
1071517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1072f5951d56SNamhyung Kim 			}
1073f5951d56SNamhyung Kim 		}
1074aca7a94dSNamhyung Kim 
1075aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
107605e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1077aca7a94dSNamhyung Kim 			width += 1;
1078aca7a94dSNamhyung Kim 
107926270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
108026d8b338SNamhyung Kim 
1081aca7a94dSNamhyung Kim 		++row;
1082aca7a94dSNamhyung Kim 		++printed;
1083aca7a94dSNamhyung Kim 	} else
1084aca7a94dSNamhyung Kim 		--row_offset;
1085aca7a94dSNamhyung Kim 
108662c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
1087c09a7e75SNamhyung Kim 		u64 total = hists__total_period(entry->hists);
108839ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
108939ee533fSNamhyung Kim 			.row_offset = row_offset,
109039ee533fSNamhyung Kim 			.is_current_entry = current_entry,
109139ee533fSNamhyung Kim 		};
1092c09a7e75SNamhyung Kim 
10934087d11cSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
10944087d11cSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
10954087d11cSNamhyung Kim 				total = entry->stat_acc->period;
10964087d11cSNamhyung Kim 			else
10974087d11cSNamhyung Kim 				total = entry->stat.period;
10984087d11cSNamhyung Kim 		}
10994087d11cSNamhyung Kim 
11004b3a3212SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT) {
11014b3a3212SNamhyung Kim 			printed += hist_browser__show_callchain_flat(browser,
11024b3a3212SNamhyung Kim 					&entry->sorted_chain, row, total,
11034b3a3212SNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
11044b3a3212SNamhyung Kim 					hist_browser__check_output_full);
11058c430a34SNamhyung Kim 		} else if (callchain_param.mode == CHAIN_FOLDED) {
11068c430a34SNamhyung Kim 			printed += hist_browser__show_callchain_folded(browser,
11078c430a34SNamhyung Kim 					&entry->sorted_chain, row, total,
11088c430a34SNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
11098c430a34SNamhyung Kim 					hist_browser__check_output_full);
11104b3a3212SNamhyung Kim 		} else {
1111c09a7e75SNamhyung Kim 			printed += hist_browser__show_callchain(browser,
111239ee533fSNamhyung Kim 					&entry->sorted_chain, 1, row, total,
111339ee533fSNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
111439ee533fSNamhyung Kim 					hist_browser__check_output_full);
11154b3a3212SNamhyung Kim 		}
1116c09a7e75SNamhyung Kim 
111739ee533fSNamhyung Kim 		if (arg.is_current_entry)
111805e8b080SArnaldo Carvalho de Melo 			browser->he_selection = entry;
1119aca7a94dSNamhyung Kim 	}
1120aca7a94dSNamhyung Kim 
1121aca7a94dSNamhyung Kim 	return printed;
1122aca7a94dSNamhyung Kim }
1123aca7a94dSNamhyung Kim 
112481a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
112581a888feSJiri Olsa {
112681a888feSJiri Olsa 	advance_hpp(hpp, inc);
112781a888feSJiri Olsa 	return hpp->size <= 0;
112881a888feSJiri Olsa }
112981a888feSJiri Olsa 
1130c6c3c02dSArnaldo Carvalho de Melo static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
113181a888feSJiri Olsa {
1132c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
113381a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
113481a888feSJiri Olsa 		.buf    = buf,
113581a888feSJiri Olsa 		.size   = size,
113681a888feSJiri Olsa 	};
113781a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
113881a888feSJiri Olsa 	size_t ret = 0;
1139c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
114081a888feSJiri Olsa 
114181a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
114281a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
114381a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
114481a888feSJiri Olsa 			return ret;
114581a888feSJiri Olsa 	}
114681a888feSJiri Olsa 
114781a888feSJiri Olsa 	perf_hpp__for_each_format(fmt) {
1148*361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
114981a888feSJiri Olsa 			continue;
115081a888feSJiri Olsa 
115181a888feSJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
115281a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
115381a888feSJiri Olsa 			break;
115481a888feSJiri Olsa 
115581a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
115681a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
115781a888feSJiri Olsa 			break;
115881a888feSJiri Olsa 	}
115981a888feSJiri Olsa 
116081a888feSJiri Olsa 	return ret;
116181a888feSJiri Olsa }
116281a888feSJiri Olsa 
1163025bf7eaSArnaldo Carvalho de Melo static void hist_browser__show_headers(struct hist_browser *browser)
1164025bf7eaSArnaldo Carvalho de Melo {
116581a888feSJiri Olsa 	char headers[1024];
116681a888feSJiri Olsa 
1167c6c3c02dSArnaldo Carvalho de Melo 	hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
1168025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1169025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
117026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1171025bf7eaSArnaldo Carvalho de Melo }
1172025bf7eaSArnaldo Carvalho de Melo 
1173aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1174aca7a94dSNamhyung Kim {
1175aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1176aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1177aca7a94dSNamhyung Kim 
1178aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
1179aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
1180aca7a94dSNamhyung Kim 	}
1181aca7a94dSNamhyung Kim }
1182aca7a94dSNamhyung Kim 
118305e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1184aca7a94dSNamhyung Kim {
1185aca7a94dSNamhyung Kim 	unsigned row = 0;
1186025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
1187aca7a94dSNamhyung Kim 	struct rb_node *nd;
118805e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1189aca7a94dSNamhyung Kim 
1190025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
1191025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1192025bf7eaSArnaldo Carvalho de Melo 		header_offset = 1;
1193025bf7eaSArnaldo Carvalho de Melo 	}
1194025bf7eaSArnaldo Carvalho de Melo 
119505e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1196979d2cacSWang Nan 	hb->he_selection = NULL;
1197979d2cacSWang Nan 	hb->selection = NULL;
1198aca7a94dSNamhyung Kim 
119905e8b080SArnaldo Carvalho de Melo 	for (nd = browser->top; nd; nd = rb_next(nd)) {
1200aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
120114135663SNamhyung Kim 		float percent;
1202aca7a94dSNamhyung Kim 
1203aca7a94dSNamhyung Kim 		if (h->filtered)
1204aca7a94dSNamhyung Kim 			continue;
1205aca7a94dSNamhyung Kim 
120614135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
1207064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1208064f1981SNamhyung Kim 			continue;
1209064f1981SNamhyung Kim 
1210aca7a94dSNamhyung Kim 		row += hist_browser__show_entry(hb, h, row);
121162c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1212aca7a94dSNamhyung Kim 			break;
1213aca7a94dSNamhyung Kim 	}
1214aca7a94dSNamhyung Kim 
1215025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
1216aca7a94dSNamhyung Kim }
1217aca7a94dSNamhyung Kim 
1218064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1219064f1981SNamhyung Kim 					     float min_pcnt)
1220aca7a94dSNamhyung Kim {
1221aca7a94dSNamhyung Kim 	while (nd != NULL) {
1222aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
122314135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1224064f1981SNamhyung Kim 
1225c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1226aca7a94dSNamhyung Kim 			return nd;
1227aca7a94dSNamhyung Kim 
1228aca7a94dSNamhyung Kim 		nd = rb_next(nd);
1229aca7a94dSNamhyung Kim 	}
1230aca7a94dSNamhyung Kim 
1231aca7a94dSNamhyung Kim 	return NULL;
1232aca7a94dSNamhyung Kim }
1233aca7a94dSNamhyung Kim 
1234064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1235064f1981SNamhyung Kim 						  float min_pcnt)
1236aca7a94dSNamhyung Kim {
1237aca7a94dSNamhyung Kim 	while (nd != NULL) {
1238aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
123914135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1240064f1981SNamhyung Kim 
1241064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1242aca7a94dSNamhyung Kim 			return nd;
1243aca7a94dSNamhyung Kim 
1244aca7a94dSNamhyung Kim 		nd = rb_prev(nd);
1245aca7a94dSNamhyung Kim 	}
1246aca7a94dSNamhyung Kim 
1247aca7a94dSNamhyung Kim 	return NULL;
1248aca7a94dSNamhyung Kim }
1249aca7a94dSNamhyung Kim 
125005e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1251aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1252aca7a94dSNamhyung Kim {
1253aca7a94dSNamhyung Kim 	struct hist_entry *h;
1254aca7a94dSNamhyung Kim 	struct rb_node *nd;
1255aca7a94dSNamhyung Kim 	bool first = true;
1256064f1981SNamhyung Kim 	struct hist_browser *hb;
1257064f1981SNamhyung Kim 
1258064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1259aca7a94dSNamhyung Kim 
126005e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1261aca7a94dSNamhyung Kim 		return;
1262aca7a94dSNamhyung Kim 
126305e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1264aca7a94dSNamhyung Kim 
1265aca7a94dSNamhyung Kim 	switch (whence) {
1266aca7a94dSNamhyung Kim 	case SEEK_SET:
1267064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
126814135663SNamhyung Kim 					   hb->min_pcnt);
1269aca7a94dSNamhyung Kim 		break;
1270aca7a94dSNamhyung Kim 	case SEEK_CUR:
127105e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1272aca7a94dSNamhyung Kim 		goto do_offset;
1273aca7a94dSNamhyung Kim 	case SEEK_END:
1274064f1981SNamhyung Kim 		nd = hists__filter_prev_entries(rb_last(browser->entries),
127514135663SNamhyung Kim 						hb->min_pcnt);
1276aca7a94dSNamhyung Kim 		first = false;
1277aca7a94dSNamhyung Kim 		break;
1278aca7a94dSNamhyung Kim 	default:
1279aca7a94dSNamhyung Kim 		return;
1280aca7a94dSNamhyung Kim 	}
1281aca7a94dSNamhyung Kim 
1282aca7a94dSNamhyung Kim 	/*
1283aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1284aca7a94dSNamhyung Kim 	 * row_offset:
1285aca7a94dSNamhyung Kim 	 */
128605e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1287aca7a94dSNamhyung Kim 	h->row_offset = 0;
1288aca7a94dSNamhyung Kim 
1289aca7a94dSNamhyung Kim 	/*
1290aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1291aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1292aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1293aca7a94dSNamhyung Kim 	 *
1294aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1295aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1296aca7a94dSNamhyung Kim 	 *
1297aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1298aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1299aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1300aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1301aca7a94dSNamhyung Kim 	 */
1302aca7a94dSNamhyung Kim do_offset:
1303837eeb75SWang Nan 	if (!nd)
1304837eeb75SWang Nan 		return;
1305837eeb75SWang Nan 
1306aca7a94dSNamhyung Kim 	if (offset > 0) {
1307aca7a94dSNamhyung Kim 		do {
1308aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
13093698dab1SNamhyung Kim 			if (h->unfolded) {
1310aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1311aca7a94dSNamhyung Kim 				if (offset > remaining) {
1312aca7a94dSNamhyung Kim 					offset -= remaining;
1313aca7a94dSNamhyung Kim 					h->row_offset = 0;
1314aca7a94dSNamhyung Kim 				} else {
1315aca7a94dSNamhyung Kim 					h->row_offset += offset;
1316aca7a94dSNamhyung Kim 					offset = 0;
131705e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1318aca7a94dSNamhyung Kim 					break;
1319aca7a94dSNamhyung Kim 				}
1320aca7a94dSNamhyung Kim 			}
132114135663SNamhyung Kim 			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1322aca7a94dSNamhyung Kim 			if (nd == NULL)
1323aca7a94dSNamhyung Kim 				break;
1324aca7a94dSNamhyung Kim 			--offset;
132505e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1326aca7a94dSNamhyung Kim 		} while (offset != 0);
1327aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1328aca7a94dSNamhyung Kim 		while (1) {
1329aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
13303698dab1SNamhyung Kim 			if (h->unfolded) {
1331aca7a94dSNamhyung Kim 				if (first) {
1332aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1333aca7a94dSNamhyung Kim 						offset += h->row_offset;
1334aca7a94dSNamhyung Kim 						h->row_offset = 0;
1335aca7a94dSNamhyung Kim 					} else {
1336aca7a94dSNamhyung Kim 						h->row_offset += offset;
1337aca7a94dSNamhyung Kim 						offset = 0;
133805e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1339aca7a94dSNamhyung Kim 						break;
1340aca7a94dSNamhyung Kim 					}
1341aca7a94dSNamhyung Kim 				} else {
1342aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1343aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1344aca7a94dSNamhyung Kim 						h->row_offset = 0;
1345aca7a94dSNamhyung Kim 					} else {
1346aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1347aca7a94dSNamhyung Kim 						offset = 0;
134805e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1349aca7a94dSNamhyung Kim 						break;
1350aca7a94dSNamhyung Kim 					}
1351aca7a94dSNamhyung Kim 				}
1352aca7a94dSNamhyung Kim 			}
1353aca7a94dSNamhyung Kim 
135414135663SNamhyung Kim 			nd = hists__filter_prev_entries(rb_prev(nd),
1355064f1981SNamhyung Kim 							hb->min_pcnt);
1356aca7a94dSNamhyung Kim 			if (nd == NULL)
1357aca7a94dSNamhyung Kim 				break;
1358aca7a94dSNamhyung Kim 			++offset;
135905e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1360aca7a94dSNamhyung Kim 			if (offset == 0) {
1361aca7a94dSNamhyung Kim 				/*
1362aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1363aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1364aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1365aca7a94dSNamhyung Kim 				 */
1366aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
13673698dab1SNamhyung Kim 				if (h->unfolded)
1368aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1369aca7a94dSNamhyung Kim 				break;
1370aca7a94dSNamhyung Kim 			}
1371aca7a94dSNamhyung Kim 			first = false;
1372aca7a94dSNamhyung Kim 		}
1373aca7a94dSNamhyung Kim 	} else {
137405e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1375aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1376aca7a94dSNamhyung Kim 		h->row_offset = 0;
1377aca7a94dSNamhyung Kim 	}
1378aca7a94dSNamhyung Kim }
1379aca7a94dSNamhyung Kim 
1380aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
138139ee533fSNamhyung Kim 					   struct hist_entry *he, FILE *fp)
1382aff3f3f6SArnaldo Carvalho de Melo {
138339ee533fSNamhyung Kim 	u64 total = hists__total_period(he->hists);
138439ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
138539ee533fSNamhyung Kim 		.fp = fp,
138639ee533fSNamhyung Kim 	};
1387aff3f3f6SArnaldo Carvalho de Melo 
138839ee533fSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
138939ee533fSNamhyung Kim 		total = he->stat_acc->period;
1390aff3f3f6SArnaldo Carvalho de Melo 
139139ee533fSNamhyung Kim 	hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
139239ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
139339ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
139439ee533fSNamhyung Kim 	return arg.printed;
1395aff3f3f6SArnaldo Carvalho de Melo }
1396aff3f3f6SArnaldo Carvalho de Melo 
1397aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1398aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1399aff3f3f6SArnaldo Carvalho de Melo {
1400aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1401aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1402aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
140326d8b338SNamhyung Kim 	struct perf_hpp hpp = {
140426d8b338SNamhyung Kim 		.buf = s,
140526d8b338SNamhyung Kim 		.size = sizeof(s),
140626d8b338SNamhyung Kim 	};
140726d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
140826d8b338SNamhyung Kim 	bool first = true;
140926d8b338SNamhyung Kim 	int ret;
1410aff3f3f6SArnaldo Carvalho de Melo 
1411aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1412aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1413aff3f3f6SArnaldo Carvalho de Melo 
1414aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1415aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
1416aff3f3f6SArnaldo Carvalho de Melo 
141726d8b338SNamhyung Kim 	perf_hpp__for_each_format(fmt) {
1418*361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
1419e67d49a7SNamhyung Kim 			continue;
1420e67d49a7SNamhyung Kim 
142126d8b338SNamhyung Kim 		if (!first) {
142226d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
142326d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
142426d8b338SNamhyung Kim 		} else
142526d8b338SNamhyung Kim 			first = false;
1426aff3f3f6SArnaldo Carvalho de Melo 
142726d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
142826d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
142926d8b338SNamhyung Kim 	}
1430aff3f3f6SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", rtrim(s));
1431aff3f3f6SArnaldo Carvalho de Melo 
1432aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
143339ee533fSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp);
1434aff3f3f6SArnaldo Carvalho de Melo 
1435aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1436aff3f3f6SArnaldo Carvalho de Melo }
1437aff3f3f6SArnaldo Carvalho de Melo 
1438aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1439aff3f3f6SArnaldo Carvalho de Melo {
1440064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1441064f1981SNamhyung Kim 						   browser->min_pcnt);
1442aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1443aff3f3f6SArnaldo Carvalho de Melo 
1444aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1445aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1446aff3f3f6SArnaldo Carvalho de Melo 
1447aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_entry(browser, h, fp);
144814135663SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1449aff3f3f6SArnaldo Carvalho de Melo 	}
1450aff3f3f6SArnaldo Carvalho de Melo 
1451aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1452aff3f3f6SArnaldo Carvalho de Melo }
1453aff3f3f6SArnaldo Carvalho de Melo 
1454aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
1455aff3f3f6SArnaldo Carvalho de Melo {
1456aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
1457aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
1458aff3f3f6SArnaldo Carvalho de Melo 
1459aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
1460aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1461aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
1462aff3f3f6SArnaldo Carvalho de Melo 			break;
1463aff3f3f6SArnaldo Carvalho de Melo 		/*
1464aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
1465aff3f3f6SArnaldo Carvalho de Melo  		 */
1466aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
1467aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1468aff3f3f6SArnaldo Carvalho de Melo 			return -1;
1469aff3f3f6SArnaldo Carvalho de Melo 		}
1470aff3f3f6SArnaldo Carvalho de Melo 	}
1471aff3f3f6SArnaldo Carvalho de Melo 
1472aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
1473aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
1474aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
14754cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
14764cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1477aff3f3f6SArnaldo Carvalho de Melo 		return -1;
1478aff3f3f6SArnaldo Carvalho de Melo 	}
1479aff3f3f6SArnaldo Carvalho de Melo 
1480aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
1481aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
1482aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
1483aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
1484aff3f3f6SArnaldo Carvalho de Melo 
1485aff3f3f6SArnaldo Carvalho de Melo 	return 0;
1486aff3f3f6SArnaldo Carvalho de Melo }
1487aff3f3f6SArnaldo Carvalho de Melo 
1488c2a51ab8SNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists,
1489b1a9ceefSNamhyung Kim 					      struct hist_browser_timer *hbt,
1490ce80d3beSKan Liang 					      struct perf_env *env)
1491aca7a94dSNamhyung Kim {
149205e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
1493aca7a94dSNamhyung Kim 
149405e8b080SArnaldo Carvalho de Melo 	if (browser) {
149505e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
149605e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
1497357cfff1SArnaldo Carvalho de Melo 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
149805e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
149905e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
1500c8302367SJiri Olsa 		browser->show_headers = symbol_conf.show_hist_headers;
1501c2a51ab8SNamhyung Kim 		browser->hbt = hbt;
1502b1a9ceefSNamhyung Kim 		browser->env = env;
1503aca7a94dSNamhyung Kim 	}
1504aca7a94dSNamhyung Kim 
150505e8b080SArnaldo Carvalho de Melo 	return browser;
1506aca7a94dSNamhyung Kim }
1507aca7a94dSNamhyung Kim 
150805e8b080SArnaldo Carvalho de Melo static void hist_browser__delete(struct hist_browser *browser)
1509aca7a94dSNamhyung Kim {
151005e8b080SArnaldo Carvalho de Melo 	free(browser);
1511aca7a94dSNamhyung Kim }
1512aca7a94dSNamhyung Kim 
151305e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1514aca7a94dSNamhyung Kim {
151505e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
1516aca7a94dSNamhyung Kim }
1517aca7a94dSNamhyung Kim 
151805e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1519aca7a94dSNamhyung Kim {
152005e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
1521aca7a94dSNamhyung Kim }
1522aca7a94dSNamhyung Kim 
15231e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
15241e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
15251e378ebdSTaeung Song {
15261e378ebdSTaeung Song 	return timer == NULL;
15271e378ebdSTaeung Song }
15281e378ebdSTaeung Song 
15291e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
15301e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
15311e378ebdSTaeung Song 				char *bf, size_t size)
1532aca7a94dSNamhyung Kim {
1533aca7a94dSNamhyung Kim 	char unit;
1534aca7a94dSNamhyung Kim 	int printed;
153505e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
153605e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
153784734b06SKan Liang 	int socket_id = hists->socket_filter;
153805e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
153905e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
1540717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
1541dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
1542717e263fSNamhyung Kim 	char buf[512];
1543717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
15449e207ddfSKan Liang 	char ref[30] = " show reference callgraph, ";
15459e207ddfSKan Liang 	bool enable_ref = false;
1546717e263fSNamhyung Kim 
1547f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
1548f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
1549f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
1550f2148330SNamhyung Kim 	}
1551f2148330SNamhyung Kim 
1552759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1553717e263fSNamhyung Kim 		struct perf_evsel *pos;
1554717e263fSNamhyung Kim 
1555717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
1556717e263fSNamhyung Kim 		ev_name = buf;
1557717e263fSNamhyung Kim 
1558717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
15594ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
15604ea062edSArnaldo Carvalho de Melo 
1561f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
15624ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
15634ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
1564f2148330SNamhyung Kim 			} else {
15654ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
15664ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
1567717e263fSNamhyung Kim 			}
1568717e263fSNamhyung Kim 		}
1569f2148330SNamhyung Kim 	}
1570aca7a94dSNamhyung Kim 
15719e207ddfSKan Liang 	if (symbol_conf.show_ref_callgraph &&
15729e207ddfSKan Liang 	    strstr(ev_name, "call-graph=no"))
15739e207ddfSKan Liang 		enable_ref = true;
1574aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
1575aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
15769e207ddfSKan Liang 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
15779e207ddfSKan Liang 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1578aca7a94dSNamhyung Kim 
1579aca7a94dSNamhyung Kim 
158005e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
1581aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
158205e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
1583aca7a94dSNamhyung Kim 	if (thread)
1584aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1585aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
1586b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
158738051234SAdrian Hunter 				    thread->tid);
1588aca7a94dSNamhyung Kim 	if (dso)
1589aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1590aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
159184734b06SKan Liang 	if (socket_id > -1)
159221394d94SKan Liang 		printed += scnprintf(bf + printed, size - printed,
159384734b06SKan Liang 				    ", Processor Socket: %d", socket_id);
15941e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
15951e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
15961e378ebdSTaeung Song 
15971e378ebdSTaeung Song 		if (top->zero)
15981e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
15991e378ebdSTaeung Song 	}
16001e378ebdSTaeung Song 
1601aca7a94dSNamhyung Kim 	return printed;
1602aca7a94dSNamhyung Kim }
1603aca7a94dSNamhyung Kim 
1604aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
1605aca7a94dSNamhyung Kim {
1606aca7a94dSNamhyung Kim 	int i;
1607aca7a94dSNamhyung Kim 
160804662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
160904662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
1610aca7a94dSNamhyung Kim }
1611aca7a94dSNamhyung Kim 
1612341487abSFeng Tang /*
1613341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
1614341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1615341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
1616341487abSFeng Tang  */
1617341487abSFeng Tang static bool is_input_name_malloced = false;
1618341487abSFeng Tang 
1619341487abSFeng Tang static int switch_data_file(void)
1620341487abSFeng Tang {
1621341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
1622341487abSFeng Tang 	DIR *pwd_dir;
1623341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
1624341487abSFeng Tang 	struct dirent *dent;
1625341487abSFeng Tang 
1626341487abSFeng Tang 	pwd = getenv("PWD");
1627341487abSFeng Tang 	if (!pwd)
1628341487abSFeng Tang 		return ret;
1629341487abSFeng Tang 
1630341487abSFeng Tang 	pwd_dir = opendir(pwd);
1631341487abSFeng Tang 	if (!pwd_dir)
1632341487abSFeng Tang 		return ret;
1633341487abSFeng Tang 
1634341487abSFeng Tang 	memset(options, 0, sizeof(options));
1635341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
1636341487abSFeng Tang 
1637341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
1638341487abSFeng Tang 		char path[PATH_MAX];
1639341487abSFeng Tang 		u64 magic;
1640341487abSFeng Tang 		char *name = dent->d_name;
1641341487abSFeng Tang 		FILE *file;
1642341487abSFeng Tang 
1643341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
1644341487abSFeng Tang 			continue;
1645341487abSFeng Tang 
1646341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1647341487abSFeng Tang 
1648341487abSFeng Tang 		file = fopen(path, "r");
1649341487abSFeng Tang 		if (!file)
1650341487abSFeng Tang 			continue;
1651341487abSFeng Tang 
1652341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
1653341487abSFeng Tang 			goto close_file_and_continue;
1654341487abSFeng Tang 
1655341487abSFeng Tang 		if (is_perf_magic(magic)) {
1656341487abSFeng Tang 			options[nr_options] = strdup(name);
1657341487abSFeng Tang 			if (!options[nr_options])
1658341487abSFeng Tang 				goto close_file_and_continue;
1659341487abSFeng Tang 
1660341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
1661341487abSFeng Tang 			if (!abs_path[nr_options]) {
166274cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
1663341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
1664341487abSFeng Tang 				fclose(file);
1665341487abSFeng Tang 				break;
1666341487abSFeng Tang 			}
1667341487abSFeng Tang 
1668341487abSFeng Tang 			nr_options++;
1669341487abSFeng Tang 		}
1670341487abSFeng Tang 
1671341487abSFeng Tang close_file_and_continue:
1672341487abSFeng Tang 		fclose(file);
1673341487abSFeng Tang 		if (nr_options >= 32) {
1674341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
1675341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
1676341487abSFeng Tang 			break;
1677341487abSFeng Tang 		}
1678341487abSFeng Tang 	}
1679341487abSFeng Tang 	closedir(pwd_dir);
1680341487abSFeng Tang 
1681341487abSFeng Tang 	if (nr_options) {
1682341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
1683341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
1684341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
1685341487abSFeng Tang 			if (tmp) {
1686341487abSFeng Tang 				if (is_input_name_malloced)
1687341487abSFeng Tang 					free((void *)input_name);
1688341487abSFeng Tang 				input_name = tmp;
1689341487abSFeng Tang 				is_input_name_malloced = true;
1690341487abSFeng Tang 				ret = 0;
1691341487abSFeng Tang 			} else
1692341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
1693341487abSFeng Tang 		}
1694341487abSFeng Tang 	}
1695341487abSFeng Tang 
1696341487abSFeng Tang 	free_popup_options(options, nr_options);
1697341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
1698341487abSFeng Tang 	return ret;
1699341487abSFeng Tang }
1700341487abSFeng Tang 
1701ea7cd592SNamhyung Kim struct popup_action {
1702ea7cd592SNamhyung Kim 	struct thread 		*thread;
1703ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
170484734b06SKan Liang 	int			socket;
1705ea7cd592SNamhyung Kim 
1706ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
1707ea7cd592SNamhyung Kim };
1708ea7cd592SNamhyung Kim 
1709bc7cad42SNamhyung Kim static int
1710ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
1711bc7cad42SNamhyung Kim {
1712bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
1713bc7cad42SNamhyung Kim 	struct annotation *notes;
1714bc7cad42SNamhyung Kim 	struct hist_entry *he;
1715bc7cad42SNamhyung Kim 	int err;
1716bc7cad42SNamhyung Kim 
1717eebd0bfcSArnaldo Carvalho de Melo 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
1718bc7cad42SNamhyung Kim 		return 0;
1719bc7cad42SNamhyung Kim 
1720ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
1721bc7cad42SNamhyung Kim 	if (!notes->src)
1722bc7cad42SNamhyung Kim 		return 0;
1723bc7cad42SNamhyung Kim 
1724bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
1725ea7cd592SNamhyung Kim 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1726bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
1727bc7cad42SNamhyung Kim 	/*
1728bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
1729bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
1730bc7cad42SNamhyung Kim 	 */
1731bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1732bc7cad42SNamhyung Kim 		return 1;
1733bc7cad42SNamhyung Kim 
1734bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1735bc7cad42SNamhyung Kim 	if (err)
1736bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
1737bc7cad42SNamhyung Kim 	return 0;
1738bc7cad42SNamhyung Kim }
1739bc7cad42SNamhyung Kim 
1740bc7cad42SNamhyung Kim static int
1741ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
1742ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
1743ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
1744bc7cad42SNamhyung Kim {
1745ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
1746ea7cd592SNamhyung Kim 		return 0;
1747ea7cd592SNamhyung Kim 
1748ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1749ea7cd592SNamhyung Kim 		return 0;
1750ea7cd592SNamhyung Kim 
1751ea7cd592SNamhyung Kim 	act->ms.map = map;
1752ea7cd592SNamhyung Kim 	act->ms.sym = sym;
1753ea7cd592SNamhyung Kim 	act->fn = do_annotate;
1754ea7cd592SNamhyung Kim 	return 1;
1755ea7cd592SNamhyung Kim }
1756ea7cd592SNamhyung Kim 
1757ea7cd592SNamhyung Kim static int
1758ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1759ea7cd592SNamhyung Kim {
1760ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
1761ea7cd592SNamhyung Kim 
1762bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
1763bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
1764bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
1765bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
1766bc7cad42SNamhyung Kim 		ui_helpline__pop();
1767bc7cad42SNamhyung Kim 	} else {
17687727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1769bc7cad42SNamhyung Kim 				   thread->comm_set ? thread__comm_str(thread) : "",
1770bc7cad42SNamhyung Kim 				   thread->tid);
1771bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
1772bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
1773bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
1774bc7cad42SNamhyung Kim 	}
1775bc7cad42SNamhyung Kim 
1776bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
1777bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
1778bc7cad42SNamhyung Kim 	return 0;
1779bc7cad42SNamhyung Kim }
1780bc7cad42SNamhyung Kim 
1781bc7cad42SNamhyung Kim static int
1782ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1783ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
1784bc7cad42SNamhyung Kim {
1785ea7cd592SNamhyung Kim 	if (thread == NULL)
1786ea7cd592SNamhyung Kim 		return 0;
1787ea7cd592SNamhyung Kim 
1788ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s(%d) thread",
1789ea7cd592SNamhyung Kim 		     browser->hists->thread_filter ? "out of" : "into",
1790ea7cd592SNamhyung Kim 		     thread->comm_set ? thread__comm_str(thread) : "",
1791ea7cd592SNamhyung Kim 		     thread->tid) < 0)
1792ea7cd592SNamhyung Kim 		return 0;
1793ea7cd592SNamhyung Kim 
1794ea7cd592SNamhyung Kim 	act->thread = thread;
1795ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
1796ea7cd592SNamhyung Kim 	return 1;
1797ea7cd592SNamhyung Kim }
1798ea7cd592SNamhyung Kim 
1799ea7cd592SNamhyung Kim static int
1800ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1801ea7cd592SNamhyung Kim {
1802045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
1803ea7cd592SNamhyung Kim 
1804bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
1805bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
1806bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
1807bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
1808bc7cad42SNamhyung Kim 		ui_helpline__pop();
1809bc7cad42SNamhyung Kim 	} else {
1810045b80ddSArnaldo Carvalho de Melo 		if (map == NULL)
1811bc7cad42SNamhyung Kim 			return 0;
18127727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1813045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1814045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
1815bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
1816bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
1817bc7cad42SNamhyung Kim 	}
1818bc7cad42SNamhyung Kim 
1819bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
1820bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
1821bc7cad42SNamhyung Kim 	return 0;
1822bc7cad42SNamhyung Kim }
1823bc7cad42SNamhyung Kim 
1824bc7cad42SNamhyung Kim static int
1825ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1826045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
1827bc7cad42SNamhyung Kim {
1828045b80ddSArnaldo Carvalho de Melo 	if (map == NULL)
1829ea7cd592SNamhyung Kim 		return 0;
1830ea7cd592SNamhyung Kim 
1831ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
1832ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
1833045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1834ea7cd592SNamhyung Kim 		return 0;
1835ea7cd592SNamhyung Kim 
1836045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
1837ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
1838ea7cd592SNamhyung Kim 	return 1;
1839ea7cd592SNamhyung Kim }
1840ea7cd592SNamhyung Kim 
1841ea7cd592SNamhyung Kim static int
1842ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
1843ea7cd592SNamhyung Kim 	      struct popup_action *act)
1844ea7cd592SNamhyung Kim {
1845ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
1846bc7cad42SNamhyung Kim 	return 0;
1847bc7cad42SNamhyung Kim }
1848bc7cad42SNamhyung Kim 
1849bc7cad42SNamhyung Kim static int
1850ea7cd592SNamhyung Kim add_map_opt(struct hist_browser *browser __maybe_unused,
1851ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
1852ea7cd592SNamhyung Kim {
1853ea7cd592SNamhyung Kim 	if (map == NULL)
1854ea7cd592SNamhyung Kim 		return 0;
1855ea7cd592SNamhyung Kim 
1856ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
1857ea7cd592SNamhyung Kim 		return 0;
1858ea7cd592SNamhyung Kim 
1859ea7cd592SNamhyung Kim 	act->ms.map = map;
1860ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
1861ea7cd592SNamhyung Kim 	return 1;
1862ea7cd592SNamhyung Kim }
1863ea7cd592SNamhyung Kim 
1864ea7cd592SNamhyung Kim static int
1865bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
1866ea7cd592SNamhyung Kim 	      struct popup_action *act)
1867bc7cad42SNamhyung Kim {
1868bc7cad42SNamhyung Kim 	char script_opt[64];
1869bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
1870bc7cad42SNamhyung Kim 
1871ea7cd592SNamhyung Kim 	if (act->thread) {
1872bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1873ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
1874ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
1875bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1876ea7cd592SNamhyung Kim 			  act->ms.sym->name);
1877bc7cad42SNamhyung Kim 	}
1878bc7cad42SNamhyung Kim 
1879bc7cad42SNamhyung Kim 	script_browse(script_opt);
1880bc7cad42SNamhyung Kim 	return 0;
1881bc7cad42SNamhyung Kim }
1882bc7cad42SNamhyung Kim 
1883bc7cad42SNamhyung Kim static int
1884ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
1885ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
1886ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
1887ea7cd592SNamhyung Kim {
1888ea7cd592SNamhyung Kim 	if (thread) {
1889ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1890ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
1891ea7cd592SNamhyung Kim 			return 0;
1892ea7cd592SNamhyung Kim 	} else if (sym) {
1893ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1894ea7cd592SNamhyung Kim 			     sym->name) < 0)
1895ea7cd592SNamhyung Kim 			return 0;
1896ea7cd592SNamhyung Kim 	} else {
1897ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
1898ea7cd592SNamhyung Kim 			return 0;
1899ea7cd592SNamhyung Kim 	}
1900ea7cd592SNamhyung Kim 
1901ea7cd592SNamhyung Kim 	act->thread = thread;
1902ea7cd592SNamhyung Kim 	act->ms.sym = sym;
1903ea7cd592SNamhyung Kim 	act->fn = do_run_script;
1904ea7cd592SNamhyung Kim 	return 1;
1905ea7cd592SNamhyung Kim }
1906ea7cd592SNamhyung Kim 
1907ea7cd592SNamhyung Kim static int
1908ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
1909ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
1910bc7cad42SNamhyung Kim {
1911bc7cad42SNamhyung Kim 	if (switch_data_file()) {
1912bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
1913bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
1914ea7cd592SNamhyung Kim 		return 0;
1915bc7cad42SNamhyung Kim 	}
1916bc7cad42SNamhyung Kim 
1917bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
1918bc7cad42SNamhyung Kim }
1919bc7cad42SNamhyung Kim 
1920ea7cd592SNamhyung Kim static int
1921ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
1922ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
1923ea7cd592SNamhyung Kim {
1924ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
1925ea7cd592SNamhyung Kim 		return 0;
1926ea7cd592SNamhyung Kim 
1927ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1928ea7cd592SNamhyung Kim 		return 0;
1929ea7cd592SNamhyung Kim 
1930ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
1931ea7cd592SNamhyung Kim 	return 1;
1932ea7cd592SNamhyung Kim }
1933ea7cd592SNamhyung Kim 
1934ea7cd592SNamhyung Kim static int
1935ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
1936ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
1937ea7cd592SNamhyung Kim {
1938ea7cd592SNamhyung Kim 	return 0;
1939ea7cd592SNamhyung Kim }
1940ea7cd592SNamhyung Kim 
1941ea7cd592SNamhyung Kim static int
1942ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
1943ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
1944ea7cd592SNamhyung Kim {
1945ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
1946ea7cd592SNamhyung Kim 		return 0;
1947ea7cd592SNamhyung Kim 
1948ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
1949ea7cd592SNamhyung Kim 	return 1;
1950ea7cd592SNamhyung Kim }
1951ea7cd592SNamhyung Kim 
195284734b06SKan Liang static int
195384734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
195484734b06SKan Liang {
195584734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
195684734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
195784734b06SKan Liang 		browser->hists->socket_filter = -1;
195884734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
195984734b06SKan Liang 	} else {
196084734b06SKan Liang 		browser->hists->socket_filter = act->socket;
196184734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
196284734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
196384734b06SKan Liang 	}
196484734b06SKan Liang 
196584734b06SKan Liang 	hists__filter_by_socket(browser->hists);
196684734b06SKan Liang 	hist_browser__reset(browser);
196784734b06SKan Liang 	return 0;
196884734b06SKan Liang }
196984734b06SKan Liang 
197084734b06SKan Liang static int
197184734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
197284734b06SKan Liang 	       char **optstr, int socket_id)
197384734b06SKan Liang {
197484734b06SKan Liang 	if (socket_id < 0)
197584734b06SKan Liang 		return 0;
197684734b06SKan Liang 
197784734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
197884734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
197984734b06SKan Liang 		     socket_id) < 0)
198084734b06SKan Liang 		return 0;
198184734b06SKan Liang 
198284734b06SKan Liang 	act->socket = socket_id;
198384734b06SKan Liang 	act->fn = do_zoom_socket;
198484734b06SKan Liang 	return 1;
198584734b06SKan Liang }
198684734b06SKan Liang 
1987112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
1988064f1981SNamhyung Kim {
1989064f1981SNamhyung Kim 	u64 nr_entries = 0;
1990064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
1991064f1981SNamhyung Kim 
1992268397cbSNamhyung Kim 	if (hb->min_pcnt == 0) {
1993268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1994268397cbSNamhyung Kim 		return;
1995268397cbSNamhyung Kim 	}
1996268397cbSNamhyung Kim 
199714135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1998064f1981SNamhyung Kim 		nr_entries++;
1999c481f930SNamhyung Kim 		nd = rb_next(nd);
2000064f1981SNamhyung Kim 	}
2001064f1981SNamhyung Kim 
2002112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2003064f1981SNamhyung Kim }
2004341487abSFeng Tang 
2005aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2006dd00d486SJiri Olsa 				    const char *helpline,
2007aca7a94dSNamhyung Kim 				    bool left_exits,
200868d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2009064f1981SNamhyung Kim 				    float min_pcnt,
2010ce80d3beSKan Liang 				    struct perf_env *env)
2011aca7a94dSNamhyung Kim {
20124ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2013b1a9ceefSNamhyung Kim 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2014aca7a94dSNamhyung Kim 	struct branch_info *bi;
2015f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2016f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2017ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2018aca7a94dSNamhyung Kim 	int nr_options = 0;
2019aca7a94dSNamhyung Kim 	int key = -1;
2020aca7a94dSNamhyung Kim 	char buf[64];
20219783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
202259dc9f25SNamhyung Kim 	struct perf_hpp_fmt *fmt;
2023aca7a94dSNamhyung Kim 
2024e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2025e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2026e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2027e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
2028e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2029e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2030e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2031e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
20327727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
20337727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
2034e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2035e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2036e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2037e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2038105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2039025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
204031eb4360SNamhyung Kim 	"m             Display context menu\n"				\
204184734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2042e8e684a5SNamhyung Kim 
2043e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
2044e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
20456dd60135SNamhyung Kim 	"i             Show header information\n"
2046e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2047e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2048e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2049e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2050e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2051e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2052e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2053e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2054e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2055e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
205642337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2057fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2058e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2059e8e684a5SNamhyung Kim 
2060aca7a94dSNamhyung Kim 	if (browser == NULL)
2061aca7a94dSNamhyung Kim 		return -1;
2062aca7a94dSNamhyung Kim 
2063ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2064ed426915SNamhyung Kim 	SLang_reset_tty();
2065ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2066ed426915SNamhyung Kim 
206703905048SNamhyung Kim 	if (min_pcnt)
2068064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2069112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2070064f1981SNamhyung Kim 
207184734b06SKan Liang 	browser->pstack = pstack__new(3);
207201f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2073aca7a94dSNamhyung Kim 		goto out;
2074aca7a94dSNamhyung Kim 
2075aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2076aca7a94dSNamhyung Kim 
2077aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2078ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2079aca7a94dSNamhyung Kim 
2080c6c3c02dSArnaldo Carvalho de Melo 	perf_hpp__for_each_format(fmt) {
208159dc9f25SNamhyung Kim 		perf_hpp__reset_width(fmt, hists);
2082c6c3c02dSArnaldo Carvalho de Melo 		/*
2083c6c3c02dSArnaldo Carvalho de Melo 		 * This is done just once, and activates the horizontal scrolling
2084c6c3c02dSArnaldo Carvalho de Melo 		 * code in the ui_browser code, it would be better to have a the
2085c6c3c02dSArnaldo Carvalho de Melo 		 * counter in the perf_hpp code, but I couldn't find doing it here
2086c6c3c02dSArnaldo Carvalho de Melo 		 * works, FIXME by setting this in hist_browser__new, for now, be
2087c6c3c02dSArnaldo Carvalho de Melo 		 * clever 8-)
2088c6c3c02dSArnaldo Carvalho de Melo 		 */
2089c6c3c02dSArnaldo Carvalho de Melo 		++browser->b.columns;
2090c6c3c02dSArnaldo Carvalho de Melo 	}
209159dc9f25SNamhyung Kim 
20925b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
20935b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
20945b591669SNamhyung Kim 
2095aca7a94dSNamhyung Kim 	while (1) {
2096f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2097045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
2098ea7cd592SNamhyung Kim 		int choice = 0;
209984734b06SKan Liang 		int socked_id = -1;
2100aca7a94dSNamhyung Kim 
2101aca7a94dSNamhyung Kim 		nr_options = 0;
2102aca7a94dSNamhyung Kim 
21035f00b0f4SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline);
2104aca7a94dSNamhyung Kim 
2105aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
2106aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
2107045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
210884734b06SKan Liang 			socked_id = browser->he_selection->socket;
2109aca7a94dSNamhyung Kim 		}
2110aca7a94dSNamhyung Kim 		switch (key) {
2111aca7a94dSNamhyung Kim 		case K_TAB:
2112aca7a94dSNamhyung Kim 		case K_UNTAB:
2113aca7a94dSNamhyung Kim 			if (nr_events == 1)
2114aca7a94dSNamhyung Kim 				continue;
2115aca7a94dSNamhyung Kim 			/*
2116aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
2117aca7a94dSNamhyung Kim 			 * go to the next or previous
2118aca7a94dSNamhyung Kim 			 */
2119aca7a94dSNamhyung Kim 			goto out_free_stack;
2120aca7a94dSNamhyung Kim 		case 'a':
21219c796ec8SArnaldo Carvalho de Melo 			if (!sort__has_sym) {
2122aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
2123aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
2124aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
2125aca7a94dSNamhyung Kim 				continue;
2126aca7a94dSNamhyung Kim 			}
2127aca7a94dSNamhyung Kim 
2128aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
2129aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
2130aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
2131aca7a94dSNamhyung Kim 				continue;
2132bc7cad42SNamhyung Kim 
2133ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
2134ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
2135ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
2136bc7cad42SNamhyung Kim 			continue;
2137aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
2138aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
2139aff3f3f6SArnaldo Carvalho de Melo 			continue;
2140aca7a94dSNamhyung Kim 		case 'd':
2141fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
2142ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
2143bc7cad42SNamhyung Kim 			continue;
2144a7cb8863SArnaldo Carvalho de Melo 		case 'V':
2145a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
2146a7cb8863SArnaldo Carvalho de Melo 			continue;
2147aca7a94dSNamhyung Kim 		case 't':
2148ea7cd592SNamhyung Kim 			actions->thread = thread;
2149ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
2150bc7cad42SNamhyung Kim 			continue;
215184734b06SKan Liang 		case 'S':
215284734b06SKan Liang 			actions->socket = socked_id;
215384734b06SKan Liang 			do_zoom_socket(browser, actions);
215484734b06SKan Liang 			continue;
21555a5626b1SArnaldo Carvalho de Melo 		case '/':
2156aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
21574aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
21584aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
2159aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2160aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
216105e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
216205e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
2163aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
2164aca7a94dSNamhyung Kim 			}
2165aca7a94dSNamhyung Kim 			continue;
2166cdbab7c2SFeng Tang 		case 'r':
2167ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
2168ea7cd592SNamhyung Kim 				actions->thread = NULL;
2169ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
2170ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
2171ea7cd592SNamhyung Kim 			}
2172c77d8d70SFeng Tang 			continue;
2173341487abSFeng Tang 		case 's':
2174bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
2175ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
2176bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
2177bc7cad42SNamhyung Kim 					goto out_free_stack;
2178bc7cad42SNamhyung Kim 			}
2179341487abSFeng Tang 			continue;
21806dd60135SNamhyung Kim 		case 'i':
21816dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
21826dd60135SNamhyung Kim 			if (env->arch)
21836dd60135SNamhyung Kim 				tui__header_window(env);
21846dd60135SNamhyung Kim 			continue;
2185105eb30fSNamhyung Kim 		case 'F':
2186105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
2187105eb30fSNamhyung Kim 			continue;
218842337a22SNamhyung Kim 		case 'z':
218942337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
219042337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
219142337a22SNamhyung Kim 
219242337a22SNamhyung Kim 				top->zero = !top->zero;
219342337a22SNamhyung Kim 			}
219442337a22SNamhyung Kim 			continue;
2195aca7a94dSNamhyung Kim 		case K_F1:
2196aca7a94dSNamhyung Kim 		case 'h':
2197aca7a94dSNamhyung Kim 		case '?':
2198aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
2199e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
2200aca7a94dSNamhyung Kim 			continue;
2201aca7a94dSNamhyung Kim 		case K_ENTER:
2202aca7a94dSNamhyung Kim 		case K_RIGHT:
220331eb4360SNamhyung Kim 		case 'm':
2204aca7a94dSNamhyung Kim 			/* menu */
2205aca7a94dSNamhyung Kim 			break;
220663ab1749SArnaldo Carvalho de Melo 		case K_ESC:
2207aca7a94dSNamhyung Kim 		case K_LEFT: {
2208aca7a94dSNamhyung Kim 			const void *top;
2209aca7a94dSNamhyung Kim 
221001f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
2211aca7a94dSNamhyung Kim 				/*
2212aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
2213aca7a94dSNamhyung Kim 				 */
2214aca7a94dSNamhyung Kim 				if (left_exits)
2215aca7a94dSNamhyung Kim 					goto out_free_stack;
221663ab1749SArnaldo Carvalho de Melo 
221763ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
221863ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
221963ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
222063ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
222163ab1749SArnaldo Carvalho de Melo 
2222aca7a94dSNamhyung Kim 				continue;
2223aca7a94dSNamhyung Kim 			}
22246422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
2225bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
22266422184bSNamhyung Kim 				/*
22276422184bSNamhyung Kim 				 * No need to set actions->dso here since
22286422184bSNamhyung Kim 				 * it's just to remove the current filter.
22296422184bSNamhyung Kim 				 * Ditto for thread below.
22306422184bSNamhyung Kim 				 */
22316422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
223284734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
22336422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
223484734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
223584734b06SKan Liang 				do_zoom_socket(browser, actions);
223684734b06SKan Liang 			}
2237aca7a94dSNamhyung Kim 			continue;
2238aca7a94dSNamhyung Kim 		}
2239aca7a94dSNamhyung Kim 		case 'q':
2240aca7a94dSNamhyung Kim 		case CTRL('c'):
2241516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
2242fbb7997eSArnaldo Carvalho de Melo 		case 'f':
224313d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
224413d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
224513d1e536SNamhyung Kim 
224613d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
224713d1e536SNamhyung Kim 				/*
224813d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
224913d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
225013d1e536SNamhyung Kim 				 */
225113d1e536SNamhyung Kim 				if (top->evlist->enabled) {
225213d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
225313d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
225413d1e536SNamhyung Kim 				} else {
225513d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
225613d1e536SNamhyung Kim 					hbt->refresh = 0;
225713d1e536SNamhyung Kim 				}
225813d1e536SNamhyung Kim 				continue;
225913d1e536SNamhyung Kim 			}
22603e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
2261aca7a94dSNamhyung Kim 		default:
22623e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
2263aca7a94dSNamhyung Kim 			continue;
2264aca7a94dSNamhyung Kim 		}
2265aca7a94dSNamhyung Kim 
22669c796ec8SArnaldo Carvalho de Melo 		if (!sort__has_sym)
2267aca7a94dSNamhyung Kim 			goto add_exit_option;
2268aca7a94dSNamhyung Kim 
22690ba332f7SArnaldo Carvalho de Melo 		if (browser->selection == NULL)
22700ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
22710ba332f7SArnaldo Carvalho de Melo 
227255369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
2273aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
22740ba332f7SArnaldo Carvalho de Melo 
22750ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
22760ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
22770ba332f7SArnaldo Carvalho de Melo 
2278ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2279ea7cd592SNamhyung Kim 						       &actions[nr_options],
2280ea7cd592SNamhyung Kim 						       &options[nr_options],
2281ea7cd592SNamhyung Kim 						       bi->from.map,
2282ea7cd592SNamhyung Kim 						       bi->from.sym);
2283ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
2284ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
2285ea7cd592SNamhyung Kim 							&actions[nr_options],
2286ea7cd592SNamhyung Kim 							&options[nr_options],
2287ea7cd592SNamhyung Kim 							bi->to.map,
2288ea7cd592SNamhyung Kim 							bi->to.sym);
2289aca7a94dSNamhyung Kim 		} else {
2290ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2291ea7cd592SNamhyung Kim 						       &actions[nr_options],
2292ea7cd592SNamhyung Kim 						       &options[nr_options],
2293ea7cd592SNamhyung Kim 						       browser->selection->map,
2294ea7cd592SNamhyung Kim 						       browser->selection->sym);
2295446fb96cSArnaldo Carvalho de Melo 		}
22960ba332f7SArnaldo Carvalho de Melo skip_annotation:
2297ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
2298ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
2299ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
2300045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
2301ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
2302ea7cd592SNamhyung Kim 					  &options[nr_options],
2303bd315aabSWang Nan 					  browser->selection ?
2304bd315aabSWang Nan 						browser->selection->map : NULL);
230584734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
230684734b06SKan Liang 					     &options[nr_options],
230784734b06SKan Liang 					     socked_id);
2308cdbab7c2SFeng Tang 		/* perf script support */
2309cdbab7c2SFeng Tang 		if (browser->he_selection) {
2310ea7cd592SNamhyung Kim 			nr_options += add_script_opt(browser,
2311ea7cd592SNamhyung Kim 						     &actions[nr_options],
2312ea7cd592SNamhyung Kim 						     &options[nr_options],
2313ea7cd592SNamhyung Kim 						     thread, NULL);
2314bd315aabSWang Nan 			/*
2315bd315aabSWang Nan 			 * Note that browser->selection != NULL
2316bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
2317bd315aabSWang Nan 			 * so we don't need to check browser->selection
2318bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
2319bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
2320bd315aabSWang Nan 			 *
2321bd315aabSWang Nan 			 * See hist_browser__show_entry.
2322bd315aabSWang Nan 			 */
2323ea7cd592SNamhyung Kim 			nr_options += add_script_opt(browser,
2324ea7cd592SNamhyung Kim 						     &actions[nr_options],
2325ea7cd592SNamhyung Kim 						     &options[nr_options],
2326ea7cd592SNamhyung Kim 						     NULL, browser->selection->sym);
2327cdbab7c2SFeng Tang 		}
2328ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
2329ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
2330ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
2331ea7cd592SNamhyung Kim 					     &options[nr_options]);
2332aca7a94dSNamhyung Kim add_exit_option:
2333ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
2334ea7cd592SNamhyung Kim 					   &options[nr_options]);
2335aca7a94dSNamhyung Kim 
2336ea7cd592SNamhyung Kim 		do {
2337ea7cd592SNamhyung Kim 			struct popup_action *act;
2338ea7cd592SNamhyung Kim 
2339ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
2340ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
2341aca7a94dSNamhyung Kim 				break;
2342aca7a94dSNamhyung Kim 
2343ea7cd592SNamhyung Kim 			act = &actions[choice];
2344ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
2345ea7cd592SNamhyung Kim 		} while (key == 1);
2346aca7a94dSNamhyung Kim 
2347bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
2348341487abSFeng Tang 			break;
2349341487abSFeng Tang 	}
2350aca7a94dSNamhyung Kim out_free_stack:
235101f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
2352aca7a94dSNamhyung Kim out:
2353aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
2354f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
2355aca7a94dSNamhyung Kim 	return key;
2356aca7a94dSNamhyung Kim }
2357aca7a94dSNamhyung Kim 
2358aca7a94dSNamhyung Kim struct perf_evsel_menu {
2359aca7a94dSNamhyung Kim 	struct ui_browser b;
2360aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
2361aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
2362064f1981SNamhyung Kim 	float min_pcnt;
2363ce80d3beSKan Liang 	struct perf_env *env;
2364aca7a94dSNamhyung Kim };
2365aca7a94dSNamhyung Kim 
2366aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
2367aca7a94dSNamhyung Kim 				   void *entry, int row)
2368aca7a94dSNamhyung Kim {
2369aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
2370aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
2371aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
23724ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2373aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
23744ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
23757289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
2376aca7a94dSNamhyung Kim 	char bf[256], unit;
2377aca7a94dSNamhyung Kim 	const char *warn = " ";
2378aca7a94dSNamhyung Kim 	size_t printed;
2379aca7a94dSNamhyung Kim 
2380aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2381aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
2382aca7a94dSNamhyung Kim 
2383759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
2384717e263fSNamhyung Kim 		struct perf_evsel *pos;
2385717e263fSNamhyung Kim 
2386717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
2387717e263fSNamhyung Kim 
2388717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
23894ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
23904ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2391717e263fSNamhyung Kim 		}
2392717e263fSNamhyung Kim 	}
2393717e263fSNamhyung Kim 
2394aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
2395aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2396aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
2397517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
2398aca7a94dSNamhyung Kim 
23994ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2400aca7a94dSNamhyung Kim 	if (nr_events != 0) {
2401aca7a94dSNamhyung Kim 		menu->lost_events = true;
2402aca7a94dSNamhyung Kim 		if (!current_entry)
2403aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
2404aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
2405aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2406aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
2407aca7a94dSNamhyung Kim 		warn = bf;
2408aca7a94dSNamhyung Kim 	}
2409aca7a94dSNamhyung Kim 
241026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
2411aca7a94dSNamhyung Kim 
2412aca7a94dSNamhyung Kim 	if (current_entry)
2413aca7a94dSNamhyung Kim 		menu->selection = evsel;
2414aca7a94dSNamhyung Kim }
2415aca7a94dSNamhyung Kim 
2416aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2417aca7a94dSNamhyung Kim 				int nr_events, const char *help,
24189783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
2419aca7a94dSNamhyung Kim {
2420aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
2421aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
2422dd00d486SJiri Olsa 	const char *title = "Available samples";
24239783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2424aca7a94dSNamhyung Kim 	int key;
2425aca7a94dSNamhyung Kim 
2426aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
2427aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
2428aca7a94dSNamhyung Kim 		return -1;
2429aca7a94dSNamhyung Kim 
2430aca7a94dSNamhyung Kim 	while (1) {
2431aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
2432aca7a94dSNamhyung Kim 
2433aca7a94dSNamhyung Kim 		switch (key) {
2434aca7a94dSNamhyung Kim 		case K_TIMER:
24359783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
2436aca7a94dSNamhyung Kim 
2437aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
2438aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
2439aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
2440aca7a94dSNamhyung Kim 			}
2441aca7a94dSNamhyung Kim 			continue;
2442aca7a94dSNamhyung Kim 		case K_RIGHT:
2443aca7a94dSNamhyung Kim 		case K_ENTER:
2444aca7a94dSNamhyung Kim 			if (!menu->selection)
2445aca7a94dSNamhyung Kim 				continue;
2446aca7a94dSNamhyung Kim 			pos = menu->selection;
2447aca7a94dSNamhyung Kim browse_hists:
2448aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
2449aca7a94dSNamhyung Kim 			/*
2450aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
2451aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
2452aca7a94dSNamhyung Kim 			 */
24539783adf7SNamhyung Kim 			if (hbt)
24549783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
2455aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
2456dd00d486SJiri Olsa 						       true, hbt,
2457064f1981SNamhyung Kim 						       menu->min_pcnt,
245868d80758SNamhyung Kim 						       menu->env);
2459aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
2460aca7a94dSNamhyung Kim 			switch (key) {
2461aca7a94dSNamhyung Kim 			case K_TAB:
2462aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
24639a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
2464aca7a94dSNamhyung Kim 				else
24659a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
2466aca7a94dSNamhyung Kim 				goto browse_hists;
2467aca7a94dSNamhyung Kim 			case K_UNTAB:
2468aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
24699a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
2470aca7a94dSNamhyung Kim 				else
2471d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
2472aca7a94dSNamhyung Kim 				goto browse_hists;
2473341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
2474aca7a94dSNamhyung Kim 			case 'q':
2475aca7a94dSNamhyung Kim 			case CTRL('c'):
2476aca7a94dSNamhyung Kim 				goto out;
247763ab1749SArnaldo Carvalho de Melo 			case K_ESC:
2478aca7a94dSNamhyung Kim 			default:
2479aca7a94dSNamhyung Kim 				continue;
2480aca7a94dSNamhyung Kim 			}
2481aca7a94dSNamhyung Kim 		case K_LEFT:
2482aca7a94dSNamhyung Kim 			continue;
2483aca7a94dSNamhyung Kim 		case K_ESC:
2484aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
2485aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
2486aca7a94dSNamhyung Kim 				continue;
2487aca7a94dSNamhyung Kim 			/* Fall thru */
2488aca7a94dSNamhyung Kim 		case 'q':
2489aca7a94dSNamhyung Kim 		case CTRL('c'):
2490aca7a94dSNamhyung Kim 			goto out;
2491aca7a94dSNamhyung Kim 		default:
2492aca7a94dSNamhyung Kim 			continue;
2493aca7a94dSNamhyung Kim 		}
2494aca7a94dSNamhyung Kim 	}
2495aca7a94dSNamhyung Kim 
2496aca7a94dSNamhyung Kim out:
2497aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
2498aca7a94dSNamhyung Kim 	return key;
2499aca7a94dSNamhyung Kim }
2500aca7a94dSNamhyung Kim 
2501316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2502fc24d7c2SNamhyung Kim 				 void *entry)
2503fc24d7c2SNamhyung Kim {
2504fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2505fc24d7c2SNamhyung Kim 
2506fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2507fc24d7c2SNamhyung Kim 		return true;
2508fc24d7c2SNamhyung Kim 
2509fc24d7c2SNamhyung Kim 	return false;
2510fc24d7c2SNamhyung Kim }
2511fc24d7c2SNamhyung Kim 
2512aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2513fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
251468d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
2515064f1981SNamhyung Kim 					   float min_pcnt,
2516ce80d3beSKan Liang 					   struct perf_env *env)
2517aca7a94dSNamhyung Kim {
2518aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
2519aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
2520aca7a94dSNamhyung Kim 		.b = {
2521aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
2522aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
2523aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
2524aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
2525fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
2526fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
2527aca7a94dSNamhyung Kim 			.priv	    = evlist,
2528aca7a94dSNamhyung Kim 		},
2529064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
253068d80758SNamhyung Kim 		.env = env,
2531aca7a94dSNamhyung Kim 	};
2532aca7a94dSNamhyung Kim 
2533aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
2534aca7a94dSNamhyung Kim 
25350050f7aaSArnaldo Carvalho de Melo 	evlist__for_each(evlist, pos) {
25367289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
2537aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
2538aca7a94dSNamhyung Kim 
2539aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
2540aca7a94dSNamhyung Kim 			menu.b.width = line_len;
2541aca7a94dSNamhyung Kim 	}
2542aca7a94dSNamhyung Kim 
2543fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2544aca7a94dSNamhyung Kim }
2545aca7a94dSNamhyung Kim 
2546aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
254768d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
2548064f1981SNamhyung Kim 				  float min_pcnt,
2549ce80d3beSKan Liang 				  struct perf_env *env)
2550aca7a94dSNamhyung Kim {
2551fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
2552fc24d7c2SNamhyung Kim 
2553fc24d7c2SNamhyung Kim single_entry:
2554fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
25559a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
2556fc24d7c2SNamhyung Kim 
2557fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
2558dd00d486SJiri Olsa 						false, hbt, min_pcnt,
2559064f1981SNamhyung Kim 						env);
2560aca7a94dSNamhyung Kim 	}
2561aca7a94dSNamhyung Kim 
2562fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
2563fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
2564fc24d7c2SNamhyung Kim 
2565fc24d7c2SNamhyung Kim 		nr_entries = 0;
25660050f7aaSArnaldo Carvalho de Melo 		evlist__for_each(evlist, pos) {
2567fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
2568fc24d7c2SNamhyung Kim 				nr_entries++;
25690050f7aaSArnaldo Carvalho de Melo 		}
2570fc24d7c2SNamhyung Kim 
2571fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
2572fc24d7c2SNamhyung Kim 			goto single_entry;
2573fc24d7c2SNamhyung Kim 	}
2574fc24d7c2SNamhyung Kim 
2575fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2576064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
2577aca7a94dSNamhyung Kim }
2578