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