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