xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 06cc1a47)
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>
9b0742e90SArnaldo Carvalho de Melo #include <sys/ttydefaults.h>
10aca7a94dSNamhyung Kim 
11aca7a94dSNamhyung Kim #include "../../util/evsel.h"
12aca7a94dSNamhyung Kim #include "../../util/evlist.h"
13aca7a94dSNamhyung Kim #include "../../util/hist.h"
14aca7a94dSNamhyung Kim #include "../../util/pstack.h"
15aca7a94dSNamhyung Kim #include "../../util/sort.h"
16aca7a94dSNamhyung Kim #include "../../util/util.h"
1742337a22SNamhyung Kim #include "../../util/top.h"
18e7ff8920SArnaldo Carvalho de Melo #include "../../util/thread.h"
1968d80758SNamhyung Kim #include "../../arch/common.h"
20aca7a94dSNamhyung Kim 
21f758990fSJiri Olsa #include "../browsers/hists.h"
22aca7a94dSNamhyung Kim #include "../helpline.h"
23aca7a94dSNamhyung Kim #include "../util.h"
24aca7a94dSNamhyung Kim #include "../ui.h"
25aca7a94dSNamhyung Kim #include "map.h"
26d755330cSJiri Olsa #include "annotate.h"
27632a5cabSArnaldo Carvalho de Melo #include "srcline.h"
28a067558eSArnaldo Carvalho de Melo #include "string2.h"
2958db1d6eSArnaldo Carvalho de Melo #include "units.h"
30aca7a94dSNamhyung Kim 
313d689ed6SArnaldo Carvalho de Melo #include "sane_ctype.h"
323d689ed6SArnaldo Carvalho de Melo 
33f5951d56SNamhyung Kim extern void hist_browser__init_hpp(void);
34f5951d56SNamhyung Kim 
355b91a86fSJiri Olsa static int perf_evsel_browser_title(struct hist_browser *browser,
361e378ebdSTaeung Song 				    char *bf, size_t size);
37112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb);
38aca7a94dSNamhyung Kim 
39c3b78952SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
40c3b78952SNamhyung Kim 					     float min_pcnt);
41c3b78952SNamhyung Kim 
42268397cbSNamhyung Kim static bool hist_browser__has_filter(struct hist_browser *hb)
43268397cbSNamhyung Kim {
445a1a99cdSJiri Olsa 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
45268397cbSNamhyung Kim }
46268397cbSNamhyung Kim 
474fabf3d1SHe Kuang static int hist_browser__get_folding(struct hist_browser *browser)
484fabf3d1SHe Kuang {
494fabf3d1SHe Kuang 	struct rb_node *nd;
504fabf3d1SHe Kuang 	struct hists *hists = browser->hists;
514fabf3d1SHe Kuang 	int unfolded_rows = 0;
524fabf3d1SHe Kuang 
534fabf3d1SHe Kuang 	for (nd = rb_first(&hists->entries);
544fabf3d1SHe Kuang 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
55f5b763feSNamhyung Kim 	     nd = rb_hierarchy_next(nd)) {
564fabf3d1SHe Kuang 		struct hist_entry *he =
574fabf3d1SHe Kuang 			rb_entry(nd, struct hist_entry, rb_node);
584fabf3d1SHe Kuang 
59f5b763feSNamhyung Kim 		if (he->leaf && he->unfolded)
604fabf3d1SHe Kuang 			unfolded_rows += he->nr_rows;
614fabf3d1SHe Kuang 	}
624fabf3d1SHe Kuang 	return unfolded_rows;
634fabf3d1SHe Kuang }
644fabf3d1SHe Kuang 
65c3b78952SNamhyung Kim static u32 hist_browser__nr_entries(struct hist_browser *hb)
66c3b78952SNamhyung Kim {
67c3b78952SNamhyung Kim 	u32 nr_entries;
68c3b78952SNamhyung Kim 
69f5b763feSNamhyung Kim 	if (symbol_conf.report_hierarchy)
70f5b763feSNamhyung Kim 		nr_entries = hb->nr_hierarchy_entries;
71f5b763feSNamhyung Kim 	else if (hist_browser__has_filter(hb))
72c3b78952SNamhyung Kim 		nr_entries = hb->nr_non_filtered_entries;
73c3b78952SNamhyung Kim 	else
74c3b78952SNamhyung Kim 		nr_entries = hb->hists->nr_entries;
75c3b78952SNamhyung Kim 
764fabf3d1SHe Kuang 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
77c3b78952SNamhyung Kim 	return nr_entries + hb->nr_callchain_rows;
78c3b78952SNamhyung Kim }
79c3b78952SNamhyung Kim 
80025bf7eaSArnaldo Carvalho de Melo static void hist_browser__update_rows(struct hist_browser *hb)
81025bf7eaSArnaldo Carvalho de Melo {
82025bf7eaSArnaldo Carvalho de Melo 	struct ui_browser *browser = &hb->b;
83f8e6710dSJiri Olsa 	struct hists *hists = hb->hists;
84f8e6710dSJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
85f8e6710dSJiri Olsa 	u16 header_offset, index_row;
86025bf7eaSArnaldo Carvalho de Melo 
87f8e6710dSJiri Olsa 	header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
88025bf7eaSArnaldo Carvalho de Melo 	browser->rows = browser->height - header_offset;
89025bf7eaSArnaldo Carvalho de Melo 	/*
90025bf7eaSArnaldo Carvalho de Melo 	 * Verify if we were at the last line and that line isn't
91025bf7eaSArnaldo Carvalho de Melo 	 * visibe because we now show the header line(s).
92025bf7eaSArnaldo Carvalho de Melo 	 */
93025bf7eaSArnaldo Carvalho de Melo 	index_row = browser->index - browser->top_idx;
94025bf7eaSArnaldo Carvalho de Melo 	if (index_row >= browser->rows)
95025bf7eaSArnaldo Carvalho de Melo 		browser->index -= index_row - browser->rows + 1;
96025bf7eaSArnaldo Carvalho de Melo }
97025bf7eaSArnaldo Carvalho de Melo 
98357cfff1SArnaldo Carvalho de Melo static void hist_browser__refresh_dimensions(struct ui_browser *browser)
99aca7a94dSNamhyung Kim {
100357cfff1SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
101357cfff1SArnaldo Carvalho de Melo 
102aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
103357cfff1SArnaldo Carvalho de Melo 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
104357cfff1SArnaldo Carvalho de Melo 	/*
105357cfff1SArnaldo Carvalho de Melo  	 * FIXME: Just keeping existing behaviour, but this really should be
106357cfff1SArnaldo Carvalho de Melo  	 *	  before updating browser->width, as it will invalidate the
107357cfff1SArnaldo Carvalho de Melo  	 *	  calculation above. Fix this and the fallout in another
108357cfff1SArnaldo Carvalho de Melo  	 *	  changeset.
109357cfff1SArnaldo Carvalho de Melo  	 */
110357cfff1SArnaldo Carvalho de Melo 	ui_browser__refresh_dimensions(browser);
111025bf7eaSArnaldo Carvalho de Melo 	hist_browser__update_rows(hb);
112aca7a94dSNamhyung Kim }
113aca7a94dSNamhyung Kim 
114ca3ff33bSArnaldo Carvalho de Melo static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
115ca3ff33bSArnaldo Carvalho de Melo {
116f8e6710dSJiri Olsa 	struct hists *hists = browser->hists;
117f8e6710dSJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
118f8e6710dSJiri Olsa 	u16 header_offset;
119025bf7eaSArnaldo Carvalho de Melo 
120f8e6710dSJiri Olsa 	header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
121025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, row + header_offset, column);
122ca3ff33bSArnaldo Carvalho de Melo }
123ca3ff33bSArnaldo Carvalho de Melo 
12405e8b080SArnaldo Carvalho de Melo static void hist_browser__reset(struct hist_browser *browser)
125aca7a94dSNamhyung Kim {
126c3b78952SNamhyung Kim 	/*
127c3b78952SNamhyung Kim 	 * The hists__remove_entry_filter() already folds non-filtered
128c3b78952SNamhyung Kim 	 * entries so we can assume it has 0 callchain rows.
129c3b78952SNamhyung Kim 	 */
130c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
131c3b78952SNamhyung Kim 
132268397cbSNamhyung Kim 	hist_browser__update_nr_entries(browser);
133c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
134357cfff1SArnaldo Carvalho de Melo 	hist_browser__refresh_dimensions(&browser->b);
13505e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
136aca7a94dSNamhyung Kim }
137aca7a94dSNamhyung Kim 
138aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
139aca7a94dSNamhyung Kim {
140aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
141aca7a94dSNamhyung Kim }
142aca7a94dSNamhyung Kim 
14305e8b080SArnaldo Carvalho de Melo static char hist_entry__folded(const struct hist_entry *he)
144aca7a94dSNamhyung Kim {
1453698dab1SNamhyung Kim 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
146aca7a94dSNamhyung Kim }
147aca7a94dSNamhyung Kim 
14805e8b080SArnaldo Carvalho de Melo static char callchain_list__folded(const struct callchain_list *cl)
149aca7a94dSNamhyung Kim {
1503698dab1SNamhyung Kim 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
151aca7a94dSNamhyung Kim }
152aca7a94dSNamhyung Kim 
1533698dab1SNamhyung Kim static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
154aca7a94dSNamhyung Kim {
1553698dab1SNamhyung Kim 	cl->unfolded = unfold ? cl->has_children : false;
156aca7a94dSNamhyung Kim }
157aca7a94dSNamhyung Kim 
15805e8b080SArnaldo Carvalho de Melo static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
159aca7a94dSNamhyung Kim {
1602a704fc8SMilian Wolff 	int n = 0;
161aca7a94dSNamhyung Kim 	struct rb_node *nd;
162aca7a94dSNamhyung Kim 
16305e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
164aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
165aca7a94dSNamhyung Kim 		struct callchain_list *chain;
166aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
167aca7a94dSNamhyung Kim 
168aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
169aca7a94dSNamhyung Kim 			++n;
1700d3eb0b7SJin Yao 
171aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
172aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
173aca7a94dSNamhyung Kim 			if (folded_sign == '+')
174aca7a94dSNamhyung Kim 				break;
175aca7a94dSNamhyung Kim 		}
176aca7a94dSNamhyung Kim 
177aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
178aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
179aca7a94dSNamhyung Kim 	}
180aca7a94dSNamhyung Kim 
181aca7a94dSNamhyung Kim 	return n;
182aca7a94dSNamhyung Kim }
183aca7a94dSNamhyung Kim 
1844b3a3212SNamhyung Kim static int callchain_node__count_flat_rows(struct callchain_node *node)
1854b3a3212SNamhyung Kim {
1864b3a3212SNamhyung Kim 	struct callchain_list *chain;
1874b3a3212SNamhyung Kim 	char folded_sign = 0;
1884b3a3212SNamhyung Kim 	int n = 0;
1894b3a3212SNamhyung Kim 
1904b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->parent_val, list) {
1914b3a3212SNamhyung Kim 		if (!folded_sign) {
1924b3a3212SNamhyung Kim 			/* only check first chain list entry */
1934b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1944b3a3212SNamhyung Kim 			if (folded_sign == '+')
1954b3a3212SNamhyung Kim 				return 1;
1964b3a3212SNamhyung Kim 		}
1974b3a3212SNamhyung Kim 		n++;
1984b3a3212SNamhyung Kim 	}
1994b3a3212SNamhyung Kim 
2004b3a3212SNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
2014b3a3212SNamhyung Kim 		if (!folded_sign) {
2024b3a3212SNamhyung Kim 			/* node->parent_val list might be empty */
2034b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
2044b3a3212SNamhyung Kim 			if (folded_sign == '+')
2054b3a3212SNamhyung Kim 				return 1;
2064b3a3212SNamhyung Kim 		}
2074b3a3212SNamhyung Kim 		n++;
2084b3a3212SNamhyung Kim 	}
2094b3a3212SNamhyung Kim 
2104b3a3212SNamhyung Kim 	return n;
2114b3a3212SNamhyung Kim }
2124b3a3212SNamhyung Kim 
2138c430a34SNamhyung Kim static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
2148c430a34SNamhyung Kim {
2158c430a34SNamhyung Kim 	return 1;
2168c430a34SNamhyung Kim }
2178c430a34SNamhyung Kim 
218aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
219aca7a94dSNamhyung Kim {
220aca7a94dSNamhyung Kim 	struct callchain_list *chain;
221aca7a94dSNamhyung Kim 	bool unfolded = false;
2222a704fc8SMilian Wolff 	int n = 0;
223aca7a94dSNamhyung Kim 
2244b3a3212SNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT)
2254b3a3212SNamhyung Kim 		return callchain_node__count_flat_rows(node);
2268c430a34SNamhyung Kim 	else if (callchain_param.mode == CHAIN_FOLDED)
2278c430a34SNamhyung Kim 		return callchain_node__count_folded_rows(node);
2284b3a3212SNamhyung Kim 
229aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
230aca7a94dSNamhyung Kim 		++n;
2310d3eb0b7SJin Yao 
2323698dab1SNamhyung Kim 		unfolded = chain->unfolded;
233aca7a94dSNamhyung Kim 	}
234aca7a94dSNamhyung Kim 
235aca7a94dSNamhyung Kim 	if (unfolded)
236aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
237aca7a94dSNamhyung Kim 
238aca7a94dSNamhyung Kim 	return n;
239aca7a94dSNamhyung Kim }
240aca7a94dSNamhyung Kim 
241aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
242aca7a94dSNamhyung Kim {
243aca7a94dSNamhyung Kim 	struct rb_node *nd;
244aca7a94dSNamhyung Kim 	int n = 0;
245aca7a94dSNamhyung Kim 
246aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
247aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
248aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
249aca7a94dSNamhyung Kim 	}
250aca7a94dSNamhyung Kim 
251aca7a94dSNamhyung Kim 	return n;
252aca7a94dSNamhyung Kim }
253aca7a94dSNamhyung Kim 
254f5b763feSNamhyung Kim static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
255f5b763feSNamhyung Kim 				bool include_children)
256f5b763feSNamhyung Kim {
257f5b763feSNamhyung Kim 	int count = 0;
258f5b763feSNamhyung Kim 	struct rb_node *node;
259f5b763feSNamhyung Kim 	struct hist_entry *child;
260f5b763feSNamhyung Kim 
261f5b763feSNamhyung Kim 	if (he->leaf)
262f5b763feSNamhyung Kim 		return callchain__count_rows(&he->sorted_chain);
263f5b763feSNamhyung Kim 
26479dded87SNamhyung Kim 	if (he->has_no_entry)
26579dded87SNamhyung Kim 		return 1;
26679dded87SNamhyung Kim 
267f5b763feSNamhyung Kim 	node = rb_first(&he->hroot_out);
268f5b763feSNamhyung Kim 	while (node) {
269f5b763feSNamhyung Kim 		float percent;
270f5b763feSNamhyung Kim 
271f5b763feSNamhyung Kim 		child = rb_entry(node, struct hist_entry, rb_node);
272f5b763feSNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
273f5b763feSNamhyung Kim 
274f5b763feSNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt) {
275f5b763feSNamhyung Kim 			count++;
276f5b763feSNamhyung Kim 
277f5b763feSNamhyung Kim 			if (include_children && child->unfolded)
278f5b763feSNamhyung Kim 				count += hierarchy_count_rows(hb, child, true);
279f5b763feSNamhyung Kim 		}
280f5b763feSNamhyung Kim 
281f5b763feSNamhyung Kim 		node = rb_next(node);
282f5b763feSNamhyung Kim 	}
283f5b763feSNamhyung Kim 	return count;
284f5b763feSNamhyung Kim }
285f5b763feSNamhyung Kim 
2863698dab1SNamhyung Kim static bool hist_entry__toggle_fold(struct hist_entry *he)
287aca7a94dSNamhyung Kim {
2883698dab1SNamhyung Kim 	if (!he)
289aca7a94dSNamhyung Kim 		return false;
290aca7a94dSNamhyung Kim 
2913698dab1SNamhyung Kim 	if (!he->has_children)
292aca7a94dSNamhyung Kim 		return false;
293aca7a94dSNamhyung Kim 
2943698dab1SNamhyung Kim 	he->unfolded = !he->unfolded;
2953698dab1SNamhyung Kim 	return true;
2963698dab1SNamhyung Kim }
2973698dab1SNamhyung Kim 
2983698dab1SNamhyung Kim static bool callchain_list__toggle_fold(struct callchain_list *cl)
2993698dab1SNamhyung Kim {
3003698dab1SNamhyung Kim 	if (!cl)
3013698dab1SNamhyung Kim 		return false;
3023698dab1SNamhyung Kim 
3033698dab1SNamhyung Kim 	if (!cl->has_children)
3043698dab1SNamhyung Kim 		return false;
3053698dab1SNamhyung Kim 
3063698dab1SNamhyung Kim 	cl->unfolded = !cl->unfolded;
307aca7a94dSNamhyung Kim 	return true;
308aca7a94dSNamhyung Kim }
309aca7a94dSNamhyung Kim 
31005e8b080SArnaldo Carvalho de Melo static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
311aca7a94dSNamhyung Kim {
31205e8b080SArnaldo Carvalho de Melo 	struct rb_node *nd = rb_first(&node->rb_root);
313aca7a94dSNamhyung Kim 
31405e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
315aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
316aca7a94dSNamhyung Kim 		struct callchain_list *chain;
317aca7a94dSNamhyung Kim 		bool first = true;
318aca7a94dSNamhyung Kim 
319aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
320aca7a94dSNamhyung Kim 			if (first) {
321aca7a94dSNamhyung Kim 				first = false;
3223698dab1SNamhyung Kim 				chain->has_children = chain->list.next != &child->val ||
323aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
324aca7a94dSNamhyung Kim 			} else
3253698dab1SNamhyung Kim 				chain->has_children = chain->list.next == &child->val &&
326aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
327aca7a94dSNamhyung Kim 		}
328aca7a94dSNamhyung Kim 
329aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
330aca7a94dSNamhyung Kim 	}
331aca7a94dSNamhyung Kim }
332aca7a94dSNamhyung Kim 
333a7444af6SNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *node,
334a7444af6SNamhyung Kim 					       bool has_sibling)
335aca7a94dSNamhyung Kim {
336aca7a94dSNamhyung Kim 	struct callchain_list *chain;
337aca7a94dSNamhyung Kim 
338a7444af6SNamhyung Kim 	chain = list_entry(node->val.next, struct callchain_list, list);
3393698dab1SNamhyung Kim 	chain->has_children = has_sibling;
340a7444af6SNamhyung Kim 
34190989035SAndres Freund 	if (!list_empty(&node->val)) {
34282162b5aSNamhyung Kim 		chain = list_entry(node->val.prev, struct callchain_list, list);
3433698dab1SNamhyung Kim 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
34482162b5aSNamhyung Kim 	}
345aca7a94dSNamhyung Kim 
34605e8b080SArnaldo Carvalho de Melo 	callchain_node__init_have_children_rb_tree(node);
347aca7a94dSNamhyung Kim }
348aca7a94dSNamhyung Kim 
34905e8b080SArnaldo Carvalho de Melo static void callchain__init_have_children(struct rb_root *root)
350aca7a94dSNamhyung Kim {
351a7444af6SNamhyung Kim 	struct rb_node *nd = rb_first(root);
352a7444af6SNamhyung Kim 	bool has_sibling = nd && rb_next(nd);
353aca7a94dSNamhyung Kim 
35405e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
355aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
356a7444af6SNamhyung Kim 		callchain_node__init_have_children(node, has_sibling);
3578c430a34SNamhyung Kim 		if (callchain_param.mode == CHAIN_FLAT ||
3588c430a34SNamhyung Kim 		    callchain_param.mode == CHAIN_FOLDED)
3594b3a3212SNamhyung Kim 			callchain_node__make_parent_list(node);
360aca7a94dSNamhyung Kim 	}
361aca7a94dSNamhyung Kim }
362aca7a94dSNamhyung Kim 
36305e8b080SArnaldo Carvalho de Melo static void hist_entry__init_have_children(struct hist_entry *he)
364aca7a94dSNamhyung Kim {
365f5b763feSNamhyung Kim 	if (he->init_have_children)
366f5b763feSNamhyung Kim 		return;
367f5b763feSNamhyung Kim 
368f5b763feSNamhyung Kim 	if (he->leaf) {
3693698dab1SNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
37005e8b080SArnaldo Carvalho de Melo 		callchain__init_have_children(&he->sorted_chain);
371f5b763feSNamhyung Kim 	} else {
372f5b763feSNamhyung Kim 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
373aca7a94dSNamhyung Kim 	}
374f5b763feSNamhyung Kim 
375f5b763feSNamhyung Kim 	he->init_have_children = true;
376aca7a94dSNamhyung Kim }
377aca7a94dSNamhyung Kim 
37805e8b080SArnaldo Carvalho de Melo static bool hist_browser__toggle_fold(struct hist_browser *browser)
379aca7a94dSNamhyung Kim {
38005e8b080SArnaldo Carvalho de Melo 	struct hist_entry *he = browser->he_selection;
3813698dab1SNamhyung Kim 	struct map_symbol *ms = browser->selection;
3823698dab1SNamhyung Kim 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
3833698dab1SNamhyung Kim 	bool has_children;
384aca7a94dSNamhyung Kim 
3854938cf0cSWang Nan 	if (!he || !ms)
3864938cf0cSWang Nan 		return false;
3874938cf0cSWang Nan 
3883698dab1SNamhyung Kim 	if (ms == &he->ms)
3893698dab1SNamhyung Kim 		has_children = hist_entry__toggle_fold(he);
3903698dab1SNamhyung Kim 	else
3913698dab1SNamhyung Kim 		has_children = callchain_list__toggle_fold(cl);
3923698dab1SNamhyung Kim 
3933698dab1SNamhyung Kim 	if (has_children) {
394f5b763feSNamhyung Kim 		int child_rows = 0;
395f5b763feSNamhyung Kim 
396aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
397c3b78952SNamhyung Kim 		browser->b.nr_entries -= he->nr_rows;
398aca7a94dSNamhyung Kim 
399f5b763feSNamhyung Kim 		if (he->leaf)
400f5b763feSNamhyung Kim 			browser->nr_callchain_rows -= he->nr_rows;
401f5b763feSNamhyung Kim 		else
402f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries -= he->nr_rows;
403f5b763feSNamhyung Kim 
404f5b763feSNamhyung Kim 		if (symbol_conf.report_hierarchy)
405f5b763feSNamhyung Kim 			child_rows = hierarchy_count_rows(browser, he, true);
406f5b763feSNamhyung Kim 
407f5b763feSNamhyung Kim 		if (he->unfolded) {
408f5b763feSNamhyung Kim 			if (he->leaf)
4090d3eb0b7SJin Yao 				he->nr_rows = callchain__count_rows(
4100d3eb0b7SJin Yao 						&he->sorted_chain);
411aca7a94dSNamhyung Kim 			else
412f5b763feSNamhyung Kim 				he->nr_rows = hierarchy_count_rows(browser, he, false);
413f5b763feSNamhyung Kim 
414f5b763feSNamhyung Kim 			/* account grand children */
415f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
416f5b763feSNamhyung Kim 				browser->b.nr_entries += child_rows - he->nr_rows;
41779dded87SNamhyung Kim 
41879dded87SNamhyung Kim 			if (!he->leaf && he->nr_rows == 0) {
41979dded87SNamhyung Kim 				he->has_no_entry = true;
42079dded87SNamhyung Kim 				he->nr_rows = 1;
42179dded87SNamhyung Kim 			}
422f5b763feSNamhyung Kim 		} else {
423f5b763feSNamhyung Kim 			if (symbol_conf.report_hierarchy)
424f5b763feSNamhyung Kim 				browser->b.nr_entries -= child_rows - he->nr_rows;
425f5b763feSNamhyung Kim 
42679dded87SNamhyung Kim 			if (he->has_no_entry)
42779dded87SNamhyung Kim 				he->has_no_entry = false;
42879dded87SNamhyung Kim 
429aca7a94dSNamhyung Kim 			he->nr_rows = 0;
430f5b763feSNamhyung Kim 		}
431c3b78952SNamhyung Kim 
432c3b78952SNamhyung Kim 		browser->b.nr_entries += he->nr_rows;
433f5b763feSNamhyung Kim 
434f5b763feSNamhyung Kim 		if (he->leaf)
435c3b78952SNamhyung Kim 			browser->nr_callchain_rows += he->nr_rows;
436f5b763feSNamhyung Kim 		else
437f5b763feSNamhyung Kim 			browser->nr_hierarchy_entries += he->nr_rows;
438aca7a94dSNamhyung Kim 
439aca7a94dSNamhyung Kim 		return true;
440aca7a94dSNamhyung Kim 	}
441aca7a94dSNamhyung Kim 
442aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
443aca7a94dSNamhyung Kim 	return false;
444aca7a94dSNamhyung Kim }
445aca7a94dSNamhyung Kim 
44605e8b080SArnaldo Carvalho de Melo static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
447aca7a94dSNamhyung Kim {
448aca7a94dSNamhyung Kim 	int n = 0;
449aca7a94dSNamhyung Kim 	struct rb_node *nd;
450aca7a94dSNamhyung Kim 
45105e8b080SArnaldo Carvalho de Melo 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
452aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
453aca7a94dSNamhyung Kim 		struct callchain_list *chain;
454aca7a94dSNamhyung Kim 		bool has_children = false;
455aca7a94dSNamhyung Kim 
456aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
457aca7a94dSNamhyung Kim 			++n;
4583698dab1SNamhyung Kim 			callchain_list__set_folding(chain, unfold);
4593698dab1SNamhyung Kim 			has_children = chain->has_children;
460aca7a94dSNamhyung Kim 		}
461aca7a94dSNamhyung Kim 
462aca7a94dSNamhyung Kim 		if (has_children)
463aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
464aca7a94dSNamhyung Kim 	}
465aca7a94dSNamhyung Kim 
466aca7a94dSNamhyung Kim 	return n;
467aca7a94dSNamhyung Kim }
468aca7a94dSNamhyung Kim 
469aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
470aca7a94dSNamhyung Kim {
471aca7a94dSNamhyung Kim 	struct callchain_list *chain;
472aca7a94dSNamhyung Kim 	bool has_children = false;
473aca7a94dSNamhyung Kim 	int n = 0;
474aca7a94dSNamhyung Kim 
475aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
476aca7a94dSNamhyung Kim 		++n;
4773698dab1SNamhyung Kim 		callchain_list__set_folding(chain, unfold);
4783698dab1SNamhyung Kim 		has_children = chain->has_children;
479aca7a94dSNamhyung Kim 	}
480aca7a94dSNamhyung Kim 
481aca7a94dSNamhyung Kim 	if (has_children)
482aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
483aca7a94dSNamhyung Kim 
484aca7a94dSNamhyung Kim 	return n;
485aca7a94dSNamhyung Kim }
486aca7a94dSNamhyung Kim 
487aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
488aca7a94dSNamhyung Kim {
489aca7a94dSNamhyung Kim 	struct rb_node *nd;
490aca7a94dSNamhyung Kim 	int n = 0;
491aca7a94dSNamhyung Kim 
492aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
493aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
494aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
495aca7a94dSNamhyung Kim 	}
496aca7a94dSNamhyung Kim 
497aca7a94dSNamhyung Kim 	return n;
498aca7a94dSNamhyung Kim }
499aca7a94dSNamhyung Kim 
500492b1010SNamhyung Kim static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
501492b1010SNamhyung Kim 				 bool unfold __maybe_unused)
502492b1010SNamhyung Kim {
503492b1010SNamhyung Kim 	float percent;
504492b1010SNamhyung Kim 	struct rb_node *nd;
505492b1010SNamhyung Kim 	struct hist_entry *child;
506492b1010SNamhyung Kim 	int n = 0;
507492b1010SNamhyung Kim 
508492b1010SNamhyung Kim 	for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
509492b1010SNamhyung Kim 		child = rb_entry(nd, struct hist_entry, rb_node);
510492b1010SNamhyung Kim 		percent = hist_entry__get_percent_limit(child);
511492b1010SNamhyung Kim 		if (!child->filtered && percent >= hb->min_pcnt)
512492b1010SNamhyung Kim 			n++;
513492b1010SNamhyung Kim 	}
514492b1010SNamhyung Kim 
515492b1010SNamhyung Kim 	return n;
516492b1010SNamhyung Kim }
517492b1010SNamhyung Kim 
518b33f9226SJiri Olsa static void __hist_entry__set_folding(struct hist_entry *he,
519492b1010SNamhyung Kim 				      struct hist_browser *hb, bool unfold)
520aca7a94dSNamhyung Kim {
52105e8b080SArnaldo Carvalho de Melo 	hist_entry__init_have_children(he);
5223698dab1SNamhyung Kim 	he->unfolded = unfold ? he->has_children : false;
523aca7a94dSNamhyung Kim 
5243698dab1SNamhyung Kim 	if (he->has_children) {
525492b1010SNamhyung Kim 		int n;
526492b1010SNamhyung Kim 
527492b1010SNamhyung Kim 		if (he->leaf)
528492b1010SNamhyung Kim 			n = callchain__set_folding(&he->sorted_chain, unfold);
529492b1010SNamhyung Kim 		else
530492b1010SNamhyung Kim 			n = hierarchy_set_folding(hb, he, unfold);
531492b1010SNamhyung Kim 
53205e8b080SArnaldo Carvalho de Melo 		he->nr_rows = unfold ? n : 0;
533aca7a94dSNamhyung Kim 	} else
53405e8b080SArnaldo Carvalho de Melo 		he->nr_rows = 0;
535aca7a94dSNamhyung Kim }
536aca7a94dSNamhyung Kim 
537b33f9226SJiri Olsa static void hist_entry__set_folding(struct hist_entry *he,
538b33f9226SJiri Olsa 				    struct hist_browser *browser, bool unfold)
539aca7a94dSNamhyung Kim {
540492b1010SNamhyung Kim 	double percent;
541aca7a94dSNamhyung Kim 
542492b1010SNamhyung Kim 	percent = hist_entry__get_percent_limit(he);
543492b1010SNamhyung Kim 	if (he->filtered || percent < browser->min_pcnt)
544b33f9226SJiri Olsa 		return;
545b33f9226SJiri Olsa 
546b33f9226SJiri Olsa 	__hist_entry__set_folding(he, browser, unfold);
547492b1010SNamhyung Kim 
548492b1010SNamhyung Kim 	if (!he->depth || unfold)
549492b1010SNamhyung Kim 		browser->nr_hierarchy_entries++;
550492b1010SNamhyung Kim 	if (he->leaf)
551c3b78952SNamhyung Kim 		browser->nr_callchain_rows += he->nr_rows;
55279dded87SNamhyung Kim 	else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
55379dded87SNamhyung Kim 		browser->nr_hierarchy_entries++;
55479dded87SNamhyung Kim 		he->has_no_entry = true;
55579dded87SNamhyung Kim 		he->nr_rows = 1;
55679dded87SNamhyung Kim 	} else
55779dded87SNamhyung Kim 		he->has_no_entry = false;
558aca7a94dSNamhyung Kim }
559b33f9226SJiri Olsa 
560b33f9226SJiri Olsa static void
561b33f9226SJiri Olsa __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
562b33f9226SJiri Olsa {
563b33f9226SJiri Olsa 	struct rb_node *nd;
564b33f9226SJiri Olsa 	struct hist_entry *he;
565b33f9226SJiri Olsa 
566b33f9226SJiri Olsa 	nd = rb_first(&browser->hists->entries);
567b33f9226SJiri Olsa 	while (nd) {
568b33f9226SJiri Olsa 		he = rb_entry(nd, struct hist_entry, rb_node);
569b33f9226SJiri Olsa 
570b33f9226SJiri Olsa 		/* set folding state even if it's currently folded */
571b33f9226SJiri Olsa 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
572b33f9226SJiri Olsa 
573b33f9226SJiri Olsa 		hist_entry__set_folding(he, browser, unfold);
574b33f9226SJiri Olsa 	}
575aca7a94dSNamhyung Kim }
576aca7a94dSNamhyung Kim 
57705e8b080SArnaldo Carvalho de Melo static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
578aca7a94dSNamhyung Kim {
579492b1010SNamhyung Kim 	browser->nr_hierarchy_entries = 0;
580c3b78952SNamhyung Kim 	browser->nr_callchain_rows = 0;
581c3b78952SNamhyung Kim 	__hist_browser__set_folding(browser, unfold);
582c3b78952SNamhyung Kim 
583c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
584aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
58505e8b080SArnaldo Carvalho de Melo 	ui_browser__reset_index(&browser->b);
586aca7a94dSNamhyung Kim }
587aca7a94dSNamhyung Kim 
5880e3fa7a7SJiri Olsa static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
5890e3fa7a7SJiri Olsa {
5900e3fa7a7SJiri Olsa 	if (!browser->he_selection)
5910e3fa7a7SJiri Olsa 		return;
5920e3fa7a7SJiri Olsa 
5930e3fa7a7SJiri Olsa 	hist_entry__set_folding(browser->he_selection, browser, unfold);
5940e3fa7a7SJiri Olsa 	browser->b.nr_entries = hist_browser__nr_entries(browser);
5950e3fa7a7SJiri Olsa }
5960e3fa7a7SJiri Olsa 
597aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
598aca7a94dSNamhyung Kim {
599aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
600aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
601aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
602aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
603aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
604aca7a94dSNamhyung Kim }
605aca7a94dSNamhyung Kim 
6065b91a86fSJiri Olsa static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
6075b91a86fSJiri Olsa {
6085b91a86fSJiri Olsa 	return browser->title ? browser->title(browser, bf, size) : 0;
6095b91a86fSJiri Olsa }
6105b91a86fSJiri Olsa 
611*06cc1a47SKan Liang int hist_browser__run(struct hist_browser *browser, const char *help,
612*06cc1a47SKan Liang 		      bool warn_lost_event)
613aca7a94dSNamhyung Kim {
614aca7a94dSNamhyung Kim 	int key;
615aca7a94dSNamhyung Kim 	char title[160];
616c2a51ab8SNamhyung Kim 	struct hist_browser_timer *hbt = browser->hbt;
6179783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
618aca7a94dSNamhyung Kim 
61905e8b080SArnaldo Carvalho de Melo 	browser->b.entries = &browser->hists->entries;
620c3b78952SNamhyung Kim 	browser->b.nr_entries = hist_browser__nr_entries(browser);
621aca7a94dSNamhyung Kim 
6225b91a86fSJiri Olsa 	hist_browser__title(browser, title, sizeof(title));
623aca7a94dSNamhyung Kim 
624090cff3eSNamhyung Kim 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
625aca7a94dSNamhyung Kim 		return -1;
626aca7a94dSNamhyung Kim 
627aca7a94dSNamhyung Kim 	while (1) {
62805e8b080SArnaldo Carvalho de Melo 		key = ui_browser__run(&browser->b, delay_secs);
629aca7a94dSNamhyung Kim 
630aca7a94dSNamhyung Kim 		switch (key) {
631fa5df943SNamhyung Kim 		case K_TIMER: {
632fa5df943SNamhyung Kim 			u64 nr_entries;
6339783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
634fa5df943SNamhyung Kim 
635c6111523SNamhyung Kim 			if (hist_browser__has_filter(browser) ||
636c6111523SNamhyung Kim 			    symbol_conf.report_hierarchy)
637112f761fSNamhyung Kim 				hist_browser__update_nr_entries(browser);
638fa5df943SNamhyung Kim 
639c3b78952SNamhyung Kim 			nr_entries = hist_browser__nr_entries(browser);
640fa5df943SNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, nr_entries);
641aca7a94dSNamhyung Kim 
642*06cc1a47SKan Liang 			if (warn_lost_event &&
643*06cc1a47SKan Liang 			    (browser->hists->stats.nr_lost_warned !=
644*06cc1a47SKan Liang 			    browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
64505e8b080SArnaldo Carvalho de Melo 				browser->hists->stats.nr_lost_warned =
64605e8b080SArnaldo Carvalho de Melo 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
64705e8b080SArnaldo Carvalho de Melo 				ui_browser__warn_lost_events(&browser->b);
648aca7a94dSNamhyung Kim 			}
649aca7a94dSNamhyung Kim 
6505b91a86fSJiri Olsa 			hist_browser__title(browser, title, sizeof(title));
65105e8b080SArnaldo Carvalho de Melo 			ui_browser__show_title(&browser->b, title);
652aca7a94dSNamhyung Kim 			continue;
653fa5df943SNamhyung Kim 		}
654aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
655aca7a94dSNamhyung Kim 			static int seq;
65605e8b080SArnaldo Carvalho de Melo 			struct hist_entry *h = rb_entry(browser->b.top,
657aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
658aca7a94dSNamhyung Kim 			ui_helpline__pop();
65962c95ae3SArnaldo Carvalho de Melo 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
66005e8b080SArnaldo Carvalho de Melo 					   seq++, browser->b.nr_entries,
66105e8b080SArnaldo Carvalho de Melo 					   browser->hists->nr_entries,
66262c95ae3SArnaldo Carvalho de Melo 					   browser->b.rows,
66305e8b080SArnaldo Carvalho de Melo 					   browser->b.index,
66405e8b080SArnaldo Carvalho de Melo 					   browser->b.top_idx,
665aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
666aca7a94dSNamhyung Kim 		}
667aca7a94dSNamhyung Kim 			break;
668aca7a94dSNamhyung Kim 		case 'C':
669aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
67005e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, false);
671aca7a94dSNamhyung Kim 			break;
6720e3fa7a7SJiri Olsa 		case 'c':
6730e3fa7a7SJiri Olsa 			/* Collapse the selected entry. */
6740e3fa7a7SJiri Olsa 			hist_browser__set_folding_selected(browser, false);
6750e3fa7a7SJiri Olsa 			break;
676aca7a94dSNamhyung Kim 		case 'E':
677aca7a94dSNamhyung Kim 			/* Expand the whole world. */
67805e8b080SArnaldo Carvalho de Melo 			hist_browser__set_folding(browser, true);
679aca7a94dSNamhyung Kim 			break;
6800e3fa7a7SJiri Olsa 		case 'e':
6810e3fa7a7SJiri Olsa 			/* Expand the selected entry. */
6820e3fa7a7SJiri Olsa 			hist_browser__set_folding_selected(browser, true);
6830e3fa7a7SJiri Olsa 			break;
684025bf7eaSArnaldo Carvalho de Melo 		case 'H':
685025bf7eaSArnaldo Carvalho de Melo 			browser->show_headers = !browser->show_headers;
686025bf7eaSArnaldo Carvalho de Melo 			hist_browser__update_rows(browser);
687025bf7eaSArnaldo Carvalho de Melo 			break;
688aca7a94dSNamhyung Kim 		case K_ENTER:
68905e8b080SArnaldo Carvalho de Melo 			if (hist_browser__toggle_fold(browser))
690aca7a94dSNamhyung Kim 				break;
691aca7a94dSNamhyung Kim 			/* fall thru */
692aca7a94dSNamhyung Kim 		default:
693aca7a94dSNamhyung Kim 			goto out;
694aca7a94dSNamhyung Kim 		}
695aca7a94dSNamhyung Kim 	}
696aca7a94dSNamhyung Kim out:
69705e8b080SArnaldo Carvalho de Melo 	ui_browser__hide(&browser->b);
698aca7a94dSNamhyung Kim 	return key;
699aca7a94dSNamhyung Kim }
700aca7a94dSNamhyung Kim 
70139ee533fSNamhyung Kim struct callchain_print_arg {
70239ee533fSNamhyung Kim 	/* for hists browser */
70339ee533fSNamhyung Kim 	off_t	row_offset;
70439ee533fSNamhyung Kim 	bool	is_current_entry;
70539ee533fSNamhyung Kim 
70639ee533fSNamhyung Kim 	/* for file dump */
70739ee533fSNamhyung Kim 	FILE	*fp;
70839ee533fSNamhyung Kim 	int	printed;
70939ee533fSNamhyung Kim };
71039ee533fSNamhyung Kim 
71139ee533fSNamhyung Kim typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
71239ee533fSNamhyung Kim 					 struct callchain_list *chain,
71339ee533fSNamhyung Kim 					 const char *str, int offset,
71439ee533fSNamhyung Kim 					 unsigned short row,
71539ee533fSNamhyung Kim 					 struct callchain_print_arg *arg);
71639ee533fSNamhyung Kim 
717f4536dddSNamhyung Kim static void hist_browser__show_callchain_entry(struct hist_browser *browser,
718f4536dddSNamhyung Kim 					       struct callchain_list *chain,
71939ee533fSNamhyung Kim 					       const char *str, int offset,
72039ee533fSNamhyung Kim 					       unsigned short row,
72139ee533fSNamhyung Kim 					       struct callchain_print_arg *arg)
722f4536dddSNamhyung Kim {
723f4536dddSNamhyung Kim 	int color, width;
72439ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
72570e97278SArnaldo Carvalho de Melo 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
726f4536dddSNamhyung Kim 
727f4536dddSNamhyung Kim 	color = HE_COLORSET_NORMAL;
728f4536dddSNamhyung Kim 	width = browser->b.width - (offset + 2);
729f4536dddSNamhyung Kim 	if (ui_browser__is_current_entry(&browser->b, row)) {
730f4536dddSNamhyung Kim 		browser->selection = &chain->ms;
731f4536dddSNamhyung Kim 		color = HE_COLORSET_SELECTED;
73239ee533fSNamhyung Kim 		arg->is_current_entry = true;
733f4536dddSNamhyung Kim 	}
734f4536dddSNamhyung Kim 
735f4536dddSNamhyung Kim 	ui_browser__set_color(&browser->b, color);
736f4536dddSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
73726270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, " ", offset);
738517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(&browser->b, "%c", folded_sign);
73970e97278SArnaldo Carvalho de Melo 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
74026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, str, width);
741f4536dddSNamhyung Kim }
742f4536dddSNamhyung Kim 
74339ee533fSNamhyung Kim static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
74439ee533fSNamhyung Kim 						  struct callchain_list *chain,
74539ee533fSNamhyung Kim 						  const char *str, int offset,
74639ee533fSNamhyung Kim 						  unsigned short row __maybe_unused,
74739ee533fSNamhyung Kim 						  struct callchain_print_arg *arg)
74839ee533fSNamhyung Kim {
74939ee533fSNamhyung Kim 	char folded_sign = callchain_list__folded(chain);
75039ee533fSNamhyung Kim 
75139ee533fSNamhyung Kim 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
75239ee533fSNamhyung Kim 				folded_sign, str);
75339ee533fSNamhyung Kim }
75439ee533fSNamhyung Kim 
75539ee533fSNamhyung Kim typedef bool (*check_output_full_fn)(struct hist_browser *browser,
75639ee533fSNamhyung Kim 				     unsigned short row);
75739ee533fSNamhyung Kim 
75839ee533fSNamhyung Kim static bool hist_browser__check_output_full(struct hist_browser *browser,
75939ee533fSNamhyung Kim 					    unsigned short row)
76039ee533fSNamhyung Kim {
76139ee533fSNamhyung Kim 	return browser->b.rows == row;
76239ee533fSNamhyung Kim }
76339ee533fSNamhyung Kim 
76439ee533fSNamhyung Kim static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
76539ee533fSNamhyung Kim 					  unsigned short row __maybe_unused)
76639ee533fSNamhyung Kim {
76739ee533fSNamhyung Kim 	return false;
76839ee533fSNamhyung Kim }
76939ee533fSNamhyung Kim 
770aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
771aca7a94dSNamhyung Kim 
77218bb8381SNamhyung Kim static int hist_browser__show_callchain_list(struct hist_browser *browser,
77318bb8381SNamhyung Kim 					     struct callchain_node *node,
77418bb8381SNamhyung Kim 					     struct callchain_list *chain,
77518bb8381SNamhyung Kim 					     unsigned short row, u64 total,
77618bb8381SNamhyung Kim 					     bool need_percent, int offset,
77718bb8381SNamhyung Kim 					     print_callchain_entry_fn print,
77818bb8381SNamhyung Kim 					     struct callchain_print_arg *arg)
77918bb8381SNamhyung Kim {
78018bb8381SNamhyung Kim 	char bf[1024], *alloc_str;
781fef51ecdSJin Yao 	char buf[64], *alloc_str2;
78218bb8381SNamhyung Kim 	const char *str;
7832a704fc8SMilian Wolff 	int ret = 1;
78418bb8381SNamhyung Kim 
78518bb8381SNamhyung Kim 	if (arg->row_offset != 0) {
78618bb8381SNamhyung Kim 		arg->row_offset--;
78718bb8381SNamhyung Kim 		return 0;
78818bb8381SNamhyung Kim 	}
78918bb8381SNamhyung Kim 
79018bb8381SNamhyung Kim 	alloc_str = NULL;
791fef51ecdSJin Yao 	alloc_str2 = NULL;
792fef51ecdSJin Yao 
79318bb8381SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
79418bb8381SNamhyung Kim 				       browser->show_dso);
79518bb8381SNamhyung Kim 
796fef51ecdSJin Yao 	if (symbol_conf.show_branchflag_count) {
797c4ee0625SJin Yao 		callchain_list_counts__printf_value(chain, NULL,
798fef51ecdSJin Yao 						    buf, sizeof(buf));
79918bb8381SNamhyung Kim 
800fef51ecdSJin Yao 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
801fef51ecdSJin Yao 			str = "Not enough memory!";
802fef51ecdSJin Yao 		else
803fef51ecdSJin Yao 			str = alloc_str2;
804fef51ecdSJin Yao 	}
805fef51ecdSJin Yao 
806fef51ecdSJin Yao 	if (need_percent) {
80718bb8381SNamhyung Kim 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
80818bb8381SNamhyung Kim 						total);
80918bb8381SNamhyung Kim 
81018bb8381SNamhyung Kim 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
81118bb8381SNamhyung Kim 			str = "Not enough memory!";
81218bb8381SNamhyung Kim 		else
81318bb8381SNamhyung Kim 			str = alloc_str;
81418bb8381SNamhyung Kim 	}
81518bb8381SNamhyung Kim 
81618bb8381SNamhyung Kim 	print(browser, chain, str, offset, row, arg);
81718bb8381SNamhyung Kim 	free(alloc_str);
818fef51ecdSJin Yao 	free(alloc_str2);
8190d3eb0b7SJin Yao 
8202a704fc8SMilian Wolff 	return ret;
82118bb8381SNamhyung Kim }
82218bb8381SNamhyung Kim 
82359c624e2SNamhyung Kim static bool check_percent_display(struct rb_node *node, u64 parent_total)
82459c624e2SNamhyung Kim {
82559c624e2SNamhyung Kim 	struct callchain_node *child;
82659c624e2SNamhyung Kim 
82759c624e2SNamhyung Kim 	if (node == NULL)
82859c624e2SNamhyung Kim 		return false;
82959c624e2SNamhyung Kim 
83059c624e2SNamhyung Kim 	if (rb_next(node))
83159c624e2SNamhyung Kim 		return true;
83259c624e2SNamhyung Kim 
83359c624e2SNamhyung Kim 	child = rb_entry(node, struct callchain_node, rb_node);
83459c624e2SNamhyung Kim 	return callchain_cumul_hits(child) != parent_total;
83559c624e2SNamhyung Kim }
83659c624e2SNamhyung Kim 
8374b3a3212SNamhyung Kim static int hist_browser__show_callchain_flat(struct hist_browser *browser,
8384b3a3212SNamhyung Kim 					     struct rb_root *root,
8394b3a3212SNamhyung Kim 					     unsigned short row, u64 total,
84059c624e2SNamhyung Kim 					     u64 parent_total,
8414b3a3212SNamhyung Kim 					     print_callchain_entry_fn print,
8424b3a3212SNamhyung Kim 					     struct callchain_print_arg *arg,
8434b3a3212SNamhyung Kim 					     check_output_full_fn is_output_full)
8444b3a3212SNamhyung Kim {
8454b3a3212SNamhyung Kim 	struct rb_node *node;
8464b3a3212SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
8474b3a3212SNamhyung Kim 	bool need_percent;
8484b3a3212SNamhyung Kim 
8494b3a3212SNamhyung Kim 	node = rb_first(root);
85059c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
8514b3a3212SNamhyung Kim 
8524b3a3212SNamhyung Kim 	while (node) {
8534b3a3212SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
8544b3a3212SNamhyung Kim 		struct rb_node *next = rb_next(node);
8554b3a3212SNamhyung Kim 		struct callchain_list *chain;
8564b3a3212SNamhyung Kim 		char folded_sign = ' ';
8574b3a3212SNamhyung Kim 		int first = true;
8584b3a3212SNamhyung Kim 		int extra_offset = 0;
8594b3a3212SNamhyung Kim 
8604b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
8614b3a3212SNamhyung Kim 			bool was_first = first;
8624b3a3212SNamhyung Kim 
8634b3a3212SNamhyung Kim 			if (first)
8644b3a3212SNamhyung Kim 				first = false;
8654b3a3212SNamhyung Kim 			else if (need_percent)
8664b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8674b3a3212SNamhyung Kim 
8684b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8694b3a3212SNamhyung Kim 
8704b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8714b3a3212SNamhyung Kim 							chain, row, total,
8724b3a3212SNamhyung Kim 							was_first && need_percent,
8734b3a3212SNamhyung Kim 							offset + extra_offset,
8744b3a3212SNamhyung Kim 							print, arg);
8754b3a3212SNamhyung Kim 
8764b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
8774b3a3212SNamhyung Kim 				goto out;
8784b3a3212SNamhyung Kim 
8794b3a3212SNamhyung Kim 			if (folded_sign == '+')
8804b3a3212SNamhyung Kim 				goto next;
8814b3a3212SNamhyung Kim 		}
8824b3a3212SNamhyung Kim 
8834b3a3212SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
8844b3a3212SNamhyung Kim 			bool was_first = first;
8854b3a3212SNamhyung Kim 
8864b3a3212SNamhyung Kim 			if (first)
8874b3a3212SNamhyung Kim 				first = false;
8884b3a3212SNamhyung Kim 			else if (need_percent)
8894b3a3212SNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
8904b3a3212SNamhyung Kim 
8914b3a3212SNamhyung Kim 			folded_sign = callchain_list__folded(chain);
8924b3a3212SNamhyung Kim 
8934b3a3212SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
8944b3a3212SNamhyung Kim 							chain, row, total,
8954b3a3212SNamhyung Kim 							was_first && need_percent,
8964b3a3212SNamhyung Kim 							offset + extra_offset,
8974b3a3212SNamhyung Kim 							print, arg);
8984b3a3212SNamhyung Kim 
8994b3a3212SNamhyung Kim 			if (is_output_full(browser, row))
9004b3a3212SNamhyung Kim 				goto out;
9014b3a3212SNamhyung Kim 
9024b3a3212SNamhyung Kim 			if (folded_sign == '+')
9034b3a3212SNamhyung Kim 				break;
9044b3a3212SNamhyung Kim 		}
9054b3a3212SNamhyung Kim 
9064b3a3212SNamhyung Kim next:
9074b3a3212SNamhyung Kim 		if (is_output_full(browser, row))
9084b3a3212SNamhyung Kim 			break;
9094b3a3212SNamhyung Kim 		node = next;
9104b3a3212SNamhyung Kim 	}
9114b3a3212SNamhyung Kim out:
9124b3a3212SNamhyung Kim 	return row - first_row;
9134b3a3212SNamhyung Kim }
9144b3a3212SNamhyung Kim 
9158c430a34SNamhyung Kim static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
9168c430a34SNamhyung Kim 						struct callchain_list *chain,
9178c430a34SNamhyung Kim 						char *value_str, char *old_str)
9188c430a34SNamhyung Kim {
9198c430a34SNamhyung Kim 	char bf[1024];
9208c430a34SNamhyung Kim 	const char *str;
9218c430a34SNamhyung Kim 	char *new;
9228c430a34SNamhyung Kim 
9238c430a34SNamhyung Kim 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
9248c430a34SNamhyung Kim 				       browser->show_dso);
9258c430a34SNamhyung Kim 	if (old_str) {
9268c430a34SNamhyung Kim 		if (asprintf(&new, "%s%s%s", old_str,
9278c430a34SNamhyung Kim 			     symbol_conf.field_sep ?: ";", str) < 0)
9288c430a34SNamhyung Kim 			new = NULL;
9298c430a34SNamhyung Kim 	} else {
9308c430a34SNamhyung Kim 		if (value_str) {
9318c430a34SNamhyung Kim 			if (asprintf(&new, "%s %s", value_str, str) < 0)
9328c430a34SNamhyung Kim 				new = NULL;
9338c430a34SNamhyung Kim 		} else {
9348c430a34SNamhyung Kim 			if (asprintf(&new, "%s", str) < 0)
9358c430a34SNamhyung Kim 				new = NULL;
9368c430a34SNamhyung Kim 		}
9378c430a34SNamhyung Kim 	}
9388c430a34SNamhyung Kim 	return new;
9398c430a34SNamhyung Kim }
9408c430a34SNamhyung Kim 
9418c430a34SNamhyung Kim static int hist_browser__show_callchain_folded(struct hist_browser *browser,
9428c430a34SNamhyung Kim 					       struct rb_root *root,
9438c430a34SNamhyung Kim 					       unsigned short row, u64 total,
94459c624e2SNamhyung Kim 					       u64 parent_total,
9458c430a34SNamhyung Kim 					       print_callchain_entry_fn print,
9468c430a34SNamhyung Kim 					       struct callchain_print_arg *arg,
9478c430a34SNamhyung Kim 					       check_output_full_fn is_output_full)
9488c430a34SNamhyung Kim {
9498c430a34SNamhyung Kim 	struct rb_node *node;
9508c430a34SNamhyung Kim 	int first_row = row, offset = LEVEL_OFFSET_STEP;
9518c430a34SNamhyung Kim 	bool need_percent;
9528c430a34SNamhyung Kim 
9538c430a34SNamhyung Kim 	node = rb_first(root);
95459c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
9558c430a34SNamhyung Kim 
9568c430a34SNamhyung Kim 	while (node) {
9578c430a34SNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
9588c430a34SNamhyung Kim 		struct rb_node *next = rb_next(node);
9598c430a34SNamhyung Kim 		struct callchain_list *chain, *first_chain = NULL;
9608c430a34SNamhyung Kim 		int first = true;
9618c430a34SNamhyung Kim 		char *value_str = NULL, *value_str_alloc = NULL;
9628c430a34SNamhyung Kim 		char *chain_str = NULL, *chain_str_alloc = NULL;
9638c430a34SNamhyung Kim 
9648c430a34SNamhyung Kim 		if (arg->row_offset != 0) {
9658c430a34SNamhyung Kim 			arg->row_offset--;
9668c430a34SNamhyung Kim 			goto next;
9678c430a34SNamhyung Kim 		}
9688c430a34SNamhyung Kim 
9698c430a34SNamhyung Kim 		if (need_percent) {
9708c430a34SNamhyung Kim 			char buf[64];
9718c430a34SNamhyung Kim 
9728c430a34SNamhyung Kim 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
9738c430a34SNamhyung Kim 			if (asprintf(&value_str, "%s", buf) < 0) {
9748c430a34SNamhyung Kim 				value_str = (char *)"<...>";
9758c430a34SNamhyung Kim 				goto do_print;
9768c430a34SNamhyung Kim 			}
9778c430a34SNamhyung Kim 			value_str_alloc = value_str;
9788c430a34SNamhyung Kim 		}
9798c430a34SNamhyung Kim 
9808c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->parent_val, list) {
9818c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9828c430a34SNamhyung Kim 						chain, value_str, chain_str);
9838c430a34SNamhyung Kim 			if (first) {
9848c430a34SNamhyung Kim 				first = false;
9858c430a34SNamhyung Kim 				first_chain = chain;
9868c430a34SNamhyung Kim 			}
9878c430a34SNamhyung Kim 
9888c430a34SNamhyung Kim 			if (chain_str == NULL) {
9898c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
9908c430a34SNamhyung Kim 				goto do_print;
9918c430a34SNamhyung Kim 			}
9928c430a34SNamhyung Kim 
9938c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
9948c430a34SNamhyung Kim 		}
9958c430a34SNamhyung Kim 
9968c430a34SNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
9978c430a34SNamhyung Kim 			chain_str = hist_browser__folded_callchain_str(browser,
9988c430a34SNamhyung Kim 						chain, value_str, chain_str);
9998c430a34SNamhyung Kim 			if (first) {
10008c430a34SNamhyung Kim 				first = false;
10018c430a34SNamhyung Kim 				first_chain = chain;
10028c430a34SNamhyung Kim 			}
10038c430a34SNamhyung Kim 
10048c430a34SNamhyung Kim 			if (chain_str == NULL) {
10058c430a34SNamhyung Kim 				chain_str = (char *)"Not enough memory!";
10068c430a34SNamhyung Kim 				goto do_print;
10078c430a34SNamhyung Kim 			}
10088c430a34SNamhyung Kim 
10098c430a34SNamhyung Kim 			chain_str_alloc = chain_str;
10108c430a34SNamhyung Kim 		}
10118c430a34SNamhyung Kim 
10128c430a34SNamhyung Kim do_print:
10138c430a34SNamhyung Kim 		print(browser, first_chain, chain_str, offset, row++, arg);
10148c430a34SNamhyung Kim 		free(value_str_alloc);
10158c430a34SNamhyung Kim 		free(chain_str_alloc);
10168c430a34SNamhyung Kim 
10178c430a34SNamhyung Kim next:
10188c430a34SNamhyung Kim 		if (is_output_full(browser, row))
10198c430a34SNamhyung Kim 			break;
10208c430a34SNamhyung Kim 		node = next;
10218c430a34SNamhyung Kim 	}
10228c430a34SNamhyung Kim 
10238c430a34SNamhyung Kim 	return row - first_row;
10248c430a34SNamhyung Kim }
10258c430a34SNamhyung Kim 
10260c841c6cSNamhyung Kim static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1027c09a7e75SNamhyung Kim 					struct rb_root *root, int level,
102839ee533fSNamhyung Kim 					unsigned short row, u64 total,
10295eca104eSNamhyung Kim 					u64 parent_total,
103039ee533fSNamhyung Kim 					print_callchain_entry_fn print,
103139ee533fSNamhyung Kim 					struct callchain_print_arg *arg,
103239ee533fSNamhyung Kim 					check_output_full_fn is_output_full)
1033aca7a94dSNamhyung Kim {
1034aca7a94dSNamhyung Kim 	struct rb_node *node;
1035f4536dddSNamhyung Kim 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
10364087d11cSNamhyung Kim 	bool need_percent;
10375eca104eSNamhyung Kim 	u64 percent_total = total;
10385eca104eSNamhyung Kim 
10395eca104eSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
10405eca104eSNamhyung Kim 		percent_total = parent_total;
1041aca7a94dSNamhyung Kim 
1042c09a7e75SNamhyung Kim 	node = rb_first(root);
104359c624e2SNamhyung Kim 	need_percent = check_percent_display(node, parent_total);
10444087d11cSNamhyung Kim 
1045aca7a94dSNamhyung Kim 	while (node) {
1046aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1047aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
1048aca7a94dSNamhyung Kim 		struct callchain_list *chain;
1049aca7a94dSNamhyung Kim 		char folded_sign = ' ';
1050aca7a94dSNamhyung Kim 		int first = true;
1051aca7a94dSNamhyung Kim 		int extra_offset = 0;
1052aca7a94dSNamhyung Kim 
1053aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
1054aca7a94dSNamhyung Kim 			bool was_first = first;
1055aca7a94dSNamhyung Kim 
1056aca7a94dSNamhyung Kim 			if (first)
1057aca7a94dSNamhyung Kim 				first = false;
10584087d11cSNamhyung Kim 			else if (need_percent)
1059aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
1060aca7a94dSNamhyung Kim 
1061aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
1062aca7a94dSNamhyung Kim 
106318bb8381SNamhyung Kim 			row += hist_browser__show_callchain_list(browser, child,
10645eca104eSNamhyung Kim 							chain, row, percent_total,
106518bb8381SNamhyung Kim 							was_first && need_percent,
106618bb8381SNamhyung Kim 							offset + extra_offset,
106718bb8381SNamhyung Kim 							print, arg);
1068c09a7e75SNamhyung Kim 
106918bb8381SNamhyung Kim 			if (is_output_full(browser, row))
1070aca7a94dSNamhyung Kim 				goto out;
107118bb8381SNamhyung Kim 
1072aca7a94dSNamhyung Kim 			if (folded_sign == '+')
1073aca7a94dSNamhyung Kim 				break;
1074aca7a94dSNamhyung Kim 		}
1075aca7a94dSNamhyung Kim 
1076aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
1077aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
1078c09a7e75SNamhyung Kim 
10790c841c6cSNamhyung Kim 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
10805eca104eSNamhyung Kim 							    new_level, row, total,
10815eca104eSNamhyung Kim 							    child->children_hit,
108239ee533fSNamhyung Kim 							    print, arg, is_output_full);
1083aca7a94dSNamhyung Kim 		}
108439ee533fSNamhyung Kim 		if (is_output_full(browser, row))
1085c09a7e75SNamhyung Kim 			break;
1086aca7a94dSNamhyung Kim 		node = next;
1087aca7a94dSNamhyung Kim 	}
1088aca7a94dSNamhyung Kim out:
1089aca7a94dSNamhyung Kim 	return row - first_row;
1090aca7a94dSNamhyung Kim }
1091aca7a94dSNamhyung Kim 
10920c841c6cSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *browser,
10930c841c6cSNamhyung Kim 					struct hist_entry *entry, int level,
10940c841c6cSNamhyung Kim 					unsigned short row,
10950c841c6cSNamhyung Kim 					print_callchain_entry_fn print,
10960c841c6cSNamhyung Kim 					struct callchain_print_arg *arg,
10970c841c6cSNamhyung Kim 					check_output_full_fn is_output_full)
10980c841c6cSNamhyung Kim {
10990c841c6cSNamhyung Kim 	u64 total = hists__total_period(entry->hists);
11005eca104eSNamhyung Kim 	u64 parent_total;
11010c841c6cSNamhyung Kim 	int printed;
11020c841c6cSNamhyung Kim 
11030c841c6cSNamhyung Kim 	if (symbol_conf.cumulate_callchain)
11045eca104eSNamhyung Kim 		parent_total = entry->stat_acc->period;
11050c841c6cSNamhyung Kim 	else
11065eca104eSNamhyung Kim 		parent_total = entry->stat.period;
11070c841c6cSNamhyung Kim 
11080c841c6cSNamhyung Kim 	if (callchain_param.mode == CHAIN_FLAT) {
11090c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_flat(browser,
11105eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11115eca104eSNamhyung Kim 						total, parent_total, print, arg,
11125eca104eSNamhyung Kim 						is_output_full);
11130c841c6cSNamhyung Kim 	} else if (callchain_param.mode == CHAIN_FOLDED) {
11140c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_folded(browser,
11155eca104eSNamhyung Kim 						&entry->sorted_chain, row,
11165eca104eSNamhyung Kim 						total, parent_total, print, arg,
11175eca104eSNamhyung Kim 						is_output_full);
11180c841c6cSNamhyung Kim 	} else {
11190c841c6cSNamhyung Kim 		printed = hist_browser__show_callchain_graph(browser,
11205eca104eSNamhyung Kim 						&entry->sorted_chain, level, row,
11215eca104eSNamhyung Kim 						total, parent_total, print, arg,
11225eca104eSNamhyung Kim 						is_output_full);
11230c841c6cSNamhyung Kim 	}
11240c841c6cSNamhyung Kim 
11250c841c6cSNamhyung Kim 	if (arg->is_current_entry)
11260c841c6cSNamhyung Kim 		browser->he_selection = entry;
11270c841c6cSNamhyung Kim 
11280c841c6cSNamhyung Kim 	return printed;
11290c841c6cSNamhyung Kim }
11300c841c6cSNamhyung Kim 
113189701460SNamhyung Kim struct hpp_arg {
113289701460SNamhyung Kim 	struct ui_browser *b;
113389701460SNamhyung Kim 	char folded_sign;
113489701460SNamhyung Kim 	bool current_entry;
113589701460SNamhyung Kim };
113689701460SNamhyung Kim 
113798ba1609SJiri Olsa int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
11382f6d9009SNamhyung Kim {
11392f6d9009SNamhyung Kim 	struct hpp_arg *arg = hpp->ptr;
1140d675107cSNamhyung Kim 	int ret, len;
11412f6d9009SNamhyung Kim 	va_list args;
11422f6d9009SNamhyung Kim 	double percent;
11432f6d9009SNamhyung Kim 
11442f6d9009SNamhyung Kim 	va_start(args, fmt);
1145d675107cSNamhyung Kim 	len = va_arg(args, int);
11462f6d9009SNamhyung Kim 	percent = va_arg(args, double);
11472f6d9009SNamhyung Kim 	va_end(args);
11485aed9d24SNamhyung Kim 
114989701460SNamhyung Kim 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
11505aed9d24SNamhyung Kim 
1151d675107cSNamhyung Kim 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1152517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(arg->b, "%s", hpp->buf);
115389701460SNamhyung Kim 
11545aed9d24SNamhyung Kim 	return ret;
1155f5951d56SNamhyung Kim }
1156f5951d56SNamhyung Kim 
1157fb821c9eSNamhyung Kim #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
11585aed9d24SNamhyung Kim static u64 __hpp_get_##_field(struct hist_entry *he)			\
11595aed9d24SNamhyung Kim {									\
11605aed9d24SNamhyung Kim 	return he->stat._field;						\
11615aed9d24SNamhyung Kim }									\
11625aed9d24SNamhyung Kim 									\
11632c5d4b4aSJiri Olsa static int								\
11645b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11652c5d4b4aSJiri Olsa 				struct perf_hpp *hpp,			\
11665aed9d24SNamhyung Kim 				struct hist_entry *he)			\
11675aed9d24SNamhyung Kim {									\
11685b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
11692f6d9009SNamhyung Kim 			__hpp__slsmg_color_printf, true);		\
11705aed9d24SNamhyung Kim }
1171f5951d56SNamhyung Kim 
11720434ddd2SNamhyung Kim #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
11730434ddd2SNamhyung Kim static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
11740434ddd2SNamhyung Kim {									\
11750434ddd2SNamhyung Kim 	return he->stat_acc->_field;					\
11760434ddd2SNamhyung Kim }									\
11770434ddd2SNamhyung Kim 									\
11780434ddd2SNamhyung Kim static int								\
11795b591669SNamhyung Kim hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
11800434ddd2SNamhyung Kim 				struct perf_hpp *hpp,			\
11810434ddd2SNamhyung Kim 				struct hist_entry *he)			\
11820434ddd2SNamhyung Kim {									\
11830434ddd2SNamhyung Kim 	if (!symbol_conf.cumulate_callchain) {				\
1184517dfdb3SArnaldo Carvalho de Melo 		struct hpp_arg *arg = hpp->ptr;				\
11855b591669SNamhyung Kim 		int len = fmt->user_len ?: fmt->len;			\
1186d675107cSNamhyung Kim 		int ret = scnprintf(hpp->buf, hpp->size,		\
11875b591669SNamhyung Kim 				    "%*s", len, "N/A");			\
1188517dfdb3SArnaldo Carvalho de Melo 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
11890434ddd2SNamhyung Kim 									\
11900434ddd2SNamhyung Kim 		return ret;						\
11910434ddd2SNamhyung Kim 	}								\
11925b591669SNamhyung Kim 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
11935b591669SNamhyung Kim 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
11940434ddd2SNamhyung Kim }
11950434ddd2SNamhyung Kim 
1196fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead, period)
1197fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1198fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1199fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1200fb821c9eSNamhyung Kim __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
12010434ddd2SNamhyung Kim __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
12025aed9d24SNamhyung Kim 
12035aed9d24SNamhyung Kim #undef __HPP_COLOR_PERCENT_FN
12040434ddd2SNamhyung Kim #undef __HPP_COLOR_ACC_PERCENT_FN
1205f5951d56SNamhyung Kim 
1206f5951d56SNamhyung Kim void hist_browser__init_hpp(void)
1207f5951d56SNamhyung Kim {
1208f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1209f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead;
1210f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1211f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_sys;
1212f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1213f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_us;
1214f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1215f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_sys;
1216f5951d56SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1217f5951d56SNamhyung Kim 				hist_browser__hpp_color_overhead_guest_us;
12180434ddd2SNamhyung Kim 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
12190434ddd2SNamhyung Kim 				hist_browser__hpp_color_overhead_acc;
1220f5951d56SNamhyung Kim }
1221f5951d56SNamhyung Kim 
122205e8b080SArnaldo Carvalho de Melo static int hist_browser__show_entry(struct hist_browser *browser,
1223aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
1224aca7a94dSNamhyung Kim 				    unsigned short row)
1225aca7a94dSNamhyung Kim {
12261240005eSJiri Olsa 	int printed = 0;
122767d25916SNamhyung Kim 	int width = browser->b.width;
1228aca7a94dSNamhyung Kim 	char folded_sign = ' ';
122905e8b080SArnaldo Carvalho de Melo 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1230aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
123163a1a3d8SNamhyung Kim 	bool first = true;
12321240005eSJiri Olsa 	struct perf_hpp_fmt *fmt;
1233aca7a94dSNamhyung Kim 
1234aca7a94dSNamhyung Kim 	if (current_entry) {
123505e8b080SArnaldo Carvalho de Melo 		browser->he_selection = entry;
123605e8b080SArnaldo Carvalho de Melo 		browser->selection = &entry->ms;
1237aca7a94dSNamhyung Kim 	}
1238aca7a94dSNamhyung Kim 
1239aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
1240aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
1241aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
1242aca7a94dSNamhyung Kim 	}
1243aca7a94dSNamhyung Kim 
1244aca7a94dSNamhyung Kim 	if (row_offset == 0) {
124589701460SNamhyung Kim 		struct hpp_arg arg = {
124689701460SNamhyung Kim 			.b		= &browser->b,
124789701460SNamhyung Kim 			.folded_sign	= folded_sign,
124889701460SNamhyung Kim 			.current_entry	= current_entry,
124989701460SNamhyung Kim 		};
1250c6c3c02dSArnaldo Carvalho de Melo 		int column = 0;
1251f5951d56SNamhyung Kim 
1252ca3ff33bSArnaldo Carvalho de Melo 		hist_browser__gotorc(browser, row, 0);
1253f5951d56SNamhyung Kim 
1254f0786af5SJiri Olsa 		hists__for_each_format(browser->hists, fmt) {
125589fee709SArnaldo Carvalho de Melo 			char s[2048];
125689fee709SArnaldo Carvalho de Melo 			struct perf_hpp hpp = {
125789fee709SArnaldo Carvalho de Melo 				.buf	= s,
125889fee709SArnaldo Carvalho de Melo 				.size	= sizeof(s),
125989fee709SArnaldo Carvalho de Melo 				.ptr	= &arg,
126089fee709SArnaldo Carvalho de Melo 			};
126189fee709SArnaldo Carvalho de Melo 
1262361459f1SNamhyung Kim 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1263361459f1SNamhyung Kim 			    column++ < browser->b.horiz_scroll)
1264e67d49a7SNamhyung Kim 				continue;
1265e67d49a7SNamhyung Kim 
1266fb821c9eSNamhyung Kim 			if (current_entry && browser->b.navkeypressed) {
1267fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1268fb821c9eSNamhyung Kim 						      HE_COLORSET_SELECTED);
1269fb821c9eSNamhyung Kim 			} else {
1270fb821c9eSNamhyung Kim 				ui_browser__set_color(&browser->b,
1271fb821c9eSNamhyung Kim 						      HE_COLORSET_NORMAL);
1272fb821c9eSNamhyung Kim 			}
1273fb821c9eSNamhyung Kim 
1274fb821c9eSNamhyung Kim 			if (first) {
12752a704fc8SMilian Wolff 				if (symbol_conf.use_callchain) {
1276517dfdb3SArnaldo Carvalho de Melo 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1277f5951d56SNamhyung Kim 					width -= 2;
1278f5951d56SNamhyung Kim 				}
127963a1a3d8SNamhyung Kim 				first = false;
1280fb821c9eSNamhyung Kim 			} else {
1281517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "  ");
1282fb821c9eSNamhyung Kim 				width -= 2;
1283fb821c9eSNamhyung Kim 			}
1284f5951d56SNamhyung Kim 
12851240005eSJiri Olsa 			if (fmt->color) {
128689fee709SArnaldo Carvalho de Melo 				int ret = fmt->color(fmt, &hpp, entry);
128789fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
128889fee709SArnaldo Carvalho de Melo 				/*
128989fee709SArnaldo Carvalho de Melo 				 * fmt->color() already used ui_browser to
129089fee709SArnaldo Carvalho de Melo 				 * print the non alignment bits, skip it (+ret):
129189fee709SArnaldo Carvalho de Melo 				 */
129289fee709SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s + ret);
1293f5951d56SNamhyung Kim 			} else {
129489fee709SArnaldo Carvalho de Melo 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1295517dfdb3SArnaldo Carvalho de Melo 				ui_browser__printf(&browser->b, "%s", s);
1296f5951d56SNamhyung Kim 			}
129789fee709SArnaldo Carvalho de Melo 			width -= hpp.buf - s;
1298f5951d56SNamhyung Kim 		}
1299aca7a94dSNamhyung Kim 
1300aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
130105e8b080SArnaldo Carvalho de Melo 		if (!browser->b.navkeypressed)
1302aca7a94dSNamhyung Kim 			width += 1;
1303aca7a94dSNamhyung Kim 
130426270a00SArnaldo Carvalho de Melo 		ui_browser__write_nstring(&browser->b, "", width);
130526d8b338SNamhyung Kim 
1306aca7a94dSNamhyung Kim 		++row;
1307aca7a94dSNamhyung Kim 		++printed;
1308aca7a94dSNamhyung Kim 	} else
1309aca7a94dSNamhyung Kim 		--row_offset;
1310aca7a94dSNamhyung Kim 
131162c95ae3SArnaldo Carvalho de Melo 	if (folded_sign == '-' && row != browser->b.rows) {
131239ee533fSNamhyung Kim 		struct callchain_print_arg arg = {
131339ee533fSNamhyung Kim 			.row_offset = row_offset,
131439ee533fSNamhyung Kim 			.is_current_entry = current_entry,
131539ee533fSNamhyung Kim 		};
1316c09a7e75SNamhyung Kim 
13170d3eb0b7SJin Yao 		printed += hist_browser__show_callchain(browser,
13180d3eb0b7SJin Yao 				entry, 1, row,
13190d3eb0b7SJin Yao 				hist_browser__show_callchain_entry,
13200d3eb0b7SJin Yao 				&arg,
13214b3a3212SNamhyung Kim 				hist_browser__check_output_full);
1322aca7a94dSNamhyung Kim 	}
1323aca7a94dSNamhyung Kim 
1324aca7a94dSNamhyung Kim 	return printed;
1325aca7a94dSNamhyung Kim }
1326aca7a94dSNamhyung Kim 
1327d0506edbSNamhyung Kim static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1328d0506edbSNamhyung Kim 					      struct hist_entry *entry,
1329d0506edbSNamhyung Kim 					      unsigned short row,
13302dbbe9f2SNamhyung Kim 					      int level)
1331d0506edbSNamhyung Kim {
1332d0506edbSNamhyung Kim 	int printed = 0;
1333d0506edbSNamhyung Kim 	int width = browser->b.width;
1334d0506edbSNamhyung Kim 	char folded_sign = ' ';
1335d0506edbSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1336d0506edbSNamhyung Kim 	off_t row_offset = entry->row_offset;
1337d0506edbSNamhyung Kim 	bool first = true;
1338d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
1339a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1340d0506edbSNamhyung Kim 	struct hpp_arg arg = {
1341d0506edbSNamhyung Kim 		.b		= &browser->b,
1342d0506edbSNamhyung Kim 		.current_entry	= current_entry,
1343d0506edbSNamhyung Kim 	};
1344d0506edbSNamhyung Kim 	int column = 0;
13452dbbe9f2SNamhyung Kim 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1346d0506edbSNamhyung Kim 
1347d0506edbSNamhyung Kim 	if (current_entry) {
1348d0506edbSNamhyung Kim 		browser->he_selection = entry;
1349d0506edbSNamhyung Kim 		browser->selection = &entry->ms;
1350d0506edbSNamhyung Kim 	}
1351d0506edbSNamhyung Kim 
1352d0506edbSNamhyung Kim 	hist_entry__init_have_children(entry);
1353d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(entry);
1354d0506edbSNamhyung Kim 	arg.folded_sign = folded_sign;
1355d0506edbSNamhyung Kim 
1356d0506edbSNamhyung Kim 	if (entry->leaf && row_offset) {
1357d0506edbSNamhyung Kim 		row_offset--;
1358d0506edbSNamhyung Kim 		goto show_callchain;
1359d0506edbSNamhyung Kim 	}
1360d0506edbSNamhyung Kim 
1361d0506edbSNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
1362d0506edbSNamhyung Kim 
1363d0506edbSNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
1364d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1365d0506edbSNamhyung Kim 	else
1366d0506edbSNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1367d0506edbSNamhyung Kim 
1368d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1369d0506edbSNamhyung Kim 	width -= level * HIERARCHY_INDENT;
1370d0506edbSNamhyung Kim 
1371a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1372a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1373a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1374a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1375d0506edbSNamhyung Kim 		char s[2048];
1376d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1377d0506edbSNamhyung Kim 			.buf		= s,
1378d0506edbSNamhyung Kim 			.size		= sizeof(s),
1379d0506edbSNamhyung Kim 			.ptr		= &arg,
1380d0506edbSNamhyung Kim 		};
1381d0506edbSNamhyung Kim 
1382d0506edbSNamhyung Kim 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1383d0506edbSNamhyung Kim 		    column++ < browser->b.horiz_scroll)
1384d0506edbSNamhyung Kim 			continue;
1385d0506edbSNamhyung Kim 
1386d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1387d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1388d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1389d0506edbSNamhyung Kim 		} else {
1390d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1391d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1392d0506edbSNamhyung Kim 		}
1393d0506edbSNamhyung Kim 
1394d0506edbSNamhyung Kim 		if (first) {
1395d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%c ", folded_sign);
13963d9f4683SNamhyung Kim 			width -= 2;
1397d0506edbSNamhyung Kim 			first = false;
1398d0506edbSNamhyung Kim 		} else {
1399d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "  ");
1400d0506edbSNamhyung Kim 			width -= 2;
1401d0506edbSNamhyung Kim 		}
1402d0506edbSNamhyung Kim 
1403d0506edbSNamhyung Kim 		if (fmt->color) {
1404d0506edbSNamhyung Kim 			int ret = fmt->color(fmt, &hpp, entry);
1405d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1406d0506edbSNamhyung Kim 			/*
1407d0506edbSNamhyung Kim 			 * fmt->color() already used ui_browser to
1408d0506edbSNamhyung Kim 			 * print the non alignment bits, skip it (+ret):
1409d0506edbSNamhyung Kim 			 */
1410d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s + ret);
1411d0506edbSNamhyung Kim 		} else {
1412d0506edbSNamhyung Kim 			int ret = fmt->entry(fmt, &hpp, entry);
1413d0506edbSNamhyung Kim 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1414d0506edbSNamhyung Kim 			ui_browser__printf(&browser->b, "%s", s);
1415d0506edbSNamhyung Kim 		}
1416d0506edbSNamhyung Kim 		width -= hpp.buf - s;
1417d0506edbSNamhyung Kim 	}
1418d0506edbSNamhyung Kim 
1419b9bf911eSNamhyung Kim 	if (!first) {
1420d0506edbSNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1421d0506edbSNamhyung Kim 		width -= hierarchy_indent;
1422b9bf911eSNamhyung Kim 	}
1423d0506edbSNamhyung Kim 
1424d0506edbSNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
1425d0506edbSNamhyung Kim 		char s[2048];
1426d0506edbSNamhyung Kim 		struct perf_hpp hpp = {
1427d0506edbSNamhyung Kim 			.buf		= s,
1428d0506edbSNamhyung Kim 			.size		= sizeof(s),
1429d0506edbSNamhyung Kim 			.ptr		= &arg,
1430d0506edbSNamhyung Kim 		};
1431d0506edbSNamhyung Kim 
1432d0506edbSNamhyung Kim 		if (current_entry && browser->b.navkeypressed) {
1433d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1434d0506edbSNamhyung Kim 					      HE_COLORSET_SELECTED);
1435d0506edbSNamhyung Kim 		} else {
1436d0506edbSNamhyung Kim 			ui_browser__set_color(&browser->b,
1437d0506edbSNamhyung Kim 					      HE_COLORSET_NORMAL);
1438d0506edbSNamhyung Kim 		}
1439d0506edbSNamhyung Kim 
14401b2dbbf4SNamhyung Kim 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1441131d51ebSNamhyung Kim 			if (first) {
1442131d51ebSNamhyung Kim 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1443131d51ebSNamhyung Kim 				first = false;
1444131d51ebSNamhyung Kim 			} else {
1445d0506edbSNamhyung Kim 				ui_browser__write_nstring(&browser->b, "", 2);
1446131d51ebSNamhyung Kim 			}
1447131d51ebSNamhyung Kim 
1448d0506edbSNamhyung Kim 			width -= 2;
1449d0506edbSNamhyung Kim 
1450d0506edbSNamhyung Kim 			/*
1451d0506edbSNamhyung Kim 			 * No need to call hist_entry__snprintf_alignment()
1452d0506edbSNamhyung Kim 			 * since this fmt is always the last column in the
1453d0506edbSNamhyung Kim 			 * hierarchy mode.
1454d0506edbSNamhyung Kim 			 */
1455d0506edbSNamhyung Kim 			if (fmt->color) {
1456d0506edbSNamhyung Kim 				width -= fmt->color(fmt, &hpp, entry);
1457d0506edbSNamhyung Kim 			} else {
1458cb1fab91SNamhyung Kim 				int i = 0;
1459cb1fab91SNamhyung Kim 
1460d0506edbSNamhyung Kim 				width -= fmt->entry(fmt, &hpp, entry);
1461cb1fab91SNamhyung Kim 				ui_browser__printf(&browser->b, "%s", ltrim(s));
1462cb1fab91SNamhyung Kim 
1463cb1fab91SNamhyung Kim 				while (isspace(s[i++]))
1464cb1fab91SNamhyung Kim 					width++;
1465d0506edbSNamhyung Kim 			}
1466d0506edbSNamhyung Kim 		}
14671b2dbbf4SNamhyung Kim 	}
1468d0506edbSNamhyung Kim 
1469d0506edbSNamhyung Kim 	/* The scroll bar isn't being used */
1470d0506edbSNamhyung Kim 	if (!browser->b.navkeypressed)
1471d0506edbSNamhyung Kim 		width += 1;
1472d0506edbSNamhyung Kim 
1473d0506edbSNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
1474d0506edbSNamhyung Kim 
1475d0506edbSNamhyung Kim 	++row;
1476d0506edbSNamhyung Kim 	++printed;
1477d0506edbSNamhyung Kim 
1478d0506edbSNamhyung Kim show_callchain:
1479d0506edbSNamhyung Kim 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1480d0506edbSNamhyung Kim 		struct callchain_print_arg carg = {
1481d0506edbSNamhyung Kim 			.row_offset = row_offset,
1482d0506edbSNamhyung Kim 		};
1483d0506edbSNamhyung Kim 
1484d0506edbSNamhyung Kim 		printed += hist_browser__show_callchain(browser, entry,
1485d0506edbSNamhyung Kim 					level + 1, row,
1486d0506edbSNamhyung Kim 					hist_browser__show_callchain_entry, &carg,
1487d0506edbSNamhyung Kim 					hist_browser__check_output_full);
1488d0506edbSNamhyung Kim 	}
1489d0506edbSNamhyung Kim 
1490d0506edbSNamhyung Kim 	return printed;
1491d0506edbSNamhyung Kim }
1492d0506edbSNamhyung Kim 
149379dded87SNamhyung Kim static int hist_browser__show_no_entry(struct hist_browser *browser,
14942dbbe9f2SNamhyung Kim 				       unsigned short row, int level)
149579dded87SNamhyung Kim {
149679dded87SNamhyung Kim 	int width = browser->b.width;
149779dded87SNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
149879dded87SNamhyung Kim 	bool first = true;
149979dded87SNamhyung Kim 	int column = 0;
150079dded87SNamhyung Kim 	int ret;
150179dded87SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1502a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
15032dbbe9f2SNamhyung Kim 	int indent = browser->hists->nr_hpp_node - 2;
150479dded87SNamhyung Kim 
150579dded87SNamhyung Kim 	if (current_entry) {
150679dded87SNamhyung Kim 		browser->he_selection = NULL;
150779dded87SNamhyung Kim 		browser->selection = NULL;
150879dded87SNamhyung Kim 	}
150979dded87SNamhyung Kim 
151079dded87SNamhyung Kim 	hist_browser__gotorc(browser, row, 0);
151179dded87SNamhyung Kim 
151279dded87SNamhyung Kim 	if (current_entry && browser->b.navkeypressed)
151379dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
151479dded87SNamhyung Kim 	else
151579dded87SNamhyung Kim 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
151679dded87SNamhyung Kim 
151779dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
151879dded87SNamhyung Kim 	width -= level * HIERARCHY_INDENT;
151979dded87SNamhyung Kim 
1520a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1521a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1522a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1523a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
152479dded87SNamhyung Kim 		if (perf_hpp__should_skip(fmt, browser->hists) ||
152579dded87SNamhyung Kim 		    column++ < browser->b.horiz_scroll)
152679dded87SNamhyung Kim 			continue;
152779dded87SNamhyung Kim 
1528da1b0407SJiri Olsa 		ret = fmt->width(fmt, NULL, browser->hists);
152979dded87SNamhyung Kim 
153079dded87SNamhyung Kim 		if (first) {
153179dded87SNamhyung Kim 			/* for folded sign */
153279dded87SNamhyung Kim 			first = false;
153379dded87SNamhyung Kim 			ret++;
153479dded87SNamhyung Kim 		} else {
153579dded87SNamhyung Kim 			/* space between columns */
153679dded87SNamhyung Kim 			ret += 2;
153779dded87SNamhyung Kim 		}
153879dded87SNamhyung Kim 
153979dded87SNamhyung Kim 		ui_browser__write_nstring(&browser->b, "", ret);
154079dded87SNamhyung Kim 		width -= ret;
154179dded87SNamhyung Kim 	}
154279dded87SNamhyung Kim 
15432dbbe9f2SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
15442dbbe9f2SNamhyung Kim 	width -= indent * HIERARCHY_INDENT;
154579dded87SNamhyung Kim 
154679dded87SNamhyung Kim 	if (column >= browser->b.horiz_scroll) {
154779dded87SNamhyung Kim 		char buf[32];
154879dded87SNamhyung Kim 
154979dded87SNamhyung Kim 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
155079dded87SNamhyung Kim 		ui_browser__printf(&browser->b, "  %s", buf);
155179dded87SNamhyung Kim 		width -= ret + 2;
155279dded87SNamhyung Kim 	}
155379dded87SNamhyung Kim 
155479dded87SNamhyung Kim 	/* The scroll bar isn't being used */
155579dded87SNamhyung Kim 	if (!browser->b.navkeypressed)
155679dded87SNamhyung Kim 		width += 1;
155779dded87SNamhyung Kim 
155879dded87SNamhyung Kim 	ui_browser__write_nstring(&browser->b, "", width);
155979dded87SNamhyung Kim 	return 1;
156079dded87SNamhyung Kim }
156179dded87SNamhyung Kim 
156281a888feSJiri Olsa static int advance_hpp_check(struct perf_hpp *hpp, int inc)
156381a888feSJiri Olsa {
156481a888feSJiri Olsa 	advance_hpp(hpp, inc);
156581a888feSJiri Olsa 	return hpp->size <= 0;
156681a888feSJiri Olsa }
156781a888feSJiri Olsa 
156869705b35SJiri Olsa static int
156969705b35SJiri Olsa hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
157069705b35SJiri Olsa 				 size_t size, int line)
157181a888feSJiri Olsa {
1572c6c3c02dSArnaldo Carvalho de Melo 	struct hists *hists = browser->hists;
157381a888feSJiri Olsa 	struct perf_hpp dummy_hpp = {
157481a888feSJiri Olsa 		.buf    = buf,
157581a888feSJiri Olsa 		.size   = size,
157681a888feSJiri Olsa 	};
157781a888feSJiri Olsa 	struct perf_hpp_fmt *fmt;
157881a888feSJiri Olsa 	size_t ret = 0;
1579c6c3c02dSArnaldo Carvalho de Melo 	int column = 0;
158029659ab4SJiri Olsa 	int span = 0;
158181a888feSJiri Olsa 
158281a888feSJiri Olsa 	if (symbol_conf.use_callchain) {
158381a888feSJiri Olsa 		ret = scnprintf(buf, size, "  ");
158481a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
158581a888feSJiri Olsa 			return ret;
158681a888feSJiri Olsa 	}
158781a888feSJiri Olsa 
1588f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1589361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
159081a888feSJiri Olsa 			continue;
159181a888feSJiri Olsa 
159229659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
159381a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
159481a888feSJiri Olsa 			break;
159581a888feSJiri Olsa 
159629659ab4SJiri Olsa 		if (span)
159729659ab4SJiri Olsa 			continue;
159829659ab4SJiri Olsa 
159981a888feSJiri Olsa 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
160081a888feSJiri Olsa 		if (advance_hpp_check(&dummy_hpp, ret))
160181a888feSJiri Olsa 			break;
160281a888feSJiri Olsa 	}
160381a888feSJiri Olsa 
160481a888feSJiri Olsa 	return ret;
160581a888feSJiri Olsa }
160681a888feSJiri Olsa 
1607d8b92400SNamhyung Kim static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1608d8b92400SNamhyung Kim {
1609d8b92400SNamhyung Kim 	struct hists *hists = browser->hists;
1610d8b92400SNamhyung Kim 	struct perf_hpp dummy_hpp = {
1611d8b92400SNamhyung Kim 		.buf    = buf,
1612d8b92400SNamhyung Kim 		.size   = size,
1613d8b92400SNamhyung Kim 	};
1614d8b92400SNamhyung Kim 	struct perf_hpp_fmt *fmt;
1615a61a22f6SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
1616d8b92400SNamhyung Kim 	size_t ret = 0;
1617d8b92400SNamhyung Kim 	int column = 0;
16182dbbe9f2SNamhyung Kim 	int indent = hists->nr_hpp_node - 2;
1619a61a22f6SNamhyung Kim 	bool first_node, first_col;
1620d8b92400SNamhyung Kim 
1621d8b92400SNamhyung Kim 	ret = scnprintf(buf, size, "  ");
1622d8b92400SNamhyung Kim 	if (advance_hpp_check(&dummy_hpp, ret))
1623d8b92400SNamhyung Kim 		return ret;
1624d8b92400SNamhyung Kim 
1625b9bf911eSNamhyung Kim 	first_node = true;
1626a61a22f6SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
1627a61a22f6SNamhyung Kim 	fmt_node = list_first_entry(&hists->hpp_formats,
1628a61a22f6SNamhyung Kim 				    struct perf_hpp_list_node, list);
1629a61a22f6SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1630d8b92400SNamhyung Kim 		if (column++ < browser->b.horiz_scroll)
1631d8b92400SNamhyung Kim 			continue;
1632d8b92400SNamhyung Kim 
163329659ab4SJiri Olsa 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1634d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1635d8b92400SNamhyung Kim 			break;
1636d8b92400SNamhyung Kim 
1637d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1638d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1639d8b92400SNamhyung Kim 			break;
1640b9bf911eSNamhyung Kim 
1641b9bf911eSNamhyung Kim 		first_node = false;
1642d8b92400SNamhyung Kim 	}
1643d8b92400SNamhyung Kim 
1644b9bf911eSNamhyung Kim 	if (!first_node) {
1645d8b92400SNamhyung Kim 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
16462dbbe9f2SNamhyung Kim 				indent * HIERARCHY_INDENT, "");
1647d8b92400SNamhyung Kim 		if (advance_hpp_check(&dummy_hpp, ret))
1648d8b92400SNamhyung Kim 			return ret;
1649b9bf911eSNamhyung Kim 	}
1650d8b92400SNamhyung Kim 
1651a61a22f6SNamhyung Kim 	first_node = true;
1652a61a22f6SNamhyung Kim 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1653a61a22f6SNamhyung Kim 		if (!first_node) {
1654d8b92400SNamhyung Kim 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1655d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1656d8b92400SNamhyung Kim 				break;
1657d8b92400SNamhyung Kim 		}
1658a61a22f6SNamhyung Kim 		first_node = false;
1659a61a22f6SNamhyung Kim 
1660a61a22f6SNamhyung Kim 		first_col = true;
1661a61a22f6SNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1662a61a22f6SNamhyung Kim 			char *start;
1663a61a22f6SNamhyung Kim 
1664a61a22f6SNamhyung Kim 			if (perf_hpp__should_skip(fmt, hists))
1665a61a22f6SNamhyung Kim 				continue;
1666a61a22f6SNamhyung Kim 
1667a61a22f6SNamhyung Kim 			if (!first_col) {
1668a61a22f6SNamhyung Kim 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1669a61a22f6SNamhyung Kim 				if (advance_hpp_check(&dummy_hpp, ret))
1670a61a22f6SNamhyung Kim 					break;
1671a61a22f6SNamhyung Kim 			}
1672a61a22f6SNamhyung Kim 			first_col = false;
1673d8b92400SNamhyung Kim 
167429659ab4SJiri Olsa 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1675d8b92400SNamhyung Kim 			dummy_hpp.buf[ret] = '\0';
1676d8b92400SNamhyung Kim 
16777d6a7e78SJiri Olsa 			start = trim(dummy_hpp.buf);
1678cb1fab91SNamhyung Kim 			ret = strlen(start);
1679cb1fab91SNamhyung Kim 
1680cb1fab91SNamhyung Kim 			if (start != dummy_hpp.buf)
1681cb1fab91SNamhyung Kim 				memmove(dummy_hpp.buf, start, ret + 1);
1682cb1fab91SNamhyung Kim 
1683d8b92400SNamhyung Kim 			if (advance_hpp_check(&dummy_hpp, ret))
1684d8b92400SNamhyung Kim 				break;
1685d8b92400SNamhyung Kim 		}
1686a61a22f6SNamhyung Kim 	}
1687d8b92400SNamhyung Kim 
1688d8b92400SNamhyung Kim 	return ret;
1689d8b92400SNamhyung Kim }
1690d8b92400SNamhyung Kim 
169101b4770dSJiri Olsa static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1692025bf7eaSArnaldo Carvalho de Melo {
169381a888feSJiri Olsa 	char headers[1024];
169481a888feSJiri Olsa 
1695d8b92400SNamhyung Kim 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1696d8b92400SNamhyung Kim 						   sizeof(headers));
169701b4770dSJiri Olsa 
1698025bf7eaSArnaldo Carvalho de Melo 	ui_browser__gotorc(&browser->b, 0, 0);
1699025bf7eaSArnaldo Carvalho de Melo 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
170026270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1701025bf7eaSArnaldo Carvalho de Melo }
1702025bf7eaSArnaldo Carvalho de Melo 
170301b4770dSJiri Olsa static void hists_browser__headers(struct hist_browser *browser)
170401b4770dSJiri Olsa {
170569705b35SJiri Olsa 	struct hists *hists = browser->hists;
170669705b35SJiri Olsa 	struct perf_hpp_list *hpp_list = hists->hpp_list;
170769705b35SJiri Olsa 
170869705b35SJiri Olsa 	int line;
170969705b35SJiri Olsa 
171069705b35SJiri Olsa 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
171101b4770dSJiri Olsa 		char headers[1024];
171201b4770dSJiri Olsa 
171301b4770dSJiri Olsa 		hists_browser__scnprintf_headers(browser, headers,
171469705b35SJiri Olsa 						 sizeof(headers), line);
171501b4770dSJiri Olsa 
171669705b35SJiri Olsa 		ui_browser__gotorc(&browser->b, line, 0);
171701b4770dSJiri Olsa 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
171801b4770dSJiri Olsa 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
171901b4770dSJiri Olsa 	}
172069705b35SJiri Olsa }
172101b4770dSJiri Olsa 
172201b4770dSJiri Olsa static void hist_browser__show_headers(struct hist_browser *browser)
172301b4770dSJiri Olsa {
172401b4770dSJiri Olsa 	if (symbol_conf.report_hierarchy)
172501b4770dSJiri Olsa 		hists_browser__hierarchy_headers(browser);
172601b4770dSJiri Olsa 	else
172701b4770dSJiri Olsa 		hists_browser__headers(browser);
172801b4770dSJiri Olsa }
172901b4770dSJiri Olsa 
1730aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
1731aca7a94dSNamhyung Kim {
1732aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
1733aca7a94dSNamhyung Kim 		struct hist_browser *hb;
1734aca7a94dSNamhyung Kim 
1735aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
1736aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
1737aca7a94dSNamhyung Kim 	}
1738aca7a94dSNamhyung Kim }
1739aca7a94dSNamhyung Kim 
174005e8b080SArnaldo Carvalho de Melo static unsigned int hist_browser__refresh(struct ui_browser *browser)
1741aca7a94dSNamhyung Kim {
1742aca7a94dSNamhyung Kim 	unsigned row = 0;
1743025bf7eaSArnaldo Carvalho de Melo 	u16 header_offset = 0;
1744aca7a94dSNamhyung Kim 	struct rb_node *nd;
174505e8b080SArnaldo Carvalho de Melo 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1746f8e6710dSJiri Olsa 	struct hists *hists = hb->hists;
1747aca7a94dSNamhyung Kim 
1748025bf7eaSArnaldo Carvalho de Melo 	if (hb->show_headers) {
1749f8e6710dSJiri Olsa 		struct perf_hpp_list *hpp_list = hists->hpp_list;
1750f8e6710dSJiri Olsa 
1751025bf7eaSArnaldo Carvalho de Melo 		hist_browser__show_headers(hb);
1752f8e6710dSJiri Olsa 		header_offset = hpp_list->nr_header_lines;
1753025bf7eaSArnaldo Carvalho de Melo 	}
1754025bf7eaSArnaldo Carvalho de Melo 
175505e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1756979d2cacSWang Nan 	hb->he_selection = NULL;
1757979d2cacSWang Nan 	hb->selection = NULL;
1758aca7a94dSNamhyung Kim 
1759d0506edbSNamhyung Kim 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1760aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
176114135663SNamhyung Kim 		float percent;
1762aca7a94dSNamhyung Kim 
1763d0506edbSNamhyung Kim 		if (h->filtered) {
1764d0506edbSNamhyung Kim 			/* let it move to sibling */
1765d0506edbSNamhyung Kim 			h->unfolded = false;
1766aca7a94dSNamhyung Kim 			continue;
1767d0506edbSNamhyung Kim 		}
1768aca7a94dSNamhyung Kim 
176914135663SNamhyung Kim 		percent = hist_entry__get_percent_limit(h);
1770064f1981SNamhyung Kim 		if (percent < hb->min_pcnt)
1771064f1981SNamhyung Kim 			continue;
1772064f1981SNamhyung Kim 
1773d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
1774d0506edbSNamhyung Kim 			row += hist_browser__show_hierarchy_entry(hb, h, row,
17752dbbe9f2SNamhyung Kim 								  h->depth);
177679dded87SNamhyung Kim 			if (row == browser->rows)
177779dded87SNamhyung Kim 				break;
177879dded87SNamhyung Kim 
177979dded87SNamhyung Kim 			if (h->has_no_entry) {
1780a61a22f6SNamhyung Kim 				hist_browser__show_no_entry(hb, row, h->depth + 1);
178179dded87SNamhyung Kim 				row++;
178279dded87SNamhyung Kim 			}
1783d0506edbSNamhyung Kim 		} else {
1784aca7a94dSNamhyung Kim 			row += hist_browser__show_entry(hb, h, row);
1785d0506edbSNamhyung Kim 		}
1786d0506edbSNamhyung Kim 
178762c95ae3SArnaldo Carvalho de Melo 		if (row == browser->rows)
1788aca7a94dSNamhyung Kim 			break;
1789aca7a94dSNamhyung Kim 	}
1790aca7a94dSNamhyung Kim 
1791025bf7eaSArnaldo Carvalho de Melo 	return row + header_offset;
1792aca7a94dSNamhyung Kim }
1793aca7a94dSNamhyung Kim 
1794064f1981SNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd,
1795064f1981SNamhyung Kim 					     float min_pcnt)
1796aca7a94dSNamhyung Kim {
1797aca7a94dSNamhyung Kim 	while (nd != NULL) {
1798aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
179914135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1800064f1981SNamhyung Kim 
1801c0f1527bSNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1802aca7a94dSNamhyung Kim 			return nd;
1803aca7a94dSNamhyung Kim 
1804d0506edbSNamhyung Kim 		/*
1805d0506edbSNamhyung Kim 		 * If it's filtered, its all children also were filtered.
1806d0506edbSNamhyung Kim 		 * So move to sibling node.
1807d0506edbSNamhyung Kim 		 */
1808d0506edbSNamhyung Kim 		if (rb_next(nd))
1809aca7a94dSNamhyung Kim 			nd = rb_next(nd);
1810d0506edbSNamhyung Kim 		else
1811d0506edbSNamhyung Kim 			nd = rb_hierarchy_next(nd);
1812aca7a94dSNamhyung Kim 	}
1813aca7a94dSNamhyung Kim 
1814aca7a94dSNamhyung Kim 	return NULL;
1815aca7a94dSNamhyung Kim }
1816aca7a94dSNamhyung Kim 
1817064f1981SNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1818064f1981SNamhyung Kim 						  float min_pcnt)
1819aca7a94dSNamhyung Kim {
1820aca7a94dSNamhyung Kim 	while (nd != NULL) {
1821aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
182214135663SNamhyung Kim 		float percent = hist_entry__get_percent_limit(h);
1823064f1981SNamhyung Kim 
1824064f1981SNamhyung Kim 		if (!h->filtered && percent >= min_pcnt)
1825aca7a94dSNamhyung Kim 			return nd;
1826aca7a94dSNamhyung Kim 
1827d0506edbSNamhyung Kim 		nd = rb_hierarchy_prev(nd);
1828aca7a94dSNamhyung Kim 	}
1829aca7a94dSNamhyung Kim 
1830aca7a94dSNamhyung Kim 	return NULL;
1831aca7a94dSNamhyung Kim }
1832aca7a94dSNamhyung Kim 
183305e8b080SArnaldo Carvalho de Melo static void ui_browser__hists_seek(struct ui_browser *browser,
1834aca7a94dSNamhyung Kim 				   off_t offset, int whence)
1835aca7a94dSNamhyung Kim {
1836aca7a94dSNamhyung Kim 	struct hist_entry *h;
1837aca7a94dSNamhyung Kim 	struct rb_node *nd;
1838aca7a94dSNamhyung Kim 	bool first = true;
1839064f1981SNamhyung Kim 	struct hist_browser *hb;
1840064f1981SNamhyung Kim 
1841064f1981SNamhyung Kim 	hb = container_of(browser, struct hist_browser, b);
1842aca7a94dSNamhyung Kim 
184305e8b080SArnaldo Carvalho de Melo 	if (browser->nr_entries == 0)
1844aca7a94dSNamhyung Kim 		return;
1845aca7a94dSNamhyung Kim 
184605e8b080SArnaldo Carvalho de Melo 	ui_browser__hists_init_top(browser);
1847aca7a94dSNamhyung Kim 
1848aca7a94dSNamhyung Kim 	switch (whence) {
1849aca7a94dSNamhyung Kim 	case SEEK_SET:
1850064f1981SNamhyung Kim 		nd = hists__filter_entries(rb_first(browser->entries),
185114135663SNamhyung Kim 					   hb->min_pcnt);
1852aca7a94dSNamhyung Kim 		break;
1853aca7a94dSNamhyung Kim 	case SEEK_CUR:
185405e8b080SArnaldo Carvalho de Melo 		nd = browser->top;
1855aca7a94dSNamhyung Kim 		goto do_offset;
1856aca7a94dSNamhyung Kim 	case SEEK_END:
1857d0506edbSNamhyung Kim 		nd = rb_hierarchy_last(rb_last(browser->entries));
1858d0506edbSNamhyung Kim 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1859aca7a94dSNamhyung Kim 		first = false;
1860aca7a94dSNamhyung Kim 		break;
1861aca7a94dSNamhyung Kim 	default:
1862aca7a94dSNamhyung Kim 		return;
1863aca7a94dSNamhyung Kim 	}
1864aca7a94dSNamhyung Kim 
1865aca7a94dSNamhyung Kim 	/*
1866aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
1867aca7a94dSNamhyung Kim 	 * row_offset:
1868aca7a94dSNamhyung Kim 	 */
186905e8b080SArnaldo Carvalho de Melo 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1870aca7a94dSNamhyung Kim 	h->row_offset = 0;
1871aca7a94dSNamhyung Kim 
1872aca7a94dSNamhyung Kim 	/*
1873aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
1874aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
1875aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
1876aca7a94dSNamhyung Kim 	 *
1877aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
1878aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
1879aca7a94dSNamhyung Kim 	 *
1880aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
1881aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
1882aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
1883aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
1884aca7a94dSNamhyung Kim 	 */
1885aca7a94dSNamhyung Kim do_offset:
1886837eeb75SWang Nan 	if (!nd)
1887837eeb75SWang Nan 		return;
1888837eeb75SWang Nan 
1889aca7a94dSNamhyung Kim 	if (offset > 0) {
1890aca7a94dSNamhyung Kim 		do {
1891aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1892d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1893aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
1894aca7a94dSNamhyung Kim 				if (offset > remaining) {
1895aca7a94dSNamhyung Kim 					offset -= remaining;
1896aca7a94dSNamhyung Kim 					h->row_offset = 0;
1897aca7a94dSNamhyung Kim 				} else {
1898aca7a94dSNamhyung Kim 					h->row_offset += offset;
1899aca7a94dSNamhyung Kim 					offset = 0;
190005e8b080SArnaldo Carvalho de Melo 					browser->top = nd;
1901aca7a94dSNamhyung Kim 					break;
1902aca7a94dSNamhyung Kim 				}
1903aca7a94dSNamhyung Kim 			}
1904d0506edbSNamhyung Kim 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1905d0506edbSNamhyung Kim 						   hb->min_pcnt);
1906aca7a94dSNamhyung Kim 			if (nd == NULL)
1907aca7a94dSNamhyung Kim 				break;
1908aca7a94dSNamhyung Kim 			--offset;
190905e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1910aca7a94dSNamhyung Kim 		} while (offset != 0);
1911aca7a94dSNamhyung Kim 	} else if (offset < 0) {
1912aca7a94dSNamhyung Kim 		while (1) {
1913aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
1914d0506edbSNamhyung Kim 			if (h->unfolded && h->leaf) {
1915aca7a94dSNamhyung Kim 				if (first) {
1916aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
1917aca7a94dSNamhyung Kim 						offset += h->row_offset;
1918aca7a94dSNamhyung Kim 						h->row_offset = 0;
1919aca7a94dSNamhyung Kim 					} else {
1920aca7a94dSNamhyung Kim 						h->row_offset += offset;
1921aca7a94dSNamhyung Kim 						offset = 0;
192205e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1923aca7a94dSNamhyung Kim 						break;
1924aca7a94dSNamhyung Kim 					}
1925aca7a94dSNamhyung Kim 				} else {
1926aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
1927aca7a94dSNamhyung Kim 						offset += h->nr_rows;
1928aca7a94dSNamhyung Kim 						h->row_offset = 0;
1929aca7a94dSNamhyung Kim 					} else {
1930aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
1931aca7a94dSNamhyung Kim 						offset = 0;
193205e8b080SArnaldo Carvalho de Melo 						browser->top = nd;
1933aca7a94dSNamhyung Kim 						break;
1934aca7a94dSNamhyung Kim 					}
1935aca7a94dSNamhyung Kim 				}
1936aca7a94dSNamhyung Kim 			}
1937aca7a94dSNamhyung Kim 
1938d0506edbSNamhyung Kim 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1939064f1981SNamhyung Kim 							hb->min_pcnt);
1940aca7a94dSNamhyung Kim 			if (nd == NULL)
1941aca7a94dSNamhyung Kim 				break;
1942aca7a94dSNamhyung Kim 			++offset;
194305e8b080SArnaldo Carvalho de Melo 			browser->top = nd;
1944aca7a94dSNamhyung Kim 			if (offset == 0) {
1945aca7a94dSNamhyung Kim 				/*
1946aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
1947aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
1948aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
1949aca7a94dSNamhyung Kim 				 */
1950aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
1951d0506edbSNamhyung Kim 				if (h->unfolded && h->leaf)
1952aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
1953aca7a94dSNamhyung Kim 				break;
1954aca7a94dSNamhyung Kim 			}
1955aca7a94dSNamhyung Kim 			first = false;
1956aca7a94dSNamhyung Kim 		}
1957aca7a94dSNamhyung Kim 	} else {
195805e8b080SArnaldo Carvalho de Melo 		browser->top = nd;
1959aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
1960aca7a94dSNamhyung Kim 		h->row_offset = 0;
1961aca7a94dSNamhyung Kim 	}
1962aca7a94dSNamhyung Kim }
1963aca7a94dSNamhyung Kim 
1964aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1965d0506edbSNamhyung Kim 					   struct hist_entry *he, FILE *fp,
1966d0506edbSNamhyung Kim 					   int level)
1967aff3f3f6SArnaldo Carvalho de Melo {
196839ee533fSNamhyung Kim 	struct callchain_print_arg arg  = {
196939ee533fSNamhyung Kim 		.fp = fp,
197039ee533fSNamhyung Kim 	};
1971aff3f3f6SArnaldo Carvalho de Melo 
1972d0506edbSNamhyung Kim 	hist_browser__show_callchain(browser, he, level, 0,
197339ee533fSNamhyung Kim 				     hist_browser__fprintf_callchain_entry, &arg,
197439ee533fSNamhyung Kim 				     hist_browser__check_dump_full);
197539ee533fSNamhyung Kim 	return arg.printed;
1976aff3f3f6SArnaldo Carvalho de Melo }
1977aff3f3f6SArnaldo Carvalho de Melo 
1978aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf_entry(struct hist_browser *browser,
1979aff3f3f6SArnaldo Carvalho de Melo 				       struct hist_entry *he, FILE *fp)
1980aff3f3f6SArnaldo Carvalho de Melo {
1981aff3f3f6SArnaldo Carvalho de Melo 	char s[8192];
1982aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
1983aff3f3f6SArnaldo Carvalho de Melo 	char folded_sign = ' ';
198426d8b338SNamhyung Kim 	struct perf_hpp hpp = {
198526d8b338SNamhyung Kim 		.buf = s,
198626d8b338SNamhyung Kim 		.size = sizeof(s),
198726d8b338SNamhyung Kim 	};
198826d8b338SNamhyung Kim 	struct perf_hpp_fmt *fmt;
198926d8b338SNamhyung Kim 	bool first = true;
199026d8b338SNamhyung Kim 	int ret;
1991aff3f3f6SArnaldo Carvalho de Melo 
19921b6b678eSArnaldo Carvalho de Melo 	if (symbol_conf.use_callchain) {
1993aff3f3f6SArnaldo Carvalho de Melo 		folded_sign = hist_entry__folded(he);
1994aff3f3f6SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%c ", folded_sign);
19951b6b678eSArnaldo Carvalho de Melo 	}
1996aff3f3f6SArnaldo Carvalho de Melo 
1997f0786af5SJiri Olsa 	hists__for_each_format(browser->hists, fmt) {
1998361459f1SNamhyung Kim 		if (perf_hpp__should_skip(fmt, he->hists))
1999e67d49a7SNamhyung Kim 			continue;
2000e67d49a7SNamhyung Kim 
200126d8b338SNamhyung Kim 		if (!first) {
200226d8b338SNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
200326d8b338SNamhyung Kim 			advance_hpp(&hpp, ret);
200426d8b338SNamhyung Kim 		} else
200526d8b338SNamhyung Kim 			first = false;
2006aff3f3f6SArnaldo Carvalho de Melo 
200726d8b338SNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
200889fee709SArnaldo Carvalho de Melo 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
200926d8b338SNamhyung Kim 		advance_hpp(&hpp, ret);
201026d8b338SNamhyung Kim 	}
201189fee709SArnaldo Carvalho de Melo 	printed += fprintf(fp, "%s\n", s);
2012aff3f3f6SArnaldo Carvalho de Melo 
2013aff3f3f6SArnaldo Carvalho de Melo 	if (folded_sign == '-')
2014d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2015d0506edbSNamhyung Kim 
2016d0506edbSNamhyung Kim 	return printed;
2017d0506edbSNamhyung Kim }
2018d0506edbSNamhyung Kim 
2019d0506edbSNamhyung Kim 
2020d0506edbSNamhyung Kim static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2021d0506edbSNamhyung Kim 						 struct hist_entry *he,
2022325a6283SNamhyung Kim 						 FILE *fp, int level)
2023d0506edbSNamhyung Kim {
2024d0506edbSNamhyung Kim 	char s[8192];
2025d0506edbSNamhyung Kim 	int printed = 0;
2026d0506edbSNamhyung Kim 	char folded_sign = ' ';
2027d0506edbSNamhyung Kim 	struct perf_hpp hpp = {
2028d0506edbSNamhyung Kim 		.buf = s,
2029d0506edbSNamhyung Kim 		.size = sizeof(s),
2030d0506edbSNamhyung Kim 	};
2031d0506edbSNamhyung Kim 	struct perf_hpp_fmt *fmt;
2032325a6283SNamhyung Kim 	struct perf_hpp_list_node *fmt_node;
2033d0506edbSNamhyung Kim 	bool first = true;
2034d0506edbSNamhyung Kim 	int ret;
2035325a6283SNamhyung Kim 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2036d0506edbSNamhyung Kim 
2037d0506edbSNamhyung Kim 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2038d0506edbSNamhyung Kim 
2039d0506edbSNamhyung Kim 	folded_sign = hist_entry__folded(he);
2040d0506edbSNamhyung Kim 	printed += fprintf(fp, "%c", folded_sign);
2041d0506edbSNamhyung Kim 
2042325a6283SNamhyung Kim 	/* the first hpp_list_node is for overhead columns */
2043325a6283SNamhyung Kim 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2044325a6283SNamhyung Kim 				    struct perf_hpp_list_node, list);
2045325a6283SNamhyung Kim 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2046d0506edbSNamhyung Kim 		if (!first) {
2047d0506edbSNamhyung Kim 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2048d0506edbSNamhyung Kim 			advance_hpp(&hpp, ret);
2049d0506edbSNamhyung Kim 		} else
2050d0506edbSNamhyung Kim 			first = false;
2051d0506edbSNamhyung Kim 
2052d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2053d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
2054d0506edbSNamhyung Kim 	}
2055d0506edbSNamhyung Kim 
2056d0506edbSNamhyung Kim 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2057d0506edbSNamhyung Kim 	advance_hpp(&hpp, ret);
2058d0506edbSNamhyung Kim 
20591b2dbbf4SNamhyung Kim 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
20601b2dbbf4SNamhyung Kim 		ret = scnprintf(hpp.buf, hpp.size, "  ");
20611b2dbbf4SNamhyung Kim 		advance_hpp(&hpp, ret);
20621b2dbbf4SNamhyung Kim 
2063d0506edbSNamhyung Kim 		ret = fmt->entry(fmt, &hpp, he);
2064d0506edbSNamhyung Kim 		advance_hpp(&hpp, ret);
20651b2dbbf4SNamhyung Kim 	}
2066d0506edbSNamhyung Kim 
2067d0506edbSNamhyung Kim 	printed += fprintf(fp, "%s\n", rtrim(s));
2068d0506edbSNamhyung Kim 
2069d0506edbSNamhyung Kim 	if (he->leaf && folded_sign == '-') {
2070d0506edbSNamhyung Kim 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2071d0506edbSNamhyung Kim 							   he->depth + 1);
2072d0506edbSNamhyung Kim 	}
2073aff3f3f6SArnaldo Carvalho de Melo 
2074aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2075aff3f3f6SArnaldo Carvalho de Melo }
2076aff3f3f6SArnaldo Carvalho de Melo 
2077aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2078aff3f3f6SArnaldo Carvalho de Melo {
2079064f1981SNamhyung Kim 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2080064f1981SNamhyung Kim 						   browser->min_pcnt);
2081aff3f3f6SArnaldo Carvalho de Melo 	int printed = 0;
2082aff3f3f6SArnaldo Carvalho de Melo 
2083aff3f3f6SArnaldo Carvalho de Melo 	while (nd) {
2084aff3f3f6SArnaldo Carvalho de Melo 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2085aff3f3f6SArnaldo Carvalho de Melo 
2086d0506edbSNamhyung Kim 		if (symbol_conf.report_hierarchy) {
2087d0506edbSNamhyung Kim 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2088d0506edbSNamhyung Kim 									 h, fp,
2089325a6283SNamhyung Kim 									 h->depth);
2090d0506edbSNamhyung Kim 		} else {
2091aff3f3f6SArnaldo Carvalho de Melo 			printed += hist_browser__fprintf_entry(browser, h, fp);
2092d0506edbSNamhyung Kim 		}
2093d0506edbSNamhyung Kim 
2094d0506edbSNamhyung Kim 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2095d0506edbSNamhyung Kim 					   browser->min_pcnt);
2096aff3f3f6SArnaldo Carvalho de Melo 	}
2097aff3f3f6SArnaldo Carvalho de Melo 
2098aff3f3f6SArnaldo Carvalho de Melo 	return printed;
2099aff3f3f6SArnaldo Carvalho de Melo }
2100aff3f3f6SArnaldo Carvalho de Melo 
2101aff3f3f6SArnaldo Carvalho de Melo static int hist_browser__dump(struct hist_browser *browser)
2102aff3f3f6SArnaldo Carvalho de Melo {
2103aff3f3f6SArnaldo Carvalho de Melo 	char filename[64];
2104aff3f3f6SArnaldo Carvalho de Melo 	FILE *fp;
2105aff3f3f6SArnaldo Carvalho de Melo 
2106aff3f3f6SArnaldo Carvalho de Melo 	while (1) {
2107aff3f3f6SArnaldo Carvalho de Melo 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2108aff3f3f6SArnaldo Carvalho de Melo 		if (access(filename, F_OK))
2109aff3f3f6SArnaldo Carvalho de Melo 			break;
2110aff3f3f6SArnaldo Carvalho de Melo 		/*
2111aff3f3f6SArnaldo Carvalho de Melo  		 * XXX: Just an arbitrary lazy upper limit
2112aff3f3f6SArnaldo Carvalho de Melo  		 */
2113aff3f3f6SArnaldo Carvalho de Melo 		if (++browser->print_seq == 8192) {
2114aff3f3f6SArnaldo Carvalho de Melo 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2115aff3f3f6SArnaldo Carvalho de Melo 			return -1;
2116aff3f3f6SArnaldo Carvalho de Melo 		}
2117aff3f3f6SArnaldo Carvalho de Melo 	}
2118aff3f3f6SArnaldo Carvalho de Melo 
2119aff3f3f6SArnaldo Carvalho de Melo 	fp = fopen(filename, "w");
2120aff3f3f6SArnaldo Carvalho de Melo 	if (fp == NULL) {
2121aff3f3f6SArnaldo Carvalho de Melo 		char bf[64];
2122c8b5f2c9SArnaldo Carvalho de Melo 		const char *err = str_error_r(errno, bf, sizeof(bf));
21234cc49d4dSKirill A. Shutemov 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2124aff3f3f6SArnaldo Carvalho de Melo 		return -1;
2125aff3f3f6SArnaldo Carvalho de Melo 	}
2126aff3f3f6SArnaldo Carvalho de Melo 
2127aff3f3f6SArnaldo Carvalho de Melo 	++browser->print_seq;
2128aff3f3f6SArnaldo Carvalho de Melo 	hist_browser__fprintf(browser, fp);
2129aff3f3f6SArnaldo Carvalho de Melo 	fclose(fp);
2130aff3f3f6SArnaldo Carvalho de Melo 	ui_helpline__fpush("%s written!", filename);
2131aff3f3f6SArnaldo Carvalho de Melo 
2132aff3f3f6SArnaldo Carvalho de Melo 	return 0;
2133aff3f3f6SArnaldo Carvalho de Melo }
2134aff3f3f6SArnaldo Carvalho de Melo 
2135fcd86426SJiri Olsa void hist_browser__init(struct hist_browser *browser,
2136fcd86426SJiri Olsa 			struct hists *hists)
2137aca7a94dSNamhyung Kim {
2138b1c7a8f7SJiri Olsa 	struct perf_hpp_fmt *fmt;
2139b1c7a8f7SJiri Olsa 
214005e8b080SArnaldo Carvalho de Melo 	browser->hists			= hists;
214105e8b080SArnaldo Carvalho de Melo 	browser->b.refresh		= hist_browser__refresh;
2142357cfff1SArnaldo Carvalho de Melo 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
214305e8b080SArnaldo Carvalho de Melo 	browser->b.seek			= ui_browser__hists_seek;
214405e8b080SArnaldo Carvalho de Melo 	browser->b.use_navkeypressed	= true;
2145c8302367SJiri Olsa 	browser->show_headers		= symbol_conf.show_hist_headers;
2146b1c7a8f7SJiri Olsa 
21478a06b0beSNamhyung Kim 	if (symbol_conf.report_hierarchy) {
21488a06b0beSNamhyung Kim 		struct perf_hpp_list_node *fmt_node;
21498a06b0beSNamhyung Kim 
21508a06b0beSNamhyung Kim 		/* count overhead columns (in the first node) */
21518a06b0beSNamhyung Kim 		fmt_node = list_first_entry(&hists->hpp_formats,
21528a06b0beSNamhyung Kim 					    struct perf_hpp_list_node, list);
21538a06b0beSNamhyung Kim 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
21548a06b0beSNamhyung Kim 			++browser->b.columns;
21558a06b0beSNamhyung Kim 
21568a06b0beSNamhyung Kim 		/* add a single column for whole hierarchy sort keys*/
21578a06b0beSNamhyung Kim 		++browser->b.columns;
21588a06b0beSNamhyung Kim 	} else {
2159e3b60bc9SNamhyung Kim 		hists__for_each_format(hists, fmt)
2160b1c7a8f7SJiri Olsa 			++browser->b.columns;
21618a06b0beSNamhyung Kim 	}
2162e3b60bc9SNamhyung Kim 
2163e3b60bc9SNamhyung Kim 	hists__reset_column_width(hists);
2164aca7a94dSNamhyung Kim }
2165aca7a94dSNamhyung Kim 
2166fcd86426SJiri Olsa struct hist_browser *hist_browser__new(struct hists *hists)
2167fcd86426SJiri Olsa {
2168fcd86426SJiri Olsa 	struct hist_browser *browser = zalloc(sizeof(*browser));
2169fcd86426SJiri Olsa 
2170fcd86426SJiri Olsa 	if (browser)
2171fcd86426SJiri Olsa 		hist_browser__init(browser, hists);
2172fcd86426SJiri Olsa 
217305e8b080SArnaldo Carvalho de Melo 	return browser;
2174aca7a94dSNamhyung Kim }
2175aca7a94dSNamhyung Kim 
2176a6ec894dSJiri Olsa static struct hist_browser *
2177a6ec894dSJiri Olsa perf_evsel_browser__new(struct perf_evsel *evsel,
2178a6ec894dSJiri Olsa 			struct hist_browser_timer *hbt,
2179a6ec894dSJiri Olsa 			struct perf_env *env)
2180a6ec894dSJiri Olsa {
2181a6ec894dSJiri Olsa 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2182a6ec894dSJiri Olsa 
2183a6ec894dSJiri Olsa 	if (browser) {
2184a6ec894dSJiri Olsa 		browser->hbt   = hbt;
2185a6ec894dSJiri Olsa 		browser->env   = env;
2186a6ec894dSJiri Olsa 		browser->title = perf_evsel_browser_title;
2187a6ec894dSJiri Olsa 	}
2188a6ec894dSJiri Olsa 	return browser;
2189a6ec894dSJiri Olsa }
2190a6ec894dSJiri Olsa 
2191dabd2012SJiri Olsa void hist_browser__delete(struct hist_browser *browser)
2192aca7a94dSNamhyung Kim {
219305e8b080SArnaldo Carvalho de Melo 	free(browser);
2194aca7a94dSNamhyung Kim }
2195aca7a94dSNamhyung Kim 
219605e8b080SArnaldo Carvalho de Melo static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2197aca7a94dSNamhyung Kim {
219805e8b080SArnaldo Carvalho de Melo 	return browser->he_selection;
2199aca7a94dSNamhyung Kim }
2200aca7a94dSNamhyung Kim 
220105e8b080SArnaldo Carvalho de Melo static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2202aca7a94dSNamhyung Kim {
220305e8b080SArnaldo Carvalho de Melo 	return browser->he_selection->thread;
2204aca7a94dSNamhyung Kim }
2205aca7a94dSNamhyung Kim 
22061e378ebdSTaeung Song /* Check whether the browser is for 'top' or 'report' */
22071e378ebdSTaeung Song static inline bool is_report_browser(void *timer)
22081e378ebdSTaeung Song {
22091e378ebdSTaeung Song 	return timer == NULL;
22101e378ebdSTaeung Song }
22111e378ebdSTaeung Song 
22125b91a86fSJiri Olsa static int perf_evsel_browser_title(struct hist_browser *browser,
22131e378ebdSTaeung Song 				char *bf, size_t size)
2214aca7a94dSNamhyung Kim {
22155b91a86fSJiri Olsa 	struct hist_browser_timer *hbt = browser->hbt;
22165b91a86fSJiri Olsa 	struct hists *hists = browser->hists;
2217aca7a94dSNamhyung Kim 	char unit;
2218aca7a94dSNamhyung Kim 	int printed;
221905e8b080SArnaldo Carvalho de Melo 	const struct dso *dso = hists->dso_filter;
222005e8b080SArnaldo Carvalho de Melo 	const struct thread *thread = hists->thread_filter;
222184734b06SKan Liang 	int socket_id = hists->socket_filter;
222205e8b080SArnaldo Carvalho de Melo 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
222305e8b080SArnaldo Carvalho de Melo 	u64 nr_events = hists->stats.total_period;
2224717e263fSNamhyung Kim 	struct perf_evsel *evsel = hists_to_evsel(hists);
2225dd00d486SJiri Olsa 	const char *ev_name = perf_evsel__name(evsel);
2226717e263fSNamhyung Kim 	char buf[512];
2227717e263fSNamhyung Kim 	size_t buflen = sizeof(buf);
22289e207ddfSKan Liang 	char ref[30] = " show reference callgraph, ";
22299e207ddfSKan Liang 	bool enable_ref = false;
2230717e263fSNamhyung Kim 
2231f2148330SNamhyung Kim 	if (symbol_conf.filter_relative) {
2232f2148330SNamhyung Kim 		nr_samples = hists->stats.nr_non_filtered_samples;
2233f2148330SNamhyung Kim 		nr_events = hists->stats.total_non_filtered_period;
2234f2148330SNamhyung Kim 	}
2235f2148330SNamhyung Kim 
2236759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
2237717e263fSNamhyung Kim 		struct perf_evsel *pos;
2238717e263fSNamhyung Kim 
2239717e263fSNamhyung Kim 		perf_evsel__group_desc(evsel, buf, buflen);
2240717e263fSNamhyung Kim 		ev_name = buf;
2241717e263fSNamhyung Kim 
2242717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
22434ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
22444ea062edSArnaldo Carvalho de Melo 
2245f2148330SNamhyung Kim 			if (symbol_conf.filter_relative) {
22464ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
22474ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_non_filtered_period;
2248f2148330SNamhyung Kim 			} else {
22494ea062edSArnaldo Carvalho de Melo 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
22504ea062edSArnaldo Carvalho de Melo 				nr_events += pos_hists->stats.total_period;
2251717e263fSNamhyung Kim 			}
2252717e263fSNamhyung Kim 		}
2253f2148330SNamhyung Kim 	}
2254aca7a94dSNamhyung Kim 
22559e207ddfSKan Liang 	if (symbol_conf.show_ref_callgraph &&
22569e207ddfSKan Liang 	    strstr(ev_name, "call-graph=no"))
22579e207ddfSKan Liang 		enable_ref = true;
2258aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
2259aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
22609e207ddfSKan Liang 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
22619e207ddfSKan Liang 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2262aca7a94dSNamhyung Kim 
2263aca7a94dSNamhyung Kim 
226405e8b080SArnaldo Carvalho de Melo 	if (hists->uid_filter_str)
2265aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
226605e8b080SArnaldo Carvalho de Melo 				    ", UID: %s", hists->uid_filter_str);
22676962ccb3SNamhyung Kim 	if (thread) {
2268fa82911aSJiri Olsa 		if (hists__has(hists, thread)) {
2269aca7a94dSNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
2270aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
2271b9c5143aSFrederic Weisbecker 				     (thread->comm_set ? thread__comm_str(thread) : ""),
227238051234SAdrian Hunter 				    thread->tid);
22736962ccb3SNamhyung Kim 		} else {
22746962ccb3SNamhyung Kim 			printed += scnprintf(bf + printed, size - printed,
22756962ccb3SNamhyung Kim 				    ", Thread: %s",
22766962ccb3SNamhyung Kim 				     (thread->comm_set ? thread__comm_str(thread) : ""));
22776962ccb3SNamhyung Kim 		}
22786962ccb3SNamhyung Kim 	}
2279aca7a94dSNamhyung Kim 	if (dso)
2280aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
2281aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
228284734b06SKan Liang 	if (socket_id > -1)
228321394d94SKan Liang 		printed += scnprintf(bf + printed, size - printed,
228484734b06SKan Liang 				    ", Processor Socket: %d", socket_id);
22851e378ebdSTaeung Song 	if (!is_report_browser(hbt)) {
22861e378ebdSTaeung Song 		struct perf_top *top = hbt->arg;
22871e378ebdSTaeung Song 
22881e378ebdSTaeung Song 		if (top->zero)
22891e378ebdSTaeung Song 			printed += scnprintf(bf + printed, size - printed, " [z]");
22901e378ebdSTaeung Song 	}
22911e378ebdSTaeung Song 
2292aca7a94dSNamhyung Kim 	return printed;
2293aca7a94dSNamhyung Kim }
2294aca7a94dSNamhyung Kim 
2295aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
2296aca7a94dSNamhyung Kim {
2297aca7a94dSNamhyung Kim 	int i;
2298aca7a94dSNamhyung Kim 
229904662523SArnaldo Carvalho de Melo 	for (i = 0; i < n; ++i)
230004662523SArnaldo Carvalho de Melo 		zfree(&options[i]);
2301aca7a94dSNamhyung Kim }
2302aca7a94dSNamhyung Kim 
2303341487abSFeng Tang /*
2304341487abSFeng Tang  * Only runtime switching of perf data file will make "input_name" point
2305341487abSFeng Tang  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2306341487abSFeng Tang  * whether we need to call free() for current "input_name" during the switch.
2307341487abSFeng Tang  */
2308341487abSFeng Tang static bool is_input_name_malloced = false;
2309341487abSFeng Tang 
2310341487abSFeng Tang static int switch_data_file(void)
2311341487abSFeng Tang {
2312341487abSFeng Tang 	char *pwd, *options[32], *abs_path[32], *tmp;
2313341487abSFeng Tang 	DIR *pwd_dir;
2314341487abSFeng Tang 	int nr_options = 0, choice = -1, ret = -1;
2315341487abSFeng Tang 	struct dirent *dent;
2316341487abSFeng Tang 
2317341487abSFeng Tang 	pwd = getenv("PWD");
2318341487abSFeng Tang 	if (!pwd)
2319341487abSFeng Tang 		return ret;
2320341487abSFeng Tang 
2321341487abSFeng Tang 	pwd_dir = opendir(pwd);
2322341487abSFeng Tang 	if (!pwd_dir)
2323341487abSFeng Tang 		return ret;
2324341487abSFeng Tang 
2325341487abSFeng Tang 	memset(options, 0, sizeof(options));
23263ef5b402SChangbin Du 	memset(abs_path, 0, sizeof(abs_path));
2327341487abSFeng Tang 
2328341487abSFeng Tang 	while ((dent = readdir(pwd_dir))) {
2329341487abSFeng Tang 		char path[PATH_MAX];
2330341487abSFeng Tang 		u64 magic;
2331341487abSFeng Tang 		char *name = dent->d_name;
2332341487abSFeng Tang 		FILE *file;
2333341487abSFeng Tang 
2334341487abSFeng Tang 		if (!(dent->d_type == DT_REG))
2335341487abSFeng Tang 			continue;
2336341487abSFeng Tang 
2337341487abSFeng Tang 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2338341487abSFeng Tang 
2339341487abSFeng Tang 		file = fopen(path, "r");
2340341487abSFeng Tang 		if (!file)
2341341487abSFeng Tang 			continue;
2342341487abSFeng Tang 
2343341487abSFeng Tang 		if (fread(&magic, 1, 8, file) < 8)
2344341487abSFeng Tang 			goto close_file_and_continue;
2345341487abSFeng Tang 
2346341487abSFeng Tang 		if (is_perf_magic(magic)) {
2347341487abSFeng Tang 			options[nr_options] = strdup(name);
2348341487abSFeng Tang 			if (!options[nr_options])
2349341487abSFeng Tang 				goto close_file_and_continue;
2350341487abSFeng Tang 
2351341487abSFeng Tang 			abs_path[nr_options] = strdup(path);
2352341487abSFeng Tang 			if (!abs_path[nr_options]) {
235374cf249dSArnaldo Carvalho de Melo 				zfree(&options[nr_options]);
2354341487abSFeng Tang 				ui__warning("Can't search all data files due to memory shortage.\n");
2355341487abSFeng Tang 				fclose(file);
2356341487abSFeng Tang 				break;
2357341487abSFeng Tang 			}
2358341487abSFeng Tang 
2359341487abSFeng Tang 			nr_options++;
2360341487abSFeng Tang 		}
2361341487abSFeng Tang 
2362341487abSFeng Tang close_file_and_continue:
2363341487abSFeng Tang 		fclose(file);
2364341487abSFeng Tang 		if (nr_options >= 32) {
2365341487abSFeng Tang 			ui__warning("Too many perf data files in PWD!\n"
2366341487abSFeng Tang 				    "Only the first 32 files will be listed.\n");
2367341487abSFeng Tang 			break;
2368341487abSFeng Tang 		}
2369341487abSFeng Tang 	}
2370341487abSFeng Tang 	closedir(pwd_dir);
2371341487abSFeng Tang 
2372341487abSFeng Tang 	if (nr_options) {
2373341487abSFeng Tang 		choice = ui__popup_menu(nr_options, options);
2374341487abSFeng Tang 		if (choice < nr_options && choice >= 0) {
2375341487abSFeng Tang 			tmp = strdup(abs_path[choice]);
2376341487abSFeng Tang 			if (tmp) {
2377341487abSFeng Tang 				if (is_input_name_malloced)
2378341487abSFeng Tang 					free((void *)input_name);
2379341487abSFeng Tang 				input_name = tmp;
2380341487abSFeng Tang 				is_input_name_malloced = true;
2381341487abSFeng Tang 				ret = 0;
2382341487abSFeng Tang 			} else
2383341487abSFeng Tang 				ui__warning("Data switch failed due to memory shortage!\n");
2384341487abSFeng Tang 		}
2385341487abSFeng Tang 	}
2386341487abSFeng Tang 
2387341487abSFeng Tang 	free_popup_options(options, nr_options);
2388341487abSFeng Tang 	free_popup_options(abs_path, nr_options);
2389341487abSFeng Tang 	return ret;
2390341487abSFeng Tang }
2391341487abSFeng Tang 
2392ea7cd592SNamhyung Kim struct popup_action {
2393ea7cd592SNamhyung Kim 	struct thread 		*thread;
2394ea7cd592SNamhyung Kim 	struct map_symbol 	ms;
239584734b06SKan Liang 	int			socket;
2396ea7cd592SNamhyung Kim 
2397ea7cd592SNamhyung Kim 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2398ea7cd592SNamhyung Kim };
2399ea7cd592SNamhyung Kim 
2400bc7cad42SNamhyung Kim static int
2401ea7cd592SNamhyung Kim do_annotate(struct hist_browser *browser, struct popup_action *act)
2402bc7cad42SNamhyung Kim {
2403bc7cad42SNamhyung Kim 	struct perf_evsel *evsel;
2404bc7cad42SNamhyung Kim 	struct annotation *notes;
2405bc7cad42SNamhyung Kim 	struct hist_entry *he;
2406bc7cad42SNamhyung Kim 	int err;
2407bc7cad42SNamhyung Kim 
2408eebd0bfcSArnaldo Carvalho de Melo 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
2409bc7cad42SNamhyung Kim 		return 0;
2410bc7cad42SNamhyung Kim 
2411ea7cd592SNamhyung Kim 	notes = symbol__annotation(act->ms.sym);
2412bc7cad42SNamhyung Kim 	if (!notes->src)
2413bc7cad42SNamhyung Kim 		return 0;
2414bc7cad42SNamhyung Kim 
2415bc7cad42SNamhyung Kim 	evsel = hists_to_evsel(browser->hists);
2416ea7cd592SNamhyung Kim 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2417bc7cad42SNamhyung Kim 	he = hist_browser__selected_entry(browser);
2418bc7cad42SNamhyung Kim 	/*
2419bc7cad42SNamhyung Kim 	 * offer option to annotate the other branch source or target
2420bc7cad42SNamhyung Kim 	 * (if they exists) when returning from annotate
2421bc7cad42SNamhyung Kim 	 */
2422bc7cad42SNamhyung Kim 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2423bc7cad42SNamhyung Kim 		return 1;
2424bc7cad42SNamhyung Kim 
2425bc7cad42SNamhyung Kim 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2426bc7cad42SNamhyung Kim 	if (err)
2427bc7cad42SNamhyung Kim 		ui_browser__handle_resize(&browser->b);
2428bc7cad42SNamhyung Kim 	return 0;
2429bc7cad42SNamhyung Kim }
2430bc7cad42SNamhyung Kim 
2431bc7cad42SNamhyung Kim static int
2432ea7cd592SNamhyung Kim add_annotate_opt(struct hist_browser *browser __maybe_unused,
2433ea7cd592SNamhyung Kim 		 struct popup_action *act, char **optstr,
2434ea7cd592SNamhyung Kim 		 struct map *map, struct symbol *sym)
2435bc7cad42SNamhyung Kim {
2436ea7cd592SNamhyung Kim 	if (sym == NULL || map->dso->annotate_warned)
2437ea7cd592SNamhyung Kim 		return 0;
2438ea7cd592SNamhyung Kim 
2439ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2440ea7cd592SNamhyung Kim 		return 0;
2441ea7cd592SNamhyung Kim 
2442ea7cd592SNamhyung Kim 	act->ms.map = map;
2443ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2444ea7cd592SNamhyung Kim 	act->fn = do_annotate;
2445ea7cd592SNamhyung Kim 	return 1;
2446ea7cd592SNamhyung Kim }
2447ea7cd592SNamhyung Kim 
2448ea7cd592SNamhyung Kim static int
2449ea7cd592SNamhyung Kim do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2450ea7cd592SNamhyung Kim {
2451ea7cd592SNamhyung Kim 	struct thread *thread = act->thread;
2452ea7cd592SNamhyung Kim 
24537cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
24547cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2455599a2f38SNamhyung Kim 		return 0;
2456599a2f38SNamhyung Kim 
2457bc7cad42SNamhyung Kim 	if (browser->hists->thread_filter) {
2458bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2459bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2460bc7cad42SNamhyung Kim 		thread__zput(browser->hists->thread_filter);
2461bc7cad42SNamhyung Kim 		ui_helpline__pop();
2462bc7cad42SNamhyung Kim 	} else {
2463fa82911aSJiri Olsa 		if (hists__has(browser->hists, thread)) {
24647727a925SArnaldo Carvalho de Melo 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2465bc7cad42SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "",
2466bc7cad42SNamhyung Kim 					   thread->tid);
24676962ccb3SNamhyung Kim 		} else {
24686962ccb3SNamhyung Kim 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
24696962ccb3SNamhyung Kim 					   thread->comm_set ? thread__comm_str(thread) : "");
24706962ccb3SNamhyung Kim 		}
24716962ccb3SNamhyung Kim 
2472bc7cad42SNamhyung Kim 		browser->hists->thread_filter = thread__get(thread);
2473bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_THREAD, false);
2474bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2475bc7cad42SNamhyung Kim 	}
2476bc7cad42SNamhyung Kim 
2477bc7cad42SNamhyung Kim 	hists__filter_by_thread(browser->hists);
2478bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2479bc7cad42SNamhyung Kim 	return 0;
2480bc7cad42SNamhyung Kim }
2481bc7cad42SNamhyung Kim 
2482bc7cad42SNamhyung Kim static int
2483ea7cd592SNamhyung Kim add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2484ea7cd592SNamhyung Kim 	       char **optstr, struct thread *thread)
2485bc7cad42SNamhyung Kim {
24866962ccb3SNamhyung Kim 	int ret;
24876962ccb3SNamhyung Kim 
24887cecb7feSJiri Olsa 	if ((!hists__has(browser->hists, thread) &&
24897cecb7feSJiri Olsa 	     !hists__has(browser->hists, comm)) || thread == NULL)
2490ea7cd592SNamhyung Kim 		return 0;
2491ea7cd592SNamhyung Kim 
2492fa82911aSJiri Olsa 	if (hists__has(browser->hists, thread)) {
24936962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2494ea7cd592SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
2495ea7cd592SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "",
24966962ccb3SNamhyung Kim 			       thread->tid);
24976962ccb3SNamhyung Kim 	} else {
24986962ccb3SNamhyung Kim 		ret = asprintf(optstr, "Zoom %s %s thread",
24996962ccb3SNamhyung Kim 			       browser->hists->thread_filter ? "out of" : "into",
25006962ccb3SNamhyung Kim 			       thread->comm_set ? thread__comm_str(thread) : "");
25016962ccb3SNamhyung Kim 	}
25026962ccb3SNamhyung Kim 	if (ret < 0)
2503ea7cd592SNamhyung Kim 		return 0;
2504ea7cd592SNamhyung Kim 
2505ea7cd592SNamhyung Kim 	act->thread = thread;
2506ea7cd592SNamhyung Kim 	act->fn = do_zoom_thread;
2507ea7cd592SNamhyung Kim 	return 1;
2508ea7cd592SNamhyung Kim }
2509ea7cd592SNamhyung Kim 
2510ea7cd592SNamhyung Kim static int
2511ea7cd592SNamhyung Kim do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2512ea7cd592SNamhyung Kim {
2513045b80ddSArnaldo Carvalho de Melo 	struct map *map = act->ms.map;
2514ea7cd592SNamhyung Kim 
251569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2516599a2f38SNamhyung Kim 		return 0;
2517599a2f38SNamhyung Kim 
2518bc7cad42SNamhyung Kim 	if (browser->hists->dso_filter) {
2519bc7cad42SNamhyung Kim 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2520bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, false);
2521bc7cad42SNamhyung Kim 		browser->hists->dso_filter = NULL;
2522bc7cad42SNamhyung Kim 		ui_helpline__pop();
2523bc7cad42SNamhyung Kim 	} else {
25247727a925SArnaldo Carvalho de Melo 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2525045b80ddSArnaldo Carvalho de Melo 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2526045b80ddSArnaldo Carvalho de Melo 		browser->hists->dso_filter = map->dso;
2527bc7cad42SNamhyung Kim 		perf_hpp__set_elide(HISTC_DSO, true);
2528bc7cad42SNamhyung Kim 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2529bc7cad42SNamhyung Kim 	}
2530bc7cad42SNamhyung Kim 
2531bc7cad42SNamhyung Kim 	hists__filter_by_dso(browser->hists);
2532bc7cad42SNamhyung Kim 	hist_browser__reset(browser);
2533bc7cad42SNamhyung Kim 	return 0;
2534bc7cad42SNamhyung Kim }
2535bc7cad42SNamhyung Kim 
2536bc7cad42SNamhyung Kim static int
2537ea7cd592SNamhyung Kim add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2538045b80ddSArnaldo Carvalho de Melo 	    char **optstr, struct map *map)
2539bc7cad42SNamhyung Kim {
254069849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2541ea7cd592SNamhyung Kim 		return 0;
2542ea7cd592SNamhyung Kim 
2543ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Zoom %s %s DSO",
2544ea7cd592SNamhyung Kim 		     browser->hists->dso_filter ? "out of" : "into",
2545045b80ddSArnaldo Carvalho de Melo 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2546ea7cd592SNamhyung Kim 		return 0;
2547ea7cd592SNamhyung Kim 
2548045b80ddSArnaldo Carvalho de Melo 	act->ms.map = map;
2549ea7cd592SNamhyung Kim 	act->fn = do_zoom_dso;
2550ea7cd592SNamhyung Kim 	return 1;
2551ea7cd592SNamhyung Kim }
2552ea7cd592SNamhyung Kim 
2553ea7cd592SNamhyung Kim static int
2554ea7cd592SNamhyung Kim do_browse_map(struct hist_browser *browser __maybe_unused,
2555ea7cd592SNamhyung Kim 	      struct popup_action *act)
2556ea7cd592SNamhyung Kim {
2557ea7cd592SNamhyung Kim 	map__browse(act->ms.map);
2558bc7cad42SNamhyung Kim 	return 0;
2559bc7cad42SNamhyung Kim }
2560bc7cad42SNamhyung Kim 
2561bc7cad42SNamhyung Kim static int
256269849fc5SJiri Olsa add_map_opt(struct hist_browser *browser,
2563ea7cd592SNamhyung Kim 	    struct popup_action *act, char **optstr, struct map *map)
2564ea7cd592SNamhyung Kim {
256569849fc5SJiri Olsa 	if (!hists__has(browser->hists, dso) || map == NULL)
2566ea7cd592SNamhyung Kim 		return 0;
2567ea7cd592SNamhyung Kim 
2568ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Browse map details") < 0)
2569ea7cd592SNamhyung Kim 		return 0;
2570ea7cd592SNamhyung Kim 
2571ea7cd592SNamhyung Kim 	act->ms.map = map;
2572ea7cd592SNamhyung Kim 	act->fn = do_browse_map;
2573ea7cd592SNamhyung Kim 	return 1;
2574ea7cd592SNamhyung Kim }
2575ea7cd592SNamhyung Kim 
2576ea7cd592SNamhyung Kim static int
2577bc7cad42SNamhyung Kim do_run_script(struct hist_browser *browser __maybe_unused,
2578ea7cd592SNamhyung Kim 	      struct popup_action *act)
2579bc7cad42SNamhyung Kim {
2580bc7cad42SNamhyung Kim 	char script_opt[64];
2581bc7cad42SNamhyung Kim 	memset(script_opt, 0, sizeof(script_opt));
2582bc7cad42SNamhyung Kim 
2583ea7cd592SNamhyung Kim 	if (act->thread) {
2584bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2585ea7cd592SNamhyung Kim 			  thread__comm_str(act->thread));
2586ea7cd592SNamhyung Kim 	} else if (act->ms.sym) {
2587bc7cad42SNamhyung Kim 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2588ea7cd592SNamhyung Kim 			  act->ms.sym->name);
2589bc7cad42SNamhyung Kim 	}
2590bc7cad42SNamhyung Kim 
2591bc7cad42SNamhyung Kim 	script_browse(script_opt);
2592bc7cad42SNamhyung Kim 	return 0;
2593bc7cad42SNamhyung Kim }
2594bc7cad42SNamhyung Kim 
2595bc7cad42SNamhyung Kim static int
2596ea7cd592SNamhyung Kim add_script_opt(struct hist_browser *browser __maybe_unused,
2597ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr,
2598ea7cd592SNamhyung Kim 	       struct thread *thread, struct symbol *sym)
2599ea7cd592SNamhyung Kim {
2600ea7cd592SNamhyung Kim 	if (thread) {
2601ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2602ea7cd592SNamhyung Kim 			     thread__comm_str(thread)) < 0)
2603ea7cd592SNamhyung Kim 			return 0;
2604ea7cd592SNamhyung Kim 	} else if (sym) {
2605ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2606ea7cd592SNamhyung Kim 			     sym->name) < 0)
2607ea7cd592SNamhyung Kim 			return 0;
2608ea7cd592SNamhyung Kim 	} else {
2609ea7cd592SNamhyung Kim 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2610ea7cd592SNamhyung Kim 			return 0;
2611ea7cd592SNamhyung Kim 	}
2612ea7cd592SNamhyung Kim 
2613ea7cd592SNamhyung Kim 	act->thread = thread;
2614ea7cd592SNamhyung Kim 	act->ms.sym = sym;
2615ea7cd592SNamhyung Kim 	act->fn = do_run_script;
2616ea7cd592SNamhyung Kim 	return 1;
2617ea7cd592SNamhyung Kim }
2618ea7cd592SNamhyung Kim 
2619ea7cd592SNamhyung Kim static int
2620ea7cd592SNamhyung Kim do_switch_data(struct hist_browser *browser __maybe_unused,
2621ea7cd592SNamhyung Kim 	       struct popup_action *act __maybe_unused)
2622bc7cad42SNamhyung Kim {
2623bc7cad42SNamhyung Kim 	if (switch_data_file()) {
2624bc7cad42SNamhyung Kim 		ui__warning("Won't switch the data files due to\n"
2625bc7cad42SNamhyung Kim 			    "no valid data file get selected!\n");
2626ea7cd592SNamhyung Kim 		return 0;
2627bc7cad42SNamhyung Kim 	}
2628bc7cad42SNamhyung Kim 
2629bc7cad42SNamhyung Kim 	return K_SWITCH_INPUT_DATA;
2630bc7cad42SNamhyung Kim }
2631bc7cad42SNamhyung Kim 
2632ea7cd592SNamhyung Kim static int
2633ea7cd592SNamhyung Kim add_switch_opt(struct hist_browser *browser,
2634ea7cd592SNamhyung Kim 	       struct popup_action *act, char **optstr)
2635ea7cd592SNamhyung Kim {
2636ea7cd592SNamhyung Kim 	if (!is_report_browser(browser->hbt))
2637ea7cd592SNamhyung Kim 		return 0;
2638ea7cd592SNamhyung Kim 
2639ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2640ea7cd592SNamhyung Kim 		return 0;
2641ea7cd592SNamhyung Kim 
2642ea7cd592SNamhyung Kim 	act->fn = do_switch_data;
2643ea7cd592SNamhyung Kim 	return 1;
2644ea7cd592SNamhyung Kim }
2645ea7cd592SNamhyung Kim 
2646ea7cd592SNamhyung Kim static int
2647ea7cd592SNamhyung Kim do_exit_browser(struct hist_browser *browser __maybe_unused,
2648ea7cd592SNamhyung Kim 		struct popup_action *act __maybe_unused)
2649ea7cd592SNamhyung Kim {
2650ea7cd592SNamhyung Kim 	return 0;
2651ea7cd592SNamhyung Kim }
2652ea7cd592SNamhyung Kim 
2653ea7cd592SNamhyung Kim static int
2654ea7cd592SNamhyung Kim add_exit_opt(struct hist_browser *browser __maybe_unused,
2655ea7cd592SNamhyung Kim 	     struct popup_action *act, char **optstr)
2656ea7cd592SNamhyung Kim {
2657ea7cd592SNamhyung Kim 	if (asprintf(optstr, "Exit") < 0)
2658ea7cd592SNamhyung Kim 		return 0;
2659ea7cd592SNamhyung Kim 
2660ea7cd592SNamhyung Kim 	act->fn = do_exit_browser;
2661ea7cd592SNamhyung Kim 	return 1;
2662ea7cd592SNamhyung Kim }
2663ea7cd592SNamhyung Kim 
266484734b06SKan Liang static int
266584734b06SKan Liang do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
266684734b06SKan Liang {
266735a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2668599a2f38SNamhyung Kim 		return 0;
2669599a2f38SNamhyung Kim 
267084734b06SKan Liang 	if (browser->hists->socket_filter > -1) {
267184734b06SKan Liang 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
267284734b06SKan Liang 		browser->hists->socket_filter = -1;
267384734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, false);
267484734b06SKan Liang 	} else {
267584734b06SKan Liang 		browser->hists->socket_filter = act->socket;
267684734b06SKan Liang 		perf_hpp__set_elide(HISTC_SOCKET, true);
267784734b06SKan Liang 		pstack__push(browser->pstack, &browser->hists->socket_filter);
267884734b06SKan Liang 	}
267984734b06SKan Liang 
268084734b06SKan Liang 	hists__filter_by_socket(browser->hists);
268184734b06SKan Liang 	hist_browser__reset(browser);
268284734b06SKan Liang 	return 0;
268384734b06SKan Liang }
268484734b06SKan Liang 
268584734b06SKan Liang static int
268684734b06SKan Liang add_socket_opt(struct hist_browser *browser, struct popup_action *act,
268784734b06SKan Liang 	       char **optstr, int socket_id)
268884734b06SKan Liang {
268935a634f7SJiri Olsa 	if (!hists__has(browser->hists, socket) || socket_id < 0)
269084734b06SKan Liang 		return 0;
269184734b06SKan Liang 
269284734b06SKan Liang 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
269384734b06SKan Liang 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
269484734b06SKan Liang 		     socket_id) < 0)
269584734b06SKan Liang 		return 0;
269684734b06SKan Liang 
269784734b06SKan Liang 	act->socket = socket_id;
269884734b06SKan Liang 	act->fn = do_zoom_socket;
269984734b06SKan Liang 	return 1;
270084734b06SKan Liang }
270184734b06SKan Liang 
2702112f761fSNamhyung Kim static void hist_browser__update_nr_entries(struct hist_browser *hb)
2703064f1981SNamhyung Kim {
2704064f1981SNamhyung Kim 	u64 nr_entries = 0;
2705064f1981SNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2706064f1981SNamhyung Kim 
2707f5b763feSNamhyung Kim 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2708268397cbSNamhyung Kim 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2709268397cbSNamhyung Kim 		return;
2710268397cbSNamhyung Kim 	}
2711268397cbSNamhyung Kim 
271214135663SNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2713064f1981SNamhyung Kim 		nr_entries++;
2714f5b763feSNamhyung Kim 		nd = rb_hierarchy_next(nd);
2715064f1981SNamhyung Kim 	}
2716064f1981SNamhyung Kim 
2717112f761fSNamhyung Kim 	hb->nr_non_filtered_entries = nr_entries;
2718f5b763feSNamhyung Kim 	hb->nr_hierarchy_entries = nr_entries;
2719064f1981SNamhyung Kim }
2720341487abSFeng Tang 
2721b62e8dfcSNamhyung Kim static void hist_browser__update_percent_limit(struct hist_browser *hb,
2722b62e8dfcSNamhyung Kim 					       double percent)
2723b62e8dfcSNamhyung Kim {
2724b62e8dfcSNamhyung Kim 	struct hist_entry *he;
2725b62e8dfcSNamhyung Kim 	struct rb_node *nd = rb_first(&hb->hists->entries);
2726b62e8dfcSNamhyung Kim 	u64 total = hists__total_period(hb->hists);
2727b62e8dfcSNamhyung Kim 	u64 min_callchain_hits = total * (percent / 100);
2728b62e8dfcSNamhyung Kim 
2729b62e8dfcSNamhyung Kim 	hb->min_pcnt = callchain_param.min_percent = percent;
2730b62e8dfcSNamhyung Kim 
2731b62e8dfcSNamhyung Kim 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2732b62e8dfcSNamhyung Kim 		he = rb_entry(nd, struct hist_entry, rb_node);
2733b62e8dfcSNamhyung Kim 
273479dded87SNamhyung Kim 		if (he->has_no_entry) {
273579dded87SNamhyung Kim 			he->has_no_entry = false;
273679dded87SNamhyung Kim 			he->nr_rows = 0;
273779dded87SNamhyung Kim 		}
273879dded87SNamhyung Kim 
2739d0506edbSNamhyung Kim 		if (!he->leaf || !symbol_conf.use_callchain)
2740d0506edbSNamhyung Kim 			goto next;
2741d0506edbSNamhyung Kim 
2742b62e8dfcSNamhyung Kim 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2743b62e8dfcSNamhyung Kim 			total = he->stat.period;
2744b62e8dfcSNamhyung Kim 
2745b62e8dfcSNamhyung Kim 			if (symbol_conf.cumulate_callchain)
2746b62e8dfcSNamhyung Kim 				total = he->stat_acc->period;
2747b62e8dfcSNamhyung Kim 
2748b62e8dfcSNamhyung Kim 			min_callchain_hits = total * (percent / 100);
2749b62e8dfcSNamhyung Kim 		}
2750b62e8dfcSNamhyung Kim 
2751b62e8dfcSNamhyung Kim 		callchain_param.sort(&he->sorted_chain, he->callchain,
2752b62e8dfcSNamhyung Kim 				     min_callchain_hits, &callchain_param);
2753b62e8dfcSNamhyung Kim 
2754d0506edbSNamhyung Kim next:
2755201fde73SNamhyung Kim 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2756d0506edbSNamhyung Kim 
2757b62e8dfcSNamhyung Kim 		/* force to re-evaluate folding state of callchains */
2758b62e8dfcSNamhyung Kim 		he->init_have_children = false;
2759492b1010SNamhyung Kim 		hist_entry__set_folding(he, hb, false);
2760b62e8dfcSNamhyung Kim 	}
2761b62e8dfcSNamhyung Kim }
2762b62e8dfcSNamhyung Kim 
2763aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2764dd00d486SJiri Olsa 				    const char *helpline,
2765aca7a94dSNamhyung Kim 				    bool left_exits,
276668d80758SNamhyung Kim 				    struct hist_browser_timer *hbt,
2767064f1981SNamhyung Kim 				    float min_pcnt,
2768*06cc1a47SKan Liang 				    struct perf_env *env,
2769*06cc1a47SKan Liang 				    bool warn_lost_event)
2770aca7a94dSNamhyung Kim {
27714ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
2772a6ec894dSJiri Olsa 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2773aca7a94dSNamhyung Kim 	struct branch_info *bi;
2774f2b487dbSNamhyung Kim #define MAX_OPTIONS  16
2775f2b487dbSNamhyung Kim 	char *options[MAX_OPTIONS];
2776ea7cd592SNamhyung Kim 	struct popup_action actions[MAX_OPTIONS];
2777aca7a94dSNamhyung Kim 	int nr_options = 0;
2778aca7a94dSNamhyung Kim 	int key = -1;
2779aca7a94dSNamhyung Kim 	char buf[64];
27809783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
2781aca7a94dSNamhyung Kim 
2782e8e684a5SNamhyung Kim #define HIST_BROWSER_HELP_COMMON					\
2783e8e684a5SNamhyung Kim 	"h/?/F1        Show this window\n"				\
2784e8e684a5SNamhyung Kim 	"UP/DOWN/PGUP\n"						\
2785e8e684a5SNamhyung Kim 	"PGDN/SPACE    Navigate\n"					\
2786e8e684a5SNamhyung Kim 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2787e8e684a5SNamhyung Kim 	"For multiple event sessions:\n\n"				\
2788e8e684a5SNamhyung Kim 	"TAB/UNTAB     Switch events\n\n"				\
2789e8e684a5SNamhyung Kim 	"For symbolic views (--sort has sym):\n\n"			\
27907727a925SArnaldo Carvalho de Melo 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
27917727a925SArnaldo Carvalho de Melo 	"ESC           Zoom out\n"					\
2792e8e684a5SNamhyung Kim 	"a             Annotate current symbol\n"			\
2793e8e684a5SNamhyung Kim 	"C             Collapse all callchains\n"			\
2794e8e684a5SNamhyung Kim 	"d             Zoom into current DSO\n"				\
2795e8e684a5SNamhyung Kim 	"E             Expand all callchains\n"				\
2796105eb30fSNamhyung Kim 	"F             Toggle percentage of filtered entries\n"		\
2797025bf7eaSArnaldo Carvalho de Melo 	"H             Display column headers\n"			\
2798b62e8dfcSNamhyung Kim 	"L             Change percent limit\n"				\
279931eb4360SNamhyung Kim 	"m             Display context menu\n"				\
280084734b06SKan Liang 	"S             Zoom into current Processor Socket\n"		\
2801e8e684a5SNamhyung Kim 
2802e8e684a5SNamhyung Kim 	/* help messages are sorted by lexical order of the hotkey */
2803e8e684a5SNamhyung Kim 	const char report_help[] = HIST_BROWSER_HELP_COMMON
28046dd60135SNamhyung Kim 	"i             Show header information\n"
2805e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2806e8e684a5SNamhyung Kim 	"r             Run available scripts\n"
2807e8e684a5SNamhyung Kim 	"s             Switch to another data file in PWD\n"
2808e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2809e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
2810e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2811e8e684a5SNamhyung Kim 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2812e8e684a5SNamhyung Kim 	"P             Print histograms to perf.hist.N\n"
2813e8e684a5SNamhyung Kim 	"t             Zoom into current Thread\n"
2814e8e684a5SNamhyung Kim 	"V             Verbose (DSO names in callchains, etc)\n"
281542337a22SNamhyung Kim 	"z             Toggle zeroing of samples\n"
2816fbb7997eSArnaldo Carvalho de Melo 	"f             Enable/Disable events\n"
2817e8e684a5SNamhyung Kim 	"/             Filter symbol by name";
2818e8e684a5SNamhyung Kim 
2819aca7a94dSNamhyung Kim 	if (browser == NULL)
2820aca7a94dSNamhyung Kim 		return -1;
2821aca7a94dSNamhyung Kim 
2822ed426915SNamhyung Kim 	/* reset abort key so that it can get Ctrl-C as a key */
2823ed426915SNamhyung Kim 	SLang_reset_tty();
2824ed426915SNamhyung Kim 	SLang_init_tty(0, 0, 0);
2825ed426915SNamhyung Kim 
282603905048SNamhyung Kim 	if (min_pcnt)
2827064f1981SNamhyung Kim 		browser->min_pcnt = min_pcnt;
2828112f761fSNamhyung Kim 	hist_browser__update_nr_entries(browser);
2829064f1981SNamhyung Kim 
283084734b06SKan Liang 	browser->pstack = pstack__new(3);
283101f00a1cSNamhyung Kim 	if (browser->pstack == NULL)
2832aca7a94dSNamhyung Kim 		goto out;
2833aca7a94dSNamhyung Kim 
2834aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
2835aca7a94dSNamhyung Kim 
2836aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
2837ea7cd592SNamhyung Kim 	memset(actions, 0, sizeof(actions));
2838aca7a94dSNamhyung Kim 
28395b591669SNamhyung Kim 	if (symbol_conf.col_width_list_str)
28405b591669SNamhyung Kim 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
28415b591669SNamhyung Kim 
2842aca7a94dSNamhyung Kim 	while (1) {
2843f3b623b8SArnaldo Carvalho de Melo 		struct thread *thread = NULL;
2844045b80ddSArnaldo Carvalho de Melo 		struct map *map = NULL;
2845ea7cd592SNamhyung Kim 		int choice = 0;
284684734b06SKan Liang 		int socked_id = -1;
2847aca7a94dSNamhyung Kim 
2848aca7a94dSNamhyung Kim 		nr_options = 0;
2849aca7a94dSNamhyung Kim 
2850*06cc1a47SKan Liang 		key = hist_browser__run(browser, helpline,
2851*06cc1a47SKan Liang 					warn_lost_event);
2852aca7a94dSNamhyung Kim 
2853aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
2854aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
2855045b80ddSArnaldo Carvalho de Melo 			map = browser->selection->map;
285684734b06SKan Liang 			socked_id = browser->he_selection->socket;
2857aca7a94dSNamhyung Kim 		}
2858aca7a94dSNamhyung Kim 		switch (key) {
2859aca7a94dSNamhyung Kim 		case K_TAB:
2860aca7a94dSNamhyung Kim 		case K_UNTAB:
2861aca7a94dSNamhyung Kim 			if (nr_events == 1)
2862aca7a94dSNamhyung Kim 				continue;
2863aca7a94dSNamhyung Kim 			/*
2864aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
2865aca7a94dSNamhyung Kim 			 * go to the next or previous
2866aca7a94dSNamhyung Kim 			 */
2867aca7a94dSNamhyung Kim 			goto out_free_stack;
2868aca7a94dSNamhyung Kim 		case 'a':
28692e0453afSJiri Olsa 			if (!hists__has(hists, sym)) {
2870aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
2871aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
2872aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
2873aca7a94dSNamhyung Kim 				continue;
2874aca7a94dSNamhyung Kim 			}
2875aca7a94dSNamhyung Kim 
2876aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
2877aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
2878aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
2879aca7a94dSNamhyung Kim 				continue;
2880bc7cad42SNamhyung Kim 
2881ea7cd592SNamhyung Kim 			actions->ms.map = browser->selection->map;
2882ea7cd592SNamhyung Kim 			actions->ms.sym = browser->selection->sym;
2883ea7cd592SNamhyung Kim 			do_annotate(browser, actions);
2884bc7cad42SNamhyung Kim 			continue;
2885aff3f3f6SArnaldo Carvalho de Melo 		case 'P':
2886aff3f3f6SArnaldo Carvalho de Melo 			hist_browser__dump(browser);
2887aff3f3f6SArnaldo Carvalho de Melo 			continue;
2888aca7a94dSNamhyung Kim 		case 'd':
2889fae00650SArnaldo Carvalho de Melo 			actions->ms.map = map;
2890ea7cd592SNamhyung Kim 			do_zoom_dso(browser, actions);
2891bc7cad42SNamhyung Kim 			continue;
2892a7cb8863SArnaldo Carvalho de Melo 		case 'V':
289321e8c810SAlexis Berlemont 			verbose = (verbose + 1) % 4;
289421e8c810SAlexis Berlemont 			browser->show_dso = verbose > 0;
289521e8c810SAlexis Berlemont 			ui_helpline__fpush("Verbosity level set to %d\n",
289621e8c810SAlexis Berlemont 					   verbose);
2897a7cb8863SArnaldo Carvalho de Melo 			continue;
2898aca7a94dSNamhyung Kim 		case 't':
2899ea7cd592SNamhyung Kim 			actions->thread = thread;
2900ea7cd592SNamhyung Kim 			do_zoom_thread(browser, actions);
2901bc7cad42SNamhyung Kim 			continue;
290284734b06SKan Liang 		case 'S':
290384734b06SKan Liang 			actions->socket = socked_id;
290484734b06SKan Liang 			do_zoom_socket(browser, actions);
290584734b06SKan Liang 			continue;
29065a5626b1SArnaldo Carvalho de Melo 		case '/':
2907aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
29084aa8e454SArnaldo Carvalho de Melo 					"Please enter the name of symbol you want to see.\n"
29094aa8e454SArnaldo Carvalho de Melo 					"To remove the filter later, press / + ENTER.",
2910aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2911aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
291205e8b080SArnaldo Carvalho de Melo 				hists->symbol_filter_str = *buf ? buf : NULL;
291305e8b080SArnaldo Carvalho de Melo 				hists__filter_by_symbol(hists);
2914aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
2915aca7a94dSNamhyung Kim 			}
2916aca7a94dSNamhyung Kim 			continue;
2917cdbab7c2SFeng Tang 		case 'r':
2918ea7cd592SNamhyung Kim 			if (is_report_browser(hbt)) {
2919ea7cd592SNamhyung Kim 				actions->thread = NULL;
2920ea7cd592SNamhyung Kim 				actions->ms.sym = NULL;
2921ea7cd592SNamhyung Kim 				do_run_script(browser, actions);
2922ea7cd592SNamhyung Kim 			}
2923c77d8d70SFeng Tang 			continue;
2924341487abSFeng Tang 		case 's':
2925bc7cad42SNamhyung Kim 			if (is_report_browser(hbt)) {
2926ea7cd592SNamhyung Kim 				key = do_switch_data(browser, actions);
2927bc7cad42SNamhyung Kim 				if (key == K_SWITCH_INPUT_DATA)
2928bc7cad42SNamhyung Kim 					goto out_free_stack;
2929bc7cad42SNamhyung Kim 			}
2930341487abSFeng Tang 			continue;
29316dd60135SNamhyung Kim 		case 'i':
29326dd60135SNamhyung Kim 			/* env->arch is NULL for live-mode (i.e. perf top) */
29336dd60135SNamhyung Kim 			if (env->arch)
29346dd60135SNamhyung Kim 				tui__header_window(env);
29356dd60135SNamhyung Kim 			continue;
2936105eb30fSNamhyung Kim 		case 'F':
2937105eb30fSNamhyung Kim 			symbol_conf.filter_relative ^= 1;
2938105eb30fSNamhyung Kim 			continue;
293942337a22SNamhyung Kim 		case 'z':
294042337a22SNamhyung Kim 			if (!is_report_browser(hbt)) {
294142337a22SNamhyung Kim 				struct perf_top *top = hbt->arg;
294242337a22SNamhyung Kim 
294342337a22SNamhyung Kim 				top->zero = !top->zero;
294442337a22SNamhyung Kim 			}
294542337a22SNamhyung Kim 			continue;
2946b62e8dfcSNamhyung Kim 		case 'L':
2947b62e8dfcSNamhyung Kim 			if (ui_browser__input_window("Percent Limit",
2948b62e8dfcSNamhyung Kim 					"Please enter the value you want to hide entries under that percent.",
2949b62e8dfcSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
2950b62e8dfcSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
2951b62e8dfcSNamhyung Kim 				char *end;
2952b62e8dfcSNamhyung Kim 				double new_percent = strtod(buf, &end);
2953b62e8dfcSNamhyung Kim 
2954b62e8dfcSNamhyung Kim 				if (new_percent < 0 || new_percent > 100) {
2955b62e8dfcSNamhyung Kim 					ui_browser__warning(&browser->b, delay_secs * 2,
2956b62e8dfcSNamhyung Kim 						"Invalid percent: %.2f", new_percent);
2957b62e8dfcSNamhyung Kim 					continue;
2958b62e8dfcSNamhyung Kim 				}
2959b62e8dfcSNamhyung Kim 
2960b62e8dfcSNamhyung Kim 				hist_browser__update_percent_limit(browser, new_percent);
2961b62e8dfcSNamhyung Kim 				hist_browser__reset(browser);
2962b62e8dfcSNamhyung Kim 			}
2963b62e8dfcSNamhyung Kim 			continue;
2964aca7a94dSNamhyung Kim 		case K_F1:
2965aca7a94dSNamhyung Kim 		case 'h':
2966aca7a94dSNamhyung Kim 		case '?':
2967aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
2968e8e684a5SNamhyung Kim 				is_report_browser(hbt) ? report_help : top_help);
2969aca7a94dSNamhyung Kim 			continue;
2970aca7a94dSNamhyung Kim 		case K_ENTER:
2971aca7a94dSNamhyung Kim 		case K_RIGHT:
297231eb4360SNamhyung Kim 		case 'm':
2973aca7a94dSNamhyung Kim 			/* menu */
2974aca7a94dSNamhyung Kim 			break;
297563ab1749SArnaldo Carvalho de Melo 		case K_ESC:
2976aca7a94dSNamhyung Kim 		case K_LEFT: {
2977aca7a94dSNamhyung Kim 			const void *top;
2978aca7a94dSNamhyung Kim 
297901f00a1cSNamhyung Kim 			if (pstack__empty(browser->pstack)) {
2980aca7a94dSNamhyung Kim 				/*
2981aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
2982aca7a94dSNamhyung Kim 				 */
2983aca7a94dSNamhyung Kim 				if (left_exits)
2984aca7a94dSNamhyung Kim 					goto out_free_stack;
298563ab1749SArnaldo Carvalho de Melo 
298663ab1749SArnaldo Carvalho de Melo 				if (key == K_ESC &&
298763ab1749SArnaldo Carvalho de Melo 				    ui_browser__dialog_yesno(&browser->b,
298863ab1749SArnaldo Carvalho de Melo 							     "Do you really want to exit?"))
298963ab1749SArnaldo Carvalho de Melo 					goto out_free_stack;
299063ab1749SArnaldo Carvalho de Melo 
2991aca7a94dSNamhyung Kim 				continue;
2992aca7a94dSNamhyung Kim 			}
29936422184bSNamhyung Kim 			top = pstack__peek(browser->pstack);
2994bc7cad42SNamhyung Kim 			if (top == &browser->hists->dso_filter) {
29956422184bSNamhyung Kim 				/*
29966422184bSNamhyung Kim 				 * No need to set actions->dso here since
29976422184bSNamhyung Kim 				 * it's just to remove the current filter.
29986422184bSNamhyung Kim 				 * Ditto for thread below.
29996422184bSNamhyung Kim 				 */
30006422184bSNamhyung Kim 				do_zoom_dso(browser, actions);
300184734b06SKan Liang 			} else if (top == &browser->hists->thread_filter) {
30026422184bSNamhyung Kim 				do_zoom_thread(browser, actions);
300384734b06SKan Liang 			} else if (top == &browser->hists->socket_filter) {
300484734b06SKan Liang 				do_zoom_socket(browser, actions);
300584734b06SKan Liang 			}
3006aca7a94dSNamhyung Kim 			continue;
3007aca7a94dSNamhyung Kim 		}
3008aca7a94dSNamhyung Kim 		case 'q':
3009aca7a94dSNamhyung Kim 		case CTRL('c'):
3010516e5368SArnaldo Carvalho de Melo 			goto out_free_stack;
3011fbb7997eSArnaldo Carvalho de Melo 		case 'f':
301213d1e536SNamhyung Kim 			if (!is_report_browser(hbt)) {
301313d1e536SNamhyung Kim 				struct perf_top *top = hbt->arg;
301413d1e536SNamhyung Kim 
301513d1e536SNamhyung Kim 				perf_evlist__toggle_enable(top->evlist);
301613d1e536SNamhyung Kim 				/*
301713d1e536SNamhyung Kim 				 * No need to refresh, resort/decay histogram
301813d1e536SNamhyung Kim 				 * entries if we are not collecting samples:
301913d1e536SNamhyung Kim 				 */
302013d1e536SNamhyung Kim 				if (top->evlist->enabled) {
302113d1e536SNamhyung Kim 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
302213d1e536SNamhyung Kim 					hbt->refresh = delay_secs;
302313d1e536SNamhyung Kim 				} else {
302413d1e536SNamhyung Kim 					helpline = "Press 'f' again to re-enable the events";
302513d1e536SNamhyung Kim 					hbt->refresh = 0;
302613d1e536SNamhyung Kim 				}
302713d1e536SNamhyung Kim 				continue;
302813d1e536SNamhyung Kim 			}
30293e323dc0SArnaldo Carvalho de Melo 			/* Fall thru */
3030aca7a94dSNamhyung Kim 		default:
30313e323dc0SArnaldo Carvalho de Melo 			helpline = "Press '?' for help on key bindings";
3032aca7a94dSNamhyung Kim 			continue;
3033aca7a94dSNamhyung Kim 		}
3034aca7a94dSNamhyung Kim 
30352e0453afSJiri Olsa 		if (!hists__has(hists, sym) || browser->selection == NULL)
30360ba332f7SArnaldo Carvalho de Melo 			goto skip_annotation;
30370ba332f7SArnaldo Carvalho de Melo 
303855369fc1SNamhyung Kim 		if (sort__mode == SORT_MODE__BRANCH) {
3039aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
30400ba332f7SArnaldo Carvalho de Melo 
30410ba332f7SArnaldo Carvalho de Melo 			if (bi == NULL)
30420ba332f7SArnaldo Carvalho de Melo 				goto skip_annotation;
30430ba332f7SArnaldo Carvalho de Melo 
3044ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3045ea7cd592SNamhyung Kim 						       &actions[nr_options],
3046ea7cd592SNamhyung Kim 						       &options[nr_options],
3047ea7cd592SNamhyung Kim 						       bi->from.map,
3048ea7cd592SNamhyung Kim 						       bi->from.sym);
3049ea7cd592SNamhyung Kim 			if (bi->to.sym != bi->from.sym)
3050ea7cd592SNamhyung Kim 				nr_options += add_annotate_opt(browser,
3051ea7cd592SNamhyung Kim 							&actions[nr_options],
3052ea7cd592SNamhyung Kim 							&options[nr_options],
3053ea7cd592SNamhyung Kim 							bi->to.map,
3054ea7cd592SNamhyung Kim 							bi->to.sym);
3055aca7a94dSNamhyung Kim 		} else {
3056ea7cd592SNamhyung Kim 			nr_options += add_annotate_opt(browser,
3057ea7cd592SNamhyung Kim 						       &actions[nr_options],
3058ea7cd592SNamhyung Kim 						       &options[nr_options],
3059ea7cd592SNamhyung Kim 						       browser->selection->map,
3060ea7cd592SNamhyung Kim 						       browser->selection->sym);
3061446fb96cSArnaldo Carvalho de Melo 		}
30620ba332f7SArnaldo Carvalho de Melo skip_annotation:
3063ea7cd592SNamhyung Kim 		nr_options += add_thread_opt(browser, &actions[nr_options],
3064ea7cd592SNamhyung Kim 					     &options[nr_options], thread);
3065ea7cd592SNamhyung Kim 		nr_options += add_dso_opt(browser, &actions[nr_options],
3066045b80ddSArnaldo Carvalho de Melo 					  &options[nr_options], map);
3067ea7cd592SNamhyung Kim 		nr_options += add_map_opt(browser, &actions[nr_options],
3068ea7cd592SNamhyung Kim 					  &options[nr_options],
3069bd315aabSWang Nan 					  browser->selection ?
3070bd315aabSWang Nan 						browser->selection->map : NULL);
307184734b06SKan Liang 		nr_options += add_socket_opt(browser, &actions[nr_options],
307284734b06SKan Liang 					     &options[nr_options],
307384734b06SKan Liang 					     socked_id);
3074cdbab7c2SFeng Tang 		/* perf script support */
3075b1baae89SNamhyung Kim 		if (!is_report_browser(hbt))
3076b1baae89SNamhyung Kim 			goto skip_scripting;
3077b1baae89SNamhyung Kim 
3078cdbab7c2SFeng Tang 		if (browser->he_selection) {
3079fa82911aSJiri Olsa 			if (hists__has(hists, thread) && thread) {
3080ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3081ea7cd592SNamhyung Kim 							     &actions[nr_options],
3082ea7cd592SNamhyung Kim 							     &options[nr_options],
3083ea7cd592SNamhyung Kim 							     thread, NULL);
30842eafd410SNamhyung Kim 			}
3085bd315aabSWang Nan 			/*
3086bd315aabSWang Nan 			 * Note that browser->selection != NULL
3087bd315aabSWang Nan 			 * when browser->he_selection is not NULL,
3088bd315aabSWang Nan 			 * so we don't need to check browser->selection
3089bd315aabSWang Nan 			 * before fetching browser->selection->sym like what
3090bd315aabSWang Nan 			 * we do before fetching browser->selection->map.
3091bd315aabSWang Nan 			 *
3092bd315aabSWang Nan 			 * See hist_browser__show_entry.
3093bd315aabSWang Nan 			 */
30942e0453afSJiri Olsa 			if (hists__has(hists, sym) && browser->selection->sym) {
3095ea7cd592SNamhyung Kim 				nr_options += add_script_opt(browser,
3096ea7cd592SNamhyung Kim 							     &actions[nr_options],
3097ea7cd592SNamhyung Kim 							     &options[nr_options],
3098ea7cd592SNamhyung Kim 							     NULL, browser->selection->sym);
3099cdbab7c2SFeng Tang 			}
3100c221acb0SNamhyung Kim 		}
3101ea7cd592SNamhyung Kim 		nr_options += add_script_opt(browser, &actions[nr_options],
3102ea7cd592SNamhyung Kim 					     &options[nr_options], NULL, NULL);
3103ea7cd592SNamhyung Kim 		nr_options += add_switch_opt(browser, &actions[nr_options],
3104ea7cd592SNamhyung Kim 					     &options[nr_options]);
3105b1baae89SNamhyung Kim skip_scripting:
3106ea7cd592SNamhyung Kim 		nr_options += add_exit_opt(browser, &actions[nr_options],
3107ea7cd592SNamhyung Kim 					   &options[nr_options]);
3108aca7a94dSNamhyung Kim 
3109ea7cd592SNamhyung Kim 		do {
3110ea7cd592SNamhyung Kim 			struct popup_action *act;
3111ea7cd592SNamhyung Kim 
3112ea7cd592SNamhyung Kim 			choice = ui__popup_menu(nr_options, options);
3113ea7cd592SNamhyung Kim 			if (choice == -1 || choice >= nr_options)
3114aca7a94dSNamhyung Kim 				break;
3115aca7a94dSNamhyung Kim 
3116ea7cd592SNamhyung Kim 			act = &actions[choice];
3117ea7cd592SNamhyung Kim 			key = act->fn(browser, act);
3118ea7cd592SNamhyung Kim 		} while (key == 1);
3119aca7a94dSNamhyung Kim 
3120bc7cad42SNamhyung Kim 		if (key == K_SWITCH_INPUT_DATA)
3121341487abSFeng Tang 			break;
3122341487abSFeng Tang 	}
3123aca7a94dSNamhyung Kim out_free_stack:
312401f00a1cSNamhyung Kim 	pstack__delete(browser->pstack);
3125aca7a94dSNamhyung Kim out:
3126aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
3127f2b487dbSNamhyung Kim 	free_popup_options(options, MAX_OPTIONS);
3128aca7a94dSNamhyung Kim 	return key;
3129aca7a94dSNamhyung Kim }
3130aca7a94dSNamhyung Kim 
3131aca7a94dSNamhyung Kim struct perf_evsel_menu {
3132aca7a94dSNamhyung Kim 	struct ui_browser b;
3133aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
3134aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
3135064f1981SNamhyung Kim 	float min_pcnt;
3136ce80d3beSKan Liang 	struct perf_env *env;
3137aca7a94dSNamhyung Kim };
3138aca7a94dSNamhyung Kim 
3139aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
3140aca7a94dSNamhyung Kim 				   void *entry, int row)
3141aca7a94dSNamhyung Kim {
3142aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
3143aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
3144aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
31454ea062edSArnaldo Carvalho de Melo 	struct hists *hists = evsel__hists(evsel);
3146aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
31474ea062edSArnaldo Carvalho de Melo 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
31487289f83cSArnaldo Carvalho de Melo 	const char *ev_name = perf_evsel__name(evsel);
3149aca7a94dSNamhyung Kim 	char bf[256], unit;
3150aca7a94dSNamhyung Kim 	const char *warn = " ";
3151aca7a94dSNamhyung Kim 	size_t printed;
3152aca7a94dSNamhyung Kim 
3153aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3154aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
3155aca7a94dSNamhyung Kim 
3156759ff497SNamhyung Kim 	if (perf_evsel__is_group_event(evsel)) {
3157717e263fSNamhyung Kim 		struct perf_evsel *pos;
3158717e263fSNamhyung Kim 
3159717e263fSNamhyung Kim 		ev_name = perf_evsel__group_name(evsel);
3160717e263fSNamhyung Kim 
3161717e263fSNamhyung Kim 		for_each_group_member(pos, evsel) {
31624ea062edSArnaldo Carvalho de Melo 			struct hists *pos_hists = evsel__hists(pos);
31634ea062edSArnaldo Carvalho de Melo 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3164717e263fSNamhyung Kim 		}
3165717e263fSNamhyung Kim 	}
3166717e263fSNamhyung Kim 
3167aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
3168aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3169aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
3170517dfdb3SArnaldo Carvalho de Melo 	ui_browser__printf(browser, "%s", bf);
3171aca7a94dSNamhyung Kim 
31724ea062edSArnaldo Carvalho de Melo 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3173aca7a94dSNamhyung Kim 	if (nr_events != 0) {
3174aca7a94dSNamhyung Kim 		menu->lost_events = true;
3175aca7a94dSNamhyung Kim 		if (!current_entry)
3176aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3177aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
3178aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3179aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
3180aca7a94dSNamhyung Kim 		warn = bf;
3181aca7a94dSNamhyung Kim 	}
3182aca7a94dSNamhyung Kim 
318326270a00SArnaldo Carvalho de Melo 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3184aca7a94dSNamhyung Kim 
3185aca7a94dSNamhyung Kim 	if (current_entry)
3186aca7a94dSNamhyung Kim 		menu->selection = evsel;
3187aca7a94dSNamhyung Kim }
3188aca7a94dSNamhyung Kim 
3189aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3190aca7a94dSNamhyung Kim 				int nr_events, const char *help,
3191*06cc1a47SKan Liang 				struct hist_browser_timer *hbt,
3192*06cc1a47SKan Liang 				bool warn_lost_event)
3193aca7a94dSNamhyung Kim {
3194aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
3195aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3196dd00d486SJiri Olsa 	const char *title = "Available samples";
31979783adf7SNamhyung Kim 	int delay_secs = hbt ? hbt->refresh : 0;
3198aca7a94dSNamhyung Kim 	int key;
3199aca7a94dSNamhyung Kim 
3200aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
3201aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3202aca7a94dSNamhyung Kim 		return -1;
3203aca7a94dSNamhyung Kim 
3204aca7a94dSNamhyung Kim 	while (1) {
3205aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
3206aca7a94dSNamhyung Kim 
3207aca7a94dSNamhyung Kim 		switch (key) {
3208aca7a94dSNamhyung Kim 		case K_TIMER:
32099783adf7SNamhyung Kim 			hbt->timer(hbt->arg);
3210aca7a94dSNamhyung Kim 
3211*06cc1a47SKan Liang 			if (!menu->lost_events_warned &&
3212*06cc1a47SKan Liang 			    menu->lost_events &&
3213*06cc1a47SKan Liang 			    warn_lost_event) {
3214aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
3215aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
3216aca7a94dSNamhyung Kim 			}
3217aca7a94dSNamhyung Kim 			continue;
3218aca7a94dSNamhyung Kim 		case K_RIGHT:
3219aca7a94dSNamhyung Kim 		case K_ENTER:
3220aca7a94dSNamhyung Kim 			if (!menu->selection)
3221aca7a94dSNamhyung Kim 				continue;
3222aca7a94dSNamhyung Kim 			pos = menu->selection;
3223aca7a94dSNamhyung Kim browse_hists:
3224aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
3225aca7a94dSNamhyung Kim 			/*
3226aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
3227aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
3228aca7a94dSNamhyung Kim 			 */
32299783adf7SNamhyung Kim 			if (hbt)
32309783adf7SNamhyung Kim 				hbt->timer(hbt->arg);
3231aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
3232dd00d486SJiri Olsa 						       true, hbt,
3233064f1981SNamhyung Kim 						       menu->min_pcnt,
3234*06cc1a47SKan Liang 						       menu->env,
3235*06cc1a47SKan Liang 						       warn_lost_event);
3236aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
3237aca7a94dSNamhyung Kim 			switch (key) {
3238aca7a94dSNamhyung Kim 			case K_TAB:
3239aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
32409a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__first(evlist);
3241aca7a94dSNamhyung Kim 				else
32429a354cdcSArnaldo Carvalho de Melo 					pos = perf_evsel__next(pos);
3243aca7a94dSNamhyung Kim 				goto browse_hists;
3244aca7a94dSNamhyung Kim 			case K_UNTAB:
3245aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
32469a354cdcSArnaldo Carvalho de Melo 					pos = perf_evlist__last(evlist);
3247aca7a94dSNamhyung Kim 				else
3248d87fcb4aSArnaldo Carvalho de Melo 					pos = perf_evsel__prev(pos);
3249aca7a94dSNamhyung Kim 				goto browse_hists;
3250341487abSFeng Tang 			case K_SWITCH_INPUT_DATA:
3251aca7a94dSNamhyung Kim 			case 'q':
3252aca7a94dSNamhyung Kim 			case CTRL('c'):
3253aca7a94dSNamhyung Kim 				goto out;
325463ab1749SArnaldo Carvalho de Melo 			case K_ESC:
3255aca7a94dSNamhyung Kim 			default:
3256aca7a94dSNamhyung Kim 				continue;
3257aca7a94dSNamhyung Kim 			}
3258aca7a94dSNamhyung Kim 		case K_LEFT:
3259aca7a94dSNamhyung Kim 			continue;
3260aca7a94dSNamhyung Kim 		case K_ESC:
3261aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
3262aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
3263aca7a94dSNamhyung Kim 				continue;
3264aca7a94dSNamhyung Kim 			/* Fall thru */
3265aca7a94dSNamhyung Kim 		case 'q':
3266aca7a94dSNamhyung Kim 		case CTRL('c'):
3267aca7a94dSNamhyung Kim 			goto out;
3268aca7a94dSNamhyung Kim 		default:
3269aca7a94dSNamhyung Kim 			continue;
3270aca7a94dSNamhyung Kim 		}
3271aca7a94dSNamhyung Kim 	}
3272aca7a94dSNamhyung Kim 
3273aca7a94dSNamhyung Kim out:
3274aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
3275aca7a94dSNamhyung Kim 	return key;
3276aca7a94dSNamhyung Kim }
3277aca7a94dSNamhyung Kim 
3278316c7136SArnaldo Carvalho de Melo static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3279fc24d7c2SNamhyung Kim 				 void *entry)
3280fc24d7c2SNamhyung Kim {
3281fc24d7c2SNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3282fc24d7c2SNamhyung Kim 
3283fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3284fc24d7c2SNamhyung Kim 		return true;
3285fc24d7c2SNamhyung Kim 
3286fc24d7c2SNamhyung Kim 	return false;
3287fc24d7c2SNamhyung Kim }
3288fc24d7c2SNamhyung Kim 
3289aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3290fc24d7c2SNamhyung Kim 					   int nr_entries, const char *help,
329168d80758SNamhyung Kim 					   struct hist_browser_timer *hbt,
3292064f1981SNamhyung Kim 					   float min_pcnt,
3293*06cc1a47SKan Liang 					   struct perf_env *env,
3294*06cc1a47SKan Liang 					   bool warn_lost_event)
3295aca7a94dSNamhyung Kim {
3296aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
3297aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
3298aca7a94dSNamhyung Kim 		.b = {
3299aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
3300aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
3301aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
3302aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
3303fc24d7c2SNamhyung Kim 			.filter	    = filter_group_entries,
3304fc24d7c2SNamhyung Kim 			.nr_entries = nr_entries,
3305aca7a94dSNamhyung Kim 			.priv	    = evlist,
3306aca7a94dSNamhyung Kim 		},
3307064f1981SNamhyung Kim 		.min_pcnt = min_pcnt,
330868d80758SNamhyung Kim 		.env = env,
3309aca7a94dSNamhyung Kim 	};
3310aca7a94dSNamhyung Kim 
3311aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
3312aca7a94dSNamhyung Kim 
3313e5cadb93SArnaldo Carvalho de Melo 	evlist__for_each_entry(evlist, pos) {
33147289f83cSArnaldo Carvalho de Melo 		const char *ev_name = perf_evsel__name(pos);
3315aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
3316aca7a94dSNamhyung Kim 
3317aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
3318aca7a94dSNamhyung Kim 			menu.b.width = line_len;
3319aca7a94dSNamhyung Kim 	}
3320aca7a94dSNamhyung Kim 
3321*06cc1a47SKan Liang 	return perf_evsel_menu__run(&menu, nr_entries, help,
3322*06cc1a47SKan Liang 				    hbt, warn_lost_event);
3323aca7a94dSNamhyung Kim }
3324aca7a94dSNamhyung Kim 
3325aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
332668d80758SNamhyung Kim 				  struct hist_browser_timer *hbt,
3327064f1981SNamhyung Kim 				  float min_pcnt,
3328*06cc1a47SKan Liang 				  struct perf_env *env,
3329*06cc1a47SKan Liang 				  bool warn_lost_event)
3330aca7a94dSNamhyung Kim {
3331fc24d7c2SNamhyung Kim 	int nr_entries = evlist->nr_entries;
3332fc24d7c2SNamhyung Kim 
3333fc24d7c2SNamhyung Kim single_entry:
3334fc24d7c2SNamhyung Kim 	if (nr_entries == 1) {
33359a354cdcSArnaldo Carvalho de Melo 		struct perf_evsel *first = perf_evlist__first(evlist);
3336fc24d7c2SNamhyung Kim 
3337fc24d7c2SNamhyung Kim 		return perf_evsel__hists_browse(first, nr_entries, help,
3338dd00d486SJiri Olsa 						false, hbt, min_pcnt,
3339*06cc1a47SKan Liang 						env, warn_lost_event);
3340aca7a94dSNamhyung Kim 	}
3341aca7a94dSNamhyung Kim 
3342fc24d7c2SNamhyung Kim 	if (symbol_conf.event_group) {
3343fc24d7c2SNamhyung Kim 		struct perf_evsel *pos;
3344fc24d7c2SNamhyung Kim 
3345fc24d7c2SNamhyung Kim 		nr_entries = 0;
3346e5cadb93SArnaldo Carvalho de Melo 		evlist__for_each_entry(evlist, pos) {
3347fc24d7c2SNamhyung Kim 			if (perf_evsel__is_group_leader(pos))
3348fc24d7c2SNamhyung Kim 				nr_entries++;
33490050f7aaSArnaldo Carvalho de Melo 		}
3350fc24d7c2SNamhyung Kim 
3351fc24d7c2SNamhyung Kim 		if (nr_entries == 1)
3352fc24d7c2SNamhyung Kim 			goto single_entry;
3353fc24d7c2SNamhyung Kim 	}
3354fc24d7c2SNamhyung Kim 
3355fc24d7c2SNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3356*06cc1a47SKan Liang 					       hbt, min_pcnt, env,
3357*06cc1a47SKan Liang 					       warn_lost_event);
3358aca7a94dSNamhyung Kim }
3359