xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 63df0e4b)
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"
3268d80758SNamhyung Kim #include "../../arch/common.h"
33c1a604dfSArnaldo Carvalho de Melo #include "../../perf.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 
410e6d6abfcSArnaldo Carvalho de Melo static bool hist_browser__he_selection_unfolded(struct hist_browser *browser)
411e6d6abfcSArnaldo Carvalho de Melo {
412e6d6abfcSArnaldo Carvalho de Melo 	return browser->he_selection ? browser->he_selection->unfolded : false;
413e6d6abfcSArnaldo Carvalho de Melo }
414e6d6abfcSArnaldo Carvalho de Melo 
415bdc633feSArnaldo Carvalho de Melo static bool hist_browser__selection_unfolded(struct hist_browser *browser)
416bdc633feSArnaldo Carvalho de Melo {
417bdc633feSArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
418bdc633feSArnaldo Carvalho de Melo 	struct map_symbol *ms = browser->selection;
419bdc633feSArnaldo Carvalho de Melo 
420bdc633feSArnaldo Carvalho de Melo 	if (!he || !ms)
421bdc633feSArnaldo Carvalho de Melo 		return false;
422bdc633feSArnaldo Carvalho de Melo 
423bdc633feSArnaldo Carvalho de Melo 	if (ms == &he->ms)
424bdc633feSArnaldo Carvalho de Melo 	       return he->unfolded;
425bdc633feSArnaldo Carvalho de Melo 
426bdc633feSArnaldo Carvalho de Melo 	return container_of(ms, struct callchain_list, ms)->unfolded;
427bdc633feSArnaldo Carvalho de Melo }
428bdc633feSArnaldo Carvalho de Melo 
429bdc633feSArnaldo Carvalho de Melo static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
430bdc633feSArnaldo Carvalho de Melo {
431bdc633feSArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
432bdc633feSArnaldo Carvalho de Melo 	struct map_symbol *ms = browser->selection;
433bdc633feSArnaldo Carvalho de Melo 	struct callchain_list *callchain_entry;
434bdc633feSArnaldo Carvalho de Melo 
435bdc633feSArnaldo Carvalho de Melo 	if (!he || !ms)
436bdc633feSArnaldo Carvalho de Melo 		return NULL;
437bdc633feSArnaldo Carvalho de Melo 
438bdc633feSArnaldo Carvalho de Melo 	if (ms == &he->ms) {
439bdc633feSArnaldo Carvalho de Melo 	       hist_entry__sym_snprintf(he, bf, size, 0);
440bdc633feSArnaldo Carvalho de Melo 	       return bf + 4; // skip the level, e.g. '[k] '
441bdc633feSArnaldo Carvalho de Melo 	}
442bdc633feSArnaldo Carvalho de Melo 
443bdc633feSArnaldo Carvalho de Melo 	callchain_entry = container_of(ms, struct callchain_list, ms);
444bdc633feSArnaldo Carvalho de Melo 	return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
445bdc633feSArnaldo Carvalho de Melo }
446bdc633feSArnaldo Carvalho de Melo 
44705e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
448aca7a94dSNamhyung Kim {
44905e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
4503698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
4513698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
4523698dab1SNamhyung Kim 	bool has_children;
453aca7a94dSNamhyung Kim 
4544938cf0cSWang Nan 	if (!he || !ms)
4554938cf0cSWang Nan 		return false;
4564938cf0cSWang Nan 
4573698dab1SNamhyung Kim 	if (ms == &he->ms)
4583698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
4593698dab1SNamhyung Kim 	else
4603698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
4613698dab1SNamhyung Kim 
4623698dab1SNamhyung Kim 	if (has_children) {
463f5b763feSNamhyung Kim 		int child_rows = 0;
464f5b763feSNamhyung Kim 
465aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
466c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
467aca7a94dSNamhyung Kim 
468f5b763feSNamhyung Kim 		if (he->leaf)
469f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
470f5b763feSNamhyung Kim 		else
471f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
472f5b763feSNamhyung Kim 
473f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
474f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
475f5b763feSNamhyung Kim 
476f5b763feSNamhyung Kim 		if (he->unfolded) {
477f5b763feSNamhyung Kim 			if (he->leaf)
4780d3eb0b7SJin Yao 				he->nr_rows = callchain__count_rows(
4790d3eb0b7SJin Yao 						&he->sorted_chain);
480aca7a94dSNamhyung Kim 			else
481f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
482f5b763feSNamhyung Kim 
483f5b763feSNamhyung Kim 			/* account grand children */
484f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
485f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
48679dded87SNamhyung Kim 
48779dded87SNamhyung Kim 			if (!he->leaf && he->nr_rows == 0) {
48879dded87SNamhyung Kim 				he->has_no_entry = true;
48979dded87SNamhyung Kim 				he->nr_rows = 1;
49079dded87SNamhyung Kim 			}
491f5b763feSNamhyung Kim 		} else {
492f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
493f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
494f5b763feSNamhyung Kim 
49579dded87SNamhyung Kim 			if (he->has_no_entry)
49679dded87SNamhyung Kim 				he->has_no_entry = false;
49779dded87SNamhyung Kim 
498aca7a94dSNamhyung Kim 			he->nr_rows = 0;
499f5b763feSNamhyung Kim 		}
500c3b78952SNamhyung Kim 
501c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
502f5b763feSNamhyung Kim 
503f5b763feSNamhyung Kim 		if (he->leaf)
504c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
505f5b763feSNamhyung Kim 		else
506f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
507aca7a94dSNamhyung Kim 
508aca7a94dSNamhyung Kim 		return true;
509aca7a94dSNamhyung Kim 	}
510aca7a94dSNamhyung Kim 
511aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
512aca7a94dSNamhyung Kim 	return false;
513aca7a94dSNamhyung Kim }
514aca7a94dSNamhyung Kim 
51505e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
516aca7a94dSNamhyung Kim {
517aca7a94dSNamhyung Kim 	int n = 0;
518aca7a94dSNamhyung Kim 	struct rb_node *nd;
519aca7a94dSNamhyung Kim 
52005e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
521aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
522aca7a94dSNamhyung Kim 		struct callchain_list *chain;
523aca7a94dSNamhyung Kim 		bool has_children = false;
524aca7a94dSNamhyung Kim 
525aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
526aca7a94dSNamhyung Kim 			++n;
5273698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
5283698dab1SNamhyung Kim 			has_children = chain->has_children;
529aca7a94dSNamhyung Kim 		}
530aca7a94dSNamhyung Kim 
531aca7a94dSNamhyung Kim 		if (has_children)
532aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
533aca7a94dSNamhyung Kim 	}
534aca7a94dSNamhyung Kim 
535aca7a94dSNamhyung Kim 	return n;
536aca7a94dSNamhyung Kim }
537aca7a94dSNamhyung Kim 
538aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
539aca7a94dSNamhyung Kim {
540aca7a94dSNamhyung Kim 	struct callchain_list *chain;
541aca7a94dSNamhyung Kim 	bool has_children = false;
542aca7a94dSNamhyung Kim 	int n = 0;
543aca7a94dSNamhyung Kim 
544aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
545aca7a94dSNamhyung Kim 		++n;
5463698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
5473698dab1SNamhyung Kim 		has_children = chain->has_children;
548aca7a94dSNamhyung Kim 	}
549aca7a94dSNamhyung Kim 
550aca7a94dSNamhyung Kim 	if (has_children)
551aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
552aca7a94dSNamhyung Kim 
553aca7a94dSNamhyung Kim 	return n;
554aca7a94dSNamhyung Kim }
555aca7a94dSNamhyung Kim 
556aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
557aca7a94dSNamhyung Kim {
558aca7a94dSNamhyung Kim 	struct rb_node *nd;
559aca7a94dSNamhyung Kim 	int n = 0;
560aca7a94dSNamhyung Kim 
561aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
562aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
563aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
564aca7a94dSNamhyung Kim 	}
565aca7a94dSNamhyung Kim 
566aca7a94dSNamhyung Kim 	return n;
567aca7a94dSNamhyung Kim }
568aca7a94dSNamhyung Kim 
569492b1010SNamhyung Kim static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
570492b1010SNamhyung Kim 				 bool unfold __maybe_unused)
571492b1010SNamhyung Kim {
572492b1010SNamhyung Kim 	float percent;
573492b1010SNamhyung Kim 	struct rb_node *nd;
574492b1010SNamhyung Kim 	struct hist_entry *child;
575492b1010SNamhyung Kim 	int n = 0;
576492b1010SNamhyung Kim 
5772eb3d689SDavidlohr Bueso 	for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
578492b1010SNamhyung Kim 		child = rb_entry(nd, struct hist_entry, rb_node);
579492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
580492b1010SNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt)
581492b1010SNamhyung Kim 			n++;
582492b1010SNamhyung Kim 	}
583492b1010SNamhyung Kim 
584492b1010SNamhyung Kim 	return n;
585492b1010SNamhyung Kim }
586492b1010SNamhyung Kim 
587b33f9226SJiri Olsa static void __hist_entry__set_folding(struct hist_entry *he,
588492b1010SNamhyung Kim 				      struct hist_browser *hb, bool unfold)
589aca7a94dSNamhyung Kim {
59005e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
5913698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
592aca7a94dSNamhyung Kim 
5933698dab1SNamhyung Kim 	if (he->has_children) {
594492b1010SNamhyung Kim 		int n;
595492b1010SNamhyung Kim 
596492b1010SNamhyung Kim 		if (he->leaf)
597492b1010SNamhyung Kim 			n = callchain__set_folding(&he->sorted_chain, unfold);
598492b1010SNamhyung Kim 		else
599492b1010SNamhyung Kim 			n = hierarchy_set_folding(hb, he, unfold);
600492b1010SNamhyung Kim 
60105e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
602aca7a94dSNamhyung Kim 	} else
60305e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
604aca7a94dSNamhyung Kim }
605aca7a94dSNamhyung Kim 
606b33f9226SJiri Olsa static void hist_entry__set_folding(struct hist_entry *he,
607b33f9226SJiri Olsa 				    struct hist_browser *browser, bool unfold)
608aca7a94dSNamhyung Kim {
609492b1010SNamhyung Kim 	double percent;
610aca7a94dSNamhyung Kim 
611492b1010SNamhyung Kim 	percent = hist_entry__get_percent_limit(he);
612492b1010SNamhyung Kim 	if (he->filtered || percent < browser->min_pcnt)
613b33f9226SJiri Olsa 		return;
614b33f9226SJiri Olsa 
615b33f9226SJiri Olsa 	__hist_entry__set_folding(he, browser, unfold);
616492b1010SNamhyung Kim 
617492b1010SNamhyung Kim 	if (!he->depth || unfold)
618492b1010SNamhyung Kim 		browser->nr_hierarchy_entries++;
619492b1010SNamhyung Kim 	if (he->leaf)
620c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
62179dded87SNamhyung Kim 	else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
62279dded87SNamhyung Kim 		browser->nr_hierarchy_entries++;
62379dded87SNamhyung Kim 		he->has_no_entry = true;
62479dded87SNamhyung Kim 		he->nr_rows = 1;
62579dded87SNamhyung Kim 	} else
62679dded87SNamhyung Kim 		he->has_no_entry = false;
627aca7a94dSNamhyung Kim }
628b33f9226SJiri Olsa 
629b33f9226SJiri Olsa static void
630b33f9226SJiri Olsa __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
631b33f9226SJiri Olsa {
632b33f9226SJiri Olsa 	struct rb_node *nd;
633b33f9226SJiri Olsa 	struct hist_entry *he;
634b33f9226SJiri Olsa 
6352eb3d689SDavidlohr Bueso 	nd = rb_first_cached(&browser->hists->entries);
636b33f9226SJiri Olsa 	while (nd) {
637b33f9226SJiri Olsa 		he = rb_entry(nd, struct hist_entry, rb_node);
638b33f9226SJiri Olsa 
639b33f9226SJiri Olsa 		/* set folding state even if it's currently folded */
640b33f9226SJiri Olsa 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
641b33f9226SJiri Olsa 
642b33f9226SJiri Olsa 		hist_entry__set_folding(he, browser, unfold);
643b33f9226SJiri Olsa 	}
644aca7a94dSNamhyung Kim }
645aca7a94dSNamhyung Kim 
64605e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
647aca7a94dSNamhyung Kim {
648492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
649c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
650c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
651c3b78952SNamhyung Kim 
652c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
653aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
65405e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
655aca7a94dSNamhyung Kim }
656aca7a94dSNamhyung Kim 
6570e3fa7a7SJiri Olsa static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
6580e3fa7a7SJiri Olsa {
6590e3fa7a7SJiri Olsa 	if (!browser->he_selection)
6600e3fa7a7SJiri Olsa 		return;
6610e3fa7a7SJiri Olsa 
6620e3fa7a7SJiri Olsa 	hist_entry__set_folding(browser->he_selection, browser, unfold);
6630e3fa7a7SJiri Olsa 	browser->b.nr_entries = hist_browser__nr_entries(browser);
6640e3fa7a7SJiri Olsa }
6650e3fa7a7SJiri Olsa 
666aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
667aca7a94dSNamhyung Kim {
668aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
669aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
670aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
671aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
672aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
673aca7a94dSNamhyung Kim }
674aca7a94dSNamhyung Kim 
6755b91a86fSJiri Olsa static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
6765b91a86fSJiri Olsa {
6775b91a86fSJiri Olsa 	return browser->title ? browser->title(browser, bf, size) : 0;
6785b91a86fSJiri Olsa }
6795b91a86fSJiri Olsa 
680376c3c22SArnaldo Carvalho de Melo static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
681aca7a94dSNamhyung Kim {
682aca7a94dSNamhyung Kim 	switch (key) {
683fa5df943SNamhyung Kim 	case K_TIMER: {
684d10ec006SArnaldo Carvalho de Melo 		struct hist_browser_timer *hbt = browser->hbt;
6850f0abbacSNamhyung Kim 		struct evsel *evsel = hists_to_evsel(browser->hists);
686fa5df943SNamhyung Kim 		u64 nr_entries;
687ceb75476SLeo Yan 
688ceb75476SLeo Yan 		WARN_ON_ONCE(!hbt);
689ceb75476SLeo Yan 
690ceb75476SLeo Yan 		if (hbt)
6919783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
692fa5df943SNamhyung Kim 
693d10ec006SArnaldo Carvalho de Melo 		if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
694112f761fSNamhyung Kim 			hist_browser__update_nr_entries(browser);
695fa5df943SNamhyung Kim 
696c3b78952SNamhyung Kim 		nr_entries = hist_browser__nr_entries(browser);
697fa5df943SNamhyung Kim 		ui_browser__update_nr_entries(&browser->b, nr_entries);
698aca7a94dSNamhyung Kim 
69906cc1a47SKan Liang 		if (warn_lost_event &&
7000f0abbacSNamhyung Kim 		    (evsel->evlist->stats.nr_lost_warned !=
7010f0abbacSNamhyung Kim 		     evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
7020f0abbacSNamhyung Kim 			evsel->evlist->stats.nr_lost_warned =
7030f0abbacSNamhyung Kim 				evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
70405e8b080SArnaldo Carvalho de Melo 			ui_browser__warn_lost_events(&browser->b);
705aca7a94dSNamhyung Kim 		}
706aca7a94dSNamhyung Kim 
707376c3c22SArnaldo Carvalho de Melo 		hist_browser__title(browser, title, size);
70805e8b080SArnaldo Carvalho de Melo 		ui_browser__show_title(&browser->b, title);
709d10ec006SArnaldo Carvalho de Melo 		break;
710fa5df943SNamhyung Kim 	}
711aca7a94dSNamhyung Kim 	case 'D': { /* Debug */
712d10ec006SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
713aca7a94dSNamhyung Kim 		static int seq;
714d10ec006SArnaldo Carvalho de Melo 
715aca7a94dSNamhyung Kim 		ui_helpline__pop();
716fdae6400SArnaldo 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",
717d10ec006SArnaldo Carvalho de Melo 				   seq++, browser->b.nr_entries, browser->hists->nr_entries,
718d10ec006SArnaldo Carvalho de Melo 				   browser->b.extra_title_lines, browser->b.rows,
719d10ec006SArnaldo Carvalho de Melo 				   browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
720aca7a94dSNamhyung Kim 	}
721aca7a94dSNamhyung Kim 		break;
722aca7a94dSNamhyung Kim 	case 'C':
723aca7a94dSNamhyung Kim 		/* Collapse the whole world. */
72405e8b080SArnaldo Carvalho de Melo 		hist_browser__set_folding(browser, false);
725aca7a94dSNamhyung Kim 		break;
7260e3fa7a7SJiri Olsa 	case 'c':
7270e3fa7a7SJiri Olsa 		/* Collapse the selected entry. */
7280e3fa7a7SJiri Olsa 		hist_browser__set_folding_selected(browser, false);
7290e3fa7a7SJiri Olsa 		break;
730aca7a94dSNamhyung Kim 	case 'E':
731aca7a94dSNamhyung Kim 		/* Expand the whole world. */
73205e8b080SArnaldo Carvalho de Melo 		hist_browser__set_folding(browser, true);
733aca7a94dSNamhyung Kim 		break;
7340e3fa7a7SJiri Olsa 	case 'e':
7350e3fa7a7SJiri Olsa 		/* Expand the selected entry. */
736e6d6abfcSArnaldo Carvalho de Melo 		hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser));
7370e3fa7a7SJiri Olsa 		break;
738025bf7eaSArnaldo Carvalho de Melo 	case 'H':
739025bf7eaSArnaldo Carvalho de Melo 		browser->show_headers = !browser->show_headers;
740025bf7eaSArnaldo Carvalho de Melo 		hist_browser__update_rows(browser);
741025bf7eaSArnaldo Carvalho de Melo 		break;
7429218a913SArnaldo Carvalho de Melo 	case '+':
74305e8b080SArnaldo Carvalho de Melo 		if (hist_browser__toggle_fold(browser))
744aca7a94dSNamhyung Kim 			break;
745aca7a94dSNamhyung Kim 		/* fall thru */
746aca7a94dSNamhyung Kim 	default:
747d10ec006SArnaldo Carvalho de Melo 		return -1;
748aca7a94dSNamhyung Kim 	}
749d10ec006SArnaldo Carvalho de Melo 
750d10ec006SArnaldo Carvalho de Melo 	return 0;
751d10ec006SArnaldo Carvalho de Melo }
752d10ec006SArnaldo Carvalho de Melo 
753d10ec006SArnaldo Carvalho de Melo int hist_browser__run(struct hist_browser *browser, const char *help,
754d10ec006SArnaldo Carvalho de Melo 		      bool warn_lost_event, int key)
755d10ec006SArnaldo Carvalho de Melo {
756d10ec006SArnaldo Carvalho de Melo 	char title[160];
757d10ec006SArnaldo Carvalho de Melo 	struct hist_browser_timer *hbt = browser->hbt;
758d10ec006SArnaldo Carvalho de Melo 	int delay_secs = hbt ? hbt->refresh : 0;
759d10ec006SArnaldo Carvalho de Melo 
760d10ec006SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
761d10ec006SArnaldo Carvalho de Melo 	browser->b.nr_entries = hist_browser__nr_entries(browser);
762d10ec006SArnaldo Carvalho de Melo 
763d10ec006SArnaldo Carvalho de Melo 	hist_browser__title(browser, title, sizeof(title));
764d10ec006SArnaldo Carvalho de Melo 
765d10ec006SArnaldo Carvalho de Melo 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
766d10ec006SArnaldo Carvalho de Melo 		return -1;
767d10ec006SArnaldo Carvalho de Melo 
768376c3c22SArnaldo Carvalho de Melo 	if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
769d10ec006SArnaldo Carvalho de Melo 		goto out;
770d10ec006SArnaldo Carvalho de Melo 
771d10ec006SArnaldo Carvalho de Melo 	while (1) {
772d10ec006SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
773d10ec006SArnaldo Carvalho de Melo 
774376c3c22SArnaldo Carvalho de Melo 		if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
775d10ec006SArnaldo Carvalho de Melo 			break;
776aca7a94dSNamhyung Kim 	}
777aca7a94dSNamhyung Kim out:
77805e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
779aca7a94dSNamhyung Kim 	return key;
780aca7a94dSNamhyung Kim }
781aca7a94dSNamhyung Kim 
78239ee533fSNamhyung Kim struct callchain_print_arg {
78339ee533fSNamhyung Kim 	/* for hists browser */
78439ee533fSNamhyung Kim 	off_t	row_offset;
78539ee533fSNamhyung Kim 	bool	is_current_entry;
78639ee533fSNamhyung Kim 
78739ee533fSNamhyung Kim 	/* for file dump */
78839ee533fSNamhyung Kim 	FILE	*fp;
78939ee533fSNamhyung Kim 	int	printed;
79039ee533fSNamhyung Kim };
79139ee533fSNamhyung Kim 
79239ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
79339ee533fSNamhyung Kim 					 struct callchain_list *chain,
79439ee533fSNamhyung Kim 					 const char *str, int offset,
79539ee533fSNamhyung Kim 					 unsigned short row,
79639ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
79739ee533fSNamhyung Kim 
798f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
799f4536dddSNamhyung Kim 					       struct callchain_list *chain,
80039ee533fSNamhyung Kim 					       const char *str, int offset,
80139ee533fSNamhyung Kim 					       unsigned short row,
80239ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
803f4536dddSNamhyung Kim {
804f4536dddSNamhyung Kim 	int color, width;
80539ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
80670e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
807f4536dddSNamhyung Kim 
808f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
809f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
810f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
811f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
812f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
81339ee533fSNamhyung Kim 		arg->is_current_entry = true;
814f4536dddSNamhyung Kim 	}
815f4536dddSNamhyung Kim 
816f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
817ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
81826270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
819517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
82070e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
82126270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
822f4536dddSNamhyung Kim }
823f4536dddSNamhyung Kim 
82439ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
82539ee533fSNamhyung Kim 						  struct callchain_list *chain,
82639ee533fSNamhyung Kim 						  const char *str, int offset,
82739ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
82839ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
82939ee533fSNamhyung Kim {
83039ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
83139ee533fSNamhyung Kim 
83239ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
83339ee533fSNamhyung Kim 				folded_sign, str);
83439ee533fSNamhyung Kim }
83539ee533fSNamhyung Kim 
83639ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
83739ee533fSNamhyung Kim 				     unsigned short row);
83839ee533fSNamhyung Kim 
83939ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
84039ee533fSNamhyung Kim 					    unsigned short row)
84139ee533fSNamhyung Kim {
84239ee533fSNamhyung Kim 	return browser->b.rows == row;
84339ee533fSNamhyung Kim }
84439ee533fSNamhyung Kim 
84539ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
84639ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
84739ee533fSNamhyung Kim {
84839ee533fSNamhyung Kim 	return false;
84939ee533fSNamhyung Kim }
85039ee533fSNamhyung Kim 
851aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
852aca7a94dSNamhyung Kim 
85318bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
85418bb8381SNamhyung Kim 					     struct callchain_node *node,
85518bb8381SNamhyung Kim 					     struct callchain_list *chain,
85618bb8381SNamhyung Kim 					     unsigned short row, u64 total,
85718bb8381SNamhyung Kim 					     bool need_percent, int offset,
85818bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
85918bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
86018bb8381SNamhyung Kim {
86118bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
862fef51ecdSJin Yao 	char buf[64], *alloc_str2;
86318bb8381SNamhyung Kim 	const char *str;
8642a704fc8SMilian Wolff 	int ret = 1;
86518bb8381SNamhyung Kim 
86618bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
86718bb8381SNamhyung Kim 		arg->row_offset--;
86818bb8381SNamhyung Kim 		return 0;
86918bb8381SNamhyung Kim 	}
87018bb8381SNamhyung Kim 
87118bb8381SNamhyung Kim 	alloc_str = NULL;
872fef51ecdSJin Yao 	alloc_str2 = NULL;
873fef51ecdSJin Yao 
87418bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
87518bb8381SNamhyung Kim 				       browser->show_dso);
87618bb8381SNamhyung Kim 
877fef51ecdSJin Yao 	if (symbol_conf.show_branchflag_count) {
878c4ee0625SJin Yao 		callchain_list_counts__printf_value(chain, NULL,
879fef51ecdSJin Yao 						    buf, sizeof(buf));
88018bb8381SNamhyung Kim 
881fef51ecdSJin Yao 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
882fef51ecdSJin Yao 			str = "Not enough memory!";
883fef51ecdSJin Yao 		else
884fef51ecdSJin Yao 			str = alloc_str2;
885fef51ecdSJin Yao 	}
886fef51ecdSJin Yao 
887fef51ecdSJin Yao 	if (need_percent) {
88818bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
88918bb8381SNamhyung Kim 						total);
89018bb8381SNamhyung Kim 
89118bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
89218bb8381SNamhyung Kim 			str = "Not enough memory!";
89318bb8381SNamhyung Kim 		else
89418bb8381SNamhyung Kim 			str = alloc_str;
89518bb8381SNamhyung Kim 	}
89618bb8381SNamhyung Kim 
89718bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
89818bb8381SNamhyung Kim 	free(alloc_str);
899fef51ecdSJin Yao 	free(alloc_str2);
9000d3eb0b7SJin Yao 
9012a704fc8SMilian Wolff 	return ret;
90218bb8381SNamhyung Kim }
90318bb8381SNamhyung Kim 
90459c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
90559c624e2SNamhyung Kim {
90659c624e2SNamhyung Kim 	struct callchain_node *child;
90759c624e2SNamhyung Kim 
90859c624e2SNamhyung Kim 	if (node == NULL)
90959c624e2SNamhyung Kim 		return false;
91059c624e2SNamhyung Kim 
91159c624e2SNamhyung Kim 	if (rb_next(node))
91259c624e2SNamhyung Kim 		return true;
91359c624e2SNamhyung Kim 
91459c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
91559c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
91659c624e2SNamhyung Kim }
91759c624e2SNamhyung Kim 
9184b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
9194b3a3212SNamhyung Kim 					     struct rb_root *root,
9204b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
92159c624e2SNamhyung Kim 					     u64 parent_total,
9224b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
9234b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
9244b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
9254b3a3212SNamhyung Kim {
9264b3a3212SNamhyung Kim 	struct rb_node *node;
9274b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
9284b3a3212SNamhyung Kim 	bool need_percent;
9294b3a3212SNamhyung Kim 
9304b3a3212SNamhyung Kim 	node = rb_first(root);
93159c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9324b3a3212SNamhyung Kim 
9334b3a3212SNamhyung Kim 	while (node) {
9344b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
9354b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
9364b3a3212SNamhyung Kim 		struct callchain_list *chain;
9374b3a3212SNamhyung Kim 		char folded_sign = ' ';
9384b3a3212SNamhyung Kim 		int first = true;
9394b3a3212SNamhyung Kim 		int extra_offset = 0;
9404b3a3212SNamhyung Kim 
9414b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9424b3a3212SNamhyung Kim 			bool was_first = first;
9434b3a3212SNamhyung Kim 
9444b3a3212SNamhyung Kim 			if (first)
9454b3a3212SNamhyung Kim 				first = false;
9464b3a3212SNamhyung Kim 			else if (need_percent)
9474b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
9484b3a3212SNamhyung Kim 
9494b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
9504b3a3212SNamhyung Kim 
9514b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
9524b3a3212SNamhyung Kim 							chain, row, total,
9534b3a3212SNamhyung Kim 							was_first && need_percent,
9544b3a3212SNamhyung Kim 							offset + extra_offset,
9554b3a3212SNamhyung Kim 							print, arg);
9564b3a3212SNamhyung Kim 
9574b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
9584b3a3212SNamhyung Kim 				goto out;
9594b3a3212SNamhyung Kim 
9604b3a3212SNamhyung Kim 			if (folded_sign == '+')
9614b3a3212SNamhyung Kim 				goto next;
9624b3a3212SNamhyung Kim 		}
9634b3a3212SNamhyung Kim 
9644b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
9654b3a3212SNamhyung Kim 			bool was_first = first;
9664b3a3212SNamhyung Kim 
9674b3a3212SNamhyung Kim 			if (first)
9684b3a3212SNamhyung Kim 				first = false;
9694b3a3212SNamhyung Kim 			else if (need_percent)
9704b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
9714b3a3212SNamhyung Kim 
9724b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
9734b3a3212SNamhyung Kim 
9744b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
9754b3a3212SNamhyung Kim 							chain, row, total,
9764b3a3212SNamhyung Kim 							was_first && need_percent,
9774b3a3212SNamhyung Kim 							offset + extra_offset,
9784b3a3212SNamhyung Kim 							print, arg);
9794b3a3212SNamhyung Kim 
9804b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
9814b3a3212SNamhyung Kim 				goto out;
9824b3a3212SNamhyung Kim 
9834b3a3212SNamhyung Kim 			if (folded_sign == '+')
9844b3a3212SNamhyung Kim 				break;
9854b3a3212SNamhyung Kim 		}
9864b3a3212SNamhyung Kim 
9874b3a3212SNamhyung Kim next:
9884b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
9894b3a3212SNamhyung Kim 			break;
9904b3a3212SNamhyung Kim 		node = next;
9914b3a3212SNamhyung Kim 	}
9924b3a3212SNamhyung Kim out:
9934b3a3212SNamhyung Kim 	return row - first_row;
9944b3a3212SNamhyung Kim }
9954b3a3212SNamhyung Kim 
9968c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
9978c430a34SNamhyung Kim 						struct callchain_list *chain,
9988c430a34SNamhyung Kim 						char *value_str, char *old_str)
9998c430a34SNamhyung Kim {
10008c430a34SNamhyung Kim 	char bf[1024];
10018c430a34SNamhyung Kim 	const char *str;
10028c430a34SNamhyung Kim 	char *new;
10038c430a34SNamhyung Kim 
10048c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
10058c430a34SNamhyung Kim 				       browser->show_dso);
10068c430a34SNamhyung Kim 	if (old_str) {
10078c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
10088c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
10098c430a34SNamhyung Kim 			new = NULL;
10108c430a34SNamhyung Kim 	} else {
10118c430a34SNamhyung Kim 		if (value_str) {
10128c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
10138c430a34SNamhyung Kim 				new = NULL;
10148c430a34SNamhyung Kim 		} else {
10158c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
10168c430a34SNamhyung Kim 				new = NULL;
10178c430a34SNamhyung Kim 		}
10188c430a34SNamhyung Kim 	}
10198c430a34SNamhyung Kim 	return new;
10208c430a34SNamhyung Kim }
10218c430a34SNamhyung Kim 
10228c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
10238c430a34SNamhyung Kim 					       struct rb_root *root,
10248c430a34SNamhyung Kim 					       unsigned short row, u64 total,
102559c624e2SNamhyung Kim 					       u64 parent_total,
10268c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
10278c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
10288c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
10298c430a34SNamhyung Kim {
10308c430a34SNamhyung Kim 	struct rb_node *node;
10318c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
10328c430a34SNamhyung Kim 	bool need_percent;
10338c430a34SNamhyung Kim 
10348c430a34SNamhyung Kim 	node = rb_first(root);
103559c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
10368c430a34SNamhyung Kim 
10378c430a34SNamhyung Kim 	while (node) {
10388c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
10398c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
10408c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
10418c430a34SNamhyung Kim 		int first = true;
10428c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
10438c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
10448c430a34SNamhyung Kim 
10458c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
10468c430a34SNamhyung Kim 			arg->row_offset--;
10478c430a34SNamhyung Kim 			goto next;
10488c430a34SNamhyung Kim 		}
10498c430a34SNamhyung Kim 
10508c430a34SNamhyung Kim 		if (need_percent) {
10518c430a34SNamhyung Kim 			char buf[64];
10528c430a34SNamhyung Kim 
10538c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
10548c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
10558c430a34SNamhyung Kim 				value_str = (char *)"<...>";
10568c430a34SNamhyung Kim 				goto do_print;
10578c430a34SNamhyung Kim 			}
10588c430a34SNamhyung Kim 			value_str_alloc = value_str;
10598c430a34SNamhyung Kim 		}
10608c430a34SNamhyung Kim 
10618c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
10628c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
10638c430a34SNamhyung Kim 						chain, value_str, chain_str);
10648c430a34SNamhyung Kim 			if (first) {
10658c430a34SNamhyung Kim 				first = false;
10668c430a34SNamhyung Kim 				first_chain = chain;
10678c430a34SNamhyung Kim 			}
10688c430a34SNamhyung Kim 
10698c430a34SNamhyung Kim 			if (chain_str == NULL) {
10708c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
10718c430a34SNamhyung Kim 				goto do_print;
10728c430a34SNamhyung Kim 			}
10738c430a34SNamhyung Kim 
10748c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
10758c430a34SNamhyung Kim 		}
10768c430a34SNamhyung Kim 
10778c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
10788c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
10798c430a34SNamhyung Kim 						chain, value_str, chain_str);
10808c430a34SNamhyung Kim 			if (first) {
10818c430a34SNamhyung Kim 				first = false;
10828c430a34SNamhyung Kim 				first_chain = chain;
10838c430a34SNamhyung Kim 			}
10848c430a34SNamhyung Kim 
10858c430a34SNamhyung Kim 			if (chain_str == NULL) {
10868c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
10878c430a34SNamhyung Kim 				goto do_print;
10888c430a34SNamhyung Kim 			}
10898c430a34SNamhyung Kim 
10908c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
10918c430a34SNamhyung Kim 		}
10928c430a34SNamhyung Kim 
10938c430a34SNamhyung Kim do_print:
10948c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
10958c430a34SNamhyung Kim 		free(value_str_alloc);
10968c430a34SNamhyung Kim 		free(chain_str_alloc);
10978c430a34SNamhyung Kim 
10988c430a34SNamhyung Kim next:
10998c430a34SNamhyung Kim 		if (is_output_full(browser, row))
11008c430a34SNamhyung Kim 			break;
11018c430a34SNamhyung Kim 		node = next;
11028c430a34SNamhyung Kim 	}
11038c430a34SNamhyung Kim 
11048c430a34SNamhyung Kim 	return row - first_row;
11058c430a34SNamhyung Kim }
11068c430a34SNamhyung Kim 
11070c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1108c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
110939ee533fSNamhyung Kim 					unsigned short row, u64 total,
11105eca104eSNamhyung Kim 					u64 parent_total,
111139ee533fSNamhyung Kim 					print_callchain_entry_fn print,
111239ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
111339ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
1114aca7a94dSNamhyung Kim {
1115aca7a94dSNamhyung Kim 	struct rb_node *node;
1116f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
11174087d11cSNamhyung Kim 	bool need_percent;
11185eca104eSNamhyung Kim 	u64 percent_total = total;
11195eca104eSNamhyung Kim 
11205eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
11215eca104eSNamhyung Kim 		percent_total = parent_total;
1122aca7a94dSNamhyung Kim 
1123c09a7e75SNamhyung Kim 	node = rb_first(root);
112459c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
11254087d11cSNamhyung Kim 
1126aca7a94dSNamhyung Kim 	while (node) {
1127aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1128aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
1129aca7a94dSNamhyung Kim 		struct callchain_list *chain;
1130aca7a94dSNamhyung Kim 		char folded_sign = ' ';
1131aca7a94dSNamhyung Kim 		int first = true;
1132aca7a94dSNamhyung Kim 		int extra_offset = 0;
1133aca7a94dSNamhyung Kim 
1134aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
1135aca7a94dSNamhyung Kim 			bool was_first = first;
1136aca7a94dSNamhyung Kim 
1137aca7a94dSNamhyung Kim 			if (first)
1138aca7a94dSNamhyung Kim 				first = false;
11394087d11cSNamhyung Kim 			else if (need_percent)
1140aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1141aca7a94dSNamhyung Kim 
1142aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1143aca7a94dSNamhyung Kim 
114418bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
11455eca104eSNamhyung Kim 							chain, row, percent_total,
114618bb8381SNamhyung Kim 							was_first && need_percent,
114718bb8381SNamhyung Kim 							offset + extra_offset,
114818bb8381SNamhyung Kim 							print, arg);
1149c09a7e75SNamhyung Kim 
115018bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1151aca7a94dSNamhyung Kim 				goto out;
115218bb8381SNamhyung Kim 
1153aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1154aca7a94dSNamhyung Kim 				break;
1155aca7a94dSNamhyung Kim 		}
1156aca7a94dSNamhyung Kim 
1157aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1158aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1159c09a7e75SNamhyung Kim 
11600c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
11615eca104eSNamhyung Kim 							    new_level, row, total,
11625eca104eSNamhyung Kim 							    child->children_hit,
116339ee533fSNamhyung Kim 							    print, arg, is_output_full);
1164aca7a94dSNamhyung Kim 		}
116539ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1166c09a7e75SNamhyung Kim 			break;
1167aca7a94dSNamhyung Kim 		node = next;
1168aca7a94dSNamhyung Kim 	}
1169aca7a94dSNamhyung Kim out:
1170aca7a94dSNamhyung Kim 	return row - first_row;
1171aca7a94dSNamhyung Kim }
1172aca7a94dSNamhyung Kim 
11730c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
11740c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
11750c841c6cSNamhyung Kim 					unsigned short row,
11760c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
11770c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
11780c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
11790c841c6cSNamhyung Kim {
11800c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
11815eca104eSNamhyung Kim 	u64 parent_total;
11820c841c6cSNamhyung Kim 	int printed;
11830c841c6cSNamhyung Kim 
11840c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
11855eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
11860c841c6cSNamhyung Kim 	else
11875eca104eSNamhyung Kim 		parent_total = entry->stat.period;
11880c841c6cSNamhyung Kim 
11890c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
11900c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
11915eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11925eca104eSNamhyung Kim 						total, parent_total, print, arg,
11935eca104eSNamhyung Kim 						is_output_full);
11940c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
11950c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
11965eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11975eca104eSNamhyung Kim 						total, parent_total, print, arg,
11985eca104eSNamhyung Kim 						is_output_full);
11990c841c6cSNamhyung Kim 	} else {
12000c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
12015eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
12025eca104eSNamhyung Kim 						total, parent_total, print, arg,
12035eca104eSNamhyung Kim 						is_output_full);
12040c841c6cSNamhyung Kim 	}
12050c841c6cSNamhyung Kim 
12060c841c6cSNamhyung Kim 	if (arg->is_current_entry)
12070c841c6cSNamhyung Kim 		browser->he_selection = entry;
12080c841c6cSNamhyung Kim 
12090c841c6cSNamhyung Kim 	return printed;
12100c841c6cSNamhyung Kim }
12110c841c6cSNamhyung Kim 
121289701460SNamhyung Kim struct hpp_arg {
121389701460SNamhyung Kim 	struct ui_browser *b;
121489701460SNamhyung Kim 	char folded_sign;
121589701460SNamhyung Kim 	bool current_entry;
121689701460SNamhyung Kim };
121789701460SNamhyung Kim 
121898ba1609SJiri Olsa int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
12192f6d9009SNamhyung Kim {
12202f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1221d675107cSNamhyung Kim 	int ret, len;
12222f6d9009SNamhyung Kim 	va_list args;
12232f6d9009SNamhyung Kim 	double percent;
12242f6d9009SNamhyung Kim 
12252f6d9009SNamhyung Kim 	va_start(args, fmt);
1226d675107cSNamhyung Kim 	len = va_arg(args, int);
12272f6d9009SNamhyung Kim 	percent = va_arg(args, double);
12282f6d9009SNamhyung Kim 	va_end(args);
12295aed9d24SNamhyung Kim 
123089701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
12315aed9d24SNamhyung Kim 
1232d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1233517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
123489701460SNamhyung Kim 
12355aed9d24SNamhyung Kim 	return ret;
1236f5951d56SNamhyung Kim }
1237f5951d56SNamhyung Kim 
1238fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
12395aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
12405aed9d24SNamhyung Kim {									\
12415aed9d24SNamhyung Kim 	return he->stat._field;						\
12425aed9d24SNamhyung Kim }									\
12435aed9d24SNamhyung Kim 									\
12442c5d4b4aSJiri Olsa static int								\
12455b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
12462c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
12475aed9d24SNamhyung Kim 				struct hist_entry *he)			\
12485aed9d24SNamhyung Kim {									\
12495b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
12502f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
12515aed9d24SNamhyung Kim }
1252f5951d56SNamhyung Kim 
12530434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
12540434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
12550434ddd2SNamhyung Kim {									\
12560434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
12570434ddd2SNamhyung Kim }									\
12580434ddd2SNamhyung Kim 									\
12590434ddd2SNamhyung Kim static int								\
12605b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
12610434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
12620434ddd2SNamhyung Kim 				struct hist_entry *he)			\
12630434ddd2SNamhyung Kim {									\
12640434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1265517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
12665b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1267d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
12685b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1269517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
12700434ddd2SNamhyung Kim 									\
12710434ddd2SNamhyung Kim 		return ret;						\
12720434ddd2SNamhyung Kim 	}								\
12735b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
12745b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
12750434ddd2SNamhyung Kim }
12760434ddd2SNamhyung Kim 
1277fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1278fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1279fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1280fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1281fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
12820434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
12835aed9d24SNamhyung Kim 
12845aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
12850434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1286f5951d56SNamhyung Kim 
1287f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1288f5951d56SNamhyung Kim {
1289f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1290f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1291f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1292f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1293f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1294f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1295f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1296f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1297f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1298f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
12990434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
13000434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
13014968ac8fSAndi Kleen 
13024968ac8fSAndi Kleen 	res_sample_init();
1303f5951d56SNamhyung Kim }
1304f5951d56SNamhyung Kim 
130505e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1306aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1307aca7a94dSNamhyung Kim 				    unsigned short row)
1308aca7a94dSNamhyung Kim {
13091240005eSJiri Olsa 	int printed = 0;
131067d25916SNamhyung Kim 	int width = browser->b.width;
1311aca7a94dSNamhyung Kim 	char folded_sign = ' ';
131205e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1313fabd37b8SArnaldo Carvalho de Melo 	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1314aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
131563a1a3d8SNamhyung Kim 	bool first = true;
13161240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1317aca7a94dSNamhyung Kim 
1318aca7a94dSNamhyung Kim 	if (current_entry) {
131905e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
132005e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1321aca7a94dSNamhyung Kim 	}
1322aca7a94dSNamhyung Kim 
1323fabd37b8SArnaldo Carvalho de Melo 	if (use_callchain) {
1324aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1325aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1326aca7a94dSNamhyung Kim 	}
1327aca7a94dSNamhyung Kim 
1328aca7a94dSNamhyung Kim 	if (row_offset == 0) {
132989701460SNamhyung Kim 		struct hpp_arg arg = {
133089701460SNamhyung Kim 			.b		= &browser->b,
133189701460SNamhyung Kim 			.folded_sign	= folded_sign,
133289701460SNamhyung Kim 			.current_entry	= current_entry,
133389701460SNamhyung Kim 		};
1334c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1335f5951d56SNamhyung Kim 
1336ef9ff601SArnaldo Carvalho de Melo 		ui_browser__gotorc(&browser->b, row, 0);
1337f5951d56SNamhyung Kim 
1338f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
133989fee709SArnaldo Carvalho de Melo 			char s[2048];
134089fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
134189fee709SArnaldo Carvalho de Melo 				.buf	= s,
134289fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
134389fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
134489fee709SArnaldo Carvalho de Melo 			};
134589fee709SArnaldo Carvalho de Melo 
1346361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1347361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1348e67d49a7SNamhyung Kim 				continue;
1349e67d49a7SNamhyung Kim 
1350fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1351fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1352fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1353fb821c9eSNamhyung Kim 			} else {
1354fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1355fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1356fb821c9eSNamhyung Kim 			}
1357fb821c9eSNamhyung Kim 
1358fb821c9eSNamhyung Kim 			if (first) {
1359fabd37b8SArnaldo Carvalho de Melo 				if (use_callchain) {
1360517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1361f5951d56SNamhyung Kim 					width -= 2;
1362f5951d56SNamhyung Kim 				}
136363a1a3d8SNamhyung Kim 				first = false;
1364fb821c9eSNamhyung Kim 			} else {
1365517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1366fb821c9eSNamhyung Kim 				width -= 2;
1367fb821c9eSNamhyung Kim 			}
1368f5951d56SNamhyung Kim 
13691240005eSJiri Olsa 			if (fmt->color) {
137089fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
137189fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
137289fee709SArnaldo Carvalho de Melo 				/*
137389fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
137489fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
137589fee709SArnaldo Carvalho de Melo 				 */
137689fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1377f5951d56SNamhyung Kim 			} else {
137889fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1379517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1380f5951d56SNamhyung Kim 			}
138189fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1382f5951d56SNamhyung Kim 		}
1383aca7a94dSNamhyung Kim 
1384aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
138505e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1386aca7a94dSNamhyung Kim 			width += 1;
1387aca7a94dSNamhyung Kim 
138826270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
138926d8b338SNamhyung Kim 
1390aca7a94dSNamhyung Kim 		++row;
1391aca7a94dSNamhyung Kim 		++printed;
1392aca7a94dSNamhyung Kim 	} else
1393aca7a94dSNamhyung Kim 		--row_offset;
1394aca7a94dSNamhyung Kim 
139562c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
139639ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
139739ee533fSNamhyung Kim 			.row_offset = row_offset,
139839ee533fSNamhyung Kim 			.is_current_entry = current_entry,
139939ee533fSNamhyung Kim 		};
1400c09a7e75SNamhyung Kim 
14010d3eb0b7SJin Yao 		printed += hist_browser__show_callchain(browser,
14020d3eb0b7SJin Yao 				entry, 1, row,
14030d3eb0b7SJin Yao 				hist_browser__show_callchain_entry,
14040d3eb0b7SJin Yao 				&arg,
14054b3a3212SNamhyung Kim 				hist_browser__check_output_full);
1406aca7a94dSNamhyung Kim 	}
1407aca7a94dSNamhyung Kim 
1408aca7a94dSNamhyung Kim 	return printed;
1409aca7a94dSNamhyung Kim }
1410aca7a94dSNamhyung Kim 
1411d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1412d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1413d0506edbSNamhyung Kim 					      unsigned short row,
14142dbbe9f2SNamhyung Kim 					      int level)
1415d0506edbSNamhyung Kim {
1416d0506edbSNamhyung Kim 	int printed = 0;
1417d0506edbSNamhyung Kim 	int width = browser->b.width;
1418d0506edbSNamhyung Kim 	char folded_sign = ' ';
1419d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1420d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1421d0506edbSNamhyung Kim 	bool first = true;
1422d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1423a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1424d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1425d0506edbSNamhyung Kim 		.b		= &browser->b,
1426d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1427d0506edbSNamhyung Kim 	};
1428d0506edbSNamhyung Kim 	int column = 0;
14292dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1430d0506edbSNamhyung Kim 
1431d0506edbSNamhyung Kim 	if (current_entry) {
1432d0506edbSNamhyung Kim 		browser->he_selection = entry;
1433d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1434d0506edbSNamhyung Kim 	}
1435d0506edbSNamhyung Kim 
1436d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1437d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1438d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1439d0506edbSNamhyung Kim 
1440d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1441d0506edbSNamhyung Kim 		row_offset--;
1442d0506edbSNamhyung Kim 		goto show_callchain;
1443d0506edbSNamhyung Kim 	}
1444d0506edbSNamhyung Kim 
1445ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
1446d0506edbSNamhyung Kim 
1447d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1448d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1449d0506edbSNamhyung Kim 	else
1450d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1451d0506edbSNamhyung Kim 
1452d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1453d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1454d0506edbSNamhyung Kim 
1455a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1456a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1457a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1458a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1459d0506edbSNamhyung Kim 		char s[2048];
1460d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1461d0506edbSNamhyung Kim 			.buf		= s,
1462d0506edbSNamhyung Kim 			.size		= sizeof(s),
1463d0506edbSNamhyung Kim 			.ptr		= &arg,
1464d0506edbSNamhyung Kim 		};
1465d0506edbSNamhyung Kim 
1466d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1467d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1468d0506edbSNamhyung Kim 			continue;
1469d0506edbSNamhyung Kim 
1470d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1471d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1472d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1473d0506edbSNamhyung Kim 		} else {
1474d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1475d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1476d0506edbSNamhyung Kim 		}
1477d0506edbSNamhyung Kim 
1478d0506edbSNamhyung Kim 		if (first) {
1479d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c ", folded_sign);
14803d9f4683SNamhyung Kim 			width -= 2;
1481d0506edbSNamhyung Kim 			first = false;
1482d0506edbSNamhyung Kim 		} else {
1483d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1484d0506edbSNamhyung Kim 			width -= 2;
1485d0506edbSNamhyung Kim 		}
1486d0506edbSNamhyung Kim 
1487d0506edbSNamhyung Kim 		if (fmt->color) {
1488d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1489d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1490d0506edbSNamhyung Kim 			/*
1491d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1492d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1493d0506edbSNamhyung Kim 			 */
1494d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1495d0506edbSNamhyung Kim 		} else {
1496d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1497d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1498d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1499d0506edbSNamhyung Kim 		}
1500d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1501d0506edbSNamhyung Kim 	}
1502d0506edbSNamhyung Kim 
1503b9bf911eSNamhyung Kim 	if (!first) {
1504d0506edbSNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1505d0506edbSNamhyung Kim 		width -= hierarchy_indent;
1506b9bf911eSNamhyung Kim 	}
1507d0506edbSNamhyung Kim 
1508d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1509d0506edbSNamhyung Kim 		char s[2048];
1510d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1511d0506edbSNamhyung Kim 			.buf		= s,
1512d0506edbSNamhyung Kim 			.size		= sizeof(s),
1513d0506edbSNamhyung Kim 			.ptr		= &arg,
1514d0506edbSNamhyung Kim 		};
1515d0506edbSNamhyung Kim 
1516d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1517d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1518d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1519d0506edbSNamhyung Kim 		} else {
1520d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1521d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1522d0506edbSNamhyung Kim 		}
1523d0506edbSNamhyung Kim 
15241b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1525131d51ebSNamhyung Kim 			if (first) {
1526131d51ebSNamhyung Kim 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1527131d51ebSNamhyung Kim 				first = false;
1528131d51ebSNamhyung Kim 			} else {
1529d0506edbSNamhyung Kim 				ui_browser__write_nstring(&browser->b, "", 2);
1530131d51ebSNamhyung Kim 			}
1531131d51ebSNamhyung Kim 
1532d0506edbSNamhyung Kim 			width -= 2;
1533d0506edbSNamhyung Kim 
1534d0506edbSNamhyung Kim 			/*
1535d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1536d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1537d0506edbSNamhyung Kim 			 * hierarchy mode.
1538d0506edbSNamhyung Kim 			 */
1539d0506edbSNamhyung Kim 			if (fmt->color) {
1540d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1541d0506edbSNamhyung Kim 			} else {
1542cb1fab91SNamhyung Kim 				int i = 0;
1543cb1fab91SNamhyung Kim 
1544d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
154532858480SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1546cb1fab91SNamhyung Kim 
1547cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1548cb1fab91SNamhyung Kim 					width++;
1549d0506edbSNamhyung Kim 			}
1550d0506edbSNamhyung Kim 		}
15511b2dbbf4SNamhyung Kim 	}
1552d0506edbSNamhyung Kim 
1553d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1554d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1555d0506edbSNamhyung Kim 		width += 1;
1556d0506edbSNamhyung Kim 
1557d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1558d0506edbSNamhyung Kim 
1559d0506edbSNamhyung Kim 	++row;
1560d0506edbSNamhyung Kim 	++printed;
1561d0506edbSNamhyung Kim 
1562d0506edbSNamhyung Kim show_callchain:
1563d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1564d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1565d0506edbSNamhyung Kim 			.row_offset = row_offset,
1566d0506edbSNamhyung Kim 		};
1567d0506edbSNamhyung Kim 
1568d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1569d0506edbSNamhyung Kim 					level + 1, row,
1570d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1571d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1572d0506edbSNamhyung Kim 	}
1573d0506edbSNamhyung Kim 
1574d0506edbSNamhyung Kim 	return printed;
1575d0506edbSNamhyung Kim }
1576d0506edbSNamhyung Kim 
157779dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
15782dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
157979dded87SNamhyung Kim {
158079dded87SNamhyung Kim 	int width = browser->b.width;
158179dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
158279dded87SNamhyung Kim 	bool first = true;
158379dded87SNamhyung Kim 	int column = 0;
158479dded87SNamhyung Kim 	int ret;
158579dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1586a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
15872dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
158879dded87SNamhyung Kim 
158979dded87SNamhyung Kim 	if (current_entry) {
159079dded87SNamhyung Kim 		browser->he_selection = NULL;
159179dded87SNamhyung Kim 		browser->selection = NULL;
159279dded87SNamhyung Kim 	}
159379dded87SNamhyung Kim 
1594ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
159579dded87SNamhyung Kim 
159679dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
159779dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
159879dded87SNamhyung Kim 	else
159979dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
160079dded87SNamhyung Kim 
160179dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
160279dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
160379dded87SNamhyung Kim 
1604a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1605a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1606a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1607a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
160879dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
160979dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
161079dded87SNamhyung Kim 			continue;
161179dded87SNamhyung Kim 
1612da1b0407SJiri Olsa 		ret = fmt->width(fmt, NULL, browser->hists);
161379dded87SNamhyung Kim 
161479dded87SNamhyung Kim 		if (first) {
161579dded87SNamhyung Kim 			/* for folded sign */
161679dded87SNamhyung Kim 			first = false;
161779dded87SNamhyung Kim 			ret++;
161879dded87SNamhyung Kim 		} else {
161979dded87SNamhyung Kim 			/* space between columns */
162079dded87SNamhyung Kim 			ret += 2;
162179dded87SNamhyung Kim 		}
162279dded87SNamhyung Kim 
162379dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
162479dded87SNamhyung Kim 		width -= ret;
162579dded87SNamhyung Kim 	}
162679dded87SNamhyung Kim 
16272dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
16282dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
162979dded87SNamhyung Kim 
163079dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
163179dded87SNamhyung Kim 		char buf[32];
163279dded87SNamhyung Kim 
163379dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
163479dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
163579dded87SNamhyung Kim 		width -= ret + 2;
163679dded87SNamhyung Kim 	}
163779dded87SNamhyung Kim 
163879dded87SNamhyung Kim 	/* The scroll bar isn't being used */
163979dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
164079dded87SNamhyung Kim 		width += 1;
164179dded87SNamhyung Kim 
164279dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
164379dded87SNamhyung Kim 	return 1;
164479dded87SNamhyung Kim }
164579dded87SNamhyung Kim 
164681a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
164781a888feSJiri Olsa {
164881a888feSJiri Olsa 	advance_hpp(hpp, inc);
164981a888feSJiri Olsa 	return hpp->size <= 0;
165081a888feSJiri Olsa }
165181a888feSJiri Olsa 
165269705b35SJiri Olsa static int
165369705b35SJiri Olsa hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
165469705b35SJiri Olsa 				 size_t size, int line)
165581a888feSJiri Olsa {
1656c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
165781a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
165881a888feSJiri Olsa 		.buf    = buf,
165981a888feSJiri Olsa 		.size   = size,
166081a888feSJiri Olsa 	};
166181a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
166281a888feSJiri Olsa 	size_t ret = 0;
1663c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
166429659ab4SJiri Olsa 	int span = 0;
166581a888feSJiri Olsa 
1666fabd37b8SArnaldo Carvalho de Melo 	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
166781a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
166881a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
166981a888feSJiri Olsa 			return ret;
167081a888feSJiri Olsa 	}
167181a888feSJiri Olsa 
1672f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1673361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
167481a888feSJiri Olsa 			continue;
167581a888feSJiri Olsa 
167629659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
167781a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
167881a888feSJiri Olsa 			break;
167981a888feSJiri Olsa 
168029659ab4SJiri Olsa 		if (span)
168129659ab4SJiri Olsa 			continue;
168229659ab4SJiri Olsa 
168381a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
168481a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
168581a888feSJiri Olsa 			break;
168681a888feSJiri Olsa 	}
168781a888feSJiri Olsa 
168881a888feSJiri Olsa 	return ret;
168981a888feSJiri Olsa }
169081a888feSJiri Olsa 
1691d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1692d8b92400SNamhyung Kim {
1693d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1694d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1695d8b92400SNamhyung Kim 		.buf    = buf,
1696d8b92400SNamhyung Kim 		.size   = size,
1697d8b92400SNamhyung Kim 	};
1698d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1699a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1700d8b92400SNamhyung Kim 	size_t ret = 0;
1701d8b92400SNamhyung Kim 	int column = 0;
17022dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1703a61a22f6SNamhyung Kim 	bool first_node, first_col;
1704d8b92400SNamhyung Kim 
1705d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, "  ");
1706d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1707d8b92400SNamhyung Kim 		return ret;
1708d8b92400SNamhyung Kim 
1709b9bf911eSNamhyung Kim 	first_node = true;
1710a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1711a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1712a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1713a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1714d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1715d8b92400SNamhyung Kim 			continue;
1716d8b92400SNamhyung Kim 
171729659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1718d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1719d8b92400SNamhyung Kim 			break;
1720d8b92400SNamhyung Kim 
1721d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1722d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1723d8b92400SNamhyung Kim 			break;
1724b9bf911eSNamhyung Kim 
1725b9bf911eSNamhyung Kim 		first_node = false;
1726d8b92400SNamhyung Kim 	}
1727d8b92400SNamhyung Kim 
1728b9bf911eSNamhyung Kim 	if (!first_node) {
1729d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
17302dbbe9f2SNamhyung Kim 				indent * HIERARCHY_INDENT, "");
1731d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1732d8b92400SNamhyung Kim 			return ret;
1733b9bf911eSNamhyung Kim 	}
1734d8b92400SNamhyung Kim 
1735a61a22f6SNamhyung Kim 	first_node = true;
1736a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1737a61a22f6SNamhyung Kim 		if (!first_node) {
1738d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1739d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1740d8b92400SNamhyung Kim 				break;
1741d8b92400SNamhyung Kim 		}
1742a61a22f6SNamhyung Kim 		first_node = false;
1743a61a22f6SNamhyung Kim 
1744a61a22f6SNamhyung Kim 		first_col = true;
1745a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1746a61a22f6SNamhyung Kim 			char *start;
1747a61a22f6SNamhyung Kim 
1748a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1749a61a22f6SNamhyung Kim 				continue;
1750a61a22f6SNamhyung Kim 
1751a61a22f6SNamhyung Kim 			if (!first_col) {
1752a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1753a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1754a61a22f6SNamhyung Kim 					break;
1755a61a22f6SNamhyung Kim 			}
1756a61a22f6SNamhyung Kim 			first_col = false;
1757d8b92400SNamhyung Kim 
175829659ab4SJiri Olsa 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1759d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1760d8b92400SNamhyung Kim 
17613ca43b60SArnaldo Carvalho de Melo 			start = strim(dummy_hpp.buf);
1762cb1fab91SNamhyung Kim 			ret = strlen(start);
1763cb1fab91SNamhyung Kim 
1764cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1765cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1766cb1fab91SNamhyung Kim 
1767d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1768d8b92400SNamhyung Kim 				break;
1769d8b92400SNamhyung Kim 		}
1770a61a22f6SNamhyung Kim 	}
1771d8b92400SNamhyung Kim 
1772d8b92400SNamhyung Kim 	return ret;
1773d8b92400SNamhyung Kim }
1774d8b92400SNamhyung Kim 
177501b4770dSJiri Olsa static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1776025bf7eaSArnaldo Carvalho de Melo {
177781a888feSJiri Olsa 	char headers[1024];
177881a888feSJiri Olsa 
1779d8b92400SNamhyung Kim 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1780d8b92400SNamhyung Kim 						   sizeof(headers));
178101b4770dSJiri Olsa 
1782025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1783025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
178426270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1785025bf7eaSArnaldo Carvalho de Melo }
1786025bf7eaSArnaldo Carvalho de Melo 
178701b4770dSJiri Olsa static void hists_browser__headers(struct hist_browser *browser)
178801b4770dSJiri Olsa {
178969705b35SJiri Olsa 	struct hists *hists = browser->hists;
179069705b35SJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
179169705b35SJiri Olsa 
179269705b35SJiri Olsa 	int line;
179369705b35SJiri Olsa 
179469705b35SJiri Olsa 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
179501b4770dSJiri Olsa 		char headers[1024];
179601b4770dSJiri Olsa 
179701b4770dSJiri Olsa 		hists_browser__scnprintf_headers(browser, headers,
179869705b35SJiri Olsa 						 sizeof(headers), line);
179901b4770dSJiri Olsa 
1800ef9ff601SArnaldo Carvalho de Melo 		ui_browser__gotorc_title(&browser->b, line, 0);
180101b4770dSJiri Olsa 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
180201b4770dSJiri Olsa 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
180301b4770dSJiri Olsa 	}
180469705b35SJiri Olsa }
180501b4770dSJiri Olsa 
180601b4770dSJiri Olsa static void hist_browser__show_headers(struct hist_browser *browser)
180701b4770dSJiri Olsa {
180801b4770dSJiri Olsa 	if (symbol_conf.report_hierarchy)
180901b4770dSJiri Olsa 		hists_browser__hierarchy_headers(browser);
181001b4770dSJiri Olsa 	else
181101b4770dSJiri Olsa 		hists_browser__headers(browser);
181201b4770dSJiri Olsa }
181301b4770dSJiri Olsa 
1814aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1815aca7a94dSNamhyung Kim {
1816aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1817aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1818aca7a94dSNamhyung Kim 
1819aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
18202eb3d689SDavidlohr Bueso 		browser->top = rb_first_cached(&hb->hists->entries);
1821aca7a94dSNamhyung Kim 	}
1822aca7a94dSNamhyung Kim }
1823aca7a94dSNamhyung Kim 
182405e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1825aca7a94dSNamhyung Kim {
1826aca7a94dSNamhyung Kim 	unsigned row = 0;
1827aca7a94dSNamhyung Kim 	struct rb_node *nd;
182805e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1829aca7a94dSNamhyung Kim 
183094e87a8bSArnaldo Carvalho de Melo 	if (hb->show_headers)
1831025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1832025bf7eaSArnaldo Carvalho de Melo 
183305e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1834979d2cacSWang Nan 	hb->he_selection = NULL;
1835979d2cacSWang Nan 	hb->selection = NULL;
1836aca7a94dSNamhyung Kim 
1837d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1838aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
183914135663SNamhyung Kim 		float percent;
1840aca7a94dSNamhyung Kim 
1841d0506edbSNamhyung Kim 		if (h->filtered) {
1842d0506edbSNamhyung Kim 			/* let it move to sibling */
1843d0506edbSNamhyung Kim 			h->unfolded = false;
1844aca7a94dSNamhyung Kim 			continue;
1845d0506edbSNamhyung Kim 		}
1846aca7a94dSNamhyung Kim 
18477fa46cbfSJin Yao 		if (symbol_conf.report_individual_block)
18487fa46cbfSJin Yao 			percent = block_info__total_cycles_percent(h);
18497fa46cbfSJin Yao 		else
185014135663SNamhyung Kim 			percent = hist_entry__get_percent_limit(h);
18517fa46cbfSJin Yao 
1852064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1853064f1981SNamhyung Kim 			continue;
1854064f1981SNamhyung Kim 
1855d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1856d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
18572dbbe9f2SNamhyung Kim 								  h->depth);
185879dded87SNamhyung Kim 			if (row == browser->rows)
185979dded87SNamhyung Kim 				break;
186079dded87SNamhyung Kim 
186179dded87SNamhyung Kim 			if (h->has_no_entry) {
1862a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
186379dded87SNamhyung Kim 				row++;
186479dded87SNamhyung Kim 			}
1865d0506edbSNamhyung Kim 		} else {
1866aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1867d0506edbSNamhyung Kim 		}
1868d0506edbSNamhyung Kim 
186962c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1870aca7a94dSNamhyung Kim 			break;
1871aca7a94dSNamhyung Kim 	}
1872aca7a94dSNamhyung Kim 
187394e87a8bSArnaldo Carvalho de Melo 	return row;
1874aca7a94dSNamhyung Kim }
1875aca7a94dSNamhyung Kim 
1876064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1877064f1981SNamhyung Kim 					     float min_pcnt)
1878aca7a94dSNamhyung Kim {
1879aca7a94dSNamhyung Kim 	while (nd != NULL) {
1880aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
188114135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1882064f1981SNamhyung Kim 
1883c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1884aca7a94dSNamhyung Kim 			return nd;
1885aca7a94dSNamhyung Kim 
1886d0506edbSNamhyung Kim 		/*
1887d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1888d0506edbSNamhyung Kim 		 * So move to sibling node.
1889d0506edbSNamhyung Kim 		 */
1890d0506edbSNamhyung Kim 		if (rb_next(nd))
1891aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1892d0506edbSNamhyung Kim 		else
1893d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1894aca7a94dSNamhyung Kim 	}
1895aca7a94dSNamhyung Kim 
1896aca7a94dSNamhyung Kim 	return NULL;
1897aca7a94dSNamhyung Kim }
1898aca7a94dSNamhyung Kim 
1899064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1900064f1981SNamhyung Kim 						  float min_pcnt)
1901aca7a94dSNamhyung Kim {
1902aca7a94dSNamhyung Kim 	while (nd != NULL) {
1903aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
190414135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1905064f1981SNamhyung Kim 
1906064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1907aca7a94dSNamhyung Kim 			return nd;
1908aca7a94dSNamhyung Kim 
1909d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1910aca7a94dSNamhyung Kim 	}
1911aca7a94dSNamhyung Kim 
1912aca7a94dSNamhyung Kim 	return NULL;
1913aca7a94dSNamhyung Kim }
1914aca7a94dSNamhyung Kim 
191505e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1916aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1917aca7a94dSNamhyung Kim {
1918aca7a94dSNamhyung Kim 	struct hist_entry *h;
1919aca7a94dSNamhyung Kim 	struct rb_node *nd;
1920aca7a94dSNamhyung Kim 	bool first = true;
1921064f1981SNamhyung Kim 	struct hist_browser *hb;
1922064f1981SNamhyung Kim 
1923064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1924aca7a94dSNamhyung Kim 
192505e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1926aca7a94dSNamhyung Kim 		return;
1927aca7a94dSNamhyung Kim 
192805e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1929aca7a94dSNamhyung Kim 
1930aca7a94dSNamhyung Kim 	switch (whence) {
1931aca7a94dSNamhyung Kim 	case SEEK_SET:
1932064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
193314135663SNamhyung Kim 					   hb->min_pcnt);
1934aca7a94dSNamhyung Kim 		break;
1935aca7a94dSNamhyung Kim 	case SEEK_CUR:
193605e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1937aca7a94dSNamhyung Kim 		goto do_offset;
1938aca7a94dSNamhyung Kim 	case SEEK_END:
1939d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1940d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1941aca7a94dSNamhyung Kim 		first = false;
1942aca7a94dSNamhyung Kim 		break;
1943aca7a94dSNamhyung Kim 	default:
1944aca7a94dSNamhyung Kim 		return;
1945aca7a94dSNamhyung Kim 	}
1946aca7a94dSNamhyung Kim 
1947aca7a94dSNamhyung Kim 	/*
1948aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1949aca7a94dSNamhyung Kim 	 * row_offset:
1950aca7a94dSNamhyung Kim 	 */
195105e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1952aca7a94dSNamhyung Kim 	h->row_offset = 0;
1953aca7a94dSNamhyung Kim 
1954aca7a94dSNamhyung Kim 	/*
1955aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1956aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1957aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1958aca7a94dSNamhyung Kim 	 *
1959aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1960aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1961aca7a94dSNamhyung Kim 	 *
1962aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1963aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1964aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1965aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1966aca7a94dSNamhyung Kim 	 */
1967aca7a94dSNamhyung Kim do_offset:
1968837eeb75SWang Nan 	if (!nd)
1969837eeb75SWang Nan 		return;
1970837eeb75SWang Nan 
1971aca7a94dSNamhyung Kim 	if (offset > 0) {
1972aca7a94dSNamhyung Kim 		do {
1973aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1974d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1975aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1976aca7a94dSNamhyung Kim 				if (offset > remaining) {
1977aca7a94dSNamhyung Kim 					offset -= remaining;
1978aca7a94dSNamhyung Kim 					h->row_offset = 0;
1979aca7a94dSNamhyung Kim 				} else {
1980aca7a94dSNamhyung Kim 					h->row_offset += offset;
1981aca7a94dSNamhyung Kim 					offset = 0;
198205e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1983aca7a94dSNamhyung Kim 					break;
1984aca7a94dSNamhyung Kim 				}
1985aca7a94dSNamhyung Kim 			}
1986d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1987d0506edbSNamhyung Kim 						   hb->min_pcnt);
1988aca7a94dSNamhyung Kim 			if (nd == NULL)
1989aca7a94dSNamhyung Kim 				break;
1990aca7a94dSNamhyung Kim 			--offset;
199105e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1992aca7a94dSNamhyung Kim 		} while (offset != 0);
1993aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1994aca7a94dSNamhyung Kim 		while (1) {
1995aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1996d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1997aca7a94dSNamhyung Kim 				if (first) {
1998aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1999aca7a94dSNamhyung Kim 						offset += h->row_offset;
2000aca7a94dSNamhyung Kim 						h->row_offset = 0;
2001aca7a94dSNamhyung Kim 					} else {
2002aca7a94dSNamhyung Kim 						h->row_offset += offset;
2003aca7a94dSNamhyung Kim 						offset = 0;
200405e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
2005aca7a94dSNamhyung Kim 						break;
2006aca7a94dSNamhyung Kim 					}
2007aca7a94dSNamhyung Kim 				} else {
2008aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
2009aca7a94dSNamhyung Kim 						offset += h->nr_rows;
2010aca7a94dSNamhyung Kim 						h->row_offset = 0;
2011aca7a94dSNamhyung Kim 					} else {
2012aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
2013aca7a94dSNamhyung Kim 						offset = 0;
201405e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
2015aca7a94dSNamhyung Kim 						break;
2016aca7a94dSNamhyung Kim 					}
2017aca7a94dSNamhyung Kim 				}
2018aca7a94dSNamhyung Kim 			}
2019aca7a94dSNamhyung Kim 
2020d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2021064f1981SNamhyung Kim 							hb->min_pcnt);
2022aca7a94dSNamhyung Kim 			if (nd == NULL)
2023aca7a94dSNamhyung Kim 				break;
2024aca7a94dSNamhyung Kim 			++offset;
202505e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
2026aca7a94dSNamhyung Kim 			if (offset == 0) {
2027aca7a94dSNamhyung Kim 				/*
2028aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
2029aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
2030aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
2031aca7a94dSNamhyung Kim 				 */
2032aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
2033d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
2034aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
2035aca7a94dSNamhyung Kim 				break;
2036aca7a94dSNamhyung Kim 			}
2037aca7a94dSNamhyung Kim 			first = false;
2038aca7a94dSNamhyung Kim 		}
2039aca7a94dSNamhyung Kim 	} else {
204005e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
2041aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
2042aca7a94dSNamhyung Kim 		h->row_offset = 0;
2043aca7a94dSNamhyung Kim 	}
2044aca7a94dSNamhyung Kim }
2045aca7a94dSNamhyung Kim 
2046aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2047d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
2048d0506edbSNamhyung Kim 					   int level)
2049aff3f3f6SArnaldo Carvalho de Melo {
205039ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
205139ee533fSNamhyung Kim 		.fp = fp,
205239ee533fSNamhyung Kim 	};
2053aff3f3f6SArnaldo Carvalho de Melo 
2054d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
205539ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
205639ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
205739ee533fSNamhyung Kim 	return arg.printed;
2058aff3f3f6SArnaldo Carvalho de Melo }
2059aff3f3f6SArnaldo Carvalho de Melo 
2060aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
2061aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
2062aff3f3f6SArnaldo Carvalho de Melo {
2063aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
2064aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2065aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
206626d8b338SNamhyung Kim 	struct perf_hpp hpp = {
206726d8b338SNamhyung Kim 		.buf = s,
206826d8b338SNamhyung Kim 		.size = sizeof(s),
206926d8b338SNamhyung Kim 	};
207026d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
207126d8b338SNamhyung Kim 	bool first = true;
207226d8b338SNamhyung Kim 	int ret;
2073aff3f3f6SArnaldo Carvalho de Melo 
2074fabd37b8SArnaldo Carvalho de Melo 	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2075aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
2076aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
20771b6b678eSArnaldo Carvalho de Melo 	}
2078aff3f3f6SArnaldo Carvalho de Melo 
2079f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
2080361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
2081e67d49a7SNamhyung Kim 			continue;
2082e67d49a7SNamhyung Kim 
208326d8b338SNamhyung Kim 		if (!first) {
208426d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
208526d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
208626d8b338SNamhyung Kim 		} else
208726d8b338SNamhyung Kim 			first = false;
2088aff3f3f6SArnaldo Carvalho de Melo 
208926d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
209089fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
209126d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
209226d8b338SNamhyung Kim 	}
209389fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
2094aff3f3f6SArnaldo Carvalho de Melo 
2095aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
2096d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2097d0506edbSNamhyung Kim 
2098d0506edbSNamhyung Kim 	return printed;
2099d0506edbSNamhyung Kim }
2100d0506edbSNamhyung Kim 
2101d0506edbSNamhyung Kim 
2102d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2103d0506edbSNamhyung Kim 						 struct hist_entry *he,
2104325a6283SNamhyung Kim 						 FILE *fp, int level)
2105d0506edbSNamhyung Kim {
2106d0506edbSNamhyung Kim 	char s[8192];
2107d0506edbSNamhyung Kim 	int printed = 0;
2108d0506edbSNamhyung Kim 	char folded_sign = ' ';
2109d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
2110d0506edbSNamhyung Kim 		.buf = s,
2111d0506edbSNamhyung Kim 		.size = sizeof(s),
2112d0506edbSNamhyung Kim 	};
2113d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
2114325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
2115d0506edbSNamhyung Kim 	bool first = true;
2116d0506edbSNamhyung Kim 	int ret;
2117325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2118d0506edbSNamhyung Kim 
2119d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2120d0506edbSNamhyung Kim 
2121d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
2122d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
2123d0506edbSNamhyung Kim 
2124325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
2125325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2126325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
2127325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2128d0506edbSNamhyung Kim 		if (!first) {
2129d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2130d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
2131d0506edbSNamhyung Kim 		} else
2132d0506edbSNamhyung Kim 			first = false;
2133d0506edbSNamhyung Kim 
2134d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2135d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
2136d0506edbSNamhyung Kim 	}
2137d0506edbSNamhyung Kim 
2138d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2139d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
2140d0506edbSNamhyung Kim 
21411b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
21421b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
21431b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
21441b2dbbf4SNamhyung Kim 
2145d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2146d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
21471b2dbbf4SNamhyung Kim 	}
2148d0506edbSNamhyung Kim 
214913c230abSArnaldo Carvalho de Melo 	strim(s);
215013c230abSArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
2151d0506edbSNamhyung Kim 
2152d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
2153d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2154d0506edbSNamhyung Kim 							   he->depth + 1);
2155d0506edbSNamhyung Kim 	}
2156aff3f3f6SArnaldo Carvalho de Melo 
2157aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2158aff3f3f6SArnaldo Carvalho de Melo }
2159aff3f3f6SArnaldo Carvalho de Melo 
2160aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2161aff3f3f6SArnaldo Carvalho de Melo {
2162064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2163064f1981SNamhyung Kim 						   browser->min_pcnt);
2164aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2165aff3f3f6SArnaldo Carvalho de Melo 
2166aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
2167aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2168aff3f3f6SArnaldo Carvalho de Melo 
2169d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
2170d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2171d0506edbSNamhyung Kim 									 h, fp,
2172325a6283SNamhyung Kim 									 h->depth);
2173d0506edbSNamhyung Kim 		} else {
2174aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
2175d0506edbSNamhyung Kim 		}
2176d0506edbSNamhyung Kim 
2177d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2178d0506edbSNamhyung Kim 					   browser->min_pcnt);
2179aff3f3f6SArnaldo Carvalho de Melo 	}
2180aff3f3f6SArnaldo Carvalho de Melo 
2181aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2182aff3f3f6SArnaldo Carvalho de Melo }
2183aff3f3f6SArnaldo Carvalho de Melo 
2184aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2185aff3f3f6SArnaldo Carvalho de Melo {
2186aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2187aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2188aff3f3f6SArnaldo Carvalho de Melo 
2189aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2190aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2191aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2192aff3f3f6SArnaldo Carvalho de Melo 			break;
2193aff3f3f6SArnaldo Carvalho de Melo 		/*
2194aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2195aff3f3f6SArnaldo Carvalho de Melo  		 */
2196aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2197aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2198aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2199aff3f3f6SArnaldo Carvalho de Melo 		}
2200aff3f3f6SArnaldo Carvalho de Melo 	}
2201aff3f3f6SArnaldo Carvalho de Melo 
2202aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2203aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2204aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
2205c8b5f2c9SArnaldo Carvalho de Melo 		const char *err = str_error_r(errno, bf, sizeof(bf));
22064cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2207aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2208aff3f3f6SArnaldo Carvalho de Melo 	}
2209aff3f3f6SArnaldo Carvalho de Melo 
2210aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2211aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2212aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2213aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2214aff3f3f6SArnaldo Carvalho de Melo 
2215aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2216aff3f3f6SArnaldo Carvalho de Melo }
2217aff3f3f6SArnaldo Carvalho de Melo 
2218fcd86426SJiri Olsa void hist_browser__init(struct hist_browser *browser,
2219fcd86426SJiri Olsa 			struct hists *hists)
2220aca7a94dSNamhyung Kim {
2221b1c7a8f7SJiri Olsa 	struct perf_hpp_fmt *fmt;
2222b1c7a8f7SJiri Olsa 
222305e8b080SArnaldo Carvalho de Melo 	browser->hists			= hists;
222405e8b080SArnaldo Carvalho de Melo 	browser->b.refresh		= hist_browser__refresh;
2225357cfff1SArnaldo Carvalho de Melo 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
222605e8b080SArnaldo Carvalho de Melo 	browser->b.seek			= ui_browser__hists_seek;
222705e8b080SArnaldo Carvalho de Melo 	browser->b.use_navkeypressed	= true;
2228c8302367SJiri Olsa 	browser->show_headers		= symbol_conf.show_hist_headers;
2229ef9ff601SArnaldo Carvalho de Melo 	hist_browser__set_title_space(browser);
2230b1c7a8f7SJiri Olsa 
22318a06b0beSNamhyung Kim 	if (symbol_conf.report_hierarchy) {
22328a06b0beSNamhyung Kim 		struct perf_hpp_list_node *fmt_node;
22338a06b0beSNamhyung Kim 
22348a06b0beSNamhyung Kim 		/* count overhead columns (in the first node) */
22358a06b0beSNamhyung Kim 		fmt_node = list_first_entry(&hists->hpp_formats,
22368a06b0beSNamhyung Kim 					    struct perf_hpp_list_node, list);
22378a06b0beSNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
22388a06b0beSNamhyung Kim 			++browser->b.columns;
22398a06b0beSNamhyung Kim 
22408a06b0beSNamhyung Kim 		/* add a single column for whole hierarchy sort keys*/
22418a06b0beSNamhyung Kim 		++browser->b.columns;
22428a06b0beSNamhyung Kim 	} else {
2243e3b60bc9SNamhyung Kim 		hists__for_each_format(hists, fmt)
2244b1c7a8f7SJiri Olsa 			++browser->b.columns;
22458a06b0beSNamhyung Kim 	}
2246e3b60bc9SNamhyung Kim 
2247e3b60bc9SNamhyung Kim 	hists__reset_column_width(hists);
2248aca7a94dSNamhyung Kim }
2249aca7a94dSNamhyung Kim 
2250fcd86426SJiri Olsa struct hist_browser *hist_browser__new(struct hists *hists)
2251fcd86426SJiri Olsa {
2252fcd86426SJiri Olsa 	struct hist_browser *browser = zalloc(sizeof(*browser));
2253fcd86426SJiri Olsa 
2254fcd86426SJiri Olsa 	if (browser)
2255fcd86426SJiri Olsa 		hist_browser__init(browser, hists);
2256fcd86426SJiri Olsa 
225705e8b080SArnaldo Carvalho de Melo 	return browser;
2258aca7a94dSNamhyung Kim }
2259aca7a94dSNamhyung Kim 
2260a6ec894dSJiri Olsa static struct hist_browser *
226132dcd021SJiri Olsa perf_evsel_browser__new(struct evsel *evsel,
2262a6ec894dSJiri Olsa 			struct hist_browser_timer *hbt,
2263cd0cccbaSArnaldo Carvalho de Melo 			struct perf_env *env,
2264cd0cccbaSArnaldo Carvalho de Melo 			struct annotation_options *annotation_opts)
2265a6ec894dSJiri Olsa {
2266a6ec894dSJiri Olsa 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2267a6ec894dSJiri Olsa 
2268a6ec894dSJiri Olsa 	if (browser) {
2269a6ec894dSJiri Olsa 		browser->hbt   = hbt;
2270a6ec894dSJiri Olsa 		browser->env   = env;
2271f016d24aSArnaldo Carvalho de Melo 		browser->title = hists_browser__scnprintf_title;
2272cd0cccbaSArnaldo Carvalho de Melo 		browser->annotation_opts = annotation_opts;
2273a6ec894dSJiri Olsa 	}
2274a6ec894dSJiri Olsa 	return browser;
2275a6ec894dSJiri Olsa }
2276a6ec894dSJiri Olsa 
2277dabd2012SJiri Olsa void hist_browser__delete(struct hist_browser *browser)
2278aca7a94dSNamhyung Kim {
227905e8b080SArnaldo Carvalho de Melo 	free(browser);
2280aca7a94dSNamhyung Kim }
2281aca7a94dSNamhyung Kim 
228205e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2283aca7a94dSNamhyung Kim {
228405e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2285aca7a94dSNamhyung Kim }
2286aca7a94dSNamhyung Kim 
228705e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2288aca7a94dSNamhyung Kim {
228905e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2290aca7a94dSNamhyung Kim }
2291aca7a94dSNamhyung Kim 
2292d61cbb85SWei Li static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2293d61cbb85SWei Li {
2294d61cbb85SWei Li 	return browser->he_selection ? browser->he_selection->res_samples : NULL;
2295d61cbb85SWei Li }
2296d61cbb85SWei Li 
22971e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
22981e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
22991e378ebdSTaeung Song {
23001e378ebdSTaeung Song 	return timer == NULL;
23011e378ebdSTaeung Song }
23021e378ebdSTaeung Song 
2303967a464aSArnaldo Carvalho de Melo static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2304967a464aSArnaldo Carvalho de Melo {
2305967a464aSArnaldo Carvalho de Melo 	struct hist_browser_timer *hbt = browser->hbt;
2306967a464aSArnaldo Carvalho de Melo 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2307967a464aSArnaldo Carvalho de Melo 
23081e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
23091e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
23101e378ebdSTaeung Song 
2311d24e3c98SJiri Olsa 		printed += scnprintf(bf + printed, size - printed,
2312d24e3c98SJiri Olsa 				     " lost: %" PRIu64 "/%" PRIu64,
2313d24e3c98SJiri Olsa 				     top->lost, top->lost_total);
2314d24e3c98SJiri Olsa 
231597f7e0b3SJiri Olsa 		printed += scnprintf(bf + printed, size - printed,
231697f7e0b3SJiri Olsa 				     " drop: %" PRIu64 "/%" PRIu64,
231797f7e0b3SJiri Olsa 				     top->drop, top->drop_total);
231897f7e0b3SJiri Olsa 
23191e378ebdSTaeung Song 		if (top->zero)
23201e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
23218aa5c8edSJiri Olsa 
23228aa5c8edSJiri Olsa 		perf_top__reset_sample_counters(top);
23231e378ebdSTaeung Song 	}
23241e378ebdSTaeung Song 
23258aa5c8edSJiri Olsa 
2326aca7a94dSNamhyung Kim 	return printed;
2327aca7a94dSNamhyung Kim }
2328aca7a94dSNamhyung Kim 
2329aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2330aca7a94dSNamhyung Kim {
2331aca7a94dSNamhyung Kim 	int i;
2332aca7a94dSNamhyung Kim 
233304662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
233404662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2335aca7a94dSNamhyung Kim }
2336aca7a94dSNamhyung Kim 
2337341487abSFeng Tang /*
2338341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2339341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2340341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2341341487abSFeng Tang  */
2342341487abSFeng Tang static bool is_input_name_malloced = false;
2343341487abSFeng Tang 
2344341487abSFeng Tang static int switch_data_file(void)
2345341487abSFeng Tang {
2346341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2347341487abSFeng Tang 	DIR *pwd_dir;
2348341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2349341487abSFeng Tang 	struct dirent *dent;
2350341487abSFeng Tang 
2351341487abSFeng Tang 	pwd = getenv("PWD");
2352341487abSFeng Tang 	if (!pwd)
2353341487abSFeng Tang 		return ret;
2354341487abSFeng Tang 
2355341487abSFeng Tang 	pwd_dir = opendir(pwd);
2356341487abSFeng Tang 	if (!pwd_dir)
2357341487abSFeng Tang 		return ret;
2358341487abSFeng Tang 
2359341487abSFeng Tang 	memset(options, 0, sizeof(options));
23603ef5b402SChangbin Du 	memset(abs_path, 0, sizeof(abs_path));
2361341487abSFeng Tang 
2362341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2363341487abSFeng Tang 		char path[PATH_MAX];
2364341487abSFeng Tang 		u64 magic;
2365341487abSFeng Tang 		char *name = dent->d_name;
2366341487abSFeng Tang 		FILE *file;
2367341487abSFeng Tang 
2368341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2369341487abSFeng Tang 			continue;
2370341487abSFeng Tang 
2371341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2372341487abSFeng Tang 
2373341487abSFeng Tang 		file = fopen(path, "r");
2374341487abSFeng Tang 		if (!file)
2375341487abSFeng Tang 			continue;
2376341487abSFeng Tang 
2377341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2378341487abSFeng Tang 			goto close_file_and_continue;
2379341487abSFeng Tang 
2380341487abSFeng Tang 		if (is_perf_magic(magic)) {
2381341487abSFeng Tang 			options[nr_options] = strdup(name);
2382341487abSFeng Tang 			if (!options[nr_options])
2383341487abSFeng Tang 				goto close_file_and_continue;
2384341487abSFeng Tang 
2385341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2386341487abSFeng Tang 			if (!abs_path[nr_options]) {
238774cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2388341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2389341487abSFeng Tang 				fclose(file);
2390341487abSFeng Tang 				break;
2391341487abSFeng Tang 			}
2392341487abSFeng Tang 
2393341487abSFeng Tang 			nr_options++;
2394341487abSFeng Tang 		}
2395341487abSFeng Tang 
2396341487abSFeng Tang close_file_and_continue:
2397341487abSFeng Tang 		fclose(file);
2398341487abSFeng Tang 		if (nr_options >= 32) {
2399341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2400341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2401341487abSFeng Tang 			break;
2402341487abSFeng Tang 		}
2403341487abSFeng Tang 	}
2404341487abSFeng Tang 	closedir(pwd_dir);
2405341487abSFeng Tang 
2406341487abSFeng Tang 	if (nr_options) {
2407d0712656SArnaldo Carvalho de Melo 		choice = ui__popup_menu(nr_options, options, NULL);
2408341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2409341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2410341487abSFeng Tang 			if (tmp) {
2411341487abSFeng Tang 				if (is_input_name_malloced)
2412341487abSFeng Tang 					free((void *)input_name);
2413341487abSFeng Tang 				input_name = tmp;
2414341487abSFeng Tang 				is_input_name_malloced = true;
2415341487abSFeng Tang 				ret = 0;
2416341487abSFeng Tang 			} else
2417341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2418341487abSFeng Tang 		}
2419341487abSFeng Tang 	}
2420341487abSFeng Tang 
2421341487abSFeng Tang 	free_popup_options(options, nr_options);
2422341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2423341487abSFeng Tang 	return ret;
2424341487abSFeng Tang }
2425341487abSFeng Tang 
2426ea7cd592SNamhyung Kim struct popup_action {
24271d6c49dfSAndi Kleen 	unsigned long		time;
2428ea7cd592SNamhyung Kim 	struct thread 		*thread;
2429ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
243084734b06SKan Liang 	int			socket;
243132dcd021SJiri Olsa 	struct evsel	*evsel;
24324968ac8fSAndi Kleen 	enum rstype		rstype;
2433ea7cd592SNamhyung Kim 
2434ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2435ea7cd592SNamhyung Kim };
2436ea7cd592SNamhyung Kim 
2437bc7cad42SNamhyung Kim static int
2438ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2439bc7cad42SNamhyung Kim {
244032dcd021SJiri Olsa 	struct evsel *evsel;
2441bc7cad42SNamhyung Kim 	struct annotation *notes;
2442bc7cad42SNamhyung Kim 	struct hist_entry *he;
2443bc7cad42SNamhyung Kim 	int err;
2444bc7cad42SNamhyung Kim 
2445f178fd2dSArnaldo Carvalho de Melo 	if (!browser->annotation_opts->objdump_path &&
2446f178fd2dSArnaldo Carvalho de Melo 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2447bc7cad42SNamhyung Kim 		return 0;
2448bc7cad42SNamhyung Kim 
2449ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2450bc7cad42SNamhyung Kim 	if (!notes->src)
2451bc7cad42SNamhyung Kim 		return 0;
2452bc7cad42SNamhyung Kim 
2453848a5e50SJin Yao 	if (browser->block_evsel)
2454848a5e50SJin Yao 		evsel = browser->block_evsel;
2455848a5e50SJin Yao 	else
2456bc7cad42SNamhyung Kim 		evsel = hists_to_evsel(browser->hists);
2457848a5e50SJin Yao 
2458cd0cccbaSArnaldo Carvalho de Melo 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2459cd0cccbaSArnaldo Carvalho de Melo 				       browser->annotation_opts);
2460bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2461bc7cad42SNamhyung Kim 	/*
2462bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2463bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2464bc7cad42SNamhyung Kim 	 */
2465bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2466bc7cad42SNamhyung Kim 		return 1;
2467bc7cad42SNamhyung Kim 
2468bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2469bc7cad42SNamhyung Kim 	if (err)
2470bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2471bc7cad42SNamhyung Kim 	return 0;
2472bc7cad42SNamhyung Kim }
2473bc7cad42SNamhyung Kim 
24747b0a0dcbSJin Yao static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
24757b0a0dcbSJin Yao {
24767b0a0dcbSJin Yao 	struct annotated_source *src;
24777b0a0dcbSJin Yao 	struct symbol *sym;
24787b0a0dcbSJin Yao 	char name[64];
24797b0a0dcbSJin Yao 
24807b0a0dcbSJin Yao 	snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
24817b0a0dcbSJin Yao 
24827b0a0dcbSJin Yao 	sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
24837b0a0dcbSJin Yao 	if (sym) {
24847b0a0dcbSJin Yao 		src = symbol__hists(sym, 1);
24857b0a0dcbSJin Yao 		if (!src) {
24867b0a0dcbSJin Yao 			symbol__delete(sym);
24877b0a0dcbSJin Yao 			return NULL;
24887b0a0dcbSJin Yao 		}
24897b0a0dcbSJin Yao 
2490*63df0e4bSIan Rogers 		dso__insert_symbol(map__dso(map), sym);
24917b0a0dcbSJin Yao 	}
24927b0a0dcbSJin Yao 
24937b0a0dcbSJin Yao 	return sym;
24947b0a0dcbSJin Yao }
24957b0a0dcbSJin Yao 
2496bc7cad42SNamhyung Kim static int
2497ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2498ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
24997b0a0dcbSJin Yao 		 struct map_symbol *ms,
25007b0a0dcbSJin Yao 		 u64 addr)
2501bc7cad42SNamhyung Kim {
2502*63df0e4bSIan Rogers 	struct dso *dso = map__dso(ms->map);
2503*63df0e4bSIan Rogers 
2504*63df0e4bSIan Rogers 	if (!ms->map || !dso || dso->annotate_warned)
25057b0a0dcbSJin Yao 		return 0;
25067b0a0dcbSJin Yao 
25077b0a0dcbSJin Yao 	if (!ms->sym)
25087b0a0dcbSJin Yao 		ms->sym = symbol__new_unresolved(addr, ms->map);
25097b0a0dcbSJin Yao 
25107b0a0dcbSJin Yao 	if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2511ea7cd592SNamhyung Kim 		return 0;
2512ea7cd592SNamhyung Kim 
2513d46a4cdfSArnaldo Carvalho de Melo 	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2514ea7cd592SNamhyung Kim 		return 0;
2515ea7cd592SNamhyung Kim 
2516d46a4cdfSArnaldo Carvalho de Melo 	act->ms = *ms;
2517ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2518ea7cd592SNamhyung Kim 	return 1;
2519ea7cd592SNamhyung Kim }
2520ea7cd592SNamhyung Kim 
2521ea7cd592SNamhyung Kim static int
2522ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2523ea7cd592SNamhyung Kim {
2524ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2525ea7cd592SNamhyung Kim 
25267cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
25277cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2528599a2f38SNamhyung Kim 		return 0;
2529599a2f38SNamhyung Kim 
2530bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2531bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2532bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2533bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2534bc7cad42SNamhyung Kim 		ui_helpline__pop();
2535bc7cad42SNamhyung Kim 	} else {
2536fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
25377727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2538bc7cad42SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "",
2539bc7cad42SNamhyung Kim 					   thread->tid);
25406962ccb3SNamhyung Kim 		} else {
25416962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
25426962ccb3SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "");
25436962ccb3SNamhyung Kim 		}
25446962ccb3SNamhyung Kim 
2545bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2546bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2547bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2548bc7cad42SNamhyung Kim 	}
2549bc7cad42SNamhyung Kim 
2550bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2551bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2552bc7cad42SNamhyung Kim 	return 0;
2553bc7cad42SNamhyung Kim }
2554bc7cad42SNamhyung Kim 
2555bc7cad42SNamhyung Kim static int
2556ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2557ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2558bc7cad42SNamhyung Kim {
25596962ccb3SNamhyung Kim 	int ret;
25606962ccb3SNamhyung Kim 
25617cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
25627cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2563ea7cd592SNamhyung Kim 		return 0;
2564ea7cd592SNamhyung Kim 
2565fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
25666962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2567ea7cd592SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
2568ea7cd592SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "",
25696962ccb3SNamhyung Kim 			       thread->tid);
25706962ccb3SNamhyung Kim 	} else {
25716962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s thread",
25726962ccb3SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
25736962ccb3SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "");
25746962ccb3SNamhyung Kim 	}
25756962ccb3SNamhyung Kim 	if (ret < 0)
2576ea7cd592SNamhyung Kim 		return 0;
2577ea7cd592SNamhyung Kim 
2578ea7cd592SNamhyung Kim 	act->thread = thread;
2579ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2580ea7cd592SNamhyung Kim 	return 1;
2581ea7cd592SNamhyung Kim }
2582ea7cd592SNamhyung Kim 
2583632003f4SArnaldo Carvalho de Melo static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2584ea7cd592SNamhyung Kim {
258569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2586599a2f38SNamhyung Kim 		return 0;
2587599a2f38SNamhyung Kim 
2588bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2589bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2590bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2591bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2592bc7cad42SNamhyung Kim 		ui_helpline__pop();
2593bc7cad42SNamhyung Kim 	} else {
2594*63df0e4bSIan Rogers 		struct dso *dso = map__dso(map);
25957727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2596*63df0e4bSIan Rogers 				   __map__is_kernel(map) ? "the Kernel" : dso->short_name);
2597*63df0e4bSIan Rogers 		browser->hists->dso_filter = dso;
2598bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2599bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2600bc7cad42SNamhyung Kim 	}
2601bc7cad42SNamhyung Kim 
2602bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2603bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2604bc7cad42SNamhyung Kim 	return 0;
2605bc7cad42SNamhyung Kim }
2606bc7cad42SNamhyung Kim 
2607bc7cad42SNamhyung Kim static int
2608632003f4SArnaldo Carvalho de Melo do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2609632003f4SArnaldo Carvalho de Melo {
2610632003f4SArnaldo Carvalho de Melo 	return hists_browser__zoom_map(browser, act->ms.map);
2611632003f4SArnaldo Carvalho de Melo }
2612632003f4SArnaldo Carvalho de Melo 
2613632003f4SArnaldo Carvalho de Melo static int
2614ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2615045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2616bc7cad42SNamhyung Kim {
261769849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2618ea7cd592SNamhyung Kim 		return 0;
2619ea7cd592SNamhyung Kim 
2620209f4e70SArnaldo Carvalho de Melo 	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2621ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
2622*63df0e4bSIan Rogers 		     __map__is_kernel(map) ? "the Kernel" : map__dso(map)->short_name) < 0)
2623ea7cd592SNamhyung Kim 		return 0;
2624ea7cd592SNamhyung Kim 
2625045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2626ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2627ea7cd592SNamhyung Kim 	return 1;
2628ea7cd592SNamhyung Kim }
2629ea7cd592SNamhyung Kim 
2630d5a599d9SArnaldo Carvalho de Melo static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2631d5a599d9SArnaldo Carvalho de Melo {
2632d5a599d9SArnaldo Carvalho de Melo 	hist_browser__toggle_fold(browser);
2633d5a599d9SArnaldo Carvalho de Melo 	return 0;
2634d5a599d9SArnaldo Carvalho de Melo }
2635d5a599d9SArnaldo Carvalho de Melo 
2636d5a599d9SArnaldo Carvalho de Melo static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2637d5a599d9SArnaldo Carvalho de Melo {
2638bdc633feSArnaldo Carvalho de Melo 	char sym_name[512];
2639d5a599d9SArnaldo Carvalho de Melo 
2640bdc633feSArnaldo Carvalho de Melo         if (!hist_browser__selection_has_children(browser))
2641d5a599d9SArnaldo Carvalho de Melo                 return 0;
2642d5a599d9SArnaldo Carvalho de Melo 
2643bdc633feSArnaldo Carvalho de Melo 	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2644bdc633feSArnaldo Carvalho de Melo 		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2645bdc633feSArnaldo Carvalho de Melo 		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2646d5a599d9SArnaldo Carvalho de Melo 		return 0;
2647d5a599d9SArnaldo Carvalho de Melo 
2648d5a599d9SArnaldo Carvalho de Melo 	act->fn = do_toggle_callchain;
2649d5a599d9SArnaldo Carvalho de Melo 	return 1;
2650d5a599d9SArnaldo Carvalho de Melo }
2651d5a599d9SArnaldo Carvalho de Melo 
2652ea7cd592SNamhyung Kim static int
2653ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2654ea7cd592SNamhyung Kim 	      struct popup_action *act)
2655ea7cd592SNamhyung Kim {
2656ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2657bc7cad42SNamhyung Kim 	return 0;
2658bc7cad42SNamhyung Kim }
2659bc7cad42SNamhyung Kim 
2660bc7cad42SNamhyung Kim static int
266169849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2662ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2663ea7cd592SNamhyung Kim {
266469849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2665ea7cd592SNamhyung Kim 		return 0;
2666ea7cd592SNamhyung Kim 
2667ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2668ea7cd592SNamhyung Kim 		return 0;
2669ea7cd592SNamhyung Kim 
2670ea7cd592SNamhyung Kim 	act->ms.map = map;
2671ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2672ea7cd592SNamhyung Kim 	return 1;
2673ea7cd592SNamhyung Kim }
2674ea7cd592SNamhyung Kim 
2675ea7cd592SNamhyung Kim static int
2676bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2677ea7cd592SNamhyung Kim 	      struct popup_action *act)
2678bc7cad42SNamhyung Kim {
26791d6c49dfSAndi Kleen 	char *script_opt;
26801d6c49dfSAndi Kleen 	int len;
26811d6c49dfSAndi Kleen 	int n = 0;
2682bc7cad42SNamhyung Kim 
26831d6c49dfSAndi Kleen 	len = 100;
26841d6c49dfSAndi Kleen 	if (act->thread)
26851d6c49dfSAndi Kleen 		len += strlen(thread__comm_str(act->thread));
26861d6c49dfSAndi Kleen 	else if (act->ms.sym)
26871d6c49dfSAndi Kleen 		len += strlen(act->ms.sym->name);
26881d6c49dfSAndi Kleen 	script_opt = malloc(len);
26891d6c49dfSAndi Kleen 	if (!script_opt)
26901d6c49dfSAndi Kleen 		return -1;
26911d6c49dfSAndi Kleen 
26921d6c49dfSAndi Kleen 	script_opt[0] = 0;
2693ea7cd592SNamhyung Kim 	if (act->thread) {
26941d6c49dfSAndi Kleen 		n = scnprintf(script_opt, len, " -c %s ",
2695ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2696ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
26971d6c49dfSAndi Kleen 		n = scnprintf(script_opt, len, " -S %s ",
2698ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2699bc7cad42SNamhyung Kim 	}
2700bc7cad42SNamhyung Kim 
27011d6c49dfSAndi Kleen 	if (act->time) {
27021d6c49dfSAndi Kleen 		char start[32], end[32];
27031d6c49dfSAndi Kleen 		unsigned long starttime = act->time;
27041d6c49dfSAndi Kleen 		unsigned long endtime = act->time + symbol_conf.time_quantum;
27051d6c49dfSAndi Kleen 
27061d6c49dfSAndi Kleen 		if (starttime == endtime) { /* Display 1ms as fallback */
27071d6c49dfSAndi Kleen 			starttime -= 1*NSEC_PER_MSEC;
27081d6c49dfSAndi Kleen 			endtime += 1*NSEC_PER_MSEC;
27091d6c49dfSAndi Kleen 		}
27101d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(starttime, start, sizeof start);
27111d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(endtime, end, sizeof end);
27121d6c49dfSAndi Kleen 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
27131d6c49dfSAndi Kleen 	}
27141d6c49dfSAndi Kleen 
27156f3da20eSAndi Kleen 	script_browse(script_opt, act->evsel);
27161d6c49dfSAndi Kleen 	free(script_opt);
2717bc7cad42SNamhyung Kim 	return 0;
2718bc7cad42SNamhyung Kim }
2719bc7cad42SNamhyung Kim 
2720bc7cad42SNamhyung Kim static int
27214968ac8fSAndi Kleen do_res_sample_script(struct hist_browser *browser __maybe_unused,
27224968ac8fSAndi Kleen 		     struct popup_action *act)
27234968ac8fSAndi Kleen {
27244968ac8fSAndi Kleen 	struct hist_entry *he;
27254968ac8fSAndi Kleen 
27264968ac8fSAndi Kleen 	he = hist_browser__selected_entry(browser);
27274968ac8fSAndi Kleen 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
27284968ac8fSAndi Kleen 	return 0;
27294968ac8fSAndi Kleen }
27304968ac8fSAndi Kleen 
27314968ac8fSAndi Kleen static int
27321d6c49dfSAndi Kleen add_script_opt_2(struct hist_browser *browser __maybe_unused,
2733ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
27341d6c49dfSAndi Kleen 	       struct thread *thread, struct symbol *sym,
273532dcd021SJiri Olsa 	       struct evsel *evsel, const char *tstr)
2736ea7cd592SNamhyung Kim {
27371d6c49dfSAndi Kleen 
2738ea7cd592SNamhyung Kim 	if (thread) {
27391d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
27401d6c49dfSAndi Kleen 			     thread__comm_str(thread), tstr) < 0)
2741ea7cd592SNamhyung Kim 			return 0;
2742ea7cd592SNamhyung Kim 	} else if (sym) {
27431d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
27441d6c49dfSAndi Kleen 			     sym->name, tstr) < 0)
2745ea7cd592SNamhyung Kim 			return 0;
2746ea7cd592SNamhyung Kim 	} else {
27471d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2748ea7cd592SNamhyung Kim 			return 0;
2749ea7cd592SNamhyung Kim 	}
2750ea7cd592SNamhyung Kim 
2751ea7cd592SNamhyung Kim 	act->thread = thread;
2752ea7cd592SNamhyung Kim 	act->ms.sym = sym;
27536f3da20eSAndi Kleen 	act->evsel = evsel;
2754ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2755ea7cd592SNamhyung Kim 	return 1;
2756ea7cd592SNamhyung Kim }
2757ea7cd592SNamhyung Kim 
2758ea7cd592SNamhyung Kim static int
27591d6c49dfSAndi Kleen add_script_opt(struct hist_browser *browser,
27601d6c49dfSAndi Kleen 	       struct popup_action *act, char **optstr,
27616f3da20eSAndi Kleen 	       struct thread *thread, struct symbol *sym,
276232dcd021SJiri Olsa 	       struct evsel *evsel)
27631d6c49dfSAndi Kleen {
27641d6c49dfSAndi Kleen 	int n, j;
27651d6c49dfSAndi Kleen 	struct hist_entry *he;
27661d6c49dfSAndi Kleen 
27676f3da20eSAndi Kleen 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
27681d6c49dfSAndi Kleen 
27691d6c49dfSAndi Kleen 	he = hist_browser__selected_entry(browser);
27701d6c49dfSAndi Kleen 	if (sort_order && strstr(sort_order, "time")) {
27711d6c49dfSAndi Kleen 		char tstr[128];
27721d6c49dfSAndi Kleen 
27731d6c49dfSAndi Kleen 		optstr++;
27741d6c49dfSAndi Kleen 		act++;
27751d6c49dfSAndi Kleen 		j = sprintf(tstr, " in ");
27761d6c49dfSAndi Kleen 		j += timestamp__scnprintf_usec(he->time, tstr + j,
27771d6c49dfSAndi Kleen 					       sizeof tstr - j);
27781d6c49dfSAndi Kleen 		j += sprintf(tstr + j, "-");
27791d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
27806f3da20eSAndi Kleen 				          tstr + j, sizeof tstr - j);
27811d6c49dfSAndi Kleen 		n += add_script_opt_2(browser, act, optstr, thread, sym,
27826f3da20eSAndi Kleen 					  evsel, tstr);
27831d6c49dfSAndi Kleen 		act->time = he->time;
27841d6c49dfSAndi Kleen 	}
27851d6c49dfSAndi Kleen 	return n;
27861d6c49dfSAndi Kleen }
27871d6c49dfSAndi Kleen 
27881d6c49dfSAndi Kleen static int
27894968ac8fSAndi Kleen add_res_sample_opt(struct hist_browser *browser __maybe_unused,
27904968ac8fSAndi Kleen 		   struct popup_action *act, char **optstr,
27914968ac8fSAndi Kleen 		   struct res_sample *res_sample,
279232dcd021SJiri Olsa 		   struct evsel *evsel,
27934968ac8fSAndi Kleen 		   enum rstype type)
27944968ac8fSAndi Kleen {
27954968ac8fSAndi Kleen 	if (!res_sample)
27964968ac8fSAndi Kleen 		return 0;
27974968ac8fSAndi Kleen 
27984968ac8fSAndi Kleen 	if (asprintf(optstr, "Show context for individual samples %s",
27994968ac8fSAndi Kleen 		type == A_ASM ? "with assembler" :
28004968ac8fSAndi Kleen 		type == A_SOURCE ? "with source" : "") < 0)
28014968ac8fSAndi Kleen 		return 0;
28024968ac8fSAndi Kleen 
28034968ac8fSAndi Kleen 	act->fn = do_res_sample_script;
28044968ac8fSAndi Kleen 	act->evsel = evsel;
28054968ac8fSAndi Kleen 	act->rstype = type;
28064968ac8fSAndi Kleen 	return 1;
28074968ac8fSAndi Kleen }
28084968ac8fSAndi Kleen 
28094968ac8fSAndi Kleen static int
2810ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2811ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2812bc7cad42SNamhyung Kim {
2813bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2814bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2815bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2816ea7cd592SNamhyung Kim 		return 0;
2817bc7cad42SNamhyung Kim 	}
2818bc7cad42SNamhyung Kim 
2819bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2820bc7cad42SNamhyung Kim }
2821bc7cad42SNamhyung Kim 
2822ea7cd592SNamhyung Kim static int
2823ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2824ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2825ea7cd592SNamhyung Kim {
2826ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2827ea7cd592SNamhyung Kim 		return 0;
2828ea7cd592SNamhyung Kim 
2829ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2830ea7cd592SNamhyung Kim 		return 0;
2831ea7cd592SNamhyung Kim 
2832ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2833ea7cd592SNamhyung Kim 	return 1;
2834ea7cd592SNamhyung Kim }
2835ea7cd592SNamhyung Kim 
2836ea7cd592SNamhyung Kim static int
2837ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2838ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2839ea7cd592SNamhyung Kim {
2840ea7cd592SNamhyung Kim 	return 0;
2841ea7cd592SNamhyung Kim }
2842ea7cd592SNamhyung Kim 
2843ea7cd592SNamhyung Kim static int
2844ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2845ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2846ea7cd592SNamhyung Kim {
2847ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2848ea7cd592SNamhyung Kim 		return 0;
2849ea7cd592SNamhyung Kim 
2850ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2851ea7cd592SNamhyung Kim 	return 1;
2852ea7cd592SNamhyung Kim }
2853ea7cd592SNamhyung Kim 
285484734b06SKan Liang static int
285584734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
285684734b06SKan Liang {
285735a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2858599a2f38SNamhyung Kim 		return 0;
2859599a2f38SNamhyung Kim 
286084734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
286184734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
286284734b06SKan Liang 		browser->hists->socket_filter = -1;
286384734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
286484734b06SKan Liang 	} else {
286584734b06SKan Liang 		browser->hists->socket_filter = act->socket;
286684734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
286784734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
286884734b06SKan Liang 	}
286984734b06SKan Liang 
287084734b06SKan Liang 	hists__filter_by_socket(browser->hists);
287184734b06SKan Liang 	hist_browser__reset(browser);
287284734b06SKan Liang 	return 0;
287384734b06SKan Liang }
287484734b06SKan Liang 
287584734b06SKan Liang static int
287684734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
287784734b06SKan Liang 	       char **optstr, int socket_id)
287884734b06SKan Liang {
287935a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
288084734b06SKan Liang 		return 0;
288184734b06SKan Liang 
288284734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
288384734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
288484734b06SKan Liang 		     socket_id) < 0)
288584734b06SKan Liang 		return 0;
288684734b06SKan Liang 
288784734b06SKan Liang 	act->socket = socket_id;
288884734b06SKan Liang 	act->fn = do_zoom_socket;
288984734b06SKan Liang 	return 1;
289084734b06SKan Liang }
289184734b06SKan Liang 
2892112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2893064f1981SNamhyung Kim {
2894064f1981SNamhyung Kim 	u64 nr_entries = 0;
28952eb3d689SDavidlohr Bueso 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2896064f1981SNamhyung Kim 
2897f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2898268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2899268397cbSNamhyung Kim 		return;
2900268397cbSNamhyung Kim 	}
2901268397cbSNamhyung Kim 
290214135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2903064f1981SNamhyung Kim 		nr_entries++;
2904f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2905064f1981SNamhyung Kim 	}
2906064f1981SNamhyung Kim 
2907112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2908f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2909064f1981SNamhyung Kim }
2910341487abSFeng Tang 
2911b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2912b62e8dfcSNamhyung Kim 					       double percent)
2913b62e8dfcSNamhyung Kim {
2914b62e8dfcSNamhyung Kim 	struct hist_entry *he;
29152eb3d689SDavidlohr Bueso 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2916b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2917b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2918b62e8dfcSNamhyung Kim 
2919b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2920b62e8dfcSNamhyung Kim 
2921b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2922b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2923b62e8dfcSNamhyung Kim 
292479dded87SNamhyung Kim 		if (he->has_no_entry) {
292579dded87SNamhyung Kim 			he->has_no_entry = false;
292679dded87SNamhyung Kim 			he->nr_rows = 0;
292779dded87SNamhyung Kim 		}
292879dded87SNamhyung Kim 
2929fabd37b8SArnaldo Carvalho de Melo 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2930d0506edbSNamhyung Kim 			goto next;
2931d0506edbSNamhyung Kim 
2932b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2933b62e8dfcSNamhyung Kim 			total = he->stat.period;
2934b62e8dfcSNamhyung Kim 
2935b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2936b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2937b62e8dfcSNamhyung Kim 
2938b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2939b62e8dfcSNamhyung Kim 		}
2940b62e8dfcSNamhyung Kim 
2941b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2942b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2943b62e8dfcSNamhyung Kim 
2944d0506edbSNamhyung Kim next:
2945201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2946d0506edbSNamhyung Kim 
2947b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2948b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2949492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2950b62e8dfcSNamhyung Kim 	}
2951b62e8dfcSNamhyung Kim }
2952b62e8dfcSNamhyung Kim 
295356933029SArnaldo Carvalho de Melo static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
295456933029SArnaldo Carvalho de Melo 			       bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
295556933029SArnaldo Carvalho de Melo 			       struct perf_env *env, bool warn_lost_event,
2956cd0cccbaSArnaldo Carvalho de Melo 			       struct annotation_options *annotation_opts)
2957aca7a94dSNamhyung Kim {
29584ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2959cd0cccbaSArnaldo Carvalho de Melo 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2960ceb75476SLeo Yan 	struct branch_info *bi = NULL;
2961f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2962f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2963ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2964aca7a94dSNamhyung Kim 	int nr_options = 0;
2965aca7a94dSNamhyung Kim 	int key = -1;
296686449b12SSong Liu 	char buf[128];
29679783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2968aca7a94dSNamhyung Kim 
2969e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2970e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2971e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2972e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
29736a02f06eSAndi Kleen 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2974e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2975e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2976e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
29777727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
29787727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
29799218a913SArnaldo Carvalho de Melo 	"+             Expand/Collapse one callchain level\n"		\
2980e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2981e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2982e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2983e6d6abfcSArnaldo Carvalho de Melo 	"e             Expand/Collapse main entry callchains\n"	\
2984e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2985105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2986025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2987209f4e70SArnaldo Carvalho de Melo 	"k             Zoom into the kernel map\n"			\
2988b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
298931eb4360SNamhyung Kim 	"m             Display context menu\n"				\
299084734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2991e8e684a5SNamhyung Kim 
2992e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
299349b8e2beSRasmus Villemoes 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
29946dd60135SNamhyung Kim 	"i             Show header information\n"
2995e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2996e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2997e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2998e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2999e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
3000dbddf174SJin Yao 	"/             Filter symbol by name\n"
3001dbddf174SJin Yao 	"0-9           Sort by event n in group";
300249b8e2beSRasmus Villemoes 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
3003e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
3004e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
3005e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
300642337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
3007fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
3008e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
3009e8e684a5SNamhyung Kim 
3010aca7a94dSNamhyung Kim 	if (browser == NULL)
3011aca7a94dSNamhyung Kim 		return -1;
3012aca7a94dSNamhyung Kim 
3013ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
3014ed426915SNamhyung Kim 	SLang_reset_tty();
3015ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
3016ed426915SNamhyung Kim 
301703905048SNamhyung Kim 	if (min_pcnt)
3018064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
3019112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
3020064f1981SNamhyung Kim 
302184734b06SKan Liang 	browser->pstack = pstack__new(3);
302201f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
3023aca7a94dSNamhyung Kim 		goto out;
3024aca7a94dSNamhyung Kim 
3025aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
3026aca7a94dSNamhyung Kim 
3027aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
3028ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
3029aca7a94dSNamhyung Kim 
30305b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
30315b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
30325b591669SNamhyung Kim 
30335c959b6dSArnaldo Carvalho de Melo 	if (!is_report_browser(hbt))
30345c959b6dSArnaldo Carvalho de Melo 		browser->b.no_samples_msg = "Collecting samples...";
30355c959b6dSArnaldo Carvalho de Melo 
3036aca7a94dSNamhyung Kim 	while (1) {
3037f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
3038045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
30394c8b9c0fSArnaldo Carvalho de Melo 		int choice;
304084734b06SKan Liang 		int socked_id = -1;
3041aca7a94dSNamhyung Kim 
30424c8b9c0fSArnaldo Carvalho de Melo 		key = 0; // reset key
30434c8b9c0fSArnaldo Carvalho de Melo do_hotkey:		 // key came straight from options ui__popup_menu()
30444c8b9c0fSArnaldo Carvalho de Melo 		choice = nr_options = 0;
30454c8b9c0fSArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3046aca7a94dSNamhyung Kim 
3047aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
3048aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
3049045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
305084734b06SKan Liang 			socked_id = browser->he_selection->socket;
3051aca7a94dSNamhyung Kim 		}
3052aca7a94dSNamhyung Kim 		switch (key) {
3053aca7a94dSNamhyung Kim 		case K_TAB:
3054aca7a94dSNamhyung Kim 		case K_UNTAB:
3055aca7a94dSNamhyung Kim 			if (nr_events == 1)
3056aca7a94dSNamhyung Kim 				continue;
3057aca7a94dSNamhyung Kim 			/*
3058aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
3059aca7a94dSNamhyung Kim 			 * go to the next or previous
3060aca7a94dSNamhyung Kim 			 */
3061aca7a94dSNamhyung Kim 			goto out_free_stack;
3062dbddf174SJin Yao 		case '0' ... '9':
3063dbddf174SJin Yao 			if (!symbol_conf.event_group ||
3064dbddf174SJin Yao 			    evsel->core.nr_members < 2) {
3065dbddf174SJin Yao 				snprintf(buf, sizeof(buf),
3066dbddf174SJin Yao 					 "Sort by index only available with group events!");
3067dbddf174SJin Yao 				helpline = buf;
3068dbddf174SJin Yao 				continue;
3069dbddf174SJin Yao 			}
3070dbddf174SJin Yao 
3071dbddf174SJin Yao 			if (key - '0' == symbol_conf.group_sort_idx)
3072dbddf174SJin Yao 				continue;
3073dbddf174SJin Yao 
3074dbddf174SJin Yao 			symbol_conf.group_sort_idx = key - '0';
3075dbddf174SJin Yao 
3076dbddf174SJin Yao 			if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3077dbddf174SJin Yao 				snprintf(buf, sizeof(buf),
3078dbddf174SJin Yao 					 "Max event group index to sort is %d (index from 0 to %d)",
3079dbddf174SJin Yao 					 evsel->core.nr_members - 1,
3080dbddf174SJin Yao 					 evsel->core.nr_members - 1);
3081dbddf174SJin Yao 				helpline = buf;
3082dbddf174SJin Yao 				continue;
3083dbddf174SJin Yao 			}
3084dbddf174SJin Yao 
3085dbddf174SJin Yao 			key = K_RELOAD;
3086dbddf174SJin Yao 			goto out_free_stack;
3087aca7a94dSNamhyung Kim 		case 'a':
30882e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
3089aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
3090aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
3091aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
3092aca7a94dSNamhyung Kim 				continue;
3093aca7a94dSNamhyung Kim 			}
3094aca7a94dSNamhyung Kim 
3095ec0479a6SJin Yao 			if (!browser->selection ||
3096ec0479a6SJin Yao 			    !browser->selection->map ||
3097*63df0e4bSIan Rogers 			    !map__dso(browser->selection->map) ||
3098*63df0e4bSIan Rogers 			    map__dso(browser->selection->map)->annotate_warned) {
3099ec0479a6SJin Yao 				continue;
3100ec0479a6SJin Yao 			}
3101ec0479a6SJin Yao 
3102ec0479a6SJin Yao 			if (!browser->selection->sym) {
3103ec0479a6SJin Yao 				if (!browser->he_selection)
3104aca7a94dSNamhyung Kim 					continue;
3105bc7cad42SNamhyung Kim 
3106ec0479a6SJin Yao 				if (sort__mode == SORT_MODE__BRANCH) {
3107ec0479a6SJin Yao 					bi = browser->he_selection->branch_info;
3108ec0479a6SJin Yao 					if (!bi || !bi->to.ms.map)
3109ec0479a6SJin Yao 						continue;
3110ec0479a6SJin Yao 
3111ec0479a6SJin Yao 					actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3112ec0479a6SJin Yao 					actions->ms.map = bi->to.ms.map;
3113ec0479a6SJin Yao 				} else {
3114ec0479a6SJin Yao 					actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3115ec0479a6SJin Yao 										 browser->selection->map);
3116ec0479a6SJin Yao 					actions->ms.map = browser->selection->map;
3117ec0479a6SJin Yao 				}
3118ec0479a6SJin Yao 
3119ec0479a6SJin Yao 				if (!actions->ms.sym)
3120ec0479a6SJin Yao 					continue;
3121ec0479a6SJin Yao 			} else {
3122ea537f22SArnaldo Carvalho de Melo 				if (symbol__annotation(browser->selection->sym)->src == NULL) {
3123ea537f22SArnaldo Carvalho de Melo 					ui_browser__warning(&browser->b, delay_secs * 2,
3124ea537f22SArnaldo Carvalho de Melo 						"No samples for the \"%s\" symbol.\n\n"
3125ea537f22SArnaldo Carvalho de Melo 						"Probably appeared just in a callchain",
3126ea537f22SArnaldo Carvalho de Melo 						browser->selection->sym->name);
3127ea537f22SArnaldo Carvalho de Melo 					continue;
3128ea537f22SArnaldo Carvalho de Melo 				}
3129ea537f22SArnaldo Carvalho de Melo 
3130ea7cd592SNamhyung Kim 				actions->ms.map = browser->selection->map;
3131ea7cd592SNamhyung Kim 				actions->ms.sym = browser->selection->sym;
3132ec0479a6SJin Yao 			}
3133ec0479a6SJin Yao 
3134ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
3135bc7cad42SNamhyung Kim 			continue;
3136aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
3137aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
3138aff3f3f6SArnaldo Carvalho de Melo 			continue;
3139aca7a94dSNamhyung Kim 		case 'd':
3140fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
3141ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
3142bc7cad42SNamhyung Kim 			continue;
3143209f4e70SArnaldo Carvalho de Melo 		case 'k':
3144209f4e70SArnaldo Carvalho de Melo 			if (browser->selection != NULL)
31455ab6d715SIan Rogers 				hists_browser__zoom_map(browser,
31465ab6d715SIan Rogers 					      maps__machine(browser->selection->maps)->vmlinux_map);
3147209f4e70SArnaldo Carvalho de Melo 			continue;
3148a7cb8863SArnaldo Carvalho de Melo 		case 'V':
314921e8c810SAlexis Berlemont 			verbose = (verbose + 1) % 4;
315021e8c810SAlexis Berlemont 			browser->show_dso = verbose > 0;
315121e8c810SAlexis Berlemont 			ui_helpline__fpush("Verbosity level set to %d\n",
315221e8c810SAlexis Berlemont 					   verbose);
3153a7cb8863SArnaldo Carvalho de Melo 			continue;
3154aca7a94dSNamhyung Kim 		case 't':
3155ea7cd592SNamhyung Kim 			actions->thread = thread;
3156ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
3157bc7cad42SNamhyung Kim 			continue;
315884734b06SKan Liang 		case 'S':
315984734b06SKan Liang 			actions->socket = socked_id;
316084734b06SKan Liang 			do_zoom_socket(browser, actions);
316184734b06SKan Liang 			continue;
31625a5626b1SArnaldo Carvalho de Melo 		case '/':
3163aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
31644aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
31654aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
3166aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
3167aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
316805e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
316905e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
3170aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
3171aca7a94dSNamhyung Kim 			}
3172aca7a94dSNamhyung Kim 			continue;
3173cdbab7c2SFeng Tang 		case 'r':
3174ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
3175ea7cd592SNamhyung Kim 				actions->thread = NULL;
3176ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
3177ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
3178ea7cd592SNamhyung Kim 			}
3179c77d8d70SFeng Tang 			continue;
3180341487abSFeng Tang 		case 's':
3181bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
3182ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
3183bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
3184bc7cad42SNamhyung Kim 					goto out_free_stack;
3185bc7cad42SNamhyung Kim 			}
3186341487abSFeng Tang 			continue;
31876dd60135SNamhyung Kim 		case 'i':
31886dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
31896dd60135SNamhyung Kim 			if (env->arch)
31906dd60135SNamhyung Kim 				tui__header_window(env);
31916dd60135SNamhyung Kim 			continue;
3192105eb30fSNamhyung Kim 		case 'F':
3193105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
3194105eb30fSNamhyung Kim 			continue;
319542337a22SNamhyung Kim 		case 'z':
319642337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
319742337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
319842337a22SNamhyung Kim 
319942337a22SNamhyung Kim 				top->zero = !top->zero;
320042337a22SNamhyung Kim 			}
320142337a22SNamhyung Kim 			continue;
3202b62e8dfcSNamhyung Kim 		case 'L':
3203b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
3204b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
3205b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
3206b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
3207b62e8dfcSNamhyung Kim 				char *end;
3208b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
3209b62e8dfcSNamhyung Kim 
3210b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
3211b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
3212b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
3213b62e8dfcSNamhyung Kim 					continue;
3214b62e8dfcSNamhyung Kim 				}
3215b62e8dfcSNamhyung Kim 
3216b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
3217b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
3218b62e8dfcSNamhyung Kim 			}
3219b62e8dfcSNamhyung Kim 			continue;
3220aca7a94dSNamhyung Kim 		case K_F1:
3221aca7a94dSNamhyung Kim 		case 'h':
3222aca7a94dSNamhyung Kim 		case '?':
3223aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
3224e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
3225aca7a94dSNamhyung Kim 			continue;
3226aca7a94dSNamhyung Kim 		case K_ENTER:
3227aca7a94dSNamhyung Kim 		case K_RIGHT:
322831eb4360SNamhyung Kim 		case 'm':
3229aca7a94dSNamhyung Kim 			/* menu */
3230aca7a94dSNamhyung Kim 			break;
323163ab1749SArnaldo Carvalho de Melo 		case K_ESC:
3232aca7a94dSNamhyung Kim 		case K_LEFT: {
3233aca7a94dSNamhyung Kim 			const void *top;
3234aca7a94dSNamhyung Kim 
323501f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
3236aca7a94dSNamhyung Kim 				/*
3237aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
3238aca7a94dSNamhyung Kim 				 */
3239aca7a94dSNamhyung Kim 				if (left_exits)
3240aca7a94dSNamhyung Kim 					goto out_free_stack;
324163ab1749SArnaldo Carvalho de Melo 
324263ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
324363ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
324463ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
324563ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
324663ab1749SArnaldo Carvalho de Melo 
3247aca7a94dSNamhyung Kim 				continue;
3248aca7a94dSNamhyung Kim 			}
32493f777403SArnaldo Carvalho de Melo 			actions->ms.map = map;
32506422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
3251bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
32526422184bSNamhyung Kim 				/*
32536422184bSNamhyung Kim 				 * No need to set actions->dso here since
32546422184bSNamhyung Kim 				 * it's just to remove the current filter.
32556422184bSNamhyung Kim 				 * Ditto for thread below.
32566422184bSNamhyung Kim 				 */
32576422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
325884734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
32596422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
326084734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
326184734b06SKan Liang 				do_zoom_socket(browser, actions);
326284734b06SKan Liang 			}
3263aca7a94dSNamhyung Kim 			continue;
3264aca7a94dSNamhyung Kim 		}
3265aca7a94dSNamhyung Kim 		case 'q':
3266aca7a94dSNamhyung Kim 		case CTRL('c'):
3267516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
3268fbb7997eSArnaldo Carvalho de Melo 		case 'f':
326913d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
327013d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
327113d1e536SNamhyung Kim 
3272ade9d208SArnaldo Carvalho de Melo 				evlist__toggle_enable(top->evlist);
327313d1e536SNamhyung Kim 				/*
327413d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
327513d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
327613d1e536SNamhyung Kim 				 */
327713d1e536SNamhyung Kim 				if (top->evlist->enabled) {
327813d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
327913d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
328013d1e536SNamhyung Kim 				} else {
328113d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
328213d1e536SNamhyung Kim 					hbt->refresh = 0;
328313d1e536SNamhyung Kim 				}
328413d1e536SNamhyung Kim 				continue;
328513d1e536SNamhyung Kim 			}
32863e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
3287aca7a94dSNamhyung Kim 		default:
32883e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
3289aca7a94dSNamhyung Kim 			continue;
3290aca7a94dSNamhyung Kim 		}
3291aca7a94dSNamhyung Kim 
32922e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
32930ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
32940ba332f7SArnaldo Carvalho de Melo 
329555369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
3296ceb75476SLeo Yan 
3297ceb75476SLeo Yan 			if (browser->he_selection)
3298aca7a94dSNamhyung Kim 				bi = browser->he_selection->branch_info;
32990ba332f7SArnaldo Carvalho de Melo 
33000ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
33010ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
33020ba332f7SArnaldo Carvalho de Melo 
3303ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3304ea7cd592SNamhyung Kim 						       &actions[nr_options],
3305ea7cd592SNamhyung Kim 						       &options[nr_options],
33067b0a0dcbSJin Yao 						       &bi->from.ms,
33077b0a0dcbSJin Yao 						       bi->from.al_addr);
3308d46a4cdfSArnaldo Carvalho de Melo 			if (bi->to.ms.sym != bi->from.ms.sym)
3309ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
3310ea7cd592SNamhyung Kim 							&actions[nr_options],
3311ea7cd592SNamhyung Kim 							&options[nr_options],
33127b0a0dcbSJin Yao 							&bi->to.ms,
33137b0a0dcbSJin Yao 							bi->to.al_addr);
3314aca7a94dSNamhyung Kim 		} else {
3315ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3316ea7cd592SNamhyung Kim 						       &actions[nr_options],
3317ea7cd592SNamhyung Kim 						       &options[nr_options],
33187b0a0dcbSJin Yao 						       browser->selection,
33197b0a0dcbSJin Yao 						       browser->he_selection->ip);
3320446fb96cSArnaldo Carvalho de Melo 		}
33210ba332f7SArnaldo Carvalho de Melo skip_annotation:
3322ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
3323ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
3324ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
3325045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
3326d5a599d9SArnaldo Carvalho de Melo 		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3327ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
3328ea7cd592SNamhyung Kim 					  &options[nr_options],
3329bd315aabSWang Nan 					  browser->selection ?
3330bd315aabSWang Nan 						browser->selection->map : NULL);
333184734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
333284734b06SKan Liang 					     &options[nr_options],
333384734b06SKan Liang 					     socked_id);
3334cdbab7c2SFeng Tang 		/* perf script support */
3335b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
3336b1baae89SNamhyung Kim 			goto skip_scripting;
3337b1baae89SNamhyung Kim 
3338cdbab7c2SFeng Tang 		if (browser->he_selection) {
3339fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
3340ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3341ea7cd592SNamhyung Kim 							     &actions[nr_options],
3342ea7cd592SNamhyung Kim 							     &options[nr_options],
33436f3da20eSAndi Kleen 							     thread, NULL, evsel);
33442eafd410SNamhyung Kim 			}
3345bd315aabSWang Nan 			/*
3346bd315aabSWang Nan 			 * Note that browser->selection != NULL
3347bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
3348bd315aabSWang Nan 			 * so we don't need to check browser->selection
3349bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
3350bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
3351bd315aabSWang Nan 			 *
3352bd315aabSWang Nan 			 * See hist_browser__show_entry.
3353bd315aabSWang Nan 			 */
33542e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
3355ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3356ea7cd592SNamhyung Kim 							     &actions[nr_options],
3357ea7cd592SNamhyung Kim 							     &options[nr_options],
33586f3da20eSAndi Kleen 							     NULL, browser->selection->sym,
33596f3da20eSAndi Kleen 							     evsel);
3360cdbab7c2SFeng Tang 			}
3361c221acb0SNamhyung Kim 		}
3362ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
33636f3da20eSAndi Kleen 					     &options[nr_options], NULL, NULL, evsel);
33644968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
33654968ac8fSAndi Kleen 						 &options[nr_options],
3366d61cbb85SWei Li 						 hist_browser__selected_res_sample(browser),
33674968ac8fSAndi Kleen 						 evsel, A_NORMAL);
33684968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
33694968ac8fSAndi Kleen 						 &options[nr_options],
3370d61cbb85SWei Li 						 hist_browser__selected_res_sample(browser),
33714968ac8fSAndi Kleen 						 evsel, A_ASM);
33724968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
33734968ac8fSAndi Kleen 						 &options[nr_options],
3374d61cbb85SWei Li 						 hist_browser__selected_res_sample(browser),
33754968ac8fSAndi Kleen 						 evsel, A_SOURCE);
3376ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
3377ea7cd592SNamhyung Kim 					     &options[nr_options]);
3378b1baae89SNamhyung Kim skip_scripting:
3379ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
3380ea7cd592SNamhyung Kim 					   &options[nr_options]);
3381aca7a94dSNamhyung Kim 
3382ea7cd592SNamhyung Kim 		do {
3383ea7cd592SNamhyung Kim 			struct popup_action *act;
3384ea7cd592SNamhyung Kim 
33854c8b9c0fSArnaldo Carvalho de Melo 			choice = ui__popup_menu(nr_options, options, &key);
33864c8b9c0fSArnaldo Carvalho de Melo 			if (choice == -1)
3387aca7a94dSNamhyung Kim 				break;
3388aca7a94dSNamhyung Kim 
33894c8b9c0fSArnaldo Carvalho de Melo 			if (choice == nr_options)
33904c8b9c0fSArnaldo Carvalho de Melo 				goto do_hotkey;
33914c8b9c0fSArnaldo Carvalho de Melo 
3392ea7cd592SNamhyung Kim 			act = &actions[choice];
3393ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
3394ea7cd592SNamhyung Kim 		} while (key == 1);
3395aca7a94dSNamhyung Kim 
3396bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3397341487abSFeng Tang 			break;
3398341487abSFeng Tang 	}
3399aca7a94dSNamhyung Kim out_free_stack:
340001f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3401aca7a94dSNamhyung Kim out:
3402aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3403f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3404aca7a94dSNamhyung Kim 	return key;
3405aca7a94dSNamhyung Kim }
3406aca7a94dSNamhyung Kim 
340732dcd021SJiri Olsa struct evsel_menu {
3408aca7a94dSNamhyung Kim 	struct ui_browser b;
340932dcd021SJiri Olsa 	struct evsel *selection;
3410cd0cccbaSArnaldo Carvalho de Melo 	struct annotation_options *annotation_opts;
3411aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3412064f1981SNamhyung Kim 	float min_pcnt;
3413ce80d3beSKan Liang 	struct perf_env *env;
3414aca7a94dSNamhyung Kim };
3415aca7a94dSNamhyung Kim 
3416aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3417aca7a94dSNamhyung Kim 				   void *entry, int row)
3418aca7a94dSNamhyung Kim {
341932dcd021SJiri Olsa 	struct evsel_menu *menu = container_of(browser,
342032dcd021SJiri Olsa 						    struct evsel_menu, b);
3421b27c4eceSJiri Olsa 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
34224ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3423aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
34240f0abbacSNamhyung Kim 	unsigned long nr_events = hists->stats.nr_samples;
34258ab2e96dSArnaldo Carvalho de Melo 	const char *ev_name = evsel__name(evsel);
3426aca7a94dSNamhyung Kim 	char bf[256], unit;
3427aca7a94dSNamhyung Kim 	const char *warn = " ";
3428aca7a94dSNamhyung Kim 	size_t printed;
3429aca7a94dSNamhyung Kim 
3430aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3431aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3432aca7a94dSNamhyung Kim 
3433c754c382SArnaldo Carvalho de Melo 	if (evsel__is_group_event(evsel)) {
343432dcd021SJiri Olsa 		struct evsel *pos;
3435717e263fSNamhyung Kim 
34368ab2e96dSArnaldo Carvalho de Melo 		ev_name = evsel__group_name(evsel);
3437717e263fSNamhyung Kim 
3438717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
34394ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
34400f0abbacSNamhyung Kim 			nr_events += pos_hists->stats.nr_samples;
3441717e263fSNamhyung Kim 		}
3442717e263fSNamhyung Kim 	}
3443717e263fSNamhyung Kim 
3444aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3445aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3446aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3447517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3448aca7a94dSNamhyung Kim 
34490f0abbacSNamhyung Kim 	nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3450aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3451aca7a94dSNamhyung Kim 		menu->lost_events = true;
3452aca7a94dSNamhyung Kim 		if (!current_entry)
3453aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3454aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3455aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3456aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3457aca7a94dSNamhyung Kim 		warn = bf;
3458aca7a94dSNamhyung Kim 	}
3459aca7a94dSNamhyung Kim 
346026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3461aca7a94dSNamhyung Kim 
3462aca7a94dSNamhyung Kim 	if (current_entry)
3463aca7a94dSNamhyung Kim 		menu->selection = evsel;
3464aca7a94dSNamhyung Kim }
3465aca7a94dSNamhyung Kim 
346632dcd021SJiri Olsa static int perf_evsel_menu__run(struct evsel_menu *menu,
3467aca7a94dSNamhyung Kim 				int nr_events, const char *help,
346806cc1a47SKan Liang 				struct hist_browser_timer *hbt,
346906cc1a47SKan Liang 				bool warn_lost_event)
3470aca7a94dSNamhyung Kim {
347163503dbaSJiri Olsa 	struct evlist *evlist = menu->b.priv;
347232dcd021SJiri Olsa 	struct evsel *pos;
3473dd00d486SJiri Olsa 	const char *title = "Available samples";
34749783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3475aca7a94dSNamhyung Kim 	int key;
3476aca7a94dSNamhyung Kim 
3477aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3478aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3479aca7a94dSNamhyung Kim 		return -1;
3480aca7a94dSNamhyung Kim 
3481aca7a94dSNamhyung Kim 	while (1) {
3482aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3483aca7a94dSNamhyung Kim 
3484aca7a94dSNamhyung Kim 		switch (key) {
3485aca7a94dSNamhyung Kim 		case K_TIMER:
3486ceb75476SLeo Yan 			if (hbt)
34879783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3488aca7a94dSNamhyung Kim 
348906cc1a47SKan Liang 			if (!menu->lost_events_warned &&
349006cc1a47SKan Liang 			    menu->lost_events &&
349106cc1a47SKan Liang 			    warn_lost_event) {
3492aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3493aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3494aca7a94dSNamhyung Kim 			}
3495aca7a94dSNamhyung Kim 			continue;
3496aca7a94dSNamhyung Kim 		case K_RIGHT:
3497aca7a94dSNamhyung Kim 		case K_ENTER:
3498aca7a94dSNamhyung Kim 			if (!menu->selection)
3499aca7a94dSNamhyung Kim 				continue;
3500aca7a94dSNamhyung Kim 			pos = menu->selection;
3501aca7a94dSNamhyung Kim browse_hists:
3502900c8eadSArnaldo Carvalho de Melo 			evlist__set_selected(evlist, pos);
3503aca7a94dSNamhyung Kim 			/*
3504aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3505aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3506aca7a94dSNamhyung Kim 			 */
35079783adf7SNamhyung Kim 			if (hbt)
35089783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
350956933029SArnaldo Carvalho de Melo 			key = evsel__hists_browse(pos, nr_events, help, true, hbt,
351056933029SArnaldo Carvalho de Melo 						  menu->min_pcnt, menu->env,
3511cd0cccbaSArnaldo Carvalho de Melo 						  warn_lost_event,
3512cd0cccbaSArnaldo Carvalho de Melo 						  menu->annotation_opts);
3513aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3514aca7a94dSNamhyung Kim 			switch (key) {
3515aca7a94dSNamhyung Kim 			case K_TAB:
3516ce9036a6SJiri Olsa 				if (pos->core.node.next == &evlist->core.entries)
3517515dbe48SJiri Olsa 					pos = evlist__first(evlist);
3518aca7a94dSNamhyung Kim 				else
3519e470daeaSArnaldo Carvalho de Melo 					pos = evsel__next(pos);
3520aca7a94dSNamhyung Kim 				goto browse_hists;
3521aca7a94dSNamhyung Kim 			case K_UNTAB:
3522ce9036a6SJiri Olsa 				if (pos->core.node.prev == &evlist->core.entries)
3523515dbe48SJiri Olsa 					pos = evlist__last(evlist);
3524aca7a94dSNamhyung Kim 				else
3525e470daeaSArnaldo Carvalho de Melo 					pos = evsel__prev(pos);
3526aca7a94dSNamhyung Kim 				goto browse_hists;
3527341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
35285e3b810aSJin Yao 			case K_RELOAD:
3529aca7a94dSNamhyung Kim 			case 'q':
3530aca7a94dSNamhyung Kim 			case CTRL('c'):
3531aca7a94dSNamhyung Kim 				goto out;
353263ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3533aca7a94dSNamhyung Kim 			default:
3534aca7a94dSNamhyung Kim 				continue;
3535aca7a94dSNamhyung Kim 			}
3536aca7a94dSNamhyung Kim 		case K_LEFT:
3537aca7a94dSNamhyung Kim 			continue;
3538aca7a94dSNamhyung Kim 		case K_ESC:
3539aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3540aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3541aca7a94dSNamhyung Kim 				continue;
3542aca7a94dSNamhyung Kim 			/* Fall thru */
3543aca7a94dSNamhyung Kim 		case 'q':
3544aca7a94dSNamhyung Kim 		case CTRL('c'):
3545aca7a94dSNamhyung Kim 			goto out;
3546aca7a94dSNamhyung Kim 		default:
3547aca7a94dSNamhyung Kim 			continue;
3548aca7a94dSNamhyung Kim 		}
3549aca7a94dSNamhyung Kim 	}
3550aca7a94dSNamhyung Kim 
3551aca7a94dSNamhyung Kim out:
3552aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3553aca7a94dSNamhyung Kim 	return key;
3554aca7a94dSNamhyung Kim }
3555aca7a94dSNamhyung Kim 
3556316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3557fc24d7c2SNamhyung Kim 				 void *entry)
3558fc24d7c2SNamhyung Kim {
3559b27c4eceSJiri Olsa 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3560fc24d7c2SNamhyung Kim 
3561c754c382SArnaldo Carvalho de Melo 	if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3562fc24d7c2SNamhyung Kim 		return true;
3563fc24d7c2SNamhyung Kim 
3564fc24d7c2SNamhyung Kim 	return false;
3565fc24d7c2SNamhyung Kim }
3566fc24d7c2SNamhyung Kim 
3567f4bd0b4aSArnaldo Carvalho de Melo static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3568f4bd0b4aSArnaldo Carvalho de Melo 				      struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3569f4bd0b4aSArnaldo Carvalho de Melo 				      bool warn_lost_event, struct annotation_options *annotation_opts)
3570aca7a94dSNamhyung Kim {
357132dcd021SJiri Olsa 	struct evsel *pos;
357232dcd021SJiri Olsa 	struct evsel_menu menu = {
3573aca7a94dSNamhyung Kim 		.b = {
3574ce9036a6SJiri Olsa 			.entries    = &evlist->core.entries,
3575aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3576aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3577aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3578fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3579fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3580aca7a94dSNamhyung Kim 			.priv	    = evlist,
3581aca7a94dSNamhyung Kim 		},
3582064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
358368d80758SNamhyung Kim 		.env = env,
3584cd0cccbaSArnaldo Carvalho de Melo 		.annotation_opts = annotation_opts,
3585aca7a94dSNamhyung Kim 	};
3586aca7a94dSNamhyung Kim 
3587aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3588aca7a94dSNamhyung Kim 
3589e5cadb93SArnaldo Carvalho de Melo 	evlist__for_each_entry(evlist, pos) {
35908ab2e96dSArnaldo Carvalho de Melo 		const char *ev_name = evsel__name(pos);
3591aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3592aca7a94dSNamhyung Kim 
3593aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3594aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3595aca7a94dSNamhyung Kim 	}
3596aca7a94dSNamhyung Kim 
359706cc1a47SKan Liang 	return perf_evsel_menu__run(&menu, nr_entries, help,
359806cc1a47SKan Liang 				    hbt, warn_lost_event);
3599aca7a94dSNamhyung Kim }
3600aca7a94dSNamhyung Kim 
3601e414fd1aSArnaldo Carvalho de Melo static bool evlist__single_entry(struct evlist *evlist)
3602bee9ca1cSArnaldo Carvalho de Melo {
3603bee9ca1cSArnaldo Carvalho de Melo 	int nr_entries = evlist->core.nr_entries;
3604bee9ca1cSArnaldo Carvalho de Melo 
3605bee9ca1cSArnaldo Carvalho de Melo 	if (nr_entries == 1)
3606bee9ca1cSArnaldo Carvalho de Melo 	       return true;
3607bee9ca1cSArnaldo Carvalho de Melo 
3608bee9ca1cSArnaldo Carvalho de Melo 	if (nr_entries == 2) {
3609bee9ca1cSArnaldo Carvalho de Melo 		struct evsel *last = evlist__last(evlist);
3610bee9ca1cSArnaldo Carvalho de Melo 
3611bee9ca1cSArnaldo Carvalho de Melo 		if (evsel__is_dummy_event(last))
3612bee9ca1cSArnaldo Carvalho de Melo 			return true;
3613bee9ca1cSArnaldo Carvalho de Melo 	}
3614bee9ca1cSArnaldo Carvalho de Melo 
3615bee9ca1cSArnaldo Carvalho de Melo 	return false;
3616bee9ca1cSArnaldo Carvalho de Melo }
3617bee9ca1cSArnaldo Carvalho de Melo 
3618f4bd0b4aSArnaldo Carvalho de Melo int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3619f4bd0b4aSArnaldo Carvalho de Melo 			     float min_pcnt, struct perf_env *env, bool warn_lost_event,
3620cd0cccbaSArnaldo Carvalho de Melo 			     struct annotation_options *annotation_opts)
3621aca7a94dSNamhyung Kim {
36226484d2f9SJiri Olsa 	int nr_entries = evlist->core.nr_entries;
3623fc24d7c2SNamhyung Kim 
3624e414fd1aSArnaldo Carvalho de Melo 	if (evlist__single_entry(evlist)) {
3625d4ccbacbSArnaldo Carvalho de Melo single_entry: {
3626515dbe48SJiri Olsa 		struct evsel *first = evlist__first(evlist);
3627fc24d7c2SNamhyung Kim 
362856933029SArnaldo Carvalho de Melo 		return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
362956933029SArnaldo Carvalho de Melo 					   env, warn_lost_event, annotation_opts);
3630aca7a94dSNamhyung Kim 	}
3631d4ccbacbSArnaldo Carvalho de Melo 	}
3632aca7a94dSNamhyung Kim 
3633fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
363432dcd021SJiri Olsa 		struct evsel *pos;
3635fc24d7c2SNamhyung Kim 
3636fc24d7c2SNamhyung Kim 		nr_entries = 0;
3637e5cadb93SArnaldo Carvalho de Melo 		evlist__for_each_entry(evlist, pos) {
3638c754c382SArnaldo Carvalho de Melo 			if (evsel__is_group_leader(pos))
3639fc24d7c2SNamhyung Kim 				nr_entries++;
36400050f7aaSArnaldo Carvalho de Melo 		}
3641fc24d7c2SNamhyung Kim 
3642fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3643fc24d7c2SNamhyung Kim 			goto single_entry;
3644fc24d7c2SNamhyung Kim 	}
3645fc24d7c2SNamhyung Kim 
3646f4bd0b4aSArnaldo Carvalho de Melo 	return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3647f4bd0b4aSArnaldo Carvalho de Melo 					  warn_lost_event, annotation_opts);
3648aca7a94dSNamhyung Kim }
36495cb456afSJin Yao 
36505cb456afSJin Yao static int block_hists_browser__title(struct hist_browser *browser, char *bf,
36515cb456afSJin Yao 				      size_t size)
36525cb456afSJin Yao {
36535cb456afSJin Yao 	struct hists *hists = evsel__hists(browser->block_evsel);
36548ab2e96dSArnaldo Carvalho de Melo 	const char *evname = evsel__name(browser->block_evsel);
36550f0abbacSNamhyung Kim 	unsigned long nr_samples = hists->stats.nr_samples;
36565cb456afSJin Yao 	int ret;
36575cb456afSJin Yao 
36585cb456afSJin Yao 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
36595cb456afSJin Yao 	if (evname)
36605cb456afSJin Yao 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
36615cb456afSJin Yao 
36625cb456afSJin Yao 	return 0;
36635cb456afSJin Yao }
36645cb456afSJin Yao 
36655cb456afSJin Yao int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3666848a5e50SJin Yao 			   float min_percent, struct perf_env *env,
3667848a5e50SJin Yao 			   struct annotation_options *annotation_opts)
36685cb456afSJin Yao {
36695cb456afSJin Yao 	struct hists *hists = &bh->block_hists;
36705cb456afSJin Yao 	struct hist_browser *browser;
36715cb456afSJin Yao 	int key = -1;
3672848a5e50SJin Yao 	struct popup_action action;
36735cb456afSJin Yao 	static const char help[] =
36745cb456afSJin Yao 	" q             Quit \n";
36755cb456afSJin Yao 
36765cb456afSJin Yao 	browser = hist_browser__new(hists);
36775cb456afSJin Yao 	if (!browser)
36785cb456afSJin Yao 		return -1;
36795cb456afSJin Yao 
36805cb456afSJin Yao 	browser->block_evsel = evsel;
36815cb456afSJin Yao 	browser->title = block_hists_browser__title;
36825cb456afSJin Yao 	browser->min_pcnt = min_percent;
3683848a5e50SJin Yao 	browser->env = env;
3684848a5e50SJin Yao 	browser->annotation_opts = annotation_opts;
36855cb456afSJin Yao 
36865cb456afSJin Yao 	/* reset abort key so that it can get Ctrl-C as a key */
36875cb456afSJin Yao 	SLang_reset_tty();
36885cb456afSJin Yao 	SLang_init_tty(0, 0, 0);
36895cb456afSJin Yao 
3690848a5e50SJin Yao 	memset(&action, 0, sizeof(action));
3691848a5e50SJin Yao 
36925cb456afSJin Yao 	while (1) {
3693d10ec006SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, "? - help", true, 0);
36945cb456afSJin Yao 
36955cb456afSJin Yao 		switch (key) {
36965cb456afSJin Yao 		case 'q':
36975cb456afSJin Yao 			goto out;
36985cb456afSJin Yao 		case '?':
36995cb456afSJin Yao 			ui_browser__help_window(&browser->b, help);
37005cb456afSJin Yao 			break;
3701848a5e50SJin Yao 		case 'a':
3702848a5e50SJin Yao 		case K_ENTER:
3703848a5e50SJin Yao 			if (!browser->selection ||
3704848a5e50SJin Yao 			    !browser->selection->sym) {
3705848a5e50SJin Yao 				continue;
3706848a5e50SJin Yao 			}
3707848a5e50SJin Yao 
3708848a5e50SJin Yao 			action.ms.map = browser->selection->map;
3709848a5e50SJin Yao 			action.ms.sym = browser->selection->sym;
3710848a5e50SJin Yao 			do_annotate(browser, &action);
3711848a5e50SJin Yao 			continue;
37125cb456afSJin Yao 		default:
37135cb456afSJin Yao 			break;
37145cb456afSJin Yao 		}
37155cb456afSJin Yao 	}
37165cb456afSJin Yao 
37175cb456afSJin Yao out:
37185cb456afSJin Yao 	hist_browser__delete(browser);
37195cb456afSJin Yao 	return 0;
37205cb456afSJin Yao }
3721