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