xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision e0d07278)
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, size_t size, 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, size);
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, sizeof(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, sizeof(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 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2292 {
2293 	return browser->he_selection ? browser->he_selection->res_samples : NULL;
2294 }
2295 
2296 /* Check whether the browser is for 'top' or 'report' */
2297 static inline bool is_report_browser(void *timer)
2298 {
2299 	return timer == NULL;
2300 }
2301 
2302 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2303 {
2304 	struct hist_browser_timer *hbt = browser->hbt;
2305 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2306 
2307 	if (!is_report_browser(hbt)) {
2308 		struct perf_top *top = hbt->arg;
2309 
2310 		printed += scnprintf(bf + printed, size - printed,
2311 				     " lost: %" PRIu64 "/%" PRIu64,
2312 				     top->lost, top->lost_total);
2313 
2314 		printed += scnprintf(bf + printed, size - printed,
2315 				     " drop: %" PRIu64 "/%" PRIu64,
2316 				     top->drop, top->drop_total);
2317 
2318 		if (top->zero)
2319 			printed += scnprintf(bf + printed, size - printed, " [z]");
2320 
2321 		perf_top__reset_sample_counters(top);
2322 	}
2323 
2324 
2325 	return printed;
2326 }
2327 
2328 static inline void free_popup_options(char **options, int n)
2329 {
2330 	int i;
2331 
2332 	for (i = 0; i < n; ++i)
2333 		zfree(&options[i]);
2334 }
2335 
2336 /*
2337  * Only runtime switching of perf data file will make "input_name" point
2338  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2339  * whether we need to call free() for current "input_name" during the switch.
2340  */
2341 static bool is_input_name_malloced = false;
2342 
2343 static int switch_data_file(void)
2344 {
2345 	char *pwd, *options[32], *abs_path[32], *tmp;
2346 	DIR *pwd_dir;
2347 	int nr_options = 0, choice = -1, ret = -1;
2348 	struct dirent *dent;
2349 
2350 	pwd = getenv("PWD");
2351 	if (!pwd)
2352 		return ret;
2353 
2354 	pwd_dir = opendir(pwd);
2355 	if (!pwd_dir)
2356 		return ret;
2357 
2358 	memset(options, 0, sizeof(options));
2359 	memset(abs_path, 0, sizeof(abs_path));
2360 
2361 	while ((dent = readdir(pwd_dir))) {
2362 		char path[PATH_MAX];
2363 		u64 magic;
2364 		char *name = dent->d_name;
2365 		FILE *file;
2366 
2367 		if (!(dent->d_type == DT_REG))
2368 			continue;
2369 
2370 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2371 
2372 		file = fopen(path, "r");
2373 		if (!file)
2374 			continue;
2375 
2376 		if (fread(&magic, 1, 8, file) < 8)
2377 			goto close_file_and_continue;
2378 
2379 		if (is_perf_magic(magic)) {
2380 			options[nr_options] = strdup(name);
2381 			if (!options[nr_options])
2382 				goto close_file_and_continue;
2383 
2384 			abs_path[nr_options] = strdup(path);
2385 			if (!abs_path[nr_options]) {
2386 				zfree(&options[nr_options]);
2387 				ui__warning("Can't search all data files due to memory shortage.\n");
2388 				fclose(file);
2389 				break;
2390 			}
2391 
2392 			nr_options++;
2393 		}
2394 
2395 close_file_and_continue:
2396 		fclose(file);
2397 		if (nr_options >= 32) {
2398 			ui__warning("Too many perf data files in PWD!\n"
2399 				    "Only the first 32 files will be listed.\n");
2400 			break;
2401 		}
2402 	}
2403 	closedir(pwd_dir);
2404 
2405 	if (nr_options) {
2406 		choice = ui__popup_menu(nr_options, options, NULL);
2407 		if (choice < nr_options && choice >= 0) {
2408 			tmp = strdup(abs_path[choice]);
2409 			if (tmp) {
2410 				if (is_input_name_malloced)
2411 					free((void *)input_name);
2412 				input_name = tmp;
2413 				is_input_name_malloced = true;
2414 				ret = 0;
2415 			} else
2416 				ui__warning("Data switch failed due to memory shortage!\n");
2417 		}
2418 	}
2419 
2420 	free_popup_options(options, nr_options);
2421 	free_popup_options(abs_path, nr_options);
2422 	return ret;
2423 }
2424 
2425 struct popup_action {
2426 	unsigned long		time;
2427 	struct thread 		*thread;
2428 	struct map_symbol 	ms;
2429 	int			socket;
2430 	struct evsel	*evsel;
2431 	enum rstype		rstype;
2432 
2433 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2434 };
2435 
2436 static int
2437 do_annotate(struct hist_browser *browser, struct popup_action *act)
2438 {
2439 	struct evsel *evsel;
2440 	struct annotation *notes;
2441 	struct hist_entry *he;
2442 	int err;
2443 
2444 	if (!browser->annotation_opts->objdump_path &&
2445 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2446 		return 0;
2447 
2448 	notes = symbol__annotation(act->ms.sym);
2449 	if (!notes->src)
2450 		return 0;
2451 
2452 	if (browser->block_evsel)
2453 		evsel = browser->block_evsel;
2454 	else
2455 		evsel = hists_to_evsel(browser->hists);
2456 
2457 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2458 				       browser->annotation_opts);
2459 	he = hist_browser__selected_entry(browser);
2460 	/*
2461 	 * offer option to annotate the other branch source or target
2462 	 * (if they exists) when returning from annotate
2463 	 */
2464 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2465 		return 1;
2466 
2467 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2468 	if (err)
2469 		ui_browser__handle_resize(&browser->b);
2470 	return 0;
2471 }
2472 
2473 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2474 {
2475 	struct annotated_source *src;
2476 	struct symbol *sym;
2477 	char name[64];
2478 
2479 	snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2480 
2481 	sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2482 	if (sym) {
2483 		src = symbol__hists(sym, 1);
2484 		if (!src) {
2485 			symbol__delete(sym);
2486 			return NULL;
2487 		}
2488 
2489 		dso__insert_symbol(map->dso, sym);
2490 	}
2491 
2492 	return sym;
2493 }
2494 
2495 static int
2496 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2497 		 struct popup_action *act, char **optstr,
2498 		 struct map_symbol *ms,
2499 		 u64 addr)
2500 {
2501 	if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned)
2502 		return 0;
2503 
2504 	if (!ms->sym)
2505 		ms->sym = symbol__new_unresolved(addr, ms->map);
2506 
2507 	if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2508 		return 0;
2509 
2510 	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2511 		return 0;
2512 
2513 	act->ms = *ms;
2514 	act->fn = do_annotate;
2515 	return 1;
2516 }
2517 
2518 static int
2519 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2520 {
2521 	struct thread *thread = act->thread;
2522 
2523 	if ((!hists__has(browser->hists, thread) &&
2524 	     !hists__has(browser->hists, comm)) || thread == NULL)
2525 		return 0;
2526 
2527 	if (browser->hists->thread_filter) {
2528 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2529 		perf_hpp__set_elide(HISTC_THREAD, false);
2530 		thread__zput(browser->hists->thread_filter);
2531 		ui_helpline__pop();
2532 	} else {
2533 		if (hists__has(browser->hists, thread)) {
2534 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2535 					   thread->comm_set ? thread__comm_str(thread) : "",
2536 					   thread->tid);
2537 		} else {
2538 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2539 					   thread->comm_set ? thread__comm_str(thread) : "");
2540 		}
2541 
2542 		browser->hists->thread_filter = thread__get(thread);
2543 		perf_hpp__set_elide(HISTC_THREAD, false);
2544 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2545 	}
2546 
2547 	hists__filter_by_thread(browser->hists);
2548 	hist_browser__reset(browser);
2549 	return 0;
2550 }
2551 
2552 static int
2553 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2554 	       char **optstr, struct thread *thread)
2555 {
2556 	int ret;
2557 
2558 	if ((!hists__has(browser->hists, thread) &&
2559 	     !hists__has(browser->hists, comm)) || thread == NULL)
2560 		return 0;
2561 
2562 	if (hists__has(browser->hists, thread)) {
2563 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2564 			       browser->hists->thread_filter ? "out of" : "into",
2565 			       thread->comm_set ? thread__comm_str(thread) : "",
2566 			       thread->tid);
2567 	} else {
2568 		ret = asprintf(optstr, "Zoom %s %s thread",
2569 			       browser->hists->thread_filter ? "out of" : "into",
2570 			       thread->comm_set ? thread__comm_str(thread) : "");
2571 	}
2572 	if (ret < 0)
2573 		return 0;
2574 
2575 	act->thread = thread;
2576 	act->fn = do_zoom_thread;
2577 	return 1;
2578 }
2579 
2580 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2581 {
2582 	if (!hists__has(browser->hists, dso) || map == NULL)
2583 		return 0;
2584 
2585 	if (browser->hists->dso_filter) {
2586 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2587 		perf_hpp__set_elide(HISTC_DSO, false);
2588 		browser->hists->dso_filter = NULL;
2589 		ui_helpline__pop();
2590 	} else {
2591 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2592 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2593 		browser->hists->dso_filter = map->dso;
2594 		perf_hpp__set_elide(HISTC_DSO, true);
2595 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2596 	}
2597 
2598 	hists__filter_by_dso(browser->hists);
2599 	hist_browser__reset(browser);
2600 	return 0;
2601 }
2602 
2603 static int
2604 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2605 {
2606 	return hists_browser__zoom_map(browser, act->ms.map);
2607 }
2608 
2609 static int
2610 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2611 	    char **optstr, struct map *map)
2612 {
2613 	if (!hists__has(browser->hists, dso) || map == NULL)
2614 		return 0;
2615 
2616 	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2617 		     browser->hists->dso_filter ? "out of" : "into",
2618 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2619 		return 0;
2620 
2621 	act->ms.map = map;
2622 	act->fn = do_zoom_dso;
2623 	return 1;
2624 }
2625 
2626 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2627 {
2628 	hist_browser__toggle_fold(browser);
2629 	return 0;
2630 }
2631 
2632 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2633 {
2634 	char sym_name[512];
2635 
2636         if (!hist_browser__selection_has_children(browser))
2637                 return 0;
2638 
2639 	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2640 		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2641 		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2642 		return 0;
2643 
2644 	act->fn = do_toggle_callchain;
2645 	return 1;
2646 }
2647 
2648 static int
2649 do_browse_map(struct hist_browser *browser __maybe_unused,
2650 	      struct popup_action *act)
2651 {
2652 	map__browse(act->ms.map);
2653 	return 0;
2654 }
2655 
2656 static int
2657 add_map_opt(struct hist_browser *browser,
2658 	    struct popup_action *act, char **optstr, struct map *map)
2659 {
2660 	if (!hists__has(browser->hists, dso) || map == NULL)
2661 		return 0;
2662 
2663 	if (asprintf(optstr, "Browse map details") < 0)
2664 		return 0;
2665 
2666 	act->ms.map = map;
2667 	act->fn = do_browse_map;
2668 	return 1;
2669 }
2670 
2671 static int
2672 do_run_script(struct hist_browser *browser __maybe_unused,
2673 	      struct popup_action *act)
2674 {
2675 	char *script_opt;
2676 	int len;
2677 	int n = 0;
2678 
2679 	len = 100;
2680 	if (act->thread)
2681 		len += strlen(thread__comm_str(act->thread));
2682 	else if (act->ms.sym)
2683 		len += strlen(act->ms.sym->name);
2684 	script_opt = malloc(len);
2685 	if (!script_opt)
2686 		return -1;
2687 
2688 	script_opt[0] = 0;
2689 	if (act->thread) {
2690 		n = scnprintf(script_opt, len, " -c %s ",
2691 			  thread__comm_str(act->thread));
2692 	} else if (act->ms.sym) {
2693 		n = scnprintf(script_opt, len, " -S %s ",
2694 			  act->ms.sym->name);
2695 	}
2696 
2697 	if (act->time) {
2698 		char start[32], end[32];
2699 		unsigned long starttime = act->time;
2700 		unsigned long endtime = act->time + symbol_conf.time_quantum;
2701 
2702 		if (starttime == endtime) { /* Display 1ms as fallback */
2703 			starttime -= 1*NSEC_PER_MSEC;
2704 			endtime += 1*NSEC_PER_MSEC;
2705 		}
2706 		timestamp__scnprintf_usec(starttime, start, sizeof start);
2707 		timestamp__scnprintf_usec(endtime, end, sizeof end);
2708 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2709 	}
2710 
2711 	script_browse(script_opt, act->evsel);
2712 	free(script_opt);
2713 	return 0;
2714 }
2715 
2716 static int
2717 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2718 		     struct popup_action *act)
2719 {
2720 	struct hist_entry *he;
2721 
2722 	he = hist_browser__selected_entry(browser);
2723 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2724 	return 0;
2725 }
2726 
2727 static int
2728 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2729 	       struct popup_action *act, char **optstr,
2730 	       struct thread *thread, struct symbol *sym,
2731 	       struct evsel *evsel, const char *tstr)
2732 {
2733 
2734 	if (thread) {
2735 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2736 			     thread__comm_str(thread), tstr) < 0)
2737 			return 0;
2738 	} else if (sym) {
2739 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2740 			     sym->name, tstr) < 0)
2741 			return 0;
2742 	} else {
2743 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2744 			return 0;
2745 	}
2746 
2747 	act->thread = thread;
2748 	act->ms.sym = sym;
2749 	act->evsel = evsel;
2750 	act->fn = do_run_script;
2751 	return 1;
2752 }
2753 
2754 static int
2755 add_script_opt(struct hist_browser *browser,
2756 	       struct popup_action *act, char **optstr,
2757 	       struct thread *thread, struct symbol *sym,
2758 	       struct evsel *evsel)
2759 {
2760 	int n, j;
2761 	struct hist_entry *he;
2762 
2763 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2764 
2765 	he = hist_browser__selected_entry(browser);
2766 	if (sort_order && strstr(sort_order, "time")) {
2767 		char tstr[128];
2768 
2769 		optstr++;
2770 		act++;
2771 		j = sprintf(tstr, " in ");
2772 		j += timestamp__scnprintf_usec(he->time, tstr + j,
2773 					       sizeof tstr - j);
2774 		j += sprintf(tstr + j, "-");
2775 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2776 				          tstr + j, sizeof tstr - j);
2777 		n += add_script_opt_2(browser, act, optstr, thread, sym,
2778 					  evsel, tstr);
2779 		act->time = he->time;
2780 	}
2781 	return n;
2782 }
2783 
2784 static int
2785 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2786 		   struct popup_action *act, char **optstr,
2787 		   struct res_sample *res_sample,
2788 		   struct evsel *evsel,
2789 		   enum rstype type)
2790 {
2791 	if (!res_sample)
2792 		return 0;
2793 
2794 	if (asprintf(optstr, "Show context for individual samples %s",
2795 		type == A_ASM ? "with assembler" :
2796 		type == A_SOURCE ? "with source" : "") < 0)
2797 		return 0;
2798 
2799 	act->fn = do_res_sample_script;
2800 	act->evsel = evsel;
2801 	act->rstype = type;
2802 	return 1;
2803 }
2804 
2805 static int
2806 do_switch_data(struct hist_browser *browser __maybe_unused,
2807 	       struct popup_action *act __maybe_unused)
2808 {
2809 	if (switch_data_file()) {
2810 		ui__warning("Won't switch the data files due to\n"
2811 			    "no valid data file get selected!\n");
2812 		return 0;
2813 	}
2814 
2815 	return K_SWITCH_INPUT_DATA;
2816 }
2817 
2818 static int
2819 add_switch_opt(struct hist_browser *browser,
2820 	       struct popup_action *act, char **optstr)
2821 {
2822 	if (!is_report_browser(browser->hbt))
2823 		return 0;
2824 
2825 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2826 		return 0;
2827 
2828 	act->fn = do_switch_data;
2829 	return 1;
2830 }
2831 
2832 static int
2833 do_exit_browser(struct hist_browser *browser __maybe_unused,
2834 		struct popup_action *act __maybe_unused)
2835 {
2836 	return 0;
2837 }
2838 
2839 static int
2840 add_exit_opt(struct hist_browser *browser __maybe_unused,
2841 	     struct popup_action *act, char **optstr)
2842 {
2843 	if (asprintf(optstr, "Exit") < 0)
2844 		return 0;
2845 
2846 	act->fn = do_exit_browser;
2847 	return 1;
2848 }
2849 
2850 static int
2851 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2852 {
2853 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2854 		return 0;
2855 
2856 	if (browser->hists->socket_filter > -1) {
2857 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2858 		browser->hists->socket_filter = -1;
2859 		perf_hpp__set_elide(HISTC_SOCKET, false);
2860 	} else {
2861 		browser->hists->socket_filter = act->socket;
2862 		perf_hpp__set_elide(HISTC_SOCKET, true);
2863 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2864 	}
2865 
2866 	hists__filter_by_socket(browser->hists);
2867 	hist_browser__reset(browser);
2868 	return 0;
2869 }
2870 
2871 static int
2872 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2873 	       char **optstr, int socket_id)
2874 {
2875 	if (!hists__has(browser->hists, socket) || socket_id < 0)
2876 		return 0;
2877 
2878 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2879 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2880 		     socket_id) < 0)
2881 		return 0;
2882 
2883 	act->socket = socket_id;
2884 	act->fn = do_zoom_socket;
2885 	return 1;
2886 }
2887 
2888 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2889 {
2890 	u64 nr_entries = 0;
2891 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2892 
2893 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2894 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2895 		return;
2896 	}
2897 
2898 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2899 		nr_entries++;
2900 		nd = rb_hierarchy_next(nd);
2901 	}
2902 
2903 	hb->nr_non_filtered_entries = nr_entries;
2904 	hb->nr_hierarchy_entries = nr_entries;
2905 }
2906 
2907 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2908 					       double percent)
2909 {
2910 	struct hist_entry *he;
2911 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2912 	u64 total = hists__total_period(hb->hists);
2913 	u64 min_callchain_hits = total * (percent / 100);
2914 
2915 	hb->min_pcnt = callchain_param.min_percent = percent;
2916 
2917 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2918 		he = rb_entry(nd, struct hist_entry, rb_node);
2919 
2920 		if (he->has_no_entry) {
2921 			he->has_no_entry = false;
2922 			he->nr_rows = 0;
2923 		}
2924 
2925 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2926 			goto next;
2927 
2928 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2929 			total = he->stat.period;
2930 
2931 			if (symbol_conf.cumulate_callchain)
2932 				total = he->stat_acc->period;
2933 
2934 			min_callchain_hits = total * (percent / 100);
2935 		}
2936 
2937 		callchain_param.sort(&he->sorted_chain, he->callchain,
2938 				     min_callchain_hits, &callchain_param);
2939 
2940 next:
2941 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2942 
2943 		/* force to re-evaluate folding state of callchains */
2944 		he->init_have_children = false;
2945 		hist_entry__set_folding(he, hb, false);
2946 	}
2947 }
2948 
2949 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2950 				    const char *helpline,
2951 				    bool left_exits,
2952 				    struct hist_browser_timer *hbt,
2953 				    float min_pcnt,
2954 				    struct perf_env *env,
2955 				    bool warn_lost_event,
2956 				    struct annotation_options *annotation_opts)
2957 {
2958 	struct hists *hists = evsel__hists(evsel);
2959 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2960 	struct branch_info *bi = NULL;
2961 #define MAX_OPTIONS  16
2962 	char *options[MAX_OPTIONS];
2963 	struct popup_action actions[MAX_OPTIONS];
2964 	int nr_options = 0;
2965 	int key = -1;
2966 	char buf[64];
2967 	int delay_secs = hbt ? hbt->refresh : 0;
2968 
2969 #define HIST_BROWSER_HELP_COMMON					\
2970 	"h/?/F1        Show this window\n"				\
2971 	"UP/DOWN/PGUP\n"						\
2972 	"PGDN/SPACE    Navigate\n"					\
2973 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2974 	"For multiple event sessions:\n\n"				\
2975 	"TAB/UNTAB     Switch events\n\n"				\
2976 	"For symbolic views (--sort has sym):\n\n"			\
2977 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2978 	"ESC           Zoom out\n"					\
2979 	"+             Expand/Collapse one callchain level\n"		\
2980 	"a             Annotate current symbol\n"			\
2981 	"C             Collapse all callchains\n"			\
2982 	"d             Zoom into current DSO\n"				\
2983 	"e             Expand/Collapse main entry callchains\n"	\
2984 	"E             Expand all callchains\n"				\
2985 	"F             Toggle percentage of filtered entries\n"		\
2986 	"H             Display column headers\n"			\
2987 	"k             Zoom into the kernel map\n"			\
2988 	"L             Change percent limit\n"				\
2989 	"m             Display context menu\n"				\
2990 	"S             Zoom into current Processor Socket\n"		\
2991 
2992 	/* help messages are sorted by lexical order of the hotkey */
2993 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
2994 	"i             Show header information\n"
2995 	"P             Print histograms to perf.hist.N\n"
2996 	"r             Run available scripts\n"
2997 	"s             Switch to another data file in PWD\n"
2998 	"t             Zoom into current Thread\n"
2999 	"V             Verbose (DSO names in callchains, etc)\n"
3000 	"/             Filter symbol by name\n"
3001 	"0-9           Sort by event n in group";
3002 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
3003 	"P             Print histograms to perf.hist.N\n"
3004 	"t             Zoom into current Thread\n"
3005 	"V             Verbose (DSO names in callchains, etc)\n"
3006 	"z             Toggle zeroing of samples\n"
3007 	"f             Enable/Disable events\n"
3008 	"/             Filter symbol by name";
3009 
3010 	if (browser == NULL)
3011 		return -1;
3012 
3013 	/* reset abort key so that it can get Ctrl-C as a key */
3014 	SLang_reset_tty();
3015 	SLang_init_tty(0, 0, 0);
3016 
3017 	if (min_pcnt)
3018 		browser->min_pcnt = min_pcnt;
3019 	hist_browser__update_nr_entries(browser);
3020 
3021 	browser->pstack = pstack__new(3);
3022 	if (browser->pstack == NULL)
3023 		goto out;
3024 
3025 	ui_helpline__push(helpline);
3026 
3027 	memset(options, 0, sizeof(options));
3028 	memset(actions, 0, sizeof(actions));
3029 
3030 	if (symbol_conf.col_width_list_str)
3031 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3032 
3033 	if (!is_report_browser(hbt))
3034 		browser->b.no_samples_msg = "Collecting samples...";
3035 
3036 	while (1) {
3037 		struct thread *thread = NULL;
3038 		struct map *map = NULL;
3039 		int choice;
3040 		int socked_id = -1;
3041 
3042 		key = 0; // reset key
3043 do_hotkey:		 // key came straight from options ui__popup_menu()
3044 		choice = nr_options = 0;
3045 		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3046 
3047 		if (browser->he_selection != NULL) {
3048 			thread = hist_browser__selected_thread(browser);
3049 			map = browser->selection->map;
3050 			socked_id = browser->he_selection->socket;
3051 		}
3052 		switch (key) {
3053 		case K_TAB:
3054 		case K_UNTAB:
3055 			if (nr_events == 1)
3056 				continue;
3057 			/*
3058 			 * Exit the browser, let hists__browser_tree
3059 			 * go to the next or previous
3060 			 */
3061 			goto out_free_stack;
3062 		case '0' ... '9':
3063 			if (!symbol_conf.event_group ||
3064 			    evsel->core.nr_members < 2) {
3065 				snprintf(buf, sizeof(buf),
3066 					 "Sort by index only available with group events!");
3067 				helpline = buf;
3068 				continue;
3069 			}
3070 
3071 			if (key - '0' == symbol_conf.group_sort_idx)
3072 				continue;
3073 
3074 			symbol_conf.group_sort_idx = key - '0';
3075 
3076 			if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3077 				snprintf(buf, sizeof(buf),
3078 					 "Max event group index to sort is %d (index from 0 to %d)",
3079 					 evsel->core.nr_members - 1,
3080 					 evsel->core.nr_members - 1);
3081 				helpline = buf;
3082 				continue;
3083 			}
3084 
3085 			key = K_RELOAD;
3086 			goto out_free_stack;
3087 		case 'a':
3088 			if (!hists__has(hists, sym)) {
3089 				ui_browser__warning(&browser->b, delay_secs * 2,
3090 			"Annotation is only available for symbolic views, "
3091 			"include \"sym*\" in --sort to use it.");
3092 				continue;
3093 			}
3094 
3095 			if (!browser->selection ||
3096 			    !browser->selection->map ||
3097 			    !browser->selection->map->dso ||
3098 			    browser->selection->map->dso->annotate_warned) {
3099 				continue;
3100 			}
3101 
3102 			if (!browser->selection->sym) {
3103 				if (!browser->he_selection)
3104 					continue;
3105 
3106 				if (sort__mode == SORT_MODE__BRANCH) {
3107 					bi = browser->he_selection->branch_info;
3108 					if (!bi || !bi->to.ms.map)
3109 						continue;
3110 
3111 					actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3112 					actions->ms.map = bi->to.ms.map;
3113 				} else {
3114 					actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3115 										 browser->selection->map);
3116 					actions->ms.map = browser->selection->map;
3117 				}
3118 
3119 				if (!actions->ms.sym)
3120 					continue;
3121 			} else {
3122 				if (symbol__annotation(browser->selection->sym)->src == NULL) {
3123 					ui_browser__warning(&browser->b, delay_secs * 2,
3124 						"No samples for the \"%s\" symbol.\n\n"
3125 						"Probably appeared just in a callchain",
3126 						browser->selection->sym->name);
3127 					continue;
3128 				}
3129 
3130 				actions->ms.map = browser->selection->map;
3131 				actions->ms.sym = browser->selection->sym;
3132 			}
3133 
3134 			do_annotate(browser, actions);
3135 			continue;
3136 		case 'P':
3137 			hist_browser__dump(browser);
3138 			continue;
3139 		case 'd':
3140 			actions->ms.map = map;
3141 			do_zoom_dso(browser, actions);
3142 			continue;
3143 		case 'k':
3144 			if (browser->selection != NULL)
3145 				hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3146 			continue;
3147 		case 'V':
3148 			verbose = (verbose + 1) % 4;
3149 			browser->show_dso = verbose > 0;
3150 			ui_helpline__fpush("Verbosity level set to %d\n",
3151 					   verbose);
3152 			continue;
3153 		case 't':
3154 			actions->thread = thread;
3155 			do_zoom_thread(browser, actions);
3156 			continue;
3157 		case 'S':
3158 			actions->socket = socked_id;
3159 			do_zoom_socket(browser, actions);
3160 			continue;
3161 		case '/':
3162 			if (ui_browser__input_window("Symbol to show",
3163 					"Please enter the name of symbol you want to see.\n"
3164 					"To remove the filter later, press / + ENTER.",
3165 					buf, "ENTER: OK, ESC: Cancel",
3166 					delay_secs * 2) == K_ENTER) {
3167 				hists->symbol_filter_str = *buf ? buf : NULL;
3168 				hists__filter_by_symbol(hists);
3169 				hist_browser__reset(browser);
3170 			}
3171 			continue;
3172 		case 'r':
3173 			if (is_report_browser(hbt)) {
3174 				actions->thread = NULL;
3175 				actions->ms.sym = NULL;
3176 				do_run_script(browser, actions);
3177 			}
3178 			continue;
3179 		case 's':
3180 			if (is_report_browser(hbt)) {
3181 				key = do_switch_data(browser, actions);
3182 				if (key == K_SWITCH_INPUT_DATA)
3183 					goto out_free_stack;
3184 			}
3185 			continue;
3186 		case 'i':
3187 			/* env->arch is NULL for live-mode (i.e. perf top) */
3188 			if (env->arch)
3189 				tui__header_window(env);
3190 			continue;
3191 		case 'F':
3192 			symbol_conf.filter_relative ^= 1;
3193 			continue;
3194 		case 'z':
3195 			if (!is_report_browser(hbt)) {
3196 				struct perf_top *top = hbt->arg;
3197 
3198 				top->zero = !top->zero;
3199 			}
3200 			continue;
3201 		case 'L':
3202 			if (ui_browser__input_window("Percent Limit",
3203 					"Please enter the value you want to hide entries under that percent.",
3204 					buf, "ENTER: OK, ESC: Cancel",
3205 					delay_secs * 2) == K_ENTER) {
3206 				char *end;
3207 				double new_percent = strtod(buf, &end);
3208 
3209 				if (new_percent < 0 || new_percent > 100) {
3210 					ui_browser__warning(&browser->b, delay_secs * 2,
3211 						"Invalid percent: %.2f", new_percent);
3212 					continue;
3213 				}
3214 
3215 				hist_browser__update_percent_limit(browser, new_percent);
3216 				hist_browser__reset(browser);
3217 			}
3218 			continue;
3219 		case K_F1:
3220 		case 'h':
3221 		case '?':
3222 			ui_browser__help_window(&browser->b,
3223 				is_report_browser(hbt) ? report_help : top_help);
3224 			continue;
3225 		case K_ENTER:
3226 		case K_RIGHT:
3227 		case 'm':
3228 			/* menu */
3229 			break;
3230 		case K_ESC:
3231 		case K_LEFT: {
3232 			const void *top;
3233 
3234 			if (pstack__empty(browser->pstack)) {
3235 				/*
3236 				 * Go back to the perf_evsel_menu__run or other user
3237 				 */
3238 				if (left_exits)
3239 					goto out_free_stack;
3240 
3241 				if (key == K_ESC &&
3242 				    ui_browser__dialog_yesno(&browser->b,
3243 							     "Do you really want to exit?"))
3244 					goto out_free_stack;
3245 
3246 				continue;
3247 			}
3248 			actions->ms.map = map;
3249 			top = pstack__peek(browser->pstack);
3250 			if (top == &browser->hists->dso_filter) {
3251 				/*
3252 				 * No need to set actions->dso here since
3253 				 * it's just to remove the current filter.
3254 				 * Ditto for thread below.
3255 				 */
3256 				do_zoom_dso(browser, actions);
3257 			} else if (top == &browser->hists->thread_filter) {
3258 				do_zoom_thread(browser, actions);
3259 			} else if (top == &browser->hists->socket_filter) {
3260 				do_zoom_socket(browser, actions);
3261 			}
3262 			continue;
3263 		}
3264 		case 'q':
3265 		case CTRL('c'):
3266 			goto out_free_stack;
3267 		case 'f':
3268 			if (!is_report_browser(hbt)) {
3269 				struct perf_top *top = hbt->arg;
3270 
3271 				perf_evlist__toggle_enable(top->evlist);
3272 				/*
3273 				 * No need to refresh, resort/decay histogram
3274 				 * entries if we are not collecting samples:
3275 				 */
3276 				if (top->evlist->enabled) {
3277 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3278 					hbt->refresh = delay_secs;
3279 				} else {
3280 					helpline = "Press 'f' again to re-enable the events";
3281 					hbt->refresh = 0;
3282 				}
3283 				continue;
3284 			}
3285 			/* Fall thru */
3286 		default:
3287 			helpline = "Press '?' for help on key bindings";
3288 			continue;
3289 		}
3290 
3291 		if (!hists__has(hists, sym) || browser->selection == NULL)
3292 			goto skip_annotation;
3293 
3294 		if (sort__mode == SORT_MODE__BRANCH) {
3295 
3296 			if (browser->he_selection)
3297 				bi = browser->he_selection->branch_info;
3298 
3299 			if (bi == NULL)
3300 				goto skip_annotation;
3301 
3302 			nr_options += add_annotate_opt(browser,
3303 						       &actions[nr_options],
3304 						       &options[nr_options],
3305 						       &bi->from.ms,
3306 						       bi->from.al_addr);
3307 			if (bi->to.ms.sym != bi->from.ms.sym)
3308 				nr_options += add_annotate_opt(browser,
3309 							&actions[nr_options],
3310 							&options[nr_options],
3311 							&bi->to.ms,
3312 							bi->to.al_addr);
3313 		} else {
3314 			nr_options += add_annotate_opt(browser,
3315 						       &actions[nr_options],
3316 						       &options[nr_options],
3317 						       browser->selection,
3318 						       browser->he_selection->ip);
3319 		}
3320 skip_annotation:
3321 		nr_options += add_thread_opt(browser, &actions[nr_options],
3322 					     &options[nr_options], thread);
3323 		nr_options += add_dso_opt(browser, &actions[nr_options],
3324 					  &options[nr_options], map);
3325 		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3326 		nr_options += add_map_opt(browser, &actions[nr_options],
3327 					  &options[nr_options],
3328 					  browser->selection ?
3329 						browser->selection->map : NULL);
3330 		nr_options += add_socket_opt(browser, &actions[nr_options],
3331 					     &options[nr_options],
3332 					     socked_id);
3333 		/* perf script support */
3334 		if (!is_report_browser(hbt))
3335 			goto skip_scripting;
3336 
3337 		if (browser->he_selection) {
3338 			if (hists__has(hists, thread) && thread) {
3339 				nr_options += add_script_opt(browser,
3340 							     &actions[nr_options],
3341 							     &options[nr_options],
3342 							     thread, NULL, evsel);
3343 			}
3344 			/*
3345 			 * Note that browser->selection != NULL
3346 			 * when browser->he_selection is not NULL,
3347 			 * so we don't need to check browser->selection
3348 			 * before fetching browser->selection->sym like what
3349 			 * we do before fetching browser->selection->map.
3350 			 *
3351 			 * See hist_browser__show_entry.
3352 			 */
3353 			if (hists__has(hists, sym) && browser->selection->sym) {
3354 				nr_options += add_script_opt(browser,
3355 							     &actions[nr_options],
3356 							     &options[nr_options],
3357 							     NULL, browser->selection->sym,
3358 							     evsel);
3359 			}
3360 		}
3361 		nr_options += add_script_opt(browser, &actions[nr_options],
3362 					     &options[nr_options], NULL, NULL, evsel);
3363 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3364 						 &options[nr_options],
3365 						 hist_browser__selected_res_sample(browser),
3366 						 evsel, A_NORMAL);
3367 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3368 						 &options[nr_options],
3369 						 hist_browser__selected_res_sample(browser),
3370 						 evsel, A_ASM);
3371 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3372 						 &options[nr_options],
3373 						 hist_browser__selected_res_sample(browser),
3374 						 evsel, A_SOURCE);
3375 		nr_options += add_switch_opt(browser, &actions[nr_options],
3376 					     &options[nr_options]);
3377 skip_scripting:
3378 		nr_options += add_exit_opt(browser, &actions[nr_options],
3379 					   &options[nr_options]);
3380 
3381 		do {
3382 			struct popup_action *act;
3383 
3384 			choice = ui__popup_menu(nr_options, options, &key);
3385 			if (choice == -1)
3386 				break;
3387 
3388 			if (choice == nr_options)
3389 				goto do_hotkey;
3390 
3391 			act = &actions[choice];
3392 			key = act->fn(browser, act);
3393 		} while (key == 1);
3394 
3395 		if (key == K_SWITCH_INPUT_DATA)
3396 			break;
3397 	}
3398 out_free_stack:
3399 	pstack__delete(browser->pstack);
3400 out:
3401 	hist_browser__delete(browser);
3402 	free_popup_options(options, MAX_OPTIONS);
3403 	return key;
3404 }
3405 
3406 struct evsel_menu {
3407 	struct ui_browser b;
3408 	struct evsel *selection;
3409 	struct annotation_options *annotation_opts;
3410 	bool lost_events, lost_events_warned;
3411 	float min_pcnt;
3412 	struct perf_env *env;
3413 };
3414 
3415 static void perf_evsel_menu__write(struct ui_browser *browser,
3416 				   void *entry, int row)
3417 {
3418 	struct evsel_menu *menu = container_of(browser,
3419 						    struct evsel_menu, b);
3420 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3421 	struct hists *hists = evsel__hists(evsel);
3422 	bool current_entry = ui_browser__is_current_entry(browser, row);
3423 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3424 	const char *ev_name = evsel__name(evsel);
3425 	char bf[256], unit;
3426 	const char *warn = " ";
3427 	size_t printed;
3428 
3429 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3430 						       HE_COLORSET_NORMAL);
3431 
3432 	if (evsel__is_group_event(evsel)) {
3433 		struct evsel *pos;
3434 
3435 		ev_name = evsel__group_name(evsel);
3436 
3437 		for_each_group_member(pos, evsel) {
3438 			struct hists *pos_hists = evsel__hists(pos);
3439 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3440 		}
3441 	}
3442 
3443 	nr_events = convert_unit(nr_events, &unit);
3444 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3445 			   unit, unit == ' ' ? "" : " ", ev_name);
3446 	ui_browser__printf(browser, "%s", bf);
3447 
3448 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3449 	if (nr_events != 0) {
3450 		menu->lost_events = true;
3451 		if (!current_entry)
3452 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3453 		nr_events = convert_unit(nr_events, &unit);
3454 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3455 				     nr_events, unit, unit == ' ' ? "" : " ");
3456 		warn = bf;
3457 	}
3458 
3459 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3460 
3461 	if (current_entry)
3462 		menu->selection = evsel;
3463 }
3464 
3465 static int perf_evsel_menu__run(struct evsel_menu *menu,
3466 				int nr_events, const char *help,
3467 				struct hist_browser_timer *hbt,
3468 				bool warn_lost_event)
3469 {
3470 	struct evlist *evlist = menu->b.priv;
3471 	struct evsel *pos;
3472 	const char *title = "Available samples";
3473 	int delay_secs = hbt ? hbt->refresh : 0;
3474 	int key;
3475 
3476 	if (ui_browser__show(&menu->b, title,
3477 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3478 		return -1;
3479 
3480 	while (1) {
3481 		key = ui_browser__run(&menu->b, delay_secs);
3482 
3483 		switch (key) {
3484 		case K_TIMER:
3485 			if (hbt)
3486 				hbt->timer(hbt->arg);
3487 
3488 			if (!menu->lost_events_warned &&
3489 			    menu->lost_events &&
3490 			    warn_lost_event) {
3491 				ui_browser__warn_lost_events(&menu->b);
3492 				menu->lost_events_warned = true;
3493 			}
3494 			continue;
3495 		case K_RIGHT:
3496 		case K_ENTER:
3497 			if (!menu->selection)
3498 				continue;
3499 			pos = menu->selection;
3500 browse_hists:
3501 			perf_evlist__set_selected(evlist, pos);
3502 			/*
3503 			 * Give the calling tool a chance to populate the non
3504 			 * default evsel resorted hists tree.
3505 			 */
3506 			if (hbt)
3507 				hbt->timer(hbt->arg);
3508 			key = perf_evsel__hists_browse(pos, nr_events, help,
3509 						       true, hbt,
3510 						       menu->min_pcnt,
3511 						       menu->env,
3512 						       warn_lost_event,
3513 						       menu->annotation_opts);
3514 			ui_browser__show_title(&menu->b, title);
3515 			switch (key) {
3516 			case K_TAB:
3517 				if (pos->core.node.next == &evlist->core.entries)
3518 					pos = evlist__first(evlist);
3519 				else
3520 					pos = evsel__next(pos);
3521 				goto browse_hists;
3522 			case K_UNTAB:
3523 				if (pos->core.node.prev == &evlist->core.entries)
3524 					pos = evlist__last(evlist);
3525 				else
3526 					pos = evsel__prev(pos);
3527 				goto browse_hists;
3528 			case K_SWITCH_INPUT_DATA:
3529 			case K_RELOAD:
3530 			case 'q':
3531 			case CTRL('c'):
3532 				goto out;
3533 			case K_ESC:
3534 			default:
3535 				continue;
3536 			}
3537 		case K_LEFT:
3538 			continue;
3539 		case K_ESC:
3540 			if (!ui_browser__dialog_yesno(&menu->b,
3541 					       "Do you really want to exit?"))
3542 				continue;
3543 			/* Fall thru */
3544 		case 'q':
3545 		case CTRL('c'):
3546 			goto out;
3547 		default:
3548 			continue;
3549 		}
3550 	}
3551 
3552 out:
3553 	ui_browser__hide(&menu->b);
3554 	return key;
3555 }
3556 
3557 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3558 				 void *entry)
3559 {
3560 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3561 
3562 	if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3563 		return true;
3564 
3565 	return false;
3566 }
3567 
3568 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3569 					   int nr_entries, const char *help,
3570 					   struct hist_browser_timer *hbt,
3571 					   float min_pcnt,
3572 					   struct perf_env *env,
3573 					   bool warn_lost_event,
3574 					   struct annotation_options *annotation_opts)
3575 {
3576 	struct evsel *pos;
3577 	struct evsel_menu menu = {
3578 		.b = {
3579 			.entries    = &evlist->core.entries,
3580 			.refresh    = ui_browser__list_head_refresh,
3581 			.seek	    = ui_browser__list_head_seek,
3582 			.write	    = perf_evsel_menu__write,
3583 			.filter	    = filter_group_entries,
3584 			.nr_entries = nr_entries,
3585 			.priv	    = evlist,
3586 		},
3587 		.min_pcnt = min_pcnt,
3588 		.env = env,
3589 		.annotation_opts = annotation_opts,
3590 	};
3591 
3592 	ui_helpline__push("Press ESC to exit");
3593 
3594 	evlist__for_each_entry(evlist, pos) {
3595 		const char *ev_name = evsel__name(pos);
3596 		size_t line_len = strlen(ev_name) + 7;
3597 
3598 		if (menu.b.width < line_len)
3599 			menu.b.width = line_len;
3600 	}
3601 
3602 	return perf_evsel_menu__run(&menu, nr_entries, help,
3603 				    hbt, warn_lost_event);
3604 }
3605 
3606 static bool perf_evlist__single_entry(struct evlist *evlist)
3607 {
3608 	int nr_entries = evlist->core.nr_entries;
3609 
3610 	if (nr_entries == 1)
3611 	       return true;
3612 
3613 	if (nr_entries == 2) {
3614 		struct evsel *last = evlist__last(evlist);
3615 
3616 		if (evsel__is_dummy_event(last))
3617 			return true;
3618 	}
3619 
3620 	return false;
3621 }
3622 
3623 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3624 				  struct hist_browser_timer *hbt,
3625 				  float min_pcnt,
3626 				  struct perf_env *env,
3627 				  bool warn_lost_event,
3628 				  struct annotation_options *annotation_opts)
3629 {
3630 	int nr_entries = evlist->core.nr_entries;
3631 
3632 single_entry:
3633 	if (perf_evlist__single_entry(evlist)) {
3634 		struct evsel *first = evlist__first(evlist);
3635 
3636 		return perf_evsel__hists_browse(first, nr_entries, help,
3637 						false, hbt, min_pcnt,
3638 						env, warn_lost_event,
3639 						annotation_opts);
3640 	}
3641 
3642 	if (symbol_conf.event_group) {
3643 		struct evsel *pos;
3644 
3645 		nr_entries = 0;
3646 		evlist__for_each_entry(evlist, pos) {
3647 			if (evsel__is_group_leader(pos))
3648 				nr_entries++;
3649 		}
3650 
3651 		if (nr_entries == 1)
3652 			goto single_entry;
3653 	}
3654 
3655 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3656 					       hbt, min_pcnt, env,
3657 					       warn_lost_event,
3658 					       annotation_opts);
3659 }
3660 
3661 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3662 				      size_t size)
3663 {
3664 	struct hists *hists = evsel__hists(browser->block_evsel);
3665 	const char *evname = evsel__name(browser->block_evsel);
3666 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3667 	int ret;
3668 
3669 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3670 	if (evname)
3671 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3672 
3673 	return 0;
3674 }
3675 
3676 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3677 			   float min_percent, struct perf_env *env,
3678 			   struct annotation_options *annotation_opts)
3679 {
3680 	struct hists *hists = &bh->block_hists;
3681 	struct hist_browser *browser;
3682 	int key = -1;
3683 	struct popup_action action;
3684 	static const char help[] =
3685 	" q             Quit \n";
3686 
3687 	browser = hist_browser__new(hists);
3688 	if (!browser)
3689 		return -1;
3690 
3691 	browser->block_evsel = evsel;
3692 	browser->title = block_hists_browser__title;
3693 	browser->min_pcnt = min_percent;
3694 	browser->env = env;
3695 	browser->annotation_opts = annotation_opts;
3696 
3697 	/* reset abort key so that it can get Ctrl-C as a key */
3698 	SLang_reset_tty();
3699 	SLang_init_tty(0, 0, 0);
3700 
3701 	memset(&action, 0, sizeof(action));
3702 
3703 	while (1) {
3704 		key = hist_browser__run(browser, "? - help", true, 0);
3705 
3706 		switch (key) {
3707 		case 'q':
3708 			goto out;
3709 		case '?':
3710 			ui_browser__help_window(&browser->b, help);
3711 			break;
3712 		case 'a':
3713 		case K_ENTER:
3714 			if (!browser->selection ||
3715 			    !browser->selection->sym) {
3716 				continue;
3717 			}
3718 
3719 			action.ms.map = browser->selection->map;
3720 			action.ms.sym = browser->selection->sym;
3721 			do_annotate(browser, &action);
3722 			continue;
3723 		default:
3724 			break;
3725 		}
3726 	}
3727 
3728 out:
3729 	hist_browser__delete(browser);
3730 	return 0;
3731 }
3732