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