xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 26270a00)
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 || symbol_conf.has_filter;
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, const char *help)
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, help) < 0)
440 		return -1;
441 
442 	while (1) {
443 		key = ui_browser__run(&browser->b, delay_secs);
444 
445 		switch (key) {
446 		case K_TIMER: {
447 			u64 nr_entries;
448 			hbt->timer(hbt->arg);
449 
450 			if (hist_browser__has_filter(browser))
451 				hist_browser__update_nr_entries(browser);
452 
453 			nr_entries = hist_browser__nr_entries(browser);
454 			ui_browser__update_nr_entries(&browser->b, nr_entries);
455 
456 			if (browser->hists->stats.nr_lost_warned !=
457 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
458 				browser->hists->stats.nr_lost_warned =
459 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
460 				ui_browser__warn_lost_events(&browser->b);
461 			}
462 
463 			hists__browser_title(browser->hists,
464 					     hbt, title, sizeof(title));
465 			ui_browser__show_title(&browser->b, title);
466 			continue;
467 		}
468 		case 'D': { /* Debug */
469 			static int seq;
470 			struct hist_entry *h = rb_entry(browser->b.top,
471 							struct hist_entry, rb_node);
472 			ui_helpline__pop();
473 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
474 					   seq++, browser->b.nr_entries,
475 					   browser->hists->nr_entries,
476 					   browser->b.rows,
477 					   browser->b.index,
478 					   browser->b.top_idx,
479 					   h->row_offset, h->nr_rows);
480 		}
481 			break;
482 		case 'C':
483 			/* Collapse the whole world. */
484 			hist_browser__set_folding(browser, false);
485 			break;
486 		case 'E':
487 			/* Expand the whole world. */
488 			hist_browser__set_folding(browser, true);
489 			break;
490 		case 'H':
491 			browser->show_headers = !browser->show_headers;
492 			hist_browser__update_rows(browser);
493 			break;
494 		case K_ENTER:
495 			if (hist_browser__toggle_fold(browser))
496 				break;
497 			/* fall thru */
498 		default:
499 			goto out;
500 		}
501 	}
502 out:
503 	ui_browser__hide(&browser->b);
504 	return key;
505 }
506 
507 struct callchain_print_arg {
508 	/* for hists browser */
509 	off_t	row_offset;
510 	bool	is_current_entry;
511 
512 	/* for file dump */
513 	FILE	*fp;
514 	int	printed;
515 };
516 
517 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
518 					 struct callchain_list *chain,
519 					 const char *str, int offset,
520 					 unsigned short row,
521 					 struct callchain_print_arg *arg);
522 
523 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
524 					       struct callchain_list *chain,
525 					       const char *str, int offset,
526 					       unsigned short row,
527 					       struct callchain_print_arg *arg)
528 {
529 	int color, width;
530 	char folded_sign = callchain_list__folded(chain);
531 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
532 
533 	color = HE_COLORSET_NORMAL;
534 	width = browser->b.width - (offset + 2);
535 	if (ui_browser__is_current_entry(&browser->b, row)) {
536 		browser->selection = &chain->ms;
537 		color = HE_COLORSET_SELECTED;
538 		arg->is_current_entry = true;
539 	}
540 
541 	ui_browser__set_color(&browser->b, color);
542 	hist_browser__gotorc(browser, row, 0);
543 	ui_browser__write_nstring(&browser->b, " ", offset);
544 	slsmg_printf("%c", folded_sign);
545 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
546 	ui_browser__write_nstring(&browser->b, str, width);
547 }
548 
549 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
550 						  struct callchain_list *chain,
551 						  const char *str, int offset,
552 						  unsigned short row __maybe_unused,
553 						  struct callchain_print_arg *arg)
554 {
555 	char folded_sign = callchain_list__folded(chain);
556 
557 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
558 				folded_sign, str);
559 }
560 
561 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
562 				     unsigned short row);
563 
564 static bool hist_browser__check_output_full(struct hist_browser *browser,
565 					    unsigned short row)
566 {
567 	return browser->b.rows == row;
568 }
569 
570 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
571 					  unsigned short row __maybe_unused)
572 {
573 	return false;
574 }
575 
576 #define LEVEL_OFFSET_STEP 3
577 
578 static int hist_browser__show_callchain(struct hist_browser *browser,
579 					struct rb_root *root, int level,
580 					unsigned short row, u64 total,
581 					print_callchain_entry_fn print,
582 					struct callchain_print_arg *arg,
583 					check_output_full_fn is_output_full)
584 {
585 	struct rb_node *node;
586 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
587 	u64 new_total;
588 	bool need_percent;
589 
590 	node = rb_first(root);
591 	need_percent = node && rb_next(node);
592 
593 	while (node) {
594 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
595 		struct rb_node *next = rb_next(node);
596 		u64 cumul = callchain_cumul_hits(child);
597 		struct callchain_list *chain;
598 		char folded_sign = ' ';
599 		int first = true;
600 		int extra_offset = 0;
601 
602 		list_for_each_entry(chain, &child->val, list) {
603 			char bf[1024], *alloc_str;
604 			const char *str;
605 			bool was_first = first;
606 
607 			if (first)
608 				first = false;
609 			else if (need_percent)
610 				extra_offset = LEVEL_OFFSET_STEP;
611 
612 			folded_sign = callchain_list__folded(chain);
613 			if (arg->row_offset != 0) {
614 				arg->row_offset--;
615 				goto do_next;
616 			}
617 
618 			alloc_str = NULL;
619 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
620 						       browser->show_dso);
621 
622 			if (was_first && need_percent) {
623 				double percent = cumul * 100.0 / total;
624 
625 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
626 					str = "Not enough memory!";
627 				else
628 					str = alloc_str;
629 			}
630 
631 			print(browser, chain, str, offset + extra_offset, row, arg);
632 
633 			free(alloc_str);
634 
635 			if (is_output_full(browser, ++row))
636 				goto out;
637 do_next:
638 			if (folded_sign == '+')
639 				break;
640 		}
641 
642 		if (folded_sign == '-') {
643 			const int new_level = level + (extra_offset ? 2 : 1);
644 
645 			if (callchain_param.mode == CHAIN_GRAPH_REL)
646 				new_total = child->children_hit;
647 			else
648 				new_total = total;
649 
650 			row += hist_browser__show_callchain(browser, &child->rb_root,
651 							    new_level, row, new_total,
652 							    print, arg, is_output_full);
653 		}
654 		if (is_output_full(browser, row))
655 			break;
656 		node = next;
657 	}
658 out:
659 	return row - first_row;
660 }
661 
662 struct hpp_arg {
663 	struct ui_browser *b;
664 	char folded_sign;
665 	bool current_entry;
666 };
667 
668 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
669 {
670 	struct hpp_arg *arg = hpp->ptr;
671 	int ret, len;
672 	va_list args;
673 	double percent;
674 
675 	va_start(args, fmt);
676 	len = va_arg(args, int);
677 	percent = va_arg(args, double);
678 	va_end(args);
679 
680 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
681 
682 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
683 	slsmg_printf("%s", hpp->buf);
684 
685 	advance_hpp(hpp, ret);
686 	return ret;
687 }
688 
689 #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
690 static u64 __hpp_get_##_field(struct hist_entry *he)			\
691 {									\
692 	return he->stat._field;						\
693 }									\
694 									\
695 static int								\
696 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
697 				struct perf_hpp *hpp,			\
698 				struct hist_entry *he)			\
699 {									\
700 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
701 			__hpp__slsmg_color_printf, true);		\
702 }
703 
704 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
705 static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
706 {									\
707 	return he->stat_acc->_field;					\
708 }									\
709 									\
710 static int								\
711 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
712 				struct perf_hpp *hpp,			\
713 				struct hist_entry *he)			\
714 {									\
715 	if (!symbol_conf.cumulate_callchain) {				\
716 		int len = fmt->user_len ?: fmt->len;			\
717 		int ret = scnprintf(hpp->buf, hpp->size,		\
718 				    "%*s", len, "N/A");			\
719 		slsmg_printf("%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 					slsmg_printf("%c ", folded_sign);
805 					width -= 2;
806 				}
807 				first = false;
808 			} else {
809 				slsmg_printf("  ");
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 				slsmg_printf("%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 
1271 	if (symbol_conf.filter_relative) {
1272 		nr_samples = hists->stats.nr_non_filtered_samples;
1273 		nr_events = hists->stats.total_non_filtered_period;
1274 	}
1275 
1276 	if (perf_evsel__is_group_event(evsel)) {
1277 		struct perf_evsel *pos;
1278 
1279 		perf_evsel__group_desc(evsel, buf, buflen);
1280 		ev_name = buf;
1281 
1282 		for_each_group_member(pos, evsel) {
1283 			struct hists *pos_hists = evsel__hists(pos);
1284 
1285 			if (symbol_conf.filter_relative) {
1286 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
1287 				nr_events += pos_hists->stats.total_non_filtered_period;
1288 			} else {
1289 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1290 				nr_events += pos_hists->stats.total_period;
1291 			}
1292 		}
1293 	}
1294 
1295 	nr_samples = convert_unit(nr_samples, &unit);
1296 	printed = scnprintf(bf, size,
1297 			   "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64,
1298 			   nr_samples, unit, ev_name, nr_events);
1299 
1300 
1301 	if (hists->uid_filter_str)
1302 		printed += snprintf(bf + printed, size - printed,
1303 				    ", UID: %s", hists->uid_filter_str);
1304 	if (thread)
1305 		printed += scnprintf(bf + printed, size - printed,
1306 				    ", Thread: %s(%d)",
1307 				     (thread->comm_set ? thread__comm_str(thread) : ""),
1308 				    thread->tid);
1309 	if (dso)
1310 		printed += scnprintf(bf + printed, size - printed,
1311 				    ", DSO: %s", dso->short_name);
1312 	if (!is_report_browser(hbt)) {
1313 		struct perf_top *top = hbt->arg;
1314 
1315 		if (top->zero)
1316 			printed += scnprintf(bf + printed, size - printed, " [z]");
1317 	}
1318 
1319 	return printed;
1320 }
1321 
1322 static inline void free_popup_options(char **options, int n)
1323 {
1324 	int i;
1325 
1326 	for (i = 0; i < n; ++i)
1327 		zfree(&options[i]);
1328 }
1329 
1330 /*
1331  * Only runtime switching of perf data file will make "input_name" point
1332  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1333  * whether we need to call free() for current "input_name" during the switch.
1334  */
1335 static bool is_input_name_malloced = false;
1336 
1337 static int switch_data_file(void)
1338 {
1339 	char *pwd, *options[32], *abs_path[32], *tmp;
1340 	DIR *pwd_dir;
1341 	int nr_options = 0, choice = -1, ret = -1;
1342 	struct dirent *dent;
1343 
1344 	pwd = getenv("PWD");
1345 	if (!pwd)
1346 		return ret;
1347 
1348 	pwd_dir = opendir(pwd);
1349 	if (!pwd_dir)
1350 		return ret;
1351 
1352 	memset(options, 0, sizeof(options));
1353 	memset(options, 0, sizeof(abs_path));
1354 
1355 	while ((dent = readdir(pwd_dir))) {
1356 		char path[PATH_MAX];
1357 		u64 magic;
1358 		char *name = dent->d_name;
1359 		FILE *file;
1360 
1361 		if (!(dent->d_type == DT_REG))
1362 			continue;
1363 
1364 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1365 
1366 		file = fopen(path, "r");
1367 		if (!file)
1368 			continue;
1369 
1370 		if (fread(&magic, 1, 8, file) < 8)
1371 			goto close_file_and_continue;
1372 
1373 		if (is_perf_magic(magic)) {
1374 			options[nr_options] = strdup(name);
1375 			if (!options[nr_options])
1376 				goto close_file_and_continue;
1377 
1378 			abs_path[nr_options] = strdup(path);
1379 			if (!abs_path[nr_options]) {
1380 				zfree(&options[nr_options]);
1381 				ui__warning("Can't search all data files due to memory shortage.\n");
1382 				fclose(file);
1383 				break;
1384 			}
1385 
1386 			nr_options++;
1387 		}
1388 
1389 close_file_and_continue:
1390 		fclose(file);
1391 		if (nr_options >= 32) {
1392 			ui__warning("Too many perf data files in PWD!\n"
1393 				    "Only the first 32 files will be listed.\n");
1394 			break;
1395 		}
1396 	}
1397 	closedir(pwd_dir);
1398 
1399 	if (nr_options) {
1400 		choice = ui__popup_menu(nr_options, options);
1401 		if (choice < nr_options && choice >= 0) {
1402 			tmp = strdup(abs_path[choice]);
1403 			if (tmp) {
1404 				if (is_input_name_malloced)
1405 					free((void *)input_name);
1406 				input_name = tmp;
1407 				is_input_name_malloced = true;
1408 				ret = 0;
1409 			} else
1410 				ui__warning("Data switch failed due to memory shortage!\n");
1411 		}
1412 	}
1413 
1414 	free_popup_options(options, nr_options);
1415 	free_popup_options(abs_path, nr_options);
1416 	return ret;
1417 }
1418 
1419 struct popup_action {
1420 	struct thread 		*thread;
1421 	struct dso		*dso;
1422 	struct map_symbol 	ms;
1423 
1424 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
1425 };
1426 
1427 static int
1428 do_annotate(struct hist_browser *browser, struct popup_action *act)
1429 {
1430 	struct perf_evsel *evsel;
1431 	struct annotation *notes;
1432 	struct hist_entry *he;
1433 	int err;
1434 
1435 	if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
1436 		return 0;
1437 
1438 	notes = symbol__annotation(act->ms.sym);
1439 	if (!notes->src)
1440 		return 0;
1441 
1442 	evsel = hists_to_evsel(browser->hists);
1443 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1444 	he = hist_browser__selected_entry(browser);
1445 	/*
1446 	 * offer option to annotate the other branch source or target
1447 	 * (if they exists) when returning from annotate
1448 	 */
1449 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1450 		return 1;
1451 
1452 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1453 	if (err)
1454 		ui_browser__handle_resize(&browser->b);
1455 	return 0;
1456 }
1457 
1458 static int
1459 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1460 		 struct popup_action *act, char **optstr,
1461 		 struct map *map, struct symbol *sym)
1462 {
1463 	if (sym == NULL || map->dso->annotate_warned)
1464 		return 0;
1465 
1466 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1467 		return 0;
1468 
1469 	act->ms.map = map;
1470 	act->ms.sym = sym;
1471 	act->fn = do_annotate;
1472 	return 1;
1473 }
1474 
1475 static int
1476 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1477 {
1478 	struct thread *thread = act->thread;
1479 
1480 	if (browser->hists->thread_filter) {
1481 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
1482 		perf_hpp__set_elide(HISTC_THREAD, false);
1483 		thread__zput(browser->hists->thread_filter);
1484 		ui_helpline__pop();
1485 	} else {
1486 		ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1487 				   thread->comm_set ? thread__comm_str(thread) : "",
1488 				   thread->tid);
1489 		browser->hists->thread_filter = thread__get(thread);
1490 		perf_hpp__set_elide(HISTC_THREAD, false);
1491 		pstack__push(browser->pstack, &browser->hists->thread_filter);
1492 	}
1493 
1494 	hists__filter_by_thread(browser->hists);
1495 	hist_browser__reset(browser);
1496 	return 0;
1497 }
1498 
1499 static int
1500 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1501 	       char **optstr, struct thread *thread)
1502 {
1503 	if (thread == NULL)
1504 		return 0;
1505 
1506 	if (asprintf(optstr, "Zoom %s %s(%d) thread",
1507 		     browser->hists->thread_filter ? "out of" : "into",
1508 		     thread->comm_set ? thread__comm_str(thread) : "",
1509 		     thread->tid) < 0)
1510 		return 0;
1511 
1512 	act->thread = thread;
1513 	act->fn = do_zoom_thread;
1514 	return 1;
1515 }
1516 
1517 static int
1518 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1519 {
1520 	struct dso *dso = act->dso;
1521 
1522 	if (browser->hists->dso_filter) {
1523 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
1524 		perf_hpp__set_elide(HISTC_DSO, false);
1525 		browser->hists->dso_filter = NULL;
1526 		ui_helpline__pop();
1527 	} else {
1528 		if (dso == NULL)
1529 			return 0;
1530 		ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1531 				   dso->kernel ? "the Kernel" : dso->short_name);
1532 		browser->hists->dso_filter = dso;
1533 		perf_hpp__set_elide(HISTC_DSO, true);
1534 		pstack__push(browser->pstack, &browser->hists->dso_filter);
1535 	}
1536 
1537 	hists__filter_by_dso(browser->hists);
1538 	hist_browser__reset(browser);
1539 	return 0;
1540 }
1541 
1542 static int
1543 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1544 	    char **optstr, struct dso *dso)
1545 {
1546 	if (dso == NULL)
1547 		return 0;
1548 
1549 	if (asprintf(optstr, "Zoom %s %s DSO",
1550 		     browser->hists->dso_filter ? "out of" : "into",
1551 		     dso->kernel ? "the Kernel" : dso->short_name) < 0)
1552 		return 0;
1553 
1554 	act->dso = dso;
1555 	act->fn = do_zoom_dso;
1556 	return 1;
1557 }
1558 
1559 static int
1560 do_browse_map(struct hist_browser *browser __maybe_unused,
1561 	      struct popup_action *act)
1562 {
1563 	map__browse(act->ms.map);
1564 	return 0;
1565 }
1566 
1567 static int
1568 add_map_opt(struct hist_browser *browser __maybe_unused,
1569 	    struct popup_action *act, char **optstr, struct map *map)
1570 {
1571 	if (map == NULL)
1572 		return 0;
1573 
1574 	if (asprintf(optstr, "Browse map details") < 0)
1575 		return 0;
1576 
1577 	act->ms.map = map;
1578 	act->fn = do_browse_map;
1579 	return 1;
1580 }
1581 
1582 static int
1583 do_run_script(struct hist_browser *browser __maybe_unused,
1584 	      struct popup_action *act)
1585 {
1586 	char script_opt[64];
1587 	memset(script_opt, 0, sizeof(script_opt));
1588 
1589 	if (act->thread) {
1590 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1591 			  thread__comm_str(act->thread));
1592 	} else if (act->ms.sym) {
1593 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1594 			  act->ms.sym->name);
1595 	}
1596 
1597 	script_browse(script_opt);
1598 	return 0;
1599 }
1600 
1601 static int
1602 add_script_opt(struct hist_browser *browser __maybe_unused,
1603 	       struct popup_action *act, char **optstr,
1604 	       struct thread *thread, struct symbol *sym)
1605 {
1606 	if (thread) {
1607 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1608 			     thread__comm_str(thread)) < 0)
1609 			return 0;
1610 	} else if (sym) {
1611 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1612 			     sym->name) < 0)
1613 			return 0;
1614 	} else {
1615 		if (asprintf(optstr, "Run scripts for all samples") < 0)
1616 			return 0;
1617 	}
1618 
1619 	act->thread = thread;
1620 	act->ms.sym = sym;
1621 	act->fn = do_run_script;
1622 	return 1;
1623 }
1624 
1625 static int
1626 do_switch_data(struct hist_browser *browser __maybe_unused,
1627 	       struct popup_action *act __maybe_unused)
1628 {
1629 	if (switch_data_file()) {
1630 		ui__warning("Won't switch the data files due to\n"
1631 			    "no valid data file get selected!\n");
1632 		return 0;
1633 	}
1634 
1635 	return K_SWITCH_INPUT_DATA;
1636 }
1637 
1638 static int
1639 add_switch_opt(struct hist_browser *browser,
1640 	       struct popup_action *act, char **optstr)
1641 {
1642 	if (!is_report_browser(browser->hbt))
1643 		return 0;
1644 
1645 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1646 		return 0;
1647 
1648 	act->fn = do_switch_data;
1649 	return 1;
1650 }
1651 
1652 static int
1653 do_exit_browser(struct hist_browser *browser __maybe_unused,
1654 		struct popup_action *act __maybe_unused)
1655 {
1656 	return 0;
1657 }
1658 
1659 static int
1660 add_exit_opt(struct hist_browser *browser __maybe_unused,
1661 	     struct popup_action *act, char **optstr)
1662 {
1663 	if (asprintf(optstr, "Exit") < 0)
1664 		return 0;
1665 
1666 	act->fn = do_exit_browser;
1667 	return 1;
1668 }
1669 
1670 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1671 {
1672 	u64 nr_entries = 0;
1673 	struct rb_node *nd = rb_first(&hb->hists->entries);
1674 
1675 	if (hb->min_pcnt == 0) {
1676 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1677 		return;
1678 	}
1679 
1680 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1681 		nr_entries++;
1682 		nd = rb_next(nd);
1683 	}
1684 
1685 	hb->nr_non_filtered_entries = nr_entries;
1686 }
1687 
1688 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1689 				    const char *helpline,
1690 				    bool left_exits,
1691 				    struct hist_browser_timer *hbt,
1692 				    float min_pcnt,
1693 				    struct perf_session_env *env)
1694 {
1695 	struct hists *hists = evsel__hists(evsel);
1696 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1697 	struct branch_info *bi;
1698 #define MAX_OPTIONS  16
1699 	char *options[MAX_OPTIONS];
1700 	struct popup_action actions[MAX_OPTIONS];
1701 	int nr_options = 0;
1702 	int key = -1;
1703 	char buf[64];
1704 	int delay_secs = hbt ? hbt->refresh : 0;
1705 	struct perf_hpp_fmt *fmt;
1706 
1707 #define HIST_BROWSER_HELP_COMMON					\
1708 	"h/?/F1        Show this window\n"				\
1709 	"UP/DOWN/PGUP\n"						\
1710 	"PGDN/SPACE    Navigate\n"					\
1711 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1712 	"For multiple event sessions:\n\n"				\
1713 	"TAB/UNTAB     Switch events\n\n"				\
1714 	"For symbolic views (--sort has sym):\n\n"			\
1715 	"->            Zoom into DSO/Threads & Annotate current symbol\n" \
1716 	"<-            Zoom out\n"					\
1717 	"a             Annotate current symbol\n"			\
1718 	"C             Collapse all callchains\n"			\
1719 	"d             Zoom into current DSO\n"				\
1720 	"E             Expand all callchains\n"				\
1721 	"F             Toggle percentage of filtered entries\n"		\
1722 	"H             Display column headers\n"			\
1723 
1724 	/* help messages are sorted by lexical order of the hotkey */
1725 	const char report_help[] = HIST_BROWSER_HELP_COMMON
1726 	"i             Show header information\n"
1727 	"P             Print histograms to perf.hist.N\n"
1728 	"r             Run available scripts\n"
1729 	"s             Switch to another data file in PWD\n"
1730 	"t             Zoom into current Thread\n"
1731 	"V             Verbose (DSO names in callchains, etc)\n"
1732 	"/             Filter symbol by name";
1733 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1734 	"P             Print histograms to perf.hist.N\n"
1735 	"t             Zoom into current Thread\n"
1736 	"V             Verbose (DSO names in callchains, etc)\n"
1737 	"z             Toggle zeroing of samples\n"
1738 	"f             Enable/Disable events\n"
1739 	"/             Filter symbol by name";
1740 
1741 	if (browser == NULL)
1742 		return -1;
1743 
1744 	/* reset abort key so that it can get Ctrl-C as a key */
1745 	SLang_reset_tty();
1746 	SLang_init_tty(0, 0, 0);
1747 
1748 	if (min_pcnt) {
1749 		browser->min_pcnt = min_pcnt;
1750 		hist_browser__update_nr_entries(browser);
1751 	}
1752 
1753 	browser->pstack = pstack__new(2);
1754 	if (browser->pstack == NULL)
1755 		goto out;
1756 
1757 	ui_helpline__push(helpline);
1758 
1759 	memset(options, 0, sizeof(options));
1760 	memset(actions, 0, sizeof(actions));
1761 
1762 	perf_hpp__for_each_format(fmt)
1763 		perf_hpp__reset_width(fmt, hists);
1764 
1765 	if (symbol_conf.col_width_list_str)
1766 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1767 
1768 	while (1) {
1769 		struct thread *thread = NULL;
1770 		struct dso *dso = NULL;
1771 		int choice = 0;
1772 
1773 		nr_options = 0;
1774 
1775 		key = hist_browser__run(browser, helpline);
1776 
1777 		if (browser->he_selection != NULL) {
1778 			thread = hist_browser__selected_thread(browser);
1779 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1780 		}
1781 		switch (key) {
1782 		case K_TAB:
1783 		case K_UNTAB:
1784 			if (nr_events == 1)
1785 				continue;
1786 			/*
1787 			 * Exit the browser, let hists__browser_tree
1788 			 * go to the next or previous
1789 			 */
1790 			goto out_free_stack;
1791 		case 'a':
1792 			if (!sort__has_sym) {
1793 				ui_browser__warning(&browser->b, delay_secs * 2,
1794 			"Annotation is only available for symbolic views, "
1795 			"include \"sym*\" in --sort to use it.");
1796 				continue;
1797 			}
1798 
1799 			if (browser->selection == NULL ||
1800 			    browser->selection->sym == NULL ||
1801 			    browser->selection->map->dso->annotate_warned)
1802 				continue;
1803 
1804 			actions->ms.map = browser->selection->map;
1805 			actions->ms.sym = browser->selection->sym;
1806 			do_annotate(browser, actions);
1807 			continue;
1808 		case 'P':
1809 			hist_browser__dump(browser);
1810 			continue;
1811 		case 'd':
1812 			actions->dso = dso;
1813 			do_zoom_dso(browser, actions);
1814 			continue;
1815 		case 'V':
1816 			browser->show_dso = !browser->show_dso;
1817 			continue;
1818 		case 't':
1819 			actions->thread = thread;
1820 			do_zoom_thread(browser, actions);
1821 			continue;
1822 		case '/':
1823 			if (ui_browser__input_window("Symbol to show",
1824 					"Please enter the name of symbol you want to see",
1825 					buf, "ENTER: OK, ESC: Cancel",
1826 					delay_secs * 2) == K_ENTER) {
1827 				hists->symbol_filter_str = *buf ? buf : NULL;
1828 				hists__filter_by_symbol(hists);
1829 				hist_browser__reset(browser);
1830 			}
1831 			continue;
1832 		case 'r':
1833 			if (is_report_browser(hbt)) {
1834 				actions->thread = NULL;
1835 				actions->ms.sym = NULL;
1836 				do_run_script(browser, actions);
1837 			}
1838 			continue;
1839 		case 's':
1840 			if (is_report_browser(hbt)) {
1841 				key = do_switch_data(browser, actions);
1842 				if (key == K_SWITCH_INPUT_DATA)
1843 					goto out_free_stack;
1844 			}
1845 			continue;
1846 		case 'i':
1847 			/* env->arch is NULL for live-mode (i.e. perf top) */
1848 			if (env->arch)
1849 				tui__header_window(env);
1850 			continue;
1851 		case 'F':
1852 			symbol_conf.filter_relative ^= 1;
1853 			continue;
1854 		case 'z':
1855 			if (!is_report_browser(hbt)) {
1856 				struct perf_top *top = hbt->arg;
1857 
1858 				top->zero = !top->zero;
1859 			}
1860 			continue;
1861 		case K_F1:
1862 		case 'h':
1863 		case '?':
1864 			ui_browser__help_window(&browser->b,
1865 				is_report_browser(hbt) ? report_help : top_help);
1866 			continue;
1867 		case K_ENTER:
1868 		case K_RIGHT:
1869 			/* menu */
1870 			break;
1871 		case K_LEFT: {
1872 			const void *top;
1873 
1874 			if (pstack__empty(browser->pstack)) {
1875 				/*
1876 				 * Go back to the perf_evsel_menu__run or other user
1877 				 */
1878 				if (left_exits)
1879 					goto out_free_stack;
1880 				continue;
1881 			}
1882 			top = pstack__peek(browser->pstack);
1883 			if (top == &browser->hists->dso_filter) {
1884 				/*
1885 				 * No need to set actions->dso here since
1886 				 * it's just to remove the current filter.
1887 				 * Ditto for thread below.
1888 				 */
1889 				do_zoom_dso(browser, actions);
1890 			}
1891 			if (top == &browser->hists->thread_filter)
1892 				do_zoom_thread(browser, actions);
1893 			continue;
1894 		}
1895 		case K_ESC:
1896 			if (!left_exits &&
1897 			    !ui_browser__dialog_yesno(&browser->b,
1898 					       "Do you really want to exit?"))
1899 				continue;
1900 			/* Fall thru */
1901 		case 'q':
1902 		case CTRL('c'):
1903 			goto out_free_stack;
1904 		case 'f':
1905 			if (!is_report_browser(hbt)) {
1906 				struct perf_top *top = hbt->arg;
1907 
1908 				perf_evlist__toggle_enable(top->evlist);
1909 				/*
1910 				 * No need to refresh, resort/decay histogram
1911 				 * entries if we are not collecting samples:
1912 				 */
1913 				if (top->evlist->enabled) {
1914 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1915 					hbt->refresh = delay_secs;
1916 				} else {
1917 					helpline = "Press 'f' again to re-enable the events";
1918 					hbt->refresh = 0;
1919 				}
1920 				continue;
1921 			}
1922 			/* Fall thru */
1923 		default:
1924 			helpline = "Press '?' for help on key bindings";
1925 			continue;
1926 		}
1927 
1928 		if (!sort__has_sym)
1929 			goto add_exit_option;
1930 
1931 		if (browser->selection == NULL)
1932 			goto skip_annotation;
1933 
1934 		if (sort__mode == SORT_MODE__BRANCH) {
1935 			bi = browser->he_selection->branch_info;
1936 
1937 			if (bi == NULL)
1938 				goto skip_annotation;
1939 
1940 			nr_options += add_annotate_opt(browser,
1941 						       &actions[nr_options],
1942 						       &options[nr_options],
1943 						       bi->from.map,
1944 						       bi->from.sym);
1945 			if (bi->to.sym != bi->from.sym)
1946 				nr_options += add_annotate_opt(browser,
1947 							&actions[nr_options],
1948 							&options[nr_options],
1949 							bi->to.map,
1950 							bi->to.sym);
1951 		} else {
1952 			nr_options += add_annotate_opt(browser,
1953 						       &actions[nr_options],
1954 						       &options[nr_options],
1955 						       browser->selection->map,
1956 						       browser->selection->sym);
1957 		}
1958 skip_annotation:
1959 		nr_options += add_thread_opt(browser, &actions[nr_options],
1960 					     &options[nr_options], thread);
1961 		nr_options += add_dso_opt(browser, &actions[nr_options],
1962 					  &options[nr_options], dso);
1963 		nr_options += add_map_opt(browser, &actions[nr_options],
1964 					  &options[nr_options],
1965 					  browser->selection->map);
1966 
1967 		/* perf script support */
1968 		if (browser->he_selection) {
1969 			nr_options += add_script_opt(browser,
1970 						     &actions[nr_options],
1971 						     &options[nr_options],
1972 						     thread, NULL);
1973 			nr_options += add_script_opt(browser,
1974 						     &actions[nr_options],
1975 						     &options[nr_options],
1976 						     NULL, browser->selection->sym);
1977 		}
1978 		nr_options += add_script_opt(browser, &actions[nr_options],
1979 					     &options[nr_options], NULL, NULL);
1980 		nr_options += add_switch_opt(browser, &actions[nr_options],
1981 					     &options[nr_options]);
1982 add_exit_option:
1983 		nr_options += add_exit_opt(browser, &actions[nr_options],
1984 					   &options[nr_options]);
1985 
1986 		do {
1987 			struct popup_action *act;
1988 
1989 			choice = ui__popup_menu(nr_options, options);
1990 			if (choice == -1 || choice >= nr_options)
1991 				break;
1992 
1993 			act = &actions[choice];
1994 			key = act->fn(browser, act);
1995 		} while (key == 1);
1996 
1997 		if (key == K_SWITCH_INPUT_DATA)
1998 			break;
1999 	}
2000 out_free_stack:
2001 	pstack__delete(browser->pstack);
2002 out:
2003 	hist_browser__delete(browser);
2004 	free_popup_options(options, MAX_OPTIONS);
2005 	return key;
2006 }
2007 
2008 struct perf_evsel_menu {
2009 	struct ui_browser b;
2010 	struct perf_evsel *selection;
2011 	bool lost_events, lost_events_warned;
2012 	float min_pcnt;
2013 	struct perf_session_env *env;
2014 };
2015 
2016 static void perf_evsel_menu__write(struct ui_browser *browser,
2017 				   void *entry, int row)
2018 {
2019 	struct perf_evsel_menu *menu = container_of(browser,
2020 						    struct perf_evsel_menu, b);
2021 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2022 	struct hists *hists = evsel__hists(evsel);
2023 	bool current_entry = ui_browser__is_current_entry(browser, row);
2024 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2025 	const char *ev_name = perf_evsel__name(evsel);
2026 	char bf[256], unit;
2027 	const char *warn = " ";
2028 	size_t printed;
2029 
2030 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2031 						       HE_COLORSET_NORMAL);
2032 
2033 	if (perf_evsel__is_group_event(evsel)) {
2034 		struct perf_evsel *pos;
2035 
2036 		ev_name = perf_evsel__group_name(evsel);
2037 
2038 		for_each_group_member(pos, evsel) {
2039 			struct hists *pos_hists = evsel__hists(pos);
2040 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2041 		}
2042 	}
2043 
2044 	nr_events = convert_unit(nr_events, &unit);
2045 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2046 			   unit, unit == ' ' ? "" : " ", ev_name);
2047 	slsmg_printf("%s", bf);
2048 
2049 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2050 	if (nr_events != 0) {
2051 		menu->lost_events = true;
2052 		if (!current_entry)
2053 			ui_browser__set_color(browser, HE_COLORSET_TOP);
2054 		nr_events = convert_unit(nr_events, &unit);
2055 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2056 				     nr_events, unit, unit == ' ' ? "" : " ");
2057 		warn = bf;
2058 	}
2059 
2060 	ui_browser__write_nstring(browser, warn, browser->width - printed);
2061 
2062 	if (current_entry)
2063 		menu->selection = evsel;
2064 }
2065 
2066 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2067 				int nr_events, const char *help,
2068 				struct hist_browser_timer *hbt)
2069 {
2070 	struct perf_evlist *evlist = menu->b.priv;
2071 	struct perf_evsel *pos;
2072 	const char *title = "Available samples";
2073 	int delay_secs = hbt ? hbt->refresh : 0;
2074 	int key;
2075 
2076 	if (ui_browser__show(&menu->b, title,
2077 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
2078 		return -1;
2079 
2080 	while (1) {
2081 		key = ui_browser__run(&menu->b, delay_secs);
2082 
2083 		switch (key) {
2084 		case K_TIMER:
2085 			hbt->timer(hbt->arg);
2086 
2087 			if (!menu->lost_events_warned && menu->lost_events) {
2088 				ui_browser__warn_lost_events(&menu->b);
2089 				menu->lost_events_warned = true;
2090 			}
2091 			continue;
2092 		case K_RIGHT:
2093 		case K_ENTER:
2094 			if (!menu->selection)
2095 				continue;
2096 			pos = menu->selection;
2097 browse_hists:
2098 			perf_evlist__set_selected(evlist, pos);
2099 			/*
2100 			 * Give the calling tool a chance to populate the non
2101 			 * default evsel resorted hists tree.
2102 			 */
2103 			if (hbt)
2104 				hbt->timer(hbt->arg);
2105 			key = perf_evsel__hists_browse(pos, nr_events, help,
2106 						       true, hbt,
2107 						       menu->min_pcnt,
2108 						       menu->env);
2109 			ui_browser__show_title(&menu->b, title);
2110 			switch (key) {
2111 			case K_TAB:
2112 				if (pos->node.next == &evlist->entries)
2113 					pos = perf_evlist__first(evlist);
2114 				else
2115 					pos = perf_evsel__next(pos);
2116 				goto browse_hists;
2117 			case K_UNTAB:
2118 				if (pos->node.prev == &evlist->entries)
2119 					pos = perf_evlist__last(evlist);
2120 				else
2121 					pos = perf_evsel__prev(pos);
2122 				goto browse_hists;
2123 			case K_ESC:
2124 				if (!ui_browser__dialog_yesno(&menu->b,
2125 						"Do you really want to exit?"))
2126 					continue;
2127 				/* Fall thru */
2128 			case K_SWITCH_INPUT_DATA:
2129 			case 'q':
2130 			case CTRL('c'):
2131 				goto out;
2132 			default:
2133 				continue;
2134 			}
2135 		case K_LEFT:
2136 			continue;
2137 		case K_ESC:
2138 			if (!ui_browser__dialog_yesno(&menu->b,
2139 					       "Do you really want to exit?"))
2140 				continue;
2141 			/* Fall thru */
2142 		case 'q':
2143 		case CTRL('c'):
2144 			goto out;
2145 		default:
2146 			continue;
2147 		}
2148 	}
2149 
2150 out:
2151 	ui_browser__hide(&menu->b);
2152 	return key;
2153 }
2154 
2155 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2156 				 void *entry)
2157 {
2158 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2159 
2160 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2161 		return true;
2162 
2163 	return false;
2164 }
2165 
2166 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2167 					   int nr_entries, const char *help,
2168 					   struct hist_browser_timer *hbt,
2169 					   float min_pcnt,
2170 					   struct perf_session_env *env)
2171 {
2172 	struct perf_evsel *pos;
2173 	struct perf_evsel_menu menu = {
2174 		.b = {
2175 			.entries    = &evlist->entries,
2176 			.refresh    = ui_browser__list_head_refresh,
2177 			.seek	    = ui_browser__list_head_seek,
2178 			.write	    = perf_evsel_menu__write,
2179 			.filter	    = filter_group_entries,
2180 			.nr_entries = nr_entries,
2181 			.priv	    = evlist,
2182 		},
2183 		.min_pcnt = min_pcnt,
2184 		.env = env,
2185 	};
2186 
2187 	ui_helpline__push("Press ESC to exit");
2188 
2189 	evlist__for_each(evlist, pos) {
2190 		const char *ev_name = perf_evsel__name(pos);
2191 		size_t line_len = strlen(ev_name) + 7;
2192 
2193 		if (menu.b.width < line_len)
2194 			menu.b.width = line_len;
2195 	}
2196 
2197 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2198 }
2199 
2200 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2201 				  struct hist_browser_timer *hbt,
2202 				  float min_pcnt,
2203 				  struct perf_session_env *env)
2204 {
2205 	int nr_entries = evlist->nr_entries;
2206 
2207 single_entry:
2208 	if (nr_entries == 1) {
2209 		struct perf_evsel *first = perf_evlist__first(evlist);
2210 
2211 		return perf_evsel__hists_browse(first, nr_entries, help,
2212 						false, hbt, min_pcnt,
2213 						env);
2214 	}
2215 
2216 	if (symbol_conf.event_group) {
2217 		struct perf_evsel *pos;
2218 
2219 		nr_entries = 0;
2220 		evlist__for_each(evlist, pos) {
2221 			if (perf_evsel__is_group_leader(pos))
2222 				nr_entries++;
2223 		}
2224 
2225 		if (nr_entries == 1)
2226 			goto single_entry;
2227 	}
2228 
2229 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2230 					       hbt, min_pcnt, env);
2231 }
2232