xref: /linux/tools/perf/ui/browsers/hists.c (revision ef1aec6000a7364a6c1ef0a6f9cbf8b98b685255)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
276b31a29SArnaldo Carvalho de Melo #include <dirent.h>
3a43783aeSArnaldo Carvalho de Melo #include <errno.h>
4fd20e811SArnaldo Carvalho de Melo #include <inttypes.h>
5aca7a94dSNamhyung Kim #include <stdio.h>
6aca7a94dSNamhyung Kim #include <stdlib.h>
7aca7a94dSNamhyung Kim #include <string.h>
8aca7a94dSNamhyung Kim #include <linux/rbtree.h>
93ca43b60SArnaldo Carvalho de Melo #include <linux/string.h>
10b0742e90SArnaldo Carvalho de Melo #include <sys/ttydefaults.h>
111d6c49dfSAndi Kleen #include <linux/time64.h>
127f7c536fSArnaldo Carvalho de Melo #include <linux/zalloc.h>
13aca7a94dSNamhyung Kim 
14b4209025SArnaldo Carvalho de Melo #include "../../util/debug.h"
154a3cec84SArnaldo Carvalho de Melo #include "../../util/dso.h"
16b10ba7f1SArnaldo Carvalho de Melo #include "../../util/callchain.h"
17aca7a94dSNamhyung Kim #include "../../util/evsel.h"
18aca7a94dSNamhyung Kim #include "../../util/evlist.h"
19f2a39fe8SArnaldo Carvalho de Melo #include "../../util/header.h"
20aca7a94dSNamhyung Kim #include "../../util/hist.h"
21209f4e70SArnaldo Carvalho de Melo #include "../../util/machine.h"
221101f69aSArnaldo Carvalho de Melo #include "../../util/map.h"
23209f4e70SArnaldo Carvalho de Melo #include "../../util/maps.h"
24daecf9e0SArnaldo Carvalho de Melo #include "../../util/symbol.h"
25d3300a3cSArnaldo Carvalho de Melo #include "../../util/map_symbol.h"
26d3300a3cSArnaldo Carvalho de Melo #include "../../util/branch.h"
27aca7a94dSNamhyung Kim #include "../../util/pstack.h"
28aca7a94dSNamhyung Kim #include "../../util/sort.h"
2942337a22SNamhyung Kim #include "../../util/top.h"
30e7ff8920SArnaldo Carvalho de Melo #include "../../util/thread.h"
317fa46cbfSJin Yao #include "../../util/block-info.h"
32f12ad272SIan Rogers #include "../../util/util.h"
3368d80758SNamhyung Kim #include "../../arch/common.h"
34aca7a94dSNamhyung Kim 
35f758990fSJiri Olsa #include "../browsers/hists.h"
36aca7a94dSNamhyung Kim #include "../helpline.h"
37aca7a94dSNamhyung Kim #include "../util.h"
38aca7a94dSNamhyung Kim #include "../ui.h"
39aca7a94dSNamhyung Kim #include "map.h"
40d755330cSJiri Olsa #include "annotate.h"
41632a5cabSArnaldo Carvalho de Melo #include "srcline.h"
42a067558eSArnaldo Carvalho de Melo #include "string2.h"
4358db1d6eSArnaldo Carvalho de Melo #include "units.h"
441d6c49dfSAndi Kleen #include "time-utils.h"
45aca7a94dSNamhyung Kim 
463052ba56SArnaldo Carvalho de Melo #include <linux/ctype.h>
473d689ed6SArnaldo Carvalho de Melo 
48f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
49f5951d56SNamhyung Kim 
50f016d24aSArnaldo Carvalho de Melo static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
51112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
52aca7a94dSNamhyung Kim 
53c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
54c3b78952SNamhyung Kim 					     float min_pcnt);
55c3b78952SNamhyung Kim 
56268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
57268397cbSNamhyung Kim {
585a1a99cdSJiri Olsa 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
59268397cbSNamhyung Kim }
60268397cbSNamhyung Kim 
614fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
624fabf3d1SHe Kuang {
634fabf3d1SHe Kuang 	struct rb_node *nd;
644fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
654fabf3d1SHe Kuang 	int unfolded_rows = 0;
664fabf3d1SHe Kuang 
672eb3d689SDavidlohr Bueso 	for (nd = rb_first_cached(&hists->entries);
684fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
69f5b763feSNamhyung Kim 	     nd = rb_hierarchy_next(nd)) {
704fabf3d1SHe Kuang 		struct hist_entry *he =
714fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
724fabf3d1SHe Kuang 
73f5b763feSNamhyung Kim 		if (he->leaf && he->unfolded)
744fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
754fabf3d1SHe Kuang 	}
764fabf3d1SHe Kuang 	return unfolded_rows;
774fabf3d1SHe Kuang }
784fabf3d1SHe Kuang 
79ef9ff601SArnaldo Carvalho de Melo static void hist_browser__set_title_space(struct hist_browser *hb)
80ef9ff601SArnaldo Carvalho de Melo {
81ef9ff601SArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
82ef9ff601SArnaldo Carvalho de Melo 	struct hists *hists = hb->hists;
83ef9ff601SArnaldo Carvalho de Melo 	struct perf_hpp_list *hpp_list = hists->hpp_list;
84ef9ff601SArnaldo Carvalho de Melo 
85ef9ff601SArnaldo Carvalho de Melo 	browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
86ef9ff601SArnaldo Carvalho de Melo }
87ef9ff601SArnaldo Carvalho de Melo 
88c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
89c3b78952SNamhyung Kim {
90c3b78952SNamhyung Kim 	u32 nr_entries;
91c3b78952SNamhyung Kim 
92f5b763feSNamhyung Kim 	if (symbol_conf.report_hierarchy)
93f5b763feSNamhyung Kim 		nr_entries = hb->nr_hierarchy_entries;
94f5b763feSNamhyung Kim 	else if (hist_browser__has_filter(hb))
95c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
96c3b78952SNamhyung Kim 	else
97c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
98c3b78952SNamhyung Kim 
994fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
100c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
101c3b78952SNamhyung Kim }
102c3b78952SNamhyung Kim 
103025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
104025bf7eaSArnaldo Carvalho de Melo {
105025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
106f8e6710dSJiri Olsa 	struct hists *hists = hb->hists;
107f8e6710dSJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
108ef9ff601SArnaldo Carvalho de Melo 	u16 index_row;
109025bf7eaSArnaldo Carvalho de Melo 
110ef9ff601SArnaldo Carvalho de Melo 	if (!hb->show_headers) {
111ef9ff601SArnaldo Carvalho de Melo 		browser->rows += browser->extra_title_lines;
112ef9ff601SArnaldo Carvalho de Melo 		browser->extra_title_lines = 0;
113ef9ff601SArnaldo Carvalho de Melo 		return;
114ef9ff601SArnaldo Carvalho de Melo 	}
115ef9ff601SArnaldo Carvalho de Melo 
116ef9ff601SArnaldo Carvalho de Melo 	browser->extra_title_lines = hpp_list->nr_header_lines;
117ef9ff601SArnaldo Carvalho de Melo 	browser->rows -= browser->extra_title_lines;
118025bf7eaSArnaldo Carvalho de Melo 	/*
119025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
1204d39c89fSIngo Molnar 	 * visible because we now show the header line(s).
121025bf7eaSArnaldo Carvalho de Melo 	 */
122025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
123025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
124025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
125025bf7eaSArnaldo Carvalho de Melo }
126025bf7eaSArnaldo Carvalho de Melo 
127357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
128aca7a94dSNamhyung Kim {
129357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
130357cfff1SArnaldo Carvalho de Melo 
131aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
132357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
133357cfff1SArnaldo Carvalho de Melo 	/*
134357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
135357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
136357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
137357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
138357cfff1SArnaldo Carvalho de Melo  	 */
139357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
140ca3ff33bSArnaldo Carvalho de Melo }
141ca3ff33bSArnaldo Carvalho de Melo 
14205e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
143aca7a94dSNamhyung Kim {
144c3b78952SNamhyung Kim 	/*
145c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
146c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
147c3b78952SNamhyung Kim 	 */
148c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
149c3b78952SNamhyung Kim 
150268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
151c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
152357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
15305e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
154aca7a94dSNamhyung Kim }
155aca7a94dSNamhyung Kim 
156aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
157aca7a94dSNamhyung Kim {
158aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
159aca7a94dSNamhyung Kim }
160aca7a94dSNamhyung Kim 
16105e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
162aca7a94dSNamhyung Kim {
1633698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
164aca7a94dSNamhyung Kim }
165aca7a94dSNamhyung Kim 
16605e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
167aca7a94dSNamhyung Kim {
1683698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
169aca7a94dSNamhyung Kim }
170aca7a94dSNamhyung Kim 
1713698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
172aca7a94dSNamhyung Kim {
1733698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
174aca7a94dSNamhyung Kim }
175aca7a94dSNamhyung Kim 
17605e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
177aca7a94dSNamhyung Kim {
1782a704fc8SMilian Wolff 	int n = 0;
179aca7a94dSNamhyung Kim 	struct rb_node *nd;
180aca7a94dSNamhyung Kim 
18105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
182aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
183aca7a94dSNamhyung Kim 		struct callchain_list *chain;
184aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
185aca7a94dSNamhyung Kim 
186aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
187aca7a94dSNamhyung Kim 			++n;
1880d3eb0b7SJin Yao 
189aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
190aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
191aca7a94dSNamhyung Kim 			if (folded_sign == '+')
192aca7a94dSNamhyung Kim 				break;
193aca7a94dSNamhyung Kim 		}
194aca7a94dSNamhyung Kim 
195aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
196aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
197aca7a94dSNamhyung Kim 	}
198aca7a94dSNamhyung Kim 
199aca7a94dSNamhyung Kim 	return n;
200aca7a94dSNamhyung Kim }
201aca7a94dSNamhyung Kim 
2024b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
2034b3a3212SNamhyung Kim {
2044b3a3212SNamhyung Kim 	struct callchain_list *chain;
2054b3a3212SNamhyung Kim 	char folded_sign = 0;
2064b3a3212SNamhyung Kim 	int n = 0;
2074b3a3212SNamhyung Kim 
2084b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
2094b3a3212SNamhyung Kim 		if (!folded_sign) {
2104b3a3212SNamhyung Kim 			/* only check first chain list entry */
2114b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2124b3a3212SNamhyung Kim 			if (folded_sign == '+')
2134b3a3212SNamhyung Kim 				return 1;
2144b3a3212SNamhyung Kim 		}
2154b3a3212SNamhyung Kim 		n++;
2164b3a3212SNamhyung Kim 	}
2174b3a3212SNamhyung Kim 
2184b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
2194b3a3212SNamhyung Kim 		if (!folded_sign) {
2204b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
2214b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2224b3a3212SNamhyung Kim 			if (folded_sign == '+')
2234b3a3212SNamhyung Kim 				return 1;
2244b3a3212SNamhyung Kim 		}
2254b3a3212SNamhyung Kim 		n++;
2264b3a3212SNamhyung Kim 	}
2274b3a3212SNamhyung Kim 
2284b3a3212SNamhyung Kim 	return n;
2294b3a3212SNamhyung Kim }
2304b3a3212SNamhyung Kim 
2318c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
2328c430a34SNamhyung Kim {
2338c430a34SNamhyung Kim 	return 1;
2348c430a34SNamhyung Kim }
2358c430a34SNamhyung Kim 
236aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
237aca7a94dSNamhyung Kim {
238aca7a94dSNamhyung Kim 	struct callchain_list *chain;
239aca7a94dSNamhyung Kim 	bool unfolded = false;
2402a704fc8SMilian Wolff 	int n = 0;
241aca7a94dSNamhyung Kim 
2424b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2434b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2448c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2458c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2464b3a3212SNamhyung Kim 
247aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
248aca7a94dSNamhyung Kim 		++n;
2490d3eb0b7SJin Yao 
2503698dab1SNamhyung Kim 		unfolded = chain->unfolded;
251aca7a94dSNamhyung Kim 	}
252aca7a94dSNamhyung Kim 
253aca7a94dSNamhyung Kim 	if (unfolded)
254aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
255aca7a94dSNamhyung Kim 
256aca7a94dSNamhyung Kim 	return n;
257aca7a94dSNamhyung Kim }
258aca7a94dSNamhyung Kim 
259aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
260aca7a94dSNamhyung Kim {
261aca7a94dSNamhyung Kim 	struct rb_node *nd;
262aca7a94dSNamhyung Kim 	int n = 0;
263aca7a94dSNamhyung Kim 
264aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
265aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
266aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
267aca7a94dSNamhyung Kim 	}
268aca7a94dSNamhyung Kim 
269aca7a94dSNamhyung Kim 	return n;
270aca7a94dSNamhyung Kim }
271aca7a94dSNamhyung Kim 
272f5b763feSNamhyung Kim static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
273f5b763feSNamhyung Kim 				bool include_children)
274f5b763feSNamhyung Kim {
275f5b763feSNamhyung Kim 	int count = 0;
276f5b763feSNamhyung Kim 	struct rb_node *node;
277f5b763feSNamhyung Kim 	struct hist_entry *child;
278f5b763feSNamhyung Kim 
279f5b763feSNamhyung Kim 	if (he->leaf)
280f5b763feSNamhyung Kim 		return callchain__count_rows(&he->sorted_chain);
281f5b763feSNamhyung Kim 
28279dded87SNamhyung Kim 	if (he->has_no_entry)
28379dded87SNamhyung Kim 		return 1;
28479dded87SNamhyung Kim 
2852eb3d689SDavidlohr Bueso 	node = rb_first_cached(&he->hroot_out);
286f5b763feSNamhyung Kim 	while (node) {
287f5b763feSNamhyung Kim 		float percent;
288f5b763feSNamhyung Kim 
289f5b763feSNamhyung Kim 		child = rb_entry(node, struct hist_entry, rb_node);
290f5b763feSNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
291f5b763feSNamhyung Kim 
292f5b763feSNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt) {
293f5b763feSNamhyung Kim 			count++;
294f5b763feSNamhyung Kim 
295f5b763feSNamhyung Kim 			if (include_children && child->unfolded)
296f5b763feSNamhyung Kim 				count += hierarchy_count_rows(hb, child, true);
297f5b763feSNamhyung Kim 		}
298f5b763feSNamhyung Kim 
299f5b763feSNamhyung Kim 		node = rb_next(node);
300f5b763feSNamhyung Kim 	}
301f5b763feSNamhyung Kim 	return count;
302f5b763feSNamhyung Kim }
303f5b763feSNamhyung Kim 
3043698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
305aca7a94dSNamhyung Kim {
3063698dab1SNamhyung Kim 	if (!he)
307aca7a94dSNamhyung Kim 		return false;
308aca7a94dSNamhyung Kim 
3093698dab1SNamhyung Kim 	if (!he->has_children)
310aca7a94dSNamhyung Kim 		return false;
311aca7a94dSNamhyung Kim 
3123698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
3133698dab1SNamhyung Kim 	return true;
3143698dab1SNamhyung Kim }
3153698dab1SNamhyung Kim 
3163698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
3173698dab1SNamhyung Kim {
3183698dab1SNamhyung Kim 	if (!cl)
3193698dab1SNamhyung Kim 		return false;
3203698dab1SNamhyung Kim 
3213698dab1SNamhyung Kim 	if (!cl->has_children)
3223698dab1SNamhyung Kim 		return false;
3233698dab1SNamhyung Kim 
3243698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
325aca7a94dSNamhyung Kim 	return true;
326aca7a94dSNamhyung Kim }
327aca7a94dSNamhyung Kim 
32805e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
329aca7a94dSNamhyung Kim {
33005e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
331aca7a94dSNamhyung Kim 
33205e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
333aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
334aca7a94dSNamhyung Kim 		struct callchain_list *chain;
335aca7a94dSNamhyung Kim 		bool first = true;
336aca7a94dSNamhyung Kim 
337aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
338aca7a94dSNamhyung Kim 			if (first) {
339aca7a94dSNamhyung Kim 				first = false;
3403698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
341aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
342aca7a94dSNamhyung Kim 			} else
3433698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
344aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
345aca7a94dSNamhyung Kim 		}
346aca7a94dSNamhyung Kim 
347aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
348aca7a94dSNamhyung Kim 	}
349aca7a94dSNamhyung Kim }
350aca7a94dSNamhyung Kim 
351a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
352a7444af6SNamhyung Kim 					       bool has_sibling)
353aca7a94dSNamhyung Kim {
354aca7a94dSNamhyung Kim 	struct callchain_list *chain;
355aca7a94dSNamhyung Kim 
356a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3573698dab1SNamhyung Kim 	chain->has_children = has_sibling;
358a7444af6SNamhyung Kim 
35990989035SAndres Freund 	if (!list_empty(&node->val)) {
36082162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3613698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
36282162b5aSNamhyung Kim 	}
363aca7a94dSNamhyung Kim 
36405e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
365aca7a94dSNamhyung Kim }
366aca7a94dSNamhyung Kim 
36705e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
368aca7a94dSNamhyung Kim {
369a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
370a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
371aca7a94dSNamhyung Kim 
37205e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
373aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3758c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3768c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3774b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
378aca7a94dSNamhyung Kim 	}
379aca7a94dSNamhyung Kim }
380aca7a94dSNamhyung Kim 
38105e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
382aca7a94dSNamhyung Kim {
383f5b763feSNamhyung Kim 	if (he->init_have_children)
384f5b763feSNamhyung Kim 		return;
385f5b763feSNamhyung Kim 
386f5b763feSNamhyung Kim 	if (he->leaf) {
3873698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
38805e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
389f5b763feSNamhyung Kim 	} else {
3902eb3d689SDavidlohr Bueso 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
391aca7a94dSNamhyung Kim 	}
392f5b763feSNamhyung Kim 
393f5b763feSNamhyung Kim 	he->init_have_children = true;
394aca7a94dSNamhyung Kim }
395aca7a94dSNamhyung Kim 
396bdc633feSArnaldo Carvalho de Melo static bool hist_browser__selection_has_children(struct hist_browser *browser)
397bdc633feSArnaldo Carvalho de Melo {
398bdc633feSArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
399bdc633feSArnaldo Carvalho de Melo 	struct map_symbol *ms = browser->selection;
400bdc633feSArnaldo Carvalho de Melo 
401bdc633feSArnaldo Carvalho de Melo 	if (!he || !ms)
402bdc633feSArnaldo Carvalho de Melo 		return false;
403bdc633feSArnaldo Carvalho de Melo 
404bdc633feSArnaldo Carvalho de Melo 	if (ms == &he->ms)
405bdc633feSArnaldo Carvalho de Melo 	       return he->has_children;
406bdc633feSArnaldo Carvalho de Melo 
407bdc633feSArnaldo Carvalho de Melo 	return container_of(ms, struct callchain_list, ms)->has_children;
408bdc633feSArnaldo Carvalho de Melo }
409bdc633feSArnaldo Carvalho de Melo 
410bdc633feSArnaldo Carvalho de Melo static bool hist_browser__selection_unfolded(struct hist_browser *browser)
411bdc633feSArnaldo Carvalho de Melo {
412bdc633feSArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
413bdc633feSArnaldo Carvalho de Melo 	struct map_symbol *ms = browser->selection;
414bdc633feSArnaldo Carvalho de Melo 
415bdc633feSArnaldo Carvalho de Melo 	if (!he || !ms)
416bdc633feSArnaldo Carvalho de Melo 		return false;
417bdc633feSArnaldo Carvalho de Melo 
418bdc633feSArnaldo Carvalho de Melo 	if (ms == &he->ms)
419bdc633feSArnaldo Carvalho de Melo 	       return he->unfolded;
420bdc633feSArnaldo Carvalho de Melo 
421bdc633feSArnaldo Carvalho de Melo 	return container_of(ms, struct callchain_list, ms)->unfolded;
422bdc633feSArnaldo Carvalho de Melo }
423bdc633feSArnaldo Carvalho de Melo 
424bdc633feSArnaldo Carvalho de Melo static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
425bdc633feSArnaldo Carvalho de Melo {
426bdc633feSArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
427bdc633feSArnaldo Carvalho de Melo 	struct map_symbol *ms = browser->selection;
428bdc633feSArnaldo Carvalho de Melo 	struct callchain_list *callchain_entry;
429bdc633feSArnaldo Carvalho de Melo 
430bdc633feSArnaldo Carvalho de Melo 	if (!he || !ms)
431bdc633feSArnaldo Carvalho de Melo 		return NULL;
432bdc633feSArnaldo Carvalho de Melo 
433bdc633feSArnaldo Carvalho de Melo 	if (ms == &he->ms) {
434bdc633feSArnaldo Carvalho de Melo 	       hist_entry__sym_snprintf(he, bf, size, 0);
435bdc633feSArnaldo Carvalho de Melo 	       return bf + 4; // skip the level, e.g. '[k] '
436bdc633feSArnaldo Carvalho de Melo 	}
437bdc633feSArnaldo Carvalho de Melo 
438bdc633feSArnaldo Carvalho de Melo 	callchain_entry = container_of(ms, struct callchain_list, ms);
439bdc633feSArnaldo Carvalho de Melo 	return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
440bdc633feSArnaldo Carvalho de Melo }
441bdc633feSArnaldo Carvalho de Melo 
44205e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
443aca7a94dSNamhyung Kim {
44405e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
4453698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
4463698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
4473698dab1SNamhyung Kim 	bool has_children;
448aca7a94dSNamhyung Kim 
4494938cf0cSWang Nan 	if (!he || !ms)
4504938cf0cSWang Nan 		return false;
4514938cf0cSWang Nan 
4523698dab1SNamhyung Kim 	if (ms == &he->ms)
4533698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
4543698dab1SNamhyung Kim 	else
4553698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
4563698dab1SNamhyung Kim 
4573698dab1SNamhyung Kim 	if (has_children) {
458f5b763feSNamhyung Kim 		int child_rows = 0;
459f5b763feSNamhyung Kim 
460aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
461c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
462aca7a94dSNamhyung Kim 
463f5b763feSNamhyung Kim 		if (he->leaf)
464f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
465f5b763feSNamhyung Kim 		else
466f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
467f5b763feSNamhyung Kim 
468f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
469f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
470f5b763feSNamhyung Kim 
471f5b763feSNamhyung Kim 		if (he->unfolded) {
472f5b763feSNamhyung Kim 			if (he->leaf)
4730d3eb0b7SJin Yao 				he->nr_rows = callchain__count_rows(
4740d3eb0b7SJin Yao 						&he->sorted_chain);
475aca7a94dSNamhyung Kim 			else
476f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
477f5b763feSNamhyung Kim 
478f5b763feSNamhyung Kim 			/* account grand children */
479f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
480f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
48179dded87SNamhyung Kim 
48279dded87SNamhyung Kim 			if (!he->leaf && he->nr_rows == 0) {
48379dded87SNamhyung Kim 				he->has_no_entry = true;
48479dded87SNamhyung Kim 				he->nr_rows = 1;
48579dded87SNamhyung Kim 			}
486f5b763feSNamhyung Kim 		} else {
487f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
488f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
489f5b763feSNamhyung Kim 
49079dded87SNamhyung Kim 			if (he->has_no_entry)
49179dded87SNamhyung Kim 				he->has_no_entry = false;
49279dded87SNamhyung Kim 
493aca7a94dSNamhyung Kim 			he->nr_rows = 0;
494f5b763feSNamhyung Kim 		}
495c3b78952SNamhyung Kim 
496c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
497f5b763feSNamhyung Kim 
498f5b763feSNamhyung Kim 		if (he->leaf)
499c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
500f5b763feSNamhyung Kim 		else
501f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
502aca7a94dSNamhyung Kim 
503aca7a94dSNamhyung Kim 		return true;
504aca7a94dSNamhyung Kim 	}
505aca7a94dSNamhyung Kim 
506aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
507aca7a94dSNamhyung Kim 	return false;
508aca7a94dSNamhyung Kim }
509aca7a94dSNamhyung Kim 
51005e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
511aca7a94dSNamhyung Kim {
512aca7a94dSNamhyung Kim 	int n = 0;
513aca7a94dSNamhyung Kim 	struct rb_node *nd;
514aca7a94dSNamhyung Kim 
51505e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
516aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
517aca7a94dSNamhyung Kim 		struct callchain_list *chain;
518aca7a94dSNamhyung Kim 		bool has_children = false;
519aca7a94dSNamhyung Kim 
520aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
521aca7a94dSNamhyung Kim 			++n;
5223698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
5233698dab1SNamhyung Kim 			has_children = chain->has_children;
524aca7a94dSNamhyung Kim 		}
525aca7a94dSNamhyung Kim 
526aca7a94dSNamhyung Kim 		if (has_children)
527aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
528aca7a94dSNamhyung Kim 	}
529aca7a94dSNamhyung Kim 
530aca7a94dSNamhyung Kim 	return n;
531aca7a94dSNamhyung Kim }
532aca7a94dSNamhyung Kim 
533aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
534aca7a94dSNamhyung Kim {
535aca7a94dSNamhyung Kim 	struct callchain_list *chain;
536aca7a94dSNamhyung Kim 	bool has_children = false;
537aca7a94dSNamhyung Kim 	int n = 0;
538aca7a94dSNamhyung Kim 
539aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
540aca7a94dSNamhyung Kim 		++n;
5413698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
5423698dab1SNamhyung Kim 		has_children = chain->has_children;
543aca7a94dSNamhyung Kim 	}
544aca7a94dSNamhyung Kim 
545aca7a94dSNamhyung Kim 	if (has_children)
546aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
547aca7a94dSNamhyung Kim 
548aca7a94dSNamhyung Kim 	return n;
549aca7a94dSNamhyung Kim }
550aca7a94dSNamhyung Kim 
551aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
552aca7a94dSNamhyung Kim {
553aca7a94dSNamhyung Kim 	struct rb_node *nd;
554aca7a94dSNamhyung Kim 	int n = 0;
555aca7a94dSNamhyung Kim 
556aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
557aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
558aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
559aca7a94dSNamhyung Kim 	}
560aca7a94dSNamhyung Kim 
561aca7a94dSNamhyung Kim 	return n;
562aca7a94dSNamhyung Kim }
563aca7a94dSNamhyung Kim 
564492b1010SNamhyung Kim static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
565492b1010SNamhyung Kim 				 bool unfold __maybe_unused)
566492b1010SNamhyung Kim {
567492b1010SNamhyung Kim 	float percent;
568492b1010SNamhyung Kim 	struct rb_node *nd;
569492b1010SNamhyung Kim 	struct hist_entry *child;
570492b1010SNamhyung Kim 	int n = 0;
571492b1010SNamhyung Kim 
5722eb3d689SDavidlohr Bueso 	for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
573492b1010SNamhyung Kim 		child = rb_entry(nd, struct hist_entry, rb_node);
574492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
575492b1010SNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt)
576492b1010SNamhyung Kim 			n++;
577492b1010SNamhyung Kim 	}
578492b1010SNamhyung Kim 
579492b1010SNamhyung Kim 	return n;
580492b1010SNamhyung Kim }
581492b1010SNamhyung Kim 
582f6b8436bSNamhyung Kim static void hist_entry__set_folding(struct hist_entry *he,
583492b1010SNamhyung Kim 				    struct hist_browser *hb, bool unfold)
584aca7a94dSNamhyung Kim {
58505e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
5863698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
587aca7a94dSNamhyung Kim 
5883698dab1SNamhyung Kim 	if (he->has_children) {
589492b1010SNamhyung Kim 		int n;
590492b1010SNamhyung Kim 
591492b1010SNamhyung Kim 		if (he->leaf)
592492b1010SNamhyung Kim 			n = callchain__set_folding(&he->sorted_chain, unfold);
593492b1010SNamhyung Kim 		else
594492b1010SNamhyung Kim 			n = hierarchy_set_folding(hb, he, unfold);
595492b1010SNamhyung Kim 
59605e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
597aca7a94dSNamhyung Kim 	} else
59805e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
599aca7a94dSNamhyung Kim }
600aca7a94dSNamhyung Kim 
601f6b8436bSNamhyung Kim static void
602f6b8436bSNamhyung Kim __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
603aca7a94dSNamhyung Kim {
604f6b8436bSNamhyung Kim 	struct rb_node *nd;
605f6b8436bSNamhyung Kim 	struct hist_entry *he;
606492b1010SNamhyung Kim 	double percent;
607aca7a94dSNamhyung Kim 
608f6b8436bSNamhyung Kim 	nd = rb_first_cached(&browser->hists->entries);
609f6b8436bSNamhyung Kim 	while (nd) {
610f6b8436bSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
611f6b8436bSNamhyung Kim 
612f6b8436bSNamhyung Kim 		/* set folding state even if it's currently folded */
613f6b8436bSNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
614f6b8436bSNamhyung Kim 
615f6b8436bSNamhyung Kim 		hist_entry__set_folding(he, browser, unfold);
616f6b8436bSNamhyung Kim 
617492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(he);
618492b1010SNamhyung Kim 		if (he->filtered || percent < browser->min_pcnt)
619f6b8436bSNamhyung Kim 			continue;
620492b1010SNamhyung Kim 
621492b1010SNamhyung Kim 		if (!he->depth || unfold)
622492b1010SNamhyung Kim 			browser->nr_hierarchy_entries++;
623492b1010SNamhyung Kim 		if (he->leaf)
624c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
62579dded87SNamhyung Kim 		else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
62679dded87SNamhyung Kim 			browser->nr_hierarchy_entries++;
62779dded87SNamhyung Kim 			he->has_no_entry = true;
62879dded87SNamhyung Kim 			he->nr_rows = 1;
62979dded87SNamhyung Kim 		} else
63079dded87SNamhyung Kim 			he->has_no_entry = false;
631aca7a94dSNamhyung Kim 	}
632aca7a94dSNamhyung Kim }
633aca7a94dSNamhyung Kim 
63405e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
635aca7a94dSNamhyung Kim {
636492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
637c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
638c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
639c3b78952SNamhyung Kim 
640c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
641aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
64205e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
643aca7a94dSNamhyung Kim }
644aca7a94dSNamhyung Kim 
6450e3fa7a7SJiri Olsa static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
6460e3fa7a7SJiri Olsa {
6470e3fa7a7SJiri Olsa 	if (!browser->he_selection)
6480e3fa7a7SJiri Olsa 		return;
6490e3fa7a7SJiri Olsa 
650f6b8436bSNamhyung Kim 	if (unfold == browser->he_selection->unfolded)
651f6b8436bSNamhyung Kim 		return;
652f6b8436bSNamhyung Kim 
653f6b8436bSNamhyung Kim 	hist_browser__toggle_fold(browser);
6540e3fa7a7SJiri Olsa }
6550e3fa7a7SJiri Olsa 
656aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
657aca7a94dSNamhyung Kim {
658aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
659aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
660aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
661aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
662aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
663aca7a94dSNamhyung Kim }
664aca7a94dSNamhyung Kim 
6655b91a86fSJiri Olsa static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
6665b91a86fSJiri Olsa {
6675b91a86fSJiri Olsa 	return browser->title ? browser->title(browser, bf, size) : 0;
6685b91a86fSJiri Olsa }
6695b91a86fSJiri Olsa 
670376c3c22SArnaldo Carvalho de Melo static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
671aca7a94dSNamhyung Kim {
672aca7a94dSNamhyung Kim 	switch (key) {
673fa5df943SNamhyung Kim 	case K_TIMER: {
674d10ec006SArnaldo Carvalho de Melo 		struct hist_browser_timer *hbt = browser->hbt;
6750f0abbacSNamhyung Kim 		struct evsel *evsel = hists_to_evsel(browser->hists);
676fa5df943SNamhyung Kim 		u64 nr_entries;
677ceb75476SLeo Yan 
678ceb75476SLeo Yan 		WARN_ON_ONCE(!hbt);
679ceb75476SLeo Yan 
680ceb75476SLeo Yan 		if (hbt)
6819783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
682fa5df943SNamhyung Kim 
683d10ec006SArnaldo Carvalho de Melo 		if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
684112f761fSNamhyung Kim 			hist_browser__update_nr_entries(browser);
685fa5df943SNamhyung Kim 
686c3b78952SNamhyung Kim 		nr_entries = hist_browser__nr_entries(browser);
687fa5df943SNamhyung Kim 		ui_browser__update_nr_entries(&browser->b, nr_entries);
688aca7a94dSNamhyung Kim 
68906cc1a47SKan Liang 		if (warn_lost_event &&
6900f0abbacSNamhyung Kim 		    (evsel->evlist->stats.nr_lost_warned !=
6910f0abbacSNamhyung Kim 		     evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
6920f0abbacSNamhyung Kim 			evsel->evlist->stats.nr_lost_warned =
6930f0abbacSNamhyung Kim 				evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
69405e8b080SArnaldo Carvalho de Melo 			ui_browser__warn_lost_events(&browser->b);
695aca7a94dSNamhyung Kim 		}
696aca7a94dSNamhyung Kim 
697376c3c22SArnaldo Carvalho de Melo 		hist_browser__title(browser, title, size);
69805e8b080SArnaldo Carvalho de Melo 		ui_browser__show_title(&browser->b, title);
699d10ec006SArnaldo Carvalho de Melo 		break;
700fa5df943SNamhyung Kim 	}
701aca7a94dSNamhyung Kim 	case 'D': { /* Debug */
702d10ec006SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
703aca7a94dSNamhyung Kim 		static int seq;
704d10ec006SArnaldo Carvalho de Melo 
705aca7a94dSNamhyung Kim 		ui_helpline__pop();
706fdae6400SArnaldo Carvalho de Melo 		ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
707d10ec006SArnaldo Carvalho de Melo 				   seq++, browser->b.nr_entries, browser->hists->nr_entries,
708d10ec006SArnaldo Carvalho de Melo 				   browser->b.extra_title_lines, browser->b.rows,
709d10ec006SArnaldo Carvalho de Melo 				   browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
710aca7a94dSNamhyung Kim 	}
711aca7a94dSNamhyung Kim 		break;
712aca7a94dSNamhyung Kim 	case 'C':
713aca7a94dSNamhyung Kim 		/* Collapse the whole world. */
71405e8b080SArnaldo Carvalho de Melo 		hist_browser__set_folding(browser, false);
715aca7a94dSNamhyung Kim 		break;
7160e3fa7a7SJiri Olsa 	case 'c':
7170e3fa7a7SJiri Olsa 		/* Collapse the selected entry. */
7180e3fa7a7SJiri Olsa 		hist_browser__set_folding_selected(browser, false);
7190e3fa7a7SJiri Olsa 		break;
720aca7a94dSNamhyung Kim 	case 'E':
721aca7a94dSNamhyung Kim 		/* Expand the whole world. */
72205e8b080SArnaldo Carvalho de Melo 		hist_browser__set_folding(browser, true);
723aca7a94dSNamhyung Kim 		break;
7240e3fa7a7SJiri Olsa 	case 'e':
725f6b8436bSNamhyung Kim 		/* Toggle expand/collapse the selected entry. */
726f6b8436bSNamhyung Kim 		hist_browser__toggle_fold(browser);
7270e3fa7a7SJiri Olsa 		break;
728025bf7eaSArnaldo Carvalho de Melo 	case 'H':
729025bf7eaSArnaldo Carvalho de Melo 		browser->show_headers = !browser->show_headers;
730025bf7eaSArnaldo Carvalho de Melo 		hist_browser__update_rows(browser);
731025bf7eaSArnaldo Carvalho de Melo 		break;
7329218a913SArnaldo Carvalho de Melo 	case '+':
73305e8b080SArnaldo Carvalho de Melo 		if (hist_browser__toggle_fold(browser))
734aca7a94dSNamhyung Kim 			break;
735aca7a94dSNamhyung Kim 		/* fall thru */
736aca7a94dSNamhyung Kim 	default:
737d10ec006SArnaldo Carvalho de Melo 		return -1;
738aca7a94dSNamhyung Kim 	}
739d10ec006SArnaldo Carvalho de Melo 
740d10ec006SArnaldo Carvalho de Melo 	return 0;
741d10ec006SArnaldo Carvalho de Melo }
742d10ec006SArnaldo Carvalho de Melo 
743d10ec006SArnaldo Carvalho de Melo int hist_browser__run(struct hist_browser *browser, const char *help,
744d10ec006SArnaldo Carvalho de Melo 		      bool warn_lost_event, int key)
745d10ec006SArnaldo Carvalho de Melo {
746d10ec006SArnaldo Carvalho de Melo 	char title[160];
747d10ec006SArnaldo Carvalho de Melo 	struct hist_browser_timer *hbt = browser->hbt;
748d10ec006SArnaldo Carvalho de Melo 	int delay_secs = hbt ? hbt->refresh : 0;
749d10ec006SArnaldo Carvalho de Melo 
750d10ec006SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
751d10ec006SArnaldo Carvalho de Melo 	browser->b.nr_entries = hist_browser__nr_entries(browser);
752d10ec006SArnaldo Carvalho de Melo 
753d10ec006SArnaldo Carvalho de Melo 	hist_browser__title(browser, title, sizeof(title));
754d10ec006SArnaldo Carvalho de Melo 
755d10ec006SArnaldo Carvalho de Melo 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
756d10ec006SArnaldo Carvalho de Melo 		return -1;
757d10ec006SArnaldo Carvalho de Melo 
758376c3c22SArnaldo Carvalho de Melo 	if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
759d10ec006SArnaldo Carvalho de Melo 		goto out;
760d10ec006SArnaldo Carvalho de Melo 
761d10ec006SArnaldo Carvalho de Melo 	while (1) {
762d10ec006SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
763d10ec006SArnaldo Carvalho de Melo 
764376c3c22SArnaldo Carvalho de Melo 		if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
765d10ec006SArnaldo Carvalho de Melo 			break;
766aca7a94dSNamhyung Kim 	}
767aca7a94dSNamhyung Kim out:
76805e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
769aca7a94dSNamhyung Kim 	return key;
770aca7a94dSNamhyung Kim }
771aca7a94dSNamhyung Kim 
77239ee533fSNamhyung Kim struct callchain_print_arg {
77339ee533fSNamhyung Kim 	/* for hists browser */
77439ee533fSNamhyung Kim 	off_t	row_offset;
77539ee533fSNamhyung Kim 	bool	is_current_entry;
77639ee533fSNamhyung Kim 
77739ee533fSNamhyung Kim 	/* for file dump */
77839ee533fSNamhyung Kim 	FILE	*fp;
77939ee533fSNamhyung Kim 	int	printed;
78039ee533fSNamhyung Kim };
78139ee533fSNamhyung Kim 
78239ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
78339ee533fSNamhyung Kim 					 struct callchain_list *chain,
78439ee533fSNamhyung Kim 					 const char *str, int offset,
78539ee533fSNamhyung Kim 					 unsigned short row,
78639ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
78739ee533fSNamhyung Kim 
788f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
789f4536dddSNamhyung Kim 					       struct callchain_list *chain,
79039ee533fSNamhyung Kim 					       const char *str, int offset,
79139ee533fSNamhyung Kim 					       unsigned short row,
79239ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
793f4536dddSNamhyung Kim {
794f4536dddSNamhyung Kim 	int color, width;
79539ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
79670e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
797f4536dddSNamhyung Kim 
798f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
799f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
800f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
801f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
802f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
80339ee533fSNamhyung Kim 		arg->is_current_entry = true;
804f4536dddSNamhyung Kim 	}
805f4536dddSNamhyung Kim 
806f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
807ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
80826270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
809517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
81070e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
81126270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
812f4536dddSNamhyung Kim }
813f4536dddSNamhyung Kim 
81439ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
81539ee533fSNamhyung Kim 						  struct callchain_list *chain,
81639ee533fSNamhyung Kim 						  const char *str, int offset,
81739ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
81839ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
81939ee533fSNamhyung Kim {
82039ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
82139ee533fSNamhyung Kim 
82239ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
82339ee533fSNamhyung Kim 				folded_sign, str);
82439ee533fSNamhyung Kim }
82539ee533fSNamhyung Kim 
82639ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
82739ee533fSNamhyung Kim 				     unsigned short row);
82839ee533fSNamhyung Kim 
82939ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
83039ee533fSNamhyung Kim 					    unsigned short row)
83139ee533fSNamhyung Kim {
83239ee533fSNamhyung Kim 	return browser->b.rows == row;
83339ee533fSNamhyung Kim }
83439ee533fSNamhyung Kim 
83539ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
83639ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
83739ee533fSNamhyung Kim {
83839ee533fSNamhyung Kim 	return false;
83939ee533fSNamhyung Kim }
84039ee533fSNamhyung Kim 
841aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
842aca7a94dSNamhyung Kim 
84318bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
84418bb8381SNamhyung Kim 					     struct callchain_node *node,
84518bb8381SNamhyung Kim 					     struct callchain_list *chain,
84618bb8381SNamhyung Kim 					     unsigned short row, u64 total,
84718bb8381SNamhyung Kim 					     bool need_percent, int offset,
84818bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
84918bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
85018bb8381SNamhyung Kim {
85118bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
852fef51ecdSJin Yao 	char buf[64], *alloc_str2;
85318bb8381SNamhyung Kim 	const char *str;
8542a704fc8SMilian Wolff 	int ret = 1;
85518bb8381SNamhyung Kim 
85618bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
85718bb8381SNamhyung Kim 		arg->row_offset--;
85818bb8381SNamhyung Kim 		return 0;
85918bb8381SNamhyung Kim 	}
86018bb8381SNamhyung Kim 
86118bb8381SNamhyung Kim 	alloc_str = NULL;
862fef51ecdSJin Yao 	alloc_str2 = NULL;
863fef51ecdSJin Yao 
86418bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
86518bb8381SNamhyung Kim 				       browser->show_dso);
86618bb8381SNamhyung Kim 
867fef51ecdSJin Yao 	if (symbol_conf.show_branchflag_count) {
868c4ee0625SJin Yao 		callchain_list_counts__printf_value(chain, NULL,
869fef51ecdSJin Yao 						    buf, sizeof(buf));
87018bb8381SNamhyung Kim 
871fef51ecdSJin Yao 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
872fef51ecdSJin Yao 			str = "Not enough memory!";
873fef51ecdSJin Yao 		else
874fef51ecdSJin Yao 			str = alloc_str2;
875fef51ecdSJin Yao 	}
876fef51ecdSJin Yao 
877fef51ecdSJin Yao 	if (need_percent) {
87818bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
87918bb8381SNamhyung Kim 						total);
88018bb8381SNamhyung Kim 
88118bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
88218bb8381SNamhyung Kim 			str = "Not enough memory!";
88318bb8381SNamhyung Kim 		else
88418bb8381SNamhyung Kim 			str = alloc_str;
88518bb8381SNamhyung Kim 	}
88618bb8381SNamhyung Kim 
88718bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
88818bb8381SNamhyung Kim 	free(alloc_str);
889fef51ecdSJin Yao 	free(alloc_str2);
8900d3eb0b7SJin Yao 
8912a704fc8SMilian Wolff 	return ret;
89218bb8381SNamhyung Kim }
89318bb8381SNamhyung Kim 
89459c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
89559c624e2SNamhyung Kim {
89659c624e2SNamhyung Kim 	struct callchain_node *child;
89759c624e2SNamhyung Kim 
89859c624e2SNamhyung Kim 	if (node == NULL)
89959c624e2SNamhyung Kim 		return false;
90059c624e2SNamhyung Kim 
90159c624e2SNamhyung Kim 	if (rb_next(node))
90259c624e2SNamhyung Kim 		return true;
90359c624e2SNamhyung Kim 
90459c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
90559c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
90659c624e2SNamhyung Kim }
90759c624e2SNamhyung Kim 
9084b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
9094b3a3212SNamhyung Kim 					     struct rb_root *root,
9104b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
91159c624e2SNamhyung Kim 					     u64 parent_total,
9124b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
9134b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
9144b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
9154b3a3212SNamhyung Kim {
9164b3a3212SNamhyung Kim 	struct rb_node *node;
9174b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
9184b3a3212SNamhyung Kim 	bool need_percent;
9194b3a3212SNamhyung Kim 
9204b3a3212SNamhyung Kim 	node = rb_first(root);
92159c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9224b3a3212SNamhyung Kim 
9234b3a3212SNamhyung Kim 	while (node) {
9244b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
9254b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
9264b3a3212SNamhyung Kim 		struct callchain_list *chain;
9274b3a3212SNamhyung Kim 		char folded_sign = ' ';
9284b3a3212SNamhyung Kim 		int first = true;
9294b3a3212SNamhyung Kim 		int extra_offset = 0;
9304b3a3212SNamhyung Kim 
9314b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9324b3a3212SNamhyung Kim 			bool was_first = first;
9334b3a3212SNamhyung Kim 
9344b3a3212SNamhyung Kim 			if (first)
9354b3a3212SNamhyung Kim 				first = false;
9364b3a3212SNamhyung Kim 			else if (need_percent)
9374b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
9384b3a3212SNamhyung Kim 
9394b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
9404b3a3212SNamhyung Kim 
9414b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
9424b3a3212SNamhyung Kim 							chain, row, total,
9434b3a3212SNamhyung Kim 							was_first && need_percent,
9444b3a3212SNamhyung Kim 							offset + extra_offset,
9454b3a3212SNamhyung Kim 							print, arg);
9464b3a3212SNamhyung Kim 
9474b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
9484b3a3212SNamhyung Kim 				goto out;
9494b3a3212SNamhyung Kim 
9504b3a3212SNamhyung Kim 			if (folded_sign == '+')
9514b3a3212SNamhyung Kim 				goto next;
9524b3a3212SNamhyung Kim 		}
9534b3a3212SNamhyung Kim 
9544b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
9554b3a3212SNamhyung Kim 			bool was_first = first;
9564b3a3212SNamhyung Kim 
9574b3a3212SNamhyung Kim 			if (first)
9584b3a3212SNamhyung Kim 				first = false;
9594b3a3212SNamhyung Kim 			else if (need_percent)
9604b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
9614b3a3212SNamhyung Kim 
9624b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
9634b3a3212SNamhyung Kim 
9644b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
9654b3a3212SNamhyung Kim 							chain, row, total,
9664b3a3212SNamhyung Kim 							was_first && need_percent,
9674b3a3212SNamhyung Kim 							offset + extra_offset,
9684b3a3212SNamhyung Kim 							print, arg);
9694b3a3212SNamhyung Kim 
9704b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
9714b3a3212SNamhyung Kim 				goto out;
9724b3a3212SNamhyung Kim 
9734b3a3212SNamhyung Kim 			if (folded_sign == '+')
9744b3a3212SNamhyung Kim 				break;
9754b3a3212SNamhyung Kim 		}
9764b3a3212SNamhyung Kim 
9774b3a3212SNamhyung Kim next:
9784b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
9794b3a3212SNamhyung Kim 			break;
9804b3a3212SNamhyung Kim 		node = next;
9814b3a3212SNamhyung Kim 	}
9824b3a3212SNamhyung Kim out:
9834b3a3212SNamhyung Kim 	return row - first_row;
9844b3a3212SNamhyung Kim }
9854b3a3212SNamhyung Kim 
9868c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
9878c430a34SNamhyung Kim 						struct callchain_list *chain,
9888c430a34SNamhyung Kim 						char *value_str, char *old_str)
9898c430a34SNamhyung Kim {
9908c430a34SNamhyung Kim 	char bf[1024];
9918c430a34SNamhyung Kim 	const char *str;
9928c430a34SNamhyung Kim 	char *new;
9938c430a34SNamhyung Kim 
9948c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
9958c430a34SNamhyung Kim 				       browser->show_dso);
9968c430a34SNamhyung Kim 	if (old_str) {
9978c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
9988c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
9998c430a34SNamhyung Kim 			new = NULL;
10008c430a34SNamhyung Kim 	} else {
10018c430a34SNamhyung Kim 		if (value_str) {
10028c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
10038c430a34SNamhyung Kim 				new = NULL;
10048c430a34SNamhyung Kim 		} else {
10058c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
10068c430a34SNamhyung Kim 				new = NULL;
10078c430a34SNamhyung Kim 		}
10088c430a34SNamhyung Kim 	}
10098c430a34SNamhyung Kim 	return new;
10108c430a34SNamhyung Kim }
10118c430a34SNamhyung Kim 
10128c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
10138c430a34SNamhyung Kim 					       struct rb_root *root,
10148c430a34SNamhyung Kim 					       unsigned short row, u64 total,
101559c624e2SNamhyung Kim 					       u64 parent_total,
10168c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
10178c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
10188c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
10198c430a34SNamhyung Kim {
10208c430a34SNamhyung Kim 	struct rb_node *node;
10218c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
10228c430a34SNamhyung Kim 	bool need_percent;
10238c430a34SNamhyung Kim 
10248c430a34SNamhyung Kim 	node = rb_first(root);
102559c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
10268c430a34SNamhyung Kim 
10278c430a34SNamhyung Kim 	while (node) {
10288c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
10298c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
10308c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
10318c430a34SNamhyung Kim 		int first = true;
10328c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
10338c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
10348c430a34SNamhyung Kim 
10358c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
10368c430a34SNamhyung Kim 			arg->row_offset--;
10378c430a34SNamhyung Kim 			goto next;
10388c430a34SNamhyung Kim 		}
10398c430a34SNamhyung Kim 
10408c430a34SNamhyung Kim 		if (need_percent) {
10418c430a34SNamhyung Kim 			char buf[64];
10428c430a34SNamhyung Kim 
10438c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
10448c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
10458c430a34SNamhyung Kim 				value_str = (char *)"<...>";
10468c430a34SNamhyung Kim 				goto do_print;
10478c430a34SNamhyung Kim 			}
10488c430a34SNamhyung Kim 			value_str_alloc = value_str;
10498c430a34SNamhyung Kim 		}
10508c430a34SNamhyung Kim 
10518c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
10528c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
10538c430a34SNamhyung Kim 						chain, value_str, chain_str);
10548c430a34SNamhyung Kim 			if (first) {
10558c430a34SNamhyung Kim 				first = false;
10568c430a34SNamhyung Kim 				first_chain = chain;
10578c430a34SNamhyung Kim 			}
10588c430a34SNamhyung Kim 
10598c430a34SNamhyung Kim 			if (chain_str == NULL) {
10608c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
10618c430a34SNamhyung Kim 				goto do_print;
10628c430a34SNamhyung Kim 			}
10638c430a34SNamhyung Kim 
10648c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
10658c430a34SNamhyung Kim 		}
10668c430a34SNamhyung Kim 
10678c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
10688c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
10698c430a34SNamhyung Kim 						chain, value_str, chain_str);
10708c430a34SNamhyung Kim 			if (first) {
10718c430a34SNamhyung Kim 				first = false;
10728c430a34SNamhyung Kim 				first_chain = chain;
10738c430a34SNamhyung Kim 			}
10748c430a34SNamhyung Kim 
10758c430a34SNamhyung Kim 			if (chain_str == NULL) {
10768c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
10778c430a34SNamhyung Kim 				goto do_print;
10788c430a34SNamhyung Kim 			}
10798c430a34SNamhyung Kim 
10808c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
10818c430a34SNamhyung Kim 		}
10828c430a34SNamhyung Kim 
10838c430a34SNamhyung Kim do_print:
10848c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
10858c430a34SNamhyung Kim 		free(value_str_alloc);
10868c430a34SNamhyung Kim 		free(chain_str_alloc);
10878c430a34SNamhyung Kim 
10888c430a34SNamhyung Kim next:
10898c430a34SNamhyung Kim 		if (is_output_full(browser, row))
10908c430a34SNamhyung Kim 			break;
10918c430a34SNamhyung Kim 		node = next;
10928c430a34SNamhyung Kim 	}
10938c430a34SNamhyung Kim 
10948c430a34SNamhyung Kim 	return row - first_row;
10958c430a34SNamhyung Kim }
10968c430a34SNamhyung Kim 
10970c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1098c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
109939ee533fSNamhyung Kim 					unsigned short row, u64 total,
11005eca104eSNamhyung Kim 					u64 parent_total,
110139ee533fSNamhyung Kim 					print_callchain_entry_fn print,
110239ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
110339ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
1104aca7a94dSNamhyung Kim {
1105aca7a94dSNamhyung Kim 	struct rb_node *node;
1106f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
11074087d11cSNamhyung Kim 	bool need_percent;
11085eca104eSNamhyung Kim 	u64 percent_total = total;
11095eca104eSNamhyung Kim 
11105eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
11115eca104eSNamhyung Kim 		percent_total = parent_total;
1112aca7a94dSNamhyung Kim 
1113c09a7e75SNamhyung Kim 	node = rb_first(root);
111459c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
11154087d11cSNamhyung Kim 
1116aca7a94dSNamhyung Kim 	while (node) {
1117aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1118aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
1119aca7a94dSNamhyung Kim 		struct callchain_list *chain;
1120aca7a94dSNamhyung Kim 		char folded_sign = ' ';
1121aca7a94dSNamhyung Kim 		int first = true;
1122aca7a94dSNamhyung Kim 		int extra_offset = 0;
1123aca7a94dSNamhyung Kim 
1124aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
1125aca7a94dSNamhyung Kim 			bool was_first = first;
1126aca7a94dSNamhyung Kim 
1127aca7a94dSNamhyung Kim 			if (first)
1128aca7a94dSNamhyung Kim 				first = false;
11294087d11cSNamhyung Kim 			else if (need_percent)
1130aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1131aca7a94dSNamhyung Kim 
1132aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1133aca7a94dSNamhyung Kim 
113418bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
11355eca104eSNamhyung Kim 							chain, row, percent_total,
113618bb8381SNamhyung Kim 							was_first && need_percent,
113718bb8381SNamhyung Kim 							offset + extra_offset,
113818bb8381SNamhyung Kim 							print, arg);
1139c09a7e75SNamhyung Kim 
114018bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1141aca7a94dSNamhyung Kim 				goto out;
114218bb8381SNamhyung Kim 
1143aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1144aca7a94dSNamhyung Kim 				break;
1145aca7a94dSNamhyung Kim 		}
1146aca7a94dSNamhyung Kim 
1147aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1148aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1149c09a7e75SNamhyung Kim 
11500c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
11515eca104eSNamhyung Kim 							    new_level, row, total,
11525eca104eSNamhyung Kim 							    child->children_hit,
115339ee533fSNamhyung Kim 							    print, arg, is_output_full);
1154aca7a94dSNamhyung Kim 		}
115539ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1156c09a7e75SNamhyung Kim 			break;
1157aca7a94dSNamhyung Kim 		node = next;
1158aca7a94dSNamhyung Kim 	}
1159aca7a94dSNamhyung Kim out:
1160aca7a94dSNamhyung Kim 	return row - first_row;
1161aca7a94dSNamhyung Kim }
1162aca7a94dSNamhyung Kim 
11630c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
11640c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
11650c841c6cSNamhyung Kim 					unsigned short row,
11660c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
11670c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
11680c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
11690c841c6cSNamhyung Kim {
11700c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
11715eca104eSNamhyung Kim 	u64 parent_total;
11720c841c6cSNamhyung Kim 	int printed;
11730c841c6cSNamhyung Kim 
11740c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
11755eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
11760c841c6cSNamhyung Kim 	else
11775eca104eSNamhyung Kim 		parent_total = entry->stat.period;
11780c841c6cSNamhyung Kim 
11790c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
11800c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
11815eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11825eca104eSNamhyung Kim 						total, parent_total, print, arg,
11835eca104eSNamhyung Kim 						is_output_full);
11840c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
11850c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
11865eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11875eca104eSNamhyung Kim 						total, parent_total, print, arg,
11885eca104eSNamhyung Kim 						is_output_full);
11890c841c6cSNamhyung Kim 	} else {
11900c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
11915eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
11925eca104eSNamhyung Kim 						total, parent_total, print, arg,
11935eca104eSNamhyung Kim 						is_output_full);
11940c841c6cSNamhyung Kim 	}
11950c841c6cSNamhyung Kim 
11960c841c6cSNamhyung Kim 	if (arg->is_current_entry)
11970c841c6cSNamhyung Kim 		browser->he_selection = entry;
11980c841c6cSNamhyung Kim 
11990c841c6cSNamhyung Kim 	return printed;
12000c841c6cSNamhyung Kim }
12010c841c6cSNamhyung Kim 
120289701460SNamhyung Kim struct hpp_arg {
120389701460SNamhyung Kim 	struct ui_browser *b;
120489701460SNamhyung Kim 	char folded_sign;
120589701460SNamhyung Kim 	bool current_entry;
120689701460SNamhyung Kim };
120789701460SNamhyung Kim 
120898ba1609SJiri Olsa int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
12092f6d9009SNamhyung Kim {
12102f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1211d675107cSNamhyung Kim 	int ret, len;
12122f6d9009SNamhyung Kim 	va_list args;
12132f6d9009SNamhyung Kim 	double percent;
12142f6d9009SNamhyung Kim 
12152f6d9009SNamhyung Kim 	va_start(args, fmt);
1216d675107cSNamhyung Kim 	len = va_arg(args, int);
12172f6d9009SNamhyung Kim 	percent = va_arg(args, double);
12182f6d9009SNamhyung Kim 	va_end(args);
12195aed9d24SNamhyung Kim 
122089701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
12215aed9d24SNamhyung Kim 
1222d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1223517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
122489701460SNamhyung Kim 
12255aed9d24SNamhyung Kim 	return ret;
1226f5951d56SNamhyung Kim }
1227f5951d56SNamhyung Kim 
1228fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
12295aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
12305aed9d24SNamhyung Kim {									\
12315aed9d24SNamhyung Kim 	return he->stat._field;						\
12325aed9d24SNamhyung Kim }									\
12335aed9d24SNamhyung Kim 									\
12342c5d4b4aSJiri Olsa static int								\
12355b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
12362c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
12375aed9d24SNamhyung Kim 				struct hist_entry *he)			\
12385aed9d24SNamhyung Kim {									\
12395b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
12402f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
12415aed9d24SNamhyung Kim }
1242f5951d56SNamhyung Kim 
12430434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
12440434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
12450434ddd2SNamhyung Kim {									\
12460434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
12470434ddd2SNamhyung Kim }									\
12480434ddd2SNamhyung Kim 									\
12490434ddd2SNamhyung Kim static int								\
12505b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
12510434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
12520434ddd2SNamhyung Kim 				struct hist_entry *he)			\
12530434ddd2SNamhyung Kim {									\
12540434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1255517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
12565b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1257d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
12585b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1259517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
12600434ddd2SNamhyung Kim 									\
12610434ddd2SNamhyung Kim 		return ret;						\
12620434ddd2SNamhyung Kim 	}								\
12635b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
12645b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
12650434ddd2SNamhyung Kim }
12660434ddd2SNamhyung Kim 
1267fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1268fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1269fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1270fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1271fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
12720434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
12735aed9d24SNamhyung Kim 
12745aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
12750434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1276f5951d56SNamhyung Kim 
1277f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1278f5951d56SNamhyung Kim {
1279f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1280f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1281f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1282f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1283f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1284f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1285f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1286f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1287f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1288f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
12890434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
12900434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
12914968ac8fSAndi Kleen 
12924968ac8fSAndi Kleen 	res_sample_init();
1293f5951d56SNamhyung Kim }
1294f5951d56SNamhyung Kim 
129505e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1296aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1297aca7a94dSNamhyung Kim 				    unsigned short row)
1298aca7a94dSNamhyung Kim {
12991240005eSJiri Olsa 	int printed = 0;
130067d25916SNamhyung Kim 	int width = browser->b.width;
1301aca7a94dSNamhyung Kim 	char folded_sign = ' ';
130205e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1303fabd37b8SArnaldo Carvalho de Melo 	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1304aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
130563a1a3d8SNamhyung Kim 	bool first = true;
13061240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1307aca7a94dSNamhyung Kim 
1308aca7a94dSNamhyung Kim 	if (current_entry) {
130905e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
131005e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1311aca7a94dSNamhyung Kim 	}
1312aca7a94dSNamhyung Kim 
1313fabd37b8SArnaldo Carvalho de Melo 	if (use_callchain) {
1314aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1315aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1316aca7a94dSNamhyung Kim 	}
1317aca7a94dSNamhyung Kim 
1318aca7a94dSNamhyung Kim 	if (row_offset == 0) {
131989701460SNamhyung Kim 		struct hpp_arg arg = {
132089701460SNamhyung Kim 			.b		= &browser->b,
132189701460SNamhyung Kim 			.folded_sign	= folded_sign,
132289701460SNamhyung Kim 			.current_entry	= current_entry,
132389701460SNamhyung Kim 		};
1324c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1325f5951d56SNamhyung Kim 
1326ef9ff601SArnaldo Carvalho de Melo 		ui_browser__gotorc(&browser->b, row, 0);
1327f5951d56SNamhyung Kim 
1328f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
132989fee709SArnaldo Carvalho de Melo 			char s[2048];
133089fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
133189fee709SArnaldo Carvalho de Melo 				.buf	= s,
133289fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
133389fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
133489fee709SArnaldo Carvalho de Melo 			};
133589fee709SArnaldo Carvalho de Melo 
1336361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1337361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1338e67d49a7SNamhyung Kim 				continue;
1339e67d49a7SNamhyung Kim 
1340fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1341fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1342fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1343fb821c9eSNamhyung Kim 			} else {
1344fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1345fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1346fb821c9eSNamhyung Kim 			}
1347fb821c9eSNamhyung Kim 
1348fb821c9eSNamhyung Kim 			if (first) {
1349fabd37b8SArnaldo Carvalho de Melo 				if (use_callchain) {
1350517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1351f5951d56SNamhyung Kim 					width -= 2;
1352f5951d56SNamhyung Kim 				}
135363a1a3d8SNamhyung Kim 				first = false;
1354fb821c9eSNamhyung Kim 			} else {
1355517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1356fb821c9eSNamhyung Kim 				width -= 2;
1357fb821c9eSNamhyung Kim 			}
1358f5951d56SNamhyung Kim 
13591240005eSJiri Olsa 			if (fmt->color) {
136089fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
136189fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
136289fee709SArnaldo Carvalho de Melo 				/*
136389fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
136489fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
136589fee709SArnaldo Carvalho de Melo 				 */
136689fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1367f5951d56SNamhyung Kim 			} else {
136889fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1369517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1370f5951d56SNamhyung Kim 			}
137189fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1372f5951d56SNamhyung Kim 		}
1373aca7a94dSNamhyung Kim 
1374aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
137505e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1376aca7a94dSNamhyung Kim 			width += 1;
1377aca7a94dSNamhyung Kim 
137826270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
137926d8b338SNamhyung Kim 
1380aca7a94dSNamhyung Kim 		++row;
1381aca7a94dSNamhyung Kim 		++printed;
1382aca7a94dSNamhyung Kim 	} else
1383aca7a94dSNamhyung Kim 		--row_offset;
1384aca7a94dSNamhyung Kim 
138562c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
138639ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
138739ee533fSNamhyung Kim 			.row_offset = row_offset,
138839ee533fSNamhyung Kim 			.is_current_entry = current_entry,
138939ee533fSNamhyung Kim 		};
1390c09a7e75SNamhyung Kim 
13910d3eb0b7SJin Yao 		printed += hist_browser__show_callchain(browser,
13920d3eb0b7SJin Yao 				entry, 1, row,
13930d3eb0b7SJin Yao 				hist_browser__show_callchain_entry,
13940d3eb0b7SJin Yao 				&arg,
13954b3a3212SNamhyung Kim 				hist_browser__check_output_full);
1396aca7a94dSNamhyung Kim 	}
1397aca7a94dSNamhyung Kim 
1398aca7a94dSNamhyung Kim 	return printed;
1399aca7a94dSNamhyung Kim }
1400aca7a94dSNamhyung Kim 
1401d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1402d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1403d0506edbSNamhyung Kim 					      unsigned short row,
14042dbbe9f2SNamhyung Kim 					      int level)
1405d0506edbSNamhyung Kim {
1406d0506edbSNamhyung Kim 	int printed = 0;
1407d0506edbSNamhyung Kim 	int width = browser->b.width;
1408d0506edbSNamhyung Kim 	char folded_sign = ' ';
1409d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1410d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1411d0506edbSNamhyung Kim 	bool first = true;
1412d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1413a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1414d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1415d0506edbSNamhyung Kim 		.b		= &browser->b,
1416d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1417d0506edbSNamhyung Kim 	};
1418d0506edbSNamhyung Kim 	int column = 0;
14192dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1420d0506edbSNamhyung Kim 
1421d0506edbSNamhyung Kim 	if (current_entry) {
1422d0506edbSNamhyung Kim 		browser->he_selection = entry;
1423d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1424d0506edbSNamhyung Kim 	}
1425d0506edbSNamhyung Kim 
1426d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1427d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1428d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1429d0506edbSNamhyung Kim 
1430d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1431d0506edbSNamhyung Kim 		row_offset--;
1432d0506edbSNamhyung Kim 		goto show_callchain;
1433d0506edbSNamhyung Kim 	}
1434d0506edbSNamhyung Kim 
1435ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
1436d0506edbSNamhyung Kim 
1437d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1438d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1439d0506edbSNamhyung Kim 	else
1440d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1441d0506edbSNamhyung Kim 
1442d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1443d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1444d0506edbSNamhyung Kim 
1445a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1446a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1447a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1448a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1449d0506edbSNamhyung Kim 		char s[2048];
1450d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1451d0506edbSNamhyung Kim 			.buf		= s,
1452d0506edbSNamhyung Kim 			.size		= sizeof(s),
1453d0506edbSNamhyung Kim 			.ptr		= &arg,
1454d0506edbSNamhyung Kim 		};
1455d0506edbSNamhyung Kim 
1456d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1457d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1458d0506edbSNamhyung Kim 			continue;
1459d0506edbSNamhyung Kim 
1460d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1461d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1462d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1463d0506edbSNamhyung Kim 		} else {
1464d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1465d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1466d0506edbSNamhyung Kim 		}
1467d0506edbSNamhyung Kim 
1468d0506edbSNamhyung Kim 		if (first) {
1469d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c ", folded_sign);
14703d9f4683SNamhyung Kim 			width -= 2;
1471d0506edbSNamhyung Kim 			first = false;
1472d0506edbSNamhyung Kim 		} else {
1473d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1474d0506edbSNamhyung Kim 			width -= 2;
1475d0506edbSNamhyung Kim 		}
1476d0506edbSNamhyung Kim 
1477d0506edbSNamhyung Kim 		if (fmt->color) {
1478d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1479d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1480d0506edbSNamhyung Kim 			/*
1481d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1482d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1483d0506edbSNamhyung Kim 			 */
1484d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1485d0506edbSNamhyung Kim 		} else {
1486d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1487d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1488d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1489d0506edbSNamhyung Kim 		}
1490d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1491d0506edbSNamhyung Kim 	}
1492d0506edbSNamhyung Kim 
1493b9bf911eSNamhyung Kim 	if (!first) {
1494d0506edbSNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1495d0506edbSNamhyung Kim 		width -= hierarchy_indent;
1496b9bf911eSNamhyung Kim 	}
1497d0506edbSNamhyung Kim 
1498d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1499d0506edbSNamhyung Kim 		char s[2048];
1500d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1501d0506edbSNamhyung Kim 			.buf		= s,
1502d0506edbSNamhyung Kim 			.size		= sizeof(s),
1503d0506edbSNamhyung Kim 			.ptr		= &arg,
1504d0506edbSNamhyung Kim 		};
1505d0506edbSNamhyung Kim 
1506d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1507d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1508d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1509d0506edbSNamhyung Kim 		} else {
1510d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1511d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1512d0506edbSNamhyung Kim 		}
1513d0506edbSNamhyung Kim 
15141b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1515131d51ebSNamhyung Kim 			if (first) {
1516131d51ebSNamhyung Kim 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1517131d51ebSNamhyung Kim 				first = false;
1518131d51ebSNamhyung Kim 			} else {
1519d0506edbSNamhyung Kim 				ui_browser__write_nstring(&browser->b, "", 2);
1520131d51ebSNamhyung Kim 			}
1521131d51ebSNamhyung Kim 
1522d0506edbSNamhyung Kim 			width -= 2;
1523d0506edbSNamhyung Kim 
1524d0506edbSNamhyung Kim 			/*
1525d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1526d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1527d0506edbSNamhyung Kim 			 * hierarchy mode.
1528d0506edbSNamhyung Kim 			 */
1529d0506edbSNamhyung Kim 			if (fmt->color) {
1530d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1531d0506edbSNamhyung Kim 			} else {
1532cb1fab91SNamhyung Kim 				int i = 0;
1533cb1fab91SNamhyung Kim 
1534d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
153532858480SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1536cb1fab91SNamhyung Kim 
1537cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1538cb1fab91SNamhyung Kim 					width++;
1539d0506edbSNamhyung Kim 			}
1540d0506edbSNamhyung Kim 		}
15411b2dbbf4SNamhyung Kim 	}
1542d0506edbSNamhyung Kim 
1543d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1544d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1545d0506edbSNamhyung Kim 		width += 1;
1546d0506edbSNamhyung Kim 
1547d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1548d0506edbSNamhyung Kim 
1549d0506edbSNamhyung Kim 	++row;
1550d0506edbSNamhyung Kim 	++printed;
1551d0506edbSNamhyung Kim 
1552d0506edbSNamhyung Kim show_callchain:
1553d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1554d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1555d0506edbSNamhyung Kim 			.row_offset = row_offset,
1556d0506edbSNamhyung Kim 		};
1557d0506edbSNamhyung Kim 
1558d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1559d0506edbSNamhyung Kim 					level + 1, row,
1560d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1561d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1562d0506edbSNamhyung Kim 	}
1563d0506edbSNamhyung Kim 
1564d0506edbSNamhyung Kim 	return printed;
1565d0506edbSNamhyung Kim }
1566d0506edbSNamhyung Kim 
156779dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
15682dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
156979dded87SNamhyung Kim {
157079dded87SNamhyung Kim 	int width = browser->b.width;
157179dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
157279dded87SNamhyung Kim 	bool first = true;
157379dded87SNamhyung Kim 	int column = 0;
157479dded87SNamhyung Kim 	int ret;
157579dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1576a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
15772dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
157879dded87SNamhyung Kim 
157979dded87SNamhyung Kim 	if (current_entry) {
158079dded87SNamhyung Kim 		browser->he_selection = NULL;
158179dded87SNamhyung Kim 		browser->selection = NULL;
158279dded87SNamhyung Kim 	}
158379dded87SNamhyung Kim 
1584ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
158579dded87SNamhyung Kim 
158679dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
158779dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
158879dded87SNamhyung Kim 	else
158979dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
159079dded87SNamhyung Kim 
159179dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
159279dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
159379dded87SNamhyung Kim 
1594a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1595a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1596a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1597a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
159879dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
159979dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
160079dded87SNamhyung Kim 			continue;
160179dded87SNamhyung Kim 
1602da1b0407SJiri Olsa 		ret = fmt->width(fmt, NULL, browser->hists);
160379dded87SNamhyung Kim 
160479dded87SNamhyung Kim 		if (first) {
160579dded87SNamhyung Kim 			/* for folded sign */
160679dded87SNamhyung Kim 			first = false;
160779dded87SNamhyung Kim 			ret++;
160879dded87SNamhyung Kim 		} else {
160979dded87SNamhyung Kim 			/* space between columns */
161079dded87SNamhyung Kim 			ret += 2;
161179dded87SNamhyung Kim 		}
161279dded87SNamhyung Kim 
161379dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
161479dded87SNamhyung Kim 		width -= ret;
161579dded87SNamhyung Kim 	}
161679dded87SNamhyung Kim 
16172dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
16182dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
161979dded87SNamhyung Kim 
162079dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
162179dded87SNamhyung Kim 		char buf[32];
162279dded87SNamhyung Kim 
162379dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
162479dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
162579dded87SNamhyung Kim 		width -= ret + 2;
162679dded87SNamhyung Kim 	}
162779dded87SNamhyung Kim 
162879dded87SNamhyung Kim 	/* The scroll bar isn't being used */
162979dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
163079dded87SNamhyung Kim 		width += 1;
163179dded87SNamhyung Kim 
163279dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
163379dded87SNamhyung Kim 	return 1;
163479dded87SNamhyung Kim }
163579dded87SNamhyung Kim 
163681a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
163781a888feSJiri Olsa {
163881a888feSJiri Olsa 	advance_hpp(hpp, inc);
163981a888feSJiri Olsa 	return hpp->size <= 0;
164081a888feSJiri Olsa }
164181a888feSJiri Olsa 
164269705b35SJiri Olsa static int
164369705b35SJiri Olsa hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
164469705b35SJiri Olsa 				 size_t size, int line)
164581a888feSJiri Olsa {
1646c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
164781a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
164881a888feSJiri Olsa 		.buf    = buf,
164981a888feSJiri Olsa 		.size   = size,
165081a888feSJiri Olsa 	};
165181a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
165281a888feSJiri Olsa 	size_t ret = 0;
1653c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
165429659ab4SJiri Olsa 	int span = 0;
165581a888feSJiri Olsa 
1656fabd37b8SArnaldo Carvalho de Melo 	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
165781a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
165881a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
165981a888feSJiri Olsa 			return ret;
166081a888feSJiri Olsa 	}
166181a888feSJiri Olsa 
1662f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1663361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
166481a888feSJiri Olsa 			continue;
166581a888feSJiri Olsa 
166629659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
166781a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
166881a888feSJiri Olsa 			break;
166981a888feSJiri Olsa 
167029659ab4SJiri Olsa 		if (span)
167129659ab4SJiri Olsa 			continue;
167229659ab4SJiri Olsa 
167381a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
167481a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
167581a888feSJiri Olsa 			break;
167681a888feSJiri Olsa 	}
167781a888feSJiri Olsa 
167881a888feSJiri Olsa 	return ret;
167981a888feSJiri Olsa }
168081a888feSJiri Olsa 
1681d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1682d8b92400SNamhyung Kim {
1683d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1684d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1685d8b92400SNamhyung Kim 		.buf    = buf,
1686d8b92400SNamhyung Kim 		.size   = size,
1687d8b92400SNamhyung Kim 	};
1688d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1689a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1690d8b92400SNamhyung Kim 	size_t ret = 0;
1691d8b92400SNamhyung Kim 	int column = 0;
16922dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1693a61a22f6SNamhyung Kim 	bool first_node, first_col;
1694d8b92400SNamhyung Kim 
1695d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, "  ");
1696d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1697d8b92400SNamhyung Kim 		return ret;
1698d8b92400SNamhyung Kim 
1699b9bf911eSNamhyung Kim 	first_node = true;
1700a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1701a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1702a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1703a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1704d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1705d8b92400SNamhyung Kim 			continue;
1706d8b92400SNamhyung Kim 
170729659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1708d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1709d8b92400SNamhyung Kim 			break;
1710d8b92400SNamhyung Kim 
1711d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1712d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1713d8b92400SNamhyung Kim 			break;
1714b9bf911eSNamhyung Kim 
1715b9bf911eSNamhyung Kim 		first_node = false;
1716d8b92400SNamhyung Kim 	}
1717d8b92400SNamhyung Kim 
1718b9bf911eSNamhyung Kim 	if (!first_node) {
1719d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
17202dbbe9f2SNamhyung Kim 				indent * HIERARCHY_INDENT, "");
1721d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1722d8b92400SNamhyung Kim 			return ret;
1723b9bf911eSNamhyung Kim 	}
1724d8b92400SNamhyung Kim 
1725a61a22f6SNamhyung Kim 	first_node = true;
1726a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1727a61a22f6SNamhyung Kim 		if (!first_node) {
1728d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1729d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1730d8b92400SNamhyung Kim 				break;
1731d8b92400SNamhyung Kim 		}
1732a61a22f6SNamhyung Kim 		first_node = false;
1733a61a22f6SNamhyung Kim 
1734a61a22f6SNamhyung Kim 		first_col = true;
1735a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1736a61a22f6SNamhyung Kim 			char *start;
1737a61a22f6SNamhyung Kim 
1738a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1739a61a22f6SNamhyung Kim 				continue;
1740a61a22f6SNamhyung Kim 
1741a61a22f6SNamhyung Kim 			if (!first_col) {
1742a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1743a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1744a61a22f6SNamhyung Kim 					break;
1745a61a22f6SNamhyung Kim 			}
1746a61a22f6SNamhyung Kim 			first_col = false;
1747d8b92400SNamhyung Kim 
174829659ab4SJiri Olsa 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1749d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1750d8b92400SNamhyung Kim 
17513ca43b60SArnaldo Carvalho de Melo 			start = strim(dummy_hpp.buf);
1752cb1fab91SNamhyung Kim 			ret = strlen(start);
1753cb1fab91SNamhyung Kim 
1754cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1755cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1756cb1fab91SNamhyung Kim 
1757d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1758d8b92400SNamhyung Kim 				break;
1759d8b92400SNamhyung Kim 		}
1760a61a22f6SNamhyung Kim 	}
1761d8b92400SNamhyung Kim 
1762d8b92400SNamhyung Kim 	return ret;
1763d8b92400SNamhyung Kim }
1764d8b92400SNamhyung Kim 
176501b4770dSJiri Olsa static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1766025bf7eaSArnaldo Carvalho de Melo {
176781a888feSJiri Olsa 	char headers[1024];
176881a888feSJiri Olsa 
1769d8b92400SNamhyung Kim 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1770d8b92400SNamhyung Kim 						   sizeof(headers));
177101b4770dSJiri Olsa 
1772e2cabf2aSNamhyung Kim 	ui_browser__gotorc_title(&browser->b, 0, 0);
1773025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
177426270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1775025bf7eaSArnaldo Carvalho de Melo }
1776025bf7eaSArnaldo Carvalho de Melo 
177701b4770dSJiri Olsa static void hists_browser__headers(struct hist_browser *browser)
177801b4770dSJiri Olsa {
177969705b35SJiri Olsa 	struct hists *hists = browser->hists;
178069705b35SJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
178169705b35SJiri Olsa 
178269705b35SJiri Olsa 	int line;
178369705b35SJiri Olsa 
178469705b35SJiri Olsa 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
178501b4770dSJiri Olsa 		char headers[1024];
178601b4770dSJiri Olsa 
178701b4770dSJiri Olsa 		hists_browser__scnprintf_headers(browser, headers,
178869705b35SJiri Olsa 						 sizeof(headers), line);
178901b4770dSJiri Olsa 
1790ef9ff601SArnaldo Carvalho de Melo 		ui_browser__gotorc_title(&browser->b, line, 0);
179101b4770dSJiri Olsa 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
179201b4770dSJiri Olsa 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
179301b4770dSJiri Olsa 	}
179469705b35SJiri Olsa }
179501b4770dSJiri Olsa 
179601b4770dSJiri Olsa static void hist_browser__show_headers(struct hist_browser *browser)
179701b4770dSJiri Olsa {
179801b4770dSJiri Olsa 	if (symbol_conf.report_hierarchy)
179901b4770dSJiri Olsa 		hists_browser__hierarchy_headers(browser);
180001b4770dSJiri Olsa 	else
180101b4770dSJiri Olsa 		hists_browser__headers(browser);
180201b4770dSJiri Olsa }
180301b4770dSJiri Olsa 
1804aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1805aca7a94dSNamhyung Kim {
1806aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1807aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1808aca7a94dSNamhyung Kim 
1809aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
18102eb3d689SDavidlohr Bueso 		browser->top = rb_first_cached(&hb->hists->entries);
1811aca7a94dSNamhyung Kim 	}
1812aca7a94dSNamhyung Kim }
1813aca7a94dSNamhyung Kim 
181405e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1815aca7a94dSNamhyung Kim {
1816aca7a94dSNamhyung Kim 	unsigned row = 0;
1817aca7a94dSNamhyung Kim 	struct rb_node *nd;
181805e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1819aca7a94dSNamhyung Kim 
182094e87a8bSArnaldo Carvalho de Melo 	if (hb->show_headers)
1821025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1822025bf7eaSArnaldo Carvalho de Melo 
182305e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1824979d2cacSWang Nan 	hb->he_selection = NULL;
1825979d2cacSWang Nan 	hb->selection = NULL;
1826aca7a94dSNamhyung Kim 
1827d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1828aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
182914135663SNamhyung Kim 		float percent;
1830aca7a94dSNamhyung Kim 
1831d0506edbSNamhyung Kim 		if (h->filtered) {
1832d0506edbSNamhyung Kim 			/* let it move to sibling */
1833d0506edbSNamhyung Kim 			h->unfolded = false;
1834aca7a94dSNamhyung Kim 			continue;
1835d0506edbSNamhyung Kim 		}
1836aca7a94dSNamhyung Kim 
18377fa46cbfSJin Yao 		if (symbol_conf.report_individual_block)
18387fa46cbfSJin Yao 			percent = block_info__total_cycles_percent(h);
18397fa46cbfSJin Yao 		else
184014135663SNamhyung Kim 			percent = hist_entry__get_percent_limit(h);
18417fa46cbfSJin Yao 
1842064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1843064f1981SNamhyung Kim 			continue;
1844064f1981SNamhyung Kim 
1845d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1846d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
18472dbbe9f2SNamhyung Kim 								  h->depth);
184879dded87SNamhyung Kim 			if (row == browser->rows)
184979dded87SNamhyung Kim 				break;
185079dded87SNamhyung Kim 
185179dded87SNamhyung Kim 			if (h->has_no_entry) {
1852a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
185379dded87SNamhyung Kim 				row++;
185479dded87SNamhyung Kim 			}
1855d0506edbSNamhyung Kim 		} else {
1856aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1857d0506edbSNamhyung Kim 		}
1858d0506edbSNamhyung Kim 
185962c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1860aca7a94dSNamhyung Kim 			break;
1861aca7a94dSNamhyung Kim 	}
1862aca7a94dSNamhyung Kim 
186394e87a8bSArnaldo Carvalho de Melo 	return row;
1864aca7a94dSNamhyung Kim }
1865aca7a94dSNamhyung Kim 
1866064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1867064f1981SNamhyung Kim 					     float min_pcnt)
1868aca7a94dSNamhyung Kim {
1869aca7a94dSNamhyung Kim 	while (nd != NULL) {
1870aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
187114135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1872064f1981SNamhyung Kim 
1873c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1874aca7a94dSNamhyung Kim 			return nd;
1875aca7a94dSNamhyung Kim 
1876d0506edbSNamhyung Kim 		/*
1877d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1878d0506edbSNamhyung Kim 		 * So move to sibling node.
1879d0506edbSNamhyung Kim 		 */
1880d0506edbSNamhyung Kim 		if (rb_next(nd))
1881aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1882d0506edbSNamhyung Kim 		else
1883d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1884aca7a94dSNamhyung Kim 	}
1885aca7a94dSNamhyung Kim 
1886aca7a94dSNamhyung Kim 	return NULL;
1887aca7a94dSNamhyung Kim }
1888aca7a94dSNamhyung Kim 
1889064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1890064f1981SNamhyung Kim 						  float min_pcnt)
1891aca7a94dSNamhyung Kim {
1892aca7a94dSNamhyung Kim 	while (nd != NULL) {
1893aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
189414135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1895064f1981SNamhyung Kim 
1896064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1897aca7a94dSNamhyung Kim 			return nd;
1898aca7a94dSNamhyung Kim 
1899d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1900aca7a94dSNamhyung Kim 	}
1901aca7a94dSNamhyung Kim 
1902aca7a94dSNamhyung Kim 	return NULL;
1903aca7a94dSNamhyung Kim }
1904aca7a94dSNamhyung Kim 
190505e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1906aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1907aca7a94dSNamhyung Kim {
1908aca7a94dSNamhyung Kim 	struct hist_entry *h;
1909aca7a94dSNamhyung Kim 	struct rb_node *nd;
1910aca7a94dSNamhyung Kim 	bool first = true;
1911064f1981SNamhyung Kim 	struct hist_browser *hb;
1912064f1981SNamhyung Kim 
1913064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1914aca7a94dSNamhyung Kim 
191505e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1916aca7a94dSNamhyung Kim 		return;
1917aca7a94dSNamhyung Kim 
191805e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1919aca7a94dSNamhyung Kim 
1920aca7a94dSNamhyung Kim 	switch (whence) {
1921aca7a94dSNamhyung Kim 	case SEEK_SET:
1922064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
192314135663SNamhyung Kim 					   hb->min_pcnt);
1924aca7a94dSNamhyung Kim 		break;
1925aca7a94dSNamhyung Kim 	case SEEK_CUR:
192605e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1927aca7a94dSNamhyung Kim 		goto do_offset;
1928aca7a94dSNamhyung Kim 	case SEEK_END:
1929d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1930d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1931aca7a94dSNamhyung Kim 		first = false;
1932aca7a94dSNamhyung Kim 		break;
1933aca7a94dSNamhyung Kim 	default:
1934aca7a94dSNamhyung Kim 		return;
1935aca7a94dSNamhyung Kim 	}
1936aca7a94dSNamhyung Kim 
1937aca7a94dSNamhyung Kim 	/*
1938aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1939aca7a94dSNamhyung Kim 	 * row_offset:
1940aca7a94dSNamhyung Kim 	 */
194105e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1942aca7a94dSNamhyung Kim 	h->row_offset = 0;
1943aca7a94dSNamhyung Kim 
1944aca7a94dSNamhyung Kim 	/*
1945aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1946aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1947aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1948aca7a94dSNamhyung Kim 	 *
1949aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1950aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1951aca7a94dSNamhyung Kim 	 *
1952aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1953aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1954aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1955aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1956aca7a94dSNamhyung Kim 	 */
1957aca7a94dSNamhyung Kim do_offset:
1958837eeb75SWang Nan 	if (!nd)
1959837eeb75SWang Nan 		return;
1960837eeb75SWang Nan 
1961aca7a94dSNamhyung Kim 	if (offset > 0) {
1962aca7a94dSNamhyung Kim 		do {
1963aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1964d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1965aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1966aca7a94dSNamhyung Kim 				if (offset > remaining) {
1967aca7a94dSNamhyung Kim 					offset -= remaining;
1968aca7a94dSNamhyung Kim 					h->row_offset = 0;
1969aca7a94dSNamhyung Kim 				} else {
1970aca7a94dSNamhyung Kim 					h->row_offset += offset;
1971aca7a94dSNamhyung Kim 					offset = 0;
197205e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1973aca7a94dSNamhyung Kim 					break;
1974aca7a94dSNamhyung Kim 				}
1975aca7a94dSNamhyung Kim 			}
1976d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1977d0506edbSNamhyung Kim 						   hb->min_pcnt);
1978aca7a94dSNamhyung Kim 			if (nd == NULL)
1979aca7a94dSNamhyung Kim 				break;
1980aca7a94dSNamhyung Kim 			--offset;
198105e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1982aca7a94dSNamhyung Kim 		} while (offset != 0);
1983aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1984aca7a94dSNamhyung Kim 		while (1) {
1985aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1986d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1987aca7a94dSNamhyung Kim 				if (first) {
1988aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1989aca7a94dSNamhyung Kim 						offset += h->row_offset;
1990aca7a94dSNamhyung Kim 						h->row_offset = 0;
1991aca7a94dSNamhyung Kim 					} else {
1992aca7a94dSNamhyung Kim 						h->row_offset += offset;
1993aca7a94dSNamhyung Kim 						offset = 0;
199405e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1995aca7a94dSNamhyung Kim 						break;
1996aca7a94dSNamhyung Kim 					}
1997aca7a94dSNamhyung Kim 				} else {
1998aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1999aca7a94dSNamhyung Kim 						offset += h->nr_rows;
2000aca7a94dSNamhyung Kim 						h->row_offset = 0;
2001aca7a94dSNamhyung Kim 					} else {
2002aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
2003aca7a94dSNamhyung Kim 						offset = 0;
200405e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
2005aca7a94dSNamhyung Kim 						break;
2006aca7a94dSNamhyung Kim 					}
2007aca7a94dSNamhyung Kim 				}
2008aca7a94dSNamhyung Kim 			}
2009aca7a94dSNamhyung Kim 
2010d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2011064f1981SNamhyung Kim 							hb->min_pcnt);
2012aca7a94dSNamhyung Kim 			if (nd == NULL)
2013aca7a94dSNamhyung Kim 				break;
2014aca7a94dSNamhyung Kim 			++offset;
201505e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
2016aca7a94dSNamhyung Kim 			if (offset == 0) {
2017aca7a94dSNamhyung Kim 				/*
2018aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
2019aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
2020aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
2021aca7a94dSNamhyung Kim 				 */
2022aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
2023d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
2024aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
2025aca7a94dSNamhyung Kim 				break;
2026aca7a94dSNamhyung Kim 			}
2027aca7a94dSNamhyung Kim 			first = false;
2028aca7a94dSNamhyung Kim 		}
2029aca7a94dSNamhyung Kim 	} else {
203005e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
2031aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
2032aca7a94dSNamhyung Kim 		h->row_offset = 0;
2033aca7a94dSNamhyung Kim 	}
2034aca7a94dSNamhyung Kim }
2035aca7a94dSNamhyung Kim 
2036aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2037d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
2038d0506edbSNamhyung Kim 					   int level)
2039aff3f3f6SArnaldo Carvalho de Melo {
204039ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
204139ee533fSNamhyung Kim 		.fp = fp,
204239ee533fSNamhyung Kim 	};
2043aff3f3f6SArnaldo Carvalho de Melo 
2044d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
204539ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
204639ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
204739ee533fSNamhyung Kim 	return arg.printed;
2048aff3f3f6SArnaldo Carvalho de Melo }
2049aff3f3f6SArnaldo Carvalho de Melo 
2050aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
2051aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
2052aff3f3f6SArnaldo Carvalho de Melo {
2053aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
2054aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2055aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
205626d8b338SNamhyung Kim 	struct perf_hpp hpp = {
205726d8b338SNamhyung Kim 		.buf = s,
205826d8b338SNamhyung Kim 		.size = sizeof(s),
205926d8b338SNamhyung Kim 	};
206026d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
206126d8b338SNamhyung Kim 	bool first = true;
206226d8b338SNamhyung Kim 	int ret;
2063aff3f3f6SArnaldo Carvalho de Melo 
2064fabd37b8SArnaldo Carvalho de Melo 	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2065aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
2066aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
20671b6b678eSArnaldo Carvalho de Melo 	}
2068aff3f3f6SArnaldo Carvalho de Melo 
2069f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
2070361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
2071e67d49a7SNamhyung Kim 			continue;
2072e67d49a7SNamhyung Kim 
207326d8b338SNamhyung Kim 		if (!first) {
207426d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
207526d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
207626d8b338SNamhyung Kim 		} else
207726d8b338SNamhyung Kim 			first = false;
2078aff3f3f6SArnaldo Carvalho de Melo 
207926d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
208089fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
208126d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
208226d8b338SNamhyung Kim 	}
208389fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
2084aff3f3f6SArnaldo Carvalho de Melo 
2085aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
2086d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2087d0506edbSNamhyung Kim 
2088d0506edbSNamhyung Kim 	return printed;
2089d0506edbSNamhyung Kim }
2090d0506edbSNamhyung Kim 
2091d0506edbSNamhyung Kim 
2092d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2093d0506edbSNamhyung Kim 						 struct hist_entry *he,
2094325a6283SNamhyung Kim 						 FILE *fp, int level)
2095d0506edbSNamhyung Kim {
2096d0506edbSNamhyung Kim 	char s[8192];
2097d0506edbSNamhyung Kim 	int printed = 0;
2098d0506edbSNamhyung Kim 	char folded_sign = ' ';
2099d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
2100d0506edbSNamhyung Kim 		.buf = s,
2101d0506edbSNamhyung Kim 		.size = sizeof(s),
2102d0506edbSNamhyung Kim 	};
2103d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
2104325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
2105d0506edbSNamhyung Kim 	bool first = true;
2106d0506edbSNamhyung Kim 	int ret;
2107325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2108d0506edbSNamhyung Kim 
2109d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2110d0506edbSNamhyung Kim 
2111d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
2112d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
2113d0506edbSNamhyung Kim 
2114325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
2115325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2116325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
2117325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2118d0506edbSNamhyung Kim 		if (!first) {
2119d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2120d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
2121d0506edbSNamhyung Kim 		} else
2122d0506edbSNamhyung Kim 			first = false;
2123d0506edbSNamhyung Kim 
2124d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2125d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
2126d0506edbSNamhyung Kim 	}
2127d0506edbSNamhyung Kim 
2128d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2129d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
2130d0506edbSNamhyung Kim 
21311b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
21321b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
21331b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
21341b2dbbf4SNamhyung Kim 
2135d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2136d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
21371b2dbbf4SNamhyung Kim 	}
2138d0506edbSNamhyung Kim 
213913c230abSArnaldo Carvalho de Melo 	strim(s);
214013c230abSArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
2141d0506edbSNamhyung Kim 
2142d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
2143d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2144d0506edbSNamhyung Kim 							   he->depth + 1);
2145d0506edbSNamhyung Kim 	}
2146aff3f3f6SArnaldo Carvalho de Melo 
2147aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2148aff3f3f6SArnaldo Carvalho de Melo }
2149aff3f3f6SArnaldo Carvalho de Melo 
2150aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2151aff3f3f6SArnaldo Carvalho de Melo {
2152064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2153064f1981SNamhyung Kim 						   browser->min_pcnt);
2154aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2155aff3f3f6SArnaldo Carvalho de Melo 
2156aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
2157aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2158aff3f3f6SArnaldo Carvalho de Melo 
2159d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
2160d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2161d0506edbSNamhyung Kim 									 h, fp,
2162325a6283SNamhyung Kim 									 h->depth);
2163d0506edbSNamhyung Kim 		} else {
2164aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
2165d0506edbSNamhyung Kim 		}
2166d0506edbSNamhyung Kim 
2167d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2168d0506edbSNamhyung Kim 					   browser->min_pcnt);
2169aff3f3f6SArnaldo Carvalho de Melo 	}
2170aff3f3f6SArnaldo Carvalho de Melo 
2171aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2172aff3f3f6SArnaldo Carvalho de Melo }
2173aff3f3f6SArnaldo Carvalho de Melo 
2174aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2175aff3f3f6SArnaldo Carvalho de Melo {
2176aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2177aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2178aff3f3f6SArnaldo Carvalho de Melo 
2179aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2180aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2181aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2182aff3f3f6SArnaldo Carvalho de Melo 			break;
2183aff3f3f6SArnaldo Carvalho de Melo 		/*
2184aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2185aff3f3f6SArnaldo Carvalho de Melo  		 */
2186aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2187aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2188aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2189aff3f3f6SArnaldo Carvalho de Melo 		}
2190aff3f3f6SArnaldo Carvalho de Melo 	}
2191aff3f3f6SArnaldo Carvalho de Melo 
2192aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2193aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2194aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
2195c8b5f2c9SArnaldo Carvalho de Melo 		const char *err = str_error_r(errno, bf, sizeof(bf));
21964cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2197aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2198aff3f3f6SArnaldo Carvalho de Melo 	}
2199aff3f3f6SArnaldo Carvalho de Melo 
2200aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2201aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2202aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2203aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2204aff3f3f6SArnaldo Carvalho de Melo 
2205aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2206aff3f3f6SArnaldo Carvalho de Melo }
2207aff3f3f6SArnaldo Carvalho de Melo 
2208fcd86426SJiri Olsa void hist_browser__init(struct hist_browser *browser,
2209fcd86426SJiri Olsa 			struct hists *hists)
2210aca7a94dSNamhyung Kim {
2211b1c7a8f7SJiri Olsa 	struct perf_hpp_fmt *fmt;
2212b1c7a8f7SJiri Olsa 
221305e8b080SArnaldo Carvalho de Melo 	browser->hists			= hists;
221405e8b080SArnaldo Carvalho de Melo 	browser->b.refresh		= hist_browser__refresh;
2215357cfff1SArnaldo Carvalho de Melo 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
221605e8b080SArnaldo Carvalho de Melo 	browser->b.seek			= ui_browser__hists_seek;
221705e8b080SArnaldo Carvalho de Melo 	browser->b.use_navkeypressed	= true;
2218c8302367SJiri Olsa 	browser->show_headers		= symbol_conf.show_hist_headers;
2219ef9ff601SArnaldo Carvalho de Melo 	hist_browser__set_title_space(browser);
2220b1c7a8f7SJiri Olsa 
22218a06b0beSNamhyung Kim 	if (symbol_conf.report_hierarchy) {
22228a06b0beSNamhyung Kim 		struct perf_hpp_list_node *fmt_node;
22238a06b0beSNamhyung Kim 
22248a06b0beSNamhyung Kim 		/* count overhead columns (in the first node) */
22258a06b0beSNamhyung Kim 		fmt_node = list_first_entry(&hists->hpp_formats,
22268a06b0beSNamhyung Kim 					    struct perf_hpp_list_node, list);
22278a06b0beSNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
22288a06b0beSNamhyung Kim 			++browser->b.columns;
22298a06b0beSNamhyung Kim 
22308a06b0beSNamhyung Kim 		/* add a single column for whole hierarchy sort keys*/
22318a06b0beSNamhyung Kim 		++browser->b.columns;
22328a06b0beSNamhyung Kim 	} else {
2233e3b60bc9SNamhyung Kim 		hists__for_each_format(hists, fmt)
2234b1c7a8f7SJiri Olsa 			++browser->b.columns;
22358a06b0beSNamhyung Kim 	}
2236e3b60bc9SNamhyung Kim 
2237e3b60bc9SNamhyung Kim 	hists__reset_column_width(hists);
2238aca7a94dSNamhyung Kim }
2239aca7a94dSNamhyung Kim 
2240fcd86426SJiri Olsa struct hist_browser *hist_browser__new(struct hists *hists)
2241fcd86426SJiri Olsa {
2242fcd86426SJiri Olsa 	struct hist_browser *browser = zalloc(sizeof(*browser));
2243fcd86426SJiri Olsa 
2244fcd86426SJiri Olsa 	if (browser)
2245fcd86426SJiri Olsa 		hist_browser__init(browser, hists);
2246fcd86426SJiri Olsa 
224705e8b080SArnaldo Carvalho de Melo 	return browser;
2248aca7a94dSNamhyung Kim }
2249aca7a94dSNamhyung Kim 
2250a6ec894dSJiri Olsa static struct hist_browser *
225132dcd021SJiri Olsa perf_evsel_browser__new(struct evsel *evsel,
2252a6ec894dSJiri Olsa 			struct hist_browser_timer *hbt,
2253cd0cccbaSArnaldo Carvalho de Melo 			struct perf_env *env,
2254cd0cccbaSArnaldo Carvalho de Melo 			struct annotation_options *annotation_opts)
2255a6ec894dSJiri Olsa {
2256a6ec894dSJiri Olsa 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2257a6ec894dSJiri Olsa 
2258a6ec894dSJiri Olsa 	if (browser) {
2259a6ec894dSJiri Olsa 		browser->hbt   = hbt;
2260a6ec894dSJiri Olsa 		browser->env   = env;
2261f016d24aSArnaldo Carvalho de Melo 		browser->title = hists_browser__scnprintf_title;
2262cd0cccbaSArnaldo Carvalho de Melo 		browser->annotation_opts = annotation_opts;
2263a6ec894dSJiri Olsa 	}
2264a6ec894dSJiri Olsa 	return browser;
2265a6ec894dSJiri Olsa }
2266a6ec894dSJiri Olsa 
2267dabd2012SJiri Olsa void hist_browser__delete(struct hist_browser *browser)
2268aca7a94dSNamhyung Kim {
226905e8b080SArnaldo Carvalho de Melo 	free(browser);
2270aca7a94dSNamhyung Kim }
2271aca7a94dSNamhyung Kim 
227205e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2273aca7a94dSNamhyung Kim {
227405e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2275aca7a94dSNamhyung Kim }
2276aca7a94dSNamhyung Kim 
227705e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2278aca7a94dSNamhyung Kim {
227905e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2280aca7a94dSNamhyung Kim }
2281aca7a94dSNamhyung Kim 
2282d61cbb85SWei Li static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2283d61cbb85SWei Li {
2284d61cbb85SWei Li 	return browser->he_selection ? browser->he_selection->res_samples : NULL;
2285d61cbb85SWei Li }
2286d61cbb85SWei Li 
22871e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
22881e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
22891e378ebdSTaeung Song {
22901e378ebdSTaeung Song 	return timer == NULL;
22911e378ebdSTaeung Song }
22921e378ebdSTaeung Song 
2293967a464aSArnaldo Carvalho de Melo static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2294967a464aSArnaldo Carvalho de Melo {
2295967a464aSArnaldo Carvalho de Melo 	struct hist_browser_timer *hbt = browser->hbt;
2296967a464aSArnaldo Carvalho de Melo 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2297967a464aSArnaldo Carvalho de Melo 
22981e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
22991e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
23001e378ebdSTaeung Song 
2301d24e3c98SJiri Olsa 		printed += scnprintf(bf + printed, size - printed,
2302d24e3c98SJiri Olsa 				     " lost: %" PRIu64 "/%" PRIu64,
2303d24e3c98SJiri Olsa 				     top->lost, top->lost_total);
2304d24e3c98SJiri Olsa 
230597f7e0b3SJiri Olsa 		printed += scnprintf(bf + printed, size - printed,
230697f7e0b3SJiri Olsa 				     " drop: %" PRIu64 "/%" PRIu64,
230797f7e0b3SJiri Olsa 				     top->drop, top->drop_total);
230897f7e0b3SJiri Olsa 
23091e378ebdSTaeung Song 		if (top->zero)
23101e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
23118aa5c8edSJiri Olsa 
23128aa5c8edSJiri Olsa 		perf_top__reset_sample_counters(top);
23131e378ebdSTaeung Song 	}
23141e378ebdSTaeung Song 
23158aa5c8edSJiri Olsa 
2316aca7a94dSNamhyung Kim 	return printed;
2317aca7a94dSNamhyung Kim }
2318aca7a94dSNamhyung Kim 
2319aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2320aca7a94dSNamhyung Kim {
2321aca7a94dSNamhyung Kim 	int i;
2322aca7a94dSNamhyung Kim 
232304662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
232404662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2325aca7a94dSNamhyung Kim }
2326aca7a94dSNamhyung Kim 
2327341487abSFeng Tang /*
2328341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2329341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2330341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2331341487abSFeng Tang  */
2332341487abSFeng Tang static bool is_input_name_malloced = false;
2333341487abSFeng Tang 
2334341487abSFeng Tang static int switch_data_file(void)
2335341487abSFeng Tang {
2336341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2337341487abSFeng Tang 	DIR *pwd_dir;
2338341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2339341487abSFeng Tang 	struct dirent *dent;
2340341487abSFeng Tang 
2341341487abSFeng Tang 	pwd = getenv("PWD");
2342341487abSFeng Tang 	if (!pwd)
2343341487abSFeng Tang 		return ret;
2344341487abSFeng Tang 
2345341487abSFeng Tang 	pwd_dir = opendir(pwd);
2346341487abSFeng Tang 	if (!pwd_dir)
2347341487abSFeng Tang 		return ret;
2348341487abSFeng Tang 
2349341487abSFeng Tang 	memset(options, 0, sizeof(options));
23503ef5b402SChangbin Du 	memset(abs_path, 0, sizeof(abs_path));
2351341487abSFeng Tang 
2352341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2353341487abSFeng Tang 		char path[PATH_MAX];
2354341487abSFeng Tang 		u64 magic;
2355341487abSFeng Tang 		char *name = dent->d_name;
2356341487abSFeng Tang 		FILE *file;
2357341487abSFeng Tang 
2358341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2359341487abSFeng Tang 			continue;
2360341487abSFeng Tang 
2361341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2362341487abSFeng Tang 
2363341487abSFeng Tang 		file = fopen(path, "r");
2364341487abSFeng Tang 		if (!file)
2365341487abSFeng Tang 			continue;
2366341487abSFeng Tang 
2367341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2368341487abSFeng Tang 			goto close_file_and_continue;
2369341487abSFeng Tang 
2370341487abSFeng Tang 		if (is_perf_magic(magic)) {
2371341487abSFeng Tang 			options[nr_options] = strdup(name);
2372341487abSFeng Tang 			if (!options[nr_options])
2373341487abSFeng Tang 				goto close_file_and_continue;
2374341487abSFeng Tang 
2375341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2376341487abSFeng Tang 			if (!abs_path[nr_options]) {
237774cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2378341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2379341487abSFeng Tang 				fclose(file);
2380341487abSFeng Tang 				break;
2381341487abSFeng Tang 			}
2382341487abSFeng Tang 
2383341487abSFeng Tang 			nr_options++;
2384341487abSFeng Tang 		}
2385341487abSFeng Tang 
2386341487abSFeng Tang close_file_and_continue:
2387341487abSFeng Tang 		fclose(file);
2388341487abSFeng Tang 		if (nr_options >= 32) {
2389341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2390341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2391341487abSFeng Tang 			break;
2392341487abSFeng Tang 		}
2393341487abSFeng Tang 	}
2394341487abSFeng Tang 	closedir(pwd_dir);
2395341487abSFeng Tang 
2396341487abSFeng Tang 	if (nr_options) {
2397d0712656SArnaldo Carvalho de Melo 		choice = ui__popup_menu(nr_options, options, NULL);
2398341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2399341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2400341487abSFeng Tang 			if (tmp) {
2401341487abSFeng Tang 				if (is_input_name_malloced)
2402341487abSFeng Tang 					free((void *)input_name);
2403341487abSFeng Tang 				input_name = tmp;
2404341487abSFeng Tang 				is_input_name_malloced = true;
2405341487abSFeng Tang 				ret = 0;
2406341487abSFeng Tang 			} else
2407341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2408341487abSFeng Tang 		}
2409341487abSFeng Tang 	}
2410341487abSFeng Tang 
2411341487abSFeng Tang 	free_popup_options(options, nr_options);
2412341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2413341487abSFeng Tang 	return ret;
2414341487abSFeng Tang }
2415341487abSFeng Tang 
2416ea7cd592SNamhyung Kim struct popup_action {
24171d6c49dfSAndi Kleen 	unsigned long		time;
2418ea7cd592SNamhyung Kim 	struct thread 		*thread;
241951e9ea99SIan Rogers 	struct evsel	*evsel;
242051e9ea99SIan Rogers 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2421ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
242284734b06SKan Liang 	int			socket;
24234968ac8fSAndi Kleen 	enum rstype		rstype;
2424ea7cd592SNamhyung Kim 
2425ea7cd592SNamhyung Kim };
2426ea7cd592SNamhyung Kim 
2427bc7cad42SNamhyung Kim static int
2428ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2429bc7cad42SNamhyung Kim {
243032dcd021SJiri Olsa 	struct evsel *evsel;
2431bc7cad42SNamhyung Kim 	struct annotation *notes;
2432bc7cad42SNamhyung Kim 	struct hist_entry *he;
2433bc7cad42SNamhyung Kim 	int err;
2434bc7cad42SNamhyung Kim 
2435f178fd2dSArnaldo Carvalho de Melo 	if (!browser->annotation_opts->objdump_path &&
2436f178fd2dSArnaldo Carvalho de Melo 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2437bc7cad42SNamhyung Kim 		return 0;
2438bc7cad42SNamhyung Kim 
2439ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2440bc7cad42SNamhyung Kim 	if (!notes->src)
2441bc7cad42SNamhyung Kim 		return 0;
2442bc7cad42SNamhyung Kim 
2443848a5e50SJin Yao 	if (browser->block_evsel)
2444848a5e50SJin Yao 		evsel = browser->block_evsel;
2445848a5e50SJin Yao 	else
2446bc7cad42SNamhyung Kim 		evsel = hists_to_evsel(browser->hists);
2447848a5e50SJin Yao 
2448cd0cccbaSArnaldo Carvalho de Melo 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2449cd0cccbaSArnaldo Carvalho de Melo 				       browser->annotation_opts);
2450bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2451bc7cad42SNamhyung Kim 	/*
2452bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2453bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2454bc7cad42SNamhyung Kim 	 */
2455bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2456bc7cad42SNamhyung Kim 		return 1;
2457bc7cad42SNamhyung Kim 
2458bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2459bc7cad42SNamhyung Kim 	if (err)
2460bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2461bc7cad42SNamhyung Kim 	return 0;
2462bc7cad42SNamhyung Kim }
2463bc7cad42SNamhyung Kim 
24647b0a0dcbSJin Yao static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
24657b0a0dcbSJin Yao {
24667b0a0dcbSJin Yao 	struct annotated_source *src;
24677b0a0dcbSJin Yao 	struct symbol *sym;
24687b0a0dcbSJin Yao 	char name[64];
24697b0a0dcbSJin Yao 
24707b0a0dcbSJin Yao 	snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
24717b0a0dcbSJin Yao 
24727b0a0dcbSJin Yao 	sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
24737b0a0dcbSJin Yao 	if (sym) {
24747b0a0dcbSJin Yao 		src = symbol__hists(sym, 1);
24757b0a0dcbSJin Yao 		if (!src) {
24767b0a0dcbSJin Yao 			symbol__delete(sym);
24777b0a0dcbSJin Yao 			return NULL;
24787b0a0dcbSJin Yao 		}
24797b0a0dcbSJin Yao 
248063df0e4bSIan Rogers 		dso__insert_symbol(map__dso(map), sym);
24817b0a0dcbSJin Yao 	}
24827b0a0dcbSJin Yao 
24837b0a0dcbSJin Yao 	return sym;
24847b0a0dcbSJin Yao }
24857b0a0dcbSJin Yao 
2486bc7cad42SNamhyung Kim static int
2487ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2488ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
24897b0a0dcbSJin Yao 		 struct map_symbol *ms,
24907b0a0dcbSJin Yao 		 u64 addr)
2491bc7cad42SNamhyung Kim {
24923ad1be6fSArnaldo Carvalho de Melo 	struct dso *dso;
249363df0e4bSIan Rogers 
24943ad1be6fSArnaldo Carvalho de Melo 	if (!ms->map || (dso = map__dso(ms->map)) == NULL || dso->annotate_warned)
24957b0a0dcbSJin Yao 		return 0;
24967b0a0dcbSJin Yao 
24977b0a0dcbSJin Yao 	if (!ms->sym)
24987b0a0dcbSJin Yao 		ms->sym = symbol__new_unresolved(addr, ms->map);
24997b0a0dcbSJin Yao 
25007b0a0dcbSJin Yao 	if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2501ea7cd592SNamhyung Kim 		return 0;
2502ea7cd592SNamhyung Kim 
2503d46a4cdfSArnaldo Carvalho de Melo 	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2504ea7cd592SNamhyung Kim 		return 0;
2505ea7cd592SNamhyung Kim 
2506d46a4cdfSArnaldo Carvalho de Melo 	act->ms = *ms;
2507ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2508ea7cd592SNamhyung Kim 	return 1;
2509ea7cd592SNamhyung Kim }
2510ea7cd592SNamhyung Kim 
2511ea7cd592SNamhyung Kim static int
2512ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2513ea7cd592SNamhyung Kim {
2514ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2515ea7cd592SNamhyung Kim 
25167cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
25177cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2518599a2f38SNamhyung Kim 		return 0;
2519599a2f38SNamhyung Kim 
2520bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2521bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2522bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2523bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2524bc7cad42SNamhyung Kim 		ui_helpline__pop();
2525bc7cad42SNamhyung Kim 	} else {
2526ee84a303SIan Rogers 		const char *comm_set_str =
2527ee84a303SIan Rogers 			thread__comm_set(thread) ? thread__comm_str(thread) : "";
2528ee84a303SIan Rogers 
2529fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
25307727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2531ee84a303SIan Rogers 					   comm_set_str, thread__tid(thread));
25326962ccb3SNamhyung Kim 		} else {
25336962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2534ee84a303SIan Rogers 					   comm_set_str);
25356962ccb3SNamhyung Kim 		}
25366962ccb3SNamhyung Kim 
2537bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2538bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2539bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2540bc7cad42SNamhyung Kim 	}
2541bc7cad42SNamhyung Kim 
2542bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2543bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2544bc7cad42SNamhyung Kim 	return 0;
2545bc7cad42SNamhyung Kim }
2546bc7cad42SNamhyung Kim 
2547bc7cad42SNamhyung Kim static int
2548ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2549ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2550bc7cad42SNamhyung Kim {
25516962ccb3SNamhyung Kim 	int ret;
2552ee84a303SIan Rogers 	const char *comm_set_str, *in_out;
25536962ccb3SNamhyung Kim 
25547cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
25557cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2556ea7cd592SNamhyung Kim 		return 0;
2557ea7cd592SNamhyung Kim 
2558ee84a303SIan Rogers 	in_out = browser->hists->thread_filter ? "out of" : "into";
2559ee84a303SIan Rogers 	comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : "";
2560fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
25616962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2562ee84a303SIan Rogers 			       in_out, comm_set_str, thread__tid(thread));
25636962ccb3SNamhyung Kim 	} else {
2564ee84a303SIan Rogers 		ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str);
25656962ccb3SNamhyung Kim 	}
25666962ccb3SNamhyung Kim 	if (ret < 0)
2567ea7cd592SNamhyung Kim 		return 0;
2568ea7cd592SNamhyung Kim 
2569ea7cd592SNamhyung Kim 	act->thread = thread;
2570ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2571ea7cd592SNamhyung Kim 	return 1;
2572ea7cd592SNamhyung Kim }
2573ea7cd592SNamhyung Kim 
2574632003f4SArnaldo Carvalho de Melo static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2575ea7cd592SNamhyung Kim {
257669849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2577599a2f38SNamhyung Kim 		return 0;
2578599a2f38SNamhyung Kim 
2579bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2580bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2581bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2582bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2583bc7cad42SNamhyung Kim 		ui_helpline__pop();
2584bc7cad42SNamhyung Kim 	} else {
258563df0e4bSIan Rogers 		struct dso *dso = map__dso(map);
25867727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
258763df0e4bSIan Rogers 				   __map__is_kernel(map) ? "the Kernel" : dso->short_name);
258863df0e4bSIan Rogers 		browser->hists->dso_filter = dso;
2589bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2590bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2591bc7cad42SNamhyung Kim 	}
2592bc7cad42SNamhyung Kim 
2593bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2594bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2595bc7cad42SNamhyung Kim 	return 0;
2596bc7cad42SNamhyung Kim }
2597bc7cad42SNamhyung Kim 
2598bc7cad42SNamhyung Kim static int
2599632003f4SArnaldo Carvalho de Melo do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2600632003f4SArnaldo Carvalho de Melo {
2601632003f4SArnaldo Carvalho de Melo 	return hists_browser__zoom_map(browser, act->ms.map);
2602632003f4SArnaldo Carvalho de Melo }
2603632003f4SArnaldo Carvalho de Melo 
2604632003f4SArnaldo Carvalho de Melo static int
2605ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2606045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2607bc7cad42SNamhyung Kim {
260869849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2609ea7cd592SNamhyung Kim 		return 0;
2610ea7cd592SNamhyung Kim 
2611209f4e70SArnaldo Carvalho de Melo 	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2612ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
261363df0e4bSIan Rogers 		     __map__is_kernel(map) ? "the Kernel" : map__dso(map)->short_name) < 0)
2614ea7cd592SNamhyung Kim 		return 0;
2615ea7cd592SNamhyung Kim 
2616045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2617ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2618ea7cd592SNamhyung Kim 	return 1;
2619ea7cd592SNamhyung Kim }
2620ea7cd592SNamhyung Kim 
2621d5a599d9SArnaldo Carvalho de Melo static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2622d5a599d9SArnaldo Carvalho de Melo {
2623d5a599d9SArnaldo Carvalho de Melo 	hist_browser__toggle_fold(browser);
2624d5a599d9SArnaldo Carvalho de Melo 	return 0;
2625d5a599d9SArnaldo Carvalho de Melo }
2626d5a599d9SArnaldo Carvalho de Melo 
2627d5a599d9SArnaldo Carvalho de Melo static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2628d5a599d9SArnaldo Carvalho de Melo {
2629bdc633feSArnaldo Carvalho de Melo 	char sym_name[512];
2630d5a599d9SArnaldo Carvalho de Melo 
2631bdc633feSArnaldo Carvalho de Melo         if (!hist_browser__selection_has_children(browser))
2632d5a599d9SArnaldo Carvalho de Melo                 return 0;
2633d5a599d9SArnaldo Carvalho de Melo 
2634bdc633feSArnaldo Carvalho de Melo 	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2635bdc633feSArnaldo Carvalho de Melo 		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2636bdc633feSArnaldo Carvalho de Melo 		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2637d5a599d9SArnaldo Carvalho de Melo 		return 0;
2638d5a599d9SArnaldo Carvalho de Melo 
2639d5a599d9SArnaldo Carvalho de Melo 	act->fn = do_toggle_callchain;
2640d5a599d9SArnaldo Carvalho de Melo 	return 1;
2641d5a599d9SArnaldo Carvalho de Melo }
2642d5a599d9SArnaldo Carvalho de Melo 
2643ea7cd592SNamhyung Kim static int
2644ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2645ea7cd592SNamhyung Kim 	      struct popup_action *act)
2646ea7cd592SNamhyung Kim {
2647ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2648bc7cad42SNamhyung Kim 	return 0;
2649bc7cad42SNamhyung Kim }
2650bc7cad42SNamhyung Kim 
2651bc7cad42SNamhyung Kim static int
265269849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2653ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2654ea7cd592SNamhyung Kim {
265569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2656ea7cd592SNamhyung Kim 		return 0;
2657ea7cd592SNamhyung Kim 
2658ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2659ea7cd592SNamhyung Kim 		return 0;
2660ea7cd592SNamhyung Kim 
2661ea7cd592SNamhyung Kim 	act->ms.map = map;
2662ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2663ea7cd592SNamhyung Kim 	return 1;
2664ea7cd592SNamhyung Kim }
2665ea7cd592SNamhyung Kim 
2666ea7cd592SNamhyung Kim static int
2667bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2668ea7cd592SNamhyung Kim 	      struct popup_action *act)
2669bc7cad42SNamhyung Kim {
26701d6c49dfSAndi Kleen 	char *script_opt;
26711d6c49dfSAndi Kleen 	int len;
26721d6c49dfSAndi Kleen 	int n = 0;
2673bc7cad42SNamhyung Kim 
26741d6c49dfSAndi Kleen 	len = 100;
26751d6c49dfSAndi Kleen 	if (act->thread)
26761d6c49dfSAndi Kleen 		len += strlen(thread__comm_str(act->thread));
26771d6c49dfSAndi Kleen 	else if (act->ms.sym)
26781d6c49dfSAndi Kleen 		len += strlen(act->ms.sym->name);
26791d6c49dfSAndi Kleen 	script_opt = malloc(len);
26801d6c49dfSAndi Kleen 	if (!script_opt)
26811d6c49dfSAndi Kleen 		return -1;
26821d6c49dfSAndi Kleen 
26831d6c49dfSAndi Kleen 	script_opt[0] = 0;
2684ea7cd592SNamhyung Kim 	if (act->thread) {
26851d6c49dfSAndi Kleen 		n = scnprintf(script_opt, len, " -c %s ",
2686ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2687ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
26881d6c49dfSAndi Kleen 		n = scnprintf(script_opt, len, " -S %s ",
2689ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2690bc7cad42SNamhyung Kim 	}
2691bc7cad42SNamhyung Kim 
26921d6c49dfSAndi Kleen 	if (act->time) {
26931d6c49dfSAndi Kleen 		char start[32], end[32];
26941d6c49dfSAndi Kleen 		unsigned long starttime = act->time;
26951d6c49dfSAndi Kleen 		unsigned long endtime = act->time + symbol_conf.time_quantum;
26961d6c49dfSAndi Kleen 
26971d6c49dfSAndi Kleen 		if (starttime == endtime) { /* Display 1ms as fallback */
26981d6c49dfSAndi Kleen 			starttime -= 1*NSEC_PER_MSEC;
26991d6c49dfSAndi Kleen 			endtime += 1*NSEC_PER_MSEC;
27001d6c49dfSAndi Kleen 		}
27011d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(starttime, start, sizeof start);
27021d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(endtime, end, sizeof end);
27031d6c49dfSAndi Kleen 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
27041d6c49dfSAndi Kleen 	}
27051d6c49dfSAndi Kleen 
27066f3da20eSAndi Kleen 	script_browse(script_opt, act->evsel);
27071d6c49dfSAndi Kleen 	free(script_opt);
2708bc7cad42SNamhyung Kim 	return 0;
2709bc7cad42SNamhyung Kim }
2710bc7cad42SNamhyung Kim 
2711bc7cad42SNamhyung Kim static int
27124968ac8fSAndi Kleen do_res_sample_script(struct hist_browser *browser __maybe_unused,
27134968ac8fSAndi Kleen 		     struct popup_action *act)
27144968ac8fSAndi Kleen {
27154968ac8fSAndi Kleen 	struct hist_entry *he;
27164968ac8fSAndi Kleen 
27174968ac8fSAndi Kleen 	he = hist_browser__selected_entry(browser);
27184968ac8fSAndi Kleen 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
27194968ac8fSAndi Kleen 	return 0;
27204968ac8fSAndi Kleen }
27214968ac8fSAndi Kleen 
27224968ac8fSAndi Kleen static int
27231d6c49dfSAndi Kleen add_script_opt_2(struct hist_browser *browser __maybe_unused,
2724ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
27251d6c49dfSAndi Kleen 	       struct thread *thread, struct symbol *sym,
272632dcd021SJiri Olsa 	       struct evsel *evsel, const char *tstr)
2727ea7cd592SNamhyung Kim {
27281d6c49dfSAndi Kleen 
2729ea7cd592SNamhyung Kim 	if (thread) {
27301d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
27311d6c49dfSAndi Kleen 			     thread__comm_str(thread), tstr) < 0)
2732ea7cd592SNamhyung Kim 			return 0;
2733ea7cd592SNamhyung Kim 	} else if (sym) {
27341d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
27351d6c49dfSAndi Kleen 			     sym->name, tstr) < 0)
2736ea7cd592SNamhyung Kim 			return 0;
2737ea7cd592SNamhyung Kim 	} else {
27381d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2739ea7cd592SNamhyung Kim 			return 0;
2740ea7cd592SNamhyung Kim 	}
2741ea7cd592SNamhyung Kim 
2742ea7cd592SNamhyung Kim 	act->thread = thread;
2743ea7cd592SNamhyung Kim 	act->ms.sym = sym;
27446f3da20eSAndi Kleen 	act->evsel = evsel;
2745ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2746ea7cd592SNamhyung Kim 	return 1;
2747ea7cd592SNamhyung Kim }
2748ea7cd592SNamhyung Kim 
2749ea7cd592SNamhyung Kim static int
27501d6c49dfSAndi Kleen add_script_opt(struct hist_browser *browser,
27511d6c49dfSAndi Kleen 	       struct popup_action *act, char **optstr,
27526f3da20eSAndi Kleen 	       struct thread *thread, struct symbol *sym,
275332dcd021SJiri Olsa 	       struct evsel *evsel)
27541d6c49dfSAndi Kleen {
27551d6c49dfSAndi Kleen 	int n, j;
27561d6c49dfSAndi Kleen 	struct hist_entry *he;
27571d6c49dfSAndi Kleen 
27586f3da20eSAndi Kleen 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
27591d6c49dfSAndi Kleen 
27601d6c49dfSAndi Kleen 	he = hist_browser__selected_entry(browser);
27611d6c49dfSAndi Kleen 	if (sort_order && strstr(sort_order, "time")) {
27621d6c49dfSAndi Kleen 		char tstr[128];
27631d6c49dfSAndi Kleen 
27641d6c49dfSAndi Kleen 		optstr++;
27651d6c49dfSAndi Kleen 		act++;
27661d6c49dfSAndi Kleen 		j = sprintf(tstr, " in ");
27671d6c49dfSAndi Kleen 		j += timestamp__scnprintf_usec(he->time, tstr + j,
27681d6c49dfSAndi Kleen 					       sizeof tstr - j);
27691d6c49dfSAndi Kleen 		j += sprintf(tstr + j, "-");
27701d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
27716f3da20eSAndi Kleen 				          tstr + j, sizeof tstr - j);
27721d6c49dfSAndi Kleen 		n += add_script_opt_2(browser, act, optstr, thread, sym,
27736f3da20eSAndi Kleen 					  evsel, tstr);
27741d6c49dfSAndi Kleen 		act->time = he->time;
27751d6c49dfSAndi Kleen 	}
27761d6c49dfSAndi Kleen 	return n;
27771d6c49dfSAndi Kleen }
27781d6c49dfSAndi Kleen 
27791d6c49dfSAndi Kleen static int
27804968ac8fSAndi Kleen add_res_sample_opt(struct hist_browser *browser __maybe_unused,
27814968ac8fSAndi Kleen 		   struct popup_action *act, char **optstr,
27824968ac8fSAndi Kleen 		   struct res_sample *res_sample,
278332dcd021SJiri Olsa 		   struct evsel *evsel,
27844968ac8fSAndi Kleen 		   enum rstype type)
27854968ac8fSAndi Kleen {
27864968ac8fSAndi Kleen 	if (!res_sample)
27874968ac8fSAndi Kleen 		return 0;
27884968ac8fSAndi Kleen 
27894968ac8fSAndi Kleen 	if (asprintf(optstr, "Show context for individual samples %s",
27904968ac8fSAndi Kleen 		type == A_ASM ? "with assembler" :
27914968ac8fSAndi Kleen 		type == A_SOURCE ? "with source" : "") < 0)
27924968ac8fSAndi Kleen 		return 0;
27934968ac8fSAndi Kleen 
27944968ac8fSAndi Kleen 	act->fn = do_res_sample_script;
27954968ac8fSAndi Kleen 	act->evsel = evsel;
27964968ac8fSAndi Kleen 	act->rstype = type;
27974968ac8fSAndi Kleen 	return 1;
27984968ac8fSAndi Kleen }
27994968ac8fSAndi Kleen 
28004968ac8fSAndi Kleen static int
2801ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2802ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2803bc7cad42SNamhyung Kim {
2804bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2805bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2806bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2807ea7cd592SNamhyung Kim 		return 0;
2808bc7cad42SNamhyung Kim 	}
2809bc7cad42SNamhyung Kim 
2810bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2811bc7cad42SNamhyung Kim }
2812bc7cad42SNamhyung Kim 
2813ea7cd592SNamhyung Kim static int
2814ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2815ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2816ea7cd592SNamhyung Kim {
2817ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2818ea7cd592SNamhyung Kim 		return 0;
2819ea7cd592SNamhyung Kim 
2820ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2821ea7cd592SNamhyung Kim 		return 0;
2822ea7cd592SNamhyung Kim 
2823ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2824ea7cd592SNamhyung Kim 	return 1;
2825ea7cd592SNamhyung Kim }
2826ea7cd592SNamhyung Kim 
2827ea7cd592SNamhyung Kim static int
2828ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2829ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2830ea7cd592SNamhyung Kim {
2831ea7cd592SNamhyung Kim 	return 0;
2832ea7cd592SNamhyung Kim }
2833ea7cd592SNamhyung Kim 
2834ea7cd592SNamhyung Kim static int
2835ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2836ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2837ea7cd592SNamhyung Kim {
2838ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2839ea7cd592SNamhyung Kim 		return 0;
2840ea7cd592SNamhyung Kim 
2841ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2842ea7cd592SNamhyung Kim 	return 1;
2843ea7cd592SNamhyung Kim }
2844ea7cd592SNamhyung Kim 
284584734b06SKan Liang static int
284684734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
284784734b06SKan Liang {
284835a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2849599a2f38SNamhyung Kim 		return 0;
2850599a2f38SNamhyung Kim 
285184734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
285284734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
285384734b06SKan Liang 		browser->hists->socket_filter = -1;
285484734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
285584734b06SKan Liang 	} else {
285684734b06SKan Liang 		browser->hists->socket_filter = act->socket;
285784734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
285884734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
285984734b06SKan Liang 	}
286084734b06SKan Liang 
286184734b06SKan Liang 	hists__filter_by_socket(browser->hists);
286284734b06SKan Liang 	hist_browser__reset(browser);
286384734b06SKan Liang 	return 0;
286484734b06SKan Liang }
286584734b06SKan Liang 
286684734b06SKan Liang static int
286784734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
286884734b06SKan Liang 	       char **optstr, int socket_id)
286984734b06SKan Liang {
287035a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
287184734b06SKan Liang 		return 0;
287284734b06SKan Liang 
287384734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
287484734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
287584734b06SKan Liang 		     socket_id) < 0)
287684734b06SKan Liang 		return 0;
287784734b06SKan Liang 
287884734b06SKan Liang 	act->socket = socket_id;
287984734b06SKan Liang 	act->fn = do_zoom_socket;
288084734b06SKan Liang 	return 1;
288184734b06SKan Liang }
288284734b06SKan Liang 
2883112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2884064f1981SNamhyung Kim {
2885064f1981SNamhyung Kim 	u64 nr_entries = 0;
28862eb3d689SDavidlohr Bueso 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2887064f1981SNamhyung Kim 
2888f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2889268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2890268397cbSNamhyung Kim 		return;
2891268397cbSNamhyung Kim 	}
2892268397cbSNamhyung Kim 
289314135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2894064f1981SNamhyung Kim 		nr_entries++;
2895f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2896064f1981SNamhyung Kim 	}
2897064f1981SNamhyung Kim 
2898112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2899f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2900064f1981SNamhyung Kim }
2901341487abSFeng Tang 
2902b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2903b62e8dfcSNamhyung Kim 					       double percent)
2904b62e8dfcSNamhyung Kim {
2905b62e8dfcSNamhyung Kim 	struct hist_entry *he;
29062eb3d689SDavidlohr Bueso 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2907b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2908b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2909b62e8dfcSNamhyung Kim 
2910b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2911b62e8dfcSNamhyung Kim 
2912b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2913b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2914b62e8dfcSNamhyung Kim 
291579dded87SNamhyung Kim 		if (he->has_no_entry) {
291679dded87SNamhyung Kim 			he->has_no_entry = false;
291779dded87SNamhyung Kim 			he->nr_rows = 0;
291879dded87SNamhyung Kim 		}
291979dded87SNamhyung Kim 
2920fabd37b8SArnaldo Carvalho de Melo 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2921d0506edbSNamhyung Kim 			goto next;
2922d0506edbSNamhyung Kim 
2923b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2924b62e8dfcSNamhyung Kim 			total = he->stat.period;
2925b62e8dfcSNamhyung Kim 
2926b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2927b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2928b62e8dfcSNamhyung Kim 
2929b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2930b62e8dfcSNamhyung Kim 		}
2931b62e8dfcSNamhyung Kim 
2932b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2933b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2934b62e8dfcSNamhyung Kim 
2935d0506edbSNamhyung Kim next:
2936201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2937d0506edbSNamhyung Kim 
2938b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2939b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2940492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2941b62e8dfcSNamhyung Kim 	}
2942b62e8dfcSNamhyung Kim }
2943b62e8dfcSNamhyung Kim 
294456933029SArnaldo Carvalho de Melo static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
294556933029SArnaldo Carvalho de Melo 			       bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
294656933029SArnaldo Carvalho de Melo 			       struct perf_env *env, bool warn_lost_event,
2947cd0cccbaSArnaldo Carvalho de Melo 			       struct annotation_options *annotation_opts)
2948aca7a94dSNamhyung Kim {
29494ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2950cd0cccbaSArnaldo Carvalho de Melo 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2951ceb75476SLeo Yan 	struct branch_info *bi = NULL;
2952f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2953f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2954ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2955aca7a94dSNamhyung Kim 	int nr_options = 0;
2956aca7a94dSNamhyung Kim 	int key = -1;
295786449b12SSong Liu 	char buf[128];
29589783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2959aca7a94dSNamhyung Kim 
2960e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2961e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2962e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2963e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
29646a02f06eSAndi Kleen 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2965e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2966e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2967e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
29687727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
29697727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
29709218a913SArnaldo Carvalho de Melo 	"+             Expand/Collapse one callchain level\n"		\
2971e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2972e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2973e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2974e6d6abfcSArnaldo Carvalho de Melo 	"e             Expand/Collapse main entry callchains\n"	\
2975e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2976105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2977025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2978209f4e70SArnaldo Carvalho de Melo 	"k             Zoom into the kernel map\n"			\
2979b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
298031eb4360SNamhyung Kim 	"m             Display context menu\n"				\
298184734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2982e8e684a5SNamhyung Kim 
2983e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
298449b8e2beSRasmus Villemoes 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
29856dd60135SNamhyung Kim 	"i             Show header information\n"
2986e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2987e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2988e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2989e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2990e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2991dbddf174SJin Yao 	"/             Filter symbol by name\n"
2992dbddf174SJin Yao 	"0-9           Sort by event n in group";
299349b8e2beSRasmus Villemoes 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
2994e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2995e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2996e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
299742337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2998fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2999e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
3000e8e684a5SNamhyung Kim 
3001aca7a94dSNamhyung Kim 	if (browser == NULL)
3002aca7a94dSNamhyung Kim 		return -1;
3003aca7a94dSNamhyung Kim 
3004ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
3005ed426915SNamhyung Kim 	SLang_reset_tty();
3006ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
3007ed426915SNamhyung Kim 
300803905048SNamhyung Kim 	if (min_pcnt)
3009064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
3010112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
3011064f1981SNamhyung Kim 
301284734b06SKan Liang 	browser->pstack = pstack__new(3);
301301f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
3014aca7a94dSNamhyung Kim 		goto out;
3015aca7a94dSNamhyung Kim 
3016aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
3017aca7a94dSNamhyung Kim 
3018aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
3019ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
3020aca7a94dSNamhyung Kim 
30215b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
30225b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
30235b591669SNamhyung Kim 
30245c959b6dSArnaldo Carvalho de Melo 	if (!is_report_browser(hbt))
30255c959b6dSArnaldo Carvalho de Melo 		browser->b.no_samples_msg = "Collecting samples...";
30265c959b6dSArnaldo Carvalho de Melo 
3027aca7a94dSNamhyung Kim 	while (1) {
3028f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
3029045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
30304c8b9c0fSArnaldo Carvalho de Melo 		int choice;
303184734b06SKan Liang 		int socked_id = -1;
3032aca7a94dSNamhyung Kim 
30334c8b9c0fSArnaldo Carvalho de Melo 		key = 0; // reset key
30344c8b9c0fSArnaldo Carvalho de Melo do_hotkey:		 // key came straight from options ui__popup_menu()
30354c8b9c0fSArnaldo Carvalho de Melo 		choice = nr_options = 0;
30364c8b9c0fSArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3037aca7a94dSNamhyung Kim 
3038aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
3039aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
3040045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
304184734b06SKan Liang 			socked_id = browser->he_selection->socket;
3042aca7a94dSNamhyung Kim 		}
3043aca7a94dSNamhyung Kim 		switch (key) {
3044aca7a94dSNamhyung Kim 		case K_TAB:
3045aca7a94dSNamhyung Kim 		case K_UNTAB:
3046aca7a94dSNamhyung Kim 			if (nr_events == 1)
3047aca7a94dSNamhyung Kim 				continue;
3048aca7a94dSNamhyung Kim 			/*
3049aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
3050aca7a94dSNamhyung Kim 			 * go to the next or previous
3051aca7a94dSNamhyung Kim 			 */
3052aca7a94dSNamhyung Kim 			goto out_free_stack;
3053dbddf174SJin Yao 		case '0' ... '9':
3054dbddf174SJin Yao 			if (!symbol_conf.event_group ||
3055dbddf174SJin Yao 			    evsel->core.nr_members < 2) {
3056dbddf174SJin Yao 				snprintf(buf, sizeof(buf),
3057dbddf174SJin Yao 					 "Sort by index only available with group events!");
3058dbddf174SJin Yao 				helpline = buf;
3059dbddf174SJin Yao 				continue;
3060dbddf174SJin Yao 			}
3061dbddf174SJin Yao 
3062dbddf174SJin Yao 			if (key - '0' == symbol_conf.group_sort_idx)
3063dbddf174SJin Yao 				continue;
3064dbddf174SJin Yao 
3065dbddf174SJin Yao 			symbol_conf.group_sort_idx = key - '0';
3066dbddf174SJin Yao 
3067dbddf174SJin Yao 			if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3068dbddf174SJin Yao 				snprintf(buf, sizeof(buf),
3069dbddf174SJin Yao 					 "Max event group index to sort is %d (index from 0 to %d)",
3070dbddf174SJin Yao 					 evsel->core.nr_members - 1,
3071dbddf174SJin Yao 					 evsel->core.nr_members - 1);
3072dbddf174SJin Yao 				helpline = buf;
3073dbddf174SJin Yao 				continue;
3074dbddf174SJin Yao 			}
3075dbddf174SJin Yao 
3076dbddf174SJin Yao 			key = K_RELOAD;
3077dbddf174SJin Yao 			goto out_free_stack;
3078aca7a94dSNamhyung Kim 		case 'a':
30792e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
3080aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
3081aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
3082aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
3083aca7a94dSNamhyung Kim 				continue;
3084aca7a94dSNamhyung Kim 			}
3085aca7a94dSNamhyung Kim 
3086ec0479a6SJin Yao 			if (!browser->selection ||
3087ec0479a6SJin Yao 			    !browser->selection->map ||
308863df0e4bSIan Rogers 			    !map__dso(browser->selection->map) ||
308963df0e4bSIan Rogers 			    map__dso(browser->selection->map)->annotate_warned) {
3090ec0479a6SJin Yao 				continue;
3091ec0479a6SJin Yao 			}
3092ec0479a6SJin Yao 
3093ec0479a6SJin Yao 			if (!browser->selection->sym) {
3094ec0479a6SJin Yao 				if (!browser->he_selection)
3095aca7a94dSNamhyung Kim 					continue;
3096bc7cad42SNamhyung Kim 
3097ec0479a6SJin Yao 				if (sort__mode == SORT_MODE__BRANCH) {
3098ec0479a6SJin Yao 					bi = browser->he_selection->branch_info;
3099ec0479a6SJin Yao 					if (!bi || !bi->to.ms.map)
3100ec0479a6SJin Yao 						continue;
3101ec0479a6SJin Yao 
3102ec0479a6SJin Yao 					actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3103ec0479a6SJin Yao 					actions->ms.map = bi->to.ms.map;
3104ec0479a6SJin Yao 				} else {
3105ec0479a6SJin Yao 					actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3106ec0479a6SJin Yao 										 browser->selection->map);
3107ec0479a6SJin Yao 					actions->ms.map = browser->selection->map;
3108ec0479a6SJin Yao 				}
3109ec0479a6SJin Yao 
3110ec0479a6SJin Yao 				if (!actions->ms.sym)
3111ec0479a6SJin Yao 					continue;
3112ec0479a6SJin Yao 			} else {
3113ea537f22SArnaldo Carvalho de Melo 				if (symbol__annotation(browser->selection->sym)->src == NULL) {
3114ea537f22SArnaldo Carvalho de Melo 					ui_browser__warning(&browser->b, delay_secs * 2,
3115ea537f22SArnaldo Carvalho de Melo 						"No samples for the \"%s\" symbol.\n\n"
3116ea537f22SArnaldo Carvalho de Melo 						"Probably appeared just in a callchain",
3117ea537f22SArnaldo Carvalho de Melo 						browser->selection->sym->name);
3118ea537f22SArnaldo Carvalho de Melo 					continue;
3119ea537f22SArnaldo Carvalho de Melo 				}
3120ea537f22SArnaldo Carvalho de Melo 
3121ea7cd592SNamhyung Kim 				actions->ms.map = browser->selection->map;
3122ea7cd592SNamhyung Kim 				actions->ms.sym = browser->selection->sym;
3123ec0479a6SJin Yao 			}
3124ec0479a6SJin Yao 
3125ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
3126bc7cad42SNamhyung Kim 			continue;
3127aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
3128aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
3129aff3f3f6SArnaldo Carvalho de Melo 			continue;
3130aca7a94dSNamhyung Kim 		case 'd':
3131fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
3132ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
3133bc7cad42SNamhyung Kim 			continue;
3134209f4e70SArnaldo Carvalho de Melo 		case 'k':
3135209f4e70SArnaldo Carvalho de Melo 			if (browser->selection != NULL)
31365ab6d715SIan Rogers 				hists_browser__zoom_map(browser,
31375ab6d715SIan Rogers 					      maps__machine(browser->selection->maps)->vmlinux_map);
3138209f4e70SArnaldo Carvalho de Melo 			continue;
3139a7cb8863SArnaldo Carvalho de Melo 		case 'V':
314021e8c810SAlexis Berlemont 			verbose = (verbose + 1) % 4;
314121e8c810SAlexis Berlemont 			browser->show_dso = verbose > 0;
314221e8c810SAlexis Berlemont 			ui_helpline__fpush("Verbosity level set to %d\n",
314321e8c810SAlexis Berlemont 					   verbose);
3144a7cb8863SArnaldo Carvalho de Melo 			continue;
3145aca7a94dSNamhyung Kim 		case 't':
3146ea7cd592SNamhyung Kim 			actions->thread = thread;
3147ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
3148bc7cad42SNamhyung Kim 			continue;
314984734b06SKan Liang 		case 'S':
315084734b06SKan Liang 			actions->socket = socked_id;
315184734b06SKan Liang 			do_zoom_socket(browser, actions);
315284734b06SKan Liang 			continue;
31535a5626b1SArnaldo Carvalho de Melo 		case '/':
3154aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
31554aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
31564aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
3157aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
3158aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
315905e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
316005e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
3161aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
3162aca7a94dSNamhyung Kim 			}
3163aca7a94dSNamhyung Kim 			continue;
3164cdbab7c2SFeng Tang 		case 'r':
3165ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
3166ea7cd592SNamhyung Kim 				actions->thread = NULL;
3167ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
3168ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
3169ea7cd592SNamhyung Kim 			}
3170c77d8d70SFeng Tang 			continue;
3171341487abSFeng Tang 		case 's':
3172bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
3173ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
3174bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
3175bc7cad42SNamhyung Kim 					goto out_free_stack;
3176bc7cad42SNamhyung Kim 			}
3177341487abSFeng Tang 			continue;
31786dd60135SNamhyung Kim 		case 'i':
31796dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
31806dd60135SNamhyung Kim 			if (env->arch)
31816dd60135SNamhyung Kim 				tui__header_window(env);
31826dd60135SNamhyung Kim 			continue;
3183105eb30fSNamhyung Kim 		case 'F':
3184105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
3185105eb30fSNamhyung Kim 			continue;
318642337a22SNamhyung Kim 		case 'z':
318742337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
318842337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
318942337a22SNamhyung Kim 
319042337a22SNamhyung Kim 				top->zero = !top->zero;
319142337a22SNamhyung Kim 			}
319242337a22SNamhyung Kim 			continue;
3193b62e8dfcSNamhyung Kim 		case 'L':
3194b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
3195b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
3196b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
3197b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
3198b62e8dfcSNamhyung Kim 				char *end;
3199b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
3200b62e8dfcSNamhyung Kim 
3201b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
3202b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
3203b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
3204b62e8dfcSNamhyung Kim 					continue;
3205b62e8dfcSNamhyung Kim 				}
3206b62e8dfcSNamhyung Kim 
3207b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
3208b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
3209b62e8dfcSNamhyung Kim 			}
3210b62e8dfcSNamhyung Kim 			continue;
3211aca7a94dSNamhyung Kim 		case K_F1:
3212aca7a94dSNamhyung Kim 		case 'h':
3213aca7a94dSNamhyung Kim 		case '?':
3214aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
3215e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
3216aca7a94dSNamhyung Kim 			continue;
3217aca7a94dSNamhyung Kim 		case K_ENTER:
3218aca7a94dSNamhyung Kim 		case K_RIGHT:
321931eb4360SNamhyung Kim 		case 'm':
3220aca7a94dSNamhyung Kim 			/* menu */
3221aca7a94dSNamhyung Kim 			break;
322263ab1749SArnaldo Carvalho de Melo 		case K_ESC:
3223aca7a94dSNamhyung Kim 		case K_LEFT: {
3224aca7a94dSNamhyung Kim 			const void *top;
3225aca7a94dSNamhyung Kim 
322601f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
3227aca7a94dSNamhyung Kim 				/*
3228aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
3229aca7a94dSNamhyung Kim 				 */
3230aca7a94dSNamhyung Kim 				if (left_exits)
3231aca7a94dSNamhyung Kim 					goto out_free_stack;
323263ab1749SArnaldo Carvalho de Melo 
323363ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
323463ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
323563ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
323663ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
323763ab1749SArnaldo Carvalho de Melo 
3238aca7a94dSNamhyung Kim 				continue;
3239aca7a94dSNamhyung Kim 			}
32403f777403SArnaldo Carvalho de Melo 			actions->ms.map = map;
32416422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
3242bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
32436422184bSNamhyung Kim 				/*
32446422184bSNamhyung Kim 				 * No need to set actions->dso here since
32456422184bSNamhyung Kim 				 * it's just to remove the current filter.
32466422184bSNamhyung Kim 				 * Ditto for thread below.
32476422184bSNamhyung Kim 				 */
32486422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
324984734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
32506422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
325184734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
325284734b06SKan Liang 				do_zoom_socket(browser, actions);
325384734b06SKan Liang 			}
3254aca7a94dSNamhyung Kim 			continue;
3255aca7a94dSNamhyung Kim 		}
3256aca7a94dSNamhyung Kim 		case 'q':
3257aca7a94dSNamhyung Kim 		case CTRL('c'):
3258516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
3259fbb7997eSArnaldo Carvalho de Melo 		case 'f':
326013d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
326113d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
326213d1e536SNamhyung Kim 
3263ade9d208SArnaldo Carvalho de Melo 				evlist__toggle_enable(top->evlist);
326413d1e536SNamhyung Kim 				/*
326513d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
326613d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
326713d1e536SNamhyung Kim 				 */
326813d1e536SNamhyung Kim 				if (top->evlist->enabled) {
326913d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
327013d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
327113d1e536SNamhyung Kim 				} else {
327213d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
327313d1e536SNamhyung Kim 					hbt->refresh = 0;
327413d1e536SNamhyung Kim 				}
327513d1e536SNamhyung Kim 				continue;
327613d1e536SNamhyung Kim 			}
32773e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
3278aca7a94dSNamhyung Kim 		default:
32793e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
3280aca7a94dSNamhyung Kim 			continue;
3281aca7a94dSNamhyung Kim 		}
3282aca7a94dSNamhyung Kim 
32832e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
32840ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
32850ba332f7SArnaldo Carvalho de Melo 
328655369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
3287ceb75476SLeo Yan 
3288ceb75476SLeo Yan 			if (browser->he_selection)
3289aca7a94dSNamhyung Kim 				bi = browser->he_selection->branch_info;
32900ba332f7SArnaldo Carvalho de Melo 
32910ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
32920ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
32930ba332f7SArnaldo Carvalho de Melo 
3294ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3295ea7cd592SNamhyung Kim 						       &actions[nr_options],
3296ea7cd592SNamhyung Kim 						       &options[nr_options],
32977b0a0dcbSJin Yao 						       &bi->from.ms,
32987b0a0dcbSJin Yao 						       bi->from.al_addr);
3299d46a4cdfSArnaldo Carvalho de Melo 			if (bi->to.ms.sym != bi->from.ms.sym)
3300ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
3301ea7cd592SNamhyung Kim 							&actions[nr_options],
3302ea7cd592SNamhyung Kim 							&options[nr_options],
33037b0a0dcbSJin Yao 							&bi->to.ms,
33047b0a0dcbSJin Yao 							bi->to.al_addr);
3305*ef1aec60SIan Rogers 		} else if (browser->he_selection) {
3306ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3307ea7cd592SNamhyung Kim 						       &actions[nr_options],
3308ea7cd592SNamhyung Kim 						       &options[nr_options],
33097b0a0dcbSJin Yao 						       browser->selection,
33107b0a0dcbSJin Yao 						       browser->he_selection->ip);
3311446fb96cSArnaldo Carvalho de Melo 		}
33120ba332f7SArnaldo Carvalho de Melo skip_annotation:
3313ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
3314ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
3315ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
3316045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
3317d5a599d9SArnaldo Carvalho de Melo 		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3318ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
3319ea7cd592SNamhyung Kim 					  &options[nr_options],
3320bd315aabSWang Nan 					  browser->selection ?
3321bd315aabSWang Nan 						browser->selection->map : NULL);
332284734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
332384734b06SKan Liang 					     &options[nr_options],
332484734b06SKan Liang 					     socked_id);
3325cdbab7c2SFeng Tang 		/* perf script support */
3326b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
3327b1baae89SNamhyung Kim 			goto skip_scripting;
3328b1baae89SNamhyung Kim 
3329cdbab7c2SFeng Tang 		if (browser->he_selection) {
3330fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
3331ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3332ea7cd592SNamhyung Kim 							     &actions[nr_options],
3333ea7cd592SNamhyung Kim 							     &options[nr_options],
33346f3da20eSAndi Kleen 							     thread, NULL, evsel);
33352eafd410SNamhyung Kim 			}
3336bd315aabSWang Nan 			/*
3337bd315aabSWang Nan 			 * Note that browser->selection != NULL
3338bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
3339bd315aabSWang Nan 			 * so we don't need to check browser->selection
3340bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
3341bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
3342bd315aabSWang Nan 			 *
3343bd315aabSWang Nan 			 * See hist_browser__show_entry.
3344bd315aabSWang Nan 			 */
33452e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
3346ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3347ea7cd592SNamhyung Kim 							     &actions[nr_options],
3348ea7cd592SNamhyung Kim 							     &options[nr_options],
33496f3da20eSAndi Kleen 							     NULL, browser->selection->sym,
33506f3da20eSAndi Kleen 							     evsel);
3351cdbab7c2SFeng Tang 			}
3352c221acb0SNamhyung Kim 		}
3353ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
33546f3da20eSAndi Kleen 					     &options[nr_options], NULL, NULL, evsel);
33554968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
33564968ac8fSAndi Kleen 						 &options[nr_options],
3357d61cbb85SWei Li 						 hist_browser__selected_res_sample(browser),
33584968ac8fSAndi Kleen 						 evsel, A_NORMAL);
33594968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
33604968ac8fSAndi Kleen 						 &options[nr_options],
3361d61cbb85SWei Li 						 hist_browser__selected_res_sample(browser),
33624968ac8fSAndi Kleen 						 evsel, A_ASM);
33634968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
33644968ac8fSAndi Kleen 						 &options[nr_options],
3365d61cbb85SWei Li 						 hist_browser__selected_res_sample(browser),
33664968ac8fSAndi Kleen 						 evsel, A_SOURCE);
3367ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
3368ea7cd592SNamhyung Kim 					     &options[nr_options]);
3369b1baae89SNamhyung Kim skip_scripting:
3370ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
3371ea7cd592SNamhyung Kim 					   &options[nr_options]);
3372aca7a94dSNamhyung Kim 
3373ea7cd592SNamhyung Kim 		do {
3374ea7cd592SNamhyung Kim 			struct popup_action *act;
3375ea7cd592SNamhyung Kim 
33764c8b9c0fSArnaldo Carvalho de Melo 			choice = ui__popup_menu(nr_options, options, &key);
33774c8b9c0fSArnaldo Carvalho de Melo 			if (choice == -1)
3378aca7a94dSNamhyung Kim 				break;
3379aca7a94dSNamhyung Kim 
33804c8b9c0fSArnaldo Carvalho de Melo 			if (choice == nr_options)
33814c8b9c0fSArnaldo Carvalho de Melo 				goto do_hotkey;
33824c8b9c0fSArnaldo Carvalho de Melo 
3383ea7cd592SNamhyung Kim 			act = &actions[choice];
3384ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
3385ea7cd592SNamhyung Kim 		} while (key == 1);
3386aca7a94dSNamhyung Kim 
3387bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3388341487abSFeng Tang 			break;
3389341487abSFeng Tang 	}
3390aca7a94dSNamhyung Kim out_free_stack:
339101f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3392aca7a94dSNamhyung Kim out:
3393aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3394f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3395aca7a94dSNamhyung Kim 	return key;
3396aca7a94dSNamhyung Kim }
3397aca7a94dSNamhyung Kim 
339832dcd021SJiri Olsa struct evsel_menu {
3399aca7a94dSNamhyung Kim 	struct ui_browser b;
340032dcd021SJiri Olsa 	struct evsel *selection;
3401cd0cccbaSArnaldo Carvalho de Melo 	struct annotation_options *annotation_opts;
3402aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3403064f1981SNamhyung Kim 	float min_pcnt;
3404ce80d3beSKan Liang 	struct perf_env *env;
3405aca7a94dSNamhyung Kim };
3406aca7a94dSNamhyung Kim 
3407aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3408aca7a94dSNamhyung Kim 				   void *entry, int row)
3409aca7a94dSNamhyung Kim {
341032dcd021SJiri Olsa 	struct evsel_menu *menu = container_of(browser,
341132dcd021SJiri Olsa 						    struct evsel_menu, b);
3412b27c4eceSJiri Olsa 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
34134ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3414aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
34150f0abbacSNamhyung Kim 	unsigned long nr_events = hists->stats.nr_samples;
34168ab2e96dSArnaldo Carvalho de Melo 	const char *ev_name = evsel__name(evsel);
3417aca7a94dSNamhyung Kim 	char bf[256], unit;
3418aca7a94dSNamhyung Kim 	const char *warn = " ";
3419aca7a94dSNamhyung Kim 	size_t printed;
3420aca7a94dSNamhyung Kim 
3421aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3422aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3423aca7a94dSNamhyung Kim 
3424c754c382SArnaldo Carvalho de Melo 	if (evsel__is_group_event(evsel)) {
342532dcd021SJiri Olsa 		struct evsel *pos;
3426717e263fSNamhyung Kim 
34278ab2e96dSArnaldo Carvalho de Melo 		ev_name = evsel__group_name(evsel);
3428717e263fSNamhyung Kim 
3429717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
34304ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
34310f0abbacSNamhyung Kim 			nr_events += pos_hists->stats.nr_samples;
3432717e263fSNamhyung Kim 		}
3433717e263fSNamhyung Kim 	}
3434717e263fSNamhyung Kim 
3435aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3436aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3437aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3438517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3439aca7a94dSNamhyung Kim 
34400f0abbacSNamhyung Kim 	nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3441aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3442aca7a94dSNamhyung Kim 		menu->lost_events = true;
3443aca7a94dSNamhyung Kim 		if (!current_entry)
3444aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3445aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3446aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3447aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3448aca7a94dSNamhyung Kim 		warn = bf;
3449aca7a94dSNamhyung Kim 	}
3450aca7a94dSNamhyung Kim 
345126270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3452aca7a94dSNamhyung Kim 
3453aca7a94dSNamhyung Kim 	if (current_entry)
3454aca7a94dSNamhyung Kim 		menu->selection = evsel;
3455aca7a94dSNamhyung Kim }
3456aca7a94dSNamhyung Kim 
345732dcd021SJiri Olsa static int perf_evsel_menu__run(struct evsel_menu *menu,
3458aca7a94dSNamhyung Kim 				int nr_events, const char *help,
345906cc1a47SKan Liang 				struct hist_browser_timer *hbt,
346006cc1a47SKan Liang 				bool warn_lost_event)
3461aca7a94dSNamhyung Kim {
346263503dbaSJiri Olsa 	struct evlist *evlist = menu->b.priv;
346332dcd021SJiri Olsa 	struct evsel *pos;
3464dd00d486SJiri Olsa 	const char *title = "Available samples";
34659783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3466aca7a94dSNamhyung Kim 	int key;
3467aca7a94dSNamhyung Kim 
3468aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3469aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3470aca7a94dSNamhyung Kim 		return -1;
3471aca7a94dSNamhyung Kim 
3472aca7a94dSNamhyung Kim 	while (1) {
3473aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3474aca7a94dSNamhyung Kim 
3475aca7a94dSNamhyung Kim 		switch (key) {
3476aca7a94dSNamhyung Kim 		case K_TIMER:
3477ceb75476SLeo Yan 			if (hbt)
34789783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3479aca7a94dSNamhyung Kim 
348006cc1a47SKan Liang 			if (!menu->lost_events_warned &&
348106cc1a47SKan Liang 			    menu->lost_events &&
348206cc1a47SKan Liang 			    warn_lost_event) {
3483aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3484aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3485aca7a94dSNamhyung Kim 			}
3486aca7a94dSNamhyung Kim 			continue;
3487aca7a94dSNamhyung Kim 		case K_RIGHT:
3488aca7a94dSNamhyung Kim 		case K_ENTER:
3489aca7a94dSNamhyung Kim 			if (!menu->selection)
3490aca7a94dSNamhyung Kim 				continue;
3491aca7a94dSNamhyung Kim 			pos = menu->selection;
3492aca7a94dSNamhyung Kim browse_hists:
3493900c8eadSArnaldo Carvalho de Melo 			evlist__set_selected(evlist, pos);
3494aca7a94dSNamhyung Kim 			/*
3495aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3496aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3497aca7a94dSNamhyung Kim 			 */
34989783adf7SNamhyung Kim 			if (hbt)
34999783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
350056933029SArnaldo Carvalho de Melo 			key = evsel__hists_browse(pos, nr_events, help, true, hbt,
350156933029SArnaldo Carvalho de Melo 						  menu->min_pcnt, menu->env,
3502cd0cccbaSArnaldo Carvalho de Melo 						  warn_lost_event,
3503cd0cccbaSArnaldo Carvalho de Melo 						  menu->annotation_opts);
3504aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3505aca7a94dSNamhyung Kim 			switch (key) {
3506aca7a94dSNamhyung Kim 			case K_TAB:
3507ce9036a6SJiri Olsa 				if (pos->core.node.next == &evlist->core.entries)
3508515dbe48SJiri Olsa 					pos = evlist__first(evlist);
3509aca7a94dSNamhyung Kim 				else
3510e470daeaSArnaldo Carvalho de Melo 					pos = evsel__next(pos);
3511aca7a94dSNamhyung Kim 				goto browse_hists;
3512aca7a94dSNamhyung Kim 			case K_UNTAB:
3513ce9036a6SJiri Olsa 				if (pos->core.node.prev == &evlist->core.entries)
3514515dbe48SJiri Olsa 					pos = evlist__last(evlist);
3515aca7a94dSNamhyung Kim 				else
3516e470daeaSArnaldo Carvalho de Melo 					pos = evsel__prev(pos);
3517aca7a94dSNamhyung Kim 				goto browse_hists;
3518341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
35195e3b810aSJin Yao 			case K_RELOAD:
3520aca7a94dSNamhyung Kim 			case 'q':
3521aca7a94dSNamhyung Kim 			case CTRL('c'):
3522aca7a94dSNamhyung Kim 				goto out;
352363ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3524aca7a94dSNamhyung Kim 			default:
3525aca7a94dSNamhyung Kim 				continue;
3526aca7a94dSNamhyung Kim 			}
3527aca7a94dSNamhyung Kim 		case K_LEFT:
3528aca7a94dSNamhyung Kim 			continue;
3529aca7a94dSNamhyung Kim 		case K_ESC:
3530aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3531aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3532aca7a94dSNamhyung Kim 				continue;
3533aca7a94dSNamhyung Kim 			/* Fall thru */
3534aca7a94dSNamhyung Kim 		case 'q':
3535aca7a94dSNamhyung Kim 		case CTRL('c'):
3536aca7a94dSNamhyung Kim 			goto out;
3537aca7a94dSNamhyung Kim 		default:
3538aca7a94dSNamhyung Kim 			continue;
3539aca7a94dSNamhyung Kim 		}
3540aca7a94dSNamhyung Kim 	}
3541aca7a94dSNamhyung Kim 
3542aca7a94dSNamhyung Kim out:
3543aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3544aca7a94dSNamhyung Kim 	return key;
3545aca7a94dSNamhyung Kim }
3546aca7a94dSNamhyung Kim 
3547316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3548fc24d7c2SNamhyung Kim 				 void *entry)
3549fc24d7c2SNamhyung Kim {
3550b27c4eceSJiri Olsa 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3551fc24d7c2SNamhyung Kim 
3552c754c382SArnaldo Carvalho de Melo 	if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3553fc24d7c2SNamhyung Kim 		return true;
3554fc24d7c2SNamhyung Kim 
3555fc24d7c2SNamhyung Kim 	return false;
3556fc24d7c2SNamhyung Kim }
3557fc24d7c2SNamhyung Kim 
3558f4bd0b4aSArnaldo Carvalho de Melo static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3559f4bd0b4aSArnaldo Carvalho de Melo 				      struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3560f4bd0b4aSArnaldo Carvalho de Melo 				      bool warn_lost_event, struct annotation_options *annotation_opts)
3561aca7a94dSNamhyung Kim {
356232dcd021SJiri Olsa 	struct evsel *pos;
356332dcd021SJiri Olsa 	struct evsel_menu menu = {
3564aca7a94dSNamhyung Kim 		.b = {
3565ce9036a6SJiri Olsa 			.entries    = &evlist->core.entries,
3566aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3567aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3568aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3569fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3570fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3571aca7a94dSNamhyung Kim 			.priv	    = evlist,
3572aca7a94dSNamhyung Kim 		},
3573064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
357468d80758SNamhyung Kim 		.env = env,
3575cd0cccbaSArnaldo Carvalho de Melo 		.annotation_opts = annotation_opts,
3576aca7a94dSNamhyung Kim 	};
3577aca7a94dSNamhyung Kim 
3578aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3579aca7a94dSNamhyung Kim 
3580e5cadb93SArnaldo Carvalho de Melo 	evlist__for_each_entry(evlist, pos) {
35818ab2e96dSArnaldo Carvalho de Melo 		const char *ev_name = evsel__name(pos);
3582aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3583aca7a94dSNamhyung Kim 
3584aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3585aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3586aca7a94dSNamhyung Kim 	}
3587aca7a94dSNamhyung Kim 
358806cc1a47SKan Liang 	return perf_evsel_menu__run(&menu, nr_entries, help,
358906cc1a47SKan Liang 				    hbt, warn_lost_event);
3590aca7a94dSNamhyung Kim }
3591aca7a94dSNamhyung Kim 
3592e414fd1aSArnaldo Carvalho de Melo static bool evlist__single_entry(struct evlist *evlist)
3593bee9ca1cSArnaldo Carvalho de Melo {
3594bee9ca1cSArnaldo Carvalho de Melo 	int nr_entries = evlist->core.nr_entries;
3595bee9ca1cSArnaldo Carvalho de Melo 
3596bee9ca1cSArnaldo Carvalho de Melo 	if (nr_entries == 1)
3597bee9ca1cSArnaldo Carvalho de Melo 	       return true;
3598bee9ca1cSArnaldo Carvalho de Melo 
3599bee9ca1cSArnaldo Carvalho de Melo 	if (nr_entries == 2) {
3600bee9ca1cSArnaldo Carvalho de Melo 		struct evsel *last = evlist__last(evlist);
3601bee9ca1cSArnaldo Carvalho de Melo 
3602bee9ca1cSArnaldo Carvalho de Melo 		if (evsel__is_dummy_event(last))
3603bee9ca1cSArnaldo Carvalho de Melo 			return true;
3604bee9ca1cSArnaldo Carvalho de Melo 	}
3605bee9ca1cSArnaldo Carvalho de Melo 
3606bee9ca1cSArnaldo Carvalho de Melo 	return false;
3607bee9ca1cSArnaldo Carvalho de Melo }
3608bee9ca1cSArnaldo Carvalho de Melo 
3609f4bd0b4aSArnaldo Carvalho de Melo int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3610f4bd0b4aSArnaldo Carvalho de Melo 			     float min_pcnt, struct perf_env *env, bool warn_lost_event,
3611cd0cccbaSArnaldo Carvalho de Melo 			     struct annotation_options *annotation_opts)
3612aca7a94dSNamhyung Kim {
36136484d2f9SJiri Olsa 	int nr_entries = evlist->core.nr_entries;
3614fc24d7c2SNamhyung Kim 
3615e414fd1aSArnaldo Carvalho de Melo 	if (evlist__single_entry(evlist)) {
3616d4ccbacbSArnaldo Carvalho de Melo single_entry: {
3617515dbe48SJiri Olsa 		struct evsel *first = evlist__first(evlist);
3618fc24d7c2SNamhyung Kim 
361956933029SArnaldo Carvalho de Melo 		return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
362056933029SArnaldo Carvalho de Melo 					   env, warn_lost_event, annotation_opts);
3621aca7a94dSNamhyung Kim 	}
3622d4ccbacbSArnaldo Carvalho de Melo 	}
3623aca7a94dSNamhyung Kim 
3624fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
362532dcd021SJiri Olsa 		struct evsel *pos;
3626fc24d7c2SNamhyung Kim 
3627fc24d7c2SNamhyung Kim 		nr_entries = 0;
3628e5cadb93SArnaldo Carvalho de Melo 		evlist__for_each_entry(evlist, pos) {
3629c754c382SArnaldo Carvalho de Melo 			if (evsel__is_group_leader(pos))
3630fc24d7c2SNamhyung Kim 				nr_entries++;
36310050f7aaSArnaldo Carvalho de Melo 		}
3632fc24d7c2SNamhyung Kim 
3633fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3634fc24d7c2SNamhyung Kim 			goto single_entry;
3635fc24d7c2SNamhyung Kim 	}
3636fc24d7c2SNamhyung Kim 
3637f4bd0b4aSArnaldo Carvalho de Melo 	return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3638f4bd0b4aSArnaldo Carvalho de Melo 					  warn_lost_event, annotation_opts);
3639aca7a94dSNamhyung Kim }
36405cb456afSJin Yao 
36415cb456afSJin Yao static int block_hists_browser__title(struct hist_browser *browser, char *bf,
36425cb456afSJin Yao 				      size_t size)
36435cb456afSJin Yao {
36445cb456afSJin Yao 	struct hists *hists = evsel__hists(browser->block_evsel);
36458ab2e96dSArnaldo Carvalho de Melo 	const char *evname = evsel__name(browser->block_evsel);
36460f0abbacSNamhyung Kim 	unsigned long nr_samples = hists->stats.nr_samples;
36475cb456afSJin Yao 	int ret;
36485cb456afSJin Yao 
36495cb456afSJin Yao 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
36505cb456afSJin Yao 	if (evname)
36515cb456afSJin Yao 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
36525cb456afSJin Yao 
36535cb456afSJin Yao 	return 0;
36545cb456afSJin Yao }
36555cb456afSJin Yao 
36565cb456afSJin Yao int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3657848a5e50SJin Yao 			   float min_percent, struct perf_env *env,
3658848a5e50SJin Yao 			   struct annotation_options *annotation_opts)
36595cb456afSJin Yao {
36605cb456afSJin Yao 	struct hists *hists = &bh->block_hists;
36615cb456afSJin Yao 	struct hist_browser *browser;
36625cb456afSJin Yao 	int key = -1;
3663848a5e50SJin Yao 	struct popup_action action;
36645cb456afSJin Yao 	static const char help[] =
36655cb456afSJin Yao 	" q             Quit \n";
36665cb456afSJin Yao 
36675cb456afSJin Yao 	browser = hist_browser__new(hists);
36685cb456afSJin Yao 	if (!browser)
36695cb456afSJin Yao 		return -1;
36705cb456afSJin Yao 
36715cb456afSJin Yao 	browser->block_evsel = evsel;
36725cb456afSJin Yao 	browser->title = block_hists_browser__title;
36735cb456afSJin Yao 	browser->min_pcnt = min_percent;
3674848a5e50SJin Yao 	browser->env = env;
3675848a5e50SJin Yao 	browser->annotation_opts = annotation_opts;
36765cb456afSJin Yao 
36775cb456afSJin Yao 	/* reset abort key so that it can get Ctrl-C as a key */
36785cb456afSJin Yao 	SLang_reset_tty();
36795cb456afSJin Yao 	SLang_init_tty(0, 0, 0);
36805cb456afSJin Yao 
3681848a5e50SJin Yao 	memset(&action, 0, sizeof(action));
3682848a5e50SJin Yao 
36835cb456afSJin Yao 	while (1) {
3684d10ec006SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, "? - help", true, 0);
36855cb456afSJin Yao 
36865cb456afSJin Yao 		switch (key) {
36875cb456afSJin Yao 		case 'q':
36885cb456afSJin Yao 			goto out;
36895cb456afSJin Yao 		case '?':
36905cb456afSJin Yao 			ui_browser__help_window(&browser->b, help);
36915cb456afSJin Yao 			break;
3692848a5e50SJin Yao 		case 'a':
3693848a5e50SJin Yao 		case K_ENTER:
3694848a5e50SJin Yao 			if (!browser->selection ||
3695848a5e50SJin Yao 			    !browser->selection->sym) {
3696848a5e50SJin Yao 				continue;
3697848a5e50SJin Yao 			}
3698848a5e50SJin Yao 
3699848a5e50SJin Yao 			action.ms.map = browser->selection->map;
3700848a5e50SJin Yao 			action.ms.sym = browser->selection->sym;
3701848a5e50SJin Yao 			do_annotate(browser, &action);
3702848a5e50SJin Yao 			continue;
37035cb456afSJin Yao 		default:
37045cb456afSJin Yao 			break;
37055cb456afSJin Yao 		}
37065cb456afSJin Yao 	}
37075cb456afSJin Yao 
37085cb456afSJin Yao out:
37095cb456afSJin Yao 	hist_browser__delete(browser);
37105cb456afSJin Yao 	return 0;
37115cb456afSJin Yao }
3712