xref: /linux/tools/perf/ui/browsers/hists.c (revision ea537f22f6e5b3e2026fc58419cc937d833b1a61)
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
120025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
121025bf7eaSArnaldo Carvalho de Melo 	 */
122025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
123025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
124025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
125025bf7eaSArnaldo Carvalho de Melo }
126025bf7eaSArnaldo Carvalho de Melo 
127357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
128aca7a94dSNamhyung Kim {
129357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
130357cfff1SArnaldo Carvalho de Melo 
131aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
132357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
133357cfff1SArnaldo Carvalho de Melo 	/*
134357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
135357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
136357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
137357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
138357cfff1SArnaldo Carvalho de Melo  	 */
139357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
140ca3ff33bSArnaldo Carvalho de Melo }
141ca3ff33bSArnaldo Carvalho de Melo 
14205e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
143aca7a94dSNamhyung Kim {
144c3b78952SNamhyung Kim 	/*
145c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
146c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
147c3b78952SNamhyung Kim 	 */
148c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
149c3b78952SNamhyung Kim 
150268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
151c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
152357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
15305e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
154aca7a94dSNamhyung Kim }
155aca7a94dSNamhyung Kim 
156aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
157aca7a94dSNamhyung Kim {
158aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
159aca7a94dSNamhyung Kim }
160aca7a94dSNamhyung Kim 
16105e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
162aca7a94dSNamhyung Kim {
1633698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
164aca7a94dSNamhyung Kim }
165aca7a94dSNamhyung Kim 
16605e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
167aca7a94dSNamhyung Kim {
1683698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
169aca7a94dSNamhyung Kim }
170aca7a94dSNamhyung Kim 
1713698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
172aca7a94dSNamhyung Kim {
1733698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
174aca7a94dSNamhyung Kim }
175aca7a94dSNamhyung Kim 
17605e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
177aca7a94dSNamhyung Kim {
1782a704fc8SMilian Wolff 	int n = 0;
179aca7a94dSNamhyung Kim 	struct rb_node *nd;
180aca7a94dSNamhyung Kim 
18105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
182aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
183aca7a94dSNamhyung Kim 		struct callchain_list *chain;
184aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
185aca7a94dSNamhyung Kim 
186aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
187aca7a94dSNamhyung Kim 			++n;
1880d3eb0b7SJin Yao 
189aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
190aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
191aca7a94dSNamhyung Kim 			if (folded_sign == '+')
192aca7a94dSNamhyung Kim 				break;
193aca7a94dSNamhyung Kim 		}
194aca7a94dSNamhyung Kim 
195aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
196aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
197aca7a94dSNamhyung Kim 	}
198aca7a94dSNamhyung Kim 
199aca7a94dSNamhyung Kim 	return n;
200aca7a94dSNamhyung Kim }
201aca7a94dSNamhyung Kim 
2024b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
2034b3a3212SNamhyung Kim {
2044b3a3212SNamhyung Kim 	struct callchain_list *chain;
2054b3a3212SNamhyung Kim 	char folded_sign = 0;
2064b3a3212SNamhyung Kim 	int n = 0;
2074b3a3212SNamhyung Kim 
2084b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
2094b3a3212SNamhyung Kim 		if (!folded_sign) {
2104b3a3212SNamhyung Kim 			/* only check first chain list entry */
2114b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2124b3a3212SNamhyung Kim 			if (folded_sign == '+')
2134b3a3212SNamhyung Kim 				return 1;
2144b3a3212SNamhyung Kim 		}
2154b3a3212SNamhyung Kim 		n++;
2164b3a3212SNamhyung Kim 	}
2174b3a3212SNamhyung Kim 
2184b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
2194b3a3212SNamhyung Kim 		if (!folded_sign) {
2204b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
2214b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2224b3a3212SNamhyung Kim 			if (folded_sign == '+')
2234b3a3212SNamhyung Kim 				return 1;
2244b3a3212SNamhyung Kim 		}
2254b3a3212SNamhyung Kim 		n++;
2264b3a3212SNamhyung Kim 	}
2274b3a3212SNamhyung Kim 
2284b3a3212SNamhyung Kim 	return n;
2294b3a3212SNamhyung Kim }
2304b3a3212SNamhyung Kim 
2318c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
2328c430a34SNamhyung Kim {
2338c430a34SNamhyung Kim 	return 1;
2348c430a34SNamhyung Kim }
2358c430a34SNamhyung Kim 
236aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
237aca7a94dSNamhyung Kim {
238aca7a94dSNamhyung Kim 	struct callchain_list *chain;
239aca7a94dSNamhyung Kim 	bool unfolded = false;
2402a704fc8SMilian Wolff 	int n = 0;
241aca7a94dSNamhyung Kim 
2424b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2434b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2448c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2458c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2464b3a3212SNamhyung Kim 
247aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
248aca7a94dSNamhyung Kim 		++n;
2490d3eb0b7SJin Yao 
2503698dab1SNamhyung Kim 		unfolded = chain->unfolded;
251aca7a94dSNamhyung Kim 	}
252aca7a94dSNamhyung Kim 
253aca7a94dSNamhyung Kim 	if (unfolded)
254aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
255aca7a94dSNamhyung Kim 
256aca7a94dSNamhyung Kim 	return n;
257aca7a94dSNamhyung Kim }
258aca7a94dSNamhyung Kim 
259aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
260aca7a94dSNamhyung Kim {
261aca7a94dSNamhyung Kim 	struct rb_node *nd;
262aca7a94dSNamhyung Kim 	int n = 0;
263aca7a94dSNamhyung Kim 
264aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
265aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
266aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
267aca7a94dSNamhyung Kim 	}
268aca7a94dSNamhyung Kim 
269aca7a94dSNamhyung Kim 	return n;
270aca7a94dSNamhyung Kim }
271aca7a94dSNamhyung Kim 
272f5b763feSNamhyung Kim static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
273f5b763feSNamhyung Kim 				bool include_children)
274f5b763feSNamhyung Kim {
275f5b763feSNamhyung Kim 	int count = 0;
276f5b763feSNamhyung Kim 	struct rb_node *node;
277f5b763feSNamhyung Kim 	struct hist_entry *child;
278f5b763feSNamhyung Kim 
279f5b763feSNamhyung Kim 	if (he->leaf)
280f5b763feSNamhyung Kim 		return callchain__count_rows(&he->sorted_chain);
281f5b763feSNamhyung Kim 
28279dded87SNamhyung Kim 	if (he->has_no_entry)
28379dded87SNamhyung Kim 		return 1;
28479dded87SNamhyung Kim 
2852eb3d689SDavidlohr Bueso 	node = rb_first_cached(&he->hroot_out);
286f5b763feSNamhyung Kim 	while (node) {
287f5b763feSNamhyung Kim 		float percent;
288f5b763feSNamhyung Kim 
289f5b763feSNamhyung Kim 		child = rb_entry(node, struct hist_entry, rb_node);
290f5b763feSNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
291f5b763feSNamhyung Kim 
292f5b763feSNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt) {
293f5b763feSNamhyung Kim 			count++;
294f5b763feSNamhyung Kim 
295f5b763feSNamhyung Kim 			if (include_children && child->unfolded)
296f5b763feSNamhyung Kim 				count += hierarchy_count_rows(hb, child, true);
297f5b763feSNamhyung Kim 		}
298f5b763feSNamhyung Kim 
299f5b763feSNamhyung Kim 		node = rb_next(node);
300f5b763feSNamhyung Kim 	}
301f5b763feSNamhyung Kim 	return count;
302f5b763feSNamhyung Kim }
303f5b763feSNamhyung Kim 
3043698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
305aca7a94dSNamhyung Kim {
3063698dab1SNamhyung Kim 	if (!he)
307aca7a94dSNamhyung Kim 		return false;
308aca7a94dSNamhyung Kim 
3093698dab1SNamhyung Kim 	if (!he->has_children)
310aca7a94dSNamhyung Kim 		return false;
311aca7a94dSNamhyung Kim 
3123698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
3133698dab1SNamhyung Kim 	return true;
3143698dab1SNamhyung Kim }
3153698dab1SNamhyung Kim 
3163698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
3173698dab1SNamhyung Kim {
3183698dab1SNamhyung Kim 	if (!cl)
3193698dab1SNamhyung Kim 		return false;
3203698dab1SNamhyung Kim 
3213698dab1SNamhyung Kim 	if (!cl->has_children)
3223698dab1SNamhyung Kim 		return false;
3233698dab1SNamhyung Kim 
3243698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
325aca7a94dSNamhyung Kim 	return true;
326aca7a94dSNamhyung Kim }
327aca7a94dSNamhyung Kim 
32805e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
329aca7a94dSNamhyung Kim {
33005e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
331aca7a94dSNamhyung Kim 
33205e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
333aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
334aca7a94dSNamhyung Kim 		struct callchain_list *chain;
335aca7a94dSNamhyung Kim 		bool first = true;
336aca7a94dSNamhyung Kim 
337aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
338aca7a94dSNamhyung Kim 			if (first) {
339aca7a94dSNamhyung Kim 				first = false;
3403698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
341aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
342aca7a94dSNamhyung Kim 			} else
3433698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
344aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
345aca7a94dSNamhyung Kim 		}
346aca7a94dSNamhyung Kim 
347aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
348aca7a94dSNamhyung Kim 	}
349aca7a94dSNamhyung Kim }
350aca7a94dSNamhyung Kim 
351a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
352a7444af6SNamhyung Kim 					       bool has_sibling)
353aca7a94dSNamhyung Kim {
354aca7a94dSNamhyung Kim 	struct callchain_list *chain;
355aca7a94dSNamhyung Kim 
356a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3573698dab1SNamhyung Kim 	chain->has_children = has_sibling;
358a7444af6SNamhyung Kim 
35990989035SAndres Freund 	if (!list_empty(&node->val)) {
36082162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3613698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
36282162b5aSNamhyung Kim 	}
363aca7a94dSNamhyung Kim 
36405e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
365aca7a94dSNamhyung Kim }
366aca7a94dSNamhyung Kim 
36705e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
368aca7a94dSNamhyung Kim {
369a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
370a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
371aca7a94dSNamhyung Kim 
37205e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
373aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3758c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3768c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3774b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
378aca7a94dSNamhyung Kim 	}
379aca7a94dSNamhyung Kim }
380aca7a94dSNamhyung Kim 
38105e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
382aca7a94dSNamhyung Kim {
383f5b763feSNamhyung Kim 	if (he->init_have_children)
384f5b763feSNamhyung Kim 		return;
385f5b763feSNamhyung Kim 
386f5b763feSNamhyung Kim 	if (he->leaf) {
3873698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
38805e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
389f5b763feSNamhyung Kim 	} else {
3902eb3d689SDavidlohr Bueso 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
391aca7a94dSNamhyung Kim 	}
392f5b763feSNamhyung Kim 
393f5b763feSNamhyung Kim 	he->init_have_children = true;
394aca7a94dSNamhyung Kim }
395aca7a94dSNamhyung Kim 
396bdc633feSArnaldo Carvalho de Melo static bool hist_browser__selection_has_children(struct hist_browser *browser)
397bdc633feSArnaldo Carvalho de Melo {
398bdc633feSArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
399bdc633feSArnaldo Carvalho de Melo 	struct map_symbol *ms = browser->selection;
400bdc633feSArnaldo Carvalho de Melo 
401bdc633feSArnaldo Carvalho de Melo 	if (!he || !ms)
402bdc633feSArnaldo Carvalho de Melo 		return false;
403bdc633feSArnaldo Carvalho de Melo 
404bdc633feSArnaldo Carvalho de Melo 	if (ms == &he->ms)
405bdc633feSArnaldo Carvalho de Melo 	       return he->has_children;
406bdc633feSArnaldo Carvalho de Melo 
407bdc633feSArnaldo Carvalho de Melo 	return container_of(ms, struct callchain_list, ms)->has_children;
408bdc633feSArnaldo Carvalho de Melo }
409bdc633feSArnaldo Carvalho de Melo 
410bdc633feSArnaldo Carvalho de Melo static bool hist_browser__selection_unfolded(struct hist_browser *browser)
411bdc633feSArnaldo Carvalho de Melo {
412bdc633feSArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
413bdc633feSArnaldo Carvalho de Melo 	struct map_symbol *ms = browser->selection;
414bdc633feSArnaldo Carvalho de Melo 
415bdc633feSArnaldo Carvalho de Melo 	if (!he || !ms)
416bdc633feSArnaldo Carvalho de Melo 		return false;
417bdc633feSArnaldo Carvalho de Melo 
418bdc633feSArnaldo Carvalho de Melo 	if (ms == &he->ms)
419bdc633feSArnaldo Carvalho de Melo 	       return he->unfolded;
420bdc633feSArnaldo Carvalho de Melo 
421bdc633feSArnaldo Carvalho de Melo 	return container_of(ms, struct callchain_list, ms)->unfolded;
422bdc633feSArnaldo Carvalho de Melo }
423bdc633feSArnaldo Carvalho de Melo 
424bdc633feSArnaldo Carvalho de Melo static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
425bdc633feSArnaldo Carvalho de Melo {
426bdc633feSArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
427bdc633feSArnaldo Carvalho de Melo 	struct map_symbol *ms = browser->selection;
428bdc633feSArnaldo Carvalho de Melo 	struct callchain_list *callchain_entry;
429bdc633feSArnaldo Carvalho de Melo 
430bdc633feSArnaldo Carvalho de Melo 	if (!he || !ms)
431bdc633feSArnaldo Carvalho de Melo 		return NULL;
432bdc633feSArnaldo Carvalho de Melo 
433bdc633feSArnaldo Carvalho de Melo 	if (ms == &he->ms) {
434bdc633feSArnaldo Carvalho de Melo 	       hist_entry__sym_snprintf(he, bf, size, 0);
435bdc633feSArnaldo Carvalho de Melo 	       return bf + 4; // skip the level, e.g. '[k] '
436bdc633feSArnaldo Carvalho de Melo 	}
437bdc633feSArnaldo Carvalho de Melo 
438bdc633feSArnaldo Carvalho de Melo 	callchain_entry = container_of(ms, struct callchain_list, ms);
439bdc633feSArnaldo Carvalho de Melo 	return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
440bdc633feSArnaldo Carvalho de Melo }
441bdc633feSArnaldo Carvalho de Melo 
44205e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
443aca7a94dSNamhyung Kim {
44405e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
4453698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
4463698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
4473698dab1SNamhyung Kim 	bool has_children;
448aca7a94dSNamhyung Kim 
4494938cf0cSWang Nan 	if (!he || !ms)
4504938cf0cSWang Nan 		return false;
4514938cf0cSWang Nan 
4523698dab1SNamhyung Kim 	if (ms == &he->ms)
4533698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
4543698dab1SNamhyung Kim 	else
4553698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
4563698dab1SNamhyung Kim 
4573698dab1SNamhyung Kim 	if (has_children) {
458f5b763feSNamhyung Kim 		int child_rows = 0;
459f5b763feSNamhyung Kim 
460aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
461c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
462aca7a94dSNamhyung Kim 
463f5b763feSNamhyung Kim 		if (he->leaf)
464f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
465f5b763feSNamhyung Kim 		else
466f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
467f5b763feSNamhyung Kim 
468f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
469f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
470f5b763feSNamhyung Kim 
471f5b763feSNamhyung Kim 		if (he->unfolded) {
472f5b763feSNamhyung Kim 			if (he->leaf)
4730d3eb0b7SJin Yao 				he->nr_rows = callchain__count_rows(
4740d3eb0b7SJin Yao 						&he->sorted_chain);
475aca7a94dSNamhyung Kim 			else
476f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
477f5b763feSNamhyung Kim 
478f5b763feSNamhyung Kim 			/* account grand children */
479f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
480f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
48179dded87SNamhyung Kim 
48279dded87SNamhyung Kim 			if (!he->leaf && he->nr_rows == 0) {
48379dded87SNamhyung Kim 				he->has_no_entry = true;
48479dded87SNamhyung Kim 				he->nr_rows = 1;
48579dded87SNamhyung Kim 			}
486f5b763feSNamhyung Kim 		} else {
487f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
488f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
489f5b763feSNamhyung Kim 
49079dded87SNamhyung Kim 			if (he->has_no_entry)
49179dded87SNamhyung Kim 				he->has_no_entry = false;
49279dded87SNamhyung Kim 
493aca7a94dSNamhyung Kim 			he->nr_rows = 0;
494f5b763feSNamhyung Kim 		}
495c3b78952SNamhyung Kim 
496c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
497f5b763feSNamhyung Kim 
498f5b763feSNamhyung Kim 		if (he->leaf)
499c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
500f5b763feSNamhyung Kim 		else
501f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
502aca7a94dSNamhyung Kim 
503aca7a94dSNamhyung Kim 		return true;
504aca7a94dSNamhyung Kim 	}
505aca7a94dSNamhyung Kim 
506aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
507aca7a94dSNamhyung Kim 	return false;
508aca7a94dSNamhyung Kim }
509aca7a94dSNamhyung Kim 
51005e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
511aca7a94dSNamhyung Kim {
512aca7a94dSNamhyung Kim 	int n = 0;
513aca7a94dSNamhyung Kim 	struct rb_node *nd;
514aca7a94dSNamhyung Kim 
51505e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
516aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
517aca7a94dSNamhyung Kim 		struct callchain_list *chain;
518aca7a94dSNamhyung Kim 		bool has_children = false;
519aca7a94dSNamhyung Kim 
520aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
521aca7a94dSNamhyung Kim 			++n;
5223698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
5233698dab1SNamhyung Kim 			has_children = chain->has_children;
524aca7a94dSNamhyung Kim 		}
525aca7a94dSNamhyung Kim 
526aca7a94dSNamhyung Kim 		if (has_children)
527aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
528aca7a94dSNamhyung Kim 	}
529aca7a94dSNamhyung Kim 
530aca7a94dSNamhyung Kim 	return n;
531aca7a94dSNamhyung Kim }
532aca7a94dSNamhyung Kim 
533aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
534aca7a94dSNamhyung Kim {
535aca7a94dSNamhyung Kim 	struct callchain_list *chain;
536aca7a94dSNamhyung Kim 	bool has_children = false;
537aca7a94dSNamhyung Kim 	int n = 0;
538aca7a94dSNamhyung Kim 
539aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
540aca7a94dSNamhyung Kim 		++n;
5413698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
5423698dab1SNamhyung Kim 		has_children = chain->has_children;
543aca7a94dSNamhyung Kim 	}
544aca7a94dSNamhyung Kim 
545aca7a94dSNamhyung Kim 	if (has_children)
546aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
547aca7a94dSNamhyung Kim 
548aca7a94dSNamhyung Kim 	return n;
549aca7a94dSNamhyung Kim }
550aca7a94dSNamhyung Kim 
551aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
552aca7a94dSNamhyung Kim {
553aca7a94dSNamhyung Kim 	struct rb_node *nd;
554aca7a94dSNamhyung Kim 	int n = 0;
555aca7a94dSNamhyung Kim 
556aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
557aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
558aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
559aca7a94dSNamhyung Kim 	}
560aca7a94dSNamhyung Kim 
561aca7a94dSNamhyung Kim 	return n;
562aca7a94dSNamhyung Kim }
563aca7a94dSNamhyung Kim 
564492b1010SNamhyung Kim static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
565492b1010SNamhyung Kim 				 bool unfold __maybe_unused)
566492b1010SNamhyung Kim {
567492b1010SNamhyung Kim 	float percent;
568492b1010SNamhyung Kim 	struct rb_node *nd;
569492b1010SNamhyung Kim 	struct hist_entry *child;
570492b1010SNamhyung Kim 	int n = 0;
571492b1010SNamhyung Kim 
5722eb3d689SDavidlohr Bueso 	for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
573492b1010SNamhyung Kim 		child = rb_entry(nd, struct hist_entry, rb_node);
574492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
575492b1010SNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt)
576492b1010SNamhyung Kim 			n++;
577492b1010SNamhyung Kim 	}
578492b1010SNamhyung Kim 
579492b1010SNamhyung Kim 	return n;
580492b1010SNamhyung Kim }
581492b1010SNamhyung Kim 
582b33f9226SJiri Olsa static void __hist_entry__set_folding(struct hist_entry *he,
583492b1010SNamhyung Kim 				      struct hist_browser *hb, bool unfold)
584aca7a94dSNamhyung Kim {
58505e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
5863698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
587aca7a94dSNamhyung Kim 
5883698dab1SNamhyung Kim 	if (he->has_children) {
589492b1010SNamhyung Kim 		int n;
590492b1010SNamhyung Kim 
591492b1010SNamhyung Kim 		if (he->leaf)
592492b1010SNamhyung Kim 			n = callchain__set_folding(&he->sorted_chain, unfold);
593492b1010SNamhyung Kim 		else
594492b1010SNamhyung Kim 			n = hierarchy_set_folding(hb, he, unfold);
595492b1010SNamhyung Kim 
59605e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
597aca7a94dSNamhyung Kim 	} else
59805e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
599aca7a94dSNamhyung Kim }
600aca7a94dSNamhyung Kim 
601b33f9226SJiri Olsa static void hist_entry__set_folding(struct hist_entry *he,
602b33f9226SJiri Olsa 				    struct hist_browser *browser, bool unfold)
603aca7a94dSNamhyung Kim {
604492b1010SNamhyung Kim 	double percent;
605aca7a94dSNamhyung Kim 
606492b1010SNamhyung Kim 	percent = hist_entry__get_percent_limit(he);
607492b1010SNamhyung Kim 	if (he->filtered || percent < browser->min_pcnt)
608b33f9226SJiri Olsa 		return;
609b33f9226SJiri Olsa 
610b33f9226SJiri Olsa 	__hist_entry__set_folding(he, browser, unfold);
611492b1010SNamhyung Kim 
612492b1010SNamhyung Kim 	if (!he->depth || unfold)
613492b1010SNamhyung Kim 		browser->nr_hierarchy_entries++;
614492b1010SNamhyung Kim 	if (he->leaf)
615c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
61679dded87SNamhyung Kim 	else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
61779dded87SNamhyung Kim 		browser->nr_hierarchy_entries++;
61879dded87SNamhyung Kim 		he->has_no_entry = true;
61979dded87SNamhyung Kim 		he->nr_rows = 1;
62079dded87SNamhyung Kim 	} else
62179dded87SNamhyung Kim 		he->has_no_entry = false;
622aca7a94dSNamhyung Kim }
623b33f9226SJiri Olsa 
624b33f9226SJiri Olsa static void
625b33f9226SJiri Olsa __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
626b33f9226SJiri Olsa {
627b33f9226SJiri Olsa 	struct rb_node *nd;
628b33f9226SJiri Olsa 	struct hist_entry *he;
629b33f9226SJiri Olsa 
6302eb3d689SDavidlohr Bueso 	nd = rb_first_cached(&browser->hists->entries);
631b33f9226SJiri Olsa 	while (nd) {
632b33f9226SJiri Olsa 		he = rb_entry(nd, struct hist_entry, rb_node);
633b33f9226SJiri Olsa 
634b33f9226SJiri Olsa 		/* set folding state even if it's currently folded */
635b33f9226SJiri Olsa 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
636b33f9226SJiri Olsa 
637b33f9226SJiri Olsa 		hist_entry__set_folding(he, browser, unfold);
638b33f9226SJiri Olsa 	}
639aca7a94dSNamhyung Kim }
640aca7a94dSNamhyung Kim 
64105e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
642aca7a94dSNamhyung Kim {
643492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
644c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
645c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
646c3b78952SNamhyung Kim 
647c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
648aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
64905e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
650aca7a94dSNamhyung Kim }
651aca7a94dSNamhyung Kim 
6520e3fa7a7SJiri Olsa static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
6530e3fa7a7SJiri Olsa {
6540e3fa7a7SJiri Olsa 	if (!browser->he_selection)
6550e3fa7a7SJiri Olsa 		return;
6560e3fa7a7SJiri Olsa 
6570e3fa7a7SJiri Olsa 	hist_entry__set_folding(browser->he_selection, browser, unfold);
6580e3fa7a7SJiri Olsa 	browser->b.nr_entries = hist_browser__nr_entries(browser);
6590e3fa7a7SJiri Olsa }
6600e3fa7a7SJiri Olsa 
661aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
662aca7a94dSNamhyung Kim {
663aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
664aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
665aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
666aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
667aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
668aca7a94dSNamhyung Kim }
669aca7a94dSNamhyung Kim 
6705b91a86fSJiri Olsa static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
6715b91a86fSJiri Olsa {
6725b91a86fSJiri Olsa 	return browser->title ? browser->title(browser, bf, size) : 0;
6735b91a86fSJiri Olsa }
6745b91a86fSJiri Olsa 
675d10ec006SArnaldo Carvalho de Melo static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, int key)
676aca7a94dSNamhyung Kim {
677aca7a94dSNamhyung Kim 	switch (key) {
678fa5df943SNamhyung Kim 	case K_TIMER: {
679d10ec006SArnaldo Carvalho de Melo 		struct hist_browser_timer *hbt = browser->hbt;
680fa5df943SNamhyung Kim 		u64 nr_entries;
681ceb75476SLeo Yan 
682ceb75476SLeo Yan 		WARN_ON_ONCE(!hbt);
683ceb75476SLeo Yan 
684ceb75476SLeo Yan 		if (hbt)
6859783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
686fa5df943SNamhyung Kim 
687d10ec006SArnaldo Carvalho de Melo 		if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
688112f761fSNamhyung Kim 			hist_browser__update_nr_entries(browser);
689fa5df943SNamhyung Kim 
690c3b78952SNamhyung Kim 		nr_entries = hist_browser__nr_entries(browser);
691fa5df943SNamhyung Kim 		ui_browser__update_nr_entries(&browser->b, nr_entries);
692aca7a94dSNamhyung Kim 
69306cc1a47SKan Liang 		if (warn_lost_event &&
69406cc1a47SKan Liang 		    (browser->hists->stats.nr_lost_warned !=
69506cc1a47SKan Liang 		    browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
69605e8b080SArnaldo Carvalho de Melo 			browser->hists->stats.nr_lost_warned =
69705e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_events[PERF_RECORD_LOST];
69805e8b080SArnaldo Carvalho de Melo 			ui_browser__warn_lost_events(&browser->b);
699aca7a94dSNamhyung Kim 		}
700aca7a94dSNamhyung Kim 
7015b91a86fSJiri Olsa 		hist_browser__title(browser, title, sizeof(title));
70205e8b080SArnaldo Carvalho de Melo 		ui_browser__show_title(&browser->b, title);
703d10ec006SArnaldo Carvalho de Melo 		break;
704fa5df943SNamhyung Kim 	}
705aca7a94dSNamhyung Kim 	case 'D': { /* Debug */
706d10ec006SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
707aca7a94dSNamhyung Kim 		static int seq;
708d10ec006SArnaldo Carvalho de Melo 
709aca7a94dSNamhyung Kim 		ui_helpline__pop();
710fdae6400SArnaldo 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",
711d10ec006SArnaldo Carvalho de Melo 				   seq++, browser->b.nr_entries, browser->hists->nr_entries,
712d10ec006SArnaldo Carvalho de Melo 				   browser->b.extra_title_lines, browser->b.rows,
713d10ec006SArnaldo Carvalho de Melo 				   browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
714aca7a94dSNamhyung Kim 	}
715aca7a94dSNamhyung Kim 		break;
716aca7a94dSNamhyung Kim 	case 'C':
717aca7a94dSNamhyung Kim 		/* Collapse the whole world. */
71805e8b080SArnaldo Carvalho de Melo 		hist_browser__set_folding(browser, false);
719aca7a94dSNamhyung Kim 		break;
7200e3fa7a7SJiri Olsa 	case 'c':
7210e3fa7a7SJiri Olsa 		/* Collapse the selected entry. */
7220e3fa7a7SJiri Olsa 		hist_browser__set_folding_selected(browser, false);
7230e3fa7a7SJiri Olsa 		break;
724aca7a94dSNamhyung Kim 	case 'E':
725aca7a94dSNamhyung Kim 		/* Expand the whole world. */
72605e8b080SArnaldo Carvalho de Melo 		hist_browser__set_folding(browser, true);
727aca7a94dSNamhyung Kim 		break;
7280e3fa7a7SJiri Olsa 	case 'e':
7290e3fa7a7SJiri Olsa 		/* Expand the selected entry. */
7300e3fa7a7SJiri Olsa 		hist_browser__set_folding_selected(browser, true);
7310e3fa7a7SJiri Olsa 		break;
732025bf7eaSArnaldo Carvalho de Melo 	case 'H':
733025bf7eaSArnaldo Carvalho de Melo 		browser->show_headers = !browser->show_headers;
734025bf7eaSArnaldo Carvalho de Melo 		hist_browser__update_rows(browser);
735025bf7eaSArnaldo Carvalho de Melo 		break;
7369218a913SArnaldo Carvalho de Melo 	case '+':
73705e8b080SArnaldo Carvalho de Melo 		if (hist_browser__toggle_fold(browser))
738aca7a94dSNamhyung Kim 			break;
739aca7a94dSNamhyung Kim 		/* fall thru */
740aca7a94dSNamhyung Kim 	default:
741d10ec006SArnaldo Carvalho de Melo 		return -1;
742aca7a94dSNamhyung Kim 	}
743d10ec006SArnaldo Carvalho de Melo 
744d10ec006SArnaldo Carvalho de Melo 	return 0;
745d10ec006SArnaldo Carvalho de Melo }
746d10ec006SArnaldo Carvalho de Melo 
747d10ec006SArnaldo Carvalho de Melo int hist_browser__run(struct hist_browser *browser, const char *help,
748d10ec006SArnaldo Carvalho de Melo 		      bool warn_lost_event, int key)
749d10ec006SArnaldo Carvalho de Melo {
750d10ec006SArnaldo Carvalho de Melo 	char title[160];
751d10ec006SArnaldo Carvalho de Melo 	struct hist_browser_timer *hbt = browser->hbt;
752d10ec006SArnaldo Carvalho de Melo 	int delay_secs = hbt ? hbt->refresh : 0;
753d10ec006SArnaldo Carvalho de Melo 
754d10ec006SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
755d10ec006SArnaldo Carvalho de Melo 	browser->b.nr_entries = hist_browser__nr_entries(browser);
756d10ec006SArnaldo Carvalho de Melo 
757d10ec006SArnaldo Carvalho de Melo 	hist_browser__title(browser, title, sizeof(title));
758d10ec006SArnaldo Carvalho de Melo 
759d10ec006SArnaldo Carvalho de Melo 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
760d10ec006SArnaldo Carvalho de Melo 		return -1;
761d10ec006SArnaldo Carvalho de Melo 
762d10ec006SArnaldo Carvalho de Melo 	if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, key))
763d10ec006SArnaldo Carvalho de Melo 		goto out;
764d10ec006SArnaldo Carvalho de Melo 
765d10ec006SArnaldo Carvalho de Melo 	while (1) {
766d10ec006SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
767d10ec006SArnaldo Carvalho de Melo 
768d10ec006SArnaldo Carvalho de Melo 		if (hist_browser__handle_hotkey(browser, warn_lost_event, title, key))
769d10ec006SArnaldo Carvalho de Melo 			break;
770aca7a94dSNamhyung Kim 	}
771aca7a94dSNamhyung Kim out:
77205e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
773aca7a94dSNamhyung Kim 	return key;
774aca7a94dSNamhyung Kim }
775aca7a94dSNamhyung Kim 
77639ee533fSNamhyung Kim struct callchain_print_arg {
77739ee533fSNamhyung Kim 	/* for hists browser */
77839ee533fSNamhyung Kim 	off_t	row_offset;
77939ee533fSNamhyung Kim 	bool	is_current_entry;
78039ee533fSNamhyung Kim 
78139ee533fSNamhyung Kim 	/* for file dump */
78239ee533fSNamhyung Kim 	FILE	*fp;
78339ee533fSNamhyung Kim 	int	printed;
78439ee533fSNamhyung Kim };
78539ee533fSNamhyung Kim 
78639ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
78739ee533fSNamhyung Kim 					 struct callchain_list *chain,
78839ee533fSNamhyung Kim 					 const char *str, int offset,
78939ee533fSNamhyung Kim 					 unsigned short row,
79039ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
79139ee533fSNamhyung Kim 
792f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
793f4536dddSNamhyung Kim 					       struct callchain_list *chain,
79439ee533fSNamhyung Kim 					       const char *str, int offset,
79539ee533fSNamhyung Kim 					       unsigned short row,
79639ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
797f4536dddSNamhyung Kim {
798f4536dddSNamhyung Kim 	int color, width;
79939ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
80070e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
801f4536dddSNamhyung Kim 
802f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
803f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
804f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
805f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
806f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
80739ee533fSNamhyung Kim 		arg->is_current_entry = true;
808f4536dddSNamhyung Kim 	}
809f4536dddSNamhyung Kim 
810f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
811ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
81226270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
813517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
81470e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
81526270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
816f4536dddSNamhyung Kim }
817f4536dddSNamhyung Kim 
81839ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
81939ee533fSNamhyung Kim 						  struct callchain_list *chain,
82039ee533fSNamhyung Kim 						  const char *str, int offset,
82139ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
82239ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
82339ee533fSNamhyung Kim {
82439ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
82539ee533fSNamhyung Kim 
82639ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
82739ee533fSNamhyung Kim 				folded_sign, str);
82839ee533fSNamhyung Kim }
82939ee533fSNamhyung Kim 
83039ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
83139ee533fSNamhyung Kim 				     unsigned short row);
83239ee533fSNamhyung Kim 
83339ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
83439ee533fSNamhyung Kim 					    unsigned short row)
83539ee533fSNamhyung Kim {
83639ee533fSNamhyung Kim 	return browser->b.rows == row;
83739ee533fSNamhyung Kim }
83839ee533fSNamhyung Kim 
83939ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
84039ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
84139ee533fSNamhyung Kim {
84239ee533fSNamhyung Kim 	return false;
84339ee533fSNamhyung Kim }
84439ee533fSNamhyung Kim 
845aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
846aca7a94dSNamhyung Kim 
84718bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
84818bb8381SNamhyung Kim 					     struct callchain_node *node,
84918bb8381SNamhyung Kim 					     struct callchain_list *chain,
85018bb8381SNamhyung Kim 					     unsigned short row, u64 total,
85118bb8381SNamhyung Kim 					     bool need_percent, int offset,
85218bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
85318bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
85418bb8381SNamhyung Kim {
85518bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
856fef51ecdSJin Yao 	char buf[64], *alloc_str2;
85718bb8381SNamhyung Kim 	const char *str;
8582a704fc8SMilian Wolff 	int ret = 1;
85918bb8381SNamhyung Kim 
86018bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
86118bb8381SNamhyung Kim 		arg->row_offset--;
86218bb8381SNamhyung Kim 		return 0;
86318bb8381SNamhyung Kim 	}
86418bb8381SNamhyung Kim 
86518bb8381SNamhyung Kim 	alloc_str = NULL;
866fef51ecdSJin Yao 	alloc_str2 = NULL;
867fef51ecdSJin Yao 
86818bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
86918bb8381SNamhyung Kim 				       browser->show_dso);
87018bb8381SNamhyung Kim 
871fef51ecdSJin Yao 	if (symbol_conf.show_branchflag_count) {
872c4ee0625SJin Yao 		callchain_list_counts__printf_value(chain, NULL,
873fef51ecdSJin Yao 						    buf, sizeof(buf));
87418bb8381SNamhyung Kim 
875fef51ecdSJin Yao 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
876fef51ecdSJin Yao 			str = "Not enough memory!";
877fef51ecdSJin Yao 		else
878fef51ecdSJin Yao 			str = alloc_str2;
879fef51ecdSJin Yao 	}
880fef51ecdSJin Yao 
881fef51ecdSJin Yao 	if (need_percent) {
88218bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
88318bb8381SNamhyung Kim 						total);
88418bb8381SNamhyung Kim 
88518bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
88618bb8381SNamhyung Kim 			str = "Not enough memory!";
88718bb8381SNamhyung Kim 		else
88818bb8381SNamhyung Kim 			str = alloc_str;
88918bb8381SNamhyung Kim 	}
89018bb8381SNamhyung Kim 
89118bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
89218bb8381SNamhyung Kim 	free(alloc_str);
893fef51ecdSJin Yao 	free(alloc_str2);
8940d3eb0b7SJin Yao 
8952a704fc8SMilian Wolff 	return ret;
89618bb8381SNamhyung Kim }
89718bb8381SNamhyung Kim 
89859c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
89959c624e2SNamhyung Kim {
90059c624e2SNamhyung Kim 	struct callchain_node *child;
90159c624e2SNamhyung Kim 
90259c624e2SNamhyung Kim 	if (node == NULL)
90359c624e2SNamhyung Kim 		return false;
90459c624e2SNamhyung Kim 
90559c624e2SNamhyung Kim 	if (rb_next(node))
90659c624e2SNamhyung Kim 		return true;
90759c624e2SNamhyung Kim 
90859c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
90959c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
91059c624e2SNamhyung Kim }
91159c624e2SNamhyung Kim 
9124b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
9134b3a3212SNamhyung Kim 					     struct rb_root *root,
9144b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
91559c624e2SNamhyung Kim 					     u64 parent_total,
9164b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
9174b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
9184b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
9194b3a3212SNamhyung Kim {
9204b3a3212SNamhyung Kim 	struct rb_node *node;
9214b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
9224b3a3212SNamhyung Kim 	bool need_percent;
9234b3a3212SNamhyung Kim 
9244b3a3212SNamhyung Kim 	node = rb_first(root);
92559c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9264b3a3212SNamhyung Kim 
9274b3a3212SNamhyung Kim 	while (node) {
9284b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
9294b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
9304b3a3212SNamhyung Kim 		struct callchain_list *chain;
9314b3a3212SNamhyung Kim 		char folded_sign = ' ';
9324b3a3212SNamhyung Kim 		int first = true;
9334b3a3212SNamhyung Kim 		int extra_offset = 0;
9344b3a3212SNamhyung Kim 
9354b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9364b3a3212SNamhyung Kim 			bool was_first = first;
9374b3a3212SNamhyung Kim 
9384b3a3212SNamhyung Kim 			if (first)
9394b3a3212SNamhyung Kim 				first = false;
9404b3a3212SNamhyung Kim 			else if (need_percent)
9414b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
9424b3a3212SNamhyung Kim 
9434b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
9444b3a3212SNamhyung Kim 
9454b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
9464b3a3212SNamhyung Kim 							chain, row, total,
9474b3a3212SNamhyung Kim 							was_first && need_percent,
9484b3a3212SNamhyung Kim 							offset + extra_offset,
9494b3a3212SNamhyung Kim 							print, arg);
9504b3a3212SNamhyung Kim 
9514b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
9524b3a3212SNamhyung Kim 				goto out;
9534b3a3212SNamhyung Kim 
9544b3a3212SNamhyung Kim 			if (folded_sign == '+')
9554b3a3212SNamhyung Kim 				goto next;
9564b3a3212SNamhyung Kim 		}
9574b3a3212SNamhyung Kim 
9584b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
9594b3a3212SNamhyung Kim 			bool was_first = first;
9604b3a3212SNamhyung Kim 
9614b3a3212SNamhyung Kim 			if (first)
9624b3a3212SNamhyung Kim 				first = false;
9634b3a3212SNamhyung Kim 			else if (need_percent)
9644b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
9654b3a3212SNamhyung Kim 
9664b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
9674b3a3212SNamhyung Kim 
9684b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
9694b3a3212SNamhyung Kim 							chain, row, total,
9704b3a3212SNamhyung Kim 							was_first && need_percent,
9714b3a3212SNamhyung Kim 							offset + extra_offset,
9724b3a3212SNamhyung Kim 							print, arg);
9734b3a3212SNamhyung Kim 
9744b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
9754b3a3212SNamhyung Kim 				goto out;
9764b3a3212SNamhyung Kim 
9774b3a3212SNamhyung Kim 			if (folded_sign == '+')
9784b3a3212SNamhyung Kim 				break;
9794b3a3212SNamhyung Kim 		}
9804b3a3212SNamhyung Kim 
9814b3a3212SNamhyung Kim next:
9824b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
9834b3a3212SNamhyung Kim 			break;
9844b3a3212SNamhyung Kim 		node = next;
9854b3a3212SNamhyung Kim 	}
9864b3a3212SNamhyung Kim out:
9874b3a3212SNamhyung Kim 	return row - first_row;
9884b3a3212SNamhyung Kim }
9894b3a3212SNamhyung Kim 
9908c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
9918c430a34SNamhyung Kim 						struct callchain_list *chain,
9928c430a34SNamhyung Kim 						char *value_str, char *old_str)
9938c430a34SNamhyung Kim {
9948c430a34SNamhyung Kim 	char bf[1024];
9958c430a34SNamhyung Kim 	const char *str;
9968c430a34SNamhyung Kim 	char *new;
9978c430a34SNamhyung Kim 
9988c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
9998c430a34SNamhyung Kim 				       browser->show_dso);
10008c430a34SNamhyung Kim 	if (old_str) {
10018c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
10028c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
10038c430a34SNamhyung Kim 			new = NULL;
10048c430a34SNamhyung Kim 	} else {
10058c430a34SNamhyung Kim 		if (value_str) {
10068c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
10078c430a34SNamhyung Kim 				new = NULL;
10088c430a34SNamhyung Kim 		} else {
10098c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
10108c430a34SNamhyung Kim 				new = NULL;
10118c430a34SNamhyung Kim 		}
10128c430a34SNamhyung Kim 	}
10138c430a34SNamhyung Kim 	return new;
10148c430a34SNamhyung Kim }
10158c430a34SNamhyung Kim 
10168c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
10178c430a34SNamhyung Kim 					       struct rb_root *root,
10188c430a34SNamhyung Kim 					       unsigned short row, u64 total,
101959c624e2SNamhyung Kim 					       u64 parent_total,
10208c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
10218c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
10228c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
10238c430a34SNamhyung Kim {
10248c430a34SNamhyung Kim 	struct rb_node *node;
10258c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
10268c430a34SNamhyung Kim 	bool need_percent;
10278c430a34SNamhyung Kim 
10288c430a34SNamhyung Kim 	node = rb_first(root);
102959c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
10308c430a34SNamhyung Kim 
10318c430a34SNamhyung Kim 	while (node) {
10328c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
10338c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
10348c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
10358c430a34SNamhyung Kim 		int first = true;
10368c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
10378c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
10388c430a34SNamhyung Kim 
10398c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
10408c430a34SNamhyung Kim 			arg->row_offset--;
10418c430a34SNamhyung Kim 			goto next;
10428c430a34SNamhyung Kim 		}
10438c430a34SNamhyung Kim 
10448c430a34SNamhyung Kim 		if (need_percent) {
10458c430a34SNamhyung Kim 			char buf[64];
10468c430a34SNamhyung Kim 
10478c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
10488c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
10498c430a34SNamhyung Kim 				value_str = (char *)"<...>";
10508c430a34SNamhyung Kim 				goto do_print;
10518c430a34SNamhyung Kim 			}
10528c430a34SNamhyung Kim 			value_str_alloc = value_str;
10538c430a34SNamhyung Kim 		}
10548c430a34SNamhyung Kim 
10558c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
10568c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
10578c430a34SNamhyung Kim 						chain, value_str, chain_str);
10588c430a34SNamhyung Kim 			if (first) {
10598c430a34SNamhyung Kim 				first = false;
10608c430a34SNamhyung Kim 				first_chain = chain;
10618c430a34SNamhyung Kim 			}
10628c430a34SNamhyung Kim 
10638c430a34SNamhyung Kim 			if (chain_str == NULL) {
10648c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
10658c430a34SNamhyung Kim 				goto do_print;
10668c430a34SNamhyung Kim 			}
10678c430a34SNamhyung Kim 
10688c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
10698c430a34SNamhyung Kim 		}
10708c430a34SNamhyung Kim 
10718c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
10728c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
10738c430a34SNamhyung Kim 						chain, value_str, chain_str);
10748c430a34SNamhyung Kim 			if (first) {
10758c430a34SNamhyung Kim 				first = false;
10768c430a34SNamhyung Kim 				first_chain = chain;
10778c430a34SNamhyung Kim 			}
10788c430a34SNamhyung Kim 
10798c430a34SNamhyung Kim 			if (chain_str == NULL) {
10808c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
10818c430a34SNamhyung Kim 				goto do_print;
10828c430a34SNamhyung Kim 			}
10838c430a34SNamhyung Kim 
10848c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
10858c430a34SNamhyung Kim 		}
10868c430a34SNamhyung Kim 
10878c430a34SNamhyung Kim do_print:
10888c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
10898c430a34SNamhyung Kim 		free(value_str_alloc);
10908c430a34SNamhyung Kim 		free(chain_str_alloc);
10918c430a34SNamhyung Kim 
10928c430a34SNamhyung Kim next:
10938c430a34SNamhyung Kim 		if (is_output_full(browser, row))
10948c430a34SNamhyung Kim 			break;
10958c430a34SNamhyung Kim 		node = next;
10968c430a34SNamhyung Kim 	}
10978c430a34SNamhyung Kim 
10988c430a34SNamhyung Kim 	return row - first_row;
10998c430a34SNamhyung Kim }
11008c430a34SNamhyung Kim 
11010c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1102c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
110339ee533fSNamhyung Kim 					unsigned short row, u64 total,
11045eca104eSNamhyung Kim 					u64 parent_total,
110539ee533fSNamhyung Kim 					print_callchain_entry_fn print,
110639ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
110739ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
1108aca7a94dSNamhyung Kim {
1109aca7a94dSNamhyung Kim 	struct rb_node *node;
1110f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
11114087d11cSNamhyung Kim 	bool need_percent;
11125eca104eSNamhyung Kim 	u64 percent_total = total;
11135eca104eSNamhyung Kim 
11145eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
11155eca104eSNamhyung Kim 		percent_total = parent_total;
1116aca7a94dSNamhyung Kim 
1117c09a7e75SNamhyung Kim 	node = rb_first(root);
111859c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
11194087d11cSNamhyung Kim 
1120aca7a94dSNamhyung Kim 	while (node) {
1121aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1122aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
1123aca7a94dSNamhyung Kim 		struct callchain_list *chain;
1124aca7a94dSNamhyung Kim 		char folded_sign = ' ';
1125aca7a94dSNamhyung Kim 		int first = true;
1126aca7a94dSNamhyung Kim 		int extra_offset = 0;
1127aca7a94dSNamhyung Kim 
1128aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
1129aca7a94dSNamhyung Kim 			bool was_first = first;
1130aca7a94dSNamhyung Kim 
1131aca7a94dSNamhyung Kim 			if (first)
1132aca7a94dSNamhyung Kim 				first = false;
11334087d11cSNamhyung Kim 			else if (need_percent)
1134aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1135aca7a94dSNamhyung Kim 
1136aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1137aca7a94dSNamhyung Kim 
113818bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
11395eca104eSNamhyung Kim 							chain, row, percent_total,
114018bb8381SNamhyung Kim 							was_first && need_percent,
114118bb8381SNamhyung Kim 							offset + extra_offset,
114218bb8381SNamhyung Kim 							print, arg);
1143c09a7e75SNamhyung Kim 
114418bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1145aca7a94dSNamhyung Kim 				goto out;
114618bb8381SNamhyung Kim 
1147aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1148aca7a94dSNamhyung Kim 				break;
1149aca7a94dSNamhyung Kim 		}
1150aca7a94dSNamhyung Kim 
1151aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1152aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1153c09a7e75SNamhyung Kim 
11540c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
11555eca104eSNamhyung Kim 							    new_level, row, total,
11565eca104eSNamhyung Kim 							    child->children_hit,
115739ee533fSNamhyung Kim 							    print, arg, is_output_full);
1158aca7a94dSNamhyung Kim 		}
115939ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1160c09a7e75SNamhyung Kim 			break;
1161aca7a94dSNamhyung Kim 		node = next;
1162aca7a94dSNamhyung Kim 	}
1163aca7a94dSNamhyung Kim out:
1164aca7a94dSNamhyung Kim 	return row - first_row;
1165aca7a94dSNamhyung Kim }
1166aca7a94dSNamhyung Kim 
11670c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
11680c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
11690c841c6cSNamhyung Kim 					unsigned short row,
11700c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
11710c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
11720c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
11730c841c6cSNamhyung Kim {
11740c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
11755eca104eSNamhyung Kim 	u64 parent_total;
11760c841c6cSNamhyung Kim 	int printed;
11770c841c6cSNamhyung Kim 
11780c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
11795eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
11800c841c6cSNamhyung Kim 	else
11815eca104eSNamhyung Kim 		parent_total = entry->stat.period;
11820c841c6cSNamhyung Kim 
11830c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
11840c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
11855eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11865eca104eSNamhyung Kim 						total, parent_total, print, arg,
11875eca104eSNamhyung Kim 						is_output_full);
11880c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
11890c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
11905eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11915eca104eSNamhyung Kim 						total, parent_total, print, arg,
11925eca104eSNamhyung Kim 						is_output_full);
11930c841c6cSNamhyung Kim 	} else {
11940c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
11955eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
11965eca104eSNamhyung Kim 						total, parent_total, print, arg,
11975eca104eSNamhyung Kim 						is_output_full);
11980c841c6cSNamhyung Kim 	}
11990c841c6cSNamhyung Kim 
12000c841c6cSNamhyung Kim 	if (arg->is_current_entry)
12010c841c6cSNamhyung Kim 		browser->he_selection = entry;
12020c841c6cSNamhyung Kim 
12030c841c6cSNamhyung Kim 	return printed;
12040c841c6cSNamhyung Kim }
12050c841c6cSNamhyung Kim 
120689701460SNamhyung Kim struct hpp_arg {
120789701460SNamhyung Kim 	struct ui_browser *b;
120889701460SNamhyung Kim 	char folded_sign;
120989701460SNamhyung Kim 	bool current_entry;
121089701460SNamhyung Kim };
121189701460SNamhyung Kim 
121298ba1609SJiri Olsa int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
12132f6d9009SNamhyung Kim {
12142f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1215d675107cSNamhyung Kim 	int ret, len;
12162f6d9009SNamhyung Kim 	va_list args;
12172f6d9009SNamhyung Kim 	double percent;
12182f6d9009SNamhyung Kim 
12192f6d9009SNamhyung Kim 	va_start(args, fmt);
1220d675107cSNamhyung Kim 	len = va_arg(args, int);
12212f6d9009SNamhyung Kim 	percent = va_arg(args, double);
12222f6d9009SNamhyung Kim 	va_end(args);
12235aed9d24SNamhyung Kim 
122489701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
12255aed9d24SNamhyung Kim 
1226d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1227517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
122889701460SNamhyung Kim 
12295aed9d24SNamhyung Kim 	return ret;
1230f5951d56SNamhyung Kim }
1231f5951d56SNamhyung Kim 
1232fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
12335aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
12345aed9d24SNamhyung Kim {									\
12355aed9d24SNamhyung Kim 	return he->stat._field;						\
12365aed9d24SNamhyung Kim }									\
12375aed9d24SNamhyung Kim 									\
12382c5d4b4aSJiri Olsa static int								\
12395b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
12402c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
12415aed9d24SNamhyung Kim 				struct hist_entry *he)			\
12425aed9d24SNamhyung Kim {									\
12435b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
12442f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
12455aed9d24SNamhyung Kim }
1246f5951d56SNamhyung Kim 
12470434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
12480434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
12490434ddd2SNamhyung Kim {									\
12500434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
12510434ddd2SNamhyung Kim }									\
12520434ddd2SNamhyung Kim 									\
12530434ddd2SNamhyung Kim static int								\
12545b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
12550434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
12560434ddd2SNamhyung Kim 				struct hist_entry *he)			\
12570434ddd2SNamhyung Kim {									\
12580434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1259517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
12605b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1261d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
12625b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1263517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
12640434ddd2SNamhyung Kim 									\
12650434ddd2SNamhyung Kim 		return ret;						\
12660434ddd2SNamhyung Kim 	}								\
12675b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
12685b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
12690434ddd2SNamhyung Kim }
12700434ddd2SNamhyung Kim 
1271fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1272fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1273fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1274fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1275fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
12760434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
12775aed9d24SNamhyung Kim 
12785aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
12790434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1280f5951d56SNamhyung Kim 
1281f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1282f5951d56SNamhyung Kim {
1283f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1284f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1285f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1286f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1287f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1288f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1289f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1290f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1291f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1292f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
12930434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
12940434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
12954968ac8fSAndi Kleen 
12964968ac8fSAndi Kleen 	res_sample_init();
1297f5951d56SNamhyung Kim }
1298f5951d56SNamhyung Kim 
129905e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1300aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1301aca7a94dSNamhyung Kim 				    unsigned short row)
1302aca7a94dSNamhyung Kim {
13031240005eSJiri Olsa 	int printed = 0;
130467d25916SNamhyung Kim 	int width = browser->b.width;
1305aca7a94dSNamhyung Kim 	char folded_sign = ' ';
130605e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1307fabd37b8SArnaldo Carvalho de Melo 	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1308aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
130963a1a3d8SNamhyung Kim 	bool first = true;
13101240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1311aca7a94dSNamhyung Kim 
1312aca7a94dSNamhyung Kim 	if (current_entry) {
131305e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
131405e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1315aca7a94dSNamhyung Kim 	}
1316aca7a94dSNamhyung Kim 
1317fabd37b8SArnaldo Carvalho de Melo 	if (use_callchain) {
1318aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1319aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1320aca7a94dSNamhyung Kim 	}
1321aca7a94dSNamhyung Kim 
1322aca7a94dSNamhyung Kim 	if (row_offset == 0) {
132389701460SNamhyung Kim 		struct hpp_arg arg = {
132489701460SNamhyung Kim 			.b		= &browser->b,
132589701460SNamhyung Kim 			.folded_sign	= folded_sign,
132689701460SNamhyung Kim 			.current_entry	= current_entry,
132789701460SNamhyung Kim 		};
1328c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1329f5951d56SNamhyung Kim 
1330ef9ff601SArnaldo Carvalho de Melo 		ui_browser__gotorc(&browser->b, row, 0);
1331f5951d56SNamhyung Kim 
1332f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
133389fee709SArnaldo Carvalho de Melo 			char s[2048];
133489fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
133589fee709SArnaldo Carvalho de Melo 				.buf	= s,
133689fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
133789fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
133889fee709SArnaldo Carvalho de Melo 			};
133989fee709SArnaldo Carvalho de Melo 
1340361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1341361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1342e67d49a7SNamhyung Kim 				continue;
1343e67d49a7SNamhyung Kim 
1344fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1345fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1346fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1347fb821c9eSNamhyung Kim 			} else {
1348fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1349fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1350fb821c9eSNamhyung Kim 			}
1351fb821c9eSNamhyung Kim 
1352fb821c9eSNamhyung Kim 			if (first) {
1353fabd37b8SArnaldo Carvalho de Melo 				if (use_callchain) {
1354517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1355f5951d56SNamhyung Kim 					width -= 2;
1356f5951d56SNamhyung Kim 				}
135763a1a3d8SNamhyung Kim 				first = false;
1358fb821c9eSNamhyung Kim 			} else {
1359517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1360fb821c9eSNamhyung Kim 				width -= 2;
1361fb821c9eSNamhyung Kim 			}
1362f5951d56SNamhyung Kim 
13631240005eSJiri Olsa 			if (fmt->color) {
136489fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
136589fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
136689fee709SArnaldo Carvalho de Melo 				/*
136789fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
136889fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
136989fee709SArnaldo Carvalho de Melo 				 */
137089fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1371f5951d56SNamhyung Kim 			} else {
137289fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1373517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1374f5951d56SNamhyung Kim 			}
137589fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1376f5951d56SNamhyung Kim 		}
1377aca7a94dSNamhyung Kim 
1378aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
137905e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1380aca7a94dSNamhyung Kim 			width += 1;
1381aca7a94dSNamhyung Kim 
138226270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
138326d8b338SNamhyung Kim 
1384aca7a94dSNamhyung Kim 		++row;
1385aca7a94dSNamhyung Kim 		++printed;
1386aca7a94dSNamhyung Kim 	} else
1387aca7a94dSNamhyung Kim 		--row_offset;
1388aca7a94dSNamhyung Kim 
138962c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
139039ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
139139ee533fSNamhyung Kim 			.row_offset = row_offset,
139239ee533fSNamhyung Kim 			.is_current_entry = current_entry,
139339ee533fSNamhyung Kim 		};
1394c09a7e75SNamhyung Kim 
13950d3eb0b7SJin Yao 		printed += hist_browser__show_callchain(browser,
13960d3eb0b7SJin Yao 				entry, 1, row,
13970d3eb0b7SJin Yao 				hist_browser__show_callchain_entry,
13980d3eb0b7SJin Yao 				&arg,
13994b3a3212SNamhyung Kim 				hist_browser__check_output_full);
1400aca7a94dSNamhyung Kim 	}
1401aca7a94dSNamhyung Kim 
1402aca7a94dSNamhyung Kim 	return printed;
1403aca7a94dSNamhyung Kim }
1404aca7a94dSNamhyung Kim 
1405d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1406d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1407d0506edbSNamhyung Kim 					      unsigned short row,
14082dbbe9f2SNamhyung Kim 					      int level)
1409d0506edbSNamhyung Kim {
1410d0506edbSNamhyung Kim 	int printed = 0;
1411d0506edbSNamhyung Kim 	int width = browser->b.width;
1412d0506edbSNamhyung Kim 	char folded_sign = ' ';
1413d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1414d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1415d0506edbSNamhyung Kim 	bool first = true;
1416d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1417a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1418d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1419d0506edbSNamhyung Kim 		.b		= &browser->b,
1420d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1421d0506edbSNamhyung Kim 	};
1422d0506edbSNamhyung Kim 	int column = 0;
14232dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1424d0506edbSNamhyung Kim 
1425d0506edbSNamhyung Kim 	if (current_entry) {
1426d0506edbSNamhyung Kim 		browser->he_selection = entry;
1427d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1428d0506edbSNamhyung Kim 	}
1429d0506edbSNamhyung Kim 
1430d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1431d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1432d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1433d0506edbSNamhyung Kim 
1434d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1435d0506edbSNamhyung Kim 		row_offset--;
1436d0506edbSNamhyung Kim 		goto show_callchain;
1437d0506edbSNamhyung Kim 	}
1438d0506edbSNamhyung Kim 
1439ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
1440d0506edbSNamhyung Kim 
1441d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1442d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1443d0506edbSNamhyung Kim 	else
1444d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1445d0506edbSNamhyung Kim 
1446d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1447d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1448d0506edbSNamhyung Kim 
1449a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1450a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1451a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1452a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1453d0506edbSNamhyung Kim 		char s[2048];
1454d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1455d0506edbSNamhyung Kim 			.buf		= s,
1456d0506edbSNamhyung Kim 			.size		= sizeof(s),
1457d0506edbSNamhyung Kim 			.ptr		= &arg,
1458d0506edbSNamhyung Kim 		};
1459d0506edbSNamhyung Kim 
1460d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1461d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1462d0506edbSNamhyung Kim 			continue;
1463d0506edbSNamhyung Kim 
1464d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1465d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1466d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1467d0506edbSNamhyung Kim 		} else {
1468d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1469d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1470d0506edbSNamhyung Kim 		}
1471d0506edbSNamhyung Kim 
1472d0506edbSNamhyung Kim 		if (first) {
1473d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c ", folded_sign);
14743d9f4683SNamhyung Kim 			width -= 2;
1475d0506edbSNamhyung Kim 			first = false;
1476d0506edbSNamhyung Kim 		} else {
1477d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1478d0506edbSNamhyung Kim 			width -= 2;
1479d0506edbSNamhyung Kim 		}
1480d0506edbSNamhyung Kim 
1481d0506edbSNamhyung Kim 		if (fmt->color) {
1482d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1483d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1484d0506edbSNamhyung Kim 			/*
1485d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1486d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1487d0506edbSNamhyung Kim 			 */
1488d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1489d0506edbSNamhyung Kim 		} else {
1490d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1491d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1492d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1493d0506edbSNamhyung Kim 		}
1494d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1495d0506edbSNamhyung Kim 	}
1496d0506edbSNamhyung Kim 
1497b9bf911eSNamhyung Kim 	if (!first) {
1498d0506edbSNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1499d0506edbSNamhyung Kim 		width -= hierarchy_indent;
1500b9bf911eSNamhyung Kim 	}
1501d0506edbSNamhyung Kim 
1502d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1503d0506edbSNamhyung Kim 		char s[2048];
1504d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1505d0506edbSNamhyung Kim 			.buf		= s,
1506d0506edbSNamhyung Kim 			.size		= sizeof(s),
1507d0506edbSNamhyung Kim 			.ptr		= &arg,
1508d0506edbSNamhyung Kim 		};
1509d0506edbSNamhyung Kim 
1510d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1511d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1512d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1513d0506edbSNamhyung Kim 		} else {
1514d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1515d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1516d0506edbSNamhyung Kim 		}
1517d0506edbSNamhyung Kim 
15181b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1519131d51ebSNamhyung Kim 			if (first) {
1520131d51ebSNamhyung Kim 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1521131d51ebSNamhyung Kim 				first = false;
1522131d51ebSNamhyung Kim 			} else {
1523d0506edbSNamhyung Kim 				ui_browser__write_nstring(&browser->b, "", 2);
1524131d51ebSNamhyung Kim 			}
1525131d51ebSNamhyung Kim 
1526d0506edbSNamhyung Kim 			width -= 2;
1527d0506edbSNamhyung Kim 
1528d0506edbSNamhyung Kim 			/*
1529d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1530d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1531d0506edbSNamhyung Kim 			 * hierarchy mode.
1532d0506edbSNamhyung Kim 			 */
1533d0506edbSNamhyung Kim 			if (fmt->color) {
1534d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1535d0506edbSNamhyung Kim 			} else {
1536cb1fab91SNamhyung Kim 				int i = 0;
1537cb1fab91SNamhyung Kim 
1538d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
153932858480SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1540cb1fab91SNamhyung Kim 
1541cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1542cb1fab91SNamhyung Kim 					width++;
1543d0506edbSNamhyung Kim 			}
1544d0506edbSNamhyung Kim 		}
15451b2dbbf4SNamhyung Kim 	}
1546d0506edbSNamhyung Kim 
1547d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1548d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1549d0506edbSNamhyung Kim 		width += 1;
1550d0506edbSNamhyung Kim 
1551d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1552d0506edbSNamhyung Kim 
1553d0506edbSNamhyung Kim 	++row;
1554d0506edbSNamhyung Kim 	++printed;
1555d0506edbSNamhyung Kim 
1556d0506edbSNamhyung Kim show_callchain:
1557d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1558d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1559d0506edbSNamhyung Kim 			.row_offset = row_offset,
1560d0506edbSNamhyung Kim 		};
1561d0506edbSNamhyung Kim 
1562d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1563d0506edbSNamhyung Kim 					level + 1, row,
1564d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1565d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1566d0506edbSNamhyung Kim 	}
1567d0506edbSNamhyung Kim 
1568d0506edbSNamhyung Kim 	return printed;
1569d0506edbSNamhyung Kim }
1570d0506edbSNamhyung Kim 
157179dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
15722dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
157379dded87SNamhyung Kim {
157479dded87SNamhyung Kim 	int width = browser->b.width;
157579dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
157679dded87SNamhyung Kim 	bool first = true;
157779dded87SNamhyung Kim 	int column = 0;
157879dded87SNamhyung Kim 	int ret;
157979dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1580a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
15812dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
158279dded87SNamhyung Kim 
158379dded87SNamhyung Kim 	if (current_entry) {
158479dded87SNamhyung Kim 		browser->he_selection = NULL;
158579dded87SNamhyung Kim 		browser->selection = NULL;
158679dded87SNamhyung Kim 	}
158779dded87SNamhyung Kim 
1588ef9ff601SArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row, 0);
158979dded87SNamhyung Kim 
159079dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
159179dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
159279dded87SNamhyung Kim 	else
159379dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
159479dded87SNamhyung Kim 
159579dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
159679dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
159779dded87SNamhyung Kim 
1598a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1599a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1600a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1601a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
160279dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
160379dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
160479dded87SNamhyung Kim 			continue;
160579dded87SNamhyung Kim 
1606da1b0407SJiri Olsa 		ret = fmt->width(fmt, NULL, browser->hists);
160779dded87SNamhyung Kim 
160879dded87SNamhyung Kim 		if (first) {
160979dded87SNamhyung Kim 			/* for folded sign */
161079dded87SNamhyung Kim 			first = false;
161179dded87SNamhyung Kim 			ret++;
161279dded87SNamhyung Kim 		} else {
161379dded87SNamhyung Kim 			/* space between columns */
161479dded87SNamhyung Kim 			ret += 2;
161579dded87SNamhyung Kim 		}
161679dded87SNamhyung Kim 
161779dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
161879dded87SNamhyung Kim 		width -= ret;
161979dded87SNamhyung Kim 	}
162079dded87SNamhyung Kim 
16212dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
16222dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
162379dded87SNamhyung Kim 
162479dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
162579dded87SNamhyung Kim 		char buf[32];
162679dded87SNamhyung Kim 
162779dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
162879dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
162979dded87SNamhyung Kim 		width -= ret + 2;
163079dded87SNamhyung Kim 	}
163179dded87SNamhyung Kim 
163279dded87SNamhyung Kim 	/* The scroll bar isn't being used */
163379dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
163479dded87SNamhyung Kim 		width += 1;
163579dded87SNamhyung Kim 
163679dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
163779dded87SNamhyung Kim 	return 1;
163879dded87SNamhyung Kim }
163979dded87SNamhyung Kim 
164081a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
164181a888feSJiri Olsa {
164281a888feSJiri Olsa 	advance_hpp(hpp, inc);
164381a888feSJiri Olsa 	return hpp->size <= 0;
164481a888feSJiri Olsa }
164581a888feSJiri Olsa 
164669705b35SJiri Olsa static int
164769705b35SJiri Olsa hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
164869705b35SJiri Olsa 				 size_t size, int line)
164981a888feSJiri Olsa {
1650c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
165181a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
165281a888feSJiri Olsa 		.buf    = buf,
165381a888feSJiri Olsa 		.size   = size,
165481a888feSJiri Olsa 	};
165581a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
165681a888feSJiri Olsa 	size_t ret = 0;
1657c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
165829659ab4SJiri Olsa 	int span = 0;
165981a888feSJiri Olsa 
1660fabd37b8SArnaldo Carvalho de Melo 	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
166181a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
166281a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
166381a888feSJiri Olsa 			return ret;
166481a888feSJiri Olsa 	}
166581a888feSJiri Olsa 
1666f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1667361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
166881a888feSJiri Olsa 			continue;
166981a888feSJiri Olsa 
167029659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
167181a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
167281a888feSJiri Olsa 			break;
167381a888feSJiri Olsa 
167429659ab4SJiri Olsa 		if (span)
167529659ab4SJiri Olsa 			continue;
167629659ab4SJiri Olsa 
167781a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
167881a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
167981a888feSJiri Olsa 			break;
168081a888feSJiri Olsa 	}
168181a888feSJiri Olsa 
168281a888feSJiri Olsa 	return ret;
168381a888feSJiri Olsa }
168481a888feSJiri Olsa 
1685d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1686d8b92400SNamhyung Kim {
1687d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1688d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1689d8b92400SNamhyung Kim 		.buf    = buf,
1690d8b92400SNamhyung Kim 		.size   = size,
1691d8b92400SNamhyung Kim 	};
1692d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1693a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1694d8b92400SNamhyung Kim 	size_t ret = 0;
1695d8b92400SNamhyung Kim 	int column = 0;
16962dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1697a61a22f6SNamhyung Kim 	bool first_node, first_col;
1698d8b92400SNamhyung Kim 
1699d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, "  ");
1700d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1701d8b92400SNamhyung Kim 		return ret;
1702d8b92400SNamhyung Kim 
1703b9bf911eSNamhyung Kim 	first_node = true;
1704a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1705a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1706a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1707a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1708d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1709d8b92400SNamhyung Kim 			continue;
1710d8b92400SNamhyung Kim 
171129659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1712d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1713d8b92400SNamhyung Kim 			break;
1714d8b92400SNamhyung Kim 
1715d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1716d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1717d8b92400SNamhyung Kim 			break;
1718b9bf911eSNamhyung Kim 
1719b9bf911eSNamhyung Kim 		first_node = false;
1720d8b92400SNamhyung Kim 	}
1721d8b92400SNamhyung Kim 
1722b9bf911eSNamhyung Kim 	if (!first_node) {
1723d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
17242dbbe9f2SNamhyung Kim 				indent * HIERARCHY_INDENT, "");
1725d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1726d8b92400SNamhyung Kim 			return ret;
1727b9bf911eSNamhyung Kim 	}
1728d8b92400SNamhyung Kim 
1729a61a22f6SNamhyung Kim 	first_node = true;
1730a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1731a61a22f6SNamhyung Kim 		if (!first_node) {
1732d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1733d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1734d8b92400SNamhyung Kim 				break;
1735d8b92400SNamhyung Kim 		}
1736a61a22f6SNamhyung Kim 		first_node = false;
1737a61a22f6SNamhyung Kim 
1738a61a22f6SNamhyung Kim 		first_col = true;
1739a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1740a61a22f6SNamhyung Kim 			char *start;
1741a61a22f6SNamhyung Kim 
1742a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1743a61a22f6SNamhyung Kim 				continue;
1744a61a22f6SNamhyung Kim 
1745a61a22f6SNamhyung Kim 			if (!first_col) {
1746a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1747a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1748a61a22f6SNamhyung Kim 					break;
1749a61a22f6SNamhyung Kim 			}
1750a61a22f6SNamhyung Kim 			first_col = false;
1751d8b92400SNamhyung Kim 
175229659ab4SJiri Olsa 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1753d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1754d8b92400SNamhyung Kim 
17553ca43b60SArnaldo Carvalho de Melo 			start = strim(dummy_hpp.buf);
1756cb1fab91SNamhyung Kim 			ret = strlen(start);
1757cb1fab91SNamhyung Kim 
1758cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1759cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1760cb1fab91SNamhyung Kim 
1761d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1762d8b92400SNamhyung Kim 				break;
1763d8b92400SNamhyung Kim 		}
1764a61a22f6SNamhyung Kim 	}
1765d8b92400SNamhyung Kim 
1766d8b92400SNamhyung Kim 	return ret;
1767d8b92400SNamhyung Kim }
1768d8b92400SNamhyung Kim 
176901b4770dSJiri Olsa static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1770025bf7eaSArnaldo Carvalho de Melo {
177181a888feSJiri Olsa 	char headers[1024];
177281a888feSJiri Olsa 
1773d8b92400SNamhyung Kim 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1774d8b92400SNamhyung Kim 						   sizeof(headers));
177501b4770dSJiri Olsa 
1776025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1777025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
177826270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1779025bf7eaSArnaldo Carvalho de Melo }
1780025bf7eaSArnaldo Carvalho de Melo 
178101b4770dSJiri Olsa static void hists_browser__headers(struct hist_browser *browser)
178201b4770dSJiri Olsa {
178369705b35SJiri Olsa 	struct hists *hists = browser->hists;
178469705b35SJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
178569705b35SJiri Olsa 
178669705b35SJiri Olsa 	int line;
178769705b35SJiri Olsa 
178869705b35SJiri Olsa 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
178901b4770dSJiri Olsa 		char headers[1024];
179001b4770dSJiri Olsa 
179101b4770dSJiri Olsa 		hists_browser__scnprintf_headers(browser, headers,
179269705b35SJiri Olsa 						 sizeof(headers), line);
179301b4770dSJiri Olsa 
1794ef9ff601SArnaldo Carvalho de Melo 		ui_browser__gotorc_title(&browser->b, line, 0);
179501b4770dSJiri Olsa 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
179601b4770dSJiri Olsa 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
179701b4770dSJiri Olsa 	}
179869705b35SJiri Olsa }
179901b4770dSJiri Olsa 
180001b4770dSJiri Olsa static void hist_browser__show_headers(struct hist_browser *browser)
180101b4770dSJiri Olsa {
180201b4770dSJiri Olsa 	if (symbol_conf.report_hierarchy)
180301b4770dSJiri Olsa 		hists_browser__hierarchy_headers(browser);
180401b4770dSJiri Olsa 	else
180501b4770dSJiri Olsa 		hists_browser__headers(browser);
180601b4770dSJiri Olsa }
180701b4770dSJiri Olsa 
1808aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1809aca7a94dSNamhyung Kim {
1810aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1811aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1812aca7a94dSNamhyung Kim 
1813aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
18142eb3d689SDavidlohr Bueso 		browser->top = rb_first_cached(&hb->hists->entries);
1815aca7a94dSNamhyung Kim 	}
1816aca7a94dSNamhyung Kim }
1817aca7a94dSNamhyung Kim 
181805e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1819aca7a94dSNamhyung Kim {
1820aca7a94dSNamhyung Kim 	unsigned row = 0;
1821aca7a94dSNamhyung Kim 	struct rb_node *nd;
182205e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1823aca7a94dSNamhyung Kim 
182494e87a8bSArnaldo Carvalho de Melo 	if (hb->show_headers)
1825025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1826025bf7eaSArnaldo Carvalho de Melo 
182705e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1828979d2cacSWang Nan 	hb->he_selection = NULL;
1829979d2cacSWang Nan 	hb->selection = NULL;
1830aca7a94dSNamhyung Kim 
1831d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1832aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
183314135663SNamhyung Kim 		float percent;
1834aca7a94dSNamhyung Kim 
1835d0506edbSNamhyung Kim 		if (h->filtered) {
1836d0506edbSNamhyung Kim 			/* let it move to sibling */
1837d0506edbSNamhyung Kim 			h->unfolded = false;
1838aca7a94dSNamhyung Kim 			continue;
1839d0506edbSNamhyung Kim 		}
1840aca7a94dSNamhyung Kim 
18417fa46cbfSJin Yao 		if (symbol_conf.report_individual_block)
18427fa46cbfSJin Yao 			percent = block_info__total_cycles_percent(h);
18437fa46cbfSJin Yao 		else
184414135663SNamhyung Kim 			percent = hist_entry__get_percent_limit(h);
18457fa46cbfSJin Yao 
1846064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1847064f1981SNamhyung Kim 			continue;
1848064f1981SNamhyung Kim 
1849d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1850d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
18512dbbe9f2SNamhyung Kim 								  h->depth);
185279dded87SNamhyung Kim 			if (row == browser->rows)
185379dded87SNamhyung Kim 				break;
185479dded87SNamhyung Kim 
185579dded87SNamhyung Kim 			if (h->has_no_entry) {
1856a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
185779dded87SNamhyung Kim 				row++;
185879dded87SNamhyung Kim 			}
1859d0506edbSNamhyung Kim 		} else {
1860aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1861d0506edbSNamhyung Kim 		}
1862d0506edbSNamhyung Kim 
186362c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1864aca7a94dSNamhyung Kim 			break;
1865aca7a94dSNamhyung Kim 	}
1866aca7a94dSNamhyung Kim 
186794e87a8bSArnaldo Carvalho de Melo 	return row;
1868aca7a94dSNamhyung Kim }
1869aca7a94dSNamhyung Kim 
1870064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1871064f1981SNamhyung Kim 					     float min_pcnt)
1872aca7a94dSNamhyung Kim {
1873aca7a94dSNamhyung Kim 	while (nd != NULL) {
1874aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
187514135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1876064f1981SNamhyung Kim 
1877c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1878aca7a94dSNamhyung Kim 			return nd;
1879aca7a94dSNamhyung Kim 
1880d0506edbSNamhyung Kim 		/*
1881d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1882d0506edbSNamhyung Kim 		 * So move to sibling node.
1883d0506edbSNamhyung Kim 		 */
1884d0506edbSNamhyung Kim 		if (rb_next(nd))
1885aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1886d0506edbSNamhyung Kim 		else
1887d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1888aca7a94dSNamhyung Kim 	}
1889aca7a94dSNamhyung Kim 
1890aca7a94dSNamhyung Kim 	return NULL;
1891aca7a94dSNamhyung Kim }
1892aca7a94dSNamhyung Kim 
1893064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1894064f1981SNamhyung Kim 						  float min_pcnt)
1895aca7a94dSNamhyung Kim {
1896aca7a94dSNamhyung Kim 	while (nd != NULL) {
1897aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
189814135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1899064f1981SNamhyung Kim 
1900064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1901aca7a94dSNamhyung Kim 			return nd;
1902aca7a94dSNamhyung Kim 
1903d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1904aca7a94dSNamhyung Kim 	}
1905aca7a94dSNamhyung Kim 
1906aca7a94dSNamhyung Kim 	return NULL;
1907aca7a94dSNamhyung Kim }
1908aca7a94dSNamhyung Kim 
190905e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1910aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1911aca7a94dSNamhyung Kim {
1912aca7a94dSNamhyung Kim 	struct hist_entry *h;
1913aca7a94dSNamhyung Kim 	struct rb_node *nd;
1914aca7a94dSNamhyung Kim 	bool first = true;
1915064f1981SNamhyung Kim 	struct hist_browser *hb;
1916064f1981SNamhyung Kim 
1917064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1918aca7a94dSNamhyung Kim 
191905e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1920aca7a94dSNamhyung Kim 		return;
1921aca7a94dSNamhyung Kim 
192205e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1923aca7a94dSNamhyung Kim 
1924aca7a94dSNamhyung Kim 	switch (whence) {
1925aca7a94dSNamhyung Kim 	case SEEK_SET:
1926064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
192714135663SNamhyung Kim 					   hb->min_pcnt);
1928aca7a94dSNamhyung Kim 		break;
1929aca7a94dSNamhyung Kim 	case SEEK_CUR:
193005e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1931aca7a94dSNamhyung Kim 		goto do_offset;
1932aca7a94dSNamhyung Kim 	case SEEK_END:
1933d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1934d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1935aca7a94dSNamhyung Kim 		first = false;
1936aca7a94dSNamhyung Kim 		break;
1937aca7a94dSNamhyung Kim 	default:
1938aca7a94dSNamhyung Kim 		return;
1939aca7a94dSNamhyung Kim 	}
1940aca7a94dSNamhyung Kim 
1941aca7a94dSNamhyung Kim 	/*
1942aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1943aca7a94dSNamhyung Kim 	 * row_offset:
1944aca7a94dSNamhyung Kim 	 */
194505e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1946aca7a94dSNamhyung Kim 	h->row_offset = 0;
1947aca7a94dSNamhyung Kim 
1948aca7a94dSNamhyung Kim 	/*
1949aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1950aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1951aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1952aca7a94dSNamhyung Kim 	 *
1953aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1954aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1955aca7a94dSNamhyung Kim 	 *
1956aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1957aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1958aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1959aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1960aca7a94dSNamhyung Kim 	 */
1961aca7a94dSNamhyung Kim do_offset:
1962837eeb75SWang Nan 	if (!nd)
1963837eeb75SWang Nan 		return;
1964837eeb75SWang Nan 
1965aca7a94dSNamhyung Kim 	if (offset > 0) {
1966aca7a94dSNamhyung Kim 		do {
1967aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1968d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1969aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1970aca7a94dSNamhyung Kim 				if (offset > remaining) {
1971aca7a94dSNamhyung Kim 					offset -= remaining;
1972aca7a94dSNamhyung Kim 					h->row_offset = 0;
1973aca7a94dSNamhyung Kim 				} else {
1974aca7a94dSNamhyung Kim 					h->row_offset += offset;
1975aca7a94dSNamhyung Kim 					offset = 0;
197605e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1977aca7a94dSNamhyung Kim 					break;
1978aca7a94dSNamhyung Kim 				}
1979aca7a94dSNamhyung Kim 			}
1980d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1981d0506edbSNamhyung Kim 						   hb->min_pcnt);
1982aca7a94dSNamhyung Kim 			if (nd == NULL)
1983aca7a94dSNamhyung Kim 				break;
1984aca7a94dSNamhyung Kim 			--offset;
198505e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1986aca7a94dSNamhyung Kim 		} while (offset != 0);
1987aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1988aca7a94dSNamhyung Kim 		while (1) {
1989aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1990d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1991aca7a94dSNamhyung Kim 				if (first) {
1992aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1993aca7a94dSNamhyung Kim 						offset += h->row_offset;
1994aca7a94dSNamhyung Kim 						h->row_offset = 0;
1995aca7a94dSNamhyung Kim 					} else {
1996aca7a94dSNamhyung Kim 						h->row_offset += offset;
1997aca7a94dSNamhyung Kim 						offset = 0;
199805e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1999aca7a94dSNamhyung Kim 						break;
2000aca7a94dSNamhyung Kim 					}
2001aca7a94dSNamhyung Kim 				} else {
2002aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
2003aca7a94dSNamhyung Kim 						offset += h->nr_rows;
2004aca7a94dSNamhyung Kim 						h->row_offset = 0;
2005aca7a94dSNamhyung Kim 					} else {
2006aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
2007aca7a94dSNamhyung Kim 						offset = 0;
200805e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
2009aca7a94dSNamhyung Kim 						break;
2010aca7a94dSNamhyung Kim 					}
2011aca7a94dSNamhyung Kim 				}
2012aca7a94dSNamhyung Kim 			}
2013aca7a94dSNamhyung Kim 
2014d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2015064f1981SNamhyung Kim 							hb->min_pcnt);
2016aca7a94dSNamhyung Kim 			if (nd == NULL)
2017aca7a94dSNamhyung Kim 				break;
2018aca7a94dSNamhyung Kim 			++offset;
201905e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
2020aca7a94dSNamhyung Kim 			if (offset == 0) {
2021aca7a94dSNamhyung Kim 				/*
2022aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
2023aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
2024aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
2025aca7a94dSNamhyung Kim 				 */
2026aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
2027d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
2028aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
2029aca7a94dSNamhyung Kim 				break;
2030aca7a94dSNamhyung Kim 			}
2031aca7a94dSNamhyung Kim 			first = false;
2032aca7a94dSNamhyung Kim 		}
2033aca7a94dSNamhyung Kim 	} else {
203405e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
2035aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
2036aca7a94dSNamhyung Kim 		h->row_offset = 0;
2037aca7a94dSNamhyung Kim 	}
2038aca7a94dSNamhyung Kim }
2039aca7a94dSNamhyung Kim 
2040aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2041d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
2042d0506edbSNamhyung Kim 					   int level)
2043aff3f3f6SArnaldo Carvalho de Melo {
204439ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
204539ee533fSNamhyung Kim 		.fp = fp,
204639ee533fSNamhyung Kim 	};
2047aff3f3f6SArnaldo Carvalho de Melo 
2048d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
204939ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
205039ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
205139ee533fSNamhyung Kim 	return arg.printed;
2052aff3f3f6SArnaldo Carvalho de Melo }
2053aff3f3f6SArnaldo Carvalho de Melo 
2054aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
2055aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
2056aff3f3f6SArnaldo Carvalho de Melo {
2057aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
2058aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2059aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
206026d8b338SNamhyung Kim 	struct perf_hpp hpp = {
206126d8b338SNamhyung Kim 		.buf = s,
206226d8b338SNamhyung Kim 		.size = sizeof(s),
206326d8b338SNamhyung Kim 	};
206426d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
206526d8b338SNamhyung Kim 	bool first = true;
206626d8b338SNamhyung Kim 	int ret;
2067aff3f3f6SArnaldo Carvalho de Melo 
2068fabd37b8SArnaldo Carvalho de Melo 	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2069aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
2070aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
20711b6b678eSArnaldo Carvalho de Melo 	}
2072aff3f3f6SArnaldo Carvalho de Melo 
2073f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
2074361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
2075e67d49a7SNamhyung Kim 			continue;
2076e67d49a7SNamhyung Kim 
207726d8b338SNamhyung Kim 		if (!first) {
207826d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
207926d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
208026d8b338SNamhyung Kim 		} else
208126d8b338SNamhyung Kim 			first = false;
2082aff3f3f6SArnaldo Carvalho de Melo 
208326d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
208489fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
208526d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
208626d8b338SNamhyung Kim 	}
208789fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
2088aff3f3f6SArnaldo Carvalho de Melo 
2089aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
2090d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2091d0506edbSNamhyung Kim 
2092d0506edbSNamhyung Kim 	return printed;
2093d0506edbSNamhyung Kim }
2094d0506edbSNamhyung Kim 
2095d0506edbSNamhyung Kim 
2096d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2097d0506edbSNamhyung Kim 						 struct hist_entry *he,
2098325a6283SNamhyung Kim 						 FILE *fp, int level)
2099d0506edbSNamhyung Kim {
2100d0506edbSNamhyung Kim 	char s[8192];
2101d0506edbSNamhyung Kim 	int printed = 0;
2102d0506edbSNamhyung Kim 	char folded_sign = ' ';
2103d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
2104d0506edbSNamhyung Kim 		.buf = s,
2105d0506edbSNamhyung Kim 		.size = sizeof(s),
2106d0506edbSNamhyung Kim 	};
2107d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
2108325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
2109d0506edbSNamhyung Kim 	bool first = true;
2110d0506edbSNamhyung Kim 	int ret;
2111325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2112d0506edbSNamhyung Kim 
2113d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2114d0506edbSNamhyung Kim 
2115d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
2116d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
2117d0506edbSNamhyung Kim 
2118325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
2119325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2120325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
2121325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2122d0506edbSNamhyung Kim 		if (!first) {
2123d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2124d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
2125d0506edbSNamhyung Kim 		} else
2126d0506edbSNamhyung Kim 			first = false;
2127d0506edbSNamhyung Kim 
2128d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2129d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
2130d0506edbSNamhyung Kim 	}
2131d0506edbSNamhyung Kim 
2132d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2133d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
2134d0506edbSNamhyung Kim 
21351b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
21361b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
21371b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
21381b2dbbf4SNamhyung Kim 
2139d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2140d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
21411b2dbbf4SNamhyung Kim 	}
2142d0506edbSNamhyung Kim 
214313c230abSArnaldo Carvalho de Melo 	strim(s);
214413c230abSArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
2145d0506edbSNamhyung Kim 
2146d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
2147d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2148d0506edbSNamhyung Kim 							   he->depth + 1);
2149d0506edbSNamhyung Kim 	}
2150aff3f3f6SArnaldo Carvalho de Melo 
2151aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2152aff3f3f6SArnaldo Carvalho de Melo }
2153aff3f3f6SArnaldo Carvalho de Melo 
2154aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2155aff3f3f6SArnaldo Carvalho de Melo {
2156064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2157064f1981SNamhyung Kim 						   browser->min_pcnt);
2158aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2159aff3f3f6SArnaldo Carvalho de Melo 
2160aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
2161aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2162aff3f3f6SArnaldo Carvalho de Melo 
2163d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
2164d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2165d0506edbSNamhyung Kim 									 h, fp,
2166325a6283SNamhyung Kim 									 h->depth);
2167d0506edbSNamhyung Kim 		} else {
2168aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
2169d0506edbSNamhyung Kim 		}
2170d0506edbSNamhyung Kim 
2171d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2172d0506edbSNamhyung Kim 					   browser->min_pcnt);
2173aff3f3f6SArnaldo Carvalho de Melo 	}
2174aff3f3f6SArnaldo Carvalho de Melo 
2175aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2176aff3f3f6SArnaldo Carvalho de Melo }
2177aff3f3f6SArnaldo Carvalho de Melo 
2178aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2179aff3f3f6SArnaldo Carvalho de Melo {
2180aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2181aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2182aff3f3f6SArnaldo Carvalho de Melo 
2183aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2184aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2185aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2186aff3f3f6SArnaldo Carvalho de Melo 			break;
2187aff3f3f6SArnaldo Carvalho de Melo 		/*
2188aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2189aff3f3f6SArnaldo Carvalho de Melo  		 */
2190aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2191aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2192aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2193aff3f3f6SArnaldo Carvalho de Melo 		}
2194aff3f3f6SArnaldo Carvalho de Melo 	}
2195aff3f3f6SArnaldo Carvalho de Melo 
2196aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2197aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2198aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
2199c8b5f2c9SArnaldo Carvalho de Melo 		const char *err = str_error_r(errno, bf, sizeof(bf));
22004cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2201aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2202aff3f3f6SArnaldo Carvalho de Melo 	}
2203aff3f3f6SArnaldo Carvalho de Melo 
2204aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2205aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2206aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2207aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2208aff3f3f6SArnaldo Carvalho de Melo 
2209aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2210aff3f3f6SArnaldo Carvalho de Melo }
2211aff3f3f6SArnaldo Carvalho de Melo 
2212fcd86426SJiri Olsa void hist_browser__init(struct hist_browser *browser,
2213fcd86426SJiri Olsa 			struct hists *hists)
2214aca7a94dSNamhyung Kim {
2215b1c7a8f7SJiri Olsa 	struct perf_hpp_fmt *fmt;
2216b1c7a8f7SJiri Olsa 
221705e8b080SArnaldo Carvalho de Melo 	browser->hists			= hists;
221805e8b080SArnaldo Carvalho de Melo 	browser->b.refresh		= hist_browser__refresh;
2219357cfff1SArnaldo Carvalho de Melo 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
222005e8b080SArnaldo Carvalho de Melo 	browser->b.seek			= ui_browser__hists_seek;
222105e8b080SArnaldo Carvalho de Melo 	browser->b.use_navkeypressed	= true;
2222c8302367SJiri Olsa 	browser->show_headers		= symbol_conf.show_hist_headers;
2223ef9ff601SArnaldo Carvalho de Melo 	hist_browser__set_title_space(browser);
2224b1c7a8f7SJiri Olsa 
22258a06b0beSNamhyung Kim 	if (symbol_conf.report_hierarchy) {
22268a06b0beSNamhyung Kim 		struct perf_hpp_list_node *fmt_node;
22278a06b0beSNamhyung Kim 
22288a06b0beSNamhyung Kim 		/* count overhead columns (in the first node) */
22298a06b0beSNamhyung Kim 		fmt_node = list_first_entry(&hists->hpp_formats,
22308a06b0beSNamhyung Kim 					    struct perf_hpp_list_node, list);
22318a06b0beSNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
22328a06b0beSNamhyung Kim 			++browser->b.columns;
22338a06b0beSNamhyung Kim 
22348a06b0beSNamhyung Kim 		/* add a single column for whole hierarchy sort keys*/
22358a06b0beSNamhyung Kim 		++browser->b.columns;
22368a06b0beSNamhyung Kim 	} else {
2237e3b60bc9SNamhyung Kim 		hists__for_each_format(hists, fmt)
2238b1c7a8f7SJiri Olsa 			++browser->b.columns;
22398a06b0beSNamhyung Kim 	}
2240e3b60bc9SNamhyung Kim 
2241e3b60bc9SNamhyung Kim 	hists__reset_column_width(hists);
2242aca7a94dSNamhyung Kim }
2243aca7a94dSNamhyung Kim 
2244fcd86426SJiri Olsa struct hist_browser *hist_browser__new(struct hists *hists)
2245fcd86426SJiri Olsa {
2246fcd86426SJiri Olsa 	struct hist_browser *browser = zalloc(sizeof(*browser));
2247fcd86426SJiri Olsa 
2248fcd86426SJiri Olsa 	if (browser)
2249fcd86426SJiri Olsa 		hist_browser__init(browser, hists);
2250fcd86426SJiri Olsa 
225105e8b080SArnaldo Carvalho de Melo 	return browser;
2252aca7a94dSNamhyung Kim }
2253aca7a94dSNamhyung Kim 
2254a6ec894dSJiri Olsa static struct hist_browser *
225532dcd021SJiri Olsa perf_evsel_browser__new(struct evsel *evsel,
2256a6ec894dSJiri Olsa 			struct hist_browser_timer *hbt,
2257cd0cccbaSArnaldo Carvalho de Melo 			struct perf_env *env,
2258cd0cccbaSArnaldo Carvalho de Melo 			struct annotation_options *annotation_opts)
2259a6ec894dSJiri Olsa {
2260a6ec894dSJiri Olsa 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2261a6ec894dSJiri Olsa 
2262a6ec894dSJiri Olsa 	if (browser) {
2263a6ec894dSJiri Olsa 		browser->hbt   = hbt;
2264a6ec894dSJiri Olsa 		browser->env   = env;
2265f016d24aSArnaldo Carvalho de Melo 		browser->title = hists_browser__scnprintf_title;
2266cd0cccbaSArnaldo Carvalho de Melo 		browser->annotation_opts = annotation_opts;
2267a6ec894dSJiri Olsa 	}
2268a6ec894dSJiri Olsa 	return browser;
2269a6ec894dSJiri Olsa }
2270a6ec894dSJiri Olsa 
2271dabd2012SJiri Olsa void hist_browser__delete(struct hist_browser *browser)
2272aca7a94dSNamhyung Kim {
227305e8b080SArnaldo Carvalho de Melo 	free(browser);
2274aca7a94dSNamhyung Kim }
2275aca7a94dSNamhyung Kim 
227605e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2277aca7a94dSNamhyung Kim {
227805e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2279aca7a94dSNamhyung Kim }
2280aca7a94dSNamhyung Kim 
228105e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2282aca7a94dSNamhyung Kim {
228305e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2284aca7a94dSNamhyung Kim }
2285aca7a94dSNamhyung Kim 
22861e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
22871e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
22881e378ebdSTaeung Song {
22891e378ebdSTaeung Song 	return timer == NULL;
22901e378ebdSTaeung Song }
22911e378ebdSTaeung Song 
2292967a464aSArnaldo Carvalho de Melo static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2293967a464aSArnaldo Carvalho de Melo {
2294967a464aSArnaldo Carvalho de Melo 	struct hist_browser_timer *hbt = browser->hbt;
2295967a464aSArnaldo Carvalho de Melo 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2296967a464aSArnaldo Carvalho de Melo 
22971e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
22981e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
22991e378ebdSTaeung Song 
2300d24e3c98SJiri Olsa 		printed += scnprintf(bf + printed, size - printed,
2301d24e3c98SJiri Olsa 				     " lost: %" PRIu64 "/%" PRIu64,
2302d24e3c98SJiri Olsa 				     top->lost, top->lost_total);
2303d24e3c98SJiri Olsa 
230497f7e0b3SJiri Olsa 		printed += scnprintf(bf + printed, size - printed,
230597f7e0b3SJiri Olsa 				     " drop: %" PRIu64 "/%" PRIu64,
230697f7e0b3SJiri Olsa 				     top->drop, top->drop_total);
230797f7e0b3SJiri Olsa 
23081e378ebdSTaeung Song 		if (top->zero)
23091e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
23108aa5c8edSJiri Olsa 
23118aa5c8edSJiri Olsa 		perf_top__reset_sample_counters(top);
23121e378ebdSTaeung Song 	}
23131e378ebdSTaeung Song 
23148aa5c8edSJiri Olsa 
2315aca7a94dSNamhyung Kim 	return printed;
2316aca7a94dSNamhyung Kim }
2317aca7a94dSNamhyung Kim 
2318aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2319aca7a94dSNamhyung Kim {
2320aca7a94dSNamhyung Kim 	int i;
2321aca7a94dSNamhyung Kim 
232204662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
232304662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2324aca7a94dSNamhyung Kim }
2325aca7a94dSNamhyung Kim 
2326341487abSFeng Tang /*
2327341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2328341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2329341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2330341487abSFeng Tang  */
2331341487abSFeng Tang static bool is_input_name_malloced = false;
2332341487abSFeng Tang 
2333341487abSFeng Tang static int switch_data_file(void)
2334341487abSFeng Tang {
2335341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2336341487abSFeng Tang 	DIR *pwd_dir;
2337341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2338341487abSFeng Tang 	struct dirent *dent;
2339341487abSFeng Tang 
2340341487abSFeng Tang 	pwd = getenv("PWD");
2341341487abSFeng Tang 	if (!pwd)
2342341487abSFeng Tang 		return ret;
2343341487abSFeng Tang 
2344341487abSFeng Tang 	pwd_dir = opendir(pwd);
2345341487abSFeng Tang 	if (!pwd_dir)
2346341487abSFeng Tang 		return ret;
2347341487abSFeng Tang 
2348341487abSFeng Tang 	memset(options, 0, sizeof(options));
23493ef5b402SChangbin Du 	memset(abs_path, 0, sizeof(abs_path));
2350341487abSFeng Tang 
2351341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2352341487abSFeng Tang 		char path[PATH_MAX];
2353341487abSFeng Tang 		u64 magic;
2354341487abSFeng Tang 		char *name = dent->d_name;
2355341487abSFeng Tang 		FILE *file;
2356341487abSFeng Tang 
2357341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2358341487abSFeng Tang 			continue;
2359341487abSFeng Tang 
2360341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2361341487abSFeng Tang 
2362341487abSFeng Tang 		file = fopen(path, "r");
2363341487abSFeng Tang 		if (!file)
2364341487abSFeng Tang 			continue;
2365341487abSFeng Tang 
2366341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2367341487abSFeng Tang 			goto close_file_and_continue;
2368341487abSFeng Tang 
2369341487abSFeng Tang 		if (is_perf_magic(magic)) {
2370341487abSFeng Tang 			options[nr_options] = strdup(name);
2371341487abSFeng Tang 			if (!options[nr_options])
2372341487abSFeng Tang 				goto close_file_and_continue;
2373341487abSFeng Tang 
2374341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2375341487abSFeng Tang 			if (!abs_path[nr_options]) {
237674cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2377341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2378341487abSFeng Tang 				fclose(file);
2379341487abSFeng Tang 				break;
2380341487abSFeng Tang 			}
2381341487abSFeng Tang 
2382341487abSFeng Tang 			nr_options++;
2383341487abSFeng Tang 		}
2384341487abSFeng Tang 
2385341487abSFeng Tang close_file_and_continue:
2386341487abSFeng Tang 		fclose(file);
2387341487abSFeng Tang 		if (nr_options >= 32) {
2388341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2389341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2390341487abSFeng Tang 			break;
2391341487abSFeng Tang 		}
2392341487abSFeng Tang 	}
2393341487abSFeng Tang 	closedir(pwd_dir);
2394341487abSFeng Tang 
2395341487abSFeng Tang 	if (nr_options) {
2396d0712656SArnaldo Carvalho de Melo 		choice = ui__popup_menu(nr_options, options, NULL);
2397341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2398341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2399341487abSFeng Tang 			if (tmp) {
2400341487abSFeng Tang 				if (is_input_name_malloced)
2401341487abSFeng Tang 					free((void *)input_name);
2402341487abSFeng Tang 				input_name = tmp;
2403341487abSFeng Tang 				is_input_name_malloced = true;
2404341487abSFeng Tang 				ret = 0;
2405341487abSFeng Tang 			} else
2406341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2407341487abSFeng Tang 		}
2408341487abSFeng Tang 	}
2409341487abSFeng Tang 
2410341487abSFeng Tang 	free_popup_options(options, nr_options);
2411341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2412341487abSFeng Tang 	return ret;
2413341487abSFeng Tang }
2414341487abSFeng Tang 
2415ea7cd592SNamhyung Kim struct popup_action {
24161d6c49dfSAndi Kleen 	unsigned long		time;
2417ea7cd592SNamhyung Kim 	struct thread 		*thread;
2418ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
241984734b06SKan Liang 	int			socket;
242032dcd021SJiri Olsa 	struct evsel	*evsel;
24214968ac8fSAndi Kleen 	enum rstype		rstype;
2422ea7cd592SNamhyung Kim 
2423ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2424ea7cd592SNamhyung Kim };
2425ea7cd592SNamhyung Kim 
2426bc7cad42SNamhyung Kim static int
2427ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2428bc7cad42SNamhyung Kim {
242932dcd021SJiri Olsa 	struct evsel *evsel;
2430bc7cad42SNamhyung Kim 	struct annotation *notes;
2431bc7cad42SNamhyung Kim 	struct hist_entry *he;
2432bc7cad42SNamhyung Kim 	int err;
2433bc7cad42SNamhyung Kim 
2434f178fd2dSArnaldo Carvalho de Melo 	if (!browser->annotation_opts->objdump_path &&
2435f178fd2dSArnaldo Carvalho de Melo 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2436bc7cad42SNamhyung Kim 		return 0;
2437bc7cad42SNamhyung Kim 
2438ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2439bc7cad42SNamhyung Kim 	if (!notes->src)
2440bc7cad42SNamhyung Kim 		return 0;
2441bc7cad42SNamhyung Kim 
2442848a5e50SJin Yao 	if (browser->block_evsel)
2443848a5e50SJin Yao 		evsel = browser->block_evsel;
2444848a5e50SJin Yao 	else
2445bc7cad42SNamhyung Kim 		evsel = hists_to_evsel(browser->hists);
2446848a5e50SJin Yao 
2447cd0cccbaSArnaldo Carvalho de Melo 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2448cd0cccbaSArnaldo Carvalho de Melo 				       browser->annotation_opts);
2449bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2450bc7cad42SNamhyung Kim 	/*
2451bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2452bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2453bc7cad42SNamhyung Kim 	 */
2454bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2455bc7cad42SNamhyung Kim 		return 1;
2456bc7cad42SNamhyung Kim 
2457bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2458bc7cad42SNamhyung Kim 	if (err)
2459bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2460bc7cad42SNamhyung Kim 	return 0;
2461bc7cad42SNamhyung Kim }
2462bc7cad42SNamhyung Kim 
2463bc7cad42SNamhyung Kim static int
2464ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2465ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
2466d46a4cdfSArnaldo Carvalho de Melo 		 struct map_symbol *ms)
2467bc7cad42SNamhyung Kim {
2468*ea537f22SArnaldo Carvalho de Melo 	if (ms->sym == NULL || ms->map->dso->annotate_warned ||
2469*ea537f22SArnaldo Carvalho de Melo 	    symbol__annotation(ms->sym)->src == NULL)
2470ea7cd592SNamhyung Kim 		return 0;
2471ea7cd592SNamhyung Kim 
2472d46a4cdfSArnaldo Carvalho de Melo 	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2473ea7cd592SNamhyung Kim 		return 0;
2474ea7cd592SNamhyung Kim 
2475d46a4cdfSArnaldo Carvalho de Melo 	act->ms = *ms;
2476ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2477ea7cd592SNamhyung Kim 	return 1;
2478ea7cd592SNamhyung Kim }
2479ea7cd592SNamhyung Kim 
2480ea7cd592SNamhyung Kim static int
2481ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2482ea7cd592SNamhyung Kim {
2483ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2484ea7cd592SNamhyung Kim 
24857cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
24867cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2487599a2f38SNamhyung Kim 		return 0;
2488599a2f38SNamhyung Kim 
2489bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2490bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2491bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2492bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2493bc7cad42SNamhyung Kim 		ui_helpline__pop();
2494bc7cad42SNamhyung Kim 	} else {
2495fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
24967727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2497bc7cad42SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "",
2498bc7cad42SNamhyung Kim 					   thread->tid);
24996962ccb3SNamhyung Kim 		} else {
25006962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
25016962ccb3SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "");
25026962ccb3SNamhyung Kim 		}
25036962ccb3SNamhyung Kim 
2504bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2505bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2506bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2507bc7cad42SNamhyung Kim 	}
2508bc7cad42SNamhyung Kim 
2509bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2510bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2511bc7cad42SNamhyung Kim 	return 0;
2512bc7cad42SNamhyung Kim }
2513bc7cad42SNamhyung Kim 
2514bc7cad42SNamhyung Kim static int
2515ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2516ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2517bc7cad42SNamhyung Kim {
25186962ccb3SNamhyung Kim 	int ret;
25196962ccb3SNamhyung Kim 
25207cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
25217cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2522ea7cd592SNamhyung Kim 		return 0;
2523ea7cd592SNamhyung Kim 
2524fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
25256962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2526ea7cd592SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
2527ea7cd592SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "",
25286962ccb3SNamhyung Kim 			       thread->tid);
25296962ccb3SNamhyung Kim 	} else {
25306962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s thread",
25316962ccb3SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
25326962ccb3SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "");
25336962ccb3SNamhyung Kim 	}
25346962ccb3SNamhyung Kim 	if (ret < 0)
2535ea7cd592SNamhyung Kim 		return 0;
2536ea7cd592SNamhyung Kim 
2537ea7cd592SNamhyung Kim 	act->thread = thread;
2538ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2539ea7cd592SNamhyung Kim 	return 1;
2540ea7cd592SNamhyung Kim }
2541ea7cd592SNamhyung Kim 
2542632003f4SArnaldo Carvalho de Melo static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2543ea7cd592SNamhyung Kim {
254469849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2545599a2f38SNamhyung Kim 		return 0;
2546599a2f38SNamhyung Kim 
2547bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2548bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2549bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2550bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2551bc7cad42SNamhyung Kim 		ui_helpline__pop();
2552bc7cad42SNamhyung Kim 	} else {
25537727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2554045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2555045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
2556bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2557bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2558bc7cad42SNamhyung Kim 	}
2559bc7cad42SNamhyung Kim 
2560bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2561bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2562bc7cad42SNamhyung Kim 	return 0;
2563bc7cad42SNamhyung Kim }
2564bc7cad42SNamhyung Kim 
2565bc7cad42SNamhyung Kim static int
2566632003f4SArnaldo Carvalho de Melo do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2567632003f4SArnaldo Carvalho de Melo {
2568632003f4SArnaldo Carvalho de Melo 	return hists_browser__zoom_map(browser, act->ms.map);
2569632003f4SArnaldo Carvalho de Melo }
2570632003f4SArnaldo Carvalho de Melo 
2571632003f4SArnaldo Carvalho de Melo static int
2572ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2573045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2574bc7cad42SNamhyung Kim {
257569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2576ea7cd592SNamhyung Kim 		return 0;
2577ea7cd592SNamhyung Kim 
2578209f4e70SArnaldo Carvalho de Melo 	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2579ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
2580045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2581ea7cd592SNamhyung Kim 		return 0;
2582ea7cd592SNamhyung Kim 
2583045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2584ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2585ea7cd592SNamhyung Kim 	return 1;
2586ea7cd592SNamhyung Kim }
2587ea7cd592SNamhyung Kim 
2588d5a599d9SArnaldo Carvalho de Melo static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2589d5a599d9SArnaldo Carvalho de Melo {
2590d5a599d9SArnaldo Carvalho de Melo 	hist_browser__toggle_fold(browser);
2591d5a599d9SArnaldo Carvalho de Melo 	return 0;
2592d5a599d9SArnaldo Carvalho de Melo }
2593d5a599d9SArnaldo Carvalho de Melo 
2594d5a599d9SArnaldo Carvalho de Melo static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2595d5a599d9SArnaldo Carvalho de Melo {
2596bdc633feSArnaldo Carvalho de Melo 	char sym_name[512];
2597d5a599d9SArnaldo Carvalho de Melo 
2598bdc633feSArnaldo Carvalho de Melo         if (!hist_browser__selection_has_children(browser))
2599d5a599d9SArnaldo Carvalho de Melo                 return 0;
2600d5a599d9SArnaldo Carvalho de Melo 
2601bdc633feSArnaldo Carvalho de Melo 	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2602bdc633feSArnaldo Carvalho de Melo 		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2603bdc633feSArnaldo Carvalho de Melo 		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2604d5a599d9SArnaldo Carvalho de Melo 		return 0;
2605d5a599d9SArnaldo Carvalho de Melo 
2606d5a599d9SArnaldo Carvalho de Melo 	act->fn = do_toggle_callchain;
2607d5a599d9SArnaldo Carvalho de Melo 	return 1;
2608d5a599d9SArnaldo Carvalho de Melo }
2609d5a599d9SArnaldo Carvalho de Melo 
2610ea7cd592SNamhyung Kim static int
2611ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2612ea7cd592SNamhyung Kim 	      struct popup_action *act)
2613ea7cd592SNamhyung Kim {
2614ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2615bc7cad42SNamhyung Kim 	return 0;
2616bc7cad42SNamhyung Kim }
2617bc7cad42SNamhyung Kim 
2618bc7cad42SNamhyung Kim static int
261969849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2620ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2621ea7cd592SNamhyung Kim {
262269849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2623ea7cd592SNamhyung Kim 		return 0;
2624ea7cd592SNamhyung Kim 
2625ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2626ea7cd592SNamhyung Kim 		return 0;
2627ea7cd592SNamhyung Kim 
2628ea7cd592SNamhyung Kim 	act->ms.map = map;
2629ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2630ea7cd592SNamhyung Kim 	return 1;
2631ea7cd592SNamhyung Kim }
2632ea7cd592SNamhyung Kim 
2633ea7cd592SNamhyung Kim static int
2634bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2635ea7cd592SNamhyung Kim 	      struct popup_action *act)
2636bc7cad42SNamhyung Kim {
26371d6c49dfSAndi Kleen 	char *script_opt;
26381d6c49dfSAndi Kleen 	int len;
26391d6c49dfSAndi Kleen 	int n = 0;
2640bc7cad42SNamhyung Kim 
26411d6c49dfSAndi Kleen 	len = 100;
26421d6c49dfSAndi Kleen 	if (act->thread)
26431d6c49dfSAndi Kleen 		len += strlen(thread__comm_str(act->thread));
26441d6c49dfSAndi Kleen 	else if (act->ms.sym)
26451d6c49dfSAndi Kleen 		len += strlen(act->ms.sym->name);
26461d6c49dfSAndi Kleen 	script_opt = malloc(len);
26471d6c49dfSAndi Kleen 	if (!script_opt)
26481d6c49dfSAndi Kleen 		return -1;
26491d6c49dfSAndi Kleen 
26501d6c49dfSAndi Kleen 	script_opt[0] = 0;
2651ea7cd592SNamhyung Kim 	if (act->thread) {
26521d6c49dfSAndi Kleen 		n = scnprintf(script_opt, len, " -c %s ",
2653ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2654ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
26551d6c49dfSAndi Kleen 		n = scnprintf(script_opt, len, " -S %s ",
2656ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2657bc7cad42SNamhyung Kim 	}
2658bc7cad42SNamhyung Kim 
26591d6c49dfSAndi Kleen 	if (act->time) {
26601d6c49dfSAndi Kleen 		char start[32], end[32];
26611d6c49dfSAndi Kleen 		unsigned long starttime = act->time;
26621d6c49dfSAndi Kleen 		unsigned long endtime = act->time + symbol_conf.time_quantum;
26631d6c49dfSAndi Kleen 
26641d6c49dfSAndi Kleen 		if (starttime == endtime) { /* Display 1ms as fallback */
26651d6c49dfSAndi Kleen 			starttime -= 1*NSEC_PER_MSEC;
26661d6c49dfSAndi Kleen 			endtime += 1*NSEC_PER_MSEC;
26671d6c49dfSAndi Kleen 		}
26681d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(starttime, start, sizeof start);
26691d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(endtime, end, sizeof end);
26701d6c49dfSAndi Kleen 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
26711d6c49dfSAndi Kleen 	}
26721d6c49dfSAndi Kleen 
26736f3da20eSAndi Kleen 	script_browse(script_opt, act->evsel);
26741d6c49dfSAndi Kleen 	free(script_opt);
2675bc7cad42SNamhyung Kim 	return 0;
2676bc7cad42SNamhyung Kim }
2677bc7cad42SNamhyung Kim 
2678bc7cad42SNamhyung Kim static int
26794968ac8fSAndi Kleen do_res_sample_script(struct hist_browser *browser __maybe_unused,
26804968ac8fSAndi Kleen 		     struct popup_action *act)
26814968ac8fSAndi Kleen {
26824968ac8fSAndi Kleen 	struct hist_entry *he;
26834968ac8fSAndi Kleen 
26844968ac8fSAndi Kleen 	he = hist_browser__selected_entry(browser);
26854968ac8fSAndi Kleen 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
26864968ac8fSAndi Kleen 	return 0;
26874968ac8fSAndi Kleen }
26884968ac8fSAndi Kleen 
26894968ac8fSAndi Kleen static int
26901d6c49dfSAndi Kleen add_script_opt_2(struct hist_browser *browser __maybe_unused,
2691ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
26921d6c49dfSAndi Kleen 	       struct thread *thread, struct symbol *sym,
269332dcd021SJiri Olsa 	       struct evsel *evsel, const char *tstr)
2694ea7cd592SNamhyung Kim {
26951d6c49dfSAndi Kleen 
2696ea7cd592SNamhyung Kim 	if (thread) {
26971d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
26981d6c49dfSAndi Kleen 			     thread__comm_str(thread), tstr) < 0)
2699ea7cd592SNamhyung Kim 			return 0;
2700ea7cd592SNamhyung Kim 	} else if (sym) {
27011d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
27021d6c49dfSAndi Kleen 			     sym->name, tstr) < 0)
2703ea7cd592SNamhyung Kim 			return 0;
2704ea7cd592SNamhyung Kim 	} else {
27051d6c49dfSAndi Kleen 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2706ea7cd592SNamhyung Kim 			return 0;
2707ea7cd592SNamhyung Kim 	}
2708ea7cd592SNamhyung Kim 
2709ea7cd592SNamhyung Kim 	act->thread = thread;
2710ea7cd592SNamhyung Kim 	act->ms.sym = sym;
27116f3da20eSAndi Kleen 	act->evsel = evsel;
2712ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2713ea7cd592SNamhyung Kim 	return 1;
2714ea7cd592SNamhyung Kim }
2715ea7cd592SNamhyung Kim 
2716ea7cd592SNamhyung Kim static int
27171d6c49dfSAndi Kleen add_script_opt(struct hist_browser *browser,
27181d6c49dfSAndi Kleen 	       struct popup_action *act, char **optstr,
27196f3da20eSAndi Kleen 	       struct thread *thread, struct symbol *sym,
272032dcd021SJiri Olsa 	       struct evsel *evsel)
27211d6c49dfSAndi Kleen {
27221d6c49dfSAndi Kleen 	int n, j;
27231d6c49dfSAndi Kleen 	struct hist_entry *he;
27241d6c49dfSAndi Kleen 
27256f3da20eSAndi Kleen 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
27261d6c49dfSAndi Kleen 
27271d6c49dfSAndi Kleen 	he = hist_browser__selected_entry(browser);
27281d6c49dfSAndi Kleen 	if (sort_order && strstr(sort_order, "time")) {
27291d6c49dfSAndi Kleen 		char tstr[128];
27301d6c49dfSAndi Kleen 
27311d6c49dfSAndi Kleen 		optstr++;
27321d6c49dfSAndi Kleen 		act++;
27331d6c49dfSAndi Kleen 		j = sprintf(tstr, " in ");
27341d6c49dfSAndi Kleen 		j += timestamp__scnprintf_usec(he->time, tstr + j,
27351d6c49dfSAndi Kleen 					       sizeof tstr - j);
27361d6c49dfSAndi Kleen 		j += sprintf(tstr + j, "-");
27371d6c49dfSAndi Kleen 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
27386f3da20eSAndi Kleen 				          tstr + j, sizeof tstr - j);
27391d6c49dfSAndi Kleen 		n += add_script_opt_2(browser, act, optstr, thread, sym,
27406f3da20eSAndi Kleen 					  evsel, tstr);
27411d6c49dfSAndi Kleen 		act->time = he->time;
27421d6c49dfSAndi Kleen 	}
27431d6c49dfSAndi Kleen 	return n;
27441d6c49dfSAndi Kleen }
27451d6c49dfSAndi Kleen 
27461d6c49dfSAndi Kleen static int
27474968ac8fSAndi Kleen add_res_sample_opt(struct hist_browser *browser __maybe_unused,
27484968ac8fSAndi Kleen 		   struct popup_action *act, char **optstr,
27494968ac8fSAndi Kleen 		   struct res_sample *res_sample,
275032dcd021SJiri Olsa 		   struct evsel *evsel,
27514968ac8fSAndi Kleen 		   enum rstype type)
27524968ac8fSAndi Kleen {
27534968ac8fSAndi Kleen 	if (!res_sample)
27544968ac8fSAndi Kleen 		return 0;
27554968ac8fSAndi Kleen 
27564968ac8fSAndi Kleen 	if (asprintf(optstr, "Show context for individual samples %s",
27574968ac8fSAndi Kleen 		type == A_ASM ? "with assembler" :
27584968ac8fSAndi Kleen 		type == A_SOURCE ? "with source" : "") < 0)
27594968ac8fSAndi Kleen 		return 0;
27604968ac8fSAndi Kleen 
27614968ac8fSAndi Kleen 	act->fn = do_res_sample_script;
27624968ac8fSAndi Kleen 	act->evsel = evsel;
27634968ac8fSAndi Kleen 	act->rstype = type;
27644968ac8fSAndi Kleen 	return 1;
27654968ac8fSAndi Kleen }
27664968ac8fSAndi Kleen 
27674968ac8fSAndi Kleen static int
2768ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2769ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2770bc7cad42SNamhyung Kim {
2771bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2772bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2773bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2774ea7cd592SNamhyung Kim 		return 0;
2775bc7cad42SNamhyung Kim 	}
2776bc7cad42SNamhyung Kim 
2777bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2778bc7cad42SNamhyung Kim }
2779bc7cad42SNamhyung Kim 
2780ea7cd592SNamhyung Kim static int
2781ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2782ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2783ea7cd592SNamhyung Kim {
2784ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2785ea7cd592SNamhyung Kim 		return 0;
2786ea7cd592SNamhyung Kim 
2787ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2788ea7cd592SNamhyung Kim 		return 0;
2789ea7cd592SNamhyung Kim 
2790ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2791ea7cd592SNamhyung Kim 	return 1;
2792ea7cd592SNamhyung Kim }
2793ea7cd592SNamhyung Kim 
2794ea7cd592SNamhyung Kim static int
2795ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2796ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2797ea7cd592SNamhyung Kim {
2798ea7cd592SNamhyung Kim 	return 0;
2799ea7cd592SNamhyung Kim }
2800ea7cd592SNamhyung Kim 
2801ea7cd592SNamhyung Kim static int
2802ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2803ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2804ea7cd592SNamhyung Kim {
2805ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2806ea7cd592SNamhyung Kim 		return 0;
2807ea7cd592SNamhyung Kim 
2808ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2809ea7cd592SNamhyung Kim 	return 1;
2810ea7cd592SNamhyung Kim }
2811ea7cd592SNamhyung Kim 
281284734b06SKan Liang static int
281384734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
281484734b06SKan Liang {
281535a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2816599a2f38SNamhyung Kim 		return 0;
2817599a2f38SNamhyung Kim 
281884734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
281984734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
282084734b06SKan Liang 		browser->hists->socket_filter = -1;
282184734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
282284734b06SKan Liang 	} else {
282384734b06SKan Liang 		browser->hists->socket_filter = act->socket;
282484734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
282584734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
282684734b06SKan Liang 	}
282784734b06SKan Liang 
282884734b06SKan Liang 	hists__filter_by_socket(browser->hists);
282984734b06SKan Liang 	hist_browser__reset(browser);
283084734b06SKan Liang 	return 0;
283184734b06SKan Liang }
283284734b06SKan Liang 
283384734b06SKan Liang static int
283484734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
283584734b06SKan Liang 	       char **optstr, int socket_id)
283684734b06SKan Liang {
283735a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
283884734b06SKan Liang 		return 0;
283984734b06SKan Liang 
284084734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
284184734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
284284734b06SKan Liang 		     socket_id) < 0)
284384734b06SKan Liang 		return 0;
284484734b06SKan Liang 
284584734b06SKan Liang 	act->socket = socket_id;
284684734b06SKan Liang 	act->fn = do_zoom_socket;
284784734b06SKan Liang 	return 1;
284884734b06SKan Liang }
284984734b06SKan Liang 
2850112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2851064f1981SNamhyung Kim {
2852064f1981SNamhyung Kim 	u64 nr_entries = 0;
28532eb3d689SDavidlohr Bueso 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2854064f1981SNamhyung Kim 
2855f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2856268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2857268397cbSNamhyung Kim 		return;
2858268397cbSNamhyung Kim 	}
2859268397cbSNamhyung Kim 
286014135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2861064f1981SNamhyung Kim 		nr_entries++;
2862f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2863064f1981SNamhyung Kim 	}
2864064f1981SNamhyung Kim 
2865112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2866f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2867064f1981SNamhyung Kim }
2868341487abSFeng Tang 
2869b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2870b62e8dfcSNamhyung Kim 					       double percent)
2871b62e8dfcSNamhyung Kim {
2872b62e8dfcSNamhyung Kim 	struct hist_entry *he;
28732eb3d689SDavidlohr Bueso 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2874b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2875b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2876b62e8dfcSNamhyung Kim 
2877b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2878b62e8dfcSNamhyung Kim 
2879b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2880b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2881b62e8dfcSNamhyung Kim 
288279dded87SNamhyung Kim 		if (he->has_no_entry) {
288379dded87SNamhyung Kim 			he->has_no_entry = false;
288479dded87SNamhyung Kim 			he->nr_rows = 0;
288579dded87SNamhyung Kim 		}
288679dded87SNamhyung Kim 
2887fabd37b8SArnaldo Carvalho de Melo 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2888d0506edbSNamhyung Kim 			goto next;
2889d0506edbSNamhyung Kim 
2890b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2891b62e8dfcSNamhyung Kim 			total = he->stat.period;
2892b62e8dfcSNamhyung Kim 
2893b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2894b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2895b62e8dfcSNamhyung Kim 
2896b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2897b62e8dfcSNamhyung Kim 		}
2898b62e8dfcSNamhyung Kim 
2899b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2900b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2901b62e8dfcSNamhyung Kim 
2902d0506edbSNamhyung Kim next:
2903201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2904d0506edbSNamhyung Kim 
2905b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2906b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2907492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2908b62e8dfcSNamhyung Kim 	}
2909b62e8dfcSNamhyung Kim }
2910b62e8dfcSNamhyung Kim 
291132dcd021SJiri Olsa static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2912dd00d486SJiri Olsa 				    const char *helpline,
2913aca7a94dSNamhyung Kim 				    bool left_exits,
291468d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2915064f1981SNamhyung Kim 				    float min_pcnt,
291606cc1a47SKan Liang 				    struct perf_env *env,
2917cd0cccbaSArnaldo Carvalho de Melo 				    bool warn_lost_event,
2918cd0cccbaSArnaldo Carvalho de Melo 				    struct annotation_options *annotation_opts)
2919aca7a94dSNamhyung Kim {
29204ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2921cd0cccbaSArnaldo Carvalho de Melo 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2922ceb75476SLeo Yan 	struct branch_info *bi = NULL;
2923f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2924f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2925ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2926aca7a94dSNamhyung Kim 	int nr_options = 0;
2927aca7a94dSNamhyung Kim 	int key = -1;
2928aca7a94dSNamhyung Kim 	char buf[64];
29299783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2930aca7a94dSNamhyung Kim 
2931e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2932e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2933e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2934e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
29356a02f06eSAndi Kleen 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2936e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2937e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2938e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
29397727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
29407727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
29419218a913SArnaldo Carvalho de Melo 	"+             Expand/Collapse one callchain level\n"		\
2942e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2943e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2944e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2945e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2946105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2947025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2948209f4e70SArnaldo Carvalho de Melo 	"k             Zoom into the kernel map\n"			\
2949b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
295031eb4360SNamhyung Kim 	"m             Display context menu\n"				\
295184734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2952e8e684a5SNamhyung Kim 
2953e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
295449b8e2beSRasmus Villemoes 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
29556dd60135SNamhyung Kim 	"i             Show header information\n"
2956e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2957e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2958e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2959e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2960e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2961e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
296249b8e2beSRasmus Villemoes 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
2963e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2964e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2965e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
296642337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2967fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2968e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2969e8e684a5SNamhyung Kim 
2970aca7a94dSNamhyung Kim 	if (browser == NULL)
2971aca7a94dSNamhyung Kim 		return -1;
2972aca7a94dSNamhyung Kim 
2973ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2974ed426915SNamhyung Kim 	SLang_reset_tty();
2975ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2976ed426915SNamhyung Kim 
297703905048SNamhyung Kim 	if (min_pcnt)
2978064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2979112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2980064f1981SNamhyung Kim 
298184734b06SKan Liang 	browser->pstack = pstack__new(3);
298201f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2983aca7a94dSNamhyung Kim 		goto out;
2984aca7a94dSNamhyung Kim 
2985aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2986aca7a94dSNamhyung Kim 
2987aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2988ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2989aca7a94dSNamhyung Kim 
29905b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
29915b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
29925b591669SNamhyung Kim 
29935c959b6dSArnaldo Carvalho de Melo 	if (!is_report_browser(hbt))
29945c959b6dSArnaldo Carvalho de Melo 		browser->b.no_samples_msg = "Collecting samples...";
29955c959b6dSArnaldo Carvalho de Melo 
2996aca7a94dSNamhyung Kim 	while (1) {
2997f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2998045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
29994c8b9c0fSArnaldo Carvalho de Melo 		int choice;
300084734b06SKan Liang 		int socked_id = -1;
3001aca7a94dSNamhyung Kim 
30024c8b9c0fSArnaldo Carvalho de Melo 		key = 0; // reset key
30034c8b9c0fSArnaldo Carvalho de Melo do_hotkey:		 // key came straight from options ui__popup_menu()
30044c8b9c0fSArnaldo Carvalho de Melo 		choice = nr_options = 0;
30054c8b9c0fSArnaldo Carvalho de Melo 		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3006aca7a94dSNamhyung Kim 
3007aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
3008aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
3009045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
301084734b06SKan Liang 			socked_id = browser->he_selection->socket;
3011aca7a94dSNamhyung Kim 		}
3012aca7a94dSNamhyung Kim 		switch (key) {
3013aca7a94dSNamhyung Kim 		case K_TAB:
3014aca7a94dSNamhyung Kim 		case K_UNTAB:
3015aca7a94dSNamhyung Kim 			if (nr_events == 1)
3016aca7a94dSNamhyung Kim 				continue;
3017aca7a94dSNamhyung Kim 			/*
3018aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
3019aca7a94dSNamhyung Kim 			 * go to the next or previous
3020aca7a94dSNamhyung Kim 			 */
3021aca7a94dSNamhyung Kim 			goto out_free_stack;
3022aca7a94dSNamhyung Kim 		case 'a':
30232e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
3024aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
3025aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
3026aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
3027aca7a94dSNamhyung Kim 				continue;
3028aca7a94dSNamhyung Kim 			}
3029aca7a94dSNamhyung Kim 
3030aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
3031aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
3032aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
3033aca7a94dSNamhyung Kim 				continue;
3034bc7cad42SNamhyung Kim 
3035*ea537f22SArnaldo Carvalho de Melo 			if (symbol__annotation(browser->selection->sym)->src == NULL) {
3036*ea537f22SArnaldo Carvalho de Melo 				ui_browser__warning(&browser->b, delay_secs * 2,
3037*ea537f22SArnaldo Carvalho de Melo 						    "No samples for the \"%s\" symbol.\n\n"
3038*ea537f22SArnaldo Carvalho de Melo 						    "Probably appeared just in a callchain",
3039*ea537f22SArnaldo Carvalho de Melo 						    browser->selection->sym->name);
3040*ea537f22SArnaldo Carvalho de Melo 				continue;
3041*ea537f22SArnaldo Carvalho de Melo 			}
3042*ea537f22SArnaldo Carvalho de Melo 
3043ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
3044ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
3045ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
3046bc7cad42SNamhyung Kim 			continue;
3047aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
3048aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
3049aff3f3f6SArnaldo Carvalho de Melo 			continue;
3050aca7a94dSNamhyung Kim 		case 'd':
3051fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
3052ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
3053bc7cad42SNamhyung Kim 			continue;
3054209f4e70SArnaldo Carvalho de Melo 		case 'k':
3055209f4e70SArnaldo Carvalho de Melo 			if (browser->selection != NULL)
3056209f4e70SArnaldo Carvalho de Melo 				hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3057209f4e70SArnaldo Carvalho de Melo 			continue;
3058a7cb8863SArnaldo Carvalho de Melo 		case 'V':
305921e8c810SAlexis Berlemont 			verbose = (verbose + 1) % 4;
306021e8c810SAlexis Berlemont 			browser->show_dso = verbose > 0;
306121e8c810SAlexis Berlemont 			ui_helpline__fpush("Verbosity level set to %d\n",
306221e8c810SAlexis Berlemont 					   verbose);
3063a7cb8863SArnaldo Carvalho de Melo 			continue;
3064aca7a94dSNamhyung Kim 		case 't':
3065ea7cd592SNamhyung Kim 			actions->thread = thread;
3066ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
3067bc7cad42SNamhyung Kim 			continue;
306884734b06SKan Liang 		case 'S':
306984734b06SKan Liang 			actions->socket = socked_id;
307084734b06SKan Liang 			do_zoom_socket(browser, actions);
307184734b06SKan Liang 			continue;
30725a5626b1SArnaldo Carvalho de Melo 		case '/':
3073aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
30744aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
30754aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
3076aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
3077aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
307805e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
307905e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
3080aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
3081aca7a94dSNamhyung Kim 			}
3082aca7a94dSNamhyung Kim 			continue;
3083cdbab7c2SFeng Tang 		case 'r':
3084ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
3085ea7cd592SNamhyung Kim 				actions->thread = NULL;
3086ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
3087ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
3088ea7cd592SNamhyung Kim 			}
3089c77d8d70SFeng Tang 			continue;
3090341487abSFeng Tang 		case 's':
3091bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
3092ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
3093bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
3094bc7cad42SNamhyung Kim 					goto out_free_stack;
3095bc7cad42SNamhyung Kim 			}
3096341487abSFeng Tang 			continue;
30976dd60135SNamhyung Kim 		case 'i':
30986dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
30996dd60135SNamhyung Kim 			if (env->arch)
31006dd60135SNamhyung Kim 				tui__header_window(env);
31016dd60135SNamhyung Kim 			continue;
3102105eb30fSNamhyung Kim 		case 'F':
3103105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
3104105eb30fSNamhyung Kim 			continue;
310542337a22SNamhyung Kim 		case 'z':
310642337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
310742337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
310842337a22SNamhyung Kim 
310942337a22SNamhyung Kim 				top->zero = !top->zero;
311042337a22SNamhyung Kim 			}
311142337a22SNamhyung Kim 			continue;
3112b62e8dfcSNamhyung Kim 		case 'L':
3113b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
3114b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
3115b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
3116b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
3117b62e8dfcSNamhyung Kim 				char *end;
3118b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
3119b62e8dfcSNamhyung Kim 
3120b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
3121b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
3122b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
3123b62e8dfcSNamhyung Kim 					continue;
3124b62e8dfcSNamhyung Kim 				}
3125b62e8dfcSNamhyung Kim 
3126b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
3127b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
3128b62e8dfcSNamhyung Kim 			}
3129b62e8dfcSNamhyung Kim 			continue;
3130aca7a94dSNamhyung Kim 		case K_F1:
3131aca7a94dSNamhyung Kim 		case 'h':
3132aca7a94dSNamhyung Kim 		case '?':
3133aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
3134e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
3135aca7a94dSNamhyung Kim 			continue;
3136aca7a94dSNamhyung Kim 		case K_ENTER:
3137aca7a94dSNamhyung Kim 		case K_RIGHT:
313831eb4360SNamhyung Kim 		case 'm':
3139aca7a94dSNamhyung Kim 			/* menu */
3140aca7a94dSNamhyung Kim 			break;
314163ab1749SArnaldo Carvalho de Melo 		case K_ESC:
3142aca7a94dSNamhyung Kim 		case K_LEFT: {
3143aca7a94dSNamhyung Kim 			const void *top;
3144aca7a94dSNamhyung Kim 
314501f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
3146aca7a94dSNamhyung Kim 				/*
3147aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
3148aca7a94dSNamhyung Kim 				 */
3149aca7a94dSNamhyung Kim 				if (left_exits)
3150aca7a94dSNamhyung Kim 					goto out_free_stack;
315163ab1749SArnaldo Carvalho de Melo 
315263ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
315363ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
315463ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
315563ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
315663ab1749SArnaldo Carvalho de Melo 
3157aca7a94dSNamhyung Kim 				continue;
3158aca7a94dSNamhyung Kim 			}
31593f777403SArnaldo Carvalho de Melo 			actions->ms.map = map;
31606422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
3161bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
31626422184bSNamhyung Kim 				/*
31636422184bSNamhyung Kim 				 * No need to set actions->dso here since
31646422184bSNamhyung Kim 				 * it's just to remove the current filter.
31656422184bSNamhyung Kim 				 * Ditto for thread below.
31666422184bSNamhyung Kim 				 */
31676422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
316884734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
31696422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
317084734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
317184734b06SKan Liang 				do_zoom_socket(browser, actions);
317284734b06SKan Liang 			}
3173aca7a94dSNamhyung Kim 			continue;
3174aca7a94dSNamhyung Kim 		}
3175aca7a94dSNamhyung Kim 		case 'q':
3176aca7a94dSNamhyung Kim 		case CTRL('c'):
3177516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
3178fbb7997eSArnaldo Carvalho de Melo 		case 'f':
317913d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
318013d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
318113d1e536SNamhyung Kim 
318213d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
318313d1e536SNamhyung Kim 				/*
318413d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
318513d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
318613d1e536SNamhyung Kim 				 */
318713d1e536SNamhyung Kim 				if (top->evlist->enabled) {
318813d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
318913d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
319013d1e536SNamhyung Kim 				} else {
319113d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
319213d1e536SNamhyung Kim 					hbt->refresh = 0;
319313d1e536SNamhyung Kim 				}
319413d1e536SNamhyung Kim 				continue;
319513d1e536SNamhyung Kim 			}
31963e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
3197aca7a94dSNamhyung Kim 		default:
31983e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
3199aca7a94dSNamhyung Kim 			continue;
3200aca7a94dSNamhyung Kim 		}
3201aca7a94dSNamhyung Kim 
32022e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
32030ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
32040ba332f7SArnaldo Carvalho de Melo 
320555369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
3206ceb75476SLeo Yan 
3207ceb75476SLeo Yan 			if (browser->he_selection)
3208aca7a94dSNamhyung Kim 				bi = browser->he_selection->branch_info;
32090ba332f7SArnaldo Carvalho de Melo 
32100ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
32110ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
32120ba332f7SArnaldo Carvalho de Melo 
3213ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3214ea7cd592SNamhyung Kim 						       &actions[nr_options],
3215ea7cd592SNamhyung Kim 						       &options[nr_options],
3216d46a4cdfSArnaldo Carvalho de Melo 						       &bi->from.ms);
3217d46a4cdfSArnaldo Carvalho de Melo 			if (bi->to.ms.sym != bi->from.ms.sym)
3218ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
3219ea7cd592SNamhyung Kim 							&actions[nr_options],
3220ea7cd592SNamhyung Kim 							&options[nr_options],
3221d46a4cdfSArnaldo Carvalho de Melo 							&bi->to.ms);
3222aca7a94dSNamhyung Kim 		} else {
3223ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3224ea7cd592SNamhyung Kim 						       &actions[nr_options],
3225ea7cd592SNamhyung Kim 						       &options[nr_options],
3226d46a4cdfSArnaldo Carvalho de Melo 						       browser->selection);
3227446fb96cSArnaldo Carvalho de Melo 		}
32280ba332f7SArnaldo Carvalho de Melo skip_annotation:
3229ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
3230ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
3231ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
3232045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
3233d5a599d9SArnaldo Carvalho de Melo 		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3234ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
3235ea7cd592SNamhyung Kim 					  &options[nr_options],
3236bd315aabSWang Nan 					  browser->selection ?
3237bd315aabSWang Nan 						browser->selection->map : NULL);
323884734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
323984734b06SKan Liang 					     &options[nr_options],
324084734b06SKan Liang 					     socked_id);
3241cdbab7c2SFeng Tang 		/* perf script support */
3242b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
3243b1baae89SNamhyung Kim 			goto skip_scripting;
3244b1baae89SNamhyung Kim 
3245cdbab7c2SFeng Tang 		if (browser->he_selection) {
3246fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
3247ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3248ea7cd592SNamhyung Kim 							     &actions[nr_options],
3249ea7cd592SNamhyung Kim 							     &options[nr_options],
32506f3da20eSAndi Kleen 							     thread, NULL, evsel);
32512eafd410SNamhyung Kim 			}
3252bd315aabSWang Nan 			/*
3253bd315aabSWang Nan 			 * Note that browser->selection != NULL
3254bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
3255bd315aabSWang Nan 			 * so we don't need to check browser->selection
3256bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
3257bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
3258bd315aabSWang Nan 			 *
3259bd315aabSWang Nan 			 * See hist_browser__show_entry.
3260bd315aabSWang Nan 			 */
32612e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
3262ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3263ea7cd592SNamhyung Kim 							     &actions[nr_options],
3264ea7cd592SNamhyung Kim 							     &options[nr_options],
32656f3da20eSAndi Kleen 							     NULL, browser->selection->sym,
32666f3da20eSAndi Kleen 							     evsel);
3267cdbab7c2SFeng Tang 			}
3268c221acb0SNamhyung Kim 		}
3269ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
32706f3da20eSAndi Kleen 					     &options[nr_options], NULL, NULL, evsel);
32714968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
32724968ac8fSAndi Kleen 						 &options[nr_options],
32734968ac8fSAndi Kleen 				 hist_browser__selected_entry(browser)->res_samples,
32744968ac8fSAndi Kleen 				 evsel, A_NORMAL);
32754968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
32764968ac8fSAndi Kleen 						 &options[nr_options],
32774968ac8fSAndi Kleen 				 hist_browser__selected_entry(browser)->res_samples,
32784968ac8fSAndi Kleen 				 evsel, A_ASM);
32794968ac8fSAndi Kleen 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
32804968ac8fSAndi Kleen 						 &options[nr_options],
32814968ac8fSAndi Kleen 				 hist_browser__selected_entry(browser)->res_samples,
32824968ac8fSAndi Kleen 				 evsel, A_SOURCE);
3283ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
3284ea7cd592SNamhyung Kim 					     &options[nr_options]);
3285b1baae89SNamhyung Kim skip_scripting:
3286ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
3287ea7cd592SNamhyung Kim 					   &options[nr_options]);
3288aca7a94dSNamhyung Kim 
3289ea7cd592SNamhyung Kim 		do {
3290ea7cd592SNamhyung Kim 			struct popup_action *act;
3291ea7cd592SNamhyung Kim 
32924c8b9c0fSArnaldo Carvalho de Melo 			choice = ui__popup_menu(nr_options, options, &key);
32934c8b9c0fSArnaldo Carvalho de Melo 			if (choice == -1)
3294aca7a94dSNamhyung Kim 				break;
3295aca7a94dSNamhyung Kim 
32964c8b9c0fSArnaldo Carvalho de Melo 			if (choice == nr_options)
32974c8b9c0fSArnaldo Carvalho de Melo 				goto do_hotkey;
32984c8b9c0fSArnaldo Carvalho de Melo 
3299ea7cd592SNamhyung Kim 			act = &actions[choice];
3300ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
3301ea7cd592SNamhyung Kim 		} while (key == 1);
3302aca7a94dSNamhyung Kim 
3303bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3304341487abSFeng Tang 			break;
3305341487abSFeng Tang 	}
3306aca7a94dSNamhyung Kim out_free_stack:
330701f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3308aca7a94dSNamhyung Kim out:
3309aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3310f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3311aca7a94dSNamhyung Kim 	return key;
3312aca7a94dSNamhyung Kim }
3313aca7a94dSNamhyung Kim 
331432dcd021SJiri Olsa struct evsel_menu {
3315aca7a94dSNamhyung Kim 	struct ui_browser b;
331632dcd021SJiri Olsa 	struct evsel *selection;
3317cd0cccbaSArnaldo Carvalho de Melo 	struct annotation_options *annotation_opts;
3318aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3319064f1981SNamhyung Kim 	float min_pcnt;
3320ce80d3beSKan Liang 	struct perf_env *env;
3321aca7a94dSNamhyung Kim };
3322aca7a94dSNamhyung Kim 
3323aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3324aca7a94dSNamhyung Kim 				   void *entry, int row)
3325aca7a94dSNamhyung Kim {
332632dcd021SJiri Olsa 	struct evsel_menu *menu = container_of(browser,
332732dcd021SJiri Olsa 						    struct evsel_menu, b);
3328b27c4eceSJiri Olsa 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
33294ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3330aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
33314ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
33327289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
3333aca7a94dSNamhyung Kim 	char bf[256], unit;
3334aca7a94dSNamhyung Kim 	const char *warn = " ";
3335aca7a94dSNamhyung Kim 	size_t printed;
3336aca7a94dSNamhyung Kim 
3337aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3338aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3339aca7a94dSNamhyung Kim 
3340759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
334132dcd021SJiri Olsa 		struct evsel *pos;
3342717e263fSNamhyung Kim 
3343717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
3344717e263fSNamhyung Kim 
3345717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
33464ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
33474ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3348717e263fSNamhyung Kim 		}
3349717e263fSNamhyung Kim 	}
3350717e263fSNamhyung Kim 
3351aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3352aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3353aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3354517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3355aca7a94dSNamhyung Kim 
33564ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3357aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3358aca7a94dSNamhyung Kim 		menu->lost_events = true;
3359aca7a94dSNamhyung Kim 		if (!current_entry)
3360aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3361aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3362aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3363aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3364aca7a94dSNamhyung Kim 		warn = bf;
3365aca7a94dSNamhyung Kim 	}
3366aca7a94dSNamhyung Kim 
336726270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3368aca7a94dSNamhyung Kim 
3369aca7a94dSNamhyung Kim 	if (current_entry)
3370aca7a94dSNamhyung Kim 		menu->selection = evsel;
3371aca7a94dSNamhyung Kim }
3372aca7a94dSNamhyung Kim 
337332dcd021SJiri Olsa static int perf_evsel_menu__run(struct evsel_menu *menu,
3374aca7a94dSNamhyung Kim 				int nr_events, const char *help,
337506cc1a47SKan Liang 				struct hist_browser_timer *hbt,
337606cc1a47SKan Liang 				bool warn_lost_event)
3377aca7a94dSNamhyung Kim {
337863503dbaSJiri Olsa 	struct evlist *evlist = menu->b.priv;
337932dcd021SJiri Olsa 	struct evsel *pos;
3380dd00d486SJiri Olsa 	const char *title = "Available samples";
33819783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3382aca7a94dSNamhyung Kim 	int key;
3383aca7a94dSNamhyung Kim 
3384aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3385aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3386aca7a94dSNamhyung Kim 		return -1;
3387aca7a94dSNamhyung Kim 
3388aca7a94dSNamhyung Kim 	while (1) {
3389aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3390aca7a94dSNamhyung Kim 
3391aca7a94dSNamhyung Kim 		switch (key) {
3392aca7a94dSNamhyung Kim 		case K_TIMER:
3393ceb75476SLeo Yan 			if (hbt)
33949783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3395aca7a94dSNamhyung Kim 
339606cc1a47SKan Liang 			if (!menu->lost_events_warned &&
339706cc1a47SKan Liang 			    menu->lost_events &&
339806cc1a47SKan Liang 			    warn_lost_event) {
3399aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3400aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3401aca7a94dSNamhyung Kim 			}
3402aca7a94dSNamhyung Kim 			continue;
3403aca7a94dSNamhyung Kim 		case K_RIGHT:
3404aca7a94dSNamhyung Kim 		case K_ENTER:
3405aca7a94dSNamhyung Kim 			if (!menu->selection)
3406aca7a94dSNamhyung Kim 				continue;
3407aca7a94dSNamhyung Kim 			pos = menu->selection;
3408aca7a94dSNamhyung Kim browse_hists:
3409aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
3410aca7a94dSNamhyung Kim 			/*
3411aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3412aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3413aca7a94dSNamhyung Kim 			 */
34149783adf7SNamhyung Kim 			if (hbt)
34159783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3416aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
3417dd00d486SJiri Olsa 						       true, hbt,
3418064f1981SNamhyung Kim 						       menu->min_pcnt,
341906cc1a47SKan Liang 						       menu->env,
3420cd0cccbaSArnaldo Carvalho de Melo 						       warn_lost_event,
3421cd0cccbaSArnaldo Carvalho de Melo 						       menu->annotation_opts);
3422aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3423aca7a94dSNamhyung Kim 			switch (key) {
3424aca7a94dSNamhyung Kim 			case K_TAB:
3425ce9036a6SJiri Olsa 				if (pos->core.node.next == &evlist->core.entries)
3426515dbe48SJiri Olsa 					pos = evlist__first(evlist);
3427aca7a94dSNamhyung Kim 				else
34289a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
3429aca7a94dSNamhyung Kim 				goto browse_hists;
3430aca7a94dSNamhyung Kim 			case K_UNTAB:
3431ce9036a6SJiri Olsa 				if (pos->core.node.prev == &evlist->core.entries)
3432515dbe48SJiri Olsa 					pos = evlist__last(evlist);
3433aca7a94dSNamhyung Kim 				else
3434d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
3435aca7a94dSNamhyung Kim 				goto browse_hists;
3436341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
3437aca7a94dSNamhyung Kim 			case 'q':
3438aca7a94dSNamhyung Kim 			case CTRL('c'):
3439aca7a94dSNamhyung Kim 				goto out;
344063ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3441aca7a94dSNamhyung Kim 			default:
3442aca7a94dSNamhyung Kim 				continue;
3443aca7a94dSNamhyung Kim 			}
3444aca7a94dSNamhyung Kim 		case K_LEFT:
3445aca7a94dSNamhyung Kim 			continue;
3446aca7a94dSNamhyung Kim 		case K_ESC:
3447aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3448aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3449aca7a94dSNamhyung Kim 				continue;
3450aca7a94dSNamhyung Kim 			/* Fall thru */
3451aca7a94dSNamhyung Kim 		case 'q':
3452aca7a94dSNamhyung Kim 		case CTRL('c'):
3453aca7a94dSNamhyung Kim 			goto out;
3454aca7a94dSNamhyung Kim 		default:
3455aca7a94dSNamhyung Kim 			continue;
3456aca7a94dSNamhyung Kim 		}
3457aca7a94dSNamhyung Kim 	}
3458aca7a94dSNamhyung Kim 
3459aca7a94dSNamhyung Kim out:
3460aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3461aca7a94dSNamhyung Kim 	return key;
3462aca7a94dSNamhyung Kim }
3463aca7a94dSNamhyung Kim 
3464316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3465fc24d7c2SNamhyung Kim 				 void *entry)
3466fc24d7c2SNamhyung Kim {
3467b27c4eceSJiri Olsa 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3468fc24d7c2SNamhyung Kim 
3469fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3470fc24d7c2SNamhyung Kim 		return true;
3471fc24d7c2SNamhyung Kim 
3472fc24d7c2SNamhyung Kim 	return false;
3473fc24d7c2SNamhyung Kim }
3474fc24d7c2SNamhyung Kim 
347563503dbaSJiri Olsa static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3476fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
347768d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
3478064f1981SNamhyung Kim 					   float min_pcnt,
347906cc1a47SKan Liang 					   struct perf_env *env,
3480cd0cccbaSArnaldo Carvalho de Melo 					   bool warn_lost_event,
3481cd0cccbaSArnaldo Carvalho de Melo 					   struct annotation_options *annotation_opts)
3482aca7a94dSNamhyung Kim {
348332dcd021SJiri Olsa 	struct evsel *pos;
348432dcd021SJiri Olsa 	struct evsel_menu menu = {
3485aca7a94dSNamhyung Kim 		.b = {
3486ce9036a6SJiri Olsa 			.entries    = &evlist->core.entries,
3487aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3488aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3489aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3490fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3491fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3492aca7a94dSNamhyung Kim 			.priv	    = evlist,
3493aca7a94dSNamhyung Kim 		},
3494064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
349568d80758SNamhyung Kim 		.env = env,
3496cd0cccbaSArnaldo Carvalho de Melo 		.annotation_opts = annotation_opts,
3497aca7a94dSNamhyung Kim 	};
3498aca7a94dSNamhyung Kim 
3499aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3500aca7a94dSNamhyung Kim 
3501e5cadb93SArnaldo Carvalho de Melo 	evlist__for_each_entry(evlist, pos) {
35027289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
3503aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3504aca7a94dSNamhyung Kim 
3505aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3506aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3507aca7a94dSNamhyung Kim 	}
3508aca7a94dSNamhyung Kim 
350906cc1a47SKan Liang 	return perf_evsel_menu__run(&menu, nr_entries, help,
351006cc1a47SKan Liang 				    hbt, warn_lost_event);
3511aca7a94dSNamhyung Kim }
3512aca7a94dSNamhyung Kim 
351363503dbaSJiri Olsa int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
351468d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
3515064f1981SNamhyung Kim 				  float min_pcnt,
351606cc1a47SKan Liang 				  struct perf_env *env,
3517cd0cccbaSArnaldo Carvalho de Melo 				  bool warn_lost_event,
3518cd0cccbaSArnaldo Carvalho de Melo 				  struct annotation_options *annotation_opts)
3519aca7a94dSNamhyung Kim {
35206484d2f9SJiri Olsa 	int nr_entries = evlist->core.nr_entries;
3521fc24d7c2SNamhyung Kim 
3522fc24d7c2SNamhyung Kim single_entry:
3523fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
3524515dbe48SJiri Olsa 		struct evsel *first = evlist__first(evlist);
3525fc24d7c2SNamhyung Kim 
3526fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
3527dd00d486SJiri Olsa 						false, hbt, min_pcnt,
3528cd0cccbaSArnaldo Carvalho de Melo 						env, warn_lost_event,
3529cd0cccbaSArnaldo Carvalho de Melo 						annotation_opts);
3530aca7a94dSNamhyung Kim 	}
3531aca7a94dSNamhyung Kim 
3532fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
353332dcd021SJiri Olsa 		struct evsel *pos;
3534fc24d7c2SNamhyung Kim 
3535fc24d7c2SNamhyung Kim 		nr_entries = 0;
3536e5cadb93SArnaldo Carvalho de Melo 		evlist__for_each_entry(evlist, pos) {
3537fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
3538fc24d7c2SNamhyung Kim 				nr_entries++;
35390050f7aaSArnaldo Carvalho de Melo 		}
3540fc24d7c2SNamhyung Kim 
3541fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3542fc24d7c2SNamhyung Kim 			goto single_entry;
3543fc24d7c2SNamhyung Kim 	}
3544fc24d7c2SNamhyung Kim 
3545fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
354606cc1a47SKan Liang 					       hbt, min_pcnt, env,
3547cd0cccbaSArnaldo Carvalho de Melo 					       warn_lost_event,
3548cd0cccbaSArnaldo Carvalho de Melo 					       annotation_opts);
3549aca7a94dSNamhyung Kim }
35505cb456afSJin Yao 
35515cb456afSJin Yao static int block_hists_browser__title(struct hist_browser *browser, char *bf,
35525cb456afSJin Yao 				      size_t size)
35535cb456afSJin Yao {
35545cb456afSJin Yao 	struct hists *hists = evsel__hists(browser->block_evsel);
35555cb456afSJin Yao 	const char *evname = perf_evsel__name(browser->block_evsel);
35565cb456afSJin Yao 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
35575cb456afSJin Yao 	int ret;
35585cb456afSJin Yao 
35595cb456afSJin Yao 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
35605cb456afSJin Yao 	if (evname)
35615cb456afSJin Yao 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
35625cb456afSJin Yao 
35635cb456afSJin Yao 	return 0;
35645cb456afSJin Yao }
35655cb456afSJin Yao 
35665cb456afSJin Yao int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3567848a5e50SJin Yao 			   float min_percent, struct perf_env *env,
3568848a5e50SJin Yao 			   struct annotation_options *annotation_opts)
35695cb456afSJin Yao {
35705cb456afSJin Yao 	struct hists *hists = &bh->block_hists;
35715cb456afSJin Yao 	struct hist_browser *browser;
35725cb456afSJin Yao 	int key = -1;
3573848a5e50SJin Yao 	struct popup_action action;
35745cb456afSJin Yao 	static const char help[] =
35755cb456afSJin Yao 	" q             Quit \n";
35765cb456afSJin Yao 
35775cb456afSJin Yao 	browser = hist_browser__new(hists);
35785cb456afSJin Yao 	if (!browser)
35795cb456afSJin Yao 		return -1;
35805cb456afSJin Yao 
35815cb456afSJin Yao 	browser->block_evsel = evsel;
35825cb456afSJin Yao 	browser->title = block_hists_browser__title;
35835cb456afSJin Yao 	browser->min_pcnt = min_percent;
3584848a5e50SJin Yao 	browser->env = env;
3585848a5e50SJin Yao 	browser->annotation_opts = annotation_opts;
35865cb456afSJin Yao 
35875cb456afSJin Yao 	/* reset abort key so that it can get Ctrl-C as a key */
35885cb456afSJin Yao 	SLang_reset_tty();
35895cb456afSJin Yao 	SLang_init_tty(0, 0, 0);
35905cb456afSJin Yao 
3591848a5e50SJin Yao 	memset(&action, 0, sizeof(action));
3592848a5e50SJin Yao 
35935cb456afSJin Yao 	while (1) {
3594d10ec006SArnaldo Carvalho de Melo 		key = hist_browser__run(browser, "? - help", true, 0);
35955cb456afSJin Yao 
35965cb456afSJin Yao 		switch (key) {
35975cb456afSJin Yao 		case 'q':
35985cb456afSJin Yao 			goto out;
35995cb456afSJin Yao 		case '?':
36005cb456afSJin Yao 			ui_browser__help_window(&browser->b, help);
36015cb456afSJin Yao 			break;
3602848a5e50SJin Yao 		case 'a':
3603848a5e50SJin Yao 		case K_ENTER:
3604848a5e50SJin Yao 			if (!browser->selection ||
3605848a5e50SJin Yao 			    !browser->selection->sym) {
3606848a5e50SJin Yao 				continue;
3607848a5e50SJin Yao 			}
3608848a5e50SJin Yao 
3609848a5e50SJin Yao 			action.ms.map = browser->selection->map;
3610848a5e50SJin Yao 			action.ms.sym = browser->selection->sym;
3611848a5e50SJin Yao 			do_annotate(browser, &action);
3612848a5e50SJin Yao 			continue;
36135cb456afSJin Yao 		default:
36145cb456afSJin Yao 			break;
36155cb456afSJin Yao 		}
36165cb456afSJin Yao 	}
36175cb456afSJin Yao 
36185cb456afSJin Yao out:
36195cb456afSJin Yao 	hist_browser__delete(browser);
36205cb456afSJin Yao 	return 0;
36215cb456afSJin Yao }
3622