xref: /linux/tools/perf/ui/browsers/hists.c (revision aca7a94d6a59d6bf2600768e752f971c6cc0ab57)
1*aca7a94dSNamhyung Kim #include <stdio.h>
2*aca7a94dSNamhyung Kim #include "../libslang.h"
3*aca7a94dSNamhyung Kim #include <stdlib.h>
4*aca7a94dSNamhyung Kim #include <string.h>
5*aca7a94dSNamhyung Kim #include <newt.h>
6*aca7a94dSNamhyung Kim #include <linux/rbtree.h>
7*aca7a94dSNamhyung Kim 
8*aca7a94dSNamhyung Kim #include "../../util/evsel.h"
9*aca7a94dSNamhyung Kim #include "../../util/evlist.h"
10*aca7a94dSNamhyung Kim #include "../../util/hist.h"
11*aca7a94dSNamhyung Kim #include "../../util/pstack.h"
12*aca7a94dSNamhyung Kim #include "../../util/sort.h"
13*aca7a94dSNamhyung Kim #include "../../util/util.h"
14*aca7a94dSNamhyung Kim 
15*aca7a94dSNamhyung Kim #include "../browser.h"
16*aca7a94dSNamhyung Kim #include "../helpline.h"
17*aca7a94dSNamhyung Kim #include "../util.h"
18*aca7a94dSNamhyung Kim #include "../ui.h"
19*aca7a94dSNamhyung Kim #include "map.h"
20*aca7a94dSNamhyung Kim 
21*aca7a94dSNamhyung Kim struct hist_browser {
22*aca7a94dSNamhyung Kim 	struct ui_browser   b;
23*aca7a94dSNamhyung Kim 	struct hists	    *hists;
24*aca7a94dSNamhyung Kim 	struct hist_entry   *he_selection;
25*aca7a94dSNamhyung Kim 	struct map_symbol   *selection;
26*aca7a94dSNamhyung Kim 	bool		     has_symbols;
27*aca7a94dSNamhyung Kim };
28*aca7a94dSNamhyung Kim 
29*aca7a94dSNamhyung Kim static int hists__browser_title(struct hists *self, char *bf, size_t size,
30*aca7a94dSNamhyung Kim 				const char *ev_name);
31*aca7a94dSNamhyung Kim 
32*aca7a94dSNamhyung Kim static void hist_browser__refresh_dimensions(struct hist_browser *self)
33*aca7a94dSNamhyung Kim {
34*aca7a94dSNamhyung Kim 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
35*aca7a94dSNamhyung Kim 	self->b.width = 3 + (hists__sort_list_width(self->hists) +
36*aca7a94dSNamhyung Kim 			     sizeof("[k]"));
37*aca7a94dSNamhyung Kim }
38*aca7a94dSNamhyung Kim 
39*aca7a94dSNamhyung Kim static void hist_browser__reset(struct hist_browser *self)
40*aca7a94dSNamhyung Kim {
41*aca7a94dSNamhyung Kim 	self->b.nr_entries = self->hists->nr_entries;
42*aca7a94dSNamhyung Kim 	hist_browser__refresh_dimensions(self);
43*aca7a94dSNamhyung Kim 	ui_browser__reset_index(&self->b);
44*aca7a94dSNamhyung Kim }
45*aca7a94dSNamhyung Kim 
46*aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded)
47*aca7a94dSNamhyung Kim {
48*aca7a94dSNamhyung Kim 	return unfolded ? '-' : '+';
49*aca7a94dSNamhyung Kim }
50*aca7a94dSNamhyung Kim 
51*aca7a94dSNamhyung Kim static char map_symbol__folded(const struct map_symbol *self)
52*aca7a94dSNamhyung Kim {
53*aca7a94dSNamhyung Kim 	return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
54*aca7a94dSNamhyung Kim }
55*aca7a94dSNamhyung Kim 
56*aca7a94dSNamhyung Kim static char hist_entry__folded(const struct hist_entry *self)
57*aca7a94dSNamhyung Kim {
58*aca7a94dSNamhyung Kim 	return map_symbol__folded(&self->ms);
59*aca7a94dSNamhyung Kim }
60*aca7a94dSNamhyung Kim 
61*aca7a94dSNamhyung Kim static char callchain_list__folded(const struct callchain_list *self)
62*aca7a94dSNamhyung Kim {
63*aca7a94dSNamhyung Kim 	return map_symbol__folded(&self->ms);
64*aca7a94dSNamhyung Kim }
65*aca7a94dSNamhyung Kim 
66*aca7a94dSNamhyung Kim static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
67*aca7a94dSNamhyung Kim {
68*aca7a94dSNamhyung Kim 	self->unfolded = unfold ? self->has_children : false;
69*aca7a94dSNamhyung Kim }
70*aca7a94dSNamhyung Kim 
71*aca7a94dSNamhyung Kim static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
72*aca7a94dSNamhyung Kim {
73*aca7a94dSNamhyung Kim 	int n = 0;
74*aca7a94dSNamhyung Kim 	struct rb_node *nd;
75*aca7a94dSNamhyung Kim 
76*aca7a94dSNamhyung Kim 	for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
77*aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
78*aca7a94dSNamhyung Kim 		struct callchain_list *chain;
79*aca7a94dSNamhyung Kim 		char folded_sign = ' '; /* No children */
80*aca7a94dSNamhyung Kim 
81*aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
82*aca7a94dSNamhyung Kim 			++n;
83*aca7a94dSNamhyung Kim 			/* We need this because we may not have children */
84*aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
85*aca7a94dSNamhyung Kim 			if (folded_sign == '+')
86*aca7a94dSNamhyung Kim 				break;
87*aca7a94dSNamhyung Kim 		}
88*aca7a94dSNamhyung Kim 
89*aca7a94dSNamhyung Kim 		if (folded_sign == '-') /* Have children and they're unfolded */
90*aca7a94dSNamhyung Kim 			n += callchain_node__count_rows_rb_tree(child);
91*aca7a94dSNamhyung Kim 	}
92*aca7a94dSNamhyung Kim 
93*aca7a94dSNamhyung Kim 	return n;
94*aca7a94dSNamhyung Kim }
95*aca7a94dSNamhyung Kim 
96*aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node)
97*aca7a94dSNamhyung Kim {
98*aca7a94dSNamhyung Kim 	struct callchain_list *chain;
99*aca7a94dSNamhyung Kim 	bool unfolded = false;
100*aca7a94dSNamhyung Kim 	int n = 0;
101*aca7a94dSNamhyung Kim 
102*aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
103*aca7a94dSNamhyung Kim 		++n;
104*aca7a94dSNamhyung Kim 		unfolded = chain->ms.unfolded;
105*aca7a94dSNamhyung Kim 	}
106*aca7a94dSNamhyung Kim 
107*aca7a94dSNamhyung Kim 	if (unfolded)
108*aca7a94dSNamhyung Kim 		n += callchain_node__count_rows_rb_tree(node);
109*aca7a94dSNamhyung Kim 
110*aca7a94dSNamhyung Kim 	return n;
111*aca7a94dSNamhyung Kim }
112*aca7a94dSNamhyung Kim 
113*aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain)
114*aca7a94dSNamhyung Kim {
115*aca7a94dSNamhyung Kim 	struct rb_node *nd;
116*aca7a94dSNamhyung Kim 	int n = 0;
117*aca7a94dSNamhyung Kim 
118*aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
119*aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
120*aca7a94dSNamhyung Kim 		n += callchain_node__count_rows(node);
121*aca7a94dSNamhyung Kim 	}
122*aca7a94dSNamhyung Kim 
123*aca7a94dSNamhyung Kim 	return n;
124*aca7a94dSNamhyung Kim }
125*aca7a94dSNamhyung Kim 
126*aca7a94dSNamhyung Kim static bool map_symbol__toggle_fold(struct map_symbol *self)
127*aca7a94dSNamhyung Kim {
128*aca7a94dSNamhyung Kim 	if (!self)
129*aca7a94dSNamhyung Kim 		return false;
130*aca7a94dSNamhyung Kim 
131*aca7a94dSNamhyung Kim 	if (!self->has_children)
132*aca7a94dSNamhyung Kim 		return false;
133*aca7a94dSNamhyung Kim 
134*aca7a94dSNamhyung Kim 	self->unfolded = !self->unfolded;
135*aca7a94dSNamhyung Kim 	return true;
136*aca7a94dSNamhyung Kim }
137*aca7a94dSNamhyung Kim 
138*aca7a94dSNamhyung Kim static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
139*aca7a94dSNamhyung Kim {
140*aca7a94dSNamhyung Kim 	struct rb_node *nd = rb_first(&self->rb_root);
141*aca7a94dSNamhyung Kim 
142*aca7a94dSNamhyung Kim 	for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
143*aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
144*aca7a94dSNamhyung Kim 		struct callchain_list *chain;
145*aca7a94dSNamhyung Kim 		bool first = true;
146*aca7a94dSNamhyung Kim 
147*aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
148*aca7a94dSNamhyung Kim 			if (first) {
149*aca7a94dSNamhyung Kim 				first = false;
150*aca7a94dSNamhyung Kim 				chain->ms.has_children = chain->list.next != &child->val ||
151*aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
152*aca7a94dSNamhyung Kim 			} else
153*aca7a94dSNamhyung Kim 				chain->ms.has_children = chain->list.next == &child->val &&
154*aca7a94dSNamhyung Kim 							 !RB_EMPTY_ROOT(&child->rb_root);
155*aca7a94dSNamhyung Kim 		}
156*aca7a94dSNamhyung Kim 
157*aca7a94dSNamhyung Kim 		callchain_node__init_have_children_rb_tree(child);
158*aca7a94dSNamhyung Kim 	}
159*aca7a94dSNamhyung Kim }
160*aca7a94dSNamhyung Kim 
161*aca7a94dSNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *self)
162*aca7a94dSNamhyung Kim {
163*aca7a94dSNamhyung Kim 	struct callchain_list *chain;
164*aca7a94dSNamhyung Kim 
165*aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &self->val, list)
166*aca7a94dSNamhyung Kim 		chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
167*aca7a94dSNamhyung Kim 
168*aca7a94dSNamhyung Kim 	callchain_node__init_have_children_rb_tree(self);
169*aca7a94dSNamhyung Kim }
170*aca7a94dSNamhyung Kim 
171*aca7a94dSNamhyung Kim static void callchain__init_have_children(struct rb_root *self)
172*aca7a94dSNamhyung Kim {
173*aca7a94dSNamhyung Kim 	struct rb_node *nd;
174*aca7a94dSNamhyung Kim 
175*aca7a94dSNamhyung Kim 	for (nd = rb_first(self); nd; nd = rb_next(nd)) {
176*aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
177*aca7a94dSNamhyung Kim 		callchain_node__init_have_children(node);
178*aca7a94dSNamhyung Kim 	}
179*aca7a94dSNamhyung Kim }
180*aca7a94dSNamhyung Kim 
181*aca7a94dSNamhyung Kim static void hist_entry__init_have_children(struct hist_entry *self)
182*aca7a94dSNamhyung Kim {
183*aca7a94dSNamhyung Kim 	if (!self->init_have_children) {
184*aca7a94dSNamhyung Kim 		self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
185*aca7a94dSNamhyung Kim 		callchain__init_have_children(&self->sorted_chain);
186*aca7a94dSNamhyung Kim 		self->init_have_children = true;
187*aca7a94dSNamhyung Kim 	}
188*aca7a94dSNamhyung Kim }
189*aca7a94dSNamhyung Kim 
190*aca7a94dSNamhyung Kim static bool hist_browser__toggle_fold(struct hist_browser *self)
191*aca7a94dSNamhyung Kim {
192*aca7a94dSNamhyung Kim 	if (map_symbol__toggle_fold(self->selection)) {
193*aca7a94dSNamhyung Kim 		struct hist_entry *he = self->he_selection;
194*aca7a94dSNamhyung Kim 
195*aca7a94dSNamhyung Kim 		hist_entry__init_have_children(he);
196*aca7a94dSNamhyung Kim 		self->hists->nr_entries -= he->nr_rows;
197*aca7a94dSNamhyung Kim 
198*aca7a94dSNamhyung Kim 		if (he->ms.unfolded)
199*aca7a94dSNamhyung Kim 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
200*aca7a94dSNamhyung Kim 		else
201*aca7a94dSNamhyung Kim 			he->nr_rows = 0;
202*aca7a94dSNamhyung Kim 		self->hists->nr_entries += he->nr_rows;
203*aca7a94dSNamhyung Kim 		self->b.nr_entries = self->hists->nr_entries;
204*aca7a94dSNamhyung Kim 
205*aca7a94dSNamhyung Kim 		return true;
206*aca7a94dSNamhyung Kim 	}
207*aca7a94dSNamhyung Kim 
208*aca7a94dSNamhyung Kim 	/* If it doesn't have children, no toggling performed */
209*aca7a94dSNamhyung Kim 	return false;
210*aca7a94dSNamhyung Kim }
211*aca7a94dSNamhyung Kim 
212*aca7a94dSNamhyung Kim static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
213*aca7a94dSNamhyung Kim {
214*aca7a94dSNamhyung Kim 	int n = 0;
215*aca7a94dSNamhyung Kim 	struct rb_node *nd;
216*aca7a94dSNamhyung Kim 
217*aca7a94dSNamhyung Kim 	for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
218*aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
219*aca7a94dSNamhyung Kim 		struct callchain_list *chain;
220*aca7a94dSNamhyung Kim 		bool has_children = false;
221*aca7a94dSNamhyung Kim 
222*aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
223*aca7a94dSNamhyung Kim 			++n;
224*aca7a94dSNamhyung Kim 			map_symbol__set_folding(&chain->ms, unfold);
225*aca7a94dSNamhyung Kim 			has_children = chain->ms.has_children;
226*aca7a94dSNamhyung Kim 		}
227*aca7a94dSNamhyung Kim 
228*aca7a94dSNamhyung Kim 		if (has_children)
229*aca7a94dSNamhyung Kim 			n += callchain_node__set_folding_rb_tree(child, unfold);
230*aca7a94dSNamhyung Kim 	}
231*aca7a94dSNamhyung Kim 
232*aca7a94dSNamhyung Kim 	return n;
233*aca7a94dSNamhyung Kim }
234*aca7a94dSNamhyung Kim 
235*aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
236*aca7a94dSNamhyung Kim {
237*aca7a94dSNamhyung Kim 	struct callchain_list *chain;
238*aca7a94dSNamhyung Kim 	bool has_children = false;
239*aca7a94dSNamhyung Kim 	int n = 0;
240*aca7a94dSNamhyung Kim 
241*aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
242*aca7a94dSNamhyung Kim 		++n;
243*aca7a94dSNamhyung Kim 		map_symbol__set_folding(&chain->ms, unfold);
244*aca7a94dSNamhyung Kim 		has_children = chain->ms.has_children;
245*aca7a94dSNamhyung Kim 	}
246*aca7a94dSNamhyung Kim 
247*aca7a94dSNamhyung Kim 	if (has_children)
248*aca7a94dSNamhyung Kim 		n += callchain_node__set_folding_rb_tree(node, unfold);
249*aca7a94dSNamhyung Kim 
250*aca7a94dSNamhyung Kim 	return n;
251*aca7a94dSNamhyung Kim }
252*aca7a94dSNamhyung Kim 
253*aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold)
254*aca7a94dSNamhyung Kim {
255*aca7a94dSNamhyung Kim 	struct rb_node *nd;
256*aca7a94dSNamhyung Kim 	int n = 0;
257*aca7a94dSNamhyung Kim 
258*aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
259*aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
260*aca7a94dSNamhyung Kim 		n += callchain_node__set_folding(node, unfold);
261*aca7a94dSNamhyung Kim 	}
262*aca7a94dSNamhyung Kim 
263*aca7a94dSNamhyung Kim 	return n;
264*aca7a94dSNamhyung Kim }
265*aca7a94dSNamhyung Kim 
266*aca7a94dSNamhyung Kim static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
267*aca7a94dSNamhyung Kim {
268*aca7a94dSNamhyung Kim 	hist_entry__init_have_children(self);
269*aca7a94dSNamhyung Kim 	map_symbol__set_folding(&self->ms, unfold);
270*aca7a94dSNamhyung Kim 
271*aca7a94dSNamhyung Kim 	if (self->ms.has_children) {
272*aca7a94dSNamhyung Kim 		int n = callchain__set_folding(&self->sorted_chain, unfold);
273*aca7a94dSNamhyung Kim 		self->nr_rows = unfold ? n : 0;
274*aca7a94dSNamhyung Kim 	} else
275*aca7a94dSNamhyung Kim 		self->nr_rows = 0;
276*aca7a94dSNamhyung Kim }
277*aca7a94dSNamhyung Kim 
278*aca7a94dSNamhyung Kim static void hists__set_folding(struct hists *self, bool unfold)
279*aca7a94dSNamhyung Kim {
280*aca7a94dSNamhyung Kim 	struct rb_node *nd;
281*aca7a94dSNamhyung Kim 
282*aca7a94dSNamhyung Kim 	self->nr_entries = 0;
283*aca7a94dSNamhyung Kim 
284*aca7a94dSNamhyung Kim 	for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
285*aca7a94dSNamhyung Kim 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
286*aca7a94dSNamhyung Kim 		hist_entry__set_folding(he, unfold);
287*aca7a94dSNamhyung Kim 		self->nr_entries += 1 + he->nr_rows;
288*aca7a94dSNamhyung Kim 	}
289*aca7a94dSNamhyung Kim }
290*aca7a94dSNamhyung Kim 
291*aca7a94dSNamhyung Kim static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
292*aca7a94dSNamhyung Kim {
293*aca7a94dSNamhyung Kim 	hists__set_folding(self->hists, unfold);
294*aca7a94dSNamhyung Kim 	self->b.nr_entries = self->hists->nr_entries;
295*aca7a94dSNamhyung Kim 	/* Go to the start, we may be way after valid entries after a collapse */
296*aca7a94dSNamhyung Kim 	ui_browser__reset_index(&self->b);
297*aca7a94dSNamhyung Kim }
298*aca7a94dSNamhyung Kim 
299*aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser)
300*aca7a94dSNamhyung Kim {
301*aca7a94dSNamhyung Kim 	ui_browser__warning(browser, 4,
302*aca7a94dSNamhyung Kim 		"Events are being lost, check IO/CPU overload!\n\n"
303*aca7a94dSNamhyung Kim 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
304*aca7a94dSNamhyung Kim 		" perf top -r 80\n\n"
305*aca7a94dSNamhyung Kim 		"Or reduce the sampling frequency.");
306*aca7a94dSNamhyung Kim }
307*aca7a94dSNamhyung Kim 
308*aca7a94dSNamhyung Kim static int hist_browser__run(struct hist_browser *self, const char *ev_name,
309*aca7a94dSNamhyung Kim 			     void(*timer)(void *arg), void *arg, int delay_secs)
310*aca7a94dSNamhyung Kim {
311*aca7a94dSNamhyung Kim 	int key;
312*aca7a94dSNamhyung Kim 	char title[160];
313*aca7a94dSNamhyung Kim 
314*aca7a94dSNamhyung Kim 	self->b.entries = &self->hists->entries;
315*aca7a94dSNamhyung Kim 	self->b.nr_entries = self->hists->nr_entries;
316*aca7a94dSNamhyung Kim 
317*aca7a94dSNamhyung Kim 	hist_browser__refresh_dimensions(self);
318*aca7a94dSNamhyung Kim 	hists__browser_title(self->hists, title, sizeof(title), ev_name);
319*aca7a94dSNamhyung Kim 
320*aca7a94dSNamhyung Kim 	if (ui_browser__show(&self->b, title,
321*aca7a94dSNamhyung Kim 			     "Press '?' for help on key bindings") < 0)
322*aca7a94dSNamhyung Kim 		return -1;
323*aca7a94dSNamhyung Kim 
324*aca7a94dSNamhyung Kim 	while (1) {
325*aca7a94dSNamhyung Kim 		key = ui_browser__run(&self->b, delay_secs);
326*aca7a94dSNamhyung Kim 
327*aca7a94dSNamhyung Kim 		switch (key) {
328*aca7a94dSNamhyung Kim 		case K_TIMER:
329*aca7a94dSNamhyung Kim 			timer(arg);
330*aca7a94dSNamhyung Kim 			ui_browser__update_nr_entries(&self->b, self->hists->nr_entries);
331*aca7a94dSNamhyung Kim 
332*aca7a94dSNamhyung Kim 			if (self->hists->stats.nr_lost_warned !=
333*aca7a94dSNamhyung Kim 			    self->hists->stats.nr_events[PERF_RECORD_LOST]) {
334*aca7a94dSNamhyung Kim 				self->hists->stats.nr_lost_warned =
335*aca7a94dSNamhyung Kim 					self->hists->stats.nr_events[PERF_RECORD_LOST];
336*aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&self->b);
337*aca7a94dSNamhyung Kim 			}
338*aca7a94dSNamhyung Kim 
339*aca7a94dSNamhyung Kim 			hists__browser_title(self->hists, title, sizeof(title), ev_name);
340*aca7a94dSNamhyung Kim 			ui_browser__show_title(&self->b, title);
341*aca7a94dSNamhyung Kim 			continue;
342*aca7a94dSNamhyung Kim 		case 'D': { /* Debug */
343*aca7a94dSNamhyung Kim 			static int seq;
344*aca7a94dSNamhyung Kim 			struct hist_entry *h = rb_entry(self->b.top,
345*aca7a94dSNamhyung Kim 							struct hist_entry, rb_node);
346*aca7a94dSNamhyung Kim 			ui_helpline__pop();
347*aca7a94dSNamhyung Kim 			ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
348*aca7a94dSNamhyung Kim 					   seq++, self->b.nr_entries,
349*aca7a94dSNamhyung Kim 					   self->hists->nr_entries,
350*aca7a94dSNamhyung Kim 					   self->b.height,
351*aca7a94dSNamhyung Kim 					   self->b.index,
352*aca7a94dSNamhyung Kim 					   self->b.top_idx,
353*aca7a94dSNamhyung Kim 					   h->row_offset, h->nr_rows);
354*aca7a94dSNamhyung Kim 		}
355*aca7a94dSNamhyung Kim 			break;
356*aca7a94dSNamhyung Kim 		case 'C':
357*aca7a94dSNamhyung Kim 			/* Collapse the whole world. */
358*aca7a94dSNamhyung Kim 			hist_browser__set_folding(self, false);
359*aca7a94dSNamhyung Kim 			break;
360*aca7a94dSNamhyung Kim 		case 'E':
361*aca7a94dSNamhyung Kim 			/* Expand the whole world. */
362*aca7a94dSNamhyung Kim 			hist_browser__set_folding(self, true);
363*aca7a94dSNamhyung Kim 			break;
364*aca7a94dSNamhyung Kim 		case K_ENTER:
365*aca7a94dSNamhyung Kim 			if (hist_browser__toggle_fold(self))
366*aca7a94dSNamhyung Kim 				break;
367*aca7a94dSNamhyung Kim 			/* fall thru */
368*aca7a94dSNamhyung Kim 		default:
369*aca7a94dSNamhyung Kim 			goto out;
370*aca7a94dSNamhyung Kim 		}
371*aca7a94dSNamhyung Kim 	}
372*aca7a94dSNamhyung Kim out:
373*aca7a94dSNamhyung Kim 	ui_browser__hide(&self->b);
374*aca7a94dSNamhyung Kim 	return key;
375*aca7a94dSNamhyung Kim }
376*aca7a94dSNamhyung Kim 
377*aca7a94dSNamhyung Kim static char *callchain_list__sym_name(struct callchain_list *self,
378*aca7a94dSNamhyung Kim 				      char *bf, size_t bfsize)
379*aca7a94dSNamhyung Kim {
380*aca7a94dSNamhyung Kim 	if (self->ms.sym)
381*aca7a94dSNamhyung Kim 		return self->ms.sym->name;
382*aca7a94dSNamhyung Kim 
383*aca7a94dSNamhyung Kim 	snprintf(bf, bfsize, "%#" PRIx64, self->ip);
384*aca7a94dSNamhyung Kim 	return bf;
385*aca7a94dSNamhyung Kim }
386*aca7a94dSNamhyung Kim 
387*aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3
388*aca7a94dSNamhyung Kim 
389*aca7a94dSNamhyung Kim static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
390*aca7a94dSNamhyung Kim 						     struct callchain_node *chain_node,
391*aca7a94dSNamhyung Kim 						     u64 total, int level,
392*aca7a94dSNamhyung Kim 						     unsigned short row,
393*aca7a94dSNamhyung Kim 						     off_t *row_offset,
394*aca7a94dSNamhyung Kim 						     bool *is_current_entry)
395*aca7a94dSNamhyung Kim {
396*aca7a94dSNamhyung Kim 	struct rb_node *node;
397*aca7a94dSNamhyung Kim 	int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
398*aca7a94dSNamhyung Kim 	u64 new_total, remaining;
399*aca7a94dSNamhyung Kim 
400*aca7a94dSNamhyung Kim 	if (callchain_param.mode == CHAIN_GRAPH_REL)
401*aca7a94dSNamhyung Kim 		new_total = chain_node->children_hit;
402*aca7a94dSNamhyung Kim 	else
403*aca7a94dSNamhyung Kim 		new_total = total;
404*aca7a94dSNamhyung Kim 
405*aca7a94dSNamhyung Kim 	remaining = new_total;
406*aca7a94dSNamhyung Kim 	node = rb_first(&chain_node->rb_root);
407*aca7a94dSNamhyung Kim 	while (node) {
408*aca7a94dSNamhyung Kim 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
409*aca7a94dSNamhyung Kim 		struct rb_node *next = rb_next(node);
410*aca7a94dSNamhyung Kim 		u64 cumul = callchain_cumul_hits(child);
411*aca7a94dSNamhyung Kim 		struct callchain_list *chain;
412*aca7a94dSNamhyung Kim 		char folded_sign = ' ';
413*aca7a94dSNamhyung Kim 		int first = true;
414*aca7a94dSNamhyung Kim 		int extra_offset = 0;
415*aca7a94dSNamhyung Kim 
416*aca7a94dSNamhyung Kim 		remaining -= cumul;
417*aca7a94dSNamhyung Kim 
418*aca7a94dSNamhyung Kim 		list_for_each_entry(chain, &child->val, list) {
419*aca7a94dSNamhyung Kim 			char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
420*aca7a94dSNamhyung Kim 			const char *str;
421*aca7a94dSNamhyung Kim 			int color;
422*aca7a94dSNamhyung Kim 			bool was_first = first;
423*aca7a94dSNamhyung Kim 
424*aca7a94dSNamhyung Kim 			if (first)
425*aca7a94dSNamhyung Kim 				first = false;
426*aca7a94dSNamhyung Kim 			else
427*aca7a94dSNamhyung Kim 				extra_offset = LEVEL_OFFSET_STEP;
428*aca7a94dSNamhyung Kim 
429*aca7a94dSNamhyung Kim 			folded_sign = callchain_list__folded(chain);
430*aca7a94dSNamhyung Kim 			if (*row_offset != 0) {
431*aca7a94dSNamhyung Kim 				--*row_offset;
432*aca7a94dSNamhyung Kim 				goto do_next;
433*aca7a94dSNamhyung Kim 			}
434*aca7a94dSNamhyung Kim 
435*aca7a94dSNamhyung Kim 			alloc_str = NULL;
436*aca7a94dSNamhyung Kim 			str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
437*aca7a94dSNamhyung Kim 			if (was_first) {
438*aca7a94dSNamhyung Kim 				double percent = cumul * 100.0 / new_total;
439*aca7a94dSNamhyung Kim 
440*aca7a94dSNamhyung Kim 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
441*aca7a94dSNamhyung Kim 					str = "Not enough memory!";
442*aca7a94dSNamhyung Kim 				else
443*aca7a94dSNamhyung Kim 					str = alloc_str;
444*aca7a94dSNamhyung Kim 			}
445*aca7a94dSNamhyung Kim 
446*aca7a94dSNamhyung Kim 			color = HE_COLORSET_NORMAL;
447*aca7a94dSNamhyung Kim 			width = self->b.width - (offset + extra_offset + 2);
448*aca7a94dSNamhyung Kim 			if (ui_browser__is_current_entry(&self->b, row)) {
449*aca7a94dSNamhyung Kim 				self->selection = &chain->ms;
450*aca7a94dSNamhyung Kim 				color = HE_COLORSET_SELECTED;
451*aca7a94dSNamhyung Kim 				*is_current_entry = true;
452*aca7a94dSNamhyung Kim 			}
453*aca7a94dSNamhyung Kim 
454*aca7a94dSNamhyung Kim 			ui_browser__set_color(&self->b, color);
455*aca7a94dSNamhyung Kim 			ui_browser__gotorc(&self->b, row, 0);
456*aca7a94dSNamhyung Kim 			slsmg_write_nstring(" ", offset + extra_offset);
457*aca7a94dSNamhyung Kim 			slsmg_printf("%c ", folded_sign);
458*aca7a94dSNamhyung Kim 			slsmg_write_nstring(str, width);
459*aca7a94dSNamhyung Kim 			free(alloc_str);
460*aca7a94dSNamhyung Kim 
461*aca7a94dSNamhyung Kim 			if (++row == self->b.height)
462*aca7a94dSNamhyung Kim 				goto out;
463*aca7a94dSNamhyung Kim do_next:
464*aca7a94dSNamhyung Kim 			if (folded_sign == '+')
465*aca7a94dSNamhyung Kim 				break;
466*aca7a94dSNamhyung Kim 		}
467*aca7a94dSNamhyung Kim 
468*aca7a94dSNamhyung Kim 		if (folded_sign == '-') {
469*aca7a94dSNamhyung Kim 			const int new_level = level + (extra_offset ? 2 : 1);
470*aca7a94dSNamhyung Kim 			row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
471*aca7a94dSNamhyung Kim 									 new_level, row, row_offset,
472*aca7a94dSNamhyung Kim 									 is_current_entry);
473*aca7a94dSNamhyung Kim 		}
474*aca7a94dSNamhyung Kim 		if (row == self->b.height)
475*aca7a94dSNamhyung Kim 			goto out;
476*aca7a94dSNamhyung Kim 		node = next;
477*aca7a94dSNamhyung Kim 	}
478*aca7a94dSNamhyung Kim out:
479*aca7a94dSNamhyung Kim 	return row - first_row;
480*aca7a94dSNamhyung Kim }
481*aca7a94dSNamhyung Kim 
482*aca7a94dSNamhyung Kim static int hist_browser__show_callchain_node(struct hist_browser *self,
483*aca7a94dSNamhyung Kim 					     struct callchain_node *node,
484*aca7a94dSNamhyung Kim 					     int level, unsigned short row,
485*aca7a94dSNamhyung Kim 					     off_t *row_offset,
486*aca7a94dSNamhyung Kim 					     bool *is_current_entry)
487*aca7a94dSNamhyung Kim {
488*aca7a94dSNamhyung Kim 	struct callchain_list *chain;
489*aca7a94dSNamhyung Kim 	int first_row = row,
490*aca7a94dSNamhyung Kim 	     offset = level * LEVEL_OFFSET_STEP,
491*aca7a94dSNamhyung Kim 	     width = self->b.width - offset;
492*aca7a94dSNamhyung Kim 	char folded_sign = ' ';
493*aca7a94dSNamhyung Kim 
494*aca7a94dSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
495*aca7a94dSNamhyung Kim 		char ipstr[BITS_PER_LONG / 4 + 1], *s;
496*aca7a94dSNamhyung Kim 		int color;
497*aca7a94dSNamhyung Kim 
498*aca7a94dSNamhyung Kim 		folded_sign = callchain_list__folded(chain);
499*aca7a94dSNamhyung Kim 
500*aca7a94dSNamhyung Kim 		if (*row_offset != 0) {
501*aca7a94dSNamhyung Kim 			--*row_offset;
502*aca7a94dSNamhyung Kim 			continue;
503*aca7a94dSNamhyung Kim 		}
504*aca7a94dSNamhyung Kim 
505*aca7a94dSNamhyung Kim 		color = HE_COLORSET_NORMAL;
506*aca7a94dSNamhyung Kim 		if (ui_browser__is_current_entry(&self->b, row)) {
507*aca7a94dSNamhyung Kim 			self->selection = &chain->ms;
508*aca7a94dSNamhyung Kim 			color = HE_COLORSET_SELECTED;
509*aca7a94dSNamhyung Kim 			*is_current_entry = true;
510*aca7a94dSNamhyung Kim 		}
511*aca7a94dSNamhyung Kim 
512*aca7a94dSNamhyung Kim 		s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
513*aca7a94dSNamhyung Kim 		ui_browser__gotorc(&self->b, row, 0);
514*aca7a94dSNamhyung Kim 		ui_browser__set_color(&self->b, color);
515*aca7a94dSNamhyung Kim 		slsmg_write_nstring(" ", offset);
516*aca7a94dSNamhyung Kim 		slsmg_printf("%c ", folded_sign);
517*aca7a94dSNamhyung Kim 		slsmg_write_nstring(s, width - 2);
518*aca7a94dSNamhyung Kim 
519*aca7a94dSNamhyung Kim 		if (++row == self->b.height)
520*aca7a94dSNamhyung Kim 			goto out;
521*aca7a94dSNamhyung Kim 	}
522*aca7a94dSNamhyung Kim 
523*aca7a94dSNamhyung Kim 	if (folded_sign == '-')
524*aca7a94dSNamhyung Kim 		row += hist_browser__show_callchain_node_rb_tree(self, node,
525*aca7a94dSNamhyung Kim 								 self->hists->stats.total_period,
526*aca7a94dSNamhyung Kim 								 level + 1, row,
527*aca7a94dSNamhyung Kim 								 row_offset,
528*aca7a94dSNamhyung Kim 								 is_current_entry);
529*aca7a94dSNamhyung Kim out:
530*aca7a94dSNamhyung Kim 	return row - first_row;
531*aca7a94dSNamhyung Kim }
532*aca7a94dSNamhyung Kim 
533*aca7a94dSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *self,
534*aca7a94dSNamhyung Kim 					struct rb_root *chain,
535*aca7a94dSNamhyung Kim 					int level, unsigned short row,
536*aca7a94dSNamhyung Kim 					off_t *row_offset,
537*aca7a94dSNamhyung Kim 					bool *is_current_entry)
538*aca7a94dSNamhyung Kim {
539*aca7a94dSNamhyung Kim 	struct rb_node *nd;
540*aca7a94dSNamhyung Kim 	int first_row = row;
541*aca7a94dSNamhyung Kim 
542*aca7a94dSNamhyung Kim 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
543*aca7a94dSNamhyung Kim 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
544*aca7a94dSNamhyung Kim 
545*aca7a94dSNamhyung Kim 		row += hist_browser__show_callchain_node(self, node, level,
546*aca7a94dSNamhyung Kim 							 row, row_offset,
547*aca7a94dSNamhyung Kim 							 is_current_entry);
548*aca7a94dSNamhyung Kim 		if (row == self->b.height)
549*aca7a94dSNamhyung Kim 			break;
550*aca7a94dSNamhyung Kim 	}
551*aca7a94dSNamhyung Kim 
552*aca7a94dSNamhyung Kim 	return row - first_row;
553*aca7a94dSNamhyung Kim }
554*aca7a94dSNamhyung Kim 
555*aca7a94dSNamhyung Kim static int hist_browser__show_entry(struct hist_browser *self,
556*aca7a94dSNamhyung Kim 				    struct hist_entry *entry,
557*aca7a94dSNamhyung Kim 				    unsigned short row)
558*aca7a94dSNamhyung Kim {
559*aca7a94dSNamhyung Kim 	char s[256];
560*aca7a94dSNamhyung Kim 	double percent;
561*aca7a94dSNamhyung Kim 	int printed = 0;
562*aca7a94dSNamhyung Kim 	int width = self->b.width - 6; /* The percentage */
563*aca7a94dSNamhyung Kim 	char folded_sign = ' ';
564*aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(&self->b, row);
565*aca7a94dSNamhyung Kim 	off_t row_offset = entry->row_offset;
566*aca7a94dSNamhyung Kim 
567*aca7a94dSNamhyung Kim 	if (current_entry) {
568*aca7a94dSNamhyung Kim 		self->he_selection = entry;
569*aca7a94dSNamhyung Kim 		self->selection = &entry->ms;
570*aca7a94dSNamhyung Kim 	}
571*aca7a94dSNamhyung Kim 
572*aca7a94dSNamhyung Kim 	if (symbol_conf.use_callchain) {
573*aca7a94dSNamhyung Kim 		hist_entry__init_have_children(entry);
574*aca7a94dSNamhyung Kim 		folded_sign = hist_entry__folded(entry);
575*aca7a94dSNamhyung Kim 	}
576*aca7a94dSNamhyung Kim 
577*aca7a94dSNamhyung Kim 	if (row_offset == 0) {
578*aca7a94dSNamhyung Kim 		hist_entry__snprintf(entry, s, sizeof(s), self->hists);
579*aca7a94dSNamhyung Kim 		percent = (entry->period * 100.0) / self->hists->stats.total_period;
580*aca7a94dSNamhyung Kim 
581*aca7a94dSNamhyung Kim 		ui_browser__set_percent_color(&self->b, percent, current_entry);
582*aca7a94dSNamhyung Kim 		ui_browser__gotorc(&self->b, row, 0);
583*aca7a94dSNamhyung Kim 		if (symbol_conf.use_callchain) {
584*aca7a94dSNamhyung Kim 			slsmg_printf("%c ", folded_sign);
585*aca7a94dSNamhyung Kim 			width -= 2;
586*aca7a94dSNamhyung Kim 		}
587*aca7a94dSNamhyung Kim 
588*aca7a94dSNamhyung Kim 		slsmg_printf(" %5.2f%%", percent);
589*aca7a94dSNamhyung Kim 
590*aca7a94dSNamhyung Kim 		/* The scroll bar isn't being used */
591*aca7a94dSNamhyung Kim 		if (!self->b.navkeypressed)
592*aca7a94dSNamhyung Kim 			width += 1;
593*aca7a94dSNamhyung Kim 
594*aca7a94dSNamhyung Kim 		if (!current_entry || !self->b.navkeypressed)
595*aca7a94dSNamhyung Kim 			ui_browser__set_color(&self->b, HE_COLORSET_NORMAL);
596*aca7a94dSNamhyung Kim 
597*aca7a94dSNamhyung Kim 		if (symbol_conf.show_nr_samples) {
598*aca7a94dSNamhyung Kim 			slsmg_printf(" %11u", entry->nr_events);
599*aca7a94dSNamhyung Kim 			width -= 12;
600*aca7a94dSNamhyung Kim 		}
601*aca7a94dSNamhyung Kim 
602*aca7a94dSNamhyung Kim 		if (symbol_conf.show_total_period) {
603*aca7a94dSNamhyung Kim 			slsmg_printf(" %12" PRIu64, entry->period);
604*aca7a94dSNamhyung Kim 			width -= 13;
605*aca7a94dSNamhyung Kim 		}
606*aca7a94dSNamhyung Kim 
607*aca7a94dSNamhyung Kim 		slsmg_write_nstring(s, width);
608*aca7a94dSNamhyung Kim 		++row;
609*aca7a94dSNamhyung Kim 		++printed;
610*aca7a94dSNamhyung Kim 	} else
611*aca7a94dSNamhyung Kim 		--row_offset;
612*aca7a94dSNamhyung Kim 
613*aca7a94dSNamhyung Kim 	if (folded_sign == '-' && row != self->b.height) {
614*aca7a94dSNamhyung Kim 		printed += hist_browser__show_callchain(self, &entry->sorted_chain,
615*aca7a94dSNamhyung Kim 							1, row, &row_offset,
616*aca7a94dSNamhyung Kim 							&current_entry);
617*aca7a94dSNamhyung Kim 		if (current_entry)
618*aca7a94dSNamhyung Kim 			self->he_selection = entry;
619*aca7a94dSNamhyung Kim 	}
620*aca7a94dSNamhyung Kim 
621*aca7a94dSNamhyung Kim 	return printed;
622*aca7a94dSNamhyung Kim }
623*aca7a94dSNamhyung Kim 
624*aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser)
625*aca7a94dSNamhyung Kim {
626*aca7a94dSNamhyung Kim 	if (browser->top == NULL) {
627*aca7a94dSNamhyung Kim 		struct hist_browser *hb;
628*aca7a94dSNamhyung Kim 
629*aca7a94dSNamhyung Kim 		hb = container_of(browser, struct hist_browser, b);
630*aca7a94dSNamhyung Kim 		browser->top = rb_first(&hb->hists->entries);
631*aca7a94dSNamhyung Kim 	}
632*aca7a94dSNamhyung Kim }
633*aca7a94dSNamhyung Kim 
634*aca7a94dSNamhyung Kim static unsigned int hist_browser__refresh(struct ui_browser *self)
635*aca7a94dSNamhyung Kim {
636*aca7a94dSNamhyung Kim 	unsigned row = 0;
637*aca7a94dSNamhyung Kim 	struct rb_node *nd;
638*aca7a94dSNamhyung Kim 	struct hist_browser *hb = container_of(self, struct hist_browser, b);
639*aca7a94dSNamhyung Kim 
640*aca7a94dSNamhyung Kim 	ui_browser__hists_init_top(self);
641*aca7a94dSNamhyung Kim 
642*aca7a94dSNamhyung Kim 	for (nd = self->top; nd; nd = rb_next(nd)) {
643*aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
644*aca7a94dSNamhyung Kim 
645*aca7a94dSNamhyung Kim 		if (h->filtered)
646*aca7a94dSNamhyung Kim 			continue;
647*aca7a94dSNamhyung Kim 
648*aca7a94dSNamhyung Kim 		row += hist_browser__show_entry(hb, h, row);
649*aca7a94dSNamhyung Kim 		if (row == self->height)
650*aca7a94dSNamhyung Kim 			break;
651*aca7a94dSNamhyung Kim 	}
652*aca7a94dSNamhyung Kim 
653*aca7a94dSNamhyung Kim 	return row;
654*aca7a94dSNamhyung Kim }
655*aca7a94dSNamhyung Kim 
656*aca7a94dSNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd)
657*aca7a94dSNamhyung Kim {
658*aca7a94dSNamhyung Kim 	while (nd != NULL) {
659*aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
660*aca7a94dSNamhyung Kim 		if (!h->filtered)
661*aca7a94dSNamhyung Kim 			return nd;
662*aca7a94dSNamhyung Kim 
663*aca7a94dSNamhyung Kim 		nd = rb_next(nd);
664*aca7a94dSNamhyung Kim 	}
665*aca7a94dSNamhyung Kim 
666*aca7a94dSNamhyung Kim 	return NULL;
667*aca7a94dSNamhyung Kim }
668*aca7a94dSNamhyung Kim 
669*aca7a94dSNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
670*aca7a94dSNamhyung Kim {
671*aca7a94dSNamhyung Kim 	while (nd != NULL) {
672*aca7a94dSNamhyung Kim 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
673*aca7a94dSNamhyung Kim 		if (!h->filtered)
674*aca7a94dSNamhyung Kim 			return nd;
675*aca7a94dSNamhyung Kim 
676*aca7a94dSNamhyung Kim 		nd = rb_prev(nd);
677*aca7a94dSNamhyung Kim 	}
678*aca7a94dSNamhyung Kim 
679*aca7a94dSNamhyung Kim 	return NULL;
680*aca7a94dSNamhyung Kim }
681*aca7a94dSNamhyung Kim 
682*aca7a94dSNamhyung Kim static void ui_browser__hists_seek(struct ui_browser *self,
683*aca7a94dSNamhyung Kim 				   off_t offset, int whence)
684*aca7a94dSNamhyung Kim {
685*aca7a94dSNamhyung Kim 	struct hist_entry *h;
686*aca7a94dSNamhyung Kim 	struct rb_node *nd;
687*aca7a94dSNamhyung Kim 	bool first = true;
688*aca7a94dSNamhyung Kim 
689*aca7a94dSNamhyung Kim 	if (self->nr_entries == 0)
690*aca7a94dSNamhyung Kim 		return;
691*aca7a94dSNamhyung Kim 
692*aca7a94dSNamhyung Kim 	ui_browser__hists_init_top(self);
693*aca7a94dSNamhyung Kim 
694*aca7a94dSNamhyung Kim 	switch (whence) {
695*aca7a94dSNamhyung Kim 	case SEEK_SET:
696*aca7a94dSNamhyung Kim 		nd = hists__filter_entries(rb_first(self->entries));
697*aca7a94dSNamhyung Kim 		break;
698*aca7a94dSNamhyung Kim 	case SEEK_CUR:
699*aca7a94dSNamhyung Kim 		nd = self->top;
700*aca7a94dSNamhyung Kim 		goto do_offset;
701*aca7a94dSNamhyung Kim 	case SEEK_END:
702*aca7a94dSNamhyung Kim 		nd = hists__filter_prev_entries(rb_last(self->entries));
703*aca7a94dSNamhyung Kim 		first = false;
704*aca7a94dSNamhyung Kim 		break;
705*aca7a94dSNamhyung Kim 	default:
706*aca7a94dSNamhyung Kim 		return;
707*aca7a94dSNamhyung Kim 	}
708*aca7a94dSNamhyung Kim 
709*aca7a94dSNamhyung Kim 	/*
710*aca7a94dSNamhyung Kim 	 * Moves not relative to the first visible entry invalidates its
711*aca7a94dSNamhyung Kim 	 * row_offset:
712*aca7a94dSNamhyung Kim 	 */
713*aca7a94dSNamhyung Kim 	h = rb_entry(self->top, struct hist_entry, rb_node);
714*aca7a94dSNamhyung Kim 	h->row_offset = 0;
715*aca7a94dSNamhyung Kim 
716*aca7a94dSNamhyung Kim 	/*
717*aca7a94dSNamhyung Kim 	 * Here we have to check if nd is expanded (+), if it is we can't go
718*aca7a94dSNamhyung Kim 	 * the next top level hist_entry, instead we must compute an offset of
719*aca7a94dSNamhyung Kim 	 * what _not_ to show and not change the first visible entry.
720*aca7a94dSNamhyung Kim 	 *
721*aca7a94dSNamhyung Kim 	 * This offset increments when we are going from top to bottom and
722*aca7a94dSNamhyung Kim 	 * decreases when we're going from bottom to top.
723*aca7a94dSNamhyung Kim 	 *
724*aca7a94dSNamhyung Kim 	 * As we don't have backpointers to the top level in the callchains
725*aca7a94dSNamhyung Kim 	 * structure, we need to always print the whole hist_entry callchain,
726*aca7a94dSNamhyung Kim 	 * skipping the first ones that are before the first visible entry
727*aca7a94dSNamhyung Kim 	 * and stop when we printed enough lines to fill the screen.
728*aca7a94dSNamhyung Kim 	 */
729*aca7a94dSNamhyung Kim do_offset:
730*aca7a94dSNamhyung Kim 	if (offset > 0) {
731*aca7a94dSNamhyung Kim 		do {
732*aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
733*aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
734*aca7a94dSNamhyung Kim 				u16 remaining = h->nr_rows - h->row_offset;
735*aca7a94dSNamhyung Kim 				if (offset > remaining) {
736*aca7a94dSNamhyung Kim 					offset -= remaining;
737*aca7a94dSNamhyung Kim 					h->row_offset = 0;
738*aca7a94dSNamhyung Kim 				} else {
739*aca7a94dSNamhyung Kim 					h->row_offset += offset;
740*aca7a94dSNamhyung Kim 					offset = 0;
741*aca7a94dSNamhyung Kim 					self->top = nd;
742*aca7a94dSNamhyung Kim 					break;
743*aca7a94dSNamhyung Kim 				}
744*aca7a94dSNamhyung Kim 			}
745*aca7a94dSNamhyung Kim 			nd = hists__filter_entries(rb_next(nd));
746*aca7a94dSNamhyung Kim 			if (nd == NULL)
747*aca7a94dSNamhyung Kim 				break;
748*aca7a94dSNamhyung Kim 			--offset;
749*aca7a94dSNamhyung Kim 			self->top = nd;
750*aca7a94dSNamhyung Kim 		} while (offset != 0);
751*aca7a94dSNamhyung Kim 	} else if (offset < 0) {
752*aca7a94dSNamhyung Kim 		while (1) {
753*aca7a94dSNamhyung Kim 			h = rb_entry(nd, struct hist_entry, rb_node);
754*aca7a94dSNamhyung Kim 			if (h->ms.unfolded) {
755*aca7a94dSNamhyung Kim 				if (first) {
756*aca7a94dSNamhyung Kim 					if (-offset > h->row_offset) {
757*aca7a94dSNamhyung Kim 						offset += h->row_offset;
758*aca7a94dSNamhyung Kim 						h->row_offset = 0;
759*aca7a94dSNamhyung Kim 					} else {
760*aca7a94dSNamhyung Kim 						h->row_offset += offset;
761*aca7a94dSNamhyung Kim 						offset = 0;
762*aca7a94dSNamhyung Kim 						self->top = nd;
763*aca7a94dSNamhyung Kim 						break;
764*aca7a94dSNamhyung Kim 					}
765*aca7a94dSNamhyung Kim 				} else {
766*aca7a94dSNamhyung Kim 					if (-offset > h->nr_rows) {
767*aca7a94dSNamhyung Kim 						offset += h->nr_rows;
768*aca7a94dSNamhyung Kim 						h->row_offset = 0;
769*aca7a94dSNamhyung Kim 					} else {
770*aca7a94dSNamhyung Kim 						h->row_offset = h->nr_rows + offset;
771*aca7a94dSNamhyung Kim 						offset = 0;
772*aca7a94dSNamhyung Kim 						self->top = nd;
773*aca7a94dSNamhyung Kim 						break;
774*aca7a94dSNamhyung Kim 					}
775*aca7a94dSNamhyung Kim 				}
776*aca7a94dSNamhyung Kim 			}
777*aca7a94dSNamhyung Kim 
778*aca7a94dSNamhyung Kim 			nd = hists__filter_prev_entries(rb_prev(nd));
779*aca7a94dSNamhyung Kim 			if (nd == NULL)
780*aca7a94dSNamhyung Kim 				break;
781*aca7a94dSNamhyung Kim 			++offset;
782*aca7a94dSNamhyung Kim 			self->top = nd;
783*aca7a94dSNamhyung Kim 			if (offset == 0) {
784*aca7a94dSNamhyung Kim 				/*
785*aca7a94dSNamhyung Kim 				 * Last unfiltered hist_entry, check if it is
786*aca7a94dSNamhyung Kim 				 * unfolded, if it is then we should have
787*aca7a94dSNamhyung Kim 				 * row_offset at its last entry.
788*aca7a94dSNamhyung Kim 				 */
789*aca7a94dSNamhyung Kim 				h = rb_entry(nd, struct hist_entry, rb_node);
790*aca7a94dSNamhyung Kim 				if (h->ms.unfolded)
791*aca7a94dSNamhyung Kim 					h->row_offset = h->nr_rows;
792*aca7a94dSNamhyung Kim 				break;
793*aca7a94dSNamhyung Kim 			}
794*aca7a94dSNamhyung Kim 			first = false;
795*aca7a94dSNamhyung Kim 		}
796*aca7a94dSNamhyung Kim 	} else {
797*aca7a94dSNamhyung Kim 		self->top = nd;
798*aca7a94dSNamhyung Kim 		h = rb_entry(nd, struct hist_entry, rb_node);
799*aca7a94dSNamhyung Kim 		h->row_offset = 0;
800*aca7a94dSNamhyung Kim 	}
801*aca7a94dSNamhyung Kim }
802*aca7a94dSNamhyung Kim 
803*aca7a94dSNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists)
804*aca7a94dSNamhyung Kim {
805*aca7a94dSNamhyung Kim 	struct hist_browser *self = zalloc(sizeof(*self));
806*aca7a94dSNamhyung Kim 
807*aca7a94dSNamhyung Kim 	if (self) {
808*aca7a94dSNamhyung Kim 		self->hists = hists;
809*aca7a94dSNamhyung Kim 		self->b.refresh = hist_browser__refresh;
810*aca7a94dSNamhyung Kim 		self->b.seek = ui_browser__hists_seek;
811*aca7a94dSNamhyung Kim 		self->b.use_navkeypressed = true;
812*aca7a94dSNamhyung Kim 		if (sort__branch_mode == 1)
813*aca7a94dSNamhyung Kim 			self->has_symbols = sort_sym_from.list.next != NULL;
814*aca7a94dSNamhyung Kim 		else
815*aca7a94dSNamhyung Kim 			self->has_symbols = sort_sym.list.next != NULL;
816*aca7a94dSNamhyung Kim 	}
817*aca7a94dSNamhyung Kim 
818*aca7a94dSNamhyung Kim 	return self;
819*aca7a94dSNamhyung Kim }
820*aca7a94dSNamhyung Kim 
821*aca7a94dSNamhyung Kim static void hist_browser__delete(struct hist_browser *self)
822*aca7a94dSNamhyung Kim {
823*aca7a94dSNamhyung Kim 	free(self);
824*aca7a94dSNamhyung Kim }
825*aca7a94dSNamhyung Kim 
826*aca7a94dSNamhyung Kim static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
827*aca7a94dSNamhyung Kim {
828*aca7a94dSNamhyung Kim 	return self->he_selection;
829*aca7a94dSNamhyung Kim }
830*aca7a94dSNamhyung Kim 
831*aca7a94dSNamhyung Kim static struct thread *hist_browser__selected_thread(struct hist_browser *self)
832*aca7a94dSNamhyung Kim {
833*aca7a94dSNamhyung Kim 	return self->he_selection->thread;
834*aca7a94dSNamhyung Kim }
835*aca7a94dSNamhyung Kim 
836*aca7a94dSNamhyung Kim static int hists__browser_title(struct hists *self, char *bf, size_t size,
837*aca7a94dSNamhyung Kim 				const char *ev_name)
838*aca7a94dSNamhyung Kim {
839*aca7a94dSNamhyung Kim 	char unit;
840*aca7a94dSNamhyung Kim 	int printed;
841*aca7a94dSNamhyung Kim 	const struct dso *dso = self->dso_filter;
842*aca7a94dSNamhyung Kim 	const struct thread *thread = self->thread_filter;
843*aca7a94dSNamhyung Kim 	unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
844*aca7a94dSNamhyung Kim 	u64 nr_events = self->stats.total_period;
845*aca7a94dSNamhyung Kim 
846*aca7a94dSNamhyung Kim 	nr_samples = convert_unit(nr_samples, &unit);
847*aca7a94dSNamhyung Kim 	printed = scnprintf(bf, size,
848*aca7a94dSNamhyung Kim 			   "Samples: %lu%c of event '%s', Event count (approx.): %lu",
849*aca7a94dSNamhyung Kim 			   nr_samples, unit, ev_name, nr_events);
850*aca7a94dSNamhyung Kim 
851*aca7a94dSNamhyung Kim 
852*aca7a94dSNamhyung Kim 	if (self->uid_filter_str)
853*aca7a94dSNamhyung Kim 		printed += snprintf(bf + printed, size - printed,
854*aca7a94dSNamhyung Kim 				    ", UID: %s", self->uid_filter_str);
855*aca7a94dSNamhyung Kim 	if (thread)
856*aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
857*aca7a94dSNamhyung Kim 				    ", Thread: %s(%d)",
858*aca7a94dSNamhyung Kim 				    (thread->comm_set ? thread->comm : ""),
859*aca7a94dSNamhyung Kim 				    thread->pid);
860*aca7a94dSNamhyung Kim 	if (dso)
861*aca7a94dSNamhyung Kim 		printed += scnprintf(bf + printed, size - printed,
862*aca7a94dSNamhyung Kim 				    ", DSO: %s", dso->short_name);
863*aca7a94dSNamhyung Kim 	return printed;
864*aca7a94dSNamhyung Kim }
865*aca7a94dSNamhyung Kim 
866*aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n)
867*aca7a94dSNamhyung Kim {
868*aca7a94dSNamhyung Kim 	int i;
869*aca7a94dSNamhyung Kim 
870*aca7a94dSNamhyung Kim 	for (i = 0; i < n; ++i) {
871*aca7a94dSNamhyung Kim 		free(options[i]);
872*aca7a94dSNamhyung Kim 		options[i] = NULL;
873*aca7a94dSNamhyung Kim 	}
874*aca7a94dSNamhyung Kim }
875*aca7a94dSNamhyung Kim 
876*aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
877*aca7a94dSNamhyung Kim 				    const char *helpline, const char *ev_name,
878*aca7a94dSNamhyung Kim 				    bool left_exits,
879*aca7a94dSNamhyung Kim 				    void(*timer)(void *arg), void *arg,
880*aca7a94dSNamhyung Kim 				    int delay_secs)
881*aca7a94dSNamhyung Kim {
882*aca7a94dSNamhyung Kim 	struct hists *self = &evsel->hists;
883*aca7a94dSNamhyung Kim 	struct hist_browser *browser = hist_browser__new(self);
884*aca7a94dSNamhyung Kim 	struct branch_info *bi;
885*aca7a94dSNamhyung Kim 	struct pstack *fstack;
886*aca7a94dSNamhyung Kim 	char *options[16];
887*aca7a94dSNamhyung Kim 	int nr_options = 0;
888*aca7a94dSNamhyung Kim 	int key = -1;
889*aca7a94dSNamhyung Kim 	char buf[64];
890*aca7a94dSNamhyung Kim 
891*aca7a94dSNamhyung Kim 	if (browser == NULL)
892*aca7a94dSNamhyung Kim 		return -1;
893*aca7a94dSNamhyung Kim 
894*aca7a94dSNamhyung Kim 	fstack = pstack__new(2);
895*aca7a94dSNamhyung Kim 	if (fstack == NULL)
896*aca7a94dSNamhyung Kim 		goto out;
897*aca7a94dSNamhyung Kim 
898*aca7a94dSNamhyung Kim 	ui_helpline__push(helpline);
899*aca7a94dSNamhyung Kim 
900*aca7a94dSNamhyung Kim 	memset(options, 0, sizeof(options));
901*aca7a94dSNamhyung Kim 
902*aca7a94dSNamhyung Kim 	while (1) {
903*aca7a94dSNamhyung Kim 		const struct thread *thread = NULL;
904*aca7a94dSNamhyung Kim 		const struct dso *dso = NULL;
905*aca7a94dSNamhyung Kim 		int choice = 0,
906*aca7a94dSNamhyung Kim 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
907*aca7a94dSNamhyung Kim 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
908*aca7a94dSNamhyung Kim 
909*aca7a94dSNamhyung Kim 		nr_options = 0;
910*aca7a94dSNamhyung Kim 
911*aca7a94dSNamhyung Kim 		key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
912*aca7a94dSNamhyung Kim 
913*aca7a94dSNamhyung Kim 		if (browser->he_selection != NULL) {
914*aca7a94dSNamhyung Kim 			thread = hist_browser__selected_thread(browser);
915*aca7a94dSNamhyung Kim 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
916*aca7a94dSNamhyung Kim 		}
917*aca7a94dSNamhyung Kim 		switch (key) {
918*aca7a94dSNamhyung Kim 		case K_TAB:
919*aca7a94dSNamhyung Kim 		case K_UNTAB:
920*aca7a94dSNamhyung Kim 			if (nr_events == 1)
921*aca7a94dSNamhyung Kim 				continue;
922*aca7a94dSNamhyung Kim 			/*
923*aca7a94dSNamhyung Kim 			 * Exit the browser, let hists__browser_tree
924*aca7a94dSNamhyung Kim 			 * go to the next or previous
925*aca7a94dSNamhyung Kim 			 */
926*aca7a94dSNamhyung Kim 			goto out_free_stack;
927*aca7a94dSNamhyung Kim 		case 'a':
928*aca7a94dSNamhyung Kim 			if (!browser->has_symbols) {
929*aca7a94dSNamhyung Kim 				ui_browser__warning(&browser->b, delay_secs * 2,
930*aca7a94dSNamhyung Kim 			"Annotation is only available for symbolic views, "
931*aca7a94dSNamhyung Kim 			"include \"sym*\" in --sort to use it.");
932*aca7a94dSNamhyung Kim 				continue;
933*aca7a94dSNamhyung Kim 			}
934*aca7a94dSNamhyung Kim 
935*aca7a94dSNamhyung Kim 			if (browser->selection == NULL ||
936*aca7a94dSNamhyung Kim 			    browser->selection->sym == NULL ||
937*aca7a94dSNamhyung Kim 			    browser->selection->map->dso->annotate_warned)
938*aca7a94dSNamhyung Kim 				continue;
939*aca7a94dSNamhyung Kim 			goto do_annotate;
940*aca7a94dSNamhyung Kim 		case 'd':
941*aca7a94dSNamhyung Kim 			goto zoom_dso;
942*aca7a94dSNamhyung Kim 		case 't':
943*aca7a94dSNamhyung Kim 			goto zoom_thread;
944*aca7a94dSNamhyung Kim 		case 's':
945*aca7a94dSNamhyung Kim 			if (ui_browser__input_window("Symbol to show",
946*aca7a94dSNamhyung Kim 					"Please enter the name of symbol you want to see",
947*aca7a94dSNamhyung Kim 					buf, "ENTER: OK, ESC: Cancel",
948*aca7a94dSNamhyung Kim 					delay_secs * 2) == K_ENTER) {
949*aca7a94dSNamhyung Kim 				self->symbol_filter_str = *buf ? buf : NULL;
950*aca7a94dSNamhyung Kim 				hists__filter_by_symbol(self);
951*aca7a94dSNamhyung Kim 				hist_browser__reset(browser);
952*aca7a94dSNamhyung Kim 			}
953*aca7a94dSNamhyung Kim 			continue;
954*aca7a94dSNamhyung Kim 		case K_F1:
955*aca7a94dSNamhyung Kim 		case 'h':
956*aca7a94dSNamhyung Kim 		case '?':
957*aca7a94dSNamhyung Kim 			ui_browser__help_window(&browser->b,
958*aca7a94dSNamhyung Kim 					"h/?/F1        Show this window\n"
959*aca7a94dSNamhyung Kim 					"UP/DOWN/PGUP\n"
960*aca7a94dSNamhyung Kim 					"PGDN/SPACE    Navigate\n"
961*aca7a94dSNamhyung Kim 					"q/ESC/CTRL+C  Exit browser\n\n"
962*aca7a94dSNamhyung Kim 					"For multiple event sessions:\n\n"
963*aca7a94dSNamhyung Kim 					"TAB/UNTAB Switch events\n\n"
964*aca7a94dSNamhyung Kim 					"For symbolic views (--sort has sym):\n\n"
965*aca7a94dSNamhyung Kim 					"->            Zoom into DSO/Threads & Annotate current symbol\n"
966*aca7a94dSNamhyung Kim 					"<-            Zoom out\n"
967*aca7a94dSNamhyung Kim 					"a             Annotate current symbol\n"
968*aca7a94dSNamhyung Kim 					"C             Collapse all callchains\n"
969*aca7a94dSNamhyung Kim 					"E             Expand all callchains\n"
970*aca7a94dSNamhyung Kim 					"d             Zoom into current DSO\n"
971*aca7a94dSNamhyung Kim 					"t             Zoom into current Thread\n"
972*aca7a94dSNamhyung Kim 					"s             Filter symbol by name");
973*aca7a94dSNamhyung Kim 			continue;
974*aca7a94dSNamhyung Kim 		case K_ENTER:
975*aca7a94dSNamhyung Kim 		case K_RIGHT:
976*aca7a94dSNamhyung Kim 			/* menu */
977*aca7a94dSNamhyung Kim 			break;
978*aca7a94dSNamhyung Kim 		case K_LEFT: {
979*aca7a94dSNamhyung Kim 			const void *top;
980*aca7a94dSNamhyung Kim 
981*aca7a94dSNamhyung Kim 			if (pstack__empty(fstack)) {
982*aca7a94dSNamhyung Kim 				/*
983*aca7a94dSNamhyung Kim 				 * Go back to the perf_evsel_menu__run or other user
984*aca7a94dSNamhyung Kim 				 */
985*aca7a94dSNamhyung Kim 				if (left_exits)
986*aca7a94dSNamhyung Kim 					goto out_free_stack;
987*aca7a94dSNamhyung Kim 				continue;
988*aca7a94dSNamhyung Kim 			}
989*aca7a94dSNamhyung Kim 			top = pstack__pop(fstack);
990*aca7a94dSNamhyung Kim 			if (top == &browser->hists->dso_filter)
991*aca7a94dSNamhyung Kim 				goto zoom_out_dso;
992*aca7a94dSNamhyung Kim 			if (top == &browser->hists->thread_filter)
993*aca7a94dSNamhyung Kim 				goto zoom_out_thread;
994*aca7a94dSNamhyung Kim 			continue;
995*aca7a94dSNamhyung Kim 		}
996*aca7a94dSNamhyung Kim 		case K_ESC:
997*aca7a94dSNamhyung Kim 			if (!left_exits &&
998*aca7a94dSNamhyung Kim 			    !ui_browser__dialog_yesno(&browser->b,
999*aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1000*aca7a94dSNamhyung Kim 				continue;
1001*aca7a94dSNamhyung Kim 			/* Fall thru */
1002*aca7a94dSNamhyung Kim 		case 'q':
1003*aca7a94dSNamhyung Kim 		case CTRL('c'):
1004*aca7a94dSNamhyung Kim 			goto out_free_stack;
1005*aca7a94dSNamhyung Kim 		default:
1006*aca7a94dSNamhyung Kim 			continue;
1007*aca7a94dSNamhyung Kim 		}
1008*aca7a94dSNamhyung Kim 
1009*aca7a94dSNamhyung Kim 		if (!browser->has_symbols)
1010*aca7a94dSNamhyung Kim 			goto add_exit_option;
1011*aca7a94dSNamhyung Kim 
1012*aca7a94dSNamhyung Kim 		if (sort__branch_mode == 1) {
1013*aca7a94dSNamhyung Kim 			bi = browser->he_selection->branch_info;
1014*aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1015*aca7a94dSNamhyung Kim 			    bi &&
1016*aca7a94dSNamhyung Kim 			    bi->from.sym != NULL &&
1017*aca7a94dSNamhyung Kim 			    !bi->from.map->dso->annotate_warned &&
1018*aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1019*aca7a94dSNamhyung Kim 					 bi->from.sym->name) > 0)
1020*aca7a94dSNamhyung Kim 				annotate_f = nr_options++;
1021*aca7a94dSNamhyung Kim 
1022*aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1023*aca7a94dSNamhyung Kim 			    bi &&
1024*aca7a94dSNamhyung Kim 			    bi->to.sym != NULL &&
1025*aca7a94dSNamhyung Kim 			    !bi->to.map->dso->annotate_warned &&
1026*aca7a94dSNamhyung Kim 			    (bi->to.sym != bi->from.sym ||
1027*aca7a94dSNamhyung Kim 			     bi->to.map->dso != bi->from.map->dso) &&
1028*aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1029*aca7a94dSNamhyung Kim 					 bi->to.sym->name) > 0)
1030*aca7a94dSNamhyung Kim 				annotate_t = nr_options++;
1031*aca7a94dSNamhyung Kim 		} else {
1032*aca7a94dSNamhyung Kim 
1033*aca7a94dSNamhyung Kim 			if (browser->selection != NULL &&
1034*aca7a94dSNamhyung Kim 			    browser->selection->sym != NULL &&
1035*aca7a94dSNamhyung Kim 			    !browser->selection->map->dso->annotate_warned &&
1036*aca7a94dSNamhyung Kim 				asprintf(&options[nr_options], "Annotate %s",
1037*aca7a94dSNamhyung Kim 					 browser->selection->sym->name) > 0)
1038*aca7a94dSNamhyung Kim 				annotate = nr_options++;
1039*aca7a94dSNamhyung Kim 		}
1040*aca7a94dSNamhyung Kim 
1041*aca7a94dSNamhyung Kim 		if (thread != NULL &&
1042*aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1043*aca7a94dSNamhyung Kim 			     (browser->hists->thread_filter ? "out of" : "into"),
1044*aca7a94dSNamhyung Kim 			     (thread->comm_set ? thread->comm : ""),
1045*aca7a94dSNamhyung Kim 			     thread->pid) > 0)
1046*aca7a94dSNamhyung Kim 			zoom_thread = nr_options++;
1047*aca7a94dSNamhyung Kim 
1048*aca7a94dSNamhyung Kim 		if (dso != NULL &&
1049*aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1050*aca7a94dSNamhyung Kim 			     (browser->hists->dso_filter ? "out of" : "into"),
1051*aca7a94dSNamhyung Kim 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1052*aca7a94dSNamhyung Kim 			zoom_dso = nr_options++;
1053*aca7a94dSNamhyung Kim 
1054*aca7a94dSNamhyung Kim 		if (browser->selection != NULL &&
1055*aca7a94dSNamhyung Kim 		    browser->selection->map != NULL &&
1056*aca7a94dSNamhyung Kim 		    asprintf(&options[nr_options], "Browse map details") > 0)
1057*aca7a94dSNamhyung Kim 			browse_map = nr_options++;
1058*aca7a94dSNamhyung Kim add_exit_option:
1059*aca7a94dSNamhyung Kim 		options[nr_options++] = (char *)"Exit";
1060*aca7a94dSNamhyung Kim retry_popup_menu:
1061*aca7a94dSNamhyung Kim 		choice = ui__popup_menu(nr_options, options);
1062*aca7a94dSNamhyung Kim 
1063*aca7a94dSNamhyung Kim 		if (choice == nr_options - 1)
1064*aca7a94dSNamhyung Kim 			break;
1065*aca7a94dSNamhyung Kim 
1066*aca7a94dSNamhyung Kim 		if (choice == -1) {
1067*aca7a94dSNamhyung Kim 			free_popup_options(options, nr_options - 1);
1068*aca7a94dSNamhyung Kim 			continue;
1069*aca7a94dSNamhyung Kim 		}
1070*aca7a94dSNamhyung Kim 
1071*aca7a94dSNamhyung Kim 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1072*aca7a94dSNamhyung Kim 			struct hist_entry *he;
1073*aca7a94dSNamhyung Kim 			int err;
1074*aca7a94dSNamhyung Kim do_annotate:
1075*aca7a94dSNamhyung Kim 			he = hist_browser__selected_entry(browser);
1076*aca7a94dSNamhyung Kim 			if (he == NULL)
1077*aca7a94dSNamhyung Kim 				continue;
1078*aca7a94dSNamhyung Kim 
1079*aca7a94dSNamhyung Kim 			/*
1080*aca7a94dSNamhyung Kim 			 * we stash the branch_info symbol + map into the
1081*aca7a94dSNamhyung Kim 			 * the ms so we don't have to rewrite all the annotation
1082*aca7a94dSNamhyung Kim 			 * code to use branch_info.
1083*aca7a94dSNamhyung Kim 			 * in branch mode, the ms struct is not used
1084*aca7a94dSNamhyung Kim 			 */
1085*aca7a94dSNamhyung Kim 			if (choice == annotate_f) {
1086*aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->from.sym;
1087*aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->from.map;
1088*aca7a94dSNamhyung Kim 			}  else if (choice == annotate_t) {
1089*aca7a94dSNamhyung Kim 				he->ms.sym = he->branch_info->to.sym;
1090*aca7a94dSNamhyung Kim 				he->ms.map = he->branch_info->to.map;
1091*aca7a94dSNamhyung Kim 			}
1092*aca7a94dSNamhyung Kim 
1093*aca7a94dSNamhyung Kim 			/*
1094*aca7a94dSNamhyung Kim 			 * Don't let this be freed, say, by hists__decay_entry.
1095*aca7a94dSNamhyung Kim 			 */
1096*aca7a94dSNamhyung Kim 			he->used = true;
1097*aca7a94dSNamhyung Kim 			err = hist_entry__tui_annotate(he, evsel->idx,
1098*aca7a94dSNamhyung Kim 						       timer, arg, delay_secs);
1099*aca7a94dSNamhyung Kim 			he->used = false;
1100*aca7a94dSNamhyung Kim 			/*
1101*aca7a94dSNamhyung Kim 			 * offer option to annotate the other branch source or target
1102*aca7a94dSNamhyung Kim 			 * (if they exists) when returning from annotate
1103*aca7a94dSNamhyung Kim 			 */
1104*aca7a94dSNamhyung Kim 			if ((err == 'q' || err == CTRL('c'))
1105*aca7a94dSNamhyung Kim 			    && annotate_t != -2 && annotate_f != -2)
1106*aca7a94dSNamhyung Kim 				goto retry_popup_menu;
1107*aca7a94dSNamhyung Kim 
1108*aca7a94dSNamhyung Kim 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1109*aca7a94dSNamhyung Kim 			if (err)
1110*aca7a94dSNamhyung Kim 				ui_browser__handle_resize(&browser->b);
1111*aca7a94dSNamhyung Kim 
1112*aca7a94dSNamhyung Kim 		} else if (choice == browse_map)
1113*aca7a94dSNamhyung Kim 			map__browse(browser->selection->map);
1114*aca7a94dSNamhyung Kim 		else if (choice == zoom_dso) {
1115*aca7a94dSNamhyung Kim zoom_dso:
1116*aca7a94dSNamhyung Kim 			if (browser->hists->dso_filter) {
1117*aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->dso_filter);
1118*aca7a94dSNamhyung Kim zoom_out_dso:
1119*aca7a94dSNamhyung Kim 				ui_helpline__pop();
1120*aca7a94dSNamhyung Kim 				browser->hists->dso_filter = NULL;
1121*aca7a94dSNamhyung Kim 				sort_dso.elide = false;
1122*aca7a94dSNamhyung Kim 			} else {
1123*aca7a94dSNamhyung Kim 				if (dso == NULL)
1124*aca7a94dSNamhyung Kim 					continue;
1125*aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1126*aca7a94dSNamhyung Kim 						   dso->kernel ? "the Kernel" : dso->short_name);
1127*aca7a94dSNamhyung Kim 				browser->hists->dso_filter = dso;
1128*aca7a94dSNamhyung Kim 				sort_dso.elide = true;
1129*aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->dso_filter);
1130*aca7a94dSNamhyung Kim 			}
1131*aca7a94dSNamhyung Kim 			hists__filter_by_dso(self);
1132*aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1133*aca7a94dSNamhyung Kim 		} else if (choice == zoom_thread) {
1134*aca7a94dSNamhyung Kim zoom_thread:
1135*aca7a94dSNamhyung Kim 			if (browser->hists->thread_filter) {
1136*aca7a94dSNamhyung Kim 				pstack__remove(fstack, &browser->hists->thread_filter);
1137*aca7a94dSNamhyung Kim zoom_out_thread:
1138*aca7a94dSNamhyung Kim 				ui_helpline__pop();
1139*aca7a94dSNamhyung Kim 				browser->hists->thread_filter = NULL;
1140*aca7a94dSNamhyung Kim 				sort_thread.elide = false;
1141*aca7a94dSNamhyung Kim 			} else {
1142*aca7a94dSNamhyung Kim 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1143*aca7a94dSNamhyung Kim 						   thread->comm_set ? thread->comm : "",
1144*aca7a94dSNamhyung Kim 						   thread->pid);
1145*aca7a94dSNamhyung Kim 				browser->hists->thread_filter = thread;
1146*aca7a94dSNamhyung Kim 				sort_thread.elide = true;
1147*aca7a94dSNamhyung Kim 				pstack__push(fstack, &browser->hists->thread_filter);
1148*aca7a94dSNamhyung Kim 			}
1149*aca7a94dSNamhyung Kim 			hists__filter_by_thread(self);
1150*aca7a94dSNamhyung Kim 			hist_browser__reset(browser);
1151*aca7a94dSNamhyung Kim 		}
1152*aca7a94dSNamhyung Kim 	}
1153*aca7a94dSNamhyung Kim out_free_stack:
1154*aca7a94dSNamhyung Kim 	pstack__delete(fstack);
1155*aca7a94dSNamhyung Kim out:
1156*aca7a94dSNamhyung Kim 	hist_browser__delete(browser);
1157*aca7a94dSNamhyung Kim 	free_popup_options(options, nr_options - 1);
1158*aca7a94dSNamhyung Kim 	return key;
1159*aca7a94dSNamhyung Kim }
1160*aca7a94dSNamhyung Kim 
1161*aca7a94dSNamhyung Kim struct perf_evsel_menu {
1162*aca7a94dSNamhyung Kim 	struct ui_browser b;
1163*aca7a94dSNamhyung Kim 	struct perf_evsel *selection;
1164*aca7a94dSNamhyung Kim 	bool lost_events, lost_events_warned;
1165*aca7a94dSNamhyung Kim };
1166*aca7a94dSNamhyung Kim 
1167*aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser,
1168*aca7a94dSNamhyung Kim 				   void *entry, int row)
1169*aca7a94dSNamhyung Kim {
1170*aca7a94dSNamhyung Kim 	struct perf_evsel_menu *menu = container_of(browser,
1171*aca7a94dSNamhyung Kim 						    struct perf_evsel_menu, b);
1172*aca7a94dSNamhyung Kim 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1173*aca7a94dSNamhyung Kim 	bool current_entry = ui_browser__is_current_entry(browser, row);
1174*aca7a94dSNamhyung Kim 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1175*aca7a94dSNamhyung Kim 	const char *ev_name = event_name(evsel);
1176*aca7a94dSNamhyung Kim 	char bf[256], unit;
1177*aca7a94dSNamhyung Kim 	const char *warn = " ";
1178*aca7a94dSNamhyung Kim 	size_t printed;
1179*aca7a94dSNamhyung Kim 
1180*aca7a94dSNamhyung Kim 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1181*aca7a94dSNamhyung Kim 						       HE_COLORSET_NORMAL);
1182*aca7a94dSNamhyung Kim 
1183*aca7a94dSNamhyung Kim 	nr_events = convert_unit(nr_events, &unit);
1184*aca7a94dSNamhyung Kim 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1185*aca7a94dSNamhyung Kim 			   unit, unit == ' ' ? "" : " ", ev_name);
1186*aca7a94dSNamhyung Kim 	slsmg_printf("%s", bf);
1187*aca7a94dSNamhyung Kim 
1188*aca7a94dSNamhyung Kim 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1189*aca7a94dSNamhyung Kim 	if (nr_events != 0) {
1190*aca7a94dSNamhyung Kim 		menu->lost_events = true;
1191*aca7a94dSNamhyung Kim 		if (!current_entry)
1192*aca7a94dSNamhyung Kim 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1193*aca7a94dSNamhyung Kim 		nr_events = convert_unit(nr_events, &unit);
1194*aca7a94dSNamhyung Kim 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1195*aca7a94dSNamhyung Kim 				     nr_events, unit, unit == ' ' ? "" : " ");
1196*aca7a94dSNamhyung Kim 		warn = bf;
1197*aca7a94dSNamhyung Kim 	}
1198*aca7a94dSNamhyung Kim 
1199*aca7a94dSNamhyung Kim 	slsmg_write_nstring(warn, browser->width - printed);
1200*aca7a94dSNamhyung Kim 
1201*aca7a94dSNamhyung Kim 	if (current_entry)
1202*aca7a94dSNamhyung Kim 		menu->selection = evsel;
1203*aca7a94dSNamhyung Kim }
1204*aca7a94dSNamhyung Kim 
1205*aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1206*aca7a94dSNamhyung Kim 				int nr_events, const char *help,
1207*aca7a94dSNamhyung Kim 				void(*timer)(void *arg), void *arg, int delay_secs)
1208*aca7a94dSNamhyung Kim {
1209*aca7a94dSNamhyung Kim 	struct perf_evlist *evlist = menu->b.priv;
1210*aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1211*aca7a94dSNamhyung Kim 	const char *ev_name, *title = "Available samples";
1212*aca7a94dSNamhyung Kim 	int key;
1213*aca7a94dSNamhyung Kim 
1214*aca7a94dSNamhyung Kim 	if (ui_browser__show(&menu->b, title,
1215*aca7a94dSNamhyung Kim 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1216*aca7a94dSNamhyung Kim 		return -1;
1217*aca7a94dSNamhyung Kim 
1218*aca7a94dSNamhyung Kim 	while (1) {
1219*aca7a94dSNamhyung Kim 		key = ui_browser__run(&menu->b, delay_secs);
1220*aca7a94dSNamhyung Kim 
1221*aca7a94dSNamhyung Kim 		switch (key) {
1222*aca7a94dSNamhyung Kim 		case K_TIMER:
1223*aca7a94dSNamhyung Kim 			timer(arg);
1224*aca7a94dSNamhyung Kim 
1225*aca7a94dSNamhyung Kim 			if (!menu->lost_events_warned && menu->lost_events) {
1226*aca7a94dSNamhyung Kim 				ui_browser__warn_lost_events(&menu->b);
1227*aca7a94dSNamhyung Kim 				menu->lost_events_warned = true;
1228*aca7a94dSNamhyung Kim 			}
1229*aca7a94dSNamhyung Kim 			continue;
1230*aca7a94dSNamhyung Kim 		case K_RIGHT:
1231*aca7a94dSNamhyung Kim 		case K_ENTER:
1232*aca7a94dSNamhyung Kim 			if (!menu->selection)
1233*aca7a94dSNamhyung Kim 				continue;
1234*aca7a94dSNamhyung Kim 			pos = menu->selection;
1235*aca7a94dSNamhyung Kim browse_hists:
1236*aca7a94dSNamhyung Kim 			perf_evlist__set_selected(evlist, pos);
1237*aca7a94dSNamhyung Kim 			/*
1238*aca7a94dSNamhyung Kim 			 * Give the calling tool a chance to populate the non
1239*aca7a94dSNamhyung Kim 			 * default evsel resorted hists tree.
1240*aca7a94dSNamhyung Kim 			 */
1241*aca7a94dSNamhyung Kim 			if (timer)
1242*aca7a94dSNamhyung Kim 				timer(arg);
1243*aca7a94dSNamhyung Kim 			ev_name = event_name(pos);
1244*aca7a94dSNamhyung Kim 			key = perf_evsel__hists_browse(pos, nr_events, help,
1245*aca7a94dSNamhyung Kim 						       ev_name, true, timer,
1246*aca7a94dSNamhyung Kim 						       arg, delay_secs);
1247*aca7a94dSNamhyung Kim 			ui_browser__show_title(&menu->b, title);
1248*aca7a94dSNamhyung Kim 			switch (key) {
1249*aca7a94dSNamhyung Kim 			case K_TAB:
1250*aca7a94dSNamhyung Kim 				if (pos->node.next == &evlist->entries)
1251*aca7a94dSNamhyung Kim 					pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1252*aca7a94dSNamhyung Kim 				else
1253*aca7a94dSNamhyung Kim 					pos = list_entry(pos->node.next, struct perf_evsel, node);
1254*aca7a94dSNamhyung Kim 				goto browse_hists;
1255*aca7a94dSNamhyung Kim 			case K_UNTAB:
1256*aca7a94dSNamhyung Kim 				if (pos->node.prev == &evlist->entries)
1257*aca7a94dSNamhyung Kim 					pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1258*aca7a94dSNamhyung Kim 				else
1259*aca7a94dSNamhyung Kim 					pos = list_entry(pos->node.prev, struct perf_evsel, node);
1260*aca7a94dSNamhyung Kim 				goto browse_hists;
1261*aca7a94dSNamhyung Kim 			case K_ESC:
1262*aca7a94dSNamhyung Kim 				if (!ui_browser__dialog_yesno(&menu->b,
1263*aca7a94dSNamhyung Kim 						"Do you really want to exit?"))
1264*aca7a94dSNamhyung Kim 					continue;
1265*aca7a94dSNamhyung Kim 				/* Fall thru */
1266*aca7a94dSNamhyung Kim 			case 'q':
1267*aca7a94dSNamhyung Kim 			case CTRL('c'):
1268*aca7a94dSNamhyung Kim 				goto out;
1269*aca7a94dSNamhyung Kim 			default:
1270*aca7a94dSNamhyung Kim 				continue;
1271*aca7a94dSNamhyung Kim 			}
1272*aca7a94dSNamhyung Kim 		case K_LEFT:
1273*aca7a94dSNamhyung Kim 			continue;
1274*aca7a94dSNamhyung Kim 		case K_ESC:
1275*aca7a94dSNamhyung Kim 			if (!ui_browser__dialog_yesno(&menu->b,
1276*aca7a94dSNamhyung Kim 					       "Do you really want to exit?"))
1277*aca7a94dSNamhyung Kim 				continue;
1278*aca7a94dSNamhyung Kim 			/* Fall thru */
1279*aca7a94dSNamhyung Kim 		case 'q':
1280*aca7a94dSNamhyung Kim 		case CTRL('c'):
1281*aca7a94dSNamhyung Kim 			goto out;
1282*aca7a94dSNamhyung Kim 		default:
1283*aca7a94dSNamhyung Kim 			continue;
1284*aca7a94dSNamhyung Kim 		}
1285*aca7a94dSNamhyung Kim 	}
1286*aca7a94dSNamhyung Kim 
1287*aca7a94dSNamhyung Kim out:
1288*aca7a94dSNamhyung Kim 	ui_browser__hide(&menu->b);
1289*aca7a94dSNamhyung Kim 	return key;
1290*aca7a94dSNamhyung Kim }
1291*aca7a94dSNamhyung Kim 
1292*aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1293*aca7a94dSNamhyung Kim 					   const char *help,
1294*aca7a94dSNamhyung Kim 					   void(*timer)(void *arg), void *arg,
1295*aca7a94dSNamhyung Kim 					   int delay_secs)
1296*aca7a94dSNamhyung Kim {
1297*aca7a94dSNamhyung Kim 	struct perf_evsel *pos;
1298*aca7a94dSNamhyung Kim 	struct perf_evsel_menu menu = {
1299*aca7a94dSNamhyung Kim 		.b = {
1300*aca7a94dSNamhyung Kim 			.entries    = &evlist->entries,
1301*aca7a94dSNamhyung Kim 			.refresh    = ui_browser__list_head_refresh,
1302*aca7a94dSNamhyung Kim 			.seek	    = ui_browser__list_head_seek,
1303*aca7a94dSNamhyung Kim 			.write	    = perf_evsel_menu__write,
1304*aca7a94dSNamhyung Kim 			.nr_entries = evlist->nr_entries,
1305*aca7a94dSNamhyung Kim 			.priv	    = evlist,
1306*aca7a94dSNamhyung Kim 		},
1307*aca7a94dSNamhyung Kim 	};
1308*aca7a94dSNamhyung Kim 
1309*aca7a94dSNamhyung Kim 	ui_helpline__push("Press ESC to exit");
1310*aca7a94dSNamhyung Kim 
1311*aca7a94dSNamhyung Kim 	list_for_each_entry(pos, &evlist->entries, node) {
1312*aca7a94dSNamhyung Kim 		const char *ev_name = event_name(pos);
1313*aca7a94dSNamhyung Kim 		size_t line_len = strlen(ev_name) + 7;
1314*aca7a94dSNamhyung Kim 
1315*aca7a94dSNamhyung Kim 		if (menu.b.width < line_len)
1316*aca7a94dSNamhyung Kim 			menu.b.width = line_len;
1317*aca7a94dSNamhyung Kim 		/*
1318*aca7a94dSNamhyung Kim 		 * Cache the evsel name, tracepoints have a _high_ cost per
1319*aca7a94dSNamhyung Kim 		 * event_name() call.
1320*aca7a94dSNamhyung Kim 		 */
1321*aca7a94dSNamhyung Kim 		if (pos->name == NULL)
1322*aca7a94dSNamhyung Kim 			pos->name = strdup(ev_name);
1323*aca7a94dSNamhyung Kim 	}
1324*aca7a94dSNamhyung Kim 
1325*aca7a94dSNamhyung Kim 	return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1326*aca7a94dSNamhyung Kim 				    arg, delay_secs);
1327*aca7a94dSNamhyung Kim }
1328*aca7a94dSNamhyung Kim 
1329*aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1330*aca7a94dSNamhyung Kim 				  void(*timer)(void *arg), void *arg,
1331*aca7a94dSNamhyung Kim 				  int delay_secs)
1332*aca7a94dSNamhyung Kim {
1333*aca7a94dSNamhyung Kim 
1334*aca7a94dSNamhyung Kim 	if (evlist->nr_entries == 1) {
1335*aca7a94dSNamhyung Kim 		struct perf_evsel *first = list_entry(evlist->entries.next,
1336*aca7a94dSNamhyung Kim 						      struct perf_evsel, node);
1337*aca7a94dSNamhyung Kim 		const char *ev_name = event_name(first);
1338*aca7a94dSNamhyung Kim 		return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1339*aca7a94dSNamhyung Kim 						ev_name, false, timer, arg,
1340*aca7a94dSNamhyung Kim 						delay_secs);
1341*aca7a94dSNamhyung Kim 	}
1342*aca7a94dSNamhyung Kim 
1343*aca7a94dSNamhyung Kim 	return __perf_evlist__tui_browse_hists(evlist, help,
1344*aca7a94dSNamhyung Kim 					       timer, arg, delay_secs);
1345*aca7a94dSNamhyung Kim }
1346