xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 31eb4360)
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 		int column = 0;
788 
789 		hist_browser__gotorc(browser, row, 0);
790 
791 		perf_hpp__for_each_format(fmt) {
792 			if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
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 					ui_browser__printf(&browser->b, "%c ", folded_sign);
806 					width -= 2;
807 				}
808 				first = false;
809 			} else {
810 				ui_browser__printf(&browser->b, "  ");
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 				ui_browser__printf(&browser->b, "%s", s);
819 			}
820 		}
821 
822 		/* The scroll bar isn't being used */
823 		if (!browser->b.navkeypressed)
824 			width += 1;
825 
826 		ui_browser__write_nstring(&browser->b, "", 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_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
866 {
867 	struct hists *hists = browser->hists;
868 	struct perf_hpp dummy_hpp = {
869 		.buf    = buf,
870 		.size   = size,
871 	};
872 	struct perf_hpp_fmt *fmt;
873 	size_t ret = 0;
874 	int column = 0;
875 
876 	if (symbol_conf.use_callchain) {
877 		ret = scnprintf(buf, size, "  ");
878 		if (advance_hpp_check(&dummy_hpp, ret))
879 			return ret;
880 	}
881 
882 	perf_hpp__for_each_format(fmt) {
883 		if (perf_hpp__should_skip(fmt)  || column++ < browser->b.horiz_scroll)
884 			continue;
885 
886 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
887 		if (advance_hpp_check(&dummy_hpp, ret))
888 			break;
889 
890 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
891 		if (advance_hpp_check(&dummy_hpp, ret))
892 			break;
893 	}
894 
895 	return ret;
896 }
897 
898 static void hist_browser__show_headers(struct hist_browser *browser)
899 {
900 	char headers[1024];
901 
902 	hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
903 	ui_browser__gotorc(&browser->b, 0, 0);
904 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
905 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
906 }
907 
908 static void ui_browser__hists_init_top(struct ui_browser *browser)
909 {
910 	if (browser->top == NULL) {
911 		struct hist_browser *hb;
912 
913 		hb = container_of(browser, struct hist_browser, b);
914 		browser->top = rb_first(&hb->hists->entries);
915 	}
916 }
917 
918 static unsigned int hist_browser__refresh(struct ui_browser *browser)
919 {
920 	unsigned row = 0;
921 	u16 header_offset = 0;
922 	struct rb_node *nd;
923 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
924 
925 	if (hb->show_headers) {
926 		hist_browser__show_headers(hb);
927 		header_offset = 1;
928 	}
929 
930 	ui_browser__hists_init_top(browser);
931 
932 	for (nd = browser->top; nd; nd = rb_next(nd)) {
933 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
934 		float percent;
935 
936 		if (h->filtered)
937 			continue;
938 
939 		percent = hist_entry__get_percent_limit(h);
940 		if (percent < hb->min_pcnt)
941 			continue;
942 
943 		row += hist_browser__show_entry(hb, h, row);
944 		if (row == browser->rows)
945 			break;
946 	}
947 
948 	return row + header_offset;
949 }
950 
951 static struct rb_node *hists__filter_entries(struct rb_node *nd,
952 					     float min_pcnt)
953 {
954 	while (nd != NULL) {
955 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
956 		float percent = hist_entry__get_percent_limit(h);
957 
958 		if (!h->filtered && percent >= min_pcnt)
959 			return nd;
960 
961 		nd = rb_next(nd);
962 	}
963 
964 	return NULL;
965 }
966 
967 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
968 						  float min_pcnt)
969 {
970 	while (nd != NULL) {
971 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
972 		float percent = hist_entry__get_percent_limit(h);
973 
974 		if (!h->filtered && percent >= min_pcnt)
975 			return nd;
976 
977 		nd = rb_prev(nd);
978 	}
979 
980 	return NULL;
981 }
982 
983 static void ui_browser__hists_seek(struct ui_browser *browser,
984 				   off_t offset, int whence)
985 {
986 	struct hist_entry *h;
987 	struct rb_node *nd;
988 	bool first = true;
989 	struct hist_browser *hb;
990 
991 	hb = container_of(browser, struct hist_browser, b);
992 
993 	if (browser->nr_entries == 0)
994 		return;
995 
996 	ui_browser__hists_init_top(browser);
997 
998 	switch (whence) {
999 	case SEEK_SET:
1000 		nd = hists__filter_entries(rb_first(browser->entries),
1001 					   hb->min_pcnt);
1002 		break;
1003 	case SEEK_CUR:
1004 		nd = browser->top;
1005 		goto do_offset;
1006 	case SEEK_END:
1007 		nd = hists__filter_prev_entries(rb_last(browser->entries),
1008 						hb->min_pcnt);
1009 		first = false;
1010 		break;
1011 	default:
1012 		return;
1013 	}
1014 
1015 	/*
1016 	 * Moves not relative to the first visible entry invalidates its
1017 	 * row_offset:
1018 	 */
1019 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1020 	h->row_offset = 0;
1021 
1022 	/*
1023 	 * Here we have to check if nd is expanded (+), if it is we can't go
1024 	 * the next top level hist_entry, instead we must compute an offset of
1025 	 * what _not_ to show and not change the first visible entry.
1026 	 *
1027 	 * This offset increments when we are going from top to bottom and
1028 	 * decreases when we're going from bottom to top.
1029 	 *
1030 	 * As we don't have backpointers to the top level in the callchains
1031 	 * structure, we need to always print the whole hist_entry callchain,
1032 	 * skipping the first ones that are before the first visible entry
1033 	 * and stop when we printed enough lines to fill the screen.
1034 	 */
1035 do_offset:
1036 	if (offset > 0) {
1037 		do {
1038 			h = rb_entry(nd, struct hist_entry, rb_node);
1039 			if (h->unfolded) {
1040 				u16 remaining = h->nr_rows - h->row_offset;
1041 				if (offset > remaining) {
1042 					offset -= remaining;
1043 					h->row_offset = 0;
1044 				} else {
1045 					h->row_offset += offset;
1046 					offset = 0;
1047 					browser->top = nd;
1048 					break;
1049 				}
1050 			}
1051 			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1052 			if (nd == NULL)
1053 				break;
1054 			--offset;
1055 			browser->top = nd;
1056 		} while (offset != 0);
1057 	} else if (offset < 0) {
1058 		while (1) {
1059 			h = rb_entry(nd, struct hist_entry, rb_node);
1060 			if (h->unfolded) {
1061 				if (first) {
1062 					if (-offset > h->row_offset) {
1063 						offset += h->row_offset;
1064 						h->row_offset = 0;
1065 					} else {
1066 						h->row_offset += offset;
1067 						offset = 0;
1068 						browser->top = nd;
1069 						break;
1070 					}
1071 				} else {
1072 					if (-offset > h->nr_rows) {
1073 						offset += h->nr_rows;
1074 						h->row_offset = 0;
1075 					} else {
1076 						h->row_offset = h->nr_rows + offset;
1077 						offset = 0;
1078 						browser->top = nd;
1079 						break;
1080 					}
1081 				}
1082 			}
1083 
1084 			nd = hists__filter_prev_entries(rb_prev(nd),
1085 							hb->min_pcnt);
1086 			if (nd == NULL)
1087 				break;
1088 			++offset;
1089 			browser->top = nd;
1090 			if (offset == 0) {
1091 				/*
1092 				 * Last unfiltered hist_entry, check if it is
1093 				 * unfolded, if it is then we should have
1094 				 * row_offset at its last entry.
1095 				 */
1096 				h = rb_entry(nd, struct hist_entry, rb_node);
1097 				if (h->unfolded)
1098 					h->row_offset = h->nr_rows;
1099 				break;
1100 			}
1101 			first = false;
1102 		}
1103 	} else {
1104 		browser->top = nd;
1105 		h = rb_entry(nd, struct hist_entry, rb_node);
1106 		h->row_offset = 0;
1107 	}
1108 }
1109 
1110 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1111 					   struct hist_entry *he, FILE *fp)
1112 {
1113 	u64 total = hists__total_period(he->hists);
1114 	struct callchain_print_arg arg  = {
1115 		.fp = fp,
1116 	};
1117 
1118 	if (symbol_conf.cumulate_callchain)
1119 		total = he->stat_acc->period;
1120 
1121 	hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1122 				     hist_browser__fprintf_callchain_entry, &arg,
1123 				     hist_browser__check_dump_full);
1124 	return arg.printed;
1125 }
1126 
1127 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1128 				       struct hist_entry *he, FILE *fp)
1129 {
1130 	char s[8192];
1131 	int printed = 0;
1132 	char folded_sign = ' ';
1133 	struct perf_hpp hpp = {
1134 		.buf = s,
1135 		.size = sizeof(s),
1136 	};
1137 	struct perf_hpp_fmt *fmt;
1138 	bool first = true;
1139 	int ret;
1140 
1141 	if (symbol_conf.use_callchain)
1142 		folded_sign = hist_entry__folded(he);
1143 
1144 	if (symbol_conf.use_callchain)
1145 		printed += fprintf(fp, "%c ", folded_sign);
1146 
1147 	perf_hpp__for_each_format(fmt) {
1148 		if (perf_hpp__should_skip(fmt))
1149 			continue;
1150 
1151 		if (!first) {
1152 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1153 			advance_hpp(&hpp, ret);
1154 		} else
1155 			first = false;
1156 
1157 		ret = fmt->entry(fmt, &hpp, he);
1158 		advance_hpp(&hpp, ret);
1159 	}
1160 	printed += fprintf(fp, "%s\n", rtrim(s));
1161 
1162 	if (folded_sign == '-')
1163 		printed += hist_browser__fprintf_callchain(browser, he, fp);
1164 
1165 	return printed;
1166 }
1167 
1168 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1169 {
1170 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1171 						   browser->min_pcnt);
1172 	int printed = 0;
1173 
1174 	while (nd) {
1175 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1176 
1177 		printed += hist_browser__fprintf_entry(browser, h, fp);
1178 		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1179 	}
1180 
1181 	return printed;
1182 }
1183 
1184 static int hist_browser__dump(struct hist_browser *browser)
1185 {
1186 	char filename[64];
1187 	FILE *fp;
1188 
1189 	while (1) {
1190 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1191 		if (access(filename, F_OK))
1192 			break;
1193 		/*
1194  		 * XXX: Just an arbitrary lazy upper limit
1195  		 */
1196 		if (++browser->print_seq == 8192) {
1197 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1198 			return -1;
1199 		}
1200 	}
1201 
1202 	fp = fopen(filename, "w");
1203 	if (fp == NULL) {
1204 		char bf[64];
1205 		const char *err = strerror_r(errno, bf, sizeof(bf));
1206 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1207 		return -1;
1208 	}
1209 
1210 	++browser->print_seq;
1211 	hist_browser__fprintf(browser, fp);
1212 	fclose(fp);
1213 	ui_helpline__fpush("%s written!", filename);
1214 
1215 	return 0;
1216 }
1217 
1218 static struct hist_browser *hist_browser__new(struct hists *hists,
1219 					      struct hist_browser_timer *hbt,
1220 					      struct perf_env *env)
1221 {
1222 	struct hist_browser *browser = zalloc(sizeof(*browser));
1223 
1224 	if (browser) {
1225 		browser->hists = hists;
1226 		browser->b.refresh = hist_browser__refresh;
1227 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1228 		browser->b.seek = ui_browser__hists_seek;
1229 		browser->b.use_navkeypressed = true;
1230 		browser->show_headers = symbol_conf.show_hist_headers;
1231 		browser->hbt = hbt;
1232 		browser->env = env;
1233 	}
1234 
1235 	return browser;
1236 }
1237 
1238 static void hist_browser__delete(struct hist_browser *browser)
1239 {
1240 	free(browser);
1241 }
1242 
1243 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1244 {
1245 	return browser->he_selection;
1246 }
1247 
1248 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1249 {
1250 	return browser->he_selection->thread;
1251 }
1252 
1253 /* Check whether the browser is for 'top' or 'report' */
1254 static inline bool is_report_browser(void *timer)
1255 {
1256 	return timer == NULL;
1257 }
1258 
1259 static int hists__browser_title(struct hists *hists,
1260 				struct hist_browser_timer *hbt,
1261 				char *bf, size_t size)
1262 {
1263 	char unit;
1264 	int printed;
1265 	const struct dso *dso = hists->dso_filter;
1266 	const struct thread *thread = hists->thread_filter;
1267 	int socket_id = hists->socket_filter;
1268 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1269 	u64 nr_events = hists->stats.total_period;
1270 	struct perf_evsel *evsel = hists_to_evsel(hists);
1271 	const char *ev_name = perf_evsel__name(evsel);
1272 	char buf[512];
1273 	size_t buflen = sizeof(buf);
1274 	char ref[30] = " show reference callgraph, ";
1275 	bool enable_ref = false;
1276 
1277 	if (symbol_conf.filter_relative) {
1278 		nr_samples = hists->stats.nr_non_filtered_samples;
1279 		nr_events = hists->stats.total_non_filtered_period;
1280 	}
1281 
1282 	if (perf_evsel__is_group_event(evsel)) {
1283 		struct perf_evsel *pos;
1284 
1285 		perf_evsel__group_desc(evsel, buf, buflen);
1286 		ev_name = buf;
1287 
1288 		for_each_group_member(pos, evsel) {
1289 			struct hists *pos_hists = evsel__hists(pos);
1290 
1291 			if (symbol_conf.filter_relative) {
1292 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
1293 				nr_events += pos_hists->stats.total_non_filtered_period;
1294 			} else {
1295 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1296 				nr_events += pos_hists->stats.total_period;
1297 			}
1298 		}
1299 	}
1300 
1301 	if (symbol_conf.show_ref_callgraph &&
1302 	    strstr(ev_name, "call-graph=no"))
1303 		enable_ref = true;
1304 	nr_samples = convert_unit(nr_samples, &unit);
1305 	printed = scnprintf(bf, size,
1306 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1307 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1308 
1309 
1310 	if (hists->uid_filter_str)
1311 		printed += snprintf(bf + printed, size - printed,
1312 				    ", UID: %s", hists->uid_filter_str);
1313 	if (thread)
1314 		printed += scnprintf(bf + printed, size - printed,
1315 				    ", Thread: %s(%d)",
1316 				     (thread->comm_set ? thread__comm_str(thread) : ""),
1317 				    thread->tid);
1318 	if (dso)
1319 		printed += scnprintf(bf + printed, size - printed,
1320 				    ", DSO: %s", dso->short_name);
1321 	if (socket_id > -1)
1322 		printed += scnprintf(bf + printed, size - printed,
1323 				    ", Processor Socket: %d", socket_id);
1324 	if (!is_report_browser(hbt)) {
1325 		struct perf_top *top = hbt->arg;
1326 
1327 		if (top->zero)
1328 			printed += scnprintf(bf + printed, size - printed, " [z]");
1329 	}
1330 
1331 	return printed;
1332 }
1333 
1334 static inline void free_popup_options(char **options, int n)
1335 {
1336 	int i;
1337 
1338 	for (i = 0; i < n; ++i)
1339 		zfree(&options[i]);
1340 }
1341 
1342 /*
1343  * Only runtime switching of perf data file will make "input_name" point
1344  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1345  * whether we need to call free() for current "input_name" during the switch.
1346  */
1347 static bool is_input_name_malloced = false;
1348 
1349 static int switch_data_file(void)
1350 {
1351 	char *pwd, *options[32], *abs_path[32], *tmp;
1352 	DIR *pwd_dir;
1353 	int nr_options = 0, choice = -1, ret = -1;
1354 	struct dirent *dent;
1355 
1356 	pwd = getenv("PWD");
1357 	if (!pwd)
1358 		return ret;
1359 
1360 	pwd_dir = opendir(pwd);
1361 	if (!pwd_dir)
1362 		return ret;
1363 
1364 	memset(options, 0, sizeof(options));
1365 	memset(options, 0, sizeof(abs_path));
1366 
1367 	while ((dent = readdir(pwd_dir))) {
1368 		char path[PATH_MAX];
1369 		u64 magic;
1370 		char *name = dent->d_name;
1371 		FILE *file;
1372 
1373 		if (!(dent->d_type == DT_REG))
1374 			continue;
1375 
1376 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1377 
1378 		file = fopen(path, "r");
1379 		if (!file)
1380 			continue;
1381 
1382 		if (fread(&magic, 1, 8, file) < 8)
1383 			goto close_file_and_continue;
1384 
1385 		if (is_perf_magic(magic)) {
1386 			options[nr_options] = strdup(name);
1387 			if (!options[nr_options])
1388 				goto close_file_and_continue;
1389 
1390 			abs_path[nr_options] = strdup(path);
1391 			if (!abs_path[nr_options]) {
1392 				zfree(&options[nr_options]);
1393 				ui__warning("Can't search all data files due to memory shortage.\n");
1394 				fclose(file);
1395 				break;
1396 			}
1397 
1398 			nr_options++;
1399 		}
1400 
1401 close_file_and_continue:
1402 		fclose(file);
1403 		if (nr_options >= 32) {
1404 			ui__warning("Too many perf data files in PWD!\n"
1405 				    "Only the first 32 files will be listed.\n");
1406 			break;
1407 		}
1408 	}
1409 	closedir(pwd_dir);
1410 
1411 	if (nr_options) {
1412 		choice = ui__popup_menu(nr_options, options);
1413 		if (choice < nr_options && choice >= 0) {
1414 			tmp = strdup(abs_path[choice]);
1415 			if (tmp) {
1416 				if (is_input_name_malloced)
1417 					free((void *)input_name);
1418 				input_name = tmp;
1419 				is_input_name_malloced = true;
1420 				ret = 0;
1421 			} else
1422 				ui__warning("Data switch failed due to memory shortage!\n");
1423 		}
1424 	}
1425 
1426 	free_popup_options(options, nr_options);
1427 	free_popup_options(abs_path, nr_options);
1428 	return ret;
1429 }
1430 
1431 struct popup_action {
1432 	struct thread 		*thread;
1433 	struct dso		*dso;
1434 	struct map_symbol 	ms;
1435 	int			socket;
1436 
1437 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
1438 };
1439 
1440 static int
1441 do_annotate(struct hist_browser *browser, struct popup_action *act)
1442 {
1443 	struct perf_evsel *evsel;
1444 	struct annotation *notes;
1445 	struct hist_entry *he;
1446 	int err;
1447 
1448 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
1449 		return 0;
1450 
1451 	notes = symbol__annotation(act->ms.sym);
1452 	if (!notes->src)
1453 		return 0;
1454 
1455 	evsel = hists_to_evsel(browser->hists);
1456 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1457 	he = hist_browser__selected_entry(browser);
1458 	/*
1459 	 * offer option to annotate the other branch source or target
1460 	 * (if they exists) when returning from annotate
1461 	 */
1462 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1463 		return 1;
1464 
1465 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1466 	if (err)
1467 		ui_browser__handle_resize(&browser->b);
1468 	return 0;
1469 }
1470 
1471 static int
1472 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1473 		 struct popup_action *act, char **optstr,
1474 		 struct map *map, struct symbol *sym)
1475 {
1476 	if (sym == NULL || map->dso->annotate_warned)
1477 		return 0;
1478 
1479 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1480 		return 0;
1481 
1482 	act->ms.map = map;
1483 	act->ms.sym = sym;
1484 	act->fn = do_annotate;
1485 	return 1;
1486 }
1487 
1488 static int
1489 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1490 {
1491 	struct thread *thread = act->thread;
1492 
1493 	if (browser->hists->thread_filter) {
1494 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
1495 		perf_hpp__set_elide(HISTC_THREAD, false);
1496 		thread__zput(browser->hists->thread_filter);
1497 		ui_helpline__pop();
1498 	} else {
1499 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1500 				   thread->comm_set ? thread__comm_str(thread) : "",
1501 				   thread->tid);
1502 		browser->hists->thread_filter = thread__get(thread);
1503 		perf_hpp__set_elide(HISTC_THREAD, false);
1504 		pstack__push(browser->pstack, &browser->hists->thread_filter);
1505 	}
1506 
1507 	hists__filter_by_thread(browser->hists);
1508 	hist_browser__reset(browser);
1509 	return 0;
1510 }
1511 
1512 static int
1513 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1514 	       char **optstr, struct thread *thread)
1515 {
1516 	if (thread == NULL)
1517 		return 0;
1518 
1519 	if (asprintf(optstr, "Zoom %s %s(%d) thread",
1520 		     browser->hists->thread_filter ? "out of" : "into",
1521 		     thread->comm_set ? thread__comm_str(thread) : "",
1522 		     thread->tid) < 0)
1523 		return 0;
1524 
1525 	act->thread = thread;
1526 	act->fn = do_zoom_thread;
1527 	return 1;
1528 }
1529 
1530 static int
1531 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1532 {
1533 	struct map *map = act->ms.map;
1534 
1535 	if (browser->hists->dso_filter) {
1536 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
1537 		perf_hpp__set_elide(HISTC_DSO, false);
1538 		browser->hists->dso_filter = NULL;
1539 		ui_helpline__pop();
1540 	} else {
1541 		if (map == NULL)
1542 			return 0;
1543 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1544 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1545 		browser->hists->dso_filter = map->dso;
1546 		perf_hpp__set_elide(HISTC_DSO, true);
1547 		pstack__push(browser->pstack, &browser->hists->dso_filter);
1548 	}
1549 
1550 	hists__filter_by_dso(browser->hists);
1551 	hist_browser__reset(browser);
1552 	return 0;
1553 }
1554 
1555 static int
1556 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1557 	    char **optstr, struct map *map)
1558 {
1559 	if (map == NULL)
1560 		return 0;
1561 
1562 	if (asprintf(optstr, "Zoom %s %s DSO",
1563 		     browser->hists->dso_filter ? "out of" : "into",
1564 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1565 		return 0;
1566 
1567 	act->ms.map = map;
1568 	act->dso = map->dso;
1569 	act->fn = do_zoom_dso;
1570 	return 1;
1571 }
1572 
1573 static int
1574 do_browse_map(struct hist_browser *browser __maybe_unused,
1575 	      struct popup_action *act)
1576 {
1577 	map__browse(act->ms.map);
1578 	return 0;
1579 }
1580 
1581 static int
1582 add_map_opt(struct hist_browser *browser __maybe_unused,
1583 	    struct popup_action *act, char **optstr, struct map *map)
1584 {
1585 	if (map == NULL)
1586 		return 0;
1587 
1588 	if (asprintf(optstr, "Browse map details") < 0)
1589 		return 0;
1590 
1591 	act->ms.map = map;
1592 	act->fn = do_browse_map;
1593 	return 1;
1594 }
1595 
1596 static int
1597 do_run_script(struct hist_browser *browser __maybe_unused,
1598 	      struct popup_action *act)
1599 {
1600 	char script_opt[64];
1601 	memset(script_opt, 0, sizeof(script_opt));
1602 
1603 	if (act->thread) {
1604 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1605 			  thread__comm_str(act->thread));
1606 	} else if (act->ms.sym) {
1607 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1608 			  act->ms.sym->name);
1609 	}
1610 
1611 	script_browse(script_opt);
1612 	return 0;
1613 }
1614 
1615 static int
1616 add_script_opt(struct hist_browser *browser __maybe_unused,
1617 	       struct popup_action *act, char **optstr,
1618 	       struct thread *thread, struct symbol *sym)
1619 {
1620 	if (thread) {
1621 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1622 			     thread__comm_str(thread)) < 0)
1623 			return 0;
1624 	} else if (sym) {
1625 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1626 			     sym->name) < 0)
1627 			return 0;
1628 	} else {
1629 		if (asprintf(optstr, "Run scripts for all samples") < 0)
1630 			return 0;
1631 	}
1632 
1633 	act->thread = thread;
1634 	act->ms.sym = sym;
1635 	act->fn = do_run_script;
1636 	return 1;
1637 }
1638 
1639 static int
1640 do_switch_data(struct hist_browser *browser __maybe_unused,
1641 	       struct popup_action *act __maybe_unused)
1642 {
1643 	if (switch_data_file()) {
1644 		ui__warning("Won't switch the data files due to\n"
1645 			    "no valid data file get selected!\n");
1646 		return 0;
1647 	}
1648 
1649 	return K_SWITCH_INPUT_DATA;
1650 }
1651 
1652 static int
1653 add_switch_opt(struct hist_browser *browser,
1654 	       struct popup_action *act, char **optstr)
1655 {
1656 	if (!is_report_browser(browser->hbt))
1657 		return 0;
1658 
1659 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1660 		return 0;
1661 
1662 	act->fn = do_switch_data;
1663 	return 1;
1664 }
1665 
1666 static int
1667 do_exit_browser(struct hist_browser *browser __maybe_unused,
1668 		struct popup_action *act __maybe_unused)
1669 {
1670 	return 0;
1671 }
1672 
1673 static int
1674 add_exit_opt(struct hist_browser *browser __maybe_unused,
1675 	     struct popup_action *act, char **optstr)
1676 {
1677 	if (asprintf(optstr, "Exit") < 0)
1678 		return 0;
1679 
1680 	act->fn = do_exit_browser;
1681 	return 1;
1682 }
1683 
1684 static int
1685 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1686 {
1687 	if (browser->hists->socket_filter > -1) {
1688 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
1689 		browser->hists->socket_filter = -1;
1690 		perf_hpp__set_elide(HISTC_SOCKET, false);
1691 	} else {
1692 		browser->hists->socket_filter = act->socket;
1693 		perf_hpp__set_elide(HISTC_SOCKET, true);
1694 		pstack__push(browser->pstack, &browser->hists->socket_filter);
1695 	}
1696 
1697 	hists__filter_by_socket(browser->hists);
1698 	hist_browser__reset(browser);
1699 	return 0;
1700 }
1701 
1702 static int
1703 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1704 	       char **optstr, int socket_id)
1705 {
1706 	if (socket_id < 0)
1707 		return 0;
1708 
1709 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
1710 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
1711 		     socket_id) < 0)
1712 		return 0;
1713 
1714 	act->socket = socket_id;
1715 	act->fn = do_zoom_socket;
1716 	return 1;
1717 }
1718 
1719 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1720 {
1721 	u64 nr_entries = 0;
1722 	struct rb_node *nd = rb_first(&hb->hists->entries);
1723 
1724 	if (hb->min_pcnt == 0) {
1725 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1726 		return;
1727 	}
1728 
1729 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1730 		nr_entries++;
1731 		nd = rb_next(nd);
1732 	}
1733 
1734 	hb->nr_non_filtered_entries = nr_entries;
1735 }
1736 
1737 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1738 				    const char *helpline,
1739 				    bool left_exits,
1740 				    struct hist_browser_timer *hbt,
1741 				    float min_pcnt,
1742 				    struct perf_env *env)
1743 {
1744 	struct hists *hists = evsel__hists(evsel);
1745 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1746 	struct branch_info *bi;
1747 #define MAX_OPTIONS  16
1748 	char *options[MAX_OPTIONS];
1749 	struct popup_action actions[MAX_OPTIONS];
1750 	int nr_options = 0;
1751 	int key = -1;
1752 	char buf[64];
1753 	int delay_secs = hbt ? hbt->refresh : 0;
1754 	struct perf_hpp_fmt *fmt;
1755 
1756 #define HIST_BROWSER_HELP_COMMON					\
1757 	"h/?/F1        Show this window\n"				\
1758 	"UP/DOWN/PGUP\n"						\
1759 	"PGDN/SPACE    Navigate\n"					\
1760 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1761 	"For multiple event sessions:\n\n"				\
1762 	"TAB/UNTAB     Switch events\n\n"				\
1763 	"For symbolic views (--sort has sym):\n\n"			\
1764 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
1765 	"ESC           Zoom out\n"					\
1766 	"a             Annotate current symbol\n"			\
1767 	"C             Collapse all callchains\n"			\
1768 	"d             Zoom into current DSO\n"				\
1769 	"E             Expand all callchains\n"				\
1770 	"F             Toggle percentage of filtered entries\n"		\
1771 	"H             Display column headers\n"			\
1772 	"m             Display context menu\n"				\
1773 	"S             Zoom into current Processor Socket\n"		\
1774 
1775 	/* help messages are sorted by lexical order of the hotkey */
1776 	const char report_help[] = HIST_BROWSER_HELP_COMMON
1777 	"i             Show header information\n"
1778 	"P             Print histograms to perf.hist.N\n"
1779 	"r             Run available scripts\n"
1780 	"s             Switch to another data file in PWD\n"
1781 	"t             Zoom into current Thread\n"
1782 	"V             Verbose (DSO names in callchains, etc)\n"
1783 	"/             Filter symbol by name";
1784 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1785 	"P             Print histograms to perf.hist.N\n"
1786 	"t             Zoom into current Thread\n"
1787 	"V             Verbose (DSO names in callchains, etc)\n"
1788 	"z             Toggle zeroing of samples\n"
1789 	"f             Enable/Disable events\n"
1790 	"/             Filter symbol by name";
1791 
1792 	if (browser == NULL)
1793 		return -1;
1794 
1795 	/* reset abort key so that it can get Ctrl-C as a key */
1796 	SLang_reset_tty();
1797 	SLang_init_tty(0, 0, 0);
1798 
1799 	if (min_pcnt) {
1800 		browser->min_pcnt = min_pcnt;
1801 		hist_browser__update_nr_entries(browser);
1802 	}
1803 
1804 	browser->pstack = pstack__new(3);
1805 	if (browser->pstack == NULL)
1806 		goto out;
1807 
1808 	ui_helpline__push(helpline);
1809 
1810 	memset(options, 0, sizeof(options));
1811 	memset(actions, 0, sizeof(actions));
1812 
1813 	perf_hpp__for_each_format(fmt) {
1814 		perf_hpp__reset_width(fmt, hists);
1815 		/*
1816 		 * This is done just once, and activates the horizontal scrolling
1817 		 * code in the ui_browser code, it would be better to have a the
1818 		 * counter in the perf_hpp code, but I couldn't find doing it here
1819 		 * works, FIXME by setting this in hist_browser__new, for now, be
1820 		 * clever 8-)
1821 		 */
1822 		++browser->b.columns;
1823 	}
1824 
1825 	if (symbol_conf.col_width_list_str)
1826 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1827 
1828 	while (1) {
1829 		struct thread *thread = NULL;
1830 		struct dso *dso = NULL;
1831 		struct map *map = NULL;
1832 		int choice = 0;
1833 		int socked_id = -1;
1834 
1835 		nr_options = 0;
1836 
1837 		key = hist_browser__run(browser, helpline);
1838 
1839 		if (browser->he_selection != NULL) {
1840 			thread = hist_browser__selected_thread(browser);
1841 			map = browser->selection->map;
1842 			if (map)
1843 				dso = map->dso;
1844 			socked_id = browser->he_selection->socket;
1845 		}
1846 		switch (key) {
1847 		case K_TAB:
1848 		case K_UNTAB:
1849 			if (nr_events == 1)
1850 				continue;
1851 			/*
1852 			 * Exit the browser, let hists__browser_tree
1853 			 * go to the next or previous
1854 			 */
1855 			goto out_free_stack;
1856 		case 'a':
1857 			if (!sort__has_sym) {
1858 				ui_browser__warning(&browser->b, delay_secs * 2,
1859 			"Annotation is only available for symbolic views, "
1860 			"include \"sym*\" in --sort to use it.");
1861 				continue;
1862 			}
1863 
1864 			if (browser->selection == NULL ||
1865 			    browser->selection->sym == NULL ||
1866 			    browser->selection->map->dso->annotate_warned)
1867 				continue;
1868 
1869 			actions->ms.map = browser->selection->map;
1870 			actions->ms.sym = browser->selection->sym;
1871 			do_annotate(browser, actions);
1872 			continue;
1873 		case 'P':
1874 			hist_browser__dump(browser);
1875 			continue;
1876 		case 'd':
1877 			actions->dso = dso;
1878 			do_zoom_dso(browser, actions);
1879 			continue;
1880 		case 'V':
1881 			browser->show_dso = !browser->show_dso;
1882 			continue;
1883 		case 't':
1884 			actions->thread = thread;
1885 			do_zoom_thread(browser, actions);
1886 			continue;
1887 		case 'S':
1888 			actions->socket = socked_id;
1889 			do_zoom_socket(browser, actions);
1890 			continue;
1891 		case '/':
1892 			if (ui_browser__input_window("Symbol to show",
1893 					"Please enter the name of symbol you want to see.\n"
1894 					"To remove the filter later, press / + ENTER.",
1895 					buf, "ENTER: OK, ESC: Cancel",
1896 					delay_secs * 2) == K_ENTER) {
1897 				hists->symbol_filter_str = *buf ? buf : NULL;
1898 				hists__filter_by_symbol(hists);
1899 				hist_browser__reset(browser);
1900 			}
1901 			continue;
1902 		case 'r':
1903 			if (is_report_browser(hbt)) {
1904 				actions->thread = NULL;
1905 				actions->ms.sym = NULL;
1906 				do_run_script(browser, actions);
1907 			}
1908 			continue;
1909 		case 's':
1910 			if (is_report_browser(hbt)) {
1911 				key = do_switch_data(browser, actions);
1912 				if (key == K_SWITCH_INPUT_DATA)
1913 					goto out_free_stack;
1914 			}
1915 			continue;
1916 		case 'i':
1917 			/* env->arch is NULL for live-mode (i.e. perf top) */
1918 			if (env->arch)
1919 				tui__header_window(env);
1920 			continue;
1921 		case 'F':
1922 			symbol_conf.filter_relative ^= 1;
1923 			continue;
1924 		case 'z':
1925 			if (!is_report_browser(hbt)) {
1926 				struct perf_top *top = hbt->arg;
1927 
1928 				top->zero = !top->zero;
1929 			}
1930 			continue;
1931 		case K_F1:
1932 		case 'h':
1933 		case '?':
1934 			ui_browser__help_window(&browser->b,
1935 				is_report_browser(hbt) ? report_help : top_help);
1936 			continue;
1937 		case K_ENTER:
1938 		case K_RIGHT:
1939 		case 'm':
1940 			/* menu */
1941 			break;
1942 		case K_ESC:
1943 		case K_LEFT: {
1944 			const void *top;
1945 
1946 			if (pstack__empty(browser->pstack)) {
1947 				/*
1948 				 * Go back to the perf_evsel_menu__run or other user
1949 				 */
1950 				if (left_exits)
1951 					goto out_free_stack;
1952 
1953 				if (key == K_ESC &&
1954 				    ui_browser__dialog_yesno(&browser->b,
1955 							     "Do you really want to exit?"))
1956 					goto out_free_stack;
1957 
1958 				continue;
1959 			}
1960 			top = pstack__peek(browser->pstack);
1961 			if (top == &browser->hists->dso_filter) {
1962 				/*
1963 				 * No need to set actions->dso here since
1964 				 * it's just to remove the current filter.
1965 				 * Ditto for thread below.
1966 				 */
1967 				do_zoom_dso(browser, actions);
1968 			} else if (top == &browser->hists->thread_filter) {
1969 				do_zoom_thread(browser, actions);
1970 			} else if (top == &browser->hists->socket_filter) {
1971 				do_zoom_socket(browser, actions);
1972 			}
1973 			continue;
1974 		}
1975 		case 'q':
1976 		case CTRL('c'):
1977 			goto out_free_stack;
1978 		case 'f':
1979 			if (!is_report_browser(hbt)) {
1980 				struct perf_top *top = hbt->arg;
1981 
1982 				perf_evlist__toggle_enable(top->evlist);
1983 				/*
1984 				 * No need to refresh, resort/decay histogram
1985 				 * entries if we are not collecting samples:
1986 				 */
1987 				if (top->evlist->enabled) {
1988 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1989 					hbt->refresh = delay_secs;
1990 				} else {
1991 					helpline = "Press 'f' again to re-enable the events";
1992 					hbt->refresh = 0;
1993 				}
1994 				continue;
1995 			}
1996 			/* Fall thru */
1997 		default:
1998 			helpline = "Press '?' for help on key bindings";
1999 			continue;
2000 		}
2001 
2002 		if (!sort__has_sym)
2003 			goto add_exit_option;
2004 
2005 		if (browser->selection == NULL)
2006 			goto skip_annotation;
2007 
2008 		if (sort__mode == SORT_MODE__BRANCH) {
2009 			bi = browser->he_selection->branch_info;
2010 
2011 			if (bi == NULL)
2012 				goto skip_annotation;
2013 
2014 			nr_options += add_annotate_opt(browser,
2015 						       &actions[nr_options],
2016 						       &options[nr_options],
2017 						       bi->from.map,
2018 						       bi->from.sym);
2019 			if (bi->to.sym != bi->from.sym)
2020 				nr_options += add_annotate_opt(browser,
2021 							&actions[nr_options],
2022 							&options[nr_options],
2023 							bi->to.map,
2024 							bi->to.sym);
2025 		} else {
2026 			nr_options += add_annotate_opt(browser,
2027 						       &actions[nr_options],
2028 						       &options[nr_options],
2029 						       browser->selection->map,
2030 						       browser->selection->sym);
2031 		}
2032 skip_annotation:
2033 		nr_options += add_thread_opt(browser, &actions[nr_options],
2034 					     &options[nr_options], thread);
2035 		nr_options += add_dso_opt(browser, &actions[nr_options],
2036 					  &options[nr_options], map);
2037 		nr_options += add_map_opt(browser, &actions[nr_options],
2038 					  &options[nr_options],
2039 					  browser->selection ?
2040 						browser->selection->map : NULL);
2041 		nr_options += add_socket_opt(browser, &actions[nr_options],
2042 					     &options[nr_options],
2043 					     socked_id);
2044 		/* perf script support */
2045 		if (browser->he_selection) {
2046 			nr_options += add_script_opt(browser,
2047 						     &actions[nr_options],
2048 						     &options[nr_options],
2049 						     thread, NULL);
2050 			/*
2051 			 * Note that browser->selection != NULL
2052 			 * when browser->he_selection is not NULL,
2053 			 * so we don't need to check browser->selection
2054 			 * before fetching browser->selection->sym like what
2055 			 * we do before fetching browser->selection->map.
2056 			 *
2057 			 * See hist_browser__show_entry.
2058 			 */
2059 			nr_options += add_script_opt(browser,
2060 						     &actions[nr_options],
2061 						     &options[nr_options],
2062 						     NULL, browser->selection->sym);
2063 		}
2064 		nr_options += add_script_opt(browser, &actions[nr_options],
2065 					     &options[nr_options], NULL, NULL);
2066 		nr_options += add_switch_opt(browser, &actions[nr_options],
2067 					     &options[nr_options]);
2068 add_exit_option:
2069 		nr_options += add_exit_opt(browser, &actions[nr_options],
2070 					   &options[nr_options]);
2071 
2072 		do {
2073 			struct popup_action *act;
2074 
2075 			choice = ui__popup_menu(nr_options, options);
2076 			if (choice == -1 || choice >= nr_options)
2077 				break;
2078 
2079 			act = &actions[choice];
2080 			key = act->fn(browser, act);
2081 		} while (key == 1);
2082 
2083 		if (key == K_SWITCH_INPUT_DATA)
2084 			break;
2085 	}
2086 out_free_stack:
2087 	pstack__delete(browser->pstack);
2088 out:
2089 	hist_browser__delete(browser);
2090 	free_popup_options(options, MAX_OPTIONS);
2091 	return key;
2092 }
2093 
2094 struct perf_evsel_menu {
2095 	struct ui_browser b;
2096 	struct perf_evsel *selection;
2097 	bool lost_events, lost_events_warned;
2098 	float min_pcnt;
2099 	struct perf_env *env;
2100 };
2101 
2102 static void perf_evsel_menu__write(struct ui_browser *browser,
2103 				   void *entry, int row)
2104 {
2105 	struct perf_evsel_menu *menu = container_of(browser,
2106 						    struct perf_evsel_menu, b);
2107 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2108 	struct hists *hists = evsel__hists(evsel);
2109 	bool current_entry = ui_browser__is_current_entry(browser, row);
2110 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2111 	const char *ev_name = perf_evsel__name(evsel);
2112 	char bf[256], unit;
2113 	const char *warn = " ";
2114 	size_t printed;
2115 
2116 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2117 						       HE_COLORSET_NORMAL);
2118 
2119 	if (perf_evsel__is_group_event(evsel)) {
2120 		struct perf_evsel *pos;
2121 
2122 		ev_name = perf_evsel__group_name(evsel);
2123 
2124 		for_each_group_member(pos, evsel) {
2125 			struct hists *pos_hists = evsel__hists(pos);
2126 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2127 		}
2128 	}
2129 
2130 	nr_events = convert_unit(nr_events, &unit);
2131 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2132 			   unit, unit == ' ' ? "" : " ", ev_name);
2133 	ui_browser__printf(browser, "%s", bf);
2134 
2135 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2136 	if (nr_events != 0) {
2137 		menu->lost_events = true;
2138 		if (!current_entry)
2139 			ui_browser__set_color(browser, HE_COLORSET_TOP);
2140 		nr_events = convert_unit(nr_events, &unit);
2141 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2142 				     nr_events, unit, unit == ' ' ? "" : " ");
2143 		warn = bf;
2144 	}
2145 
2146 	ui_browser__write_nstring(browser, warn, browser->width - printed);
2147 
2148 	if (current_entry)
2149 		menu->selection = evsel;
2150 }
2151 
2152 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2153 				int nr_events, const char *help,
2154 				struct hist_browser_timer *hbt)
2155 {
2156 	struct perf_evlist *evlist = menu->b.priv;
2157 	struct perf_evsel *pos;
2158 	const char *title = "Available samples";
2159 	int delay_secs = hbt ? hbt->refresh : 0;
2160 	int key;
2161 
2162 	if (ui_browser__show(&menu->b, title,
2163 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
2164 		return -1;
2165 
2166 	while (1) {
2167 		key = ui_browser__run(&menu->b, delay_secs);
2168 
2169 		switch (key) {
2170 		case K_TIMER:
2171 			hbt->timer(hbt->arg);
2172 
2173 			if (!menu->lost_events_warned && menu->lost_events) {
2174 				ui_browser__warn_lost_events(&menu->b);
2175 				menu->lost_events_warned = true;
2176 			}
2177 			continue;
2178 		case K_RIGHT:
2179 		case K_ENTER:
2180 			if (!menu->selection)
2181 				continue;
2182 			pos = menu->selection;
2183 browse_hists:
2184 			perf_evlist__set_selected(evlist, pos);
2185 			/*
2186 			 * Give the calling tool a chance to populate the non
2187 			 * default evsel resorted hists tree.
2188 			 */
2189 			if (hbt)
2190 				hbt->timer(hbt->arg);
2191 			key = perf_evsel__hists_browse(pos, nr_events, help,
2192 						       true, hbt,
2193 						       menu->min_pcnt,
2194 						       menu->env);
2195 			ui_browser__show_title(&menu->b, title);
2196 			switch (key) {
2197 			case K_TAB:
2198 				if (pos->node.next == &evlist->entries)
2199 					pos = perf_evlist__first(evlist);
2200 				else
2201 					pos = perf_evsel__next(pos);
2202 				goto browse_hists;
2203 			case K_UNTAB:
2204 				if (pos->node.prev == &evlist->entries)
2205 					pos = perf_evlist__last(evlist);
2206 				else
2207 					pos = perf_evsel__prev(pos);
2208 				goto browse_hists;
2209 			case K_SWITCH_INPUT_DATA:
2210 			case 'q':
2211 			case CTRL('c'):
2212 				goto out;
2213 			case K_ESC:
2214 			default:
2215 				continue;
2216 			}
2217 		case K_LEFT:
2218 			continue;
2219 		case K_ESC:
2220 			if (!ui_browser__dialog_yesno(&menu->b,
2221 					       "Do you really want to exit?"))
2222 				continue;
2223 			/* Fall thru */
2224 		case 'q':
2225 		case CTRL('c'):
2226 			goto out;
2227 		default:
2228 			continue;
2229 		}
2230 	}
2231 
2232 out:
2233 	ui_browser__hide(&menu->b);
2234 	return key;
2235 }
2236 
2237 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2238 				 void *entry)
2239 {
2240 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2241 
2242 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2243 		return true;
2244 
2245 	return false;
2246 }
2247 
2248 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2249 					   int nr_entries, const char *help,
2250 					   struct hist_browser_timer *hbt,
2251 					   float min_pcnt,
2252 					   struct perf_env *env)
2253 {
2254 	struct perf_evsel *pos;
2255 	struct perf_evsel_menu menu = {
2256 		.b = {
2257 			.entries    = &evlist->entries,
2258 			.refresh    = ui_browser__list_head_refresh,
2259 			.seek	    = ui_browser__list_head_seek,
2260 			.write	    = perf_evsel_menu__write,
2261 			.filter	    = filter_group_entries,
2262 			.nr_entries = nr_entries,
2263 			.priv	    = evlist,
2264 		},
2265 		.min_pcnt = min_pcnt,
2266 		.env = env,
2267 	};
2268 
2269 	ui_helpline__push("Press ESC to exit");
2270 
2271 	evlist__for_each(evlist, pos) {
2272 		const char *ev_name = perf_evsel__name(pos);
2273 		size_t line_len = strlen(ev_name) + 7;
2274 
2275 		if (menu.b.width < line_len)
2276 			menu.b.width = line_len;
2277 	}
2278 
2279 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2280 }
2281 
2282 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2283 				  struct hist_browser_timer *hbt,
2284 				  float min_pcnt,
2285 				  struct perf_env *env)
2286 {
2287 	int nr_entries = evlist->nr_entries;
2288 
2289 single_entry:
2290 	if (nr_entries == 1) {
2291 		struct perf_evsel *first = perf_evlist__first(evlist);
2292 
2293 		return perf_evsel__hists_browse(first, nr_entries, help,
2294 						false, hbt, min_pcnt,
2295 						env);
2296 	}
2297 
2298 	if (symbol_conf.event_group) {
2299 		struct perf_evsel *pos;
2300 
2301 		nr_entries = 0;
2302 		evlist__for_each(evlist, pos) {
2303 			if (perf_evsel__is_group_leader(pos))
2304 				nr_entries++;
2305 		}
2306 
2307 		if (nr_entries == 1)
2308 			goto single_entry;
2309 	}
2310 
2311 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2312 					       hbt, min_pcnt, env);
2313 }
2314