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