xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision f5b763fe)
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;
35*f5b763feSNamhyung Kim 	u64		     nr_hierarchy_entries;
36c3b78952SNamhyung Kim 	u64		     nr_callchain_rows;
37aca7a94dSNamhyung Kim };
38aca7a94dSNamhyung Kim 
39f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
40f5951d56SNamhyung Kim 
411e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
421e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
431e378ebdSTaeung Song 				char *bf, size_t size);
44112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
45aca7a94dSNamhyung Kim 
46c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
47c3b78952SNamhyung Kim 					     float min_pcnt);
48c3b78952SNamhyung Kim 
49268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
50268397cbSNamhyung Kim {
519c0fa8ddSArnaldo Carvalho de Melo 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
52268397cbSNamhyung Kim }
53268397cbSNamhyung Kim 
544fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
554fabf3d1SHe Kuang {
564fabf3d1SHe Kuang 	struct rb_node *nd;
574fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
584fabf3d1SHe Kuang 	int unfolded_rows = 0;
594fabf3d1SHe Kuang 
604fabf3d1SHe Kuang 	for (nd = rb_first(&hists->entries);
614fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
62*f5b763feSNamhyung Kim 	     nd = rb_hierarchy_next(nd)) {
634fabf3d1SHe Kuang 		struct hist_entry *he =
644fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
654fabf3d1SHe Kuang 
66*f5b763feSNamhyung Kim 		if (he->leaf && he->unfolded)
674fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
684fabf3d1SHe Kuang 	}
694fabf3d1SHe Kuang 	return unfolded_rows;
704fabf3d1SHe Kuang }
714fabf3d1SHe Kuang 
72c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
73c3b78952SNamhyung Kim {
74c3b78952SNamhyung Kim 	u32 nr_entries;
75c3b78952SNamhyung Kim 
76*f5b763feSNamhyung Kim 	if (symbol_conf.report_hierarchy)
77*f5b763feSNamhyung Kim 		nr_entries = hb->nr_hierarchy_entries;
78*f5b763feSNamhyung Kim 	else if (hist_browser__has_filter(hb))
79c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
80c3b78952SNamhyung Kim 	else
81c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
82c3b78952SNamhyung Kim 
834fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
84c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
85c3b78952SNamhyung Kim }
86c3b78952SNamhyung Kim 
87025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
88025bf7eaSArnaldo Carvalho de Melo {
89025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
90025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
91025bf7eaSArnaldo Carvalho de Melo 
92025bf7eaSArnaldo Carvalho de Melo 	browser->rows = browser->height - header_offset;
93025bf7eaSArnaldo Carvalho de Melo 	/*
94025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
95025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
96025bf7eaSArnaldo Carvalho de Melo 	 */
97025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
98025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
99025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
100025bf7eaSArnaldo Carvalho de Melo }
101025bf7eaSArnaldo Carvalho de Melo 
102357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
103aca7a94dSNamhyung Kim {
104357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
105357cfff1SArnaldo Carvalho de Melo 
106aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
107357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
108357cfff1SArnaldo Carvalho de Melo 	/*
109357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
110357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
111357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
112357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
113357cfff1SArnaldo Carvalho de Melo  	 */
114357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
115025bf7eaSArnaldo Carvalho de Melo 	hist_browser__update_rows(hb);
116aca7a94dSNamhyung Kim }
117aca7a94dSNamhyung Kim 
118ca3ff33bSArnaldo Carvalho de Melo static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
119ca3ff33bSArnaldo Carvalho de Melo {
120025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = browser->show_headers ? 1 : 0;
121025bf7eaSArnaldo Carvalho de Melo 
122025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row + header_offset, column);
123ca3ff33bSArnaldo Carvalho de Melo }
124ca3ff33bSArnaldo Carvalho de Melo 
12505e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
126aca7a94dSNamhyung Kim {
127c3b78952SNamhyung Kim 	/*
128c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
129c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
130c3b78952SNamhyung Kim 	 */
131c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
132c3b78952SNamhyung Kim 
133268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
134c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
135357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
13605e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
137aca7a94dSNamhyung Kim }
138aca7a94dSNamhyung Kim 
139aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
140aca7a94dSNamhyung Kim {
141aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
142aca7a94dSNamhyung Kim }
143aca7a94dSNamhyung Kim 
14405e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
145aca7a94dSNamhyung Kim {
1463698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
147aca7a94dSNamhyung Kim }
148aca7a94dSNamhyung Kim 
14905e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
150aca7a94dSNamhyung Kim {
1513698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
152aca7a94dSNamhyung Kim }
153aca7a94dSNamhyung Kim 
1543698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
155aca7a94dSNamhyung Kim {
1563698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
157aca7a94dSNamhyung Kim }
158aca7a94dSNamhyung Kim 
15905e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
160aca7a94dSNamhyung Kim {
161aca7a94dSNamhyung Kim 	int n = 0;
162aca7a94dSNamhyung Kim 	struct rb_node *nd;
163aca7a94dSNamhyung Kim 
16405e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
165aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
166aca7a94dSNamhyung Kim 		struct callchain_list *chain;
167aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
168aca7a94dSNamhyung Kim 
169aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
170aca7a94dSNamhyung Kim 			++n;
171aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
172aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
173aca7a94dSNamhyung Kim 			if (folded_sign == '+')
174aca7a94dSNamhyung Kim 				break;
175aca7a94dSNamhyung Kim 		}
176aca7a94dSNamhyung Kim 
177aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
178aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
179aca7a94dSNamhyung Kim 	}
180aca7a94dSNamhyung Kim 
181aca7a94dSNamhyung Kim 	return n;
182aca7a94dSNamhyung Kim }
183aca7a94dSNamhyung Kim 
1844b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
1854b3a3212SNamhyung Kim {
1864b3a3212SNamhyung Kim 	struct callchain_list *chain;
1874b3a3212SNamhyung Kim 	char folded_sign = 0;
1884b3a3212SNamhyung Kim 	int n = 0;
1894b3a3212SNamhyung Kim 
1904b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
1914b3a3212SNamhyung Kim 		if (!folded_sign) {
1924b3a3212SNamhyung Kim 			/* only check first chain list entry */
1934b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1944b3a3212SNamhyung Kim 			if (folded_sign == '+')
1954b3a3212SNamhyung Kim 				return 1;
1964b3a3212SNamhyung Kim 		}
1974b3a3212SNamhyung Kim 		n++;
1984b3a3212SNamhyung Kim 	}
1994b3a3212SNamhyung Kim 
2004b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
2014b3a3212SNamhyung Kim 		if (!folded_sign) {
2024b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
2034b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2044b3a3212SNamhyung Kim 			if (folded_sign == '+')
2054b3a3212SNamhyung Kim 				return 1;
2064b3a3212SNamhyung Kim 		}
2074b3a3212SNamhyung Kim 		n++;
2084b3a3212SNamhyung Kim 	}
2094b3a3212SNamhyung Kim 
2104b3a3212SNamhyung Kim 	return n;
2114b3a3212SNamhyung Kim }
2124b3a3212SNamhyung Kim 
2138c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
2148c430a34SNamhyung Kim {
2158c430a34SNamhyung Kim 	return 1;
2168c430a34SNamhyung Kim }
2178c430a34SNamhyung Kim 
218aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
219aca7a94dSNamhyung Kim {
220aca7a94dSNamhyung Kim 	struct callchain_list *chain;
221aca7a94dSNamhyung Kim 	bool unfolded = false;
222aca7a94dSNamhyung Kim 	int n = 0;
223aca7a94dSNamhyung Kim 
2244b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2254b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2268c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2278c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2284b3a3212SNamhyung Kim 
229aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
230aca7a94dSNamhyung Kim 		++n;
2313698dab1SNamhyung Kim 		unfolded = chain->unfolded;
232aca7a94dSNamhyung Kim 	}
233aca7a94dSNamhyung Kim 
234aca7a94dSNamhyung Kim 	if (unfolded)
235aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
236aca7a94dSNamhyung Kim 
237aca7a94dSNamhyung Kim 	return n;
238aca7a94dSNamhyung Kim }
239aca7a94dSNamhyung Kim 
240aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
241aca7a94dSNamhyung Kim {
242aca7a94dSNamhyung Kim 	struct rb_node *nd;
243aca7a94dSNamhyung Kim 	int n = 0;
244aca7a94dSNamhyung Kim 
245aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
246aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
247aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
248aca7a94dSNamhyung Kim 	}
249aca7a94dSNamhyung Kim 
250aca7a94dSNamhyung Kim 	return n;
251aca7a94dSNamhyung Kim }
252aca7a94dSNamhyung Kim 
253*f5b763feSNamhyung Kim static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
254*f5b763feSNamhyung Kim 				bool include_children)
255*f5b763feSNamhyung Kim {
256*f5b763feSNamhyung Kim 	int count = 0;
257*f5b763feSNamhyung Kim 	struct rb_node *node;
258*f5b763feSNamhyung Kim 	struct hist_entry *child;
259*f5b763feSNamhyung Kim 
260*f5b763feSNamhyung Kim 	if (he->leaf)
261*f5b763feSNamhyung Kim 		return callchain__count_rows(&he->sorted_chain);
262*f5b763feSNamhyung Kim 
263*f5b763feSNamhyung Kim 	node = rb_first(&he->hroot_out);
264*f5b763feSNamhyung Kim 	while (node) {
265*f5b763feSNamhyung Kim 		float percent;
266*f5b763feSNamhyung Kim 
267*f5b763feSNamhyung Kim 		child = rb_entry(node, struct hist_entry, rb_node);
268*f5b763feSNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
269*f5b763feSNamhyung Kim 
270*f5b763feSNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt) {
271*f5b763feSNamhyung Kim 			count++;
272*f5b763feSNamhyung Kim 
273*f5b763feSNamhyung Kim 			if (include_children && child->unfolded)
274*f5b763feSNamhyung Kim 				count += hierarchy_count_rows(hb, child, true);
275*f5b763feSNamhyung Kim 		}
276*f5b763feSNamhyung Kim 
277*f5b763feSNamhyung Kim 		node = rb_next(node);
278*f5b763feSNamhyung Kim 	}
279*f5b763feSNamhyung Kim 	return count;
280*f5b763feSNamhyung Kim }
281*f5b763feSNamhyung Kim 
2823698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
283aca7a94dSNamhyung Kim {
2843698dab1SNamhyung Kim 	if (!he)
285aca7a94dSNamhyung Kim 		return false;
286aca7a94dSNamhyung Kim 
2873698dab1SNamhyung Kim 	if (!he->has_children)
288aca7a94dSNamhyung Kim 		return false;
289aca7a94dSNamhyung Kim 
2903698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
2913698dab1SNamhyung Kim 	return true;
2923698dab1SNamhyung Kim }
2933698dab1SNamhyung Kim 
2943698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
2953698dab1SNamhyung Kim {
2963698dab1SNamhyung Kim 	if (!cl)
2973698dab1SNamhyung Kim 		return false;
2983698dab1SNamhyung Kim 
2993698dab1SNamhyung Kim 	if (!cl->has_children)
3003698dab1SNamhyung Kim 		return false;
3013698dab1SNamhyung Kim 
3023698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
303aca7a94dSNamhyung Kim 	return true;
304aca7a94dSNamhyung Kim }
305aca7a94dSNamhyung Kim 
30605e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
307aca7a94dSNamhyung Kim {
30805e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
309aca7a94dSNamhyung Kim 
31005e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
311aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
312aca7a94dSNamhyung Kim 		struct callchain_list *chain;
313aca7a94dSNamhyung Kim 		bool first = true;
314aca7a94dSNamhyung Kim 
315aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
316aca7a94dSNamhyung Kim 			if (first) {
317aca7a94dSNamhyung Kim 				first = false;
3183698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
319aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
320aca7a94dSNamhyung Kim 			} else
3213698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
322aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
323aca7a94dSNamhyung Kim 		}
324aca7a94dSNamhyung Kim 
325aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
326aca7a94dSNamhyung Kim 	}
327aca7a94dSNamhyung Kim }
328aca7a94dSNamhyung Kim 
329a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
330a7444af6SNamhyung Kim 					       bool has_sibling)
331aca7a94dSNamhyung Kim {
332aca7a94dSNamhyung Kim 	struct callchain_list *chain;
333aca7a94dSNamhyung Kim 
334a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3353698dab1SNamhyung Kim 	chain->has_children = has_sibling;
336a7444af6SNamhyung Kim 
3374b3a3212SNamhyung Kim 	if (node->val.next != node->val.prev) {
33882162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3393698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
34082162b5aSNamhyung Kim 	}
341aca7a94dSNamhyung Kim 
34205e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
343aca7a94dSNamhyung Kim }
344aca7a94dSNamhyung Kim 
34505e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
346aca7a94dSNamhyung Kim {
347a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
348a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
349aca7a94dSNamhyung Kim 
35005e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
351aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
352a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3538c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3548c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3554b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
356aca7a94dSNamhyung Kim 	}
357aca7a94dSNamhyung Kim }
358aca7a94dSNamhyung Kim 
35905e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
360aca7a94dSNamhyung Kim {
361*f5b763feSNamhyung Kim 	if (he->init_have_children)
362*f5b763feSNamhyung Kim 		return;
363*f5b763feSNamhyung Kim 
364*f5b763feSNamhyung Kim 	if (he->leaf) {
3653698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
36605e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
367*f5b763feSNamhyung Kim 	} else {
368*f5b763feSNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
369aca7a94dSNamhyung Kim 	}
370*f5b763feSNamhyung Kim 
371*f5b763feSNamhyung Kim 	he->init_have_children = true;
372aca7a94dSNamhyung Kim }
373aca7a94dSNamhyung Kim 
37405e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
375aca7a94dSNamhyung Kim {
37605e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
3773698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
3783698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
3793698dab1SNamhyung Kim 	bool has_children;
380aca7a94dSNamhyung Kim 
3814938cf0cSWang Nan 	if (!he || !ms)
3824938cf0cSWang Nan 		return false;
3834938cf0cSWang Nan 
3843698dab1SNamhyung Kim 	if (ms == &he->ms)
3853698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
3863698dab1SNamhyung Kim 	else
3873698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
3883698dab1SNamhyung Kim 
3893698dab1SNamhyung Kim 	if (has_children) {
390*f5b763feSNamhyung Kim 		int child_rows = 0;
391*f5b763feSNamhyung Kim 
392aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
393c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
394aca7a94dSNamhyung Kim 
395*f5b763feSNamhyung Kim 		if (he->leaf)
396*f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
397*f5b763feSNamhyung Kim 		else
398*f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
399*f5b763feSNamhyung Kim 
400*f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
401*f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
402*f5b763feSNamhyung Kim 
403*f5b763feSNamhyung Kim 		if (he->unfolded) {
404*f5b763feSNamhyung Kim 			if (he->leaf)
405aca7a94dSNamhyung Kim 				he->nr_rows = callchain__count_rows(&he->sorted_chain);
406aca7a94dSNamhyung Kim 			else
407*f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
408*f5b763feSNamhyung Kim 
409*f5b763feSNamhyung Kim 			/* account grand children */
410*f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
411*f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
412*f5b763feSNamhyung Kim 		} else {
413*f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
414*f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
415*f5b763feSNamhyung Kim 
416aca7a94dSNamhyung Kim 			he->nr_rows = 0;
417*f5b763feSNamhyung Kim 		}
418c3b78952SNamhyung Kim 
419c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
420*f5b763feSNamhyung Kim 
421*f5b763feSNamhyung Kim 		if (he->leaf)
422c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
423*f5b763feSNamhyung Kim 		else
424*f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
425aca7a94dSNamhyung Kim 
426aca7a94dSNamhyung Kim 		return true;
427aca7a94dSNamhyung Kim 	}
428aca7a94dSNamhyung Kim 
429aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
430aca7a94dSNamhyung Kim 	return false;
431aca7a94dSNamhyung Kim }
432aca7a94dSNamhyung Kim 
43305e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
434aca7a94dSNamhyung Kim {
435aca7a94dSNamhyung Kim 	int n = 0;
436aca7a94dSNamhyung Kim 	struct rb_node *nd;
437aca7a94dSNamhyung Kim 
43805e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
439aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
440aca7a94dSNamhyung Kim 		struct callchain_list *chain;
441aca7a94dSNamhyung Kim 		bool has_children = false;
442aca7a94dSNamhyung Kim 
443aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
444aca7a94dSNamhyung Kim 			++n;
4453698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
4463698dab1SNamhyung Kim 			has_children = chain->has_children;
447aca7a94dSNamhyung Kim 		}
448aca7a94dSNamhyung Kim 
449aca7a94dSNamhyung Kim 		if (has_children)
450aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
451aca7a94dSNamhyung Kim 	}
452aca7a94dSNamhyung Kim 
453aca7a94dSNamhyung Kim 	return n;
454aca7a94dSNamhyung Kim }
455aca7a94dSNamhyung Kim 
456aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
457aca7a94dSNamhyung Kim {
458aca7a94dSNamhyung Kim 	struct callchain_list *chain;
459aca7a94dSNamhyung Kim 	bool has_children = false;
460aca7a94dSNamhyung Kim 	int n = 0;
461aca7a94dSNamhyung Kim 
462aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
463aca7a94dSNamhyung Kim 		++n;
4643698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
4653698dab1SNamhyung Kim 		has_children = chain->has_children;
466aca7a94dSNamhyung Kim 	}
467aca7a94dSNamhyung Kim 
468aca7a94dSNamhyung Kim 	if (has_children)
469aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
470aca7a94dSNamhyung Kim 
471aca7a94dSNamhyung Kim 	return n;
472aca7a94dSNamhyung Kim }
473aca7a94dSNamhyung Kim 
474aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
475aca7a94dSNamhyung Kim {
476aca7a94dSNamhyung Kim 	struct rb_node *nd;
477aca7a94dSNamhyung Kim 	int n = 0;
478aca7a94dSNamhyung Kim 
479aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
480aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
481aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
482aca7a94dSNamhyung Kim 	}
483aca7a94dSNamhyung Kim 
484aca7a94dSNamhyung Kim 	return n;
485aca7a94dSNamhyung Kim }
486aca7a94dSNamhyung Kim 
48705e8b080SArnaldo Carvalho de Melo static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
488aca7a94dSNamhyung Kim {
48905e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
4903698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
491aca7a94dSNamhyung Kim 
4923698dab1SNamhyung Kim 	if (he->has_children) {
49305e8b080SArnaldo Carvalho de Melo 		int n = callchain__set_folding(&he->sorted_chain, unfold);
49405e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
495aca7a94dSNamhyung Kim 	} else
49605e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
497aca7a94dSNamhyung Kim }
498aca7a94dSNamhyung Kim 
499c3b78952SNamhyung Kim static void
500c3b78952SNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
501aca7a94dSNamhyung Kim {
502aca7a94dSNamhyung Kim 	struct rb_node *nd;
503c3b78952SNamhyung Kim 	struct hists *hists = browser->hists;
504aca7a94dSNamhyung Kim 
505c3b78952SNamhyung Kim 	for (nd = rb_first(&hists->entries);
50614135663SNamhyung Kim 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
507c3b78952SNamhyung Kim 	     nd = rb_next(nd)) {
508aca7a94dSNamhyung Kim 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
509aca7a94dSNamhyung Kim 		hist_entry__set_folding(he, unfold);
510c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
511aca7a94dSNamhyung Kim 	}
512aca7a94dSNamhyung Kim }
513aca7a94dSNamhyung Kim 
51405e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
515aca7a94dSNamhyung Kim {
516c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
517c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
518c3b78952SNamhyung Kim 
519c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
520aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
52105e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
522aca7a94dSNamhyung Kim }
523aca7a94dSNamhyung Kim 
524aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
525aca7a94dSNamhyung Kim {
526aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
527aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
528aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
529aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
530aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
531aca7a94dSNamhyung Kim }
532aca7a94dSNamhyung Kim 
5335f00b0f4SArnaldo Carvalho de Melo static int hist_browser__run(struct hist_browser *browser, const char *help)
534aca7a94dSNamhyung Kim {
535aca7a94dSNamhyung Kim 	int key;
536aca7a94dSNamhyung Kim 	char title[160];
537c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
5389783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
539aca7a94dSNamhyung Kim 
54005e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
541c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
542aca7a94dSNamhyung Kim 
5431e378ebdSTaeung Song 	hists__browser_title(browser->hists, hbt, title, sizeof(title));
544aca7a94dSNamhyung Kim 
545090cff3eSNamhyung Kim 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
546aca7a94dSNamhyung Kim 		return -1;
547aca7a94dSNamhyung Kim 
548aca7a94dSNamhyung Kim 	while (1) {
54905e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
550aca7a94dSNamhyung Kim 
551aca7a94dSNamhyung Kim 		switch (key) {
552fa5df943SNamhyung Kim 		case K_TIMER: {
553fa5df943SNamhyung Kim 			u64 nr_entries;
5549783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
555fa5df943SNamhyung Kim 
556c3b78952SNamhyung Kim 			if (hist_browser__has_filter(browser))
557112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
558fa5df943SNamhyung Kim 
559c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
560fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
561aca7a94dSNamhyung Kim 
56205e8b080SArnaldo Carvalho de Melo 			if (browser->hists->stats.nr_lost_warned !=
56305e8b080SArnaldo Carvalho de Melo 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
56405e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
56505e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
56605e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
567aca7a94dSNamhyung Kim 			}
568aca7a94dSNamhyung Kim 
5691e378ebdSTaeung Song 			hists__browser_title(browser->hists,
5701e378ebdSTaeung Song 					     hbt, title, sizeof(title));
57105e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
572aca7a94dSNamhyung Kim 			continue;
573fa5df943SNamhyung Kim 		}
574aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
575aca7a94dSNamhyung Kim 			static int seq;
57605e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
577aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
578aca7a94dSNamhyung Kim 			ui_helpline__pop();
57962c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
58005e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
58105e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
58262c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
58305e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
58405e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
585aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
586aca7a94dSNamhyung Kim 		}
587aca7a94dSNamhyung Kim 			break;
588aca7a94dSNamhyung Kim 		case 'C':
589aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
59005e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
591aca7a94dSNamhyung Kim 			break;
592aca7a94dSNamhyung Kim 		case 'E':
593aca7a94dSNamhyung Kim 			/* Expand the whole world. */
59405e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
595aca7a94dSNamhyung Kim 			break;
596025bf7eaSArnaldo Carvalho de Melo 		case 'H':
597025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
598025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
599025bf7eaSArnaldo Carvalho de Melo 			break;
600aca7a94dSNamhyung Kim 		case K_ENTER:
60105e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
602aca7a94dSNamhyung Kim 				break;
603aca7a94dSNamhyung Kim 			/* fall thru */
604aca7a94dSNamhyung Kim 		default:
605aca7a94dSNamhyung Kim 			goto out;
606aca7a94dSNamhyung Kim 		}
607aca7a94dSNamhyung Kim 	}
608aca7a94dSNamhyung Kim out:
60905e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
610aca7a94dSNamhyung Kim 	return key;
611aca7a94dSNamhyung Kim }
612aca7a94dSNamhyung Kim 
61339ee533fSNamhyung Kim struct callchain_print_arg {
61439ee533fSNamhyung Kim 	/* for hists browser */
61539ee533fSNamhyung Kim 	off_t	row_offset;
61639ee533fSNamhyung Kim 	bool	is_current_entry;
61739ee533fSNamhyung Kim 
61839ee533fSNamhyung Kim 	/* for file dump */
61939ee533fSNamhyung Kim 	FILE	*fp;
62039ee533fSNamhyung Kim 	int	printed;
62139ee533fSNamhyung Kim };
62239ee533fSNamhyung Kim 
62339ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
62439ee533fSNamhyung Kim 					 struct callchain_list *chain,
62539ee533fSNamhyung Kim 					 const char *str, int offset,
62639ee533fSNamhyung Kim 					 unsigned short row,
62739ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
62839ee533fSNamhyung Kim 
629f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
630f4536dddSNamhyung Kim 					       struct callchain_list *chain,
63139ee533fSNamhyung Kim 					       const char *str, int offset,
63239ee533fSNamhyung Kim 					       unsigned short row,
63339ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
634f4536dddSNamhyung Kim {
635f4536dddSNamhyung Kim 	int color, width;
63639ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
63770e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
638f4536dddSNamhyung Kim 
639f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
640f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
641f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
642f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
643f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
64439ee533fSNamhyung Kim 		arg->is_current_entry = true;
645f4536dddSNamhyung Kim 	}
646f4536dddSNamhyung Kim 
647f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
648f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
64926270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
650517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
65170e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
65226270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
653f4536dddSNamhyung Kim }
654f4536dddSNamhyung Kim 
65539ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
65639ee533fSNamhyung Kim 						  struct callchain_list *chain,
65739ee533fSNamhyung Kim 						  const char *str, int offset,
65839ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
65939ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
66039ee533fSNamhyung Kim {
66139ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
66239ee533fSNamhyung Kim 
66339ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
66439ee533fSNamhyung Kim 				folded_sign, str);
66539ee533fSNamhyung Kim }
66639ee533fSNamhyung Kim 
66739ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
66839ee533fSNamhyung Kim 				     unsigned short row);
66939ee533fSNamhyung Kim 
67039ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
67139ee533fSNamhyung Kim 					    unsigned short row)
67239ee533fSNamhyung Kim {
67339ee533fSNamhyung Kim 	return browser->b.rows == row;
67439ee533fSNamhyung Kim }
67539ee533fSNamhyung Kim 
67639ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
67739ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
67839ee533fSNamhyung Kim {
67939ee533fSNamhyung Kim 	return false;
68039ee533fSNamhyung Kim }
68139ee533fSNamhyung Kim 
682aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
683aca7a94dSNamhyung Kim 
68418bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
68518bb8381SNamhyung Kim 					     struct callchain_node *node,
68618bb8381SNamhyung Kim 					     struct callchain_list *chain,
68718bb8381SNamhyung Kim 					     unsigned short row, u64 total,
68818bb8381SNamhyung Kim 					     bool need_percent, int offset,
68918bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
69018bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
69118bb8381SNamhyung Kim {
69218bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
69318bb8381SNamhyung Kim 	const char *str;
69418bb8381SNamhyung Kim 
69518bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
69618bb8381SNamhyung Kim 		arg->row_offset--;
69718bb8381SNamhyung Kim 		return 0;
69818bb8381SNamhyung Kim 	}
69918bb8381SNamhyung Kim 
70018bb8381SNamhyung Kim 	alloc_str = NULL;
70118bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
70218bb8381SNamhyung Kim 				       browser->show_dso);
70318bb8381SNamhyung Kim 
70418bb8381SNamhyung Kim 	if (need_percent) {
70518bb8381SNamhyung Kim 		char buf[64];
70618bb8381SNamhyung Kim 
70718bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
70818bb8381SNamhyung Kim 						total);
70918bb8381SNamhyung Kim 
71018bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
71118bb8381SNamhyung Kim 			str = "Not enough memory!";
71218bb8381SNamhyung Kim 		else
71318bb8381SNamhyung Kim 			str = alloc_str;
71418bb8381SNamhyung Kim 	}
71518bb8381SNamhyung Kim 
71618bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
71718bb8381SNamhyung Kim 
71818bb8381SNamhyung Kim 	free(alloc_str);
71918bb8381SNamhyung Kim 	return 1;
72018bb8381SNamhyung Kim }
72118bb8381SNamhyung Kim 
72259c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
72359c624e2SNamhyung Kim {
72459c624e2SNamhyung Kim 	struct callchain_node *child;
72559c624e2SNamhyung Kim 
72659c624e2SNamhyung Kim 	if (node == NULL)
72759c624e2SNamhyung Kim 		return false;
72859c624e2SNamhyung Kim 
72959c624e2SNamhyung Kim 	if (rb_next(node))
73059c624e2SNamhyung Kim 		return true;
73159c624e2SNamhyung Kim 
73259c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
73359c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
73459c624e2SNamhyung Kim }
73559c624e2SNamhyung Kim 
7364b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
7374b3a3212SNamhyung Kim 					     struct rb_root *root,
7384b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
73959c624e2SNamhyung Kim 					     u64 parent_total,
7404b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
7414b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
7424b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
7434b3a3212SNamhyung Kim {
7444b3a3212SNamhyung Kim 	struct rb_node *node;
7454b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
7464b3a3212SNamhyung Kim 	bool need_percent;
7474b3a3212SNamhyung Kim 
7484b3a3212SNamhyung Kim 	node = rb_first(root);
74959c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
7504b3a3212SNamhyung Kim 
7514b3a3212SNamhyung Kim 	while (node) {
7524b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
7534b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
7544b3a3212SNamhyung Kim 		struct callchain_list *chain;
7554b3a3212SNamhyung Kim 		char folded_sign = ' ';
7564b3a3212SNamhyung Kim 		int first = true;
7574b3a3212SNamhyung Kim 		int extra_offset = 0;
7584b3a3212SNamhyung Kim 
7594b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
7604b3a3212SNamhyung Kim 			bool was_first = first;
7614b3a3212SNamhyung Kim 
7624b3a3212SNamhyung Kim 			if (first)
7634b3a3212SNamhyung Kim 				first = false;
7644b3a3212SNamhyung Kim 			else if (need_percent)
7654b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
7664b3a3212SNamhyung Kim 
7674b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
7684b3a3212SNamhyung Kim 
7694b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
7704b3a3212SNamhyung Kim 							chain, row, total,
7714b3a3212SNamhyung Kim 							was_first && need_percent,
7724b3a3212SNamhyung Kim 							offset + extra_offset,
7734b3a3212SNamhyung Kim 							print, arg);
7744b3a3212SNamhyung Kim 
7754b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
7764b3a3212SNamhyung Kim 				goto out;
7774b3a3212SNamhyung Kim 
7784b3a3212SNamhyung Kim 			if (folded_sign == '+')
7794b3a3212SNamhyung Kim 				goto next;
7804b3a3212SNamhyung Kim 		}
7814b3a3212SNamhyung Kim 
7824b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
7834b3a3212SNamhyung Kim 			bool was_first = first;
7844b3a3212SNamhyung Kim 
7854b3a3212SNamhyung Kim 			if (first)
7864b3a3212SNamhyung Kim 				first = false;
7874b3a3212SNamhyung Kim 			else if (need_percent)
7884b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
7894b3a3212SNamhyung Kim 
7904b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
7914b3a3212SNamhyung Kim 
7924b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
7934b3a3212SNamhyung Kim 							chain, row, total,
7944b3a3212SNamhyung Kim 							was_first && need_percent,
7954b3a3212SNamhyung Kim 							offset + extra_offset,
7964b3a3212SNamhyung Kim 							print, arg);
7974b3a3212SNamhyung Kim 
7984b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
7994b3a3212SNamhyung Kim 				goto out;
8004b3a3212SNamhyung Kim 
8014b3a3212SNamhyung Kim 			if (folded_sign == '+')
8024b3a3212SNamhyung Kim 				break;
8034b3a3212SNamhyung Kim 		}
8044b3a3212SNamhyung Kim 
8054b3a3212SNamhyung Kim next:
8064b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
8074b3a3212SNamhyung Kim 			break;
8084b3a3212SNamhyung Kim 		node = next;
8094b3a3212SNamhyung Kim 	}
8104b3a3212SNamhyung Kim out:
8114b3a3212SNamhyung Kim 	return row - first_row;
8124b3a3212SNamhyung Kim }
8134b3a3212SNamhyung Kim 
8148c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
8158c430a34SNamhyung Kim 						struct callchain_list *chain,
8168c430a34SNamhyung Kim 						char *value_str, char *old_str)
8178c430a34SNamhyung Kim {
8188c430a34SNamhyung Kim 	char bf[1024];
8198c430a34SNamhyung Kim 	const char *str;
8208c430a34SNamhyung Kim 	char *new;
8218c430a34SNamhyung Kim 
8228c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
8238c430a34SNamhyung Kim 				       browser->show_dso);
8248c430a34SNamhyung Kim 	if (old_str) {
8258c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
8268c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
8278c430a34SNamhyung Kim 			new = NULL;
8288c430a34SNamhyung Kim 	} else {
8298c430a34SNamhyung Kim 		if (value_str) {
8308c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
8318c430a34SNamhyung Kim 				new = NULL;
8328c430a34SNamhyung Kim 		} else {
8338c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
8348c430a34SNamhyung Kim 				new = NULL;
8358c430a34SNamhyung Kim 		}
8368c430a34SNamhyung Kim 	}
8378c430a34SNamhyung Kim 	return new;
8388c430a34SNamhyung Kim }
8398c430a34SNamhyung Kim 
8408c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
8418c430a34SNamhyung Kim 					       struct rb_root *root,
8428c430a34SNamhyung Kim 					       unsigned short row, u64 total,
84359c624e2SNamhyung Kim 					       u64 parent_total,
8448c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
8458c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
8468c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
8478c430a34SNamhyung Kim {
8488c430a34SNamhyung Kim 	struct rb_node *node;
8498c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
8508c430a34SNamhyung Kim 	bool need_percent;
8518c430a34SNamhyung Kim 
8528c430a34SNamhyung Kim 	node = rb_first(root);
85359c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
8548c430a34SNamhyung Kim 
8558c430a34SNamhyung Kim 	while (node) {
8568c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
8578c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
8588c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
8598c430a34SNamhyung Kim 		int first = true;
8608c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
8618c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
8628c430a34SNamhyung Kim 
8638c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
8648c430a34SNamhyung Kim 			arg->row_offset--;
8658c430a34SNamhyung Kim 			goto next;
8668c430a34SNamhyung Kim 		}
8678c430a34SNamhyung Kim 
8688c430a34SNamhyung Kim 		if (need_percent) {
8698c430a34SNamhyung Kim 			char buf[64];
8708c430a34SNamhyung Kim 
8718c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
8728c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
8738c430a34SNamhyung Kim 				value_str = (char *)"<...>";
8748c430a34SNamhyung Kim 				goto do_print;
8758c430a34SNamhyung Kim 			}
8768c430a34SNamhyung Kim 			value_str_alloc = value_str;
8778c430a34SNamhyung Kim 		}
8788c430a34SNamhyung Kim 
8798c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
8808c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
8818c430a34SNamhyung Kim 						chain, value_str, chain_str);
8828c430a34SNamhyung Kim 			if (first) {
8838c430a34SNamhyung Kim 				first = false;
8848c430a34SNamhyung Kim 				first_chain = chain;
8858c430a34SNamhyung Kim 			}
8868c430a34SNamhyung Kim 
8878c430a34SNamhyung Kim 			if (chain_str == NULL) {
8888c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
8898c430a34SNamhyung Kim 				goto do_print;
8908c430a34SNamhyung Kim 			}
8918c430a34SNamhyung Kim 
8928c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
8938c430a34SNamhyung Kim 		}
8948c430a34SNamhyung Kim 
8958c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
8968c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
8978c430a34SNamhyung Kim 						chain, value_str, chain_str);
8988c430a34SNamhyung Kim 			if (first) {
8998c430a34SNamhyung Kim 				first = false;
9008c430a34SNamhyung Kim 				first_chain = chain;
9018c430a34SNamhyung Kim 			}
9028c430a34SNamhyung Kim 
9038c430a34SNamhyung Kim 			if (chain_str == NULL) {
9048c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9058c430a34SNamhyung Kim 				goto do_print;
9068c430a34SNamhyung Kim 			}
9078c430a34SNamhyung Kim 
9088c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9098c430a34SNamhyung Kim 		}
9108c430a34SNamhyung Kim 
9118c430a34SNamhyung Kim do_print:
9128c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
9138c430a34SNamhyung Kim 		free(value_str_alloc);
9148c430a34SNamhyung Kim 		free(chain_str_alloc);
9158c430a34SNamhyung Kim 
9168c430a34SNamhyung Kim next:
9178c430a34SNamhyung Kim 		if (is_output_full(browser, row))
9188c430a34SNamhyung Kim 			break;
9198c430a34SNamhyung Kim 		node = next;
9208c430a34SNamhyung Kim 	}
9218c430a34SNamhyung Kim 
9228c430a34SNamhyung Kim 	return row - first_row;
9238c430a34SNamhyung Kim }
9248c430a34SNamhyung Kim 
9250c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
926c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
92739ee533fSNamhyung Kim 					unsigned short row, u64 total,
9285eca104eSNamhyung Kim 					u64 parent_total,
92939ee533fSNamhyung Kim 					print_callchain_entry_fn print,
93039ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
93139ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
932aca7a94dSNamhyung Kim {
933aca7a94dSNamhyung Kim 	struct rb_node *node;
934f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
9354087d11cSNamhyung Kim 	bool need_percent;
9365eca104eSNamhyung Kim 	u64 percent_total = total;
9375eca104eSNamhyung Kim 
9385eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
9395eca104eSNamhyung Kim 		percent_total = parent_total;
940aca7a94dSNamhyung Kim 
941c09a7e75SNamhyung Kim 	node = rb_first(root);
94259c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9434087d11cSNamhyung Kim 
944aca7a94dSNamhyung Kim 	while (node) {
945aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
946aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
947aca7a94dSNamhyung Kim 		struct callchain_list *chain;
948aca7a94dSNamhyung Kim 		char folded_sign = ' ';
949aca7a94dSNamhyung Kim 		int first = true;
950aca7a94dSNamhyung Kim 		int extra_offset = 0;
951aca7a94dSNamhyung Kim 
952aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
953aca7a94dSNamhyung Kim 			bool was_first = first;
954aca7a94dSNamhyung Kim 
955aca7a94dSNamhyung Kim 			if (first)
956aca7a94dSNamhyung Kim 				first = false;
9574087d11cSNamhyung Kim 			else if (need_percent)
958aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
959aca7a94dSNamhyung Kim 
960aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
961aca7a94dSNamhyung Kim 
96218bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
9635eca104eSNamhyung Kim 							chain, row, percent_total,
96418bb8381SNamhyung Kim 							was_first && need_percent,
96518bb8381SNamhyung Kim 							offset + extra_offset,
96618bb8381SNamhyung Kim 							print, arg);
967c09a7e75SNamhyung Kim 
96818bb8381SNamhyung Kim 			if (is_output_full(browser, row))
969aca7a94dSNamhyung Kim 				goto out;
97018bb8381SNamhyung Kim 
971aca7a94dSNamhyung Kim 			if (folded_sign == '+')
972aca7a94dSNamhyung Kim 				break;
973aca7a94dSNamhyung Kim 		}
974aca7a94dSNamhyung Kim 
975aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
976aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
977c09a7e75SNamhyung Kim 
9780c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
9795eca104eSNamhyung Kim 							    new_level, row, total,
9805eca104eSNamhyung Kim 							    child->children_hit,
98139ee533fSNamhyung Kim 							    print, arg, is_output_full);
982aca7a94dSNamhyung Kim 		}
98339ee533fSNamhyung Kim 		if (is_output_full(browser, row))
984c09a7e75SNamhyung Kim 			break;
985aca7a94dSNamhyung Kim 		node = next;
986aca7a94dSNamhyung Kim 	}
987aca7a94dSNamhyung Kim out:
988aca7a94dSNamhyung Kim 	return row - first_row;
989aca7a94dSNamhyung Kim }
990aca7a94dSNamhyung Kim 
9910c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
9920c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
9930c841c6cSNamhyung Kim 					unsigned short row,
9940c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
9950c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
9960c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
9970c841c6cSNamhyung Kim {
9980c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
9995eca104eSNamhyung Kim 	u64 parent_total;
10000c841c6cSNamhyung Kim 	int printed;
10010c841c6cSNamhyung Kim 
10020c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
10035eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
10040c841c6cSNamhyung Kim 	else
10055eca104eSNamhyung Kim 		parent_total = entry->stat.period;
10060c841c6cSNamhyung Kim 
10070c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
10080c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
10095eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10105eca104eSNamhyung Kim 						total, parent_total, print, arg,
10115eca104eSNamhyung Kim 						is_output_full);
10120c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
10130c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
10145eca104eSNamhyung Kim 						&entry->sorted_chain, row,
10155eca104eSNamhyung Kim 						total, parent_total, print, arg,
10165eca104eSNamhyung Kim 						is_output_full);
10170c841c6cSNamhyung Kim 	} else {
10180c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
10195eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
10205eca104eSNamhyung Kim 						total, parent_total, print, arg,
10215eca104eSNamhyung Kim 						is_output_full);
10220c841c6cSNamhyung Kim 	}
10230c841c6cSNamhyung Kim 
10240c841c6cSNamhyung Kim 	if (arg->is_current_entry)
10250c841c6cSNamhyung Kim 		browser->he_selection = entry;
10260c841c6cSNamhyung Kim 
10270c841c6cSNamhyung Kim 	return printed;
10280c841c6cSNamhyung Kim }
10290c841c6cSNamhyung Kim 
103089701460SNamhyung Kim struct hpp_arg {
103189701460SNamhyung Kim 	struct ui_browser *b;
103289701460SNamhyung Kim 	char folded_sign;
103389701460SNamhyung Kim 	bool current_entry;
103489701460SNamhyung Kim };
103589701460SNamhyung Kim 
10362f6d9009SNamhyung Kim static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
10372f6d9009SNamhyung Kim {
10382f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1039d675107cSNamhyung Kim 	int ret, len;
10402f6d9009SNamhyung Kim 	va_list args;
10412f6d9009SNamhyung Kim 	double percent;
10422f6d9009SNamhyung Kim 
10432f6d9009SNamhyung Kim 	va_start(args, fmt);
1044d675107cSNamhyung Kim 	len = va_arg(args, int);
10452f6d9009SNamhyung Kim 	percent = va_arg(args, double);
10462f6d9009SNamhyung Kim 	va_end(args);
10475aed9d24SNamhyung Kim 
104889701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
10495aed9d24SNamhyung Kim 
1050d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1051517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
105289701460SNamhyung Kim 
10532f6d9009SNamhyung Kim 	advance_hpp(hpp, ret);
10545aed9d24SNamhyung Kim 	return ret;
1055f5951d56SNamhyung Kim }
1056f5951d56SNamhyung Kim 
1057fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
10585aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
10595aed9d24SNamhyung Kim {									\
10605aed9d24SNamhyung Kim 	return he->stat._field;						\
10615aed9d24SNamhyung Kim }									\
10625aed9d24SNamhyung Kim 									\
10632c5d4b4aSJiri Olsa static int								\
10645b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
10652c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
10665aed9d24SNamhyung Kim 				struct hist_entry *he)			\
10675aed9d24SNamhyung Kim {									\
10685b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
10692f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
10705aed9d24SNamhyung Kim }
1071f5951d56SNamhyung Kim 
10720434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
10730434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
10740434ddd2SNamhyung Kim {									\
10750434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
10760434ddd2SNamhyung Kim }									\
10770434ddd2SNamhyung Kim 									\
10780434ddd2SNamhyung Kim static int								\
10795b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
10800434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
10810434ddd2SNamhyung Kim 				struct hist_entry *he)			\
10820434ddd2SNamhyung Kim {									\
10830434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1084517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
10855b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1086d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
10875b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1088517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
10890434ddd2SNamhyung Kim 									\
10900434ddd2SNamhyung Kim 		return ret;						\
10910434ddd2SNamhyung Kim 	}								\
10925b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
10935b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
10940434ddd2SNamhyung Kim }
10950434ddd2SNamhyung Kim 
1096fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1097fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1098fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1099fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1100fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
11010434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
11025aed9d24SNamhyung Kim 
11035aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
11040434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1105f5951d56SNamhyung Kim 
1106f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1107f5951d56SNamhyung Kim {
1108f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1109f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1110f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1111f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1112f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1113f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1114f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1115f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1116f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1117f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
11180434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
11190434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
1120f5951d56SNamhyung Kim }
1121f5951d56SNamhyung Kim 
112205e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1123aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1124aca7a94dSNamhyung Kim 				    unsigned short row)
1125aca7a94dSNamhyung Kim {
11261240005eSJiri Olsa 	int printed = 0;
112767d25916SNamhyung Kim 	int width = browser->b.width;
1128aca7a94dSNamhyung Kim 	char folded_sign = ' ';
112905e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1130aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
113163a1a3d8SNamhyung Kim 	bool first = true;
11321240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1133aca7a94dSNamhyung Kim 
1134aca7a94dSNamhyung Kim 	if (current_entry) {
113505e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
113605e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1137aca7a94dSNamhyung Kim 	}
1138aca7a94dSNamhyung Kim 
1139aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
1140aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1141aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1142aca7a94dSNamhyung Kim 	}
1143aca7a94dSNamhyung Kim 
1144aca7a94dSNamhyung Kim 	if (row_offset == 0) {
114589701460SNamhyung Kim 		struct hpp_arg arg = {
114689701460SNamhyung Kim 			.b		= &browser->b,
114789701460SNamhyung Kim 			.folded_sign	= folded_sign,
114889701460SNamhyung Kim 			.current_entry	= current_entry,
114989701460SNamhyung Kim 		};
1150c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1151f5951d56SNamhyung Kim 
1152ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
1153f5951d56SNamhyung Kim 
1154f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
115589fee709SArnaldo Carvalho de Melo 			char s[2048];
115689fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
115789fee709SArnaldo Carvalho de Melo 				.buf	= s,
115889fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
115989fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
116089fee709SArnaldo Carvalho de Melo 			};
116189fee709SArnaldo Carvalho de Melo 
1162361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1163361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1164e67d49a7SNamhyung Kim 				continue;
1165e67d49a7SNamhyung Kim 
1166fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1167fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1168fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1169fb821c9eSNamhyung Kim 			} else {
1170fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1171fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1172fb821c9eSNamhyung Kim 			}
1173fb821c9eSNamhyung Kim 
1174fb821c9eSNamhyung Kim 			if (first) {
1175fb821c9eSNamhyung Kim 				if (symbol_conf.use_callchain) {
1176517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1177f5951d56SNamhyung Kim 					width -= 2;
1178f5951d56SNamhyung Kim 				}
117963a1a3d8SNamhyung Kim 				first = false;
1180fb821c9eSNamhyung Kim 			} else {
1181517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1182fb821c9eSNamhyung Kim 				width -= 2;
1183fb821c9eSNamhyung Kim 			}
1184f5951d56SNamhyung Kim 
11851240005eSJiri Olsa 			if (fmt->color) {
118689fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
118789fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
118889fee709SArnaldo Carvalho de Melo 				/*
118989fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
119089fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
119189fee709SArnaldo Carvalho de Melo 				 */
119289fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1193f5951d56SNamhyung Kim 			} else {
119489fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1195517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1196f5951d56SNamhyung Kim 			}
119789fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1198f5951d56SNamhyung Kim 		}
1199aca7a94dSNamhyung Kim 
1200aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
120105e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1202aca7a94dSNamhyung Kim 			width += 1;
1203aca7a94dSNamhyung Kim 
120426270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
120526d8b338SNamhyung Kim 
1206aca7a94dSNamhyung Kim 		++row;
1207aca7a94dSNamhyung Kim 		++printed;
1208aca7a94dSNamhyung Kim 	} else
1209aca7a94dSNamhyung Kim 		--row_offset;
1210aca7a94dSNamhyung Kim 
121162c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
121239ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
121339ee533fSNamhyung Kim 			.row_offset = row_offset,
121439ee533fSNamhyung Kim 			.is_current_entry = current_entry,
121539ee533fSNamhyung Kim 		};
1216c09a7e75SNamhyung Kim 
12170c841c6cSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry, 1, row,
12184b3a3212SNamhyung Kim 					hist_browser__show_callchain_entry, &arg,
12194b3a3212SNamhyung Kim 					hist_browser__check_output_full);
1220aca7a94dSNamhyung Kim 	}
1221aca7a94dSNamhyung Kim 
1222aca7a94dSNamhyung Kim 	return printed;
1223aca7a94dSNamhyung Kim }
1224aca7a94dSNamhyung Kim 
122581a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
122681a888feSJiri Olsa {
122781a888feSJiri Olsa 	advance_hpp(hpp, inc);
122881a888feSJiri Olsa 	return hpp->size <= 0;
122981a888feSJiri Olsa }
123081a888feSJiri Olsa 
1231c6c3c02dSArnaldo Carvalho de Melo static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
123281a888feSJiri Olsa {
1233c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
123481a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
123581a888feSJiri Olsa 		.buf    = buf,
123681a888feSJiri Olsa 		.size   = size,
123781a888feSJiri Olsa 	};
123881a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
123981a888feSJiri Olsa 	size_t ret = 0;
1240c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
124181a888feSJiri Olsa 
124281a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
124381a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
124481a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
124581a888feSJiri Olsa 			return ret;
124681a888feSJiri Olsa 	}
124781a888feSJiri Olsa 
1248f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1249361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
125081a888feSJiri Olsa 			continue;
125181a888feSJiri Olsa 
125281a888feSJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
125381a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
125481a888feSJiri Olsa 			break;
125581a888feSJiri Olsa 
125681a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
125781a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
125881a888feSJiri Olsa 			break;
125981a888feSJiri Olsa 	}
126081a888feSJiri Olsa 
126181a888feSJiri Olsa 	return ret;
126281a888feSJiri Olsa }
126381a888feSJiri Olsa 
1264025bf7eaSArnaldo Carvalho de Melo static void hist_browser__show_headers(struct hist_browser *browser)
1265025bf7eaSArnaldo Carvalho de Melo {
126681a888feSJiri Olsa 	char headers[1024];
126781a888feSJiri Olsa 
1268c6c3c02dSArnaldo Carvalho de Melo 	hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
1269025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1270025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
127126270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1272025bf7eaSArnaldo Carvalho de Melo }
1273025bf7eaSArnaldo Carvalho de Melo 
1274aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1275aca7a94dSNamhyung Kim {
1276aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1277aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1278aca7a94dSNamhyung Kim 
1279aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
1280aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
1281aca7a94dSNamhyung Kim 	}
1282aca7a94dSNamhyung Kim }
1283aca7a94dSNamhyung Kim 
128405e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1285aca7a94dSNamhyung Kim {
1286aca7a94dSNamhyung Kim 	unsigned row = 0;
1287025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
1288aca7a94dSNamhyung Kim 	struct rb_node *nd;
128905e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1290aca7a94dSNamhyung Kim 
1291025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
1292025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1293025bf7eaSArnaldo Carvalho de Melo 		header_offset = 1;
1294025bf7eaSArnaldo Carvalho de Melo 	}
1295025bf7eaSArnaldo Carvalho de Melo 
129605e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1297979d2cacSWang Nan 	hb->he_selection = NULL;
1298979d2cacSWang Nan 	hb->selection = NULL;
1299aca7a94dSNamhyung Kim 
130005e8b080SArnaldo Carvalho de Melo 	for (nd = browser->top; nd; nd = rb_next(nd)) {
1301aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
130214135663SNamhyung Kim 		float percent;
1303aca7a94dSNamhyung Kim 
1304aca7a94dSNamhyung Kim 		if (h->filtered)
1305aca7a94dSNamhyung Kim 			continue;
1306aca7a94dSNamhyung Kim 
130714135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
1308064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1309064f1981SNamhyung Kim 			continue;
1310064f1981SNamhyung Kim 
1311aca7a94dSNamhyung Kim 		row += hist_browser__show_entry(hb, h, row);
131262c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1313aca7a94dSNamhyung Kim 			break;
1314aca7a94dSNamhyung Kim 	}
1315aca7a94dSNamhyung Kim 
1316025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
1317aca7a94dSNamhyung Kim }
1318aca7a94dSNamhyung Kim 
1319064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1320064f1981SNamhyung Kim 					     float min_pcnt)
1321aca7a94dSNamhyung Kim {
1322aca7a94dSNamhyung Kim 	while (nd != NULL) {
1323aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
132414135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1325064f1981SNamhyung Kim 
1326c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1327aca7a94dSNamhyung Kim 			return nd;
1328aca7a94dSNamhyung Kim 
1329aca7a94dSNamhyung Kim 		nd = rb_next(nd);
1330aca7a94dSNamhyung Kim 	}
1331aca7a94dSNamhyung Kim 
1332aca7a94dSNamhyung Kim 	return NULL;
1333aca7a94dSNamhyung Kim }
1334aca7a94dSNamhyung Kim 
1335064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1336064f1981SNamhyung Kim 						  float min_pcnt)
1337aca7a94dSNamhyung Kim {
1338aca7a94dSNamhyung Kim 	while (nd != NULL) {
1339aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
134014135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1341064f1981SNamhyung Kim 
1342064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1343aca7a94dSNamhyung Kim 			return nd;
1344aca7a94dSNamhyung Kim 
1345aca7a94dSNamhyung Kim 		nd = rb_prev(nd);
1346aca7a94dSNamhyung Kim 	}
1347aca7a94dSNamhyung Kim 
1348aca7a94dSNamhyung Kim 	return NULL;
1349aca7a94dSNamhyung Kim }
1350aca7a94dSNamhyung Kim 
135105e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1352aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1353aca7a94dSNamhyung Kim {
1354aca7a94dSNamhyung Kim 	struct hist_entry *h;
1355aca7a94dSNamhyung Kim 	struct rb_node *nd;
1356aca7a94dSNamhyung Kim 	bool first = true;
1357064f1981SNamhyung Kim 	struct hist_browser *hb;
1358064f1981SNamhyung Kim 
1359064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1360aca7a94dSNamhyung Kim 
136105e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1362aca7a94dSNamhyung Kim 		return;
1363aca7a94dSNamhyung Kim 
136405e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1365aca7a94dSNamhyung Kim 
1366aca7a94dSNamhyung Kim 	switch (whence) {
1367aca7a94dSNamhyung Kim 	case SEEK_SET:
1368064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
136914135663SNamhyung Kim 					   hb->min_pcnt);
1370aca7a94dSNamhyung Kim 		break;
1371aca7a94dSNamhyung Kim 	case SEEK_CUR:
137205e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1373aca7a94dSNamhyung Kim 		goto do_offset;
1374aca7a94dSNamhyung Kim 	case SEEK_END:
1375064f1981SNamhyung Kim 		nd = hists__filter_prev_entries(rb_last(browser->entries),
137614135663SNamhyung Kim 						hb->min_pcnt);
1377aca7a94dSNamhyung Kim 		first = false;
1378aca7a94dSNamhyung Kim 		break;
1379aca7a94dSNamhyung Kim 	default:
1380aca7a94dSNamhyung Kim 		return;
1381aca7a94dSNamhyung Kim 	}
1382aca7a94dSNamhyung Kim 
1383aca7a94dSNamhyung Kim 	/*
1384aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1385aca7a94dSNamhyung Kim 	 * row_offset:
1386aca7a94dSNamhyung Kim 	 */
138705e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1388aca7a94dSNamhyung Kim 	h->row_offset = 0;
1389aca7a94dSNamhyung Kim 
1390aca7a94dSNamhyung Kim 	/*
1391aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1392aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1393aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1394aca7a94dSNamhyung Kim 	 *
1395aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1396aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1397aca7a94dSNamhyung Kim 	 *
1398aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1399aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1400aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1401aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1402aca7a94dSNamhyung Kim 	 */
1403aca7a94dSNamhyung Kim do_offset:
1404837eeb75SWang Nan 	if (!nd)
1405837eeb75SWang Nan 		return;
1406837eeb75SWang Nan 
1407aca7a94dSNamhyung Kim 	if (offset > 0) {
1408aca7a94dSNamhyung Kim 		do {
1409aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
14103698dab1SNamhyung Kim 			if (h->unfolded) {
1411aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1412aca7a94dSNamhyung Kim 				if (offset > remaining) {
1413aca7a94dSNamhyung Kim 					offset -= remaining;
1414aca7a94dSNamhyung Kim 					h->row_offset = 0;
1415aca7a94dSNamhyung Kim 				} else {
1416aca7a94dSNamhyung Kim 					h->row_offset += offset;
1417aca7a94dSNamhyung Kim 					offset = 0;
141805e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1419aca7a94dSNamhyung Kim 					break;
1420aca7a94dSNamhyung Kim 				}
1421aca7a94dSNamhyung Kim 			}
142214135663SNamhyung Kim 			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1423aca7a94dSNamhyung Kim 			if (nd == NULL)
1424aca7a94dSNamhyung Kim 				break;
1425aca7a94dSNamhyung Kim 			--offset;
142605e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1427aca7a94dSNamhyung Kim 		} while (offset != 0);
1428aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1429aca7a94dSNamhyung Kim 		while (1) {
1430aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
14313698dab1SNamhyung Kim 			if (h->unfolded) {
1432aca7a94dSNamhyung Kim 				if (first) {
1433aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1434aca7a94dSNamhyung Kim 						offset += h->row_offset;
1435aca7a94dSNamhyung Kim 						h->row_offset = 0;
1436aca7a94dSNamhyung Kim 					} else {
1437aca7a94dSNamhyung Kim 						h->row_offset += offset;
1438aca7a94dSNamhyung Kim 						offset = 0;
143905e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1440aca7a94dSNamhyung Kim 						break;
1441aca7a94dSNamhyung Kim 					}
1442aca7a94dSNamhyung Kim 				} else {
1443aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1444aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1445aca7a94dSNamhyung Kim 						h->row_offset = 0;
1446aca7a94dSNamhyung Kim 					} else {
1447aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1448aca7a94dSNamhyung Kim 						offset = 0;
144905e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1450aca7a94dSNamhyung Kim 						break;
1451aca7a94dSNamhyung Kim 					}
1452aca7a94dSNamhyung Kim 				}
1453aca7a94dSNamhyung Kim 			}
1454aca7a94dSNamhyung Kim 
145514135663SNamhyung Kim 			nd = hists__filter_prev_entries(rb_prev(nd),
1456064f1981SNamhyung Kim 							hb->min_pcnt);
1457aca7a94dSNamhyung Kim 			if (nd == NULL)
1458aca7a94dSNamhyung Kim 				break;
1459aca7a94dSNamhyung Kim 			++offset;
146005e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1461aca7a94dSNamhyung Kim 			if (offset == 0) {
1462aca7a94dSNamhyung Kim 				/*
1463aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1464aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1465aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1466aca7a94dSNamhyung Kim 				 */
1467aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
14683698dab1SNamhyung Kim 				if (h->unfolded)
1469aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1470aca7a94dSNamhyung Kim 				break;
1471aca7a94dSNamhyung Kim 			}
1472aca7a94dSNamhyung Kim 			first = false;
1473aca7a94dSNamhyung Kim 		}
1474aca7a94dSNamhyung Kim 	} else {
147505e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1476aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1477aca7a94dSNamhyung Kim 		h->row_offset = 0;
1478aca7a94dSNamhyung Kim 	}
1479aca7a94dSNamhyung Kim }
1480aca7a94dSNamhyung Kim 
1481aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
148239ee533fSNamhyung Kim 					   struct hist_entry *he, FILE *fp)
1483aff3f3f6SArnaldo Carvalho de Melo {
148439ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
148539ee533fSNamhyung Kim 		.fp = fp,
148639ee533fSNamhyung Kim 	};
1487aff3f3f6SArnaldo Carvalho de Melo 
14880c841c6cSNamhyung Kim 	hist_browser__show_callchain(browser, he, 1, 0,
148939ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
149039ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
149139ee533fSNamhyung Kim 	return arg.printed;
1492aff3f3f6SArnaldo Carvalho de Melo }
1493aff3f3f6SArnaldo Carvalho de Melo 
1494aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1495aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1496aff3f3f6SArnaldo Carvalho de Melo {
1497aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1498aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1499aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
150026d8b338SNamhyung Kim 	struct perf_hpp hpp = {
150126d8b338SNamhyung Kim 		.buf = s,
150226d8b338SNamhyung Kim 		.size = sizeof(s),
150326d8b338SNamhyung Kim 	};
150426d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
150526d8b338SNamhyung Kim 	bool first = true;
150626d8b338SNamhyung Kim 	int ret;
1507aff3f3f6SArnaldo Carvalho de Melo 
1508aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1509aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1510aff3f3f6SArnaldo Carvalho de Melo 
1511aff3f3f6SArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain)
1512aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
1513aff3f3f6SArnaldo Carvalho de Melo 
1514f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1515361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
1516e67d49a7SNamhyung Kim 			continue;
1517e67d49a7SNamhyung Kim 
151826d8b338SNamhyung Kim 		if (!first) {
151926d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
152026d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
152126d8b338SNamhyung Kim 		} else
152226d8b338SNamhyung Kim 			first = false;
1523aff3f3f6SArnaldo Carvalho de Melo 
152426d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
152589fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
152626d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
152726d8b338SNamhyung Kim 	}
152889fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
1529aff3f3f6SArnaldo Carvalho de Melo 
1530aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
153139ee533fSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp);
1532aff3f3f6SArnaldo Carvalho de Melo 
1533aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1534aff3f3f6SArnaldo Carvalho de Melo }
1535aff3f3f6SArnaldo Carvalho de Melo 
1536aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1537aff3f3f6SArnaldo Carvalho de Melo {
1538064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1539064f1981SNamhyung Kim 						   browser->min_pcnt);
1540aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1541aff3f3f6SArnaldo Carvalho de Melo 
1542aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
1543aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1544aff3f3f6SArnaldo Carvalho de Melo 
1545aff3f3f6SArnaldo Carvalho de Melo 		printed += hist_browser__fprintf_entry(browser, h, fp);
154614135663SNamhyung Kim 		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1547aff3f3f6SArnaldo Carvalho de Melo 	}
1548aff3f3f6SArnaldo Carvalho de Melo 
1549aff3f3f6SArnaldo Carvalho de Melo 	return printed;
1550aff3f3f6SArnaldo Carvalho de Melo }
1551aff3f3f6SArnaldo Carvalho de Melo 
1552aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
1553aff3f3f6SArnaldo Carvalho de Melo {
1554aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
1555aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
1556aff3f3f6SArnaldo Carvalho de Melo 
1557aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
1558aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1559aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
1560aff3f3f6SArnaldo Carvalho de Melo 			break;
1561aff3f3f6SArnaldo Carvalho de Melo 		/*
1562aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
1563aff3f3f6SArnaldo Carvalho de Melo  		 */
1564aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
1565aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1566aff3f3f6SArnaldo Carvalho de Melo 			return -1;
1567aff3f3f6SArnaldo Carvalho de Melo 		}
1568aff3f3f6SArnaldo Carvalho de Melo 	}
1569aff3f3f6SArnaldo Carvalho de Melo 
1570aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
1571aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
1572aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
15734cc49d4dSKirill A. Shutemov 		const char *err = strerror_r(errno, bf, sizeof(bf));
15744cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1575aff3f3f6SArnaldo Carvalho de Melo 		return -1;
1576aff3f3f6SArnaldo Carvalho de Melo 	}
1577aff3f3f6SArnaldo Carvalho de Melo 
1578aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
1579aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
1580aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
1581aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
1582aff3f3f6SArnaldo Carvalho de Melo 
1583aff3f3f6SArnaldo Carvalho de Melo 	return 0;
1584aff3f3f6SArnaldo Carvalho de Melo }
1585aff3f3f6SArnaldo Carvalho de Melo 
1586c2a51ab8SNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists,
1587b1a9ceefSNamhyung Kim 					      struct hist_browser_timer *hbt,
1588ce80d3beSKan Liang 					      struct perf_env *env)
1589aca7a94dSNamhyung Kim {
159005e8b080SArnaldo Carvalho de Melo 	struct hist_browser *browser = zalloc(sizeof(*browser));
1591aca7a94dSNamhyung Kim 
159205e8b080SArnaldo Carvalho de Melo 	if (browser) {
159305e8b080SArnaldo Carvalho de Melo 		browser->hists = hists;
159405e8b080SArnaldo Carvalho de Melo 		browser->b.refresh = hist_browser__refresh;
1595357cfff1SArnaldo Carvalho de Melo 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
159605e8b080SArnaldo Carvalho de Melo 		browser->b.seek = ui_browser__hists_seek;
159705e8b080SArnaldo Carvalho de Melo 		browser->b.use_navkeypressed = true;
1598c8302367SJiri Olsa 		browser->show_headers = symbol_conf.show_hist_headers;
1599c2a51ab8SNamhyung Kim 		browser->hbt = hbt;
1600b1a9ceefSNamhyung Kim 		browser->env = env;
1601aca7a94dSNamhyung Kim 	}
1602aca7a94dSNamhyung Kim 
160305e8b080SArnaldo Carvalho de Melo 	return browser;
1604aca7a94dSNamhyung Kim }
1605aca7a94dSNamhyung Kim 
160605e8b080SArnaldo Carvalho de Melo static void hist_browser__delete(struct hist_browser *browser)
1607aca7a94dSNamhyung Kim {
160805e8b080SArnaldo Carvalho de Melo 	free(browser);
1609aca7a94dSNamhyung Kim }
1610aca7a94dSNamhyung Kim 
161105e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1612aca7a94dSNamhyung Kim {
161305e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
1614aca7a94dSNamhyung Kim }
1615aca7a94dSNamhyung Kim 
161605e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1617aca7a94dSNamhyung Kim {
161805e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
1619aca7a94dSNamhyung Kim }
1620aca7a94dSNamhyung Kim 
16211e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
16221e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
16231e378ebdSTaeung Song {
16241e378ebdSTaeung Song 	return timer == NULL;
16251e378ebdSTaeung Song }
16261e378ebdSTaeung Song 
16271e378ebdSTaeung Song static int hists__browser_title(struct hists *hists,
16281e378ebdSTaeung Song 				struct hist_browser_timer *hbt,
16291e378ebdSTaeung Song 				char *bf, size_t size)
1630aca7a94dSNamhyung Kim {
1631aca7a94dSNamhyung Kim 	char unit;
1632aca7a94dSNamhyung Kim 	int printed;
163305e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
163405e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
163584734b06SKan Liang 	int socket_id = hists->socket_filter;
163605e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
163705e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
1638717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
1639dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
1640717e263fSNamhyung Kim 	char buf[512];
1641717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
16429e207ddfSKan Liang 	char ref[30] = " show reference callgraph, ";
16439e207ddfSKan Liang 	bool enable_ref = false;
1644717e263fSNamhyung Kim 
1645f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
1646f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
1647f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
1648f2148330SNamhyung Kim 	}
1649f2148330SNamhyung Kim 
1650759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
1651717e263fSNamhyung Kim 		struct perf_evsel *pos;
1652717e263fSNamhyung Kim 
1653717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
1654717e263fSNamhyung Kim 		ev_name = buf;
1655717e263fSNamhyung Kim 
1656717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
16574ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
16584ea062edSArnaldo Carvalho de Melo 
1659f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
16604ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
16614ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
1662f2148330SNamhyung Kim 			} else {
16634ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
16644ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
1665717e263fSNamhyung Kim 			}
1666717e263fSNamhyung Kim 		}
1667f2148330SNamhyung Kim 	}
1668aca7a94dSNamhyung Kim 
16699e207ddfSKan Liang 	if (symbol_conf.show_ref_callgraph &&
16709e207ddfSKan Liang 	    strstr(ev_name, "call-graph=no"))
16719e207ddfSKan Liang 		enable_ref = true;
1672aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
1673aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
16749e207ddfSKan Liang 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
16759e207ddfSKan Liang 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1676aca7a94dSNamhyung Kim 
1677aca7a94dSNamhyung Kim 
167805e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
1679aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
168005e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
1681aca7a94dSNamhyung Kim 	if (thread)
1682aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1683aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
1684b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
168538051234SAdrian Hunter 				    thread->tid);
1686aca7a94dSNamhyung Kim 	if (dso)
1687aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
1688aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
168984734b06SKan Liang 	if (socket_id > -1)
169021394d94SKan Liang 		printed += scnprintf(bf + printed, size - printed,
169184734b06SKan Liang 				    ", Processor Socket: %d", socket_id);
16921e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
16931e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
16941e378ebdSTaeung Song 
16951e378ebdSTaeung Song 		if (top->zero)
16961e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
16971e378ebdSTaeung Song 	}
16981e378ebdSTaeung Song 
1699aca7a94dSNamhyung Kim 	return printed;
1700aca7a94dSNamhyung Kim }
1701aca7a94dSNamhyung Kim 
1702aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
1703aca7a94dSNamhyung Kim {
1704aca7a94dSNamhyung Kim 	int i;
1705aca7a94dSNamhyung Kim 
170604662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
170704662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
1708aca7a94dSNamhyung Kim }
1709aca7a94dSNamhyung Kim 
1710341487abSFeng Tang /*
1711341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
1712341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1713341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
1714341487abSFeng Tang  */
1715341487abSFeng Tang static bool is_input_name_malloced = false;
1716341487abSFeng Tang 
1717341487abSFeng Tang static int switch_data_file(void)
1718341487abSFeng Tang {
1719341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
1720341487abSFeng Tang 	DIR *pwd_dir;
1721341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
1722341487abSFeng Tang 	struct dirent *dent;
1723341487abSFeng Tang 
1724341487abSFeng Tang 	pwd = getenv("PWD");
1725341487abSFeng Tang 	if (!pwd)
1726341487abSFeng Tang 		return ret;
1727341487abSFeng Tang 
1728341487abSFeng Tang 	pwd_dir = opendir(pwd);
1729341487abSFeng Tang 	if (!pwd_dir)
1730341487abSFeng Tang 		return ret;
1731341487abSFeng Tang 
1732341487abSFeng Tang 	memset(options, 0, sizeof(options));
1733341487abSFeng Tang 	memset(options, 0, sizeof(abs_path));
1734341487abSFeng Tang 
1735341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
1736341487abSFeng Tang 		char path[PATH_MAX];
1737341487abSFeng Tang 		u64 magic;
1738341487abSFeng Tang 		char *name = dent->d_name;
1739341487abSFeng Tang 		FILE *file;
1740341487abSFeng Tang 
1741341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
1742341487abSFeng Tang 			continue;
1743341487abSFeng Tang 
1744341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1745341487abSFeng Tang 
1746341487abSFeng Tang 		file = fopen(path, "r");
1747341487abSFeng Tang 		if (!file)
1748341487abSFeng Tang 			continue;
1749341487abSFeng Tang 
1750341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
1751341487abSFeng Tang 			goto close_file_and_continue;
1752341487abSFeng Tang 
1753341487abSFeng Tang 		if (is_perf_magic(magic)) {
1754341487abSFeng Tang 			options[nr_options] = strdup(name);
1755341487abSFeng Tang 			if (!options[nr_options])
1756341487abSFeng Tang 				goto close_file_and_continue;
1757341487abSFeng Tang 
1758341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
1759341487abSFeng Tang 			if (!abs_path[nr_options]) {
176074cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
1761341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
1762341487abSFeng Tang 				fclose(file);
1763341487abSFeng Tang 				break;
1764341487abSFeng Tang 			}
1765341487abSFeng Tang 
1766341487abSFeng Tang 			nr_options++;
1767341487abSFeng Tang 		}
1768341487abSFeng Tang 
1769341487abSFeng Tang close_file_and_continue:
1770341487abSFeng Tang 		fclose(file);
1771341487abSFeng Tang 		if (nr_options >= 32) {
1772341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
1773341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
1774341487abSFeng Tang 			break;
1775341487abSFeng Tang 		}
1776341487abSFeng Tang 	}
1777341487abSFeng Tang 	closedir(pwd_dir);
1778341487abSFeng Tang 
1779341487abSFeng Tang 	if (nr_options) {
1780341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
1781341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
1782341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
1783341487abSFeng Tang 			if (tmp) {
1784341487abSFeng Tang 				if (is_input_name_malloced)
1785341487abSFeng Tang 					free((void *)input_name);
1786341487abSFeng Tang 				input_name = tmp;
1787341487abSFeng Tang 				is_input_name_malloced = true;
1788341487abSFeng Tang 				ret = 0;
1789341487abSFeng Tang 			} else
1790341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
1791341487abSFeng Tang 		}
1792341487abSFeng Tang 	}
1793341487abSFeng Tang 
1794341487abSFeng Tang 	free_popup_options(options, nr_options);
1795341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
1796341487abSFeng Tang 	return ret;
1797341487abSFeng Tang }
1798341487abSFeng Tang 
1799ea7cd592SNamhyung Kim struct popup_action {
1800ea7cd592SNamhyung Kim 	struct thread 		*thread;
1801ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
180284734b06SKan Liang 	int			socket;
1803ea7cd592SNamhyung Kim 
1804ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
1805ea7cd592SNamhyung Kim };
1806ea7cd592SNamhyung Kim 
1807bc7cad42SNamhyung Kim static int
1808ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
1809bc7cad42SNamhyung Kim {
1810bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
1811bc7cad42SNamhyung Kim 	struct annotation *notes;
1812bc7cad42SNamhyung Kim 	struct hist_entry *he;
1813bc7cad42SNamhyung Kim 	int err;
1814bc7cad42SNamhyung Kim 
1815eebd0bfcSArnaldo Carvalho de Melo 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
1816bc7cad42SNamhyung Kim 		return 0;
1817bc7cad42SNamhyung Kim 
1818ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
1819bc7cad42SNamhyung Kim 	if (!notes->src)
1820bc7cad42SNamhyung Kim 		return 0;
1821bc7cad42SNamhyung Kim 
1822bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
1823ea7cd592SNamhyung Kim 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1824bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
1825bc7cad42SNamhyung Kim 	/*
1826bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
1827bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
1828bc7cad42SNamhyung Kim 	 */
1829bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1830bc7cad42SNamhyung Kim 		return 1;
1831bc7cad42SNamhyung Kim 
1832bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1833bc7cad42SNamhyung Kim 	if (err)
1834bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
1835bc7cad42SNamhyung Kim 	return 0;
1836bc7cad42SNamhyung Kim }
1837bc7cad42SNamhyung Kim 
1838bc7cad42SNamhyung Kim static int
1839ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
1840ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
1841ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
1842bc7cad42SNamhyung Kim {
1843ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
1844ea7cd592SNamhyung Kim 		return 0;
1845ea7cd592SNamhyung Kim 
1846ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1847ea7cd592SNamhyung Kim 		return 0;
1848ea7cd592SNamhyung Kim 
1849ea7cd592SNamhyung Kim 	act->ms.map = map;
1850ea7cd592SNamhyung Kim 	act->ms.sym = sym;
1851ea7cd592SNamhyung Kim 	act->fn = do_annotate;
1852ea7cd592SNamhyung Kim 	return 1;
1853ea7cd592SNamhyung Kim }
1854ea7cd592SNamhyung Kim 
1855ea7cd592SNamhyung Kim static int
1856ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1857ea7cd592SNamhyung Kim {
1858ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
1859ea7cd592SNamhyung Kim 
1860bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
1861bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
1862bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
1863bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
1864bc7cad42SNamhyung Kim 		ui_helpline__pop();
1865bc7cad42SNamhyung Kim 	} else {
18667727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1867bc7cad42SNamhyung Kim 				   thread->comm_set ? thread__comm_str(thread) : "",
1868bc7cad42SNamhyung Kim 				   thread->tid);
1869bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
1870bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
1871bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
1872bc7cad42SNamhyung Kim 	}
1873bc7cad42SNamhyung Kim 
1874bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
1875bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
1876bc7cad42SNamhyung Kim 	return 0;
1877bc7cad42SNamhyung Kim }
1878bc7cad42SNamhyung Kim 
1879bc7cad42SNamhyung Kim static int
1880ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1881ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
1882bc7cad42SNamhyung Kim {
18832eafd410SNamhyung Kim 	if (!sort__has_thread || thread == NULL)
1884ea7cd592SNamhyung Kim 		return 0;
1885ea7cd592SNamhyung Kim 
1886ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s(%d) thread",
1887ea7cd592SNamhyung Kim 		     browser->hists->thread_filter ? "out of" : "into",
1888ea7cd592SNamhyung Kim 		     thread->comm_set ? thread__comm_str(thread) : "",
1889ea7cd592SNamhyung Kim 		     thread->tid) < 0)
1890ea7cd592SNamhyung Kim 		return 0;
1891ea7cd592SNamhyung Kim 
1892ea7cd592SNamhyung Kim 	act->thread = thread;
1893ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
1894ea7cd592SNamhyung Kim 	return 1;
1895ea7cd592SNamhyung Kim }
1896ea7cd592SNamhyung Kim 
1897ea7cd592SNamhyung Kim static int
1898ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1899ea7cd592SNamhyung Kim {
1900045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
1901ea7cd592SNamhyung Kim 
1902bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
1903bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
1904bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
1905bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
1906bc7cad42SNamhyung Kim 		ui_helpline__pop();
1907bc7cad42SNamhyung Kim 	} else {
1908045b80ddSArnaldo Carvalho de Melo 		if (map == NULL)
1909bc7cad42SNamhyung Kim 			return 0;
19107727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1911045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1912045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
1913bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
1914bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
1915bc7cad42SNamhyung Kim 	}
1916bc7cad42SNamhyung Kim 
1917bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
1918bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
1919bc7cad42SNamhyung Kim 	return 0;
1920bc7cad42SNamhyung Kim }
1921bc7cad42SNamhyung Kim 
1922bc7cad42SNamhyung Kim static int
1923ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1924045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
1925bc7cad42SNamhyung Kim {
1926b1447a54SNamhyung Kim 	if (!sort__has_dso || map == NULL)
1927ea7cd592SNamhyung Kim 		return 0;
1928ea7cd592SNamhyung Kim 
1929ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
1930ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
1931045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1932ea7cd592SNamhyung Kim 		return 0;
1933ea7cd592SNamhyung Kim 
1934045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
1935ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
1936ea7cd592SNamhyung Kim 	return 1;
1937ea7cd592SNamhyung Kim }
1938ea7cd592SNamhyung Kim 
1939ea7cd592SNamhyung Kim static int
1940ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
1941ea7cd592SNamhyung Kim 	      struct popup_action *act)
1942ea7cd592SNamhyung Kim {
1943ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
1944bc7cad42SNamhyung Kim 	return 0;
1945bc7cad42SNamhyung Kim }
1946bc7cad42SNamhyung Kim 
1947bc7cad42SNamhyung Kim static int
1948ea7cd592SNamhyung Kim add_map_opt(struct hist_browser *browser __maybe_unused,
1949ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
1950ea7cd592SNamhyung Kim {
1951b1447a54SNamhyung Kim 	if (!sort__has_dso || map == NULL)
1952ea7cd592SNamhyung Kim 		return 0;
1953ea7cd592SNamhyung Kim 
1954ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
1955ea7cd592SNamhyung Kim 		return 0;
1956ea7cd592SNamhyung Kim 
1957ea7cd592SNamhyung Kim 	act->ms.map = map;
1958ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
1959ea7cd592SNamhyung Kim 	return 1;
1960ea7cd592SNamhyung Kim }
1961ea7cd592SNamhyung Kim 
1962ea7cd592SNamhyung Kim static int
1963bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
1964ea7cd592SNamhyung Kim 	      struct popup_action *act)
1965bc7cad42SNamhyung Kim {
1966bc7cad42SNamhyung Kim 	char script_opt[64];
1967bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
1968bc7cad42SNamhyung Kim 
1969ea7cd592SNamhyung Kim 	if (act->thread) {
1970bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1971ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
1972ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
1973bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1974ea7cd592SNamhyung Kim 			  act->ms.sym->name);
1975bc7cad42SNamhyung Kim 	}
1976bc7cad42SNamhyung Kim 
1977bc7cad42SNamhyung Kim 	script_browse(script_opt);
1978bc7cad42SNamhyung Kim 	return 0;
1979bc7cad42SNamhyung Kim }
1980bc7cad42SNamhyung Kim 
1981bc7cad42SNamhyung Kim static int
1982ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
1983ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
1984ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
1985ea7cd592SNamhyung Kim {
1986ea7cd592SNamhyung Kim 	if (thread) {
1987ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1988ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
1989ea7cd592SNamhyung Kim 			return 0;
1990ea7cd592SNamhyung Kim 	} else if (sym) {
1991ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1992ea7cd592SNamhyung Kim 			     sym->name) < 0)
1993ea7cd592SNamhyung Kim 			return 0;
1994ea7cd592SNamhyung Kim 	} else {
1995ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
1996ea7cd592SNamhyung Kim 			return 0;
1997ea7cd592SNamhyung Kim 	}
1998ea7cd592SNamhyung Kim 
1999ea7cd592SNamhyung Kim 	act->thread = thread;
2000ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2001ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2002ea7cd592SNamhyung Kim 	return 1;
2003ea7cd592SNamhyung Kim }
2004ea7cd592SNamhyung Kim 
2005ea7cd592SNamhyung Kim static int
2006ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2007ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2008bc7cad42SNamhyung Kim {
2009bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2010bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2011bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2012ea7cd592SNamhyung Kim 		return 0;
2013bc7cad42SNamhyung Kim 	}
2014bc7cad42SNamhyung Kim 
2015bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2016bc7cad42SNamhyung Kim }
2017bc7cad42SNamhyung Kim 
2018ea7cd592SNamhyung Kim static int
2019ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2020ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2021ea7cd592SNamhyung Kim {
2022ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2023ea7cd592SNamhyung Kim 		return 0;
2024ea7cd592SNamhyung Kim 
2025ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2026ea7cd592SNamhyung Kim 		return 0;
2027ea7cd592SNamhyung Kim 
2028ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2029ea7cd592SNamhyung Kim 	return 1;
2030ea7cd592SNamhyung Kim }
2031ea7cd592SNamhyung Kim 
2032ea7cd592SNamhyung Kim static int
2033ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2034ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2035ea7cd592SNamhyung Kim {
2036ea7cd592SNamhyung Kim 	return 0;
2037ea7cd592SNamhyung Kim }
2038ea7cd592SNamhyung Kim 
2039ea7cd592SNamhyung Kim static int
2040ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2041ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2042ea7cd592SNamhyung Kim {
2043ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2044ea7cd592SNamhyung Kim 		return 0;
2045ea7cd592SNamhyung Kim 
2046ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2047ea7cd592SNamhyung Kim 	return 1;
2048ea7cd592SNamhyung Kim }
2049ea7cd592SNamhyung Kim 
205084734b06SKan Liang static int
205184734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
205284734b06SKan Liang {
205384734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
205484734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
205584734b06SKan Liang 		browser->hists->socket_filter = -1;
205684734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
205784734b06SKan Liang 	} else {
205884734b06SKan Liang 		browser->hists->socket_filter = act->socket;
205984734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
206084734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
206184734b06SKan Liang 	}
206284734b06SKan Liang 
206384734b06SKan Liang 	hists__filter_by_socket(browser->hists);
206484734b06SKan Liang 	hist_browser__reset(browser);
206584734b06SKan Liang 	return 0;
206684734b06SKan Liang }
206784734b06SKan Liang 
206884734b06SKan Liang static int
206984734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
207084734b06SKan Liang 	       char **optstr, int socket_id)
207184734b06SKan Liang {
2072d9695d9fSNamhyung Kim 	if (!sort__has_socket || socket_id < 0)
207384734b06SKan Liang 		return 0;
207484734b06SKan Liang 
207584734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
207684734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
207784734b06SKan Liang 		     socket_id) < 0)
207884734b06SKan Liang 		return 0;
207984734b06SKan Liang 
208084734b06SKan Liang 	act->socket = socket_id;
208184734b06SKan Liang 	act->fn = do_zoom_socket;
208284734b06SKan Liang 	return 1;
208384734b06SKan Liang }
208484734b06SKan Liang 
2085112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2086064f1981SNamhyung Kim {
2087064f1981SNamhyung Kim 	u64 nr_entries = 0;
2088064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2089064f1981SNamhyung Kim 
2090*f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2091268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2092268397cbSNamhyung Kim 		return;
2093268397cbSNamhyung Kim 	}
2094268397cbSNamhyung Kim 
209514135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2096064f1981SNamhyung Kim 		nr_entries++;
2097*f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2098064f1981SNamhyung Kim 	}
2099064f1981SNamhyung Kim 
2100112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2101*f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2102064f1981SNamhyung Kim }
2103341487abSFeng Tang 
2104b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2105b62e8dfcSNamhyung Kim 					       double percent)
2106b62e8dfcSNamhyung Kim {
2107b62e8dfcSNamhyung Kim 	struct hist_entry *he;
2108b62e8dfcSNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2109b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2110b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2111b62e8dfcSNamhyung Kim 
2112b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2113b62e8dfcSNamhyung Kim 
2114b62e8dfcSNamhyung Kim 	if (!symbol_conf.use_callchain)
2115b62e8dfcSNamhyung Kim 		return;
2116b62e8dfcSNamhyung Kim 
2117b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2118b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2119b62e8dfcSNamhyung Kim 
2120b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2121b62e8dfcSNamhyung Kim 			total = he->stat.period;
2122b62e8dfcSNamhyung Kim 
2123b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2124b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2125b62e8dfcSNamhyung Kim 
2126b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2127b62e8dfcSNamhyung Kim 		}
2128b62e8dfcSNamhyung Kim 
2129b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2130b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2131b62e8dfcSNamhyung Kim 
2132b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2133b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2134b62e8dfcSNamhyung Kim 		hist_entry__set_folding(he, false);
2135b62e8dfcSNamhyung Kim 
2136b62e8dfcSNamhyung Kim 		nd = rb_next(nd);
2137b62e8dfcSNamhyung Kim 	}
2138b62e8dfcSNamhyung Kim }
2139b62e8dfcSNamhyung Kim 
2140aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2141dd00d486SJiri Olsa 				    const char *helpline,
2142aca7a94dSNamhyung Kim 				    bool left_exits,
214368d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2144064f1981SNamhyung Kim 				    float min_pcnt,
2145ce80d3beSKan Liang 				    struct perf_env *env)
2146aca7a94dSNamhyung Kim {
21474ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2148b1a9ceefSNamhyung Kim 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2149aca7a94dSNamhyung Kim 	struct branch_info *bi;
2150f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2151f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2152ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2153aca7a94dSNamhyung Kim 	int nr_options = 0;
2154aca7a94dSNamhyung Kim 	int key = -1;
2155aca7a94dSNamhyung Kim 	char buf[64];
21569783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
215759dc9f25SNamhyung Kim 	struct perf_hpp_fmt *fmt;
2158aca7a94dSNamhyung Kim 
2159e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2160e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2161e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2162e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
2163e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2164e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2165e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2166e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
21677727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
21687727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
2169e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2170e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2171e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2172e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2173105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2174025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2175b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
217631eb4360SNamhyung Kim 	"m             Display context menu\n"				\
217784734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2178e8e684a5SNamhyung Kim 
2179e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
2180e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
21816dd60135SNamhyung Kim 	"i             Show header information\n"
2182e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2183e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2184e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2185e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2186e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2187e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2188e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2189e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2190e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2191e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
219242337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2193fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2194e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2195e8e684a5SNamhyung Kim 
2196aca7a94dSNamhyung Kim 	if (browser == NULL)
2197aca7a94dSNamhyung Kim 		return -1;
2198aca7a94dSNamhyung Kim 
2199ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2200ed426915SNamhyung Kim 	SLang_reset_tty();
2201ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2202ed426915SNamhyung Kim 
220303905048SNamhyung Kim 	if (min_pcnt)
2204064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2205112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2206064f1981SNamhyung Kim 
220784734b06SKan Liang 	browser->pstack = pstack__new(3);
220801f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2209aca7a94dSNamhyung Kim 		goto out;
2210aca7a94dSNamhyung Kim 
2211aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2212aca7a94dSNamhyung Kim 
2213aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2214ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2215aca7a94dSNamhyung Kim 
2216f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
221759dc9f25SNamhyung Kim 		perf_hpp__reset_width(fmt, hists);
2218c6c3c02dSArnaldo Carvalho de Melo 		/*
2219c6c3c02dSArnaldo Carvalho de Melo 		 * This is done just once, and activates the horizontal scrolling
2220c6c3c02dSArnaldo Carvalho de Melo 		 * code in the ui_browser code, it would be better to have a the
2221c6c3c02dSArnaldo Carvalho de Melo 		 * counter in the perf_hpp code, but I couldn't find doing it here
2222c6c3c02dSArnaldo Carvalho de Melo 		 * works, FIXME by setting this in hist_browser__new, for now, be
2223c6c3c02dSArnaldo Carvalho de Melo 		 * clever 8-)
2224c6c3c02dSArnaldo Carvalho de Melo 		 */
2225c6c3c02dSArnaldo Carvalho de Melo 		++browser->b.columns;
2226c6c3c02dSArnaldo Carvalho de Melo 	}
222759dc9f25SNamhyung Kim 
22285b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
22295b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
22305b591669SNamhyung Kim 
2231aca7a94dSNamhyung Kim 	while (1) {
2232f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2233045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
2234ea7cd592SNamhyung Kim 		int choice = 0;
223584734b06SKan Liang 		int socked_id = -1;
2236aca7a94dSNamhyung Kim 
2237aca7a94dSNamhyung Kim 		nr_options = 0;
2238aca7a94dSNamhyung Kim 
22395f00b0f4SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline);
2240aca7a94dSNamhyung Kim 
2241aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
2242aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
2243045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
224484734b06SKan Liang 			socked_id = browser->he_selection->socket;
2245aca7a94dSNamhyung Kim 		}
2246aca7a94dSNamhyung Kim 		switch (key) {
2247aca7a94dSNamhyung Kim 		case K_TAB:
2248aca7a94dSNamhyung Kim 		case K_UNTAB:
2249aca7a94dSNamhyung Kim 			if (nr_events == 1)
2250aca7a94dSNamhyung Kim 				continue;
2251aca7a94dSNamhyung Kim 			/*
2252aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
2253aca7a94dSNamhyung Kim 			 * go to the next or previous
2254aca7a94dSNamhyung Kim 			 */
2255aca7a94dSNamhyung Kim 			goto out_free_stack;
2256aca7a94dSNamhyung Kim 		case 'a':
22579c796ec8SArnaldo Carvalho de Melo 			if (!sort__has_sym) {
2258aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
2259aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
2260aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
2261aca7a94dSNamhyung Kim 				continue;
2262aca7a94dSNamhyung Kim 			}
2263aca7a94dSNamhyung Kim 
2264aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
2265aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
2266aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
2267aca7a94dSNamhyung Kim 				continue;
2268bc7cad42SNamhyung Kim 
2269ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
2270ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
2271ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
2272bc7cad42SNamhyung Kim 			continue;
2273aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
2274aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
2275aff3f3f6SArnaldo Carvalho de Melo 			continue;
2276aca7a94dSNamhyung Kim 		case 'd':
2277fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
2278ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
2279bc7cad42SNamhyung Kim 			continue;
2280a7cb8863SArnaldo Carvalho de Melo 		case 'V':
2281a7cb8863SArnaldo Carvalho de Melo 			browser->show_dso = !browser->show_dso;
2282a7cb8863SArnaldo Carvalho de Melo 			continue;
2283aca7a94dSNamhyung Kim 		case 't':
2284ea7cd592SNamhyung Kim 			actions->thread = thread;
2285ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
2286bc7cad42SNamhyung Kim 			continue;
228784734b06SKan Liang 		case 'S':
228884734b06SKan Liang 			actions->socket = socked_id;
228984734b06SKan Liang 			do_zoom_socket(browser, actions);
229084734b06SKan Liang 			continue;
22915a5626b1SArnaldo Carvalho de Melo 		case '/':
2292aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
22934aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
22944aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
2295aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2296aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
229705e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
229805e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
2299aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
2300aca7a94dSNamhyung Kim 			}
2301aca7a94dSNamhyung Kim 			continue;
2302cdbab7c2SFeng Tang 		case 'r':
2303ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
2304ea7cd592SNamhyung Kim 				actions->thread = NULL;
2305ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
2306ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
2307ea7cd592SNamhyung Kim 			}
2308c77d8d70SFeng Tang 			continue;
2309341487abSFeng Tang 		case 's':
2310bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
2311ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
2312bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
2313bc7cad42SNamhyung Kim 					goto out_free_stack;
2314bc7cad42SNamhyung Kim 			}
2315341487abSFeng Tang 			continue;
23166dd60135SNamhyung Kim 		case 'i':
23176dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
23186dd60135SNamhyung Kim 			if (env->arch)
23196dd60135SNamhyung Kim 				tui__header_window(env);
23206dd60135SNamhyung Kim 			continue;
2321105eb30fSNamhyung Kim 		case 'F':
2322105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
2323105eb30fSNamhyung Kim 			continue;
232442337a22SNamhyung Kim 		case 'z':
232542337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
232642337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
232742337a22SNamhyung Kim 
232842337a22SNamhyung Kim 				top->zero = !top->zero;
232942337a22SNamhyung Kim 			}
233042337a22SNamhyung Kim 			continue;
2331b62e8dfcSNamhyung Kim 		case 'L':
2332b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
2333b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
2334b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2335b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
2336b62e8dfcSNamhyung Kim 				char *end;
2337b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
2338b62e8dfcSNamhyung Kim 
2339b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
2340b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
2341b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
2342b62e8dfcSNamhyung Kim 					continue;
2343b62e8dfcSNamhyung Kim 				}
2344b62e8dfcSNamhyung Kim 
2345b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
2346b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
2347b62e8dfcSNamhyung Kim 			}
2348b62e8dfcSNamhyung Kim 			continue;
2349aca7a94dSNamhyung Kim 		case K_F1:
2350aca7a94dSNamhyung Kim 		case 'h':
2351aca7a94dSNamhyung Kim 		case '?':
2352aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
2353e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
2354aca7a94dSNamhyung Kim 			continue;
2355aca7a94dSNamhyung Kim 		case K_ENTER:
2356aca7a94dSNamhyung Kim 		case K_RIGHT:
235731eb4360SNamhyung Kim 		case 'm':
2358aca7a94dSNamhyung Kim 			/* menu */
2359aca7a94dSNamhyung Kim 			break;
236063ab1749SArnaldo Carvalho de Melo 		case K_ESC:
2361aca7a94dSNamhyung Kim 		case K_LEFT: {
2362aca7a94dSNamhyung Kim 			const void *top;
2363aca7a94dSNamhyung Kim 
236401f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
2365aca7a94dSNamhyung Kim 				/*
2366aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
2367aca7a94dSNamhyung Kim 				 */
2368aca7a94dSNamhyung Kim 				if (left_exits)
2369aca7a94dSNamhyung Kim 					goto out_free_stack;
237063ab1749SArnaldo Carvalho de Melo 
237163ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
237263ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
237363ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
237463ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
237563ab1749SArnaldo Carvalho de Melo 
2376aca7a94dSNamhyung Kim 				continue;
2377aca7a94dSNamhyung Kim 			}
23786422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
2379bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
23806422184bSNamhyung Kim 				/*
23816422184bSNamhyung Kim 				 * No need to set actions->dso here since
23826422184bSNamhyung Kim 				 * it's just to remove the current filter.
23836422184bSNamhyung Kim 				 * Ditto for thread below.
23846422184bSNamhyung Kim 				 */
23856422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
238684734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
23876422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
238884734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
238984734b06SKan Liang 				do_zoom_socket(browser, actions);
239084734b06SKan Liang 			}
2391aca7a94dSNamhyung Kim 			continue;
2392aca7a94dSNamhyung Kim 		}
2393aca7a94dSNamhyung Kim 		case 'q':
2394aca7a94dSNamhyung Kim 		case CTRL('c'):
2395516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
2396fbb7997eSArnaldo Carvalho de Melo 		case 'f':
239713d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
239813d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
239913d1e536SNamhyung Kim 
240013d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
240113d1e536SNamhyung Kim 				/*
240213d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
240313d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
240413d1e536SNamhyung Kim 				 */
240513d1e536SNamhyung Kim 				if (top->evlist->enabled) {
240613d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
240713d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
240813d1e536SNamhyung Kim 				} else {
240913d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
241013d1e536SNamhyung Kim 					hbt->refresh = 0;
241113d1e536SNamhyung Kim 				}
241213d1e536SNamhyung Kim 				continue;
241313d1e536SNamhyung Kim 			}
24143e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
2415aca7a94dSNamhyung Kim 		default:
24163e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
2417aca7a94dSNamhyung Kim 			continue;
2418aca7a94dSNamhyung Kim 		}
2419aca7a94dSNamhyung Kim 
24204056132eSNamhyung Kim 		if (!sort__has_sym || browser->selection == NULL)
24210ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
24220ba332f7SArnaldo Carvalho de Melo 
242355369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
2424aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
24250ba332f7SArnaldo Carvalho de Melo 
24260ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
24270ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
24280ba332f7SArnaldo Carvalho de Melo 
2429ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2430ea7cd592SNamhyung Kim 						       &actions[nr_options],
2431ea7cd592SNamhyung Kim 						       &options[nr_options],
2432ea7cd592SNamhyung Kim 						       bi->from.map,
2433ea7cd592SNamhyung Kim 						       bi->from.sym);
2434ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
2435ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
2436ea7cd592SNamhyung Kim 							&actions[nr_options],
2437ea7cd592SNamhyung Kim 							&options[nr_options],
2438ea7cd592SNamhyung Kim 							bi->to.map,
2439ea7cd592SNamhyung Kim 							bi->to.sym);
2440aca7a94dSNamhyung Kim 		} else {
2441ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
2442ea7cd592SNamhyung Kim 						       &actions[nr_options],
2443ea7cd592SNamhyung Kim 						       &options[nr_options],
2444ea7cd592SNamhyung Kim 						       browser->selection->map,
2445ea7cd592SNamhyung Kim 						       browser->selection->sym);
2446446fb96cSArnaldo Carvalho de Melo 		}
24470ba332f7SArnaldo Carvalho de Melo skip_annotation:
2448ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
2449ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
2450ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
2451045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
2452ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
2453ea7cd592SNamhyung Kim 					  &options[nr_options],
2454bd315aabSWang Nan 					  browser->selection ?
2455bd315aabSWang Nan 						browser->selection->map : NULL);
245684734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
245784734b06SKan Liang 					     &options[nr_options],
245884734b06SKan Liang 					     socked_id);
2459cdbab7c2SFeng Tang 		/* perf script support */
2460b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
2461b1baae89SNamhyung Kim 			goto skip_scripting;
2462b1baae89SNamhyung Kim 
2463cdbab7c2SFeng Tang 		if (browser->he_selection) {
24642eafd410SNamhyung Kim 			if (sort__has_thread && thread) {
2465ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
2466ea7cd592SNamhyung Kim 							     &actions[nr_options],
2467ea7cd592SNamhyung Kim 							     &options[nr_options],
2468ea7cd592SNamhyung Kim 							     thread, NULL);
24692eafd410SNamhyung Kim 			}
2470bd315aabSWang Nan 			/*
2471bd315aabSWang Nan 			 * Note that browser->selection != NULL
2472bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
2473bd315aabSWang Nan 			 * so we don't need to check browser->selection
2474bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
2475bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
2476bd315aabSWang Nan 			 *
2477bd315aabSWang Nan 			 * See hist_browser__show_entry.
2478bd315aabSWang Nan 			 */
2479c221acb0SNamhyung Kim 			if (sort__has_sym && browser->selection->sym) {
2480ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
2481ea7cd592SNamhyung Kim 							     &actions[nr_options],
2482ea7cd592SNamhyung Kim 							     &options[nr_options],
2483ea7cd592SNamhyung Kim 							     NULL, browser->selection->sym);
2484cdbab7c2SFeng Tang 			}
2485c221acb0SNamhyung Kim 		}
2486ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
2487ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
2488ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
2489ea7cd592SNamhyung Kim 					     &options[nr_options]);
2490b1baae89SNamhyung Kim skip_scripting:
2491ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
2492ea7cd592SNamhyung Kim 					   &options[nr_options]);
2493aca7a94dSNamhyung Kim 
2494ea7cd592SNamhyung Kim 		do {
2495ea7cd592SNamhyung Kim 			struct popup_action *act;
2496ea7cd592SNamhyung Kim 
2497ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
2498ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
2499aca7a94dSNamhyung Kim 				break;
2500aca7a94dSNamhyung Kim 
2501ea7cd592SNamhyung Kim 			act = &actions[choice];
2502ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
2503ea7cd592SNamhyung Kim 		} while (key == 1);
2504aca7a94dSNamhyung Kim 
2505bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
2506341487abSFeng Tang 			break;
2507341487abSFeng Tang 	}
2508aca7a94dSNamhyung Kim out_free_stack:
250901f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
2510aca7a94dSNamhyung Kim out:
2511aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
2512f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
2513aca7a94dSNamhyung Kim 	return key;
2514aca7a94dSNamhyung Kim }
2515aca7a94dSNamhyung Kim 
2516aca7a94dSNamhyung Kim struct perf_evsel_menu {
2517aca7a94dSNamhyung Kim 	struct ui_browser b;
2518aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
2519aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
2520064f1981SNamhyung Kim 	float min_pcnt;
2521ce80d3beSKan Liang 	struct perf_env *env;
2522aca7a94dSNamhyung Kim };
2523aca7a94dSNamhyung Kim 
2524aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
2525aca7a94dSNamhyung Kim 				   void *entry, int row)
2526aca7a94dSNamhyung Kim {
2527aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
2528aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
2529aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
25304ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2531aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
25324ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
25337289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
2534aca7a94dSNamhyung Kim 	char bf[256], unit;
2535aca7a94dSNamhyung Kim 	const char *warn = " ";
2536aca7a94dSNamhyung Kim 	size_t printed;
2537aca7a94dSNamhyung Kim 
2538aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2539aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
2540aca7a94dSNamhyung Kim 
2541759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
2542717e263fSNamhyung Kim 		struct perf_evsel *pos;
2543717e263fSNamhyung Kim 
2544717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
2545717e263fSNamhyung Kim 
2546717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
25474ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
25484ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2549717e263fSNamhyung Kim 		}
2550717e263fSNamhyung Kim 	}
2551717e263fSNamhyung Kim 
2552aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
2553aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2554aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
2555517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
2556aca7a94dSNamhyung Kim 
25574ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2558aca7a94dSNamhyung Kim 	if (nr_events != 0) {
2559aca7a94dSNamhyung Kim 		menu->lost_events = true;
2560aca7a94dSNamhyung Kim 		if (!current_entry)
2561aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
2562aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
2563aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2564aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
2565aca7a94dSNamhyung Kim 		warn = bf;
2566aca7a94dSNamhyung Kim 	}
2567aca7a94dSNamhyung Kim 
256826270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
2569aca7a94dSNamhyung Kim 
2570aca7a94dSNamhyung Kim 	if (current_entry)
2571aca7a94dSNamhyung Kim 		menu->selection = evsel;
2572aca7a94dSNamhyung Kim }
2573aca7a94dSNamhyung Kim 
2574aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2575aca7a94dSNamhyung Kim 				int nr_events, const char *help,
25769783adf7SNamhyung Kim 				struct hist_browser_timer *hbt)
2577aca7a94dSNamhyung Kim {
2578aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
2579aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
2580dd00d486SJiri Olsa 	const char *title = "Available samples";
25819783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2582aca7a94dSNamhyung Kim 	int key;
2583aca7a94dSNamhyung Kim 
2584aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
2585aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
2586aca7a94dSNamhyung Kim 		return -1;
2587aca7a94dSNamhyung Kim 
2588aca7a94dSNamhyung Kim 	while (1) {
2589aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
2590aca7a94dSNamhyung Kim 
2591aca7a94dSNamhyung Kim 		switch (key) {
2592aca7a94dSNamhyung Kim 		case K_TIMER:
25939783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
2594aca7a94dSNamhyung Kim 
2595aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
2596aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
2597aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
2598aca7a94dSNamhyung Kim 			}
2599aca7a94dSNamhyung Kim 			continue;
2600aca7a94dSNamhyung Kim 		case K_RIGHT:
2601aca7a94dSNamhyung Kim 		case K_ENTER:
2602aca7a94dSNamhyung Kim 			if (!menu->selection)
2603aca7a94dSNamhyung Kim 				continue;
2604aca7a94dSNamhyung Kim 			pos = menu->selection;
2605aca7a94dSNamhyung Kim browse_hists:
2606aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
2607aca7a94dSNamhyung Kim 			/*
2608aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
2609aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
2610aca7a94dSNamhyung Kim 			 */
26119783adf7SNamhyung Kim 			if (hbt)
26129783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
2613aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
2614dd00d486SJiri Olsa 						       true, hbt,
2615064f1981SNamhyung Kim 						       menu->min_pcnt,
261668d80758SNamhyung Kim 						       menu->env);
2617aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
2618aca7a94dSNamhyung Kim 			switch (key) {
2619aca7a94dSNamhyung Kim 			case K_TAB:
2620aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
26219a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
2622aca7a94dSNamhyung Kim 				else
26239a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
2624aca7a94dSNamhyung Kim 				goto browse_hists;
2625aca7a94dSNamhyung Kim 			case K_UNTAB:
2626aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
26279a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
2628aca7a94dSNamhyung Kim 				else
2629d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
2630aca7a94dSNamhyung Kim 				goto browse_hists;
2631341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
2632aca7a94dSNamhyung Kim 			case 'q':
2633aca7a94dSNamhyung Kim 			case CTRL('c'):
2634aca7a94dSNamhyung Kim 				goto out;
263563ab1749SArnaldo Carvalho de Melo 			case K_ESC:
2636aca7a94dSNamhyung Kim 			default:
2637aca7a94dSNamhyung Kim 				continue;
2638aca7a94dSNamhyung Kim 			}
2639aca7a94dSNamhyung Kim 		case K_LEFT:
2640aca7a94dSNamhyung Kim 			continue;
2641aca7a94dSNamhyung Kim 		case K_ESC:
2642aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
2643aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
2644aca7a94dSNamhyung Kim 				continue;
2645aca7a94dSNamhyung Kim 			/* Fall thru */
2646aca7a94dSNamhyung Kim 		case 'q':
2647aca7a94dSNamhyung Kim 		case CTRL('c'):
2648aca7a94dSNamhyung Kim 			goto out;
2649aca7a94dSNamhyung Kim 		default:
2650aca7a94dSNamhyung Kim 			continue;
2651aca7a94dSNamhyung Kim 		}
2652aca7a94dSNamhyung Kim 	}
2653aca7a94dSNamhyung Kim 
2654aca7a94dSNamhyung Kim out:
2655aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
2656aca7a94dSNamhyung Kim 	return key;
2657aca7a94dSNamhyung Kim }
2658aca7a94dSNamhyung Kim 
2659316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2660fc24d7c2SNamhyung Kim 				 void *entry)
2661fc24d7c2SNamhyung Kim {
2662fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2663fc24d7c2SNamhyung Kim 
2664fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2665fc24d7c2SNamhyung Kim 		return true;
2666fc24d7c2SNamhyung Kim 
2667fc24d7c2SNamhyung Kim 	return false;
2668fc24d7c2SNamhyung Kim }
2669fc24d7c2SNamhyung Kim 
2670aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2671fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
267268d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
2673064f1981SNamhyung Kim 					   float min_pcnt,
2674ce80d3beSKan Liang 					   struct perf_env *env)
2675aca7a94dSNamhyung Kim {
2676aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
2677aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
2678aca7a94dSNamhyung Kim 		.b = {
2679aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
2680aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
2681aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
2682aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
2683fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
2684fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
2685aca7a94dSNamhyung Kim 			.priv	    = evlist,
2686aca7a94dSNamhyung Kim 		},
2687064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
268868d80758SNamhyung Kim 		.env = env,
2689aca7a94dSNamhyung Kim 	};
2690aca7a94dSNamhyung Kim 
2691aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
2692aca7a94dSNamhyung Kim 
26930050f7aaSArnaldo Carvalho de Melo 	evlist__for_each(evlist, pos) {
26947289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
2695aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
2696aca7a94dSNamhyung Kim 
2697aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
2698aca7a94dSNamhyung Kim 			menu.b.width = line_len;
2699aca7a94dSNamhyung Kim 	}
2700aca7a94dSNamhyung Kim 
2701fc24d7c2SNamhyung Kim 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2702aca7a94dSNamhyung Kim }
2703aca7a94dSNamhyung Kim 
2704aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
270568d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
2706064f1981SNamhyung Kim 				  float min_pcnt,
2707ce80d3beSKan Liang 				  struct perf_env *env)
2708aca7a94dSNamhyung Kim {
2709fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
2710fc24d7c2SNamhyung Kim 
2711fc24d7c2SNamhyung Kim single_entry:
2712fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
27139a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
2714fc24d7c2SNamhyung Kim 
2715fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
2716dd00d486SJiri Olsa 						false, hbt, min_pcnt,
2717064f1981SNamhyung Kim 						env);
2718aca7a94dSNamhyung Kim 	}
2719aca7a94dSNamhyung Kim 
2720fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
2721fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
2722fc24d7c2SNamhyung Kim 
2723fc24d7c2SNamhyung Kim 		nr_entries = 0;
27240050f7aaSArnaldo Carvalho de Melo 		evlist__for_each(evlist, pos) {
2725fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
2726fc24d7c2SNamhyung Kim 				nr_entries++;
27270050f7aaSArnaldo Carvalho de Melo 		}
2728fc24d7c2SNamhyung Kim 
2729fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
2730fc24d7c2SNamhyung Kim 			goto single_entry;
2731fc24d7c2SNamhyung Kim 	}
2732fc24d7c2SNamhyung Kim 
2733fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2734064f1981SNamhyung Kim 					       hbt, min_pcnt, env);
2735aca7a94dSNamhyung Kim }
2736