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