xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 86edee97)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <dirent.h>
3 #include <errno.h>
4 #include <inttypes.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <linux/rbtree.h>
9 #include <linux/string.h>
10 #include <sys/ttydefaults.h>
11 #include <linux/time64.h>
12 #include <linux/zalloc.h>
13 
14 #include "../../util/debug.h"
15 #include "../../util/dso.h"
16 #include "../../util/callchain.h"
17 #include "../../util/evsel.h"
18 #include "../../util/evlist.h"
19 #include "../../util/header.h"
20 #include "../../util/hist.h"
21 #include "../../util/machine.h"
22 #include "../../util/map.h"
23 #include "../../util/maps.h"
24 #include "../../util/symbol.h"
25 #include "../../util/map_symbol.h"
26 #include "../../util/branch.h"
27 #include "../../util/pstack.h"
28 #include "../../util/sort.h"
29 #include "../../util/top.h"
30 #include "../../util/thread.h"
31 #include "../../util/block-info.h"
32 #include "../../arch/common.h"
33 #include "../../perf.h"
34 
35 #include "../browsers/hists.h"
36 #include "../helpline.h"
37 #include "../util.h"
38 #include "../ui.h"
39 #include "map.h"
40 #include "annotate.h"
41 #include "srcline.h"
42 #include "string2.h"
43 #include "units.h"
44 #include "time-utils.h"
45 
46 #include <linux/ctype.h>
47 
48 extern void hist_browser__init_hpp(void);
49 
50 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
51 static void hist_browser__update_nr_entries(struct hist_browser *hb);
52 
53 static struct rb_node *hists__filter_entries(struct rb_node *nd,
54 					     float min_pcnt);
55 
56 static bool hist_browser__has_filter(struct hist_browser *hb)
57 {
58 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
59 }
60 
61 static int hist_browser__get_folding(struct hist_browser *browser)
62 {
63 	struct rb_node *nd;
64 	struct hists *hists = browser->hists;
65 	int unfolded_rows = 0;
66 
67 	for (nd = rb_first_cached(&hists->entries);
68 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
69 	     nd = rb_hierarchy_next(nd)) {
70 		struct hist_entry *he =
71 			rb_entry(nd, struct hist_entry, rb_node);
72 
73 		if (he->leaf && he->unfolded)
74 			unfolded_rows += he->nr_rows;
75 	}
76 	return unfolded_rows;
77 }
78 
79 static void hist_browser__set_title_space(struct hist_browser *hb)
80 {
81 	struct ui_browser *browser = &hb->b;
82 	struct hists *hists = hb->hists;
83 	struct perf_hpp_list *hpp_list = hists->hpp_list;
84 
85 	browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
86 }
87 
88 static u32 hist_browser__nr_entries(struct hist_browser *hb)
89 {
90 	u32 nr_entries;
91 
92 	if (symbol_conf.report_hierarchy)
93 		nr_entries = hb->nr_hierarchy_entries;
94 	else if (hist_browser__has_filter(hb))
95 		nr_entries = hb->nr_non_filtered_entries;
96 	else
97 		nr_entries = hb->hists->nr_entries;
98 
99 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
100 	return nr_entries + hb->nr_callchain_rows;
101 }
102 
103 static void hist_browser__update_rows(struct hist_browser *hb)
104 {
105 	struct ui_browser *browser = &hb->b;
106 	struct hists *hists = hb->hists;
107 	struct perf_hpp_list *hpp_list = hists->hpp_list;
108 	u16 index_row;
109 
110 	if (!hb->show_headers) {
111 		browser->rows += browser->extra_title_lines;
112 		browser->extra_title_lines = 0;
113 		return;
114 	}
115 
116 	browser->extra_title_lines = hpp_list->nr_header_lines;
117 	browser->rows -= browser->extra_title_lines;
118 	/*
119 	 * Verify if we were at the last line and that line isn't
120 	 * visibe because we now show the header line(s).
121 	 */
122 	index_row = browser->index - browser->top_idx;
123 	if (index_row >= browser->rows)
124 		browser->index -= index_row - browser->rows + 1;
125 }
126 
127 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
128 {
129 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
130 
131 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
132 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
133 	/*
134  	 * FIXME: Just keeping existing behaviour, but this really should be
135  	 *	  before updating browser->width, as it will invalidate the
136  	 *	  calculation above. Fix this and the fallout in another
137  	 *	  changeset.
138  	 */
139 	ui_browser__refresh_dimensions(browser);
140 }
141 
142 static void hist_browser__reset(struct hist_browser *browser)
143 {
144 	/*
145 	 * The hists__remove_entry_filter() already folds non-filtered
146 	 * entries so we can assume it has 0 callchain rows.
147 	 */
148 	browser->nr_callchain_rows = 0;
149 
150 	hist_browser__update_nr_entries(browser);
151 	browser->b.nr_entries = hist_browser__nr_entries(browser);
152 	hist_browser__refresh_dimensions(&browser->b);
153 	ui_browser__reset_index(&browser->b);
154 }
155 
156 static char tree__folded_sign(bool unfolded)
157 {
158 	return unfolded ? '-' : '+';
159 }
160 
161 static char hist_entry__folded(const struct hist_entry *he)
162 {
163 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
164 }
165 
166 static char callchain_list__folded(const struct callchain_list *cl)
167 {
168 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
169 }
170 
171 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
172 {
173 	cl->unfolded = unfold ? cl->has_children : false;
174 }
175 
176 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
177 {
178 	int n = 0;
179 	struct rb_node *nd;
180 
181 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
182 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
183 		struct callchain_list *chain;
184 		char folded_sign = ' '; /* No children */
185 
186 		list_for_each_entry(chain, &child->val, list) {
187 			++n;
188 
189 			/* We need this because we may not have children */
190 			folded_sign = callchain_list__folded(chain);
191 			if (folded_sign == '+')
192 				break;
193 		}
194 
195 		if (folded_sign == '-') /* Have children and they're unfolded */
196 			n += callchain_node__count_rows_rb_tree(child);
197 	}
198 
199 	return n;
200 }
201 
202 static int callchain_node__count_flat_rows(struct callchain_node *node)
203 {
204 	struct callchain_list *chain;
205 	char folded_sign = 0;
206 	int n = 0;
207 
208 	list_for_each_entry(chain, &node->parent_val, list) {
209 		if (!folded_sign) {
210 			/* only check first chain list entry */
211 			folded_sign = callchain_list__folded(chain);
212 			if (folded_sign == '+')
213 				return 1;
214 		}
215 		n++;
216 	}
217 
218 	list_for_each_entry(chain, &node->val, list) {
219 		if (!folded_sign) {
220 			/* node->parent_val list might be empty */
221 			folded_sign = callchain_list__folded(chain);
222 			if (folded_sign == '+')
223 				return 1;
224 		}
225 		n++;
226 	}
227 
228 	return n;
229 }
230 
231 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
232 {
233 	return 1;
234 }
235 
236 static int callchain_node__count_rows(struct callchain_node *node)
237 {
238 	struct callchain_list *chain;
239 	bool unfolded = false;
240 	int n = 0;
241 
242 	if (callchain_param.mode == CHAIN_FLAT)
243 		return callchain_node__count_flat_rows(node);
244 	else if (callchain_param.mode == CHAIN_FOLDED)
245 		return callchain_node__count_folded_rows(node);
246 
247 	list_for_each_entry(chain, &node->val, list) {
248 		++n;
249 
250 		unfolded = chain->unfolded;
251 	}
252 
253 	if (unfolded)
254 		n += callchain_node__count_rows_rb_tree(node);
255 
256 	return n;
257 }
258 
259 static int callchain__count_rows(struct rb_root *chain)
260 {
261 	struct rb_node *nd;
262 	int n = 0;
263 
264 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
265 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
266 		n += callchain_node__count_rows(node);
267 	}
268 
269 	return n;
270 }
271 
272 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
273 				bool include_children)
274 {
275 	int count = 0;
276 	struct rb_node *node;
277 	struct hist_entry *child;
278 
279 	if (he->leaf)
280 		return callchain__count_rows(&he->sorted_chain);
281 
282 	if (he->has_no_entry)
283 		return 1;
284 
285 	node = rb_first_cached(&he->hroot_out);
286 	while (node) {
287 		float percent;
288 
289 		child = rb_entry(node, struct hist_entry, rb_node);
290 		percent = hist_entry__get_percent_limit(child);
291 
292 		if (!child->filtered && percent >= hb->min_pcnt) {
293 			count++;
294 
295 			if (include_children && child->unfolded)
296 				count += hierarchy_count_rows(hb, child, true);
297 		}
298 
299 		node = rb_next(node);
300 	}
301 	return count;
302 }
303 
304 static bool hist_entry__toggle_fold(struct hist_entry *he)
305 {
306 	if (!he)
307 		return false;
308 
309 	if (!he->has_children)
310 		return false;
311 
312 	he->unfolded = !he->unfolded;
313 	return true;
314 }
315 
316 static bool callchain_list__toggle_fold(struct callchain_list *cl)
317 {
318 	if (!cl)
319 		return false;
320 
321 	if (!cl->has_children)
322 		return false;
323 
324 	cl->unfolded = !cl->unfolded;
325 	return true;
326 }
327 
328 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
329 {
330 	struct rb_node *nd = rb_first(&node->rb_root);
331 
332 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
333 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
334 		struct callchain_list *chain;
335 		bool first = true;
336 
337 		list_for_each_entry(chain, &child->val, list) {
338 			if (first) {
339 				first = false;
340 				chain->has_children = chain->list.next != &child->val ||
341 							 !RB_EMPTY_ROOT(&child->rb_root);
342 			} else
343 				chain->has_children = chain->list.next == &child->val &&
344 							 !RB_EMPTY_ROOT(&child->rb_root);
345 		}
346 
347 		callchain_node__init_have_children_rb_tree(child);
348 	}
349 }
350 
351 static void callchain_node__init_have_children(struct callchain_node *node,
352 					       bool has_sibling)
353 {
354 	struct callchain_list *chain;
355 
356 	chain = list_entry(node->val.next, struct callchain_list, list);
357 	chain->has_children = has_sibling;
358 
359 	if (!list_empty(&node->val)) {
360 		chain = list_entry(node->val.prev, struct callchain_list, list);
361 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
362 	}
363 
364 	callchain_node__init_have_children_rb_tree(node);
365 }
366 
367 static void callchain__init_have_children(struct rb_root *root)
368 {
369 	struct rb_node *nd = rb_first(root);
370 	bool has_sibling = nd && rb_next(nd);
371 
372 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
373 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374 		callchain_node__init_have_children(node, has_sibling);
375 		if (callchain_param.mode == CHAIN_FLAT ||
376 		    callchain_param.mode == CHAIN_FOLDED)
377 			callchain_node__make_parent_list(node);
378 	}
379 }
380 
381 static void hist_entry__init_have_children(struct hist_entry *he)
382 {
383 	if (he->init_have_children)
384 		return;
385 
386 	if (he->leaf) {
387 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
388 		callchain__init_have_children(&he->sorted_chain);
389 	} else {
390 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
391 	}
392 
393 	he->init_have_children = true;
394 }
395 
396 static bool hist_browser__selection_has_children(struct hist_browser *browser)
397 {
398 	struct hist_entry *he = browser->he_selection;
399 	struct map_symbol *ms = browser->selection;
400 
401 	if (!he || !ms)
402 		return false;
403 
404 	if (ms == &he->ms)
405 	       return he->has_children;
406 
407 	return container_of(ms, struct callchain_list, ms)->has_children;
408 }
409 
410 static bool hist_browser__he_selection_unfolded(struct hist_browser *browser)
411 {
412 	return browser->he_selection ? browser->he_selection->unfolded : false;
413 }
414 
415 static bool hist_browser__selection_unfolded(struct hist_browser *browser)
416 {
417 	struct hist_entry *he = browser->he_selection;
418 	struct map_symbol *ms = browser->selection;
419 
420 	if (!he || !ms)
421 		return false;
422 
423 	if (ms == &he->ms)
424 	       return he->unfolded;
425 
426 	return container_of(ms, struct callchain_list, ms)->unfolded;
427 }
428 
429 static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
430 {
431 	struct hist_entry *he = browser->he_selection;
432 	struct map_symbol *ms = browser->selection;
433 	struct callchain_list *callchain_entry;
434 
435 	if (!he || !ms)
436 		return NULL;
437 
438 	if (ms == &he->ms) {
439 	       hist_entry__sym_snprintf(he, bf, size, 0);
440 	       return bf + 4; // skip the level, e.g. '[k] '
441 	}
442 
443 	callchain_entry = container_of(ms, struct callchain_list, ms);
444 	return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
445 }
446 
447 static bool hist_browser__toggle_fold(struct hist_browser *browser)
448 {
449 	struct hist_entry *he = browser->he_selection;
450 	struct map_symbol *ms = browser->selection;
451 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
452 	bool has_children;
453 
454 	if (!he || !ms)
455 		return false;
456 
457 	if (ms == &he->ms)
458 		has_children = hist_entry__toggle_fold(he);
459 	else
460 		has_children = callchain_list__toggle_fold(cl);
461 
462 	if (has_children) {
463 		int child_rows = 0;
464 
465 		hist_entry__init_have_children(he);
466 		browser->b.nr_entries -= he->nr_rows;
467 
468 		if (he->leaf)
469 			browser->nr_callchain_rows -= he->nr_rows;
470 		else
471 			browser->nr_hierarchy_entries -= he->nr_rows;
472 
473 		if (symbol_conf.report_hierarchy)
474 			child_rows = hierarchy_count_rows(browser, he, true);
475 
476 		if (he->unfolded) {
477 			if (he->leaf)
478 				he->nr_rows = callchain__count_rows(
479 						&he->sorted_chain);
480 			else
481 				he->nr_rows = hierarchy_count_rows(browser, he, false);
482 
483 			/* account grand children */
484 			if (symbol_conf.report_hierarchy)
485 				browser->b.nr_entries += child_rows - he->nr_rows;
486 
487 			if (!he->leaf && he->nr_rows == 0) {
488 				he->has_no_entry = true;
489 				he->nr_rows = 1;
490 			}
491 		} else {
492 			if (symbol_conf.report_hierarchy)
493 				browser->b.nr_entries -= child_rows - he->nr_rows;
494 
495 			if (he->has_no_entry)
496 				he->has_no_entry = false;
497 
498 			he->nr_rows = 0;
499 		}
500 
501 		browser->b.nr_entries += he->nr_rows;
502 
503 		if (he->leaf)
504 			browser->nr_callchain_rows += he->nr_rows;
505 		else
506 			browser->nr_hierarchy_entries += he->nr_rows;
507 
508 		return true;
509 	}
510 
511 	/* If it doesn't have children, no toggling performed */
512 	return false;
513 }
514 
515 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
516 {
517 	int n = 0;
518 	struct rb_node *nd;
519 
520 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
521 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
522 		struct callchain_list *chain;
523 		bool has_children = false;
524 
525 		list_for_each_entry(chain, &child->val, list) {
526 			++n;
527 			callchain_list__set_folding(chain, unfold);
528 			has_children = chain->has_children;
529 		}
530 
531 		if (has_children)
532 			n += callchain_node__set_folding_rb_tree(child, unfold);
533 	}
534 
535 	return n;
536 }
537 
538 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
539 {
540 	struct callchain_list *chain;
541 	bool has_children = false;
542 	int n = 0;
543 
544 	list_for_each_entry(chain, &node->val, list) {
545 		++n;
546 		callchain_list__set_folding(chain, unfold);
547 		has_children = chain->has_children;
548 	}
549 
550 	if (has_children)
551 		n += callchain_node__set_folding_rb_tree(node, unfold);
552 
553 	return n;
554 }
555 
556 static int callchain__set_folding(struct rb_root *chain, bool unfold)
557 {
558 	struct rb_node *nd;
559 	int n = 0;
560 
561 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
562 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
563 		n += callchain_node__set_folding(node, unfold);
564 	}
565 
566 	return n;
567 }
568 
569 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
570 				 bool unfold __maybe_unused)
571 {
572 	float percent;
573 	struct rb_node *nd;
574 	struct hist_entry *child;
575 	int n = 0;
576 
577 	for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
578 		child = rb_entry(nd, struct hist_entry, rb_node);
579 		percent = hist_entry__get_percent_limit(child);
580 		if (!child->filtered && percent >= hb->min_pcnt)
581 			n++;
582 	}
583 
584 	return n;
585 }
586 
587 static void __hist_entry__set_folding(struct hist_entry *he,
588 				      struct hist_browser *hb, bool unfold)
589 {
590 	hist_entry__init_have_children(he);
591 	he->unfolded = unfold ? he->has_children : false;
592 
593 	if (he->has_children) {
594 		int n;
595 
596 		if (he->leaf)
597 			n = callchain__set_folding(&he->sorted_chain, unfold);
598 		else
599 			n = hierarchy_set_folding(hb, he, unfold);
600 
601 		he->nr_rows = unfold ? n : 0;
602 	} else
603 		he->nr_rows = 0;
604 }
605 
606 static void hist_entry__set_folding(struct hist_entry *he,
607 				    struct hist_browser *browser, bool unfold)
608 {
609 	double percent;
610 
611 	percent = hist_entry__get_percent_limit(he);
612 	if (he->filtered || percent < browser->min_pcnt)
613 		return;
614 
615 	__hist_entry__set_folding(he, browser, unfold);
616 
617 	if (!he->depth || unfold)
618 		browser->nr_hierarchy_entries++;
619 	if (he->leaf)
620 		browser->nr_callchain_rows += he->nr_rows;
621 	else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
622 		browser->nr_hierarchy_entries++;
623 		he->has_no_entry = true;
624 		he->nr_rows = 1;
625 	} else
626 		he->has_no_entry = false;
627 }
628 
629 static void
630 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
631 {
632 	struct rb_node *nd;
633 	struct hist_entry *he;
634 
635 	nd = rb_first_cached(&browser->hists->entries);
636 	while (nd) {
637 		he = rb_entry(nd, struct hist_entry, rb_node);
638 
639 		/* set folding state even if it's currently folded */
640 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
641 
642 		hist_entry__set_folding(he, browser, unfold);
643 	}
644 }
645 
646 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
647 {
648 	browser->nr_hierarchy_entries = 0;
649 	browser->nr_callchain_rows = 0;
650 	__hist_browser__set_folding(browser, unfold);
651 
652 	browser->b.nr_entries = hist_browser__nr_entries(browser);
653 	/* Go to the start, we may be way after valid entries after a collapse */
654 	ui_browser__reset_index(&browser->b);
655 }
656 
657 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
658 {
659 	if (!browser->he_selection)
660 		return;
661 
662 	hist_entry__set_folding(browser->he_selection, browser, unfold);
663 	browser->b.nr_entries = hist_browser__nr_entries(browser);
664 }
665 
666 static void ui_browser__warn_lost_events(struct ui_browser *browser)
667 {
668 	ui_browser__warning(browser, 4,
669 		"Events are being lost, check IO/CPU overload!\n\n"
670 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
671 		" perf top -r 80\n\n"
672 		"Or reduce the sampling frequency.");
673 }
674 
675 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
676 {
677 	return browser->title ? browser->title(browser, bf, size) : 0;
678 }
679 
680 static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, int key)
681 {
682 	switch (key) {
683 	case K_TIMER: {
684 		struct hist_browser_timer *hbt = browser->hbt;
685 		u64 nr_entries;
686 
687 		WARN_ON_ONCE(!hbt);
688 
689 		if (hbt)
690 			hbt->timer(hbt->arg);
691 
692 		if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
693 			hist_browser__update_nr_entries(browser);
694 
695 		nr_entries = hist_browser__nr_entries(browser);
696 		ui_browser__update_nr_entries(&browser->b, nr_entries);
697 
698 		if (warn_lost_event &&
699 		    (browser->hists->stats.nr_lost_warned !=
700 		    browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
701 			browser->hists->stats.nr_lost_warned =
702 				browser->hists->stats.nr_events[PERF_RECORD_LOST];
703 			ui_browser__warn_lost_events(&browser->b);
704 		}
705 
706 		hist_browser__title(browser, title, sizeof(title));
707 		ui_browser__show_title(&browser->b, title);
708 		break;
709 	}
710 	case 'D': { /* Debug */
711 		struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
712 		static int seq;
713 
714 		ui_helpline__pop();
715 		ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
716 				   seq++, browser->b.nr_entries, browser->hists->nr_entries,
717 				   browser->b.extra_title_lines, browser->b.rows,
718 				   browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
719 	}
720 		break;
721 	case 'C':
722 		/* Collapse the whole world. */
723 		hist_browser__set_folding(browser, false);
724 		break;
725 	case 'c':
726 		/* Collapse the selected entry. */
727 		hist_browser__set_folding_selected(browser, false);
728 		break;
729 	case 'E':
730 		/* Expand the whole world. */
731 		hist_browser__set_folding(browser, true);
732 		break;
733 	case 'e':
734 		/* Expand the selected entry. */
735 		hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser));
736 		break;
737 	case 'H':
738 		browser->show_headers = !browser->show_headers;
739 		hist_browser__update_rows(browser);
740 		break;
741 	case '+':
742 		if (hist_browser__toggle_fold(browser))
743 			break;
744 		/* fall thru */
745 	default:
746 		return -1;
747 	}
748 
749 	return 0;
750 }
751 
752 int hist_browser__run(struct hist_browser *browser, const char *help,
753 		      bool warn_lost_event, int key)
754 {
755 	char title[160];
756 	struct hist_browser_timer *hbt = browser->hbt;
757 	int delay_secs = hbt ? hbt->refresh : 0;
758 
759 	browser->b.entries = &browser->hists->entries;
760 	browser->b.nr_entries = hist_browser__nr_entries(browser);
761 
762 	hist_browser__title(browser, title, sizeof(title));
763 
764 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
765 		return -1;
766 
767 	if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, key))
768 		goto out;
769 
770 	while (1) {
771 		key = ui_browser__run(&browser->b, delay_secs);
772 
773 		if (hist_browser__handle_hotkey(browser, warn_lost_event, title, key))
774 			break;
775 	}
776 out:
777 	ui_browser__hide(&browser->b);
778 	return key;
779 }
780 
781 struct callchain_print_arg {
782 	/* for hists browser */
783 	off_t	row_offset;
784 	bool	is_current_entry;
785 
786 	/* for file dump */
787 	FILE	*fp;
788 	int	printed;
789 };
790 
791 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
792 					 struct callchain_list *chain,
793 					 const char *str, int offset,
794 					 unsigned short row,
795 					 struct callchain_print_arg *arg);
796 
797 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
798 					       struct callchain_list *chain,
799 					       const char *str, int offset,
800 					       unsigned short row,
801 					       struct callchain_print_arg *arg)
802 {
803 	int color, width;
804 	char folded_sign = callchain_list__folded(chain);
805 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
806 
807 	color = HE_COLORSET_NORMAL;
808 	width = browser->b.width - (offset + 2);
809 	if (ui_browser__is_current_entry(&browser->b, row)) {
810 		browser->selection = &chain->ms;
811 		color = HE_COLORSET_SELECTED;
812 		arg->is_current_entry = true;
813 	}
814 
815 	ui_browser__set_color(&browser->b, color);
816 	ui_browser__gotorc(&browser->b, row, 0);
817 	ui_browser__write_nstring(&browser->b, " ", offset);
818 	ui_browser__printf(&browser->b, "%c", folded_sign);
819 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
820 	ui_browser__write_nstring(&browser->b, str, width);
821 }
822 
823 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
824 						  struct callchain_list *chain,
825 						  const char *str, int offset,
826 						  unsigned short row __maybe_unused,
827 						  struct callchain_print_arg *arg)
828 {
829 	char folded_sign = callchain_list__folded(chain);
830 
831 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
832 				folded_sign, str);
833 }
834 
835 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
836 				     unsigned short row);
837 
838 static bool hist_browser__check_output_full(struct hist_browser *browser,
839 					    unsigned short row)
840 {
841 	return browser->b.rows == row;
842 }
843 
844 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
845 					  unsigned short row __maybe_unused)
846 {
847 	return false;
848 }
849 
850 #define LEVEL_OFFSET_STEP 3
851 
852 static int hist_browser__show_callchain_list(struct hist_browser *browser,
853 					     struct callchain_node *node,
854 					     struct callchain_list *chain,
855 					     unsigned short row, u64 total,
856 					     bool need_percent, int offset,
857 					     print_callchain_entry_fn print,
858 					     struct callchain_print_arg *arg)
859 {
860 	char bf[1024], *alloc_str;
861 	char buf[64], *alloc_str2;
862 	const char *str;
863 	int ret = 1;
864 
865 	if (arg->row_offset != 0) {
866 		arg->row_offset--;
867 		return 0;
868 	}
869 
870 	alloc_str = NULL;
871 	alloc_str2 = NULL;
872 
873 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
874 				       browser->show_dso);
875 
876 	if (symbol_conf.show_branchflag_count) {
877 		callchain_list_counts__printf_value(chain, NULL,
878 						    buf, sizeof(buf));
879 
880 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
881 			str = "Not enough memory!";
882 		else
883 			str = alloc_str2;
884 	}
885 
886 	if (need_percent) {
887 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
888 						total);
889 
890 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
891 			str = "Not enough memory!";
892 		else
893 			str = alloc_str;
894 	}
895 
896 	print(browser, chain, str, offset, row, arg);
897 	free(alloc_str);
898 	free(alloc_str2);
899 
900 	return ret;
901 }
902 
903 static bool check_percent_display(struct rb_node *node, u64 parent_total)
904 {
905 	struct callchain_node *child;
906 
907 	if (node == NULL)
908 		return false;
909 
910 	if (rb_next(node))
911 		return true;
912 
913 	child = rb_entry(node, struct callchain_node, rb_node);
914 	return callchain_cumul_hits(child) != parent_total;
915 }
916 
917 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
918 					     struct rb_root *root,
919 					     unsigned short row, u64 total,
920 					     u64 parent_total,
921 					     print_callchain_entry_fn print,
922 					     struct callchain_print_arg *arg,
923 					     check_output_full_fn is_output_full)
924 {
925 	struct rb_node *node;
926 	int first_row = row, offset = LEVEL_OFFSET_STEP;
927 	bool need_percent;
928 
929 	node = rb_first(root);
930 	need_percent = check_percent_display(node, parent_total);
931 
932 	while (node) {
933 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
934 		struct rb_node *next = rb_next(node);
935 		struct callchain_list *chain;
936 		char folded_sign = ' ';
937 		int first = true;
938 		int extra_offset = 0;
939 
940 		list_for_each_entry(chain, &child->parent_val, list) {
941 			bool was_first = first;
942 
943 			if (first)
944 				first = false;
945 			else if (need_percent)
946 				extra_offset = LEVEL_OFFSET_STEP;
947 
948 			folded_sign = callchain_list__folded(chain);
949 
950 			row += hist_browser__show_callchain_list(browser, child,
951 							chain, row, total,
952 							was_first && need_percent,
953 							offset + extra_offset,
954 							print, arg);
955 
956 			if (is_output_full(browser, row))
957 				goto out;
958 
959 			if (folded_sign == '+')
960 				goto next;
961 		}
962 
963 		list_for_each_entry(chain, &child->val, list) {
964 			bool was_first = first;
965 
966 			if (first)
967 				first = false;
968 			else if (need_percent)
969 				extra_offset = LEVEL_OFFSET_STEP;
970 
971 			folded_sign = callchain_list__folded(chain);
972 
973 			row += hist_browser__show_callchain_list(browser, child,
974 							chain, row, total,
975 							was_first && need_percent,
976 							offset + extra_offset,
977 							print, arg);
978 
979 			if (is_output_full(browser, row))
980 				goto out;
981 
982 			if (folded_sign == '+')
983 				break;
984 		}
985 
986 next:
987 		if (is_output_full(browser, row))
988 			break;
989 		node = next;
990 	}
991 out:
992 	return row - first_row;
993 }
994 
995 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
996 						struct callchain_list *chain,
997 						char *value_str, char *old_str)
998 {
999 	char bf[1024];
1000 	const char *str;
1001 	char *new;
1002 
1003 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
1004 				       browser->show_dso);
1005 	if (old_str) {
1006 		if (asprintf(&new, "%s%s%s", old_str,
1007 			     symbol_conf.field_sep ?: ";", str) < 0)
1008 			new = NULL;
1009 	} else {
1010 		if (value_str) {
1011 			if (asprintf(&new, "%s %s", value_str, str) < 0)
1012 				new = NULL;
1013 		} else {
1014 			if (asprintf(&new, "%s", str) < 0)
1015 				new = NULL;
1016 		}
1017 	}
1018 	return new;
1019 }
1020 
1021 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1022 					       struct rb_root *root,
1023 					       unsigned short row, u64 total,
1024 					       u64 parent_total,
1025 					       print_callchain_entry_fn print,
1026 					       struct callchain_print_arg *arg,
1027 					       check_output_full_fn is_output_full)
1028 {
1029 	struct rb_node *node;
1030 	int first_row = row, offset = LEVEL_OFFSET_STEP;
1031 	bool need_percent;
1032 
1033 	node = rb_first(root);
1034 	need_percent = check_percent_display(node, parent_total);
1035 
1036 	while (node) {
1037 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1038 		struct rb_node *next = rb_next(node);
1039 		struct callchain_list *chain, *first_chain = NULL;
1040 		int first = true;
1041 		char *value_str = NULL, *value_str_alloc = NULL;
1042 		char *chain_str = NULL, *chain_str_alloc = NULL;
1043 
1044 		if (arg->row_offset != 0) {
1045 			arg->row_offset--;
1046 			goto next;
1047 		}
1048 
1049 		if (need_percent) {
1050 			char buf[64];
1051 
1052 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1053 			if (asprintf(&value_str, "%s", buf) < 0) {
1054 				value_str = (char *)"<...>";
1055 				goto do_print;
1056 			}
1057 			value_str_alloc = value_str;
1058 		}
1059 
1060 		list_for_each_entry(chain, &child->parent_val, list) {
1061 			chain_str = hist_browser__folded_callchain_str(browser,
1062 						chain, value_str, chain_str);
1063 			if (first) {
1064 				first = false;
1065 				first_chain = chain;
1066 			}
1067 
1068 			if (chain_str == NULL) {
1069 				chain_str = (char *)"Not enough memory!";
1070 				goto do_print;
1071 			}
1072 
1073 			chain_str_alloc = chain_str;
1074 		}
1075 
1076 		list_for_each_entry(chain, &child->val, list) {
1077 			chain_str = hist_browser__folded_callchain_str(browser,
1078 						chain, value_str, chain_str);
1079 			if (first) {
1080 				first = false;
1081 				first_chain = chain;
1082 			}
1083 
1084 			if (chain_str == NULL) {
1085 				chain_str = (char *)"Not enough memory!";
1086 				goto do_print;
1087 			}
1088 
1089 			chain_str_alloc = chain_str;
1090 		}
1091 
1092 do_print:
1093 		print(browser, first_chain, chain_str, offset, row++, arg);
1094 		free(value_str_alloc);
1095 		free(chain_str_alloc);
1096 
1097 next:
1098 		if (is_output_full(browser, row))
1099 			break;
1100 		node = next;
1101 	}
1102 
1103 	return row - first_row;
1104 }
1105 
1106 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1107 					struct rb_root *root, int level,
1108 					unsigned short row, u64 total,
1109 					u64 parent_total,
1110 					print_callchain_entry_fn print,
1111 					struct callchain_print_arg *arg,
1112 					check_output_full_fn is_output_full)
1113 {
1114 	struct rb_node *node;
1115 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1116 	bool need_percent;
1117 	u64 percent_total = total;
1118 
1119 	if (callchain_param.mode == CHAIN_GRAPH_REL)
1120 		percent_total = parent_total;
1121 
1122 	node = rb_first(root);
1123 	need_percent = check_percent_display(node, parent_total);
1124 
1125 	while (node) {
1126 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1127 		struct rb_node *next = rb_next(node);
1128 		struct callchain_list *chain;
1129 		char folded_sign = ' ';
1130 		int first = true;
1131 		int extra_offset = 0;
1132 
1133 		list_for_each_entry(chain, &child->val, list) {
1134 			bool was_first = first;
1135 
1136 			if (first)
1137 				first = false;
1138 			else if (need_percent)
1139 				extra_offset = LEVEL_OFFSET_STEP;
1140 
1141 			folded_sign = callchain_list__folded(chain);
1142 
1143 			row += hist_browser__show_callchain_list(browser, child,
1144 							chain, row, percent_total,
1145 							was_first && need_percent,
1146 							offset + extra_offset,
1147 							print, arg);
1148 
1149 			if (is_output_full(browser, row))
1150 				goto out;
1151 
1152 			if (folded_sign == '+')
1153 				break;
1154 		}
1155 
1156 		if (folded_sign == '-') {
1157 			const int new_level = level + (extra_offset ? 2 : 1);
1158 
1159 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1160 							    new_level, row, total,
1161 							    child->children_hit,
1162 							    print, arg, is_output_full);
1163 		}
1164 		if (is_output_full(browser, row))
1165 			break;
1166 		node = next;
1167 	}
1168 out:
1169 	return row - first_row;
1170 }
1171 
1172 static int hist_browser__show_callchain(struct hist_browser *browser,
1173 					struct hist_entry *entry, int level,
1174 					unsigned short row,
1175 					print_callchain_entry_fn print,
1176 					struct callchain_print_arg *arg,
1177 					check_output_full_fn is_output_full)
1178 {
1179 	u64 total = hists__total_period(entry->hists);
1180 	u64 parent_total;
1181 	int printed;
1182 
1183 	if (symbol_conf.cumulate_callchain)
1184 		parent_total = entry->stat_acc->period;
1185 	else
1186 		parent_total = entry->stat.period;
1187 
1188 	if (callchain_param.mode == CHAIN_FLAT) {
1189 		printed = hist_browser__show_callchain_flat(browser,
1190 						&entry->sorted_chain, row,
1191 						total, parent_total, print, arg,
1192 						is_output_full);
1193 	} else if (callchain_param.mode == CHAIN_FOLDED) {
1194 		printed = hist_browser__show_callchain_folded(browser,
1195 						&entry->sorted_chain, row,
1196 						total, parent_total, print, arg,
1197 						is_output_full);
1198 	} else {
1199 		printed = hist_browser__show_callchain_graph(browser,
1200 						&entry->sorted_chain, level, row,
1201 						total, parent_total, print, arg,
1202 						is_output_full);
1203 	}
1204 
1205 	if (arg->is_current_entry)
1206 		browser->he_selection = entry;
1207 
1208 	return printed;
1209 }
1210 
1211 struct hpp_arg {
1212 	struct ui_browser *b;
1213 	char folded_sign;
1214 	bool current_entry;
1215 };
1216 
1217 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1218 {
1219 	struct hpp_arg *arg = hpp->ptr;
1220 	int ret, len;
1221 	va_list args;
1222 	double percent;
1223 
1224 	va_start(args, fmt);
1225 	len = va_arg(args, int);
1226 	percent = va_arg(args, double);
1227 	va_end(args);
1228 
1229 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1230 
1231 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1232 	ui_browser__printf(arg->b, "%s", hpp->buf);
1233 
1234 	return ret;
1235 }
1236 
1237 #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
1238 static u64 __hpp_get_##_field(struct hist_entry *he)			\
1239 {									\
1240 	return he->stat._field;						\
1241 }									\
1242 									\
1243 static int								\
1244 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1245 				struct perf_hpp *hpp,			\
1246 				struct hist_entry *he)			\
1247 {									\
1248 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
1249 			__hpp__slsmg_color_printf, true);		\
1250 }
1251 
1252 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
1253 static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
1254 {									\
1255 	return he->stat_acc->_field;					\
1256 }									\
1257 									\
1258 static int								\
1259 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1260 				struct perf_hpp *hpp,			\
1261 				struct hist_entry *he)			\
1262 {									\
1263 	if (!symbol_conf.cumulate_callchain) {				\
1264 		struct hpp_arg *arg = hpp->ptr;				\
1265 		int len = fmt->user_len ?: fmt->len;			\
1266 		int ret = scnprintf(hpp->buf, hpp->size,		\
1267 				    "%*s", len, "N/A");			\
1268 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
1269 									\
1270 		return ret;						\
1271 	}								\
1272 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
1273 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
1274 }
1275 
1276 __HPP_COLOR_PERCENT_FN(overhead, period)
1277 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1278 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1279 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1280 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1281 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1282 
1283 #undef __HPP_COLOR_PERCENT_FN
1284 #undef __HPP_COLOR_ACC_PERCENT_FN
1285 
1286 void hist_browser__init_hpp(void)
1287 {
1288 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1289 				hist_browser__hpp_color_overhead;
1290 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1291 				hist_browser__hpp_color_overhead_sys;
1292 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1293 				hist_browser__hpp_color_overhead_us;
1294 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1295 				hist_browser__hpp_color_overhead_guest_sys;
1296 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1297 				hist_browser__hpp_color_overhead_guest_us;
1298 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1299 				hist_browser__hpp_color_overhead_acc;
1300 
1301 	res_sample_init();
1302 }
1303 
1304 static int hist_browser__show_entry(struct hist_browser *browser,
1305 				    struct hist_entry *entry,
1306 				    unsigned short row)
1307 {
1308 	int printed = 0;
1309 	int width = browser->b.width;
1310 	char folded_sign = ' ';
1311 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1312 	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1313 	off_t row_offset = entry->row_offset;
1314 	bool first = true;
1315 	struct perf_hpp_fmt *fmt;
1316 
1317 	if (current_entry) {
1318 		browser->he_selection = entry;
1319 		browser->selection = &entry->ms;
1320 	}
1321 
1322 	if (use_callchain) {
1323 		hist_entry__init_have_children(entry);
1324 		folded_sign = hist_entry__folded(entry);
1325 	}
1326 
1327 	if (row_offset == 0) {
1328 		struct hpp_arg arg = {
1329 			.b		= &browser->b,
1330 			.folded_sign	= folded_sign,
1331 			.current_entry	= current_entry,
1332 		};
1333 		int column = 0;
1334 
1335 		ui_browser__gotorc(&browser->b, row, 0);
1336 
1337 		hists__for_each_format(browser->hists, fmt) {
1338 			char s[2048];
1339 			struct perf_hpp hpp = {
1340 				.buf	= s,
1341 				.size	= sizeof(s),
1342 				.ptr	= &arg,
1343 			};
1344 
1345 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1346 			    column++ < browser->b.horiz_scroll)
1347 				continue;
1348 
1349 			if (current_entry && browser->b.navkeypressed) {
1350 				ui_browser__set_color(&browser->b,
1351 						      HE_COLORSET_SELECTED);
1352 			} else {
1353 				ui_browser__set_color(&browser->b,
1354 						      HE_COLORSET_NORMAL);
1355 			}
1356 
1357 			if (first) {
1358 				if (use_callchain) {
1359 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1360 					width -= 2;
1361 				}
1362 				first = false;
1363 			} else {
1364 				ui_browser__printf(&browser->b, "  ");
1365 				width -= 2;
1366 			}
1367 
1368 			if (fmt->color) {
1369 				int ret = fmt->color(fmt, &hpp, entry);
1370 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1371 				/*
1372 				 * fmt->color() already used ui_browser to
1373 				 * print the non alignment bits, skip it (+ret):
1374 				 */
1375 				ui_browser__printf(&browser->b, "%s", s + ret);
1376 			} else {
1377 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1378 				ui_browser__printf(&browser->b, "%s", s);
1379 			}
1380 			width -= hpp.buf - s;
1381 		}
1382 
1383 		/* The scroll bar isn't being used */
1384 		if (!browser->b.navkeypressed)
1385 			width += 1;
1386 
1387 		ui_browser__write_nstring(&browser->b, "", width);
1388 
1389 		++row;
1390 		++printed;
1391 	} else
1392 		--row_offset;
1393 
1394 	if (folded_sign == '-' && row != browser->b.rows) {
1395 		struct callchain_print_arg arg = {
1396 			.row_offset = row_offset,
1397 			.is_current_entry = current_entry,
1398 		};
1399 
1400 		printed += hist_browser__show_callchain(browser,
1401 				entry, 1, row,
1402 				hist_browser__show_callchain_entry,
1403 				&arg,
1404 				hist_browser__check_output_full);
1405 	}
1406 
1407 	return printed;
1408 }
1409 
1410 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1411 					      struct hist_entry *entry,
1412 					      unsigned short row,
1413 					      int level)
1414 {
1415 	int printed = 0;
1416 	int width = browser->b.width;
1417 	char folded_sign = ' ';
1418 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1419 	off_t row_offset = entry->row_offset;
1420 	bool first = true;
1421 	struct perf_hpp_fmt *fmt;
1422 	struct perf_hpp_list_node *fmt_node;
1423 	struct hpp_arg arg = {
1424 		.b		= &browser->b,
1425 		.current_entry	= current_entry,
1426 	};
1427 	int column = 0;
1428 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1429 
1430 	if (current_entry) {
1431 		browser->he_selection = entry;
1432 		browser->selection = &entry->ms;
1433 	}
1434 
1435 	hist_entry__init_have_children(entry);
1436 	folded_sign = hist_entry__folded(entry);
1437 	arg.folded_sign = folded_sign;
1438 
1439 	if (entry->leaf && row_offset) {
1440 		row_offset--;
1441 		goto show_callchain;
1442 	}
1443 
1444 	ui_browser__gotorc(&browser->b, row, 0);
1445 
1446 	if (current_entry && browser->b.navkeypressed)
1447 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1448 	else
1449 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1450 
1451 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1452 	width -= level * HIERARCHY_INDENT;
1453 
1454 	/* the first hpp_list_node is for overhead columns */
1455 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1456 				    struct perf_hpp_list_node, list);
1457 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1458 		char s[2048];
1459 		struct perf_hpp hpp = {
1460 			.buf		= s,
1461 			.size		= sizeof(s),
1462 			.ptr		= &arg,
1463 		};
1464 
1465 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1466 		    column++ < browser->b.horiz_scroll)
1467 			continue;
1468 
1469 		if (current_entry && browser->b.navkeypressed) {
1470 			ui_browser__set_color(&browser->b,
1471 					      HE_COLORSET_SELECTED);
1472 		} else {
1473 			ui_browser__set_color(&browser->b,
1474 					      HE_COLORSET_NORMAL);
1475 		}
1476 
1477 		if (first) {
1478 			ui_browser__printf(&browser->b, "%c ", folded_sign);
1479 			width -= 2;
1480 			first = false;
1481 		} else {
1482 			ui_browser__printf(&browser->b, "  ");
1483 			width -= 2;
1484 		}
1485 
1486 		if (fmt->color) {
1487 			int ret = fmt->color(fmt, &hpp, entry);
1488 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1489 			/*
1490 			 * fmt->color() already used ui_browser to
1491 			 * print the non alignment bits, skip it (+ret):
1492 			 */
1493 			ui_browser__printf(&browser->b, "%s", s + ret);
1494 		} else {
1495 			int ret = fmt->entry(fmt, &hpp, entry);
1496 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1497 			ui_browser__printf(&browser->b, "%s", s);
1498 		}
1499 		width -= hpp.buf - s;
1500 	}
1501 
1502 	if (!first) {
1503 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1504 		width -= hierarchy_indent;
1505 	}
1506 
1507 	if (column >= browser->b.horiz_scroll) {
1508 		char s[2048];
1509 		struct perf_hpp hpp = {
1510 			.buf		= s,
1511 			.size		= sizeof(s),
1512 			.ptr		= &arg,
1513 		};
1514 
1515 		if (current_entry && browser->b.navkeypressed) {
1516 			ui_browser__set_color(&browser->b,
1517 					      HE_COLORSET_SELECTED);
1518 		} else {
1519 			ui_browser__set_color(&browser->b,
1520 					      HE_COLORSET_NORMAL);
1521 		}
1522 
1523 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1524 			if (first) {
1525 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1526 				first = false;
1527 			} else {
1528 				ui_browser__write_nstring(&browser->b, "", 2);
1529 			}
1530 
1531 			width -= 2;
1532 
1533 			/*
1534 			 * No need to call hist_entry__snprintf_alignment()
1535 			 * since this fmt is always the last column in the
1536 			 * hierarchy mode.
1537 			 */
1538 			if (fmt->color) {
1539 				width -= fmt->color(fmt, &hpp, entry);
1540 			} else {
1541 				int i = 0;
1542 
1543 				width -= fmt->entry(fmt, &hpp, entry);
1544 				ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1545 
1546 				while (isspace(s[i++]))
1547 					width++;
1548 			}
1549 		}
1550 	}
1551 
1552 	/* The scroll bar isn't being used */
1553 	if (!browser->b.navkeypressed)
1554 		width += 1;
1555 
1556 	ui_browser__write_nstring(&browser->b, "", width);
1557 
1558 	++row;
1559 	++printed;
1560 
1561 show_callchain:
1562 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1563 		struct callchain_print_arg carg = {
1564 			.row_offset = row_offset,
1565 		};
1566 
1567 		printed += hist_browser__show_callchain(browser, entry,
1568 					level + 1, row,
1569 					hist_browser__show_callchain_entry, &carg,
1570 					hist_browser__check_output_full);
1571 	}
1572 
1573 	return printed;
1574 }
1575 
1576 static int hist_browser__show_no_entry(struct hist_browser *browser,
1577 				       unsigned short row, int level)
1578 {
1579 	int width = browser->b.width;
1580 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1581 	bool first = true;
1582 	int column = 0;
1583 	int ret;
1584 	struct perf_hpp_fmt *fmt;
1585 	struct perf_hpp_list_node *fmt_node;
1586 	int indent = browser->hists->nr_hpp_node - 2;
1587 
1588 	if (current_entry) {
1589 		browser->he_selection = NULL;
1590 		browser->selection = NULL;
1591 	}
1592 
1593 	ui_browser__gotorc(&browser->b, row, 0);
1594 
1595 	if (current_entry && browser->b.navkeypressed)
1596 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1597 	else
1598 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1599 
1600 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1601 	width -= level * HIERARCHY_INDENT;
1602 
1603 	/* the first hpp_list_node is for overhead columns */
1604 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1605 				    struct perf_hpp_list_node, list);
1606 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1607 		if (perf_hpp__should_skip(fmt, browser->hists) ||
1608 		    column++ < browser->b.horiz_scroll)
1609 			continue;
1610 
1611 		ret = fmt->width(fmt, NULL, browser->hists);
1612 
1613 		if (first) {
1614 			/* for folded sign */
1615 			first = false;
1616 			ret++;
1617 		} else {
1618 			/* space between columns */
1619 			ret += 2;
1620 		}
1621 
1622 		ui_browser__write_nstring(&browser->b, "", ret);
1623 		width -= ret;
1624 	}
1625 
1626 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1627 	width -= indent * HIERARCHY_INDENT;
1628 
1629 	if (column >= browser->b.horiz_scroll) {
1630 		char buf[32];
1631 
1632 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1633 		ui_browser__printf(&browser->b, "  %s", buf);
1634 		width -= ret + 2;
1635 	}
1636 
1637 	/* The scroll bar isn't being used */
1638 	if (!browser->b.navkeypressed)
1639 		width += 1;
1640 
1641 	ui_browser__write_nstring(&browser->b, "", width);
1642 	return 1;
1643 }
1644 
1645 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1646 {
1647 	advance_hpp(hpp, inc);
1648 	return hpp->size <= 0;
1649 }
1650 
1651 static int
1652 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1653 				 size_t size, int line)
1654 {
1655 	struct hists *hists = browser->hists;
1656 	struct perf_hpp dummy_hpp = {
1657 		.buf    = buf,
1658 		.size   = size,
1659 	};
1660 	struct perf_hpp_fmt *fmt;
1661 	size_t ret = 0;
1662 	int column = 0;
1663 	int span = 0;
1664 
1665 	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1666 		ret = scnprintf(buf, size, "  ");
1667 		if (advance_hpp_check(&dummy_hpp, ret))
1668 			return ret;
1669 	}
1670 
1671 	hists__for_each_format(browser->hists, fmt) {
1672 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1673 			continue;
1674 
1675 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1676 		if (advance_hpp_check(&dummy_hpp, ret))
1677 			break;
1678 
1679 		if (span)
1680 			continue;
1681 
1682 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1683 		if (advance_hpp_check(&dummy_hpp, ret))
1684 			break;
1685 	}
1686 
1687 	return ret;
1688 }
1689 
1690 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1691 {
1692 	struct hists *hists = browser->hists;
1693 	struct perf_hpp dummy_hpp = {
1694 		.buf    = buf,
1695 		.size   = size,
1696 	};
1697 	struct perf_hpp_fmt *fmt;
1698 	struct perf_hpp_list_node *fmt_node;
1699 	size_t ret = 0;
1700 	int column = 0;
1701 	int indent = hists->nr_hpp_node - 2;
1702 	bool first_node, first_col;
1703 
1704 	ret = scnprintf(buf, size, "  ");
1705 	if (advance_hpp_check(&dummy_hpp, ret))
1706 		return ret;
1707 
1708 	first_node = true;
1709 	/* the first hpp_list_node is for overhead columns */
1710 	fmt_node = list_first_entry(&hists->hpp_formats,
1711 				    struct perf_hpp_list_node, list);
1712 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1713 		if (column++ < browser->b.horiz_scroll)
1714 			continue;
1715 
1716 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1717 		if (advance_hpp_check(&dummy_hpp, ret))
1718 			break;
1719 
1720 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1721 		if (advance_hpp_check(&dummy_hpp, ret))
1722 			break;
1723 
1724 		first_node = false;
1725 	}
1726 
1727 	if (!first_node) {
1728 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1729 				indent * HIERARCHY_INDENT, "");
1730 		if (advance_hpp_check(&dummy_hpp, ret))
1731 			return ret;
1732 	}
1733 
1734 	first_node = true;
1735 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1736 		if (!first_node) {
1737 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1738 			if (advance_hpp_check(&dummy_hpp, ret))
1739 				break;
1740 		}
1741 		first_node = false;
1742 
1743 		first_col = true;
1744 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1745 			char *start;
1746 
1747 			if (perf_hpp__should_skip(fmt, hists))
1748 				continue;
1749 
1750 			if (!first_col) {
1751 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1752 				if (advance_hpp_check(&dummy_hpp, ret))
1753 					break;
1754 			}
1755 			first_col = false;
1756 
1757 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1758 			dummy_hpp.buf[ret] = '\0';
1759 
1760 			start = strim(dummy_hpp.buf);
1761 			ret = strlen(start);
1762 
1763 			if (start != dummy_hpp.buf)
1764 				memmove(dummy_hpp.buf, start, ret + 1);
1765 
1766 			if (advance_hpp_check(&dummy_hpp, ret))
1767 				break;
1768 		}
1769 	}
1770 
1771 	return ret;
1772 }
1773 
1774 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1775 {
1776 	char headers[1024];
1777 
1778 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1779 						   sizeof(headers));
1780 
1781 	ui_browser__gotorc(&browser->b, 0, 0);
1782 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1783 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1784 }
1785 
1786 static void hists_browser__headers(struct hist_browser *browser)
1787 {
1788 	struct hists *hists = browser->hists;
1789 	struct perf_hpp_list *hpp_list = hists->hpp_list;
1790 
1791 	int line;
1792 
1793 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
1794 		char headers[1024];
1795 
1796 		hists_browser__scnprintf_headers(browser, headers,
1797 						 sizeof(headers), line);
1798 
1799 		ui_browser__gotorc_title(&browser->b, line, 0);
1800 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1801 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1802 	}
1803 }
1804 
1805 static void hist_browser__show_headers(struct hist_browser *browser)
1806 {
1807 	if (symbol_conf.report_hierarchy)
1808 		hists_browser__hierarchy_headers(browser);
1809 	else
1810 		hists_browser__headers(browser);
1811 }
1812 
1813 static void ui_browser__hists_init_top(struct ui_browser *browser)
1814 {
1815 	if (browser->top == NULL) {
1816 		struct hist_browser *hb;
1817 
1818 		hb = container_of(browser, struct hist_browser, b);
1819 		browser->top = rb_first_cached(&hb->hists->entries);
1820 	}
1821 }
1822 
1823 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1824 {
1825 	unsigned row = 0;
1826 	struct rb_node *nd;
1827 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1828 
1829 	if (hb->show_headers)
1830 		hist_browser__show_headers(hb);
1831 
1832 	ui_browser__hists_init_top(browser);
1833 	hb->he_selection = NULL;
1834 	hb->selection = NULL;
1835 
1836 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1837 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1838 		float percent;
1839 
1840 		if (h->filtered) {
1841 			/* let it move to sibling */
1842 			h->unfolded = false;
1843 			continue;
1844 		}
1845 
1846 		if (symbol_conf.report_individual_block)
1847 			percent = block_info__total_cycles_percent(h);
1848 		else
1849 			percent = hist_entry__get_percent_limit(h);
1850 
1851 		if (percent < hb->min_pcnt)
1852 			continue;
1853 
1854 		if (symbol_conf.report_hierarchy) {
1855 			row += hist_browser__show_hierarchy_entry(hb, h, row,
1856 								  h->depth);
1857 			if (row == browser->rows)
1858 				break;
1859 
1860 			if (h->has_no_entry) {
1861 				hist_browser__show_no_entry(hb, row, h->depth + 1);
1862 				row++;
1863 			}
1864 		} else {
1865 			row += hist_browser__show_entry(hb, h, row);
1866 		}
1867 
1868 		if (row == browser->rows)
1869 			break;
1870 	}
1871 
1872 	return row;
1873 }
1874 
1875 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1876 					     float min_pcnt)
1877 {
1878 	while (nd != NULL) {
1879 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1880 		float percent = hist_entry__get_percent_limit(h);
1881 
1882 		if (!h->filtered && percent >= min_pcnt)
1883 			return nd;
1884 
1885 		/*
1886 		 * If it's filtered, its all children also were filtered.
1887 		 * So move to sibling node.
1888 		 */
1889 		if (rb_next(nd))
1890 			nd = rb_next(nd);
1891 		else
1892 			nd = rb_hierarchy_next(nd);
1893 	}
1894 
1895 	return NULL;
1896 }
1897 
1898 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1899 						  float min_pcnt)
1900 {
1901 	while (nd != NULL) {
1902 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1903 		float percent = hist_entry__get_percent_limit(h);
1904 
1905 		if (!h->filtered && percent >= min_pcnt)
1906 			return nd;
1907 
1908 		nd = rb_hierarchy_prev(nd);
1909 	}
1910 
1911 	return NULL;
1912 }
1913 
1914 static void ui_browser__hists_seek(struct ui_browser *browser,
1915 				   off_t offset, int whence)
1916 {
1917 	struct hist_entry *h;
1918 	struct rb_node *nd;
1919 	bool first = true;
1920 	struct hist_browser *hb;
1921 
1922 	hb = container_of(browser, struct hist_browser, b);
1923 
1924 	if (browser->nr_entries == 0)
1925 		return;
1926 
1927 	ui_browser__hists_init_top(browser);
1928 
1929 	switch (whence) {
1930 	case SEEK_SET:
1931 		nd = hists__filter_entries(rb_first(browser->entries),
1932 					   hb->min_pcnt);
1933 		break;
1934 	case SEEK_CUR:
1935 		nd = browser->top;
1936 		goto do_offset;
1937 	case SEEK_END:
1938 		nd = rb_hierarchy_last(rb_last(browser->entries));
1939 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1940 		first = false;
1941 		break;
1942 	default:
1943 		return;
1944 	}
1945 
1946 	/*
1947 	 * Moves not relative to the first visible entry invalidates its
1948 	 * row_offset:
1949 	 */
1950 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1951 	h->row_offset = 0;
1952 
1953 	/*
1954 	 * Here we have to check if nd is expanded (+), if it is we can't go
1955 	 * the next top level hist_entry, instead we must compute an offset of
1956 	 * what _not_ to show and not change the first visible entry.
1957 	 *
1958 	 * This offset increments when we are going from top to bottom and
1959 	 * decreases when we're going from bottom to top.
1960 	 *
1961 	 * As we don't have backpointers to the top level in the callchains
1962 	 * structure, we need to always print the whole hist_entry callchain,
1963 	 * skipping the first ones that are before the first visible entry
1964 	 * and stop when we printed enough lines to fill the screen.
1965 	 */
1966 do_offset:
1967 	if (!nd)
1968 		return;
1969 
1970 	if (offset > 0) {
1971 		do {
1972 			h = rb_entry(nd, struct hist_entry, rb_node);
1973 			if (h->unfolded && h->leaf) {
1974 				u16 remaining = h->nr_rows - h->row_offset;
1975 				if (offset > remaining) {
1976 					offset -= remaining;
1977 					h->row_offset = 0;
1978 				} else {
1979 					h->row_offset += offset;
1980 					offset = 0;
1981 					browser->top = nd;
1982 					break;
1983 				}
1984 			}
1985 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1986 						   hb->min_pcnt);
1987 			if (nd == NULL)
1988 				break;
1989 			--offset;
1990 			browser->top = nd;
1991 		} while (offset != 0);
1992 	} else if (offset < 0) {
1993 		while (1) {
1994 			h = rb_entry(nd, struct hist_entry, rb_node);
1995 			if (h->unfolded && h->leaf) {
1996 				if (first) {
1997 					if (-offset > h->row_offset) {
1998 						offset += h->row_offset;
1999 						h->row_offset = 0;
2000 					} else {
2001 						h->row_offset += offset;
2002 						offset = 0;
2003 						browser->top = nd;
2004 						break;
2005 					}
2006 				} else {
2007 					if (-offset > h->nr_rows) {
2008 						offset += h->nr_rows;
2009 						h->row_offset = 0;
2010 					} else {
2011 						h->row_offset = h->nr_rows + offset;
2012 						offset = 0;
2013 						browser->top = nd;
2014 						break;
2015 					}
2016 				}
2017 			}
2018 
2019 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2020 							hb->min_pcnt);
2021 			if (nd == NULL)
2022 				break;
2023 			++offset;
2024 			browser->top = nd;
2025 			if (offset == 0) {
2026 				/*
2027 				 * Last unfiltered hist_entry, check if it is
2028 				 * unfolded, if it is then we should have
2029 				 * row_offset at its last entry.
2030 				 */
2031 				h = rb_entry(nd, struct hist_entry, rb_node);
2032 				if (h->unfolded && h->leaf)
2033 					h->row_offset = h->nr_rows;
2034 				break;
2035 			}
2036 			first = false;
2037 		}
2038 	} else {
2039 		browser->top = nd;
2040 		h = rb_entry(nd, struct hist_entry, rb_node);
2041 		h->row_offset = 0;
2042 	}
2043 }
2044 
2045 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2046 					   struct hist_entry *he, FILE *fp,
2047 					   int level)
2048 {
2049 	struct callchain_print_arg arg  = {
2050 		.fp = fp,
2051 	};
2052 
2053 	hist_browser__show_callchain(browser, he, level, 0,
2054 				     hist_browser__fprintf_callchain_entry, &arg,
2055 				     hist_browser__check_dump_full);
2056 	return arg.printed;
2057 }
2058 
2059 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2060 				       struct hist_entry *he, FILE *fp)
2061 {
2062 	char s[8192];
2063 	int printed = 0;
2064 	char folded_sign = ' ';
2065 	struct perf_hpp hpp = {
2066 		.buf = s,
2067 		.size = sizeof(s),
2068 	};
2069 	struct perf_hpp_fmt *fmt;
2070 	bool first = true;
2071 	int ret;
2072 
2073 	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2074 		folded_sign = hist_entry__folded(he);
2075 		printed += fprintf(fp, "%c ", folded_sign);
2076 	}
2077 
2078 	hists__for_each_format(browser->hists, fmt) {
2079 		if (perf_hpp__should_skip(fmt, he->hists))
2080 			continue;
2081 
2082 		if (!first) {
2083 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2084 			advance_hpp(&hpp, ret);
2085 		} else
2086 			first = false;
2087 
2088 		ret = fmt->entry(fmt, &hpp, he);
2089 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2090 		advance_hpp(&hpp, ret);
2091 	}
2092 	printed += fprintf(fp, "%s\n", s);
2093 
2094 	if (folded_sign == '-')
2095 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2096 
2097 	return printed;
2098 }
2099 
2100 
2101 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2102 						 struct hist_entry *he,
2103 						 FILE *fp, int level)
2104 {
2105 	char s[8192];
2106 	int printed = 0;
2107 	char folded_sign = ' ';
2108 	struct perf_hpp hpp = {
2109 		.buf = s,
2110 		.size = sizeof(s),
2111 	};
2112 	struct perf_hpp_fmt *fmt;
2113 	struct perf_hpp_list_node *fmt_node;
2114 	bool first = true;
2115 	int ret;
2116 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2117 
2118 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2119 
2120 	folded_sign = hist_entry__folded(he);
2121 	printed += fprintf(fp, "%c", folded_sign);
2122 
2123 	/* the first hpp_list_node is for overhead columns */
2124 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2125 				    struct perf_hpp_list_node, list);
2126 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2127 		if (!first) {
2128 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2129 			advance_hpp(&hpp, ret);
2130 		} else
2131 			first = false;
2132 
2133 		ret = fmt->entry(fmt, &hpp, he);
2134 		advance_hpp(&hpp, ret);
2135 	}
2136 
2137 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2138 	advance_hpp(&hpp, ret);
2139 
2140 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2141 		ret = scnprintf(hpp.buf, hpp.size, "  ");
2142 		advance_hpp(&hpp, ret);
2143 
2144 		ret = fmt->entry(fmt, &hpp, he);
2145 		advance_hpp(&hpp, ret);
2146 	}
2147 
2148 	strim(s);
2149 	printed += fprintf(fp, "%s\n", s);
2150 
2151 	if (he->leaf && folded_sign == '-') {
2152 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2153 							   he->depth + 1);
2154 	}
2155 
2156 	return printed;
2157 }
2158 
2159 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2160 {
2161 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2162 						   browser->min_pcnt);
2163 	int printed = 0;
2164 
2165 	while (nd) {
2166 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2167 
2168 		if (symbol_conf.report_hierarchy) {
2169 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2170 									 h, fp,
2171 									 h->depth);
2172 		} else {
2173 			printed += hist_browser__fprintf_entry(browser, h, fp);
2174 		}
2175 
2176 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2177 					   browser->min_pcnt);
2178 	}
2179 
2180 	return printed;
2181 }
2182 
2183 static int hist_browser__dump(struct hist_browser *browser)
2184 {
2185 	char filename[64];
2186 	FILE *fp;
2187 
2188 	while (1) {
2189 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2190 		if (access(filename, F_OK))
2191 			break;
2192 		/*
2193  		 * XXX: Just an arbitrary lazy upper limit
2194  		 */
2195 		if (++browser->print_seq == 8192) {
2196 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2197 			return -1;
2198 		}
2199 	}
2200 
2201 	fp = fopen(filename, "w");
2202 	if (fp == NULL) {
2203 		char bf[64];
2204 		const char *err = str_error_r(errno, bf, sizeof(bf));
2205 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2206 		return -1;
2207 	}
2208 
2209 	++browser->print_seq;
2210 	hist_browser__fprintf(browser, fp);
2211 	fclose(fp);
2212 	ui_helpline__fpush("%s written!", filename);
2213 
2214 	return 0;
2215 }
2216 
2217 void hist_browser__init(struct hist_browser *browser,
2218 			struct hists *hists)
2219 {
2220 	struct perf_hpp_fmt *fmt;
2221 
2222 	browser->hists			= hists;
2223 	browser->b.refresh		= hist_browser__refresh;
2224 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
2225 	browser->b.seek			= ui_browser__hists_seek;
2226 	browser->b.use_navkeypressed	= true;
2227 	browser->show_headers		= symbol_conf.show_hist_headers;
2228 	hist_browser__set_title_space(browser);
2229 
2230 	if (symbol_conf.report_hierarchy) {
2231 		struct perf_hpp_list_node *fmt_node;
2232 
2233 		/* count overhead columns (in the first node) */
2234 		fmt_node = list_first_entry(&hists->hpp_formats,
2235 					    struct perf_hpp_list_node, list);
2236 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2237 			++browser->b.columns;
2238 
2239 		/* add a single column for whole hierarchy sort keys*/
2240 		++browser->b.columns;
2241 	} else {
2242 		hists__for_each_format(hists, fmt)
2243 			++browser->b.columns;
2244 	}
2245 
2246 	hists__reset_column_width(hists);
2247 }
2248 
2249 struct hist_browser *hist_browser__new(struct hists *hists)
2250 {
2251 	struct hist_browser *browser = zalloc(sizeof(*browser));
2252 
2253 	if (browser)
2254 		hist_browser__init(browser, hists);
2255 
2256 	return browser;
2257 }
2258 
2259 static struct hist_browser *
2260 perf_evsel_browser__new(struct evsel *evsel,
2261 			struct hist_browser_timer *hbt,
2262 			struct perf_env *env,
2263 			struct annotation_options *annotation_opts)
2264 {
2265 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2266 
2267 	if (browser) {
2268 		browser->hbt   = hbt;
2269 		browser->env   = env;
2270 		browser->title = hists_browser__scnprintf_title;
2271 		browser->annotation_opts = annotation_opts;
2272 	}
2273 	return browser;
2274 }
2275 
2276 void hist_browser__delete(struct hist_browser *browser)
2277 {
2278 	free(browser);
2279 }
2280 
2281 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2282 {
2283 	return browser->he_selection;
2284 }
2285 
2286 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2287 {
2288 	return browser->he_selection->thread;
2289 }
2290 
2291 /* Check whether the browser is for 'top' or 'report' */
2292 static inline bool is_report_browser(void *timer)
2293 {
2294 	return timer == NULL;
2295 }
2296 
2297 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2298 {
2299 	struct hist_browser_timer *hbt = browser->hbt;
2300 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2301 
2302 	if (!is_report_browser(hbt)) {
2303 		struct perf_top *top = hbt->arg;
2304 
2305 		printed += scnprintf(bf + printed, size - printed,
2306 				     " lost: %" PRIu64 "/%" PRIu64,
2307 				     top->lost, top->lost_total);
2308 
2309 		printed += scnprintf(bf + printed, size - printed,
2310 				     " drop: %" PRIu64 "/%" PRIu64,
2311 				     top->drop, top->drop_total);
2312 
2313 		if (top->zero)
2314 			printed += scnprintf(bf + printed, size - printed, " [z]");
2315 
2316 		perf_top__reset_sample_counters(top);
2317 	}
2318 
2319 
2320 	return printed;
2321 }
2322 
2323 static inline void free_popup_options(char **options, int n)
2324 {
2325 	int i;
2326 
2327 	for (i = 0; i < n; ++i)
2328 		zfree(&options[i]);
2329 }
2330 
2331 /*
2332  * Only runtime switching of perf data file will make "input_name" point
2333  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2334  * whether we need to call free() for current "input_name" during the switch.
2335  */
2336 static bool is_input_name_malloced = false;
2337 
2338 static int switch_data_file(void)
2339 {
2340 	char *pwd, *options[32], *abs_path[32], *tmp;
2341 	DIR *pwd_dir;
2342 	int nr_options = 0, choice = -1, ret = -1;
2343 	struct dirent *dent;
2344 
2345 	pwd = getenv("PWD");
2346 	if (!pwd)
2347 		return ret;
2348 
2349 	pwd_dir = opendir(pwd);
2350 	if (!pwd_dir)
2351 		return ret;
2352 
2353 	memset(options, 0, sizeof(options));
2354 	memset(abs_path, 0, sizeof(abs_path));
2355 
2356 	while ((dent = readdir(pwd_dir))) {
2357 		char path[PATH_MAX];
2358 		u64 magic;
2359 		char *name = dent->d_name;
2360 		FILE *file;
2361 
2362 		if (!(dent->d_type == DT_REG))
2363 			continue;
2364 
2365 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2366 
2367 		file = fopen(path, "r");
2368 		if (!file)
2369 			continue;
2370 
2371 		if (fread(&magic, 1, 8, file) < 8)
2372 			goto close_file_and_continue;
2373 
2374 		if (is_perf_magic(magic)) {
2375 			options[nr_options] = strdup(name);
2376 			if (!options[nr_options])
2377 				goto close_file_and_continue;
2378 
2379 			abs_path[nr_options] = strdup(path);
2380 			if (!abs_path[nr_options]) {
2381 				zfree(&options[nr_options]);
2382 				ui__warning("Can't search all data files due to memory shortage.\n");
2383 				fclose(file);
2384 				break;
2385 			}
2386 
2387 			nr_options++;
2388 		}
2389 
2390 close_file_and_continue:
2391 		fclose(file);
2392 		if (nr_options >= 32) {
2393 			ui__warning("Too many perf data files in PWD!\n"
2394 				    "Only the first 32 files will be listed.\n");
2395 			break;
2396 		}
2397 	}
2398 	closedir(pwd_dir);
2399 
2400 	if (nr_options) {
2401 		choice = ui__popup_menu(nr_options, options, NULL);
2402 		if (choice < nr_options && choice >= 0) {
2403 			tmp = strdup(abs_path[choice]);
2404 			if (tmp) {
2405 				if (is_input_name_malloced)
2406 					free((void *)input_name);
2407 				input_name = tmp;
2408 				is_input_name_malloced = true;
2409 				ret = 0;
2410 			} else
2411 				ui__warning("Data switch failed due to memory shortage!\n");
2412 		}
2413 	}
2414 
2415 	free_popup_options(options, nr_options);
2416 	free_popup_options(abs_path, nr_options);
2417 	return ret;
2418 }
2419 
2420 struct popup_action {
2421 	unsigned long		time;
2422 	struct thread 		*thread;
2423 	struct map_symbol 	ms;
2424 	int			socket;
2425 	struct evsel	*evsel;
2426 	enum rstype		rstype;
2427 
2428 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2429 };
2430 
2431 static int
2432 do_annotate(struct hist_browser *browser, struct popup_action *act)
2433 {
2434 	struct evsel *evsel;
2435 	struct annotation *notes;
2436 	struct hist_entry *he;
2437 	int err;
2438 
2439 	if (!browser->annotation_opts->objdump_path &&
2440 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2441 		return 0;
2442 
2443 	notes = symbol__annotation(act->ms.sym);
2444 	if (!notes->src)
2445 		return 0;
2446 
2447 	if (browser->block_evsel)
2448 		evsel = browser->block_evsel;
2449 	else
2450 		evsel = hists_to_evsel(browser->hists);
2451 
2452 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2453 				       browser->annotation_opts);
2454 	he = hist_browser__selected_entry(browser);
2455 	/*
2456 	 * offer option to annotate the other branch source or target
2457 	 * (if they exists) when returning from annotate
2458 	 */
2459 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2460 		return 1;
2461 
2462 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2463 	if (err)
2464 		ui_browser__handle_resize(&browser->b);
2465 	return 0;
2466 }
2467 
2468 static int
2469 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2470 		 struct popup_action *act, char **optstr,
2471 		 struct map_symbol *ms)
2472 {
2473 	if (ms->sym == NULL || ms->map->dso->annotate_warned ||
2474 	    symbol__annotation(ms->sym)->src == NULL)
2475 		return 0;
2476 
2477 	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2478 		return 0;
2479 
2480 	act->ms = *ms;
2481 	act->fn = do_annotate;
2482 	return 1;
2483 }
2484 
2485 static int
2486 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2487 {
2488 	struct thread *thread = act->thread;
2489 
2490 	if ((!hists__has(browser->hists, thread) &&
2491 	     !hists__has(browser->hists, comm)) || thread == NULL)
2492 		return 0;
2493 
2494 	if (browser->hists->thread_filter) {
2495 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2496 		perf_hpp__set_elide(HISTC_THREAD, false);
2497 		thread__zput(browser->hists->thread_filter);
2498 		ui_helpline__pop();
2499 	} else {
2500 		if (hists__has(browser->hists, thread)) {
2501 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2502 					   thread->comm_set ? thread__comm_str(thread) : "",
2503 					   thread->tid);
2504 		} else {
2505 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2506 					   thread->comm_set ? thread__comm_str(thread) : "");
2507 		}
2508 
2509 		browser->hists->thread_filter = thread__get(thread);
2510 		perf_hpp__set_elide(HISTC_THREAD, false);
2511 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2512 	}
2513 
2514 	hists__filter_by_thread(browser->hists);
2515 	hist_browser__reset(browser);
2516 	return 0;
2517 }
2518 
2519 static int
2520 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2521 	       char **optstr, struct thread *thread)
2522 {
2523 	int ret;
2524 
2525 	if ((!hists__has(browser->hists, thread) &&
2526 	     !hists__has(browser->hists, comm)) || thread == NULL)
2527 		return 0;
2528 
2529 	if (hists__has(browser->hists, thread)) {
2530 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2531 			       browser->hists->thread_filter ? "out of" : "into",
2532 			       thread->comm_set ? thread__comm_str(thread) : "",
2533 			       thread->tid);
2534 	} else {
2535 		ret = asprintf(optstr, "Zoom %s %s thread",
2536 			       browser->hists->thread_filter ? "out of" : "into",
2537 			       thread->comm_set ? thread__comm_str(thread) : "");
2538 	}
2539 	if (ret < 0)
2540 		return 0;
2541 
2542 	act->thread = thread;
2543 	act->fn = do_zoom_thread;
2544 	return 1;
2545 }
2546 
2547 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2548 {
2549 	if (!hists__has(browser->hists, dso) || map == NULL)
2550 		return 0;
2551 
2552 	if (browser->hists->dso_filter) {
2553 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2554 		perf_hpp__set_elide(HISTC_DSO, false);
2555 		browser->hists->dso_filter = NULL;
2556 		ui_helpline__pop();
2557 	} else {
2558 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2559 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2560 		browser->hists->dso_filter = map->dso;
2561 		perf_hpp__set_elide(HISTC_DSO, true);
2562 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2563 	}
2564 
2565 	hists__filter_by_dso(browser->hists);
2566 	hist_browser__reset(browser);
2567 	return 0;
2568 }
2569 
2570 static int
2571 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2572 {
2573 	return hists_browser__zoom_map(browser, act->ms.map);
2574 }
2575 
2576 static int
2577 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2578 	    char **optstr, struct map *map)
2579 {
2580 	if (!hists__has(browser->hists, dso) || map == NULL)
2581 		return 0;
2582 
2583 	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2584 		     browser->hists->dso_filter ? "out of" : "into",
2585 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2586 		return 0;
2587 
2588 	act->ms.map = map;
2589 	act->fn = do_zoom_dso;
2590 	return 1;
2591 }
2592 
2593 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2594 {
2595 	hist_browser__toggle_fold(browser);
2596 	return 0;
2597 }
2598 
2599 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2600 {
2601 	char sym_name[512];
2602 
2603         if (!hist_browser__selection_has_children(browser))
2604                 return 0;
2605 
2606 	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2607 		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2608 		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2609 		return 0;
2610 
2611 	act->fn = do_toggle_callchain;
2612 	return 1;
2613 }
2614 
2615 static int
2616 do_browse_map(struct hist_browser *browser __maybe_unused,
2617 	      struct popup_action *act)
2618 {
2619 	map__browse(act->ms.map);
2620 	return 0;
2621 }
2622 
2623 static int
2624 add_map_opt(struct hist_browser *browser,
2625 	    struct popup_action *act, char **optstr, struct map *map)
2626 {
2627 	if (!hists__has(browser->hists, dso) || map == NULL)
2628 		return 0;
2629 
2630 	if (asprintf(optstr, "Browse map details") < 0)
2631 		return 0;
2632 
2633 	act->ms.map = map;
2634 	act->fn = do_browse_map;
2635 	return 1;
2636 }
2637 
2638 static int
2639 do_run_script(struct hist_browser *browser __maybe_unused,
2640 	      struct popup_action *act)
2641 {
2642 	char *script_opt;
2643 	int len;
2644 	int n = 0;
2645 
2646 	len = 100;
2647 	if (act->thread)
2648 		len += strlen(thread__comm_str(act->thread));
2649 	else if (act->ms.sym)
2650 		len += strlen(act->ms.sym->name);
2651 	script_opt = malloc(len);
2652 	if (!script_opt)
2653 		return -1;
2654 
2655 	script_opt[0] = 0;
2656 	if (act->thread) {
2657 		n = scnprintf(script_opt, len, " -c %s ",
2658 			  thread__comm_str(act->thread));
2659 	} else if (act->ms.sym) {
2660 		n = scnprintf(script_opt, len, " -S %s ",
2661 			  act->ms.sym->name);
2662 	}
2663 
2664 	if (act->time) {
2665 		char start[32], end[32];
2666 		unsigned long starttime = act->time;
2667 		unsigned long endtime = act->time + symbol_conf.time_quantum;
2668 
2669 		if (starttime == endtime) { /* Display 1ms as fallback */
2670 			starttime -= 1*NSEC_PER_MSEC;
2671 			endtime += 1*NSEC_PER_MSEC;
2672 		}
2673 		timestamp__scnprintf_usec(starttime, start, sizeof start);
2674 		timestamp__scnprintf_usec(endtime, end, sizeof end);
2675 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2676 	}
2677 
2678 	script_browse(script_opt, act->evsel);
2679 	free(script_opt);
2680 	return 0;
2681 }
2682 
2683 static int
2684 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2685 		     struct popup_action *act)
2686 {
2687 	struct hist_entry *he;
2688 
2689 	he = hist_browser__selected_entry(browser);
2690 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2691 	return 0;
2692 }
2693 
2694 static int
2695 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2696 	       struct popup_action *act, char **optstr,
2697 	       struct thread *thread, struct symbol *sym,
2698 	       struct evsel *evsel, const char *tstr)
2699 {
2700 
2701 	if (thread) {
2702 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2703 			     thread__comm_str(thread), tstr) < 0)
2704 			return 0;
2705 	} else if (sym) {
2706 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2707 			     sym->name, tstr) < 0)
2708 			return 0;
2709 	} else {
2710 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2711 			return 0;
2712 	}
2713 
2714 	act->thread = thread;
2715 	act->ms.sym = sym;
2716 	act->evsel = evsel;
2717 	act->fn = do_run_script;
2718 	return 1;
2719 }
2720 
2721 static int
2722 add_script_opt(struct hist_browser *browser,
2723 	       struct popup_action *act, char **optstr,
2724 	       struct thread *thread, struct symbol *sym,
2725 	       struct evsel *evsel)
2726 {
2727 	int n, j;
2728 	struct hist_entry *he;
2729 
2730 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2731 
2732 	he = hist_browser__selected_entry(browser);
2733 	if (sort_order && strstr(sort_order, "time")) {
2734 		char tstr[128];
2735 
2736 		optstr++;
2737 		act++;
2738 		j = sprintf(tstr, " in ");
2739 		j += timestamp__scnprintf_usec(he->time, tstr + j,
2740 					       sizeof tstr - j);
2741 		j += sprintf(tstr + j, "-");
2742 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2743 				          tstr + j, sizeof tstr - j);
2744 		n += add_script_opt_2(browser, act, optstr, thread, sym,
2745 					  evsel, tstr);
2746 		act->time = he->time;
2747 	}
2748 	return n;
2749 }
2750 
2751 static int
2752 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2753 		   struct popup_action *act, char **optstr,
2754 		   struct res_sample *res_sample,
2755 		   struct evsel *evsel,
2756 		   enum rstype type)
2757 {
2758 	if (!res_sample)
2759 		return 0;
2760 
2761 	if (asprintf(optstr, "Show context for individual samples %s",
2762 		type == A_ASM ? "with assembler" :
2763 		type == A_SOURCE ? "with source" : "") < 0)
2764 		return 0;
2765 
2766 	act->fn = do_res_sample_script;
2767 	act->evsel = evsel;
2768 	act->rstype = type;
2769 	return 1;
2770 }
2771 
2772 static int
2773 do_switch_data(struct hist_browser *browser __maybe_unused,
2774 	       struct popup_action *act __maybe_unused)
2775 {
2776 	if (switch_data_file()) {
2777 		ui__warning("Won't switch the data files due to\n"
2778 			    "no valid data file get selected!\n");
2779 		return 0;
2780 	}
2781 
2782 	return K_SWITCH_INPUT_DATA;
2783 }
2784 
2785 static int
2786 add_switch_opt(struct hist_browser *browser,
2787 	       struct popup_action *act, char **optstr)
2788 {
2789 	if (!is_report_browser(browser->hbt))
2790 		return 0;
2791 
2792 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2793 		return 0;
2794 
2795 	act->fn = do_switch_data;
2796 	return 1;
2797 }
2798 
2799 static int
2800 do_exit_browser(struct hist_browser *browser __maybe_unused,
2801 		struct popup_action *act __maybe_unused)
2802 {
2803 	return 0;
2804 }
2805 
2806 static int
2807 add_exit_opt(struct hist_browser *browser __maybe_unused,
2808 	     struct popup_action *act, char **optstr)
2809 {
2810 	if (asprintf(optstr, "Exit") < 0)
2811 		return 0;
2812 
2813 	act->fn = do_exit_browser;
2814 	return 1;
2815 }
2816 
2817 static int
2818 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2819 {
2820 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2821 		return 0;
2822 
2823 	if (browser->hists->socket_filter > -1) {
2824 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2825 		browser->hists->socket_filter = -1;
2826 		perf_hpp__set_elide(HISTC_SOCKET, false);
2827 	} else {
2828 		browser->hists->socket_filter = act->socket;
2829 		perf_hpp__set_elide(HISTC_SOCKET, true);
2830 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2831 	}
2832 
2833 	hists__filter_by_socket(browser->hists);
2834 	hist_browser__reset(browser);
2835 	return 0;
2836 }
2837 
2838 static int
2839 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2840 	       char **optstr, int socket_id)
2841 {
2842 	if (!hists__has(browser->hists, socket) || socket_id < 0)
2843 		return 0;
2844 
2845 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2846 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2847 		     socket_id) < 0)
2848 		return 0;
2849 
2850 	act->socket = socket_id;
2851 	act->fn = do_zoom_socket;
2852 	return 1;
2853 }
2854 
2855 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2856 {
2857 	u64 nr_entries = 0;
2858 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2859 
2860 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2861 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2862 		return;
2863 	}
2864 
2865 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2866 		nr_entries++;
2867 		nd = rb_hierarchy_next(nd);
2868 	}
2869 
2870 	hb->nr_non_filtered_entries = nr_entries;
2871 	hb->nr_hierarchy_entries = nr_entries;
2872 }
2873 
2874 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2875 					       double percent)
2876 {
2877 	struct hist_entry *he;
2878 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2879 	u64 total = hists__total_period(hb->hists);
2880 	u64 min_callchain_hits = total * (percent / 100);
2881 
2882 	hb->min_pcnt = callchain_param.min_percent = percent;
2883 
2884 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2885 		he = rb_entry(nd, struct hist_entry, rb_node);
2886 
2887 		if (he->has_no_entry) {
2888 			he->has_no_entry = false;
2889 			he->nr_rows = 0;
2890 		}
2891 
2892 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2893 			goto next;
2894 
2895 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2896 			total = he->stat.period;
2897 
2898 			if (symbol_conf.cumulate_callchain)
2899 				total = he->stat_acc->period;
2900 
2901 			min_callchain_hits = total * (percent / 100);
2902 		}
2903 
2904 		callchain_param.sort(&he->sorted_chain, he->callchain,
2905 				     min_callchain_hits, &callchain_param);
2906 
2907 next:
2908 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2909 
2910 		/* force to re-evaluate folding state of callchains */
2911 		he->init_have_children = false;
2912 		hist_entry__set_folding(he, hb, false);
2913 	}
2914 }
2915 
2916 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2917 				    const char *helpline,
2918 				    bool left_exits,
2919 				    struct hist_browser_timer *hbt,
2920 				    float min_pcnt,
2921 				    struct perf_env *env,
2922 				    bool warn_lost_event,
2923 				    struct annotation_options *annotation_opts)
2924 {
2925 	struct hists *hists = evsel__hists(evsel);
2926 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2927 	struct branch_info *bi = NULL;
2928 #define MAX_OPTIONS  16
2929 	char *options[MAX_OPTIONS];
2930 	struct popup_action actions[MAX_OPTIONS];
2931 	int nr_options = 0;
2932 	int key = -1;
2933 	char buf[64];
2934 	int delay_secs = hbt ? hbt->refresh : 0;
2935 
2936 #define HIST_BROWSER_HELP_COMMON					\
2937 	"h/?/F1        Show this window\n"				\
2938 	"UP/DOWN/PGUP\n"						\
2939 	"PGDN/SPACE    Navigate\n"					\
2940 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2941 	"For multiple event sessions:\n\n"				\
2942 	"TAB/UNTAB     Switch events\n\n"				\
2943 	"For symbolic views (--sort has sym):\n\n"			\
2944 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2945 	"ESC           Zoom out\n"					\
2946 	"+             Expand/Collapse one callchain level\n"		\
2947 	"a             Annotate current symbol\n"			\
2948 	"C             Collapse all callchains\n"			\
2949 	"d             Zoom into current DSO\n"				\
2950 	"e             Expand/Collapse main entry callchains\n"	\
2951 	"E             Expand all callchains\n"				\
2952 	"F             Toggle percentage of filtered entries\n"		\
2953 	"H             Display column headers\n"			\
2954 	"k             Zoom into the kernel map\n"			\
2955 	"L             Change percent limit\n"				\
2956 	"m             Display context menu\n"				\
2957 	"S             Zoom into current Processor Socket\n"		\
2958 
2959 	/* help messages are sorted by lexical order of the hotkey */
2960 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
2961 	"i             Show header information\n"
2962 	"P             Print histograms to perf.hist.N\n"
2963 	"r             Run available scripts\n"
2964 	"s             Switch to another data file in PWD\n"
2965 	"t             Zoom into current Thread\n"
2966 	"V             Verbose (DSO names in callchains, etc)\n"
2967 	"/             Filter symbol by name";
2968 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
2969 	"P             Print histograms to perf.hist.N\n"
2970 	"t             Zoom into current Thread\n"
2971 	"V             Verbose (DSO names in callchains, etc)\n"
2972 	"z             Toggle zeroing of samples\n"
2973 	"f             Enable/Disable events\n"
2974 	"/             Filter symbol by name";
2975 
2976 	if (browser == NULL)
2977 		return -1;
2978 
2979 	/* reset abort key so that it can get Ctrl-C as a key */
2980 	SLang_reset_tty();
2981 	SLang_init_tty(0, 0, 0);
2982 
2983 	if (min_pcnt)
2984 		browser->min_pcnt = min_pcnt;
2985 	hist_browser__update_nr_entries(browser);
2986 
2987 	browser->pstack = pstack__new(3);
2988 	if (browser->pstack == NULL)
2989 		goto out;
2990 
2991 	ui_helpline__push(helpline);
2992 
2993 	memset(options, 0, sizeof(options));
2994 	memset(actions, 0, sizeof(actions));
2995 
2996 	if (symbol_conf.col_width_list_str)
2997 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2998 
2999 	if (!is_report_browser(hbt))
3000 		browser->b.no_samples_msg = "Collecting samples...";
3001 
3002 	while (1) {
3003 		struct thread *thread = NULL;
3004 		struct map *map = NULL;
3005 		int choice;
3006 		int socked_id = -1;
3007 
3008 		key = 0; // reset key
3009 do_hotkey:		 // key came straight from options ui__popup_menu()
3010 		choice = nr_options = 0;
3011 		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3012 
3013 		if (browser->he_selection != NULL) {
3014 			thread = hist_browser__selected_thread(browser);
3015 			map = browser->selection->map;
3016 			socked_id = browser->he_selection->socket;
3017 		}
3018 		switch (key) {
3019 		case K_TAB:
3020 		case K_UNTAB:
3021 			if (nr_events == 1)
3022 				continue;
3023 			/*
3024 			 * Exit the browser, let hists__browser_tree
3025 			 * go to the next or previous
3026 			 */
3027 			goto out_free_stack;
3028 		case 'a':
3029 			if (!hists__has(hists, sym)) {
3030 				ui_browser__warning(&browser->b, delay_secs * 2,
3031 			"Annotation is only available for symbolic views, "
3032 			"include \"sym*\" in --sort to use it.");
3033 				continue;
3034 			}
3035 
3036 			if (browser->selection == NULL ||
3037 			    browser->selection->sym == NULL ||
3038 			    browser->selection->map->dso->annotate_warned)
3039 				continue;
3040 
3041 			if (symbol__annotation(browser->selection->sym)->src == NULL) {
3042 				ui_browser__warning(&browser->b, delay_secs * 2,
3043 						    "No samples for the \"%s\" symbol.\n\n"
3044 						    "Probably appeared just in a callchain",
3045 						    browser->selection->sym->name);
3046 				continue;
3047 			}
3048 
3049 			actions->ms.map = browser->selection->map;
3050 			actions->ms.sym = browser->selection->sym;
3051 			do_annotate(browser, actions);
3052 			continue;
3053 		case 'P':
3054 			hist_browser__dump(browser);
3055 			continue;
3056 		case 'd':
3057 			actions->ms.map = map;
3058 			do_zoom_dso(browser, actions);
3059 			continue;
3060 		case 'k':
3061 			if (browser->selection != NULL)
3062 				hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3063 			continue;
3064 		case 'V':
3065 			verbose = (verbose + 1) % 4;
3066 			browser->show_dso = verbose > 0;
3067 			ui_helpline__fpush("Verbosity level set to %d\n",
3068 					   verbose);
3069 			continue;
3070 		case 't':
3071 			actions->thread = thread;
3072 			do_zoom_thread(browser, actions);
3073 			continue;
3074 		case 'S':
3075 			actions->socket = socked_id;
3076 			do_zoom_socket(browser, actions);
3077 			continue;
3078 		case '/':
3079 			if (ui_browser__input_window("Symbol to show",
3080 					"Please enter the name of symbol you want to see.\n"
3081 					"To remove the filter later, press / + ENTER.",
3082 					buf, "ENTER: OK, ESC: Cancel",
3083 					delay_secs * 2) == K_ENTER) {
3084 				hists->symbol_filter_str = *buf ? buf : NULL;
3085 				hists__filter_by_symbol(hists);
3086 				hist_browser__reset(browser);
3087 			}
3088 			continue;
3089 		case 'r':
3090 			if (is_report_browser(hbt)) {
3091 				actions->thread = NULL;
3092 				actions->ms.sym = NULL;
3093 				do_run_script(browser, actions);
3094 			}
3095 			continue;
3096 		case 's':
3097 			if (is_report_browser(hbt)) {
3098 				key = do_switch_data(browser, actions);
3099 				if (key == K_SWITCH_INPUT_DATA)
3100 					goto out_free_stack;
3101 			}
3102 			continue;
3103 		case 'i':
3104 			/* env->arch is NULL for live-mode (i.e. perf top) */
3105 			if (env->arch)
3106 				tui__header_window(env);
3107 			continue;
3108 		case 'F':
3109 			symbol_conf.filter_relative ^= 1;
3110 			continue;
3111 		case 'z':
3112 			if (!is_report_browser(hbt)) {
3113 				struct perf_top *top = hbt->arg;
3114 
3115 				top->zero = !top->zero;
3116 			}
3117 			continue;
3118 		case 'L':
3119 			if (ui_browser__input_window("Percent Limit",
3120 					"Please enter the value you want to hide entries under that percent.",
3121 					buf, "ENTER: OK, ESC: Cancel",
3122 					delay_secs * 2) == K_ENTER) {
3123 				char *end;
3124 				double new_percent = strtod(buf, &end);
3125 
3126 				if (new_percent < 0 || new_percent > 100) {
3127 					ui_browser__warning(&browser->b, delay_secs * 2,
3128 						"Invalid percent: %.2f", new_percent);
3129 					continue;
3130 				}
3131 
3132 				hist_browser__update_percent_limit(browser, new_percent);
3133 				hist_browser__reset(browser);
3134 			}
3135 			continue;
3136 		case K_F1:
3137 		case 'h':
3138 		case '?':
3139 			ui_browser__help_window(&browser->b,
3140 				is_report_browser(hbt) ? report_help : top_help);
3141 			continue;
3142 		case K_ENTER:
3143 		case K_RIGHT:
3144 		case 'm':
3145 			/* menu */
3146 			break;
3147 		case K_ESC:
3148 		case K_LEFT: {
3149 			const void *top;
3150 
3151 			if (pstack__empty(browser->pstack)) {
3152 				/*
3153 				 * Go back to the perf_evsel_menu__run or other user
3154 				 */
3155 				if (left_exits)
3156 					goto out_free_stack;
3157 
3158 				if (key == K_ESC &&
3159 				    ui_browser__dialog_yesno(&browser->b,
3160 							     "Do you really want to exit?"))
3161 					goto out_free_stack;
3162 
3163 				continue;
3164 			}
3165 			actions->ms.map = map;
3166 			top = pstack__peek(browser->pstack);
3167 			if (top == &browser->hists->dso_filter) {
3168 				/*
3169 				 * No need to set actions->dso here since
3170 				 * it's just to remove the current filter.
3171 				 * Ditto for thread below.
3172 				 */
3173 				do_zoom_dso(browser, actions);
3174 			} else if (top == &browser->hists->thread_filter) {
3175 				do_zoom_thread(browser, actions);
3176 			} else if (top == &browser->hists->socket_filter) {
3177 				do_zoom_socket(browser, actions);
3178 			}
3179 			continue;
3180 		}
3181 		case 'q':
3182 		case CTRL('c'):
3183 			goto out_free_stack;
3184 		case 'f':
3185 			if (!is_report_browser(hbt)) {
3186 				struct perf_top *top = hbt->arg;
3187 
3188 				perf_evlist__toggle_enable(top->evlist);
3189 				/*
3190 				 * No need to refresh, resort/decay histogram
3191 				 * entries if we are not collecting samples:
3192 				 */
3193 				if (top->evlist->enabled) {
3194 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3195 					hbt->refresh = delay_secs;
3196 				} else {
3197 					helpline = "Press 'f' again to re-enable the events";
3198 					hbt->refresh = 0;
3199 				}
3200 				continue;
3201 			}
3202 			/* Fall thru */
3203 		default:
3204 			helpline = "Press '?' for help on key bindings";
3205 			continue;
3206 		}
3207 
3208 		if (!hists__has(hists, sym) || browser->selection == NULL)
3209 			goto skip_annotation;
3210 
3211 		if (sort__mode == SORT_MODE__BRANCH) {
3212 
3213 			if (browser->he_selection)
3214 				bi = browser->he_selection->branch_info;
3215 
3216 			if (bi == NULL)
3217 				goto skip_annotation;
3218 
3219 			nr_options += add_annotate_opt(browser,
3220 						       &actions[nr_options],
3221 						       &options[nr_options],
3222 						       &bi->from.ms);
3223 			if (bi->to.ms.sym != bi->from.ms.sym)
3224 				nr_options += add_annotate_opt(browser,
3225 							&actions[nr_options],
3226 							&options[nr_options],
3227 							&bi->to.ms);
3228 		} else {
3229 			nr_options += add_annotate_opt(browser,
3230 						       &actions[nr_options],
3231 						       &options[nr_options],
3232 						       browser->selection);
3233 		}
3234 skip_annotation:
3235 		nr_options += add_thread_opt(browser, &actions[nr_options],
3236 					     &options[nr_options], thread);
3237 		nr_options += add_dso_opt(browser, &actions[nr_options],
3238 					  &options[nr_options], map);
3239 		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3240 		nr_options += add_map_opt(browser, &actions[nr_options],
3241 					  &options[nr_options],
3242 					  browser->selection ?
3243 						browser->selection->map : NULL);
3244 		nr_options += add_socket_opt(browser, &actions[nr_options],
3245 					     &options[nr_options],
3246 					     socked_id);
3247 		/* perf script support */
3248 		if (!is_report_browser(hbt))
3249 			goto skip_scripting;
3250 
3251 		if (browser->he_selection) {
3252 			if (hists__has(hists, thread) && thread) {
3253 				nr_options += add_script_opt(browser,
3254 							     &actions[nr_options],
3255 							     &options[nr_options],
3256 							     thread, NULL, evsel);
3257 			}
3258 			/*
3259 			 * Note that browser->selection != NULL
3260 			 * when browser->he_selection is not NULL,
3261 			 * so we don't need to check browser->selection
3262 			 * before fetching browser->selection->sym like what
3263 			 * we do before fetching browser->selection->map.
3264 			 *
3265 			 * See hist_browser__show_entry.
3266 			 */
3267 			if (hists__has(hists, sym) && browser->selection->sym) {
3268 				nr_options += add_script_opt(browser,
3269 							     &actions[nr_options],
3270 							     &options[nr_options],
3271 							     NULL, browser->selection->sym,
3272 							     evsel);
3273 			}
3274 		}
3275 		nr_options += add_script_opt(browser, &actions[nr_options],
3276 					     &options[nr_options], NULL, NULL, evsel);
3277 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3278 						 &options[nr_options],
3279 				 hist_browser__selected_entry(browser)->res_samples,
3280 				 evsel, A_NORMAL);
3281 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3282 						 &options[nr_options],
3283 				 hist_browser__selected_entry(browser)->res_samples,
3284 				 evsel, A_ASM);
3285 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3286 						 &options[nr_options],
3287 				 hist_browser__selected_entry(browser)->res_samples,
3288 				 evsel, A_SOURCE);
3289 		nr_options += add_switch_opt(browser, &actions[nr_options],
3290 					     &options[nr_options]);
3291 skip_scripting:
3292 		nr_options += add_exit_opt(browser, &actions[nr_options],
3293 					   &options[nr_options]);
3294 
3295 		do {
3296 			struct popup_action *act;
3297 
3298 			choice = ui__popup_menu(nr_options, options, &key);
3299 			if (choice == -1)
3300 				break;
3301 
3302 			if (choice == nr_options)
3303 				goto do_hotkey;
3304 
3305 			act = &actions[choice];
3306 			key = act->fn(browser, act);
3307 		} while (key == 1);
3308 
3309 		if (key == K_SWITCH_INPUT_DATA)
3310 			break;
3311 	}
3312 out_free_stack:
3313 	pstack__delete(browser->pstack);
3314 out:
3315 	hist_browser__delete(browser);
3316 	free_popup_options(options, MAX_OPTIONS);
3317 	return key;
3318 }
3319 
3320 struct evsel_menu {
3321 	struct ui_browser b;
3322 	struct evsel *selection;
3323 	struct annotation_options *annotation_opts;
3324 	bool lost_events, lost_events_warned;
3325 	float min_pcnt;
3326 	struct perf_env *env;
3327 };
3328 
3329 static void perf_evsel_menu__write(struct ui_browser *browser,
3330 				   void *entry, int row)
3331 {
3332 	struct evsel_menu *menu = container_of(browser,
3333 						    struct evsel_menu, b);
3334 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3335 	struct hists *hists = evsel__hists(evsel);
3336 	bool current_entry = ui_browser__is_current_entry(browser, row);
3337 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3338 	const char *ev_name = perf_evsel__name(evsel);
3339 	char bf[256], unit;
3340 	const char *warn = " ";
3341 	size_t printed;
3342 
3343 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3344 						       HE_COLORSET_NORMAL);
3345 
3346 	if (perf_evsel__is_group_event(evsel)) {
3347 		struct evsel *pos;
3348 
3349 		ev_name = perf_evsel__group_name(evsel);
3350 
3351 		for_each_group_member(pos, evsel) {
3352 			struct hists *pos_hists = evsel__hists(pos);
3353 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3354 		}
3355 	}
3356 
3357 	nr_events = convert_unit(nr_events, &unit);
3358 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3359 			   unit, unit == ' ' ? "" : " ", ev_name);
3360 	ui_browser__printf(browser, "%s", bf);
3361 
3362 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3363 	if (nr_events != 0) {
3364 		menu->lost_events = true;
3365 		if (!current_entry)
3366 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3367 		nr_events = convert_unit(nr_events, &unit);
3368 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3369 				     nr_events, unit, unit == ' ' ? "" : " ");
3370 		warn = bf;
3371 	}
3372 
3373 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3374 
3375 	if (current_entry)
3376 		menu->selection = evsel;
3377 }
3378 
3379 static int perf_evsel_menu__run(struct evsel_menu *menu,
3380 				int nr_events, const char *help,
3381 				struct hist_browser_timer *hbt,
3382 				bool warn_lost_event)
3383 {
3384 	struct evlist *evlist = menu->b.priv;
3385 	struct evsel *pos;
3386 	const char *title = "Available samples";
3387 	int delay_secs = hbt ? hbt->refresh : 0;
3388 	int key;
3389 
3390 	if (ui_browser__show(&menu->b, title,
3391 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3392 		return -1;
3393 
3394 	while (1) {
3395 		key = ui_browser__run(&menu->b, delay_secs);
3396 
3397 		switch (key) {
3398 		case K_TIMER:
3399 			if (hbt)
3400 				hbt->timer(hbt->arg);
3401 
3402 			if (!menu->lost_events_warned &&
3403 			    menu->lost_events &&
3404 			    warn_lost_event) {
3405 				ui_browser__warn_lost_events(&menu->b);
3406 				menu->lost_events_warned = true;
3407 			}
3408 			continue;
3409 		case K_RIGHT:
3410 		case K_ENTER:
3411 			if (!menu->selection)
3412 				continue;
3413 			pos = menu->selection;
3414 browse_hists:
3415 			perf_evlist__set_selected(evlist, pos);
3416 			/*
3417 			 * Give the calling tool a chance to populate the non
3418 			 * default evsel resorted hists tree.
3419 			 */
3420 			if (hbt)
3421 				hbt->timer(hbt->arg);
3422 			key = perf_evsel__hists_browse(pos, nr_events, help,
3423 						       true, hbt,
3424 						       menu->min_pcnt,
3425 						       menu->env,
3426 						       warn_lost_event,
3427 						       menu->annotation_opts);
3428 			ui_browser__show_title(&menu->b, title);
3429 			switch (key) {
3430 			case K_TAB:
3431 				if (pos->core.node.next == &evlist->core.entries)
3432 					pos = evlist__first(evlist);
3433 				else
3434 					pos = perf_evsel__next(pos);
3435 				goto browse_hists;
3436 			case K_UNTAB:
3437 				if (pos->core.node.prev == &evlist->core.entries)
3438 					pos = evlist__last(evlist);
3439 				else
3440 					pos = perf_evsel__prev(pos);
3441 				goto browse_hists;
3442 			case K_SWITCH_INPUT_DATA:
3443 			case 'q':
3444 			case CTRL('c'):
3445 				goto out;
3446 			case K_ESC:
3447 			default:
3448 				continue;
3449 			}
3450 		case K_LEFT:
3451 			continue;
3452 		case K_ESC:
3453 			if (!ui_browser__dialog_yesno(&menu->b,
3454 					       "Do you really want to exit?"))
3455 				continue;
3456 			/* Fall thru */
3457 		case 'q':
3458 		case CTRL('c'):
3459 			goto out;
3460 		default:
3461 			continue;
3462 		}
3463 	}
3464 
3465 out:
3466 	ui_browser__hide(&menu->b);
3467 	return key;
3468 }
3469 
3470 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3471 				 void *entry)
3472 {
3473 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3474 
3475 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3476 		return true;
3477 
3478 	return false;
3479 }
3480 
3481 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3482 					   int nr_entries, const char *help,
3483 					   struct hist_browser_timer *hbt,
3484 					   float min_pcnt,
3485 					   struct perf_env *env,
3486 					   bool warn_lost_event,
3487 					   struct annotation_options *annotation_opts)
3488 {
3489 	struct evsel *pos;
3490 	struct evsel_menu menu = {
3491 		.b = {
3492 			.entries    = &evlist->core.entries,
3493 			.refresh    = ui_browser__list_head_refresh,
3494 			.seek	    = ui_browser__list_head_seek,
3495 			.write	    = perf_evsel_menu__write,
3496 			.filter	    = filter_group_entries,
3497 			.nr_entries = nr_entries,
3498 			.priv	    = evlist,
3499 		},
3500 		.min_pcnt = min_pcnt,
3501 		.env = env,
3502 		.annotation_opts = annotation_opts,
3503 	};
3504 
3505 	ui_helpline__push("Press ESC to exit");
3506 
3507 	evlist__for_each_entry(evlist, pos) {
3508 		const char *ev_name = perf_evsel__name(pos);
3509 		size_t line_len = strlen(ev_name) + 7;
3510 
3511 		if (menu.b.width < line_len)
3512 			menu.b.width = line_len;
3513 	}
3514 
3515 	return perf_evsel_menu__run(&menu, nr_entries, help,
3516 				    hbt, warn_lost_event);
3517 }
3518 
3519 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3520 				  struct hist_browser_timer *hbt,
3521 				  float min_pcnt,
3522 				  struct perf_env *env,
3523 				  bool warn_lost_event,
3524 				  struct annotation_options *annotation_opts)
3525 {
3526 	int nr_entries = evlist->core.nr_entries;
3527 
3528 single_entry:
3529 	if (nr_entries == 1) {
3530 		struct evsel *first = evlist__first(evlist);
3531 
3532 		return perf_evsel__hists_browse(first, nr_entries, help,
3533 						false, hbt, min_pcnt,
3534 						env, warn_lost_event,
3535 						annotation_opts);
3536 	}
3537 
3538 	if (symbol_conf.event_group) {
3539 		struct evsel *pos;
3540 
3541 		nr_entries = 0;
3542 		evlist__for_each_entry(evlist, pos) {
3543 			if (perf_evsel__is_group_leader(pos))
3544 				nr_entries++;
3545 		}
3546 
3547 		if (nr_entries == 1)
3548 			goto single_entry;
3549 	}
3550 
3551 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3552 					       hbt, min_pcnt, env,
3553 					       warn_lost_event,
3554 					       annotation_opts);
3555 }
3556 
3557 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3558 				      size_t size)
3559 {
3560 	struct hists *hists = evsel__hists(browser->block_evsel);
3561 	const char *evname = perf_evsel__name(browser->block_evsel);
3562 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3563 	int ret;
3564 
3565 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3566 	if (evname)
3567 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3568 
3569 	return 0;
3570 }
3571 
3572 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3573 			   float min_percent, struct perf_env *env,
3574 			   struct annotation_options *annotation_opts)
3575 {
3576 	struct hists *hists = &bh->block_hists;
3577 	struct hist_browser *browser;
3578 	int key = -1;
3579 	struct popup_action action;
3580 	static const char help[] =
3581 	" q             Quit \n";
3582 
3583 	browser = hist_browser__new(hists);
3584 	if (!browser)
3585 		return -1;
3586 
3587 	browser->block_evsel = evsel;
3588 	browser->title = block_hists_browser__title;
3589 	browser->min_pcnt = min_percent;
3590 	browser->env = env;
3591 	browser->annotation_opts = annotation_opts;
3592 
3593 	/* reset abort key so that it can get Ctrl-C as a key */
3594 	SLang_reset_tty();
3595 	SLang_init_tty(0, 0, 0);
3596 
3597 	memset(&action, 0, sizeof(action));
3598 
3599 	while (1) {
3600 		key = hist_browser__run(browser, "? - help", true, 0);
3601 
3602 		switch (key) {
3603 		case 'q':
3604 			goto out;
3605 		case '?':
3606 			ui_browser__help_window(&browser->b, help);
3607 			break;
3608 		case 'a':
3609 		case K_ENTER:
3610 			if (!browser->selection ||
3611 			    !browser->selection->sym) {
3612 				continue;
3613 			}
3614 
3615 			action.ms.map = browser->selection->map;
3616 			action.ms.sym = browser->selection->sym;
3617 			do_annotate(browser, &action);
3618 			continue;
3619 		default:
3620 			break;
3621 		}
3622 	}
3623 
3624 out:
3625 	hist_browser__delete(browser);
3626 	return 0;
3627 }
3628