xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 861e10be)
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <newt.h>
6 #include <linux/rbtree.h>
7 
8 #include "../../util/evsel.h"
9 #include "../../util/evlist.h"
10 #include "../../util/hist.h"
11 #include "../../util/pstack.h"
12 #include "../../util/sort.h"
13 #include "../../util/util.h"
14 #include "../../arch/common.h"
15 
16 #include "../browser.h"
17 #include "../helpline.h"
18 #include "../util.h"
19 #include "../ui.h"
20 #include "map.h"
21 
22 struct hist_browser {
23 	struct ui_browser   b;
24 	struct hists	    *hists;
25 	struct hist_entry   *he_selection;
26 	struct map_symbol   *selection;
27 	int		     print_seq;
28 	bool		     show_dso;
29 	bool		     has_symbols;
30 };
31 
32 extern void hist_browser__init_hpp(void);
33 
34 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
35 				const char *ev_name);
36 
37 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
38 {
39 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
40 	browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
41 			     sizeof("[k]"));
42 }
43 
44 static void hist_browser__reset(struct hist_browser *browser)
45 {
46 	browser->b.nr_entries = browser->hists->nr_entries;
47 	hist_browser__refresh_dimensions(browser);
48 	ui_browser__reset_index(&browser->b);
49 }
50 
51 static char tree__folded_sign(bool unfolded)
52 {
53 	return unfolded ? '-' : '+';
54 }
55 
56 static char map_symbol__folded(const struct map_symbol *ms)
57 {
58 	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
59 }
60 
61 static char hist_entry__folded(const struct hist_entry *he)
62 {
63 	return map_symbol__folded(&he->ms);
64 }
65 
66 static char callchain_list__folded(const struct callchain_list *cl)
67 {
68 	return map_symbol__folded(&cl->ms);
69 }
70 
71 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
72 {
73 	ms->unfolded = unfold ? ms->has_children : false;
74 }
75 
76 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
77 {
78 	int n = 0;
79 	struct rb_node *nd;
80 
81 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
82 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
83 		struct callchain_list *chain;
84 		char folded_sign = ' '; /* No children */
85 
86 		list_for_each_entry(chain, &child->val, list) {
87 			++n;
88 			/* We need this because we may not have children */
89 			folded_sign = callchain_list__folded(chain);
90 			if (folded_sign == '+')
91 				break;
92 		}
93 
94 		if (folded_sign == '-') /* Have children and they're unfolded */
95 			n += callchain_node__count_rows_rb_tree(child);
96 	}
97 
98 	return n;
99 }
100 
101 static int callchain_node__count_rows(struct callchain_node *node)
102 {
103 	struct callchain_list *chain;
104 	bool unfolded = false;
105 	int n = 0;
106 
107 	list_for_each_entry(chain, &node->val, list) {
108 		++n;
109 		unfolded = chain->ms.unfolded;
110 	}
111 
112 	if (unfolded)
113 		n += callchain_node__count_rows_rb_tree(node);
114 
115 	return n;
116 }
117 
118 static int callchain__count_rows(struct rb_root *chain)
119 {
120 	struct rb_node *nd;
121 	int n = 0;
122 
123 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
124 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
125 		n += callchain_node__count_rows(node);
126 	}
127 
128 	return n;
129 }
130 
131 static bool map_symbol__toggle_fold(struct map_symbol *ms)
132 {
133 	if (!ms)
134 		return false;
135 
136 	if (!ms->has_children)
137 		return false;
138 
139 	ms->unfolded = !ms->unfolded;
140 	return true;
141 }
142 
143 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
144 {
145 	struct rb_node *nd = rb_first(&node->rb_root);
146 
147 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
148 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149 		struct callchain_list *chain;
150 		bool first = true;
151 
152 		list_for_each_entry(chain, &child->val, list) {
153 			if (first) {
154 				first = false;
155 				chain->ms.has_children = chain->list.next != &child->val ||
156 							 !RB_EMPTY_ROOT(&child->rb_root);
157 			} else
158 				chain->ms.has_children = chain->list.next == &child->val &&
159 							 !RB_EMPTY_ROOT(&child->rb_root);
160 		}
161 
162 		callchain_node__init_have_children_rb_tree(child);
163 	}
164 }
165 
166 static void callchain_node__init_have_children(struct callchain_node *node)
167 {
168 	struct callchain_list *chain;
169 
170 	list_for_each_entry(chain, &node->val, list)
171 		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
172 
173 	callchain_node__init_have_children_rb_tree(node);
174 }
175 
176 static void callchain__init_have_children(struct rb_root *root)
177 {
178 	struct rb_node *nd;
179 
180 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
181 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
182 		callchain_node__init_have_children(node);
183 	}
184 }
185 
186 static void hist_entry__init_have_children(struct hist_entry *he)
187 {
188 	if (!he->init_have_children) {
189 		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
190 		callchain__init_have_children(&he->sorted_chain);
191 		he->init_have_children = true;
192 	}
193 }
194 
195 static bool hist_browser__toggle_fold(struct hist_browser *browser)
196 {
197 	if (map_symbol__toggle_fold(browser->selection)) {
198 		struct hist_entry *he = browser->he_selection;
199 
200 		hist_entry__init_have_children(he);
201 		browser->hists->nr_entries -= he->nr_rows;
202 
203 		if (he->ms.unfolded)
204 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
205 		else
206 			he->nr_rows = 0;
207 		browser->hists->nr_entries += he->nr_rows;
208 		browser->b.nr_entries = browser->hists->nr_entries;
209 
210 		return true;
211 	}
212 
213 	/* If it doesn't have children, no toggling performed */
214 	return false;
215 }
216 
217 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
218 {
219 	int n = 0;
220 	struct rb_node *nd;
221 
222 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
223 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
224 		struct callchain_list *chain;
225 		bool has_children = false;
226 
227 		list_for_each_entry(chain, &child->val, list) {
228 			++n;
229 			map_symbol__set_folding(&chain->ms, unfold);
230 			has_children = chain->ms.has_children;
231 		}
232 
233 		if (has_children)
234 			n += callchain_node__set_folding_rb_tree(child, unfold);
235 	}
236 
237 	return n;
238 }
239 
240 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
241 {
242 	struct callchain_list *chain;
243 	bool has_children = false;
244 	int n = 0;
245 
246 	list_for_each_entry(chain, &node->val, list) {
247 		++n;
248 		map_symbol__set_folding(&chain->ms, unfold);
249 		has_children = chain->ms.has_children;
250 	}
251 
252 	if (has_children)
253 		n += callchain_node__set_folding_rb_tree(node, unfold);
254 
255 	return n;
256 }
257 
258 static int callchain__set_folding(struct rb_root *chain, bool unfold)
259 {
260 	struct rb_node *nd;
261 	int n = 0;
262 
263 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
264 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
265 		n += callchain_node__set_folding(node, unfold);
266 	}
267 
268 	return n;
269 }
270 
271 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
272 {
273 	hist_entry__init_have_children(he);
274 	map_symbol__set_folding(&he->ms, unfold);
275 
276 	if (he->ms.has_children) {
277 		int n = callchain__set_folding(&he->sorted_chain, unfold);
278 		he->nr_rows = unfold ? n : 0;
279 	} else
280 		he->nr_rows = 0;
281 }
282 
283 static void hists__set_folding(struct hists *hists, bool unfold)
284 {
285 	struct rb_node *nd;
286 
287 	hists->nr_entries = 0;
288 
289 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
290 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291 		hist_entry__set_folding(he, unfold);
292 		hists->nr_entries += 1 + he->nr_rows;
293 	}
294 }
295 
296 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
297 {
298 	hists__set_folding(browser->hists, unfold);
299 	browser->b.nr_entries = browser->hists->nr_entries;
300 	/* Go to the start, we may be way after valid entries after a collapse */
301 	ui_browser__reset_index(&browser->b);
302 }
303 
304 static void ui_browser__warn_lost_events(struct ui_browser *browser)
305 {
306 	ui_browser__warning(browser, 4,
307 		"Events are being lost, check IO/CPU overload!\n\n"
308 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
309 		" perf top -r 80\n\n"
310 		"Or reduce the sampling frequency.");
311 }
312 
313 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
314 			     struct hist_browser_timer *hbt)
315 {
316 	int key;
317 	char title[160];
318 	int delay_secs = hbt ? hbt->refresh : 0;
319 
320 	browser->b.entries = &browser->hists->entries;
321 	browser->b.nr_entries = browser->hists->nr_entries;
322 
323 	hist_browser__refresh_dimensions(browser);
324 	hists__browser_title(browser->hists, title, sizeof(title), ev_name);
325 
326 	if (ui_browser__show(&browser->b, title,
327 			     "Press '?' for help on key bindings") < 0)
328 		return -1;
329 
330 	while (1) {
331 		key = ui_browser__run(&browser->b, delay_secs);
332 
333 		switch (key) {
334 		case K_TIMER:
335 			hbt->timer(hbt->arg);
336 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
337 
338 			if (browser->hists->stats.nr_lost_warned !=
339 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
340 				browser->hists->stats.nr_lost_warned =
341 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
342 				ui_browser__warn_lost_events(&browser->b);
343 			}
344 
345 			hists__browser_title(browser->hists, title, sizeof(title), ev_name);
346 			ui_browser__show_title(&browser->b, title);
347 			continue;
348 		case 'D': { /* Debug */
349 			static int seq;
350 			struct hist_entry *h = rb_entry(browser->b.top,
351 							struct hist_entry, rb_node);
352 			ui_helpline__pop();
353 			ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
354 					   seq++, browser->b.nr_entries,
355 					   browser->hists->nr_entries,
356 					   browser->b.height,
357 					   browser->b.index,
358 					   browser->b.top_idx,
359 					   h->row_offset, h->nr_rows);
360 		}
361 			break;
362 		case 'C':
363 			/* Collapse the whole world. */
364 			hist_browser__set_folding(browser, false);
365 			break;
366 		case 'E':
367 			/* Expand the whole world. */
368 			hist_browser__set_folding(browser, true);
369 			break;
370 		case K_ENTER:
371 			if (hist_browser__toggle_fold(browser))
372 				break;
373 			/* fall thru */
374 		default:
375 			goto out;
376 		}
377 	}
378 out:
379 	ui_browser__hide(&browser->b);
380 	return key;
381 }
382 
383 static char *callchain_list__sym_name(struct callchain_list *cl,
384 				      char *bf, size_t bfsize, bool show_dso)
385 {
386 	int printed;
387 
388 	if (cl->ms.sym)
389 		printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
390 	else
391 		printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
392 
393 	if (show_dso)
394 		scnprintf(bf + printed, bfsize - printed, " %s",
395 			  cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
396 
397 	return bf;
398 }
399 
400 #define LEVEL_OFFSET_STEP 3
401 
402 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
403 						     struct callchain_node *chain_node,
404 						     u64 total, int level,
405 						     unsigned short row,
406 						     off_t *row_offset,
407 						     bool *is_current_entry)
408 {
409 	struct rb_node *node;
410 	int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
411 	u64 new_total, remaining;
412 
413 	if (callchain_param.mode == CHAIN_GRAPH_REL)
414 		new_total = chain_node->children_hit;
415 	else
416 		new_total = total;
417 
418 	remaining = new_total;
419 	node = rb_first(&chain_node->rb_root);
420 	while (node) {
421 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
422 		struct rb_node *next = rb_next(node);
423 		u64 cumul = callchain_cumul_hits(child);
424 		struct callchain_list *chain;
425 		char folded_sign = ' ';
426 		int first = true;
427 		int extra_offset = 0;
428 
429 		remaining -= cumul;
430 
431 		list_for_each_entry(chain, &child->val, list) {
432 			char bf[1024], *alloc_str;
433 			const char *str;
434 			int color;
435 			bool was_first = first;
436 
437 			if (first)
438 				first = false;
439 			else
440 				extra_offset = LEVEL_OFFSET_STEP;
441 
442 			folded_sign = callchain_list__folded(chain);
443 			if (*row_offset != 0) {
444 				--*row_offset;
445 				goto do_next;
446 			}
447 
448 			alloc_str = NULL;
449 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
450 						       browser->show_dso);
451 			if (was_first) {
452 				double percent = cumul * 100.0 / new_total;
453 
454 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
455 					str = "Not enough memory!";
456 				else
457 					str = alloc_str;
458 			}
459 
460 			color = HE_COLORSET_NORMAL;
461 			width = browser->b.width - (offset + extra_offset + 2);
462 			if (ui_browser__is_current_entry(&browser->b, row)) {
463 				browser->selection = &chain->ms;
464 				color = HE_COLORSET_SELECTED;
465 				*is_current_entry = true;
466 			}
467 
468 			ui_browser__set_color(&browser->b, color);
469 			ui_browser__gotorc(&browser->b, row, 0);
470 			slsmg_write_nstring(" ", offset + extra_offset);
471 			slsmg_printf("%c ", folded_sign);
472 			slsmg_write_nstring(str, width);
473 			free(alloc_str);
474 
475 			if (++row == browser->b.height)
476 				goto out;
477 do_next:
478 			if (folded_sign == '+')
479 				break;
480 		}
481 
482 		if (folded_sign == '-') {
483 			const int new_level = level + (extra_offset ? 2 : 1);
484 			row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
485 									 new_level, row, row_offset,
486 									 is_current_entry);
487 		}
488 		if (row == browser->b.height)
489 			goto out;
490 		node = next;
491 	}
492 out:
493 	return row - first_row;
494 }
495 
496 static int hist_browser__show_callchain_node(struct hist_browser *browser,
497 					     struct callchain_node *node,
498 					     int level, unsigned short row,
499 					     off_t *row_offset,
500 					     bool *is_current_entry)
501 {
502 	struct callchain_list *chain;
503 	int first_row = row,
504 	     offset = level * LEVEL_OFFSET_STEP,
505 	     width = browser->b.width - offset;
506 	char folded_sign = ' ';
507 
508 	list_for_each_entry(chain, &node->val, list) {
509 		char bf[1024], *s;
510 		int color;
511 
512 		folded_sign = callchain_list__folded(chain);
513 
514 		if (*row_offset != 0) {
515 			--*row_offset;
516 			continue;
517 		}
518 
519 		color = HE_COLORSET_NORMAL;
520 		if (ui_browser__is_current_entry(&browser->b, row)) {
521 			browser->selection = &chain->ms;
522 			color = HE_COLORSET_SELECTED;
523 			*is_current_entry = true;
524 		}
525 
526 		s = callchain_list__sym_name(chain, bf, sizeof(bf),
527 					     browser->show_dso);
528 		ui_browser__gotorc(&browser->b, row, 0);
529 		ui_browser__set_color(&browser->b, color);
530 		slsmg_write_nstring(" ", offset);
531 		slsmg_printf("%c ", folded_sign);
532 		slsmg_write_nstring(s, width - 2);
533 
534 		if (++row == browser->b.height)
535 			goto out;
536 	}
537 
538 	if (folded_sign == '-')
539 		row += hist_browser__show_callchain_node_rb_tree(browser, node,
540 								 browser->hists->stats.total_period,
541 								 level + 1, row,
542 								 row_offset,
543 								 is_current_entry);
544 out:
545 	return row - first_row;
546 }
547 
548 static int hist_browser__show_callchain(struct hist_browser *browser,
549 					struct rb_root *chain,
550 					int level, unsigned short row,
551 					off_t *row_offset,
552 					bool *is_current_entry)
553 {
554 	struct rb_node *nd;
555 	int first_row = row;
556 
557 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
558 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
559 
560 		row += hist_browser__show_callchain_node(browser, node, level,
561 							 row, row_offset,
562 							 is_current_entry);
563 		if (row == browser->b.height)
564 			break;
565 	}
566 
567 	return row - first_row;
568 }
569 
570 struct hpp_arg {
571 	struct ui_browser *b;
572 	char folded_sign;
573 	bool current_entry;
574 };
575 
576 static int __hpp__color_callchain(struct hpp_arg *arg)
577 {
578 	if (!symbol_conf.use_callchain)
579 		return 0;
580 
581 	slsmg_printf("%c ", arg->folded_sign);
582 	return 2;
583 }
584 
585 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
586 			    u64 (*get_field)(struct hist_entry *),
587 			    int (*callchain_cb)(struct hpp_arg *))
588 {
589 	int ret = 0;
590 	double percent = 0.0;
591 	struct hists *hists = he->hists;
592 	struct hpp_arg *arg = hpp->ptr;
593 
594 	if (hists->stats.total_period)
595 		percent = 100.0 * get_field(he) / hists->stats.total_period;
596 
597 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
598 
599 	if (callchain_cb)
600 		ret += callchain_cb(arg);
601 
602 	ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
603 	slsmg_printf("%s", hpp->buf);
604 
605 	if (symbol_conf.event_group) {
606 		int prev_idx, idx_delta;
607 		struct perf_evsel *evsel = hists_to_evsel(hists);
608 		struct hist_entry *pair;
609 		int nr_members = evsel->nr_members;
610 
611 		if (nr_members <= 1)
612 			goto out;
613 
614 		prev_idx = perf_evsel__group_idx(evsel);
615 
616 		list_for_each_entry(pair, &he->pairs.head, pairs.node) {
617 			u64 period = get_field(pair);
618 			u64 total = pair->hists->stats.total_period;
619 
620 			if (!total)
621 				continue;
622 
623 			evsel = hists_to_evsel(pair->hists);
624 			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
625 
626 			while (idx_delta--) {
627 				/*
628 				 * zero-fill group members in the middle which
629 				 * have no sample
630 				 */
631 				ui_browser__set_percent_color(arg->b, 0.0,
632 							arg->current_entry);
633 				ret += scnprintf(hpp->buf, hpp->size,
634 						 " %6.2f%%", 0.0);
635 				slsmg_printf("%s", hpp->buf);
636 			}
637 
638 			percent = 100.0 * period / total;
639 			ui_browser__set_percent_color(arg->b, percent,
640 						      arg->current_entry);
641 			ret += scnprintf(hpp->buf, hpp->size,
642 					 " %6.2f%%", percent);
643 			slsmg_printf("%s", hpp->buf);
644 
645 			prev_idx = perf_evsel__group_idx(evsel);
646 		}
647 
648 		idx_delta = nr_members - prev_idx - 1;
649 
650 		while (idx_delta--) {
651 			/*
652 			 * zero-fill group members at last which have no sample
653 			 */
654 			ui_browser__set_percent_color(arg->b, 0.0,
655 						      arg->current_entry);
656 			ret += scnprintf(hpp->buf, hpp->size,
657 					 " %6.2f%%", 0.0);
658 			slsmg_printf("%s", hpp->buf);
659 		}
660 	}
661 out:
662 	if (!arg->current_entry || !arg->b->navkeypressed)
663 		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
664 
665 	return ret;
666 }
667 
668 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)			\
669 static u64 __hpp_get_##_field(struct hist_entry *he)			\
670 {									\
671 	return he->stat._field;						\
672 }									\
673 									\
674 static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp,	\
675 					   struct hist_entry *he)	\
676 {									\
677 	return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);	\
678 }
679 
680 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
681 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
682 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
683 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
684 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
685 
686 #undef __HPP_COLOR_PERCENT_FN
687 
688 void hist_browser__init_hpp(void)
689 {
690 	perf_hpp__column_enable(PERF_HPP__OVERHEAD);
691 
692 	perf_hpp__init();
693 
694 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
695 				hist_browser__hpp_color_overhead;
696 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
697 				hist_browser__hpp_color_overhead_sys;
698 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
699 				hist_browser__hpp_color_overhead_us;
700 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
701 				hist_browser__hpp_color_overhead_guest_sys;
702 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
703 				hist_browser__hpp_color_overhead_guest_us;
704 }
705 
706 static int hist_browser__show_entry(struct hist_browser *browser,
707 				    struct hist_entry *entry,
708 				    unsigned short row)
709 {
710 	char s[256];
711 	int printed = 0;
712 	int width = browser->b.width;
713 	char folded_sign = ' ';
714 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
715 	off_t row_offset = entry->row_offset;
716 	bool first = true;
717 	struct perf_hpp_fmt *fmt;
718 
719 	if (current_entry) {
720 		browser->he_selection = entry;
721 		browser->selection = &entry->ms;
722 	}
723 
724 	if (symbol_conf.use_callchain) {
725 		hist_entry__init_have_children(entry);
726 		folded_sign = hist_entry__folded(entry);
727 	}
728 
729 	if (row_offset == 0) {
730 		struct hpp_arg arg = {
731 			.b 		= &browser->b,
732 			.folded_sign	= folded_sign,
733 			.current_entry	= current_entry,
734 		};
735 		struct perf_hpp hpp = {
736 			.buf		= s,
737 			.size		= sizeof(s),
738 			.ptr		= &arg,
739 		};
740 
741 		ui_browser__gotorc(&browser->b, row, 0);
742 
743 		perf_hpp__for_each_format(fmt) {
744 			if (!first) {
745 				slsmg_printf("  ");
746 				width -= 2;
747 			}
748 			first = false;
749 
750 			if (fmt->color) {
751 				width -= fmt->color(&hpp, entry);
752 			} else {
753 				width -= fmt->entry(&hpp, entry);
754 				slsmg_printf("%s", s);
755 			}
756 		}
757 
758 		/* The scroll bar isn't being used */
759 		if (!browser->b.navkeypressed)
760 			width += 1;
761 
762 		hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
763 		slsmg_write_nstring(s, width);
764 		++row;
765 		++printed;
766 	} else
767 		--row_offset;
768 
769 	if (folded_sign == '-' && row != browser->b.height) {
770 		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
771 							1, row, &row_offset,
772 							&current_entry);
773 		if (current_entry)
774 			browser->he_selection = entry;
775 	}
776 
777 	return printed;
778 }
779 
780 static void ui_browser__hists_init_top(struct ui_browser *browser)
781 {
782 	if (browser->top == NULL) {
783 		struct hist_browser *hb;
784 
785 		hb = container_of(browser, struct hist_browser, b);
786 		browser->top = rb_first(&hb->hists->entries);
787 	}
788 }
789 
790 static unsigned int hist_browser__refresh(struct ui_browser *browser)
791 {
792 	unsigned row = 0;
793 	struct rb_node *nd;
794 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
795 
796 	ui_browser__hists_init_top(browser);
797 
798 	for (nd = browser->top; nd; nd = rb_next(nd)) {
799 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
800 
801 		if (h->filtered)
802 			continue;
803 
804 		row += hist_browser__show_entry(hb, h, row);
805 		if (row == browser->height)
806 			break;
807 	}
808 
809 	return row;
810 }
811 
812 static struct rb_node *hists__filter_entries(struct rb_node *nd)
813 {
814 	while (nd != NULL) {
815 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
816 		if (!h->filtered)
817 			return nd;
818 
819 		nd = rb_next(nd);
820 	}
821 
822 	return NULL;
823 }
824 
825 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
826 {
827 	while (nd != NULL) {
828 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
829 		if (!h->filtered)
830 			return nd;
831 
832 		nd = rb_prev(nd);
833 	}
834 
835 	return NULL;
836 }
837 
838 static void ui_browser__hists_seek(struct ui_browser *browser,
839 				   off_t offset, int whence)
840 {
841 	struct hist_entry *h;
842 	struct rb_node *nd;
843 	bool first = true;
844 
845 	if (browser->nr_entries == 0)
846 		return;
847 
848 	ui_browser__hists_init_top(browser);
849 
850 	switch (whence) {
851 	case SEEK_SET:
852 		nd = hists__filter_entries(rb_first(browser->entries));
853 		break;
854 	case SEEK_CUR:
855 		nd = browser->top;
856 		goto do_offset;
857 	case SEEK_END:
858 		nd = hists__filter_prev_entries(rb_last(browser->entries));
859 		first = false;
860 		break;
861 	default:
862 		return;
863 	}
864 
865 	/*
866 	 * Moves not relative to the first visible entry invalidates its
867 	 * row_offset:
868 	 */
869 	h = rb_entry(browser->top, struct hist_entry, rb_node);
870 	h->row_offset = 0;
871 
872 	/*
873 	 * Here we have to check if nd is expanded (+), if it is we can't go
874 	 * the next top level hist_entry, instead we must compute an offset of
875 	 * what _not_ to show and not change the first visible entry.
876 	 *
877 	 * This offset increments when we are going from top to bottom and
878 	 * decreases when we're going from bottom to top.
879 	 *
880 	 * As we don't have backpointers to the top level in the callchains
881 	 * structure, we need to always print the whole hist_entry callchain,
882 	 * skipping the first ones that are before the first visible entry
883 	 * and stop when we printed enough lines to fill the screen.
884 	 */
885 do_offset:
886 	if (offset > 0) {
887 		do {
888 			h = rb_entry(nd, struct hist_entry, rb_node);
889 			if (h->ms.unfolded) {
890 				u16 remaining = h->nr_rows - h->row_offset;
891 				if (offset > remaining) {
892 					offset -= remaining;
893 					h->row_offset = 0;
894 				} else {
895 					h->row_offset += offset;
896 					offset = 0;
897 					browser->top = nd;
898 					break;
899 				}
900 			}
901 			nd = hists__filter_entries(rb_next(nd));
902 			if (nd == NULL)
903 				break;
904 			--offset;
905 			browser->top = nd;
906 		} while (offset != 0);
907 	} else if (offset < 0) {
908 		while (1) {
909 			h = rb_entry(nd, struct hist_entry, rb_node);
910 			if (h->ms.unfolded) {
911 				if (first) {
912 					if (-offset > h->row_offset) {
913 						offset += h->row_offset;
914 						h->row_offset = 0;
915 					} else {
916 						h->row_offset += offset;
917 						offset = 0;
918 						browser->top = nd;
919 						break;
920 					}
921 				} else {
922 					if (-offset > h->nr_rows) {
923 						offset += h->nr_rows;
924 						h->row_offset = 0;
925 					} else {
926 						h->row_offset = h->nr_rows + offset;
927 						offset = 0;
928 						browser->top = nd;
929 						break;
930 					}
931 				}
932 			}
933 
934 			nd = hists__filter_prev_entries(rb_prev(nd));
935 			if (nd == NULL)
936 				break;
937 			++offset;
938 			browser->top = nd;
939 			if (offset == 0) {
940 				/*
941 				 * Last unfiltered hist_entry, check if it is
942 				 * unfolded, if it is then we should have
943 				 * row_offset at its last entry.
944 				 */
945 				h = rb_entry(nd, struct hist_entry, rb_node);
946 				if (h->ms.unfolded)
947 					h->row_offset = h->nr_rows;
948 				break;
949 			}
950 			first = false;
951 		}
952 	} else {
953 		browser->top = nd;
954 		h = rb_entry(nd, struct hist_entry, rb_node);
955 		h->row_offset = 0;
956 	}
957 }
958 
959 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
960 							struct callchain_node *chain_node,
961 							u64 total, int level,
962 							FILE *fp)
963 {
964 	struct rb_node *node;
965 	int offset = level * LEVEL_OFFSET_STEP;
966 	u64 new_total, remaining;
967 	int printed = 0;
968 
969 	if (callchain_param.mode == CHAIN_GRAPH_REL)
970 		new_total = chain_node->children_hit;
971 	else
972 		new_total = total;
973 
974 	remaining = new_total;
975 	node = rb_first(&chain_node->rb_root);
976 	while (node) {
977 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
978 		struct rb_node *next = rb_next(node);
979 		u64 cumul = callchain_cumul_hits(child);
980 		struct callchain_list *chain;
981 		char folded_sign = ' ';
982 		int first = true;
983 		int extra_offset = 0;
984 
985 		remaining -= cumul;
986 
987 		list_for_each_entry(chain, &child->val, list) {
988 			char bf[1024], *alloc_str;
989 			const char *str;
990 			bool was_first = first;
991 
992 			if (first)
993 				first = false;
994 			else
995 				extra_offset = LEVEL_OFFSET_STEP;
996 
997 			folded_sign = callchain_list__folded(chain);
998 
999 			alloc_str = NULL;
1000 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
1001 						       browser->show_dso);
1002 			if (was_first) {
1003 				double percent = cumul * 100.0 / new_total;
1004 
1005 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1006 					str = "Not enough memory!";
1007 				else
1008 					str = alloc_str;
1009 			}
1010 
1011 			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1012 			free(alloc_str);
1013 			if (folded_sign == '+')
1014 				break;
1015 		}
1016 
1017 		if (folded_sign == '-') {
1018 			const int new_level = level + (extra_offset ? 2 : 1);
1019 			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1020 										new_level, fp);
1021 		}
1022 
1023 		node = next;
1024 	}
1025 
1026 	return printed;
1027 }
1028 
1029 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1030 						struct callchain_node *node,
1031 						int level, FILE *fp)
1032 {
1033 	struct callchain_list *chain;
1034 	int offset = level * LEVEL_OFFSET_STEP;
1035 	char folded_sign = ' ';
1036 	int printed = 0;
1037 
1038 	list_for_each_entry(chain, &node->val, list) {
1039 		char bf[1024], *s;
1040 
1041 		folded_sign = callchain_list__folded(chain);
1042 		s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1043 		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1044 	}
1045 
1046 	if (folded_sign == '-')
1047 		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1048 									browser->hists->stats.total_period,
1049 									level + 1,  fp);
1050 	return printed;
1051 }
1052 
1053 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1054 					   struct rb_root *chain, int level, FILE *fp)
1055 {
1056 	struct rb_node *nd;
1057 	int printed = 0;
1058 
1059 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1060 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1061 
1062 		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1063 	}
1064 
1065 	return printed;
1066 }
1067 
1068 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1069 				       struct hist_entry *he, FILE *fp)
1070 {
1071 	char s[8192];
1072 	double percent;
1073 	int printed = 0;
1074 	char folded_sign = ' ';
1075 
1076 	if (symbol_conf.use_callchain)
1077 		folded_sign = hist_entry__folded(he);
1078 
1079 	hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1080 	percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1081 
1082 	if (symbol_conf.use_callchain)
1083 		printed += fprintf(fp, "%c ", folded_sign);
1084 
1085 	printed += fprintf(fp, " %5.2f%%", percent);
1086 
1087 	if (symbol_conf.show_nr_samples)
1088 		printed += fprintf(fp, " %11u", he->stat.nr_events);
1089 
1090 	if (symbol_conf.show_total_period)
1091 		printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1092 
1093 	printed += fprintf(fp, "%s\n", rtrim(s));
1094 
1095 	if (folded_sign == '-')
1096 		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1097 
1098 	return printed;
1099 }
1100 
1101 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1102 {
1103 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
1104 	int printed = 0;
1105 
1106 	while (nd) {
1107 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1108 
1109 		printed += hist_browser__fprintf_entry(browser, h, fp);
1110 		nd = hists__filter_entries(rb_next(nd));
1111 	}
1112 
1113 	return printed;
1114 }
1115 
1116 static int hist_browser__dump(struct hist_browser *browser)
1117 {
1118 	char filename[64];
1119 	FILE *fp;
1120 
1121 	while (1) {
1122 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1123 		if (access(filename, F_OK))
1124 			break;
1125 		/*
1126  		 * XXX: Just an arbitrary lazy upper limit
1127  		 */
1128 		if (++browser->print_seq == 8192) {
1129 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1130 			return -1;
1131 		}
1132 	}
1133 
1134 	fp = fopen(filename, "w");
1135 	if (fp == NULL) {
1136 		char bf[64];
1137 		const char *err = strerror_r(errno, bf, sizeof(bf));
1138 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1139 		return -1;
1140 	}
1141 
1142 	++browser->print_seq;
1143 	hist_browser__fprintf(browser, fp);
1144 	fclose(fp);
1145 	ui_helpline__fpush("%s written!", filename);
1146 
1147 	return 0;
1148 }
1149 
1150 static struct hist_browser *hist_browser__new(struct hists *hists)
1151 {
1152 	struct hist_browser *browser = zalloc(sizeof(*browser));
1153 
1154 	if (browser) {
1155 		browser->hists = hists;
1156 		browser->b.refresh = hist_browser__refresh;
1157 		browser->b.seek = ui_browser__hists_seek;
1158 		browser->b.use_navkeypressed = true;
1159 		if (sort__branch_mode == 1)
1160 			browser->has_symbols = sort_sym_from.list.next != NULL;
1161 		else
1162 			browser->has_symbols = sort_sym.list.next != NULL;
1163 	}
1164 
1165 	return browser;
1166 }
1167 
1168 static void hist_browser__delete(struct hist_browser *browser)
1169 {
1170 	free(browser);
1171 }
1172 
1173 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1174 {
1175 	return browser->he_selection;
1176 }
1177 
1178 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1179 {
1180 	return browser->he_selection->thread;
1181 }
1182 
1183 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1184 				const char *ev_name)
1185 {
1186 	char unit;
1187 	int printed;
1188 	const struct dso *dso = hists->dso_filter;
1189 	const struct thread *thread = hists->thread_filter;
1190 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1191 	u64 nr_events = hists->stats.total_period;
1192 	struct perf_evsel *evsel = hists_to_evsel(hists);
1193 	char buf[512];
1194 	size_t buflen = sizeof(buf);
1195 
1196 	if (symbol_conf.event_group && evsel->nr_members > 1) {
1197 		struct perf_evsel *pos;
1198 
1199 		perf_evsel__group_desc(evsel, buf, buflen);
1200 		ev_name = buf;
1201 
1202 		for_each_group_member(pos, evsel) {
1203 			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1204 			nr_events += pos->hists.stats.total_period;
1205 		}
1206 	}
1207 
1208 	nr_samples = convert_unit(nr_samples, &unit);
1209 	printed = scnprintf(bf, size,
1210 			   "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1211 			   nr_samples, unit, ev_name, nr_events);
1212 
1213 
1214 	if (hists->uid_filter_str)
1215 		printed += snprintf(bf + printed, size - printed,
1216 				    ", UID: %s", hists->uid_filter_str);
1217 	if (thread)
1218 		printed += scnprintf(bf + printed, size - printed,
1219 				    ", Thread: %s(%d)",
1220 				    (thread->comm_set ? thread->comm : ""),
1221 				    thread->pid);
1222 	if (dso)
1223 		printed += scnprintf(bf + printed, size - printed,
1224 				    ", DSO: %s", dso->short_name);
1225 	return printed;
1226 }
1227 
1228 static inline void free_popup_options(char **options, int n)
1229 {
1230 	int i;
1231 
1232 	for (i = 0; i < n; ++i) {
1233 		free(options[i]);
1234 		options[i] = NULL;
1235 	}
1236 }
1237 
1238 /* Check whether the browser is for 'top' or 'report' */
1239 static inline bool is_report_browser(void *timer)
1240 {
1241 	return timer == NULL;
1242 }
1243 
1244 /*
1245  * Only runtime switching of perf data file will make "input_name" point
1246  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1247  * whether we need to call free() for current "input_name" during the switch.
1248  */
1249 static bool is_input_name_malloced = false;
1250 
1251 static int switch_data_file(void)
1252 {
1253 	char *pwd, *options[32], *abs_path[32], *tmp;
1254 	DIR *pwd_dir;
1255 	int nr_options = 0, choice = -1, ret = -1;
1256 	struct dirent *dent;
1257 
1258 	pwd = getenv("PWD");
1259 	if (!pwd)
1260 		return ret;
1261 
1262 	pwd_dir = opendir(pwd);
1263 	if (!pwd_dir)
1264 		return ret;
1265 
1266 	memset(options, 0, sizeof(options));
1267 	memset(options, 0, sizeof(abs_path));
1268 
1269 	while ((dent = readdir(pwd_dir))) {
1270 		char path[PATH_MAX];
1271 		u64 magic;
1272 		char *name = dent->d_name;
1273 		FILE *file;
1274 
1275 		if (!(dent->d_type == DT_REG))
1276 			continue;
1277 
1278 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1279 
1280 		file = fopen(path, "r");
1281 		if (!file)
1282 			continue;
1283 
1284 		if (fread(&magic, 1, 8, file) < 8)
1285 			goto close_file_and_continue;
1286 
1287 		if (is_perf_magic(magic)) {
1288 			options[nr_options] = strdup(name);
1289 			if (!options[nr_options])
1290 				goto close_file_and_continue;
1291 
1292 			abs_path[nr_options] = strdup(path);
1293 			if (!abs_path[nr_options]) {
1294 				free(options[nr_options]);
1295 				ui__warning("Can't search all data files due to memory shortage.\n");
1296 				fclose(file);
1297 				break;
1298 			}
1299 
1300 			nr_options++;
1301 		}
1302 
1303 close_file_and_continue:
1304 		fclose(file);
1305 		if (nr_options >= 32) {
1306 			ui__warning("Too many perf data files in PWD!\n"
1307 				    "Only the first 32 files will be listed.\n");
1308 			break;
1309 		}
1310 	}
1311 	closedir(pwd_dir);
1312 
1313 	if (nr_options) {
1314 		choice = ui__popup_menu(nr_options, options);
1315 		if (choice < nr_options && choice >= 0) {
1316 			tmp = strdup(abs_path[choice]);
1317 			if (tmp) {
1318 				if (is_input_name_malloced)
1319 					free((void *)input_name);
1320 				input_name = tmp;
1321 				is_input_name_malloced = true;
1322 				ret = 0;
1323 			} else
1324 				ui__warning("Data switch failed due to memory shortage!\n");
1325 		}
1326 	}
1327 
1328 	free_popup_options(options, nr_options);
1329 	free_popup_options(abs_path, nr_options);
1330 	return ret;
1331 }
1332 
1333 
1334 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1335 				    const char *helpline, const char *ev_name,
1336 				    bool left_exits,
1337 				    struct hist_browser_timer *hbt,
1338 				    struct perf_session_env *env)
1339 {
1340 	struct hists *hists = &evsel->hists;
1341 	struct hist_browser *browser = hist_browser__new(hists);
1342 	struct branch_info *bi;
1343 	struct pstack *fstack;
1344 	char *options[16];
1345 	int nr_options = 0;
1346 	int key = -1;
1347 	char buf[64];
1348 	char script_opt[64];
1349 	int delay_secs = hbt ? hbt->refresh : 0;
1350 
1351 	if (browser == NULL)
1352 		return -1;
1353 
1354 	fstack = pstack__new(2);
1355 	if (fstack == NULL)
1356 		goto out;
1357 
1358 	ui_helpline__push(helpline);
1359 
1360 	memset(options, 0, sizeof(options));
1361 
1362 	while (1) {
1363 		const struct thread *thread = NULL;
1364 		const struct dso *dso = NULL;
1365 		int choice = 0,
1366 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1367 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1368 		int scripts_comm = -2, scripts_symbol = -2,
1369 		    scripts_all = -2, switch_data = -2;
1370 
1371 		nr_options = 0;
1372 
1373 		key = hist_browser__run(browser, ev_name, hbt);
1374 
1375 		if (browser->he_selection != NULL) {
1376 			thread = hist_browser__selected_thread(browser);
1377 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1378 		}
1379 		switch (key) {
1380 		case K_TAB:
1381 		case K_UNTAB:
1382 			if (nr_events == 1)
1383 				continue;
1384 			/*
1385 			 * Exit the browser, let hists__browser_tree
1386 			 * go to the next or previous
1387 			 */
1388 			goto out_free_stack;
1389 		case 'a':
1390 			if (!browser->has_symbols) {
1391 				ui_browser__warning(&browser->b, delay_secs * 2,
1392 			"Annotation is only available for symbolic views, "
1393 			"include \"sym*\" in --sort to use it.");
1394 				continue;
1395 			}
1396 
1397 			if (browser->selection == NULL ||
1398 			    browser->selection->sym == NULL ||
1399 			    browser->selection->map->dso->annotate_warned)
1400 				continue;
1401 			goto do_annotate;
1402 		case 'P':
1403 			hist_browser__dump(browser);
1404 			continue;
1405 		case 'd':
1406 			goto zoom_dso;
1407 		case 'V':
1408 			browser->show_dso = !browser->show_dso;
1409 			continue;
1410 		case 't':
1411 			goto zoom_thread;
1412 		case '/':
1413 			if (ui_browser__input_window("Symbol to show",
1414 					"Please enter the name of symbol you want to see",
1415 					buf, "ENTER: OK, ESC: Cancel",
1416 					delay_secs * 2) == K_ENTER) {
1417 				hists->symbol_filter_str = *buf ? buf : NULL;
1418 				hists__filter_by_symbol(hists);
1419 				hist_browser__reset(browser);
1420 			}
1421 			continue;
1422 		case 'r':
1423 			if (is_report_browser(hbt))
1424 				goto do_scripts;
1425 			continue;
1426 		case 's':
1427 			if (is_report_browser(hbt))
1428 				goto do_data_switch;
1429 			continue;
1430 		case K_F1:
1431 		case 'h':
1432 		case '?':
1433 			ui_browser__help_window(&browser->b,
1434 					"h/?/F1        Show this window\n"
1435 					"UP/DOWN/PGUP\n"
1436 					"PGDN/SPACE    Navigate\n"
1437 					"q/ESC/CTRL+C  Exit browser\n\n"
1438 					"For multiple event sessions:\n\n"
1439 					"TAB/UNTAB Switch events\n\n"
1440 					"For symbolic views (--sort has sym):\n\n"
1441 					"->            Zoom into DSO/Threads & Annotate current symbol\n"
1442 					"<-            Zoom out\n"
1443 					"a             Annotate current symbol\n"
1444 					"C             Collapse all callchains\n"
1445 					"E             Expand all callchains\n"
1446 					"d             Zoom into current DSO\n"
1447 					"t             Zoom into current Thread\n"
1448 					"r             Run available scripts('perf report' only)\n"
1449 					"s             Switch to another data file in PWD ('perf report' only)\n"
1450 					"P             Print histograms to perf.hist.N\n"
1451 					"V             Verbose (DSO names in callchains, etc)\n"
1452 					"/             Filter symbol by name");
1453 			continue;
1454 		case K_ENTER:
1455 		case K_RIGHT:
1456 			/* menu */
1457 			break;
1458 		case K_LEFT: {
1459 			const void *top;
1460 
1461 			if (pstack__empty(fstack)) {
1462 				/*
1463 				 * Go back to the perf_evsel_menu__run or other user
1464 				 */
1465 				if (left_exits)
1466 					goto out_free_stack;
1467 				continue;
1468 			}
1469 			top = pstack__pop(fstack);
1470 			if (top == &browser->hists->dso_filter)
1471 				goto zoom_out_dso;
1472 			if (top == &browser->hists->thread_filter)
1473 				goto zoom_out_thread;
1474 			continue;
1475 		}
1476 		case K_ESC:
1477 			if (!left_exits &&
1478 			    !ui_browser__dialog_yesno(&browser->b,
1479 					       "Do you really want to exit?"))
1480 				continue;
1481 			/* Fall thru */
1482 		case 'q':
1483 		case CTRL('c'):
1484 			goto out_free_stack;
1485 		default:
1486 			continue;
1487 		}
1488 
1489 		if (!browser->has_symbols)
1490 			goto add_exit_option;
1491 
1492 		if (sort__branch_mode == 1) {
1493 			bi = browser->he_selection->branch_info;
1494 			if (browser->selection != NULL &&
1495 			    bi &&
1496 			    bi->from.sym != NULL &&
1497 			    !bi->from.map->dso->annotate_warned &&
1498 				asprintf(&options[nr_options], "Annotate %s",
1499 					 bi->from.sym->name) > 0)
1500 				annotate_f = nr_options++;
1501 
1502 			if (browser->selection != NULL &&
1503 			    bi &&
1504 			    bi->to.sym != NULL &&
1505 			    !bi->to.map->dso->annotate_warned &&
1506 			    (bi->to.sym != bi->from.sym ||
1507 			     bi->to.map->dso != bi->from.map->dso) &&
1508 				asprintf(&options[nr_options], "Annotate %s",
1509 					 bi->to.sym->name) > 0)
1510 				annotate_t = nr_options++;
1511 		} else {
1512 
1513 			if (browser->selection != NULL &&
1514 			    browser->selection->sym != NULL &&
1515 			    !browser->selection->map->dso->annotate_warned &&
1516 				asprintf(&options[nr_options], "Annotate %s",
1517 					 browser->selection->sym->name) > 0)
1518 				annotate = nr_options++;
1519 		}
1520 
1521 		if (thread != NULL &&
1522 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1523 			     (browser->hists->thread_filter ? "out of" : "into"),
1524 			     (thread->comm_set ? thread->comm : ""),
1525 			     thread->pid) > 0)
1526 			zoom_thread = nr_options++;
1527 
1528 		if (dso != NULL &&
1529 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1530 			     (browser->hists->dso_filter ? "out of" : "into"),
1531 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1532 			zoom_dso = nr_options++;
1533 
1534 		if (browser->selection != NULL &&
1535 		    browser->selection->map != NULL &&
1536 		    asprintf(&options[nr_options], "Browse map details") > 0)
1537 			browse_map = nr_options++;
1538 
1539 		/* perf script support */
1540 		if (browser->he_selection) {
1541 			struct symbol *sym;
1542 
1543 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1544 				browser->he_selection->thread->comm) > 0)
1545 				scripts_comm = nr_options++;
1546 
1547 			sym = browser->he_selection->ms.sym;
1548 			if (sym && sym->namelen &&
1549 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1550 						sym->name) > 0)
1551 				scripts_symbol = nr_options++;
1552 		}
1553 
1554 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1555 			scripts_all = nr_options++;
1556 
1557 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1558 				"Switch to another data file in PWD") > 0)
1559 			switch_data = nr_options++;
1560 add_exit_option:
1561 		options[nr_options++] = (char *)"Exit";
1562 retry_popup_menu:
1563 		choice = ui__popup_menu(nr_options, options);
1564 
1565 		if (choice == nr_options - 1)
1566 			break;
1567 
1568 		if (choice == -1) {
1569 			free_popup_options(options, nr_options - 1);
1570 			continue;
1571 		}
1572 
1573 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1574 			struct hist_entry *he;
1575 			int err;
1576 do_annotate:
1577 			if (!objdump_path && perf_session_env__lookup_objdump(env))
1578 				continue;
1579 
1580 			he = hist_browser__selected_entry(browser);
1581 			if (he == NULL)
1582 				continue;
1583 
1584 			/*
1585 			 * we stash the branch_info symbol + map into the
1586 			 * the ms so we don't have to rewrite all the annotation
1587 			 * code to use branch_info.
1588 			 * in branch mode, the ms struct is not used
1589 			 */
1590 			if (choice == annotate_f) {
1591 				he->ms.sym = he->branch_info->from.sym;
1592 				he->ms.map = he->branch_info->from.map;
1593 			}  else if (choice == annotate_t) {
1594 				he->ms.sym = he->branch_info->to.sym;
1595 				he->ms.map = he->branch_info->to.map;
1596 			}
1597 
1598 			/*
1599 			 * Don't let this be freed, say, by hists__decay_entry.
1600 			 */
1601 			he->used = true;
1602 			err = hist_entry__tui_annotate(he, evsel->idx, hbt);
1603 			he->used = false;
1604 			/*
1605 			 * offer option to annotate the other branch source or target
1606 			 * (if they exists) when returning from annotate
1607 			 */
1608 			if ((err == 'q' || err == CTRL('c'))
1609 			    && annotate_t != -2 && annotate_f != -2)
1610 				goto retry_popup_menu;
1611 
1612 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1613 			if (err)
1614 				ui_browser__handle_resize(&browser->b);
1615 
1616 		} else if (choice == browse_map)
1617 			map__browse(browser->selection->map);
1618 		else if (choice == zoom_dso) {
1619 zoom_dso:
1620 			if (browser->hists->dso_filter) {
1621 				pstack__remove(fstack, &browser->hists->dso_filter);
1622 zoom_out_dso:
1623 				ui_helpline__pop();
1624 				browser->hists->dso_filter = NULL;
1625 				sort_dso.elide = false;
1626 			} else {
1627 				if (dso == NULL)
1628 					continue;
1629 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1630 						   dso->kernel ? "the Kernel" : dso->short_name);
1631 				browser->hists->dso_filter = dso;
1632 				sort_dso.elide = true;
1633 				pstack__push(fstack, &browser->hists->dso_filter);
1634 			}
1635 			hists__filter_by_dso(hists);
1636 			hist_browser__reset(browser);
1637 		} else if (choice == zoom_thread) {
1638 zoom_thread:
1639 			if (browser->hists->thread_filter) {
1640 				pstack__remove(fstack, &browser->hists->thread_filter);
1641 zoom_out_thread:
1642 				ui_helpline__pop();
1643 				browser->hists->thread_filter = NULL;
1644 				sort_thread.elide = false;
1645 			} else {
1646 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1647 						   thread->comm_set ? thread->comm : "",
1648 						   thread->pid);
1649 				browser->hists->thread_filter = thread;
1650 				sort_thread.elide = true;
1651 				pstack__push(fstack, &browser->hists->thread_filter);
1652 			}
1653 			hists__filter_by_thread(hists);
1654 			hist_browser__reset(browser);
1655 		}
1656 		/* perf scripts support */
1657 		else if (choice == scripts_all || choice == scripts_comm ||
1658 				choice == scripts_symbol) {
1659 do_scripts:
1660 			memset(script_opt, 0, 64);
1661 
1662 			if (choice == scripts_comm)
1663 				sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1664 
1665 			if (choice == scripts_symbol)
1666 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1667 
1668 			script_browse(script_opt);
1669 		}
1670 		/* Switch to another data file */
1671 		else if (choice == switch_data) {
1672 do_data_switch:
1673 			if (!switch_data_file()) {
1674 				key = K_SWITCH_INPUT_DATA;
1675 				break;
1676 			} else
1677 				ui__warning("Won't switch the data files due to\n"
1678 					"no valid data file get selected!\n");
1679 		}
1680 	}
1681 out_free_stack:
1682 	pstack__delete(fstack);
1683 out:
1684 	hist_browser__delete(browser);
1685 	free_popup_options(options, nr_options - 1);
1686 	return key;
1687 }
1688 
1689 struct perf_evsel_menu {
1690 	struct ui_browser b;
1691 	struct perf_evsel *selection;
1692 	bool lost_events, lost_events_warned;
1693 	struct perf_session_env *env;
1694 };
1695 
1696 static void perf_evsel_menu__write(struct ui_browser *browser,
1697 				   void *entry, int row)
1698 {
1699 	struct perf_evsel_menu *menu = container_of(browser,
1700 						    struct perf_evsel_menu, b);
1701 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1702 	bool current_entry = ui_browser__is_current_entry(browser, row);
1703 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1704 	const char *ev_name = perf_evsel__name(evsel);
1705 	char bf[256], unit;
1706 	const char *warn = " ";
1707 	size_t printed;
1708 
1709 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1710 						       HE_COLORSET_NORMAL);
1711 
1712 	if (symbol_conf.event_group && evsel->nr_members > 1) {
1713 		struct perf_evsel *pos;
1714 
1715 		ev_name = perf_evsel__group_name(evsel);
1716 
1717 		for_each_group_member(pos, evsel) {
1718 			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1719 		}
1720 	}
1721 
1722 	nr_events = convert_unit(nr_events, &unit);
1723 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1724 			   unit, unit == ' ' ? "" : " ", ev_name);
1725 	slsmg_printf("%s", bf);
1726 
1727 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1728 	if (nr_events != 0) {
1729 		menu->lost_events = true;
1730 		if (!current_entry)
1731 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1732 		nr_events = convert_unit(nr_events, &unit);
1733 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1734 				     nr_events, unit, unit == ' ' ? "" : " ");
1735 		warn = bf;
1736 	}
1737 
1738 	slsmg_write_nstring(warn, browser->width - printed);
1739 
1740 	if (current_entry)
1741 		menu->selection = evsel;
1742 }
1743 
1744 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1745 				int nr_events, const char *help,
1746 				struct hist_browser_timer *hbt)
1747 {
1748 	struct perf_evlist *evlist = menu->b.priv;
1749 	struct perf_evsel *pos;
1750 	const char *ev_name, *title = "Available samples";
1751 	int delay_secs = hbt ? hbt->refresh : 0;
1752 	int key;
1753 
1754 	if (ui_browser__show(&menu->b, title,
1755 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1756 		return -1;
1757 
1758 	while (1) {
1759 		key = ui_browser__run(&menu->b, delay_secs);
1760 
1761 		switch (key) {
1762 		case K_TIMER:
1763 			hbt->timer(hbt->arg);
1764 
1765 			if (!menu->lost_events_warned && menu->lost_events) {
1766 				ui_browser__warn_lost_events(&menu->b);
1767 				menu->lost_events_warned = true;
1768 			}
1769 			continue;
1770 		case K_RIGHT:
1771 		case K_ENTER:
1772 			if (!menu->selection)
1773 				continue;
1774 			pos = menu->selection;
1775 browse_hists:
1776 			perf_evlist__set_selected(evlist, pos);
1777 			/*
1778 			 * Give the calling tool a chance to populate the non
1779 			 * default evsel resorted hists tree.
1780 			 */
1781 			if (hbt)
1782 				hbt->timer(hbt->arg);
1783 			ev_name = perf_evsel__name(pos);
1784 			key = perf_evsel__hists_browse(pos, nr_events, help,
1785 						       ev_name, true, hbt,
1786 						       menu->env);
1787 			ui_browser__show_title(&menu->b, title);
1788 			switch (key) {
1789 			case K_TAB:
1790 				if (pos->node.next == &evlist->entries)
1791 					pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1792 				else
1793 					pos = list_entry(pos->node.next, struct perf_evsel, node);
1794 				goto browse_hists;
1795 			case K_UNTAB:
1796 				if (pos->node.prev == &evlist->entries)
1797 					pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1798 				else
1799 					pos = list_entry(pos->node.prev, struct perf_evsel, node);
1800 				goto browse_hists;
1801 			case K_ESC:
1802 				if (!ui_browser__dialog_yesno(&menu->b,
1803 						"Do you really want to exit?"))
1804 					continue;
1805 				/* Fall thru */
1806 			case K_SWITCH_INPUT_DATA:
1807 			case 'q':
1808 			case CTRL('c'):
1809 				goto out;
1810 			default:
1811 				continue;
1812 			}
1813 		case K_LEFT:
1814 			continue;
1815 		case K_ESC:
1816 			if (!ui_browser__dialog_yesno(&menu->b,
1817 					       "Do you really want to exit?"))
1818 				continue;
1819 			/* Fall thru */
1820 		case 'q':
1821 		case CTRL('c'):
1822 			goto out;
1823 		default:
1824 			continue;
1825 		}
1826 	}
1827 
1828 out:
1829 	ui_browser__hide(&menu->b);
1830 	return key;
1831 }
1832 
1833 static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1834 				 void *entry)
1835 {
1836 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1837 
1838 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1839 		return true;
1840 
1841 	return false;
1842 }
1843 
1844 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1845 					   int nr_entries, const char *help,
1846 					   struct hist_browser_timer *hbt,
1847 					   struct perf_session_env *env)
1848 {
1849 	struct perf_evsel *pos;
1850 	struct perf_evsel_menu menu = {
1851 		.b = {
1852 			.entries    = &evlist->entries,
1853 			.refresh    = ui_browser__list_head_refresh,
1854 			.seek	    = ui_browser__list_head_seek,
1855 			.write	    = perf_evsel_menu__write,
1856 			.filter	    = filter_group_entries,
1857 			.nr_entries = nr_entries,
1858 			.priv	    = evlist,
1859 		},
1860 		.env = env,
1861 	};
1862 
1863 	ui_helpline__push("Press ESC to exit");
1864 
1865 	list_for_each_entry(pos, &evlist->entries, node) {
1866 		const char *ev_name = perf_evsel__name(pos);
1867 		size_t line_len = strlen(ev_name) + 7;
1868 
1869 		if (menu.b.width < line_len)
1870 			menu.b.width = line_len;
1871 	}
1872 
1873 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1874 }
1875 
1876 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1877 				  struct hist_browser_timer *hbt,
1878 				  struct perf_session_env *env)
1879 {
1880 	int nr_entries = evlist->nr_entries;
1881 
1882 single_entry:
1883 	if (nr_entries == 1) {
1884 		struct perf_evsel *first = list_entry(evlist->entries.next,
1885 						      struct perf_evsel, node);
1886 		const char *ev_name = perf_evsel__name(first);
1887 
1888 		return perf_evsel__hists_browse(first, nr_entries, help,
1889 						ev_name, false, hbt, env);
1890 	}
1891 
1892 	if (symbol_conf.event_group) {
1893 		struct perf_evsel *pos;
1894 
1895 		nr_entries = 0;
1896 		list_for_each_entry(pos, &evlist->entries, node)
1897 			if (perf_evsel__is_group_leader(pos))
1898 				nr_entries++;
1899 
1900 		if (nr_entries == 1)
1901 			goto single_entry;
1902 	}
1903 
1904 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1905 					       hbt, env);
1906 }
1907