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