xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 569820be)
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 "../../util/util.h"
33 #include "../../arch/common.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 	 * visible 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 		struct evsel *evsel = hists_to_evsel(browser->hists);
686 		u64 nr_entries;
687 
688 		WARN_ON_ONCE(!hbt);
689 
690 		if (hbt)
691 			hbt->timer(hbt->arg);
692 
693 		if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
694 			hist_browser__update_nr_entries(browser);
695 
696 		nr_entries = hist_browser__nr_entries(browser);
697 		ui_browser__update_nr_entries(&browser->b, nr_entries);
698 
699 		if (warn_lost_event &&
700 		    (evsel->evlist->stats.nr_lost_warned !=
701 		     evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
702 			evsel->evlist->stats.nr_lost_warned =
703 				evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
704 			ui_browser__warn_lost_events(&browser->b);
705 		}
706 
707 		hist_browser__title(browser, title, size);
708 		ui_browser__show_title(&browser->b, title);
709 		break;
710 	}
711 	case 'D': { /* Debug */
712 		struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
713 		static int seq;
714 
715 		ui_helpline__pop();
716 		ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
717 				   seq++, browser->b.nr_entries, browser->hists->nr_entries,
718 				   browser->b.extra_title_lines, browser->b.rows,
719 				   browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
720 	}
721 		break;
722 	case 'C':
723 		/* Collapse the whole world. */
724 		hist_browser__set_folding(browser, false);
725 		break;
726 	case 'c':
727 		/* Collapse the selected entry. */
728 		hist_browser__set_folding_selected(browser, false);
729 		break;
730 	case 'E':
731 		/* Expand the whole world. */
732 		hist_browser__set_folding(browser, true);
733 		break;
734 	case 'e':
735 		/* Expand the selected entry. */
736 		hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser));
737 		break;
738 	case 'H':
739 		browser->show_headers = !browser->show_headers;
740 		hist_browser__update_rows(browser);
741 		break;
742 	case '+':
743 		if (hist_browser__toggle_fold(browser))
744 			break;
745 		/* fall thru */
746 	default:
747 		return -1;
748 	}
749 
750 	return 0;
751 }
752 
753 int hist_browser__run(struct hist_browser *browser, const char *help,
754 		      bool warn_lost_event, int key)
755 {
756 	char title[160];
757 	struct hist_browser_timer *hbt = browser->hbt;
758 	int delay_secs = hbt ? hbt->refresh : 0;
759 
760 	browser->b.entries = &browser->hists->entries;
761 	browser->b.nr_entries = hist_browser__nr_entries(browser);
762 
763 	hist_browser__title(browser, title, sizeof(title));
764 
765 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
766 		return -1;
767 
768 	if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
769 		goto out;
770 
771 	while (1) {
772 		key = ui_browser__run(&browser->b, delay_secs);
773 
774 		if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
775 			break;
776 	}
777 out:
778 	ui_browser__hide(&browser->b);
779 	return key;
780 }
781 
782 struct callchain_print_arg {
783 	/* for hists browser */
784 	off_t	row_offset;
785 	bool	is_current_entry;
786 
787 	/* for file dump */
788 	FILE	*fp;
789 	int	printed;
790 };
791 
792 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
793 					 struct callchain_list *chain,
794 					 const char *str, int offset,
795 					 unsigned short row,
796 					 struct callchain_print_arg *arg);
797 
798 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
799 					       struct callchain_list *chain,
800 					       const char *str, int offset,
801 					       unsigned short row,
802 					       struct callchain_print_arg *arg)
803 {
804 	int color, width;
805 	char folded_sign = callchain_list__folded(chain);
806 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
807 
808 	color = HE_COLORSET_NORMAL;
809 	width = browser->b.width - (offset + 2);
810 	if (ui_browser__is_current_entry(&browser->b, row)) {
811 		browser->selection = &chain->ms;
812 		color = HE_COLORSET_SELECTED;
813 		arg->is_current_entry = true;
814 	}
815 
816 	ui_browser__set_color(&browser->b, color);
817 	ui_browser__gotorc(&browser->b, row, 0);
818 	ui_browser__write_nstring(&browser->b, " ", offset);
819 	ui_browser__printf(&browser->b, "%c", folded_sign);
820 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
821 	ui_browser__write_nstring(&browser->b, str, width);
822 }
823 
824 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
825 						  struct callchain_list *chain,
826 						  const char *str, int offset,
827 						  unsigned short row __maybe_unused,
828 						  struct callchain_print_arg *arg)
829 {
830 	char folded_sign = callchain_list__folded(chain);
831 
832 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
833 				folded_sign, str);
834 }
835 
836 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
837 				     unsigned short row);
838 
839 static bool hist_browser__check_output_full(struct hist_browser *browser,
840 					    unsigned short row)
841 {
842 	return browser->b.rows == row;
843 }
844 
845 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
846 					  unsigned short row __maybe_unused)
847 {
848 	return false;
849 }
850 
851 #define LEVEL_OFFSET_STEP 3
852 
853 static int hist_browser__show_callchain_list(struct hist_browser *browser,
854 					     struct callchain_node *node,
855 					     struct callchain_list *chain,
856 					     unsigned short row, u64 total,
857 					     bool need_percent, int offset,
858 					     print_callchain_entry_fn print,
859 					     struct callchain_print_arg *arg)
860 {
861 	char bf[1024], *alloc_str;
862 	char buf[64], *alloc_str2;
863 	const char *str;
864 	int ret = 1;
865 
866 	if (arg->row_offset != 0) {
867 		arg->row_offset--;
868 		return 0;
869 	}
870 
871 	alloc_str = NULL;
872 	alloc_str2 = NULL;
873 
874 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
875 				       browser->show_dso);
876 
877 	if (symbol_conf.show_branchflag_count) {
878 		callchain_list_counts__printf_value(chain, NULL,
879 						    buf, sizeof(buf));
880 
881 		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
882 			str = "Not enough memory!";
883 		else
884 			str = alloc_str2;
885 	}
886 
887 	if (need_percent) {
888 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
889 						total);
890 
891 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
892 			str = "Not enough memory!";
893 		else
894 			str = alloc_str;
895 	}
896 
897 	print(browser, chain, str, offset, row, arg);
898 	free(alloc_str);
899 	free(alloc_str2);
900 
901 	return ret;
902 }
903 
904 static bool check_percent_display(struct rb_node *node, u64 parent_total)
905 {
906 	struct callchain_node *child;
907 
908 	if (node == NULL)
909 		return false;
910 
911 	if (rb_next(node))
912 		return true;
913 
914 	child = rb_entry(node, struct callchain_node, rb_node);
915 	return callchain_cumul_hits(child) != parent_total;
916 }
917 
918 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
919 					     struct rb_root *root,
920 					     unsigned short row, u64 total,
921 					     u64 parent_total,
922 					     print_callchain_entry_fn print,
923 					     struct callchain_print_arg *arg,
924 					     check_output_full_fn is_output_full)
925 {
926 	struct rb_node *node;
927 	int first_row = row, offset = LEVEL_OFFSET_STEP;
928 	bool need_percent;
929 
930 	node = rb_first(root);
931 	need_percent = check_percent_display(node, parent_total);
932 
933 	while (node) {
934 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
935 		struct rb_node *next = rb_next(node);
936 		struct callchain_list *chain;
937 		char folded_sign = ' ';
938 		int first = true;
939 		int extra_offset = 0;
940 
941 		list_for_each_entry(chain, &child->parent_val, list) {
942 			bool was_first = first;
943 
944 			if (first)
945 				first = false;
946 			else if (need_percent)
947 				extra_offset = LEVEL_OFFSET_STEP;
948 
949 			folded_sign = callchain_list__folded(chain);
950 
951 			row += hist_browser__show_callchain_list(browser, child,
952 							chain, row, total,
953 							was_first && need_percent,
954 							offset + extra_offset,
955 							print, arg);
956 
957 			if (is_output_full(browser, row))
958 				goto out;
959 
960 			if (folded_sign == '+')
961 				goto next;
962 		}
963 
964 		list_for_each_entry(chain, &child->val, list) {
965 			bool was_first = first;
966 
967 			if (first)
968 				first = false;
969 			else if (need_percent)
970 				extra_offset = LEVEL_OFFSET_STEP;
971 
972 			folded_sign = callchain_list__folded(chain);
973 
974 			row += hist_browser__show_callchain_list(browser, child,
975 							chain, row, total,
976 							was_first && need_percent,
977 							offset + extra_offset,
978 							print, arg);
979 
980 			if (is_output_full(browser, row))
981 				goto out;
982 
983 			if (folded_sign == '+')
984 				break;
985 		}
986 
987 next:
988 		if (is_output_full(browser, row))
989 			break;
990 		node = next;
991 	}
992 out:
993 	return row - first_row;
994 }
995 
996 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
997 						struct callchain_list *chain,
998 						char *value_str, char *old_str)
999 {
1000 	char bf[1024];
1001 	const char *str;
1002 	char *new;
1003 
1004 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
1005 				       browser->show_dso);
1006 	if (old_str) {
1007 		if (asprintf(&new, "%s%s%s", old_str,
1008 			     symbol_conf.field_sep ?: ";", str) < 0)
1009 			new = NULL;
1010 	} else {
1011 		if (value_str) {
1012 			if (asprintf(&new, "%s %s", value_str, str) < 0)
1013 				new = NULL;
1014 		} else {
1015 			if (asprintf(&new, "%s", str) < 0)
1016 				new = NULL;
1017 		}
1018 	}
1019 	return new;
1020 }
1021 
1022 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1023 					       struct rb_root *root,
1024 					       unsigned short row, u64 total,
1025 					       u64 parent_total,
1026 					       print_callchain_entry_fn print,
1027 					       struct callchain_print_arg *arg,
1028 					       check_output_full_fn is_output_full)
1029 {
1030 	struct rb_node *node;
1031 	int first_row = row, offset = LEVEL_OFFSET_STEP;
1032 	bool need_percent;
1033 
1034 	node = rb_first(root);
1035 	need_percent = check_percent_display(node, parent_total);
1036 
1037 	while (node) {
1038 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1039 		struct rb_node *next = rb_next(node);
1040 		struct callchain_list *chain, *first_chain = NULL;
1041 		int first = true;
1042 		char *value_str = NULL, *value_str_alloc = NULL;
1043 		char *chain_str = NULL, *chain_str_alloc = NULL;
1044 
1045 		if (arg->row_offset != 0) {
1046 			arg->row_offset--;
1047 			goto next;
1048 		}
1049 
1050 		if (need_percent) {
1051 			char buf[64];
1052 
1053 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1054 			if (asprintf(&value_str, "%s", buf) < 0) {
1055 				value_str = (char *)"<...>";
1056 				goto do_print;
1057 			}
1058 			value_str_alloc = value_str;
1059 		}
1060 
1061 		list_for_each_entry(chain, &child->parent_val, list) {
1062 			chain_str = hist_browser__folded_callchain_str(browser,
1063 						chain, value_str, chain_str);
1064 			if (first) {
1065 				first = false;
1066 				first_chain = chain;
1067 			}
1068 
1069 			if (chain_str == NULL) {
1070 				chain_str = (char *)"Not enough memory!";
1071 				goto do_print;
1072 			}
1073 
1074 			chain_str_alloc = chain_str;
1075 		}
1076 
1077 		list_for_each_entry(chain, &child->val, list) {
1078 			chain_str = hist_browser__folded_callchain_str(browser,
1079 						chain, value_str, chain_str);
1080 			if (first) {
1081 				first = false;
1082 				first_chain = chain;
1083 			}
1084 
1085 			if (chain_str == NULL) {
1086 				chain_str = (char *)"Not enough memory!";
1087 				goto do_print;
1088 			}
1089 
1090 			chain_str_alloc = chain_str;
1091 		}
1092 
1093 do_print:
1094 		print(browser, first_chain, chain_str, offset, row++, arg);
1095 		free(value_str_alloc);
1096 		free(chain_str_alloc);
1097 
1098 next:
1099 		if (is_output_full(browser, row))
1100 			break;
1101 		node = next;
1102 	}
1103 
1104 	return row - first_row;
1105 }
1106 
1107 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1108 					struct rb_root *root, int level,
1109 					unsigned short row, u64 total,
1110 					u64 parent_total,
1111 					print_callchain_entry_fn print,
1112 					struct callchain_print_arg *arg,
1113 					check_output_full_fn is_output_full)
1114 {
1115 	struct rb_node *node;
1116 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1117 	bool need_percent;
1118 	u64 percent_total = total;
1119 
1120 	if (callchain_param.mode == CHAIN_GRAPH_REL)
1121 		percent_total = parent_total;
1122 
1123 	node = rb_first(root);
1124 	need_percent = check_percent_display(node, parent_total);
1125 
1126 	while (node) {
1127 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1128 		struct rb_node *next = rb_next(node);
1129 		struct callchain_list *chain;
1130 		char folded_sign = ' ';
1131 		int first = true;
1132 		int extra_offset = 0;
1133 
1134 		list_for_each_entry(chain, &child->val, list) {
1135 			bool was_first = first;
1136 
1137 			if (first)
1138 				first = false;
1139 			else if (need_percent)
1140 				extra_offset = LEVEL_OFFSET_STEP;
1141 
1142 			folded_sign = callchain_list__folded(chain);
1143 
1144 			row += hist_browser__show_callchain_list(browser, child,
1145 							chain, row, percent_total,
1146 							was_first && need_percent,
1147 							offset + extra_offset,
1148 							print, arg);
1149 
1150 			if (is_output_full(browser, row))
1151 				goto out;
1152 
1153 			if (folded_sign == '+')
1154 				break;
1155 		}
1156 
1157 		if (folded_sign == '-') {
1158 			const int new_level = level + (extra_offset ? 2 : 1);
1159 
1160 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1161 							    new_level, row, total,
1162 							    child->children_hit,
1163 							    print, arg, is_output_full);
1164 		}
1165 		if (is_output_full(browser, row))
1166 			break;
1167 		node = next;
1168 	}
1169 out:
1170 	return row - first_row;
1171 }
1172 
1173 static int hist_browser__show_callchain(struct hist_browser *browser,
1174 					struct hist_entry *entry, int level,
1175 					unsigned short row,
1176 					print_callchain_entry_fn print,
1177 					struct callchain_print_arg *arg,
1178 					check_output_full_fn is_output_full)
1179 {
1180 	u64 total = hists__total_period(entry->hists);
1181 	u64 parent_total;
1182 	int printed;
1183 
1184 	if (symbol_conf.cumulate_callchain)
1185 		parent_total = entry->stat_acc->period;
1186 	else
1187 		parent_total = entry->stat.period;
1188 
1189 	if (callchain_param.mode == CHAIN_FLAT) {
1190 		printed = hist_browser__show_callchain_flat(browser,
1191 						&entry->sorted_chain, row,
1192 						total, parent_total, print, arg,
1193 						is_output_full);
1194 	} else if (callchain_param.mode == CHAIN_FOLDED) {
1195 		printed = hist_browser__show_callchain_folded(browser,
1196 						&entry->sorted_chain, row,
1197 						total, parent_total, print, arg,
1198 						is_output_full);
1199 	} else {
1200 		printed = hist_browser__show_callchain_graph(browser,
1201 						&entry->sorted_chain, level, row,
1202 						total, parent_total, print, arg,
1203 						is_output_full);
1204 	}
1205 
1206 	if (arg->is_current_entry)
1207 		browser->he_selection = entry;
1208 
1209 	return printed;
1210 }
1211 
1212 struct hpp_arg {
1213 	struct ui_browser *b;
1214 	char folded_sign;
1215 	bool current_entry;
1216 };
1217 
1218 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1219 {
1220 	struct hpp_arg *arg = hpp->ptr;
1221 	int ret, len;
1222 	va_list args;
1223 	double percent;
1224 
1225 	va_start(args, fmt);
1226 	len = va_arg(args, int);
1227 	percent = va_arg(args, double);
1228 	va_end(args);
1229 
1230 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1231 
1232 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1233 	ui_browser__printf(arg->b, "%s", hpp->buf);
1234 
1235 	return ret;
1236 }
1237 
1238 #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
1239 static u64 __hpp_get_##_field(struct hist_entry *he)			\
1240 {									\
1241 	return he->stat._field;						\
1242 }									\
1243 									\
1244 static int								\
1245 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1246 				struct perf_hpp *hpp,			\
1247 				struct hist_entry *he)			\
1248 {									\
1249 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
1250 			__hpp__slsmg_color_printf, true);		\
1251 }
1252 
1253 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
1254 static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
1255 {									\
1256 	return he->stat_acc->_field;					\
1257 }									\
1258 									\
1259 static int								\
1260 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1261 				struct perf_hpp *hpp,			\
1262 				struct hist_entry *he)			\
1263 {									\
1264 	if (!symbol_conf.cumulate_callchain) {				\
1265 		struct hpp_arg *arg = hpp->ptr;				\
1266 		int len = fmt->user_len ?: fmt->len;			\
1267 		int ret = scnprintf(hpp->buf, hpp->size,		\
1268 				    "%*s", len, "N/A");			\
1269 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
1270 									\
1271 		return ret;						\
1272 	}								\
1273 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
1274 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
1275 }
1276 
1277 __HPP_COLOR_PERCENT_FN(overhead, period)
1278 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1279 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1280 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1281 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1282 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1283 
1284 #undef __HPP_COLOR_PERCENT_FN
1285 #undef __HPP_COLOR_ACC_PERCENT_FN
1286 
1287 void hist_browser__init_hpp(void)
1288 {
1289 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1290 				hist_browser__hpp_color_overhead;
1291 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1292 				hist_browser__hpp_color_overhead_sys;
1293 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1294 				hist_browser__hpp_color_overhead_us;
1295 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1296 				hist_browser__hpp_color_overhead_guest_sys;
1297 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1298 				hist_browser__hpp_color_overhead_guest_us;
1299 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1300 				hist_browser__hpp_color_overhead_acc;
1301 
1302 	res_sample_init();
1303 }
1304 
1305 static int hist_browser__show_entry(struct hist_browser *browser,
1306 				    struct hist_entry *entry,
1307 				    unsigned short row)
1308 {
1309 	int printed = 0;
1310 	int width = browser->b.width;
1311 	char folded_sign = ' ';
1312 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1313 	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1314 	off_t row_offset = entry->row_offset;
1315 	bool first = true;
1316 	struct perf_hpp_fmt *fmt;
1317 
1318 	if (current_entry) {
1319 		browser->he_selection = entry;
1320 		browser->selection = &entry->ms;
1321 	}
1322 
1323 	if (use_callchain) {
1324 		hist_entry__init_have_children(entry);
1325 		folded_sign = hist_entry__folded(entry);
1326 	}
1327 
1328 	if (row_offset == 0) {
1329 		struct hpp_arg arg = {
1330 			.b		= &browser->b,
1331 			.folded_sign	= folded_sign,
1332 			.current_entry	= current_entry,
1333 		};
1334 		int column = 0;
1335 
1336 		ui_browser__gotorc(&browser->b, row, 0);
1337 
1338 		hists__for_each_format(browser->hists, fmt) {
1339 			char s[2048];
1340 			struct perf_hpp hpp = {
1341 				.buf	= s,
1342 				.size	= sizeof(s),
1343 				.ptr	= &arg,
1344 			};
1345 
1346 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1347 			    column++ < browser->b.horiz_scroll)
1348 				continue;
1349 
1350 			if (current_entry && browser->b.navkeypressed) {
1351 				ui_browser__set_color(&browser->b,
1352 						      HE_COLORSET_SELECTED);
1353 			} else {
1354 				ui_browser__set_color(&browser->b,
1355 						      HE_COLORSET_NORMAL);
1356 			}
1357 
1358 			if (first) {
1359 				if (use_callchain) {
1360 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1361 					width -= 2;
1362 				}
1363 				first = false;
1364 			} else {
1365 				ui_browser__printf(&browser->b, "  ");
1366 				width -= 2;
1367 			}
1368 
1369 			if (fmt->color) {
1370 				int ret = fmt->color(fmt, &hpp, entry);
1371 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1372 				/*
1373 				 * fmt->color() already used ui_browser to
1374 				 * print the non alignment bits, skip it (+ret):
1375 				 */
1376 				ui_browser__printf(&browser->b, "%s", s + ret);
1377 			} else {
1378 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1379 				ui_browser__printf(&browser->b, "%s", s);
1380 			}
1381 			width -= hpp.buf - s;
1382 		}
1383 
1384 		/* The scroll bar isn't being used */
1385 		if (!browser->b.navkeypressed)
1386 			width += 1;
1387 
1388 		ui_browser__write_nstring(&browser->b, "", width);
1389 
1390 		++row;
1391 		++printed;
1392 	} else
1393 		--row_offset;
1394 
1395 	if (folded_sign == '-' && row != browser->b.rows) {
1396 		struct callchain_print_arg arg = {
1397 			.row_offset = row_offset,
1398 			.is_current_entry = current_entry,
1399 		};
1400 
1401 		printed += hist_browser__show_callchain(browser,
1402 				entry, 1, row,
1403 				hist_browser__show_callchain_entry,
1404 				&arg,
1405 				hist_browser__check_output_full);
1406 	}
1407 
1408 	return printed;
1409 }
1410 
1411 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1412 					      struct hist_entry *entry,
1413 					      unsigned short row,
1414 					      int level)
1415 {
1416 	int printed = 0;
1417 	int width = browser->b.width;
1418 	char folded_sign = ' ';
1419 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1420 	off_t row_offset = entry->row_offset;
1421 	bool first = true;
1422 	struct perf_hpp_fmt *fmt;
1423 	struct perf_hpp_list_node *fmt_node;
1424 	struct hpp_arg arg = {
1425 		.b		= &browser->b,
1426 		.current_entry	= current_entry,
1427 	};
1428 	int column = 0;
1429 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1430 
1431 	if (current_entry) {
1432 		browser->he_selection = entry;
1433 		browser->selection = &entry->ms;
1434 	}
1435 
1436 	hist_entry__init_have_children(entry);
1437 	folded_sign = hist_entry__folded(entry);
1438 	arg.folded_sign = folded_sign;
1439 
1440 	if (entry->leaf && row_offset) {
1441 		row_offset--;
1442 		goto show_callchain;
1443 	}
1444 
1445 	ui_browser__gotorc(&browser->b, row, 0);
1446 
1447 	if (current_entry && browser->b.navkeypressed)
1448 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1449 	else
1450 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1451 
1452 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1453 	width -= level * HIERARCHY_INDENT;
1454 
1455 	/* the first hpp_list_node is for overhead columns */
1456 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1457 				    struct perf_hpp_list_node, list);
1458 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1459 		char s[2048];
1460 		struct perf_hpp hpp = {
1461 			.buf		= s,
1462 			.size		= sizeof(s),
1463 			.ptr		= &arg,
1464 		};
1465 
1466 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1467 		    column++ < browser->b.horiz_scroll)
1468 			continue;
1469 
1470 		if (current_entry && browser->b.navkeypressed) {
1471 			ui_browser__set_color(&browser->b,
1472 					      HE_COLORSET_SELECTED);
1473 		} else {
1474 			ui_browser__set_color(&browser->b,
1475 					      HE_COLORSET_NORMAL);
1476 		}
1477 
1478 		if (first) {
1479 			ui_browser__printf(&browser->b, "%c ", folded_sign);
1480 			width -= 2;
1481 			first = false;
1482 		} else {
1483 			ui_browser__printf(&browser->b, "  ");
1484 			width -= 2;
1485 		}
1486 
1487 		if (fmt->color) {
1488 			int ret = fmt->color(fmt, &hpp, entry);
1489 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1490 			/*
1491 			 * fmt->color() already used ui_browser to
1492 			 * print the non alignment bits, skip it (+ret):
1493 			 */
1494 			ui_browser__printf(&browser->b, "%s", s + ret);
1495 		} else {
1496 			int ret = fmt->entry(fmt, &hpp, entry);
1497 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1498 			ui_browser__printf(&browser->b, "%s", s);
1499 		}
1500 		width -= hpp.buf - s;
1501 	}
1502 
1503 	if (!first) {
1504 		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1505 		width -= hierarchy_indent;
1506 	}
1507 
1508 	if (column >= browser->b.horiz_scroll) {
1509 		char s[2048];
1510 		struct perf_hpp hpp = {
1511 			.buf		= s,
1512 			.size		= sizeof(s),
1513 			.ptr		= &arg,
1514 		};
1515 
1516 		if (current_entry && browser->b.navkeypressed) {
1517 			ui_browser__set_color(&browser->b,
1518 					      HE_COLORSET_SELECTED);
1519 		} else {
1520 			ui_browser__set_color(&browser->b,
1521 					      HE_COLORSET_NORMAL);
1522 		}
1523 
1524 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1525 			if (first) {
1526 				ui_browser__printf(&browser->b, "%c ", folded_sign);
1527 				first = false;
1528 			} else {
1529 				ui_browser__write_nstring(&browser->b, "", 2);
1530 			}
1531 
1532 			width -= 2;
1533 
1534 			/*
1535 			 * No need to call hist_entry__snprintf_alignment()
1536 			 * since this fmt is always the last column in the
1537 			 * hierarchy mode.
1538 			 */
1539 			if (fmt->color) {
1540 				width -= fmt->color(fmt, &hpp, entry);
1541 			} else {
1542 				int i = 0;
1543 
1544 				width -= fmt->entry(fmt, &hpp, entry);
1545 				ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1546 
1547 				while (isspace(s[i++]))
1548 					width++;
1549 			}
1550 		}
1551 	}
1552 
1553 	/* The scroll bar isn't being used */
1554 	if (!browser->b.navkeypressed)
1555 		width += 1;
1556 
1557 	ui_browser__write_nstring(&browser->b, "", width);
1558 
1559 	++row;
1560 	++printed;
1561 
1562 show_callchain:
1563 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1564 		struct callchain_print_arg carg = {
1565 			.row_offset = row_offset,
1566 		};
1567 
1568 		printed += hist_browser__show_callchain(browser, entry,
1569 					level + 1, row,
1570 					hist_browser__show_callchain_entry, &carg,
1571 					hist_browser__check_output_full);
1572 	}
1573 
1574 	return printed;
1575 }
1576 
1577 static int hist_browser__show_no_entry(struct hist_browser *browser,
1578 				       unsigned short row, int level)
1579 {
1580 	int width = browser->b.width;
1581 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1582 	bool first = true;
1583 	int column = 0;
1584 	int ret;
1585 	struct perf_hpp_fmt *fmt;
1586 	struct perf_hpp_list_node *fmt_node;
1587 	int indent = browser->hists->nr_hpp_node - 2;
1588 
1589 	if (current_entry) {
1590 		browser->he_selection = NULL;
1591 		browser->selection = NULL;
1592 	}
1593 
1594 	ui_browser__gotorc(&browser->b, row, 0);
1595 
1596 	if (current_entry && browser->b.navkeypressed)
1597 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1598 	else
1599 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1600 
1601 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1602 	width -= level * HIERARCHY_INDENT;
1603 
1604 	/* the first hpp_list_node is for overhead columns */
1605 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1606 				    struct perf_hpp_list_node, list);
1607 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1608 		if (perf_hpp__should_skip(fmt, browser->hists) ||
1609 		    column++ < browser->b.horiz_scroll)
1610 			continue;
1611 
1612 		ret = fmt->width(fmt, NULL, browser->hists);
1613 
1614 		if (first) {
1615 			/* for folded sign */
1616 			first = false;
1617 			ret++;
1618 		} else {
1619 			/* space between columns */
1620 			ret += 2;
1621 		}
1622 
1623 		ui_browser__write_nstring(&browser->b, "", ret);
1624 		width -= ret;
1625 	}
1626 
1627 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1628 	width -= indent * HIERARCHY_INDENT;
1629 
1630 	if (column >= browser->b.horiz_scroll) {
1631 		char buf[32];
1632 
1633 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1634 		ui_browser__printf(&browser->b, "  %s", buf);
1635 		width -= ret + 2;
1636 	}
1637 
1638 	/* The scroll bar isn't being used */
1639 	if (!browser->b.navkeypressed)
1640 		width += 1;
1641 
1642 	ui_browser__write_nstring(&browser->b, "", width);
1643 	return 1;
1644 }
1645 
1646 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1647 {
1648 	advance_hpp(hpp, inc);
1649 	return hpp->size <= 0;
1650 }
1651 
1652 static int
1653 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1654 				 size_t size, int line)
1655 {
1656 	struct hists *hists = browser->hists;
1657 	struct perf_hpp dummy_hpp = {
1658 		.buf    = buf,
1659 		.size   = size,
1660 	};
1661 	struct perf_hpp_fmt *fmt;
1662 	size_t ret = 0;
1663 	int column = 0;
1664 	int span = 0;
1665 
1666 	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1667 		ret = scnprintf(buf, size, "  ");
1668 		if (advance_hpp_check(&dummy_hpp, ret))
1669 			return ret;
1670 	}
1671 
1672 	hists__for_each_format(browser->hists, fmt) {
1673 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1674 			continue;
1675 
1676 		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1677 		if (advance_hpp_check(&dummy_hpp, ret))
1678 			break;
1679 
1680 		if (span)
1681 			continue;
1682 
1683 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1684 		if (advance_hpp_check(&dummy_hpp, ret))
1685 			break;
1686 	}
1687 
1688 	return ret;
1689 }
1690 
1691 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1692 {
1693 	struct hists *hists = browser->hists;
1694 	struct perf_hpp dummy_hpp = {
1695 		.buf    = buf,
1696 		.size   = size,
1697 	};
1698 	struct perf_hpp_fmt *fmt;
1699 	struct perf_hpp_list_node *fmt_node;
1700 	size_t ret = 0;
1701 	int column = 0;
1702 	int indent = hists->nr_hpp_node - 2;
1703 	bool first_node, first_col;
1704 
1705 	ret = scnprintf(buf, size, "  ");
1706 	if (advance_hpp_check(&dummy_hpp, ret))
1707 		return ret;
1708 
1709 	first_node = true;
1710 	/* the first hpp_list_node is for overhead columns */
1711 	fmt_node = list_first_entry(&hists->hpp_formats,
1712 				    struct perf_hpp_list_node, list);
1713 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1714 		if (column++ < browser->b.horiz_scroll)
1715 			continue;
1716 
1717 		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1718 		if (advance_hpp_check(&dummy_hpp, ret))
1719 			break;
1720 
1721 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1722 		if (advance_hpp_check(&dummy_hpp, ret))
1723 			break;
1724 
1725 		first_node = false;
1726 	}
1727 
1728 	if (!first_node) {
1729 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1730 				indent * HIERARCHY_INDENT, "");
1731 		if (advance_hpp_check(&dummy_hpp, ret))
1732 			return ret;
1733 	}
1734 
1735 	first_node = true;
1736 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1737 		if (!first_node) {
1738 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1739 			if (advance_hpp_check(&dummy_hpp, ret))
1740 				break;
1741 		}
1742 		first_node = false;
1743 
1744 		first_col = true;
1745 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1746 			char *start;
1747 
1748 			if (perf_hpp__should_skip(fmt, hists))
1749 				continue;
1750 
1751 			if (!first_col) {
1752 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1753 				if (advance_hpp_check(&dummy_hpp, ret))
1754 					break;
1755 			}
1756 			first_col = false;
1757 
1758 			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1759 			dummy_hpp.buf[ret] = '\0';
1760 
1761 			start = strim(dummy_hpp.buf);
1762 			ret = strlen(start);
1763 
1764 			if (start != dummy_hpp.buf)
1765 				memmove(dummy_hpp.buf, start, ret + 1);
1766 
1767 			if (advance_hpp_check(&dummy_hpp, ret))
1768 				break;
1769 		}
1770 	}
1771 
1772 	return ret;
1773 }
1774 
1775 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1776 {
1777 	char headers[1024];
1778 
1779 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1780 						   sizeof(headers));
1781 
1782 	ui_browser__gotorc(&browser->b, 0, 0);
1783 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1784 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1785 }
1786 
1787 static void hists_browser__headers(struct hist_browser *browser)
1788 {
1789 	struct hists *hists = browser->hists;
1790 	struct perf_hpp_list *hpp_list = hists->hpp_list;
1791 
1792 	int line;
1793 
1794 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
1795 		char headers[1024];
1796 
1797 		hists_browser__scnprintf_headers(browser, headers,
1798 						 sizeof(headers), line);
1799 
1800 		ui_browser__gotorc_title(&browser->b, line, 0);
1801 		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1802 		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1803 	}
1804 }
1805 
1806 static void hist_browser__show_headers(struct hist_browser *browser)
1807 {
1808 	if (symbol_conf.report_hierarchy)
1809 		hists_browser__hierarchy_headers(browser);
1810 	else
1811 		hists_browser__headers(browser);
1812 }
1813 
1814 static void ui_browser__hists_init_top(struct ui_browser *browser)
1815 {
1816 	if (browser->top == NULL) {
1817 		struct hist_browser *hb;
1818 
1819 		hb = container_of(browser, struct hist_browser, b);
1820 		browser->top = rb_first_cached(&hb->hists->entries);
1821 	}
1822 }
1823 
1824 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1825 {
1826 	unsigned row = 0;
1827 	struct rb_node *nd;
1828 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1829 
1830 	if (hb->show_headers)
1831 		hist_browser__show_headers(hb);
1832 
1833 	ui_browser__hists_init_top(browser);
1834 	hb->he_selection = NULL;
1835 	hb->selection = NULL;
1836 
1837 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1838 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1839 		float percent;
1840 
1841 		if (h->filtered) {
1842 			/* let it move to sibling */
1843 			h->unfolded = false;
1844 			continue;
1845 		}
1846 
1847 		if (symbol_conf.report_individual_block)
1848 			percent = block_info__total_cycles_percent(h);
1849 		else
1850 			percent = hist_entry__get_percent_limit(h);
1851 
1852 		if (percent < hb->min_pcnt)
1853 			continue;
1854 
1855 		if (symbol_conf.report_hierarchy) {
1856 			row += hist_browser__show_hierarchy_entry(hb, h, row,
1857 								  h->depth);
1858 			if (row == browser->rows)
1859 				break;
1860 
1861 			if (h->has_no_entry) {
1862 				hist_browser__show_no_entry(hb, row, h->depth + 1);
1863 				row++;
1864 			}
1865 		} else {
1866 			row += hist_browser__show_entry(hb, h, row);
1867 		}
1868 
1869 		if (row == browser->rows)
1870 			break;
1871 	}
1872 
1873 	return row;
1874 }
1875 
1876 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1877 					     float min_pcnt)
1878 {
1879 	while (nd != NULL) {
1880 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1881 		float percent = hist_entry__get_percent_limit(h);
1882 
1883 		if (!h->filtered && percent >= min_pcnt)
1884 			return nd;
1885 
1886 		/*
1887 		 * If it's filtered, its all children also were filtered.
1888 		 * So move to sibling node.
1889 		 */
1890 		if (rb_next(nd))
1891 			nd = rb_next(nd);
1892 		else
1893 			nd = rb_hierarchy_next(nd);
1894 	}
1895 
1896 	return NULL;
1897 }
1898 
1899 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1900 						  float min_pcnt)
1901 {
1902 	while (nd != NULL) {
1903 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1904 		float percent = hist_entry__get_percent_limit(h);
1905 
1906 		if (!h->filtered && percent >= min_pcnt)
1907 			return nd;
1908 
1909 		nd = rb_hierarchy_prev(nd);
1910 	}
1911 
1912 	return NULL;
1913 }
1914 
1915 static void ui_browser__hists_seek(struct ui_browser *browser,
1916 				   off_t offset, int whence)
1917 {
1918 	struct hist_entry *h;
1919 	struct rb_node *nd;
1920 	bool first = true;
1921 	struct hist_browser *hb;
1922 
1923 	hb = container_of(browser, struct hist_browser, b);
1924 
1925 	if (browser->nr_entries == 0)
1926 		return;
1927 
1928 	ui_browser__hists_init_top(browser);
1929 
1930 	switch (whence) {
1931 	case SEEK_SET:
1932 		nd = hists__filter_entries(rb_first(browser->entries),
1933 					   hb->min_pcnt);
1934 		break;
1935 	case SEEK_CUR:
1936 		nd = browser->top;
1937 		goto do_offset;
1938 	case SEEK_END:
1939 		nd = rb_hierarchy_last(rb_last(browser->entries));
1940 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1941 		first = false;
1942 		break;
1943 	default:
1944 		return;
1945 	}
1946 
1947 	/*
1948 	 * Moves not relative to the first visible entry invalidates its
1949 	 * row_offset:
1950 	 */
1951 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1952 	h->row_offset = 0;
1953 
1954 	/*
1955 	 * Here we have to check if nd is expanded (+), if it is we can't go
1956 	 * the next top level hist_entry, instead we must compute an offset of
1957 	 * what _not_ to show and not change the first visible entry.
1958 	 *
1959 	 * This offset increments when we are going from top to bottom and
1960 	 * decreases when we're going from bottom to top.
1961 	 *
1962 	 * As we don't have backpointers to the top level in the callchains
1963 	 * structure, we need to always print the whole hist_entry callchain,
1964 	 * skipping the first ones that are before the first visible entry
1965 	 * and stop when we printed enough lines to fill the screen.
1966 	 */
1967 do_offset:
1968 	if (!nd)
1969 		return;
1970 
1971 	if (offset > 0) {
1972 		do {
1973 			h = rb_entry(nd, struct hist_entry, rb_node);
1974 			if (h->unfolded && h->leaf) {
1975 				u16 remaining = h->nr_rows - h->row_offset;
1976 				if (offset > remaining) {
1977 					offset -= remaining;
1978 					h->row_offset = 0;
1979 				} else {
1980 					h->row_offset += offset;
1981 					offset = 0;
1982 					browser->top = nd;
1983 					break;
1984 				}
1985 			}
1986 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1987 						   hb->min_pcnt);
1988 			if (nd == NULL)
1989 				break;
1990 			--offset;
1991 			browser->top = nd;
1992 		} while (offset != 0);
1993 	} else if (offset < 0) {
1994 		while (1) {
1995 			h = rb_entry(nd, struct hist_entry, rb_node);
1996 			if (h->unfolded && h->leaf) {
1997 				if (first) {
1998 					if (-offset > h->row_offset) {
1999 						offset += h->row_offset;
2000 						h->row_offset = 0;
2001 					} else {
2002 						h->row_offset += offset;
2003 						offset = 0;
2004 						browser->top = nd;
2005 						break;
2006 					}
2007 				} else {
2008 					if (-offset > h->nr_rows) {
2009 						offset += h->nr_rows;
2010 						h->row_offset = 0;
2011 					} else {
2012 						h->row_offset = h->nr_rows + offset;
2013 						offset = 0;
2014 						browser->top = nd;
2015 						break;
2016 					}
2017 				}
2018 			}
2019 
2020 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2021 							hb->min_pcnt);
2022 			if (nd == NULL)
2023 				break;
2024 			++offset;
2025 			browser->top = nd;
2026 			if (offset == 0) {
2027 				/*
2028 				 * Last unfiltered hist_entry, check if it is
2029 				 * unfolded, if it is then we should have
2030 				 * row_offset at its last entry.
2031 				 */
2032 				h = rb_entry(nd, struct hist_entry, rb_node);
2033 				if (h->unfolded && h->leaf)
2034 					h->row_offset = h->nr_rows;
2035 				break;
2036 			}
2037 			first = false;
2038 		}
2039 	} else {
2040 		browser->top = nd;
2041 		h = rb_entry(nd, struct hist_entry, rb_node);
2042 		h->row_offset = 0;
2043 	}
2044 }
2045 
2046 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2047 					   struct hist_entry *he, FILE *fp,
2048 					   int level)
2049 {
2050 	struct callchain_print_arg arg  = {
2051 		.fp = fp,
2052 	};
2053 
2054 	hist_browser__show_callchain(browser, he, level, 0,
2055 				     hist_browser__fprintf_callchain_entry, &arg,
2056 				     hist_browser__check_dump_full);
2057 	return arg.printed;
2058 }
2059 
2060 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2061 				       struct hist_entry *he, FILE *fp)
2062 {
2063 	char s[8192];
2064 	int printed = 0;
2065 	char folded_sign = ' ';
2066 	struct perf_hpp hpp = {
2067 		.buf = s,
2068 		.size = sizeof(s),
2069 	};
2070 	struct perf_hpp_fmt *fmt;
2071 	bool first = true;
2072 	int ret;
2073 
2074 	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2075 		folded_sign = hist_entry__folded(he);
2076 		printed += fprintf(fp, "%c ", folded_sign);
2077 	}
2078 
2079 	hists__for_each_format(browser->hists, fmt) {
2080 		if (perf_hpp__should_skip(fmt, he->hists))
2081 			continue;
2082 
2083 		if (!first) {
2084 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2085 			advance_hpp(&hpp, ret);
2086 		} else
2087 			first = false;
2088 
2089 		ret = fmt->entry(fmt, &hpp, he);
2090 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2091 		advance_hpp(&hpp, ret);
2092 	}
2093 	printed += fprintf(fp, "%s\n", s);
2094 
2095 	if (folded_sign == '-')
2096 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2097 
2098 	return printed;
2099 }
2100 
2101 
2102 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2103 						 struct hist_entry *he,
2104 						 FILE *fp, int level)
2105 {
2106 	char s[8192];
2107 	int printed = 0;
2108 	char folded_sign = ' ';
2109 	struct perf_hpp hpp = {
2110 		.buf = s,
2111 		.size = sizeof(s),
2112 	};
2113 	struct perf_hpp_fmt *fmt;
2114 	struct perf_hpp_list_node *fmt_node;
2115 	bool first = true;
2116 	int ret;
2117 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2118 
2119 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2120 
2121 	folded_sign = hist_entry__folded(he);
2122 	printed += fprintf(fp, "%c", folded_sign);
2123 
2124 	/* the first hpp_list_node is for overhead columns */
2125 	fmt_node = list_first_entry(&he->hists->hpp_formats,
2126 				    struct perf_hpp_list_node, list);
2127 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2128 		if (!first) {
2129 			ret = scnprintf(hpp.buf, hpp.size, "  ");
2130 			advance_hpp(&hpp, ret);
2131 		} else
2132 			first = false;
2133 
2134 		ret = fmt->entry(fmt, &hpp, he);
2135 		advance_hpp(&hpp, ret);
2136 	}
2137 
2138 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2139 	advance_hpp(&hpp, ret);
2140 
2141 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2142 		ret = scnprintf(hpp.buf, hpp.size, "  ");
2143 		advance_hpp(&hpp, ret);
2144 
2145 		ret = fmt->entry(fmt, &hpp, he);
2146 		advance_hpp(&hpp, ret);
2147 	}
2148 
2149 	strim(s);
2150 	printed += fprintf(fp, "%s\n", s);
2151 
2152 	if (he->leaf && folded_sign == '-') {
2153 		printed += hist_browser__fprintf_callchain(browser, he, fp,
2154 							   he->depth + 1);
2155 	}
2156 
2157 	return printed;
2158 }
2159 
2160 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2161 {
2162 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2163 						   browser->min_pcnt);
2164 	int printed = 0;
2165 
2166 	while (nd) {
2167 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2168 
2169 		if (symbol_conf.report_hierarchy) {
2170 			printed += hist_browser__fprintf_hierarchy_entry(browser,
2171 									 h, fp,
2172 									 h->depth);
2173 		} else {
2174 			printed += hist_browser__fprintf_entry(browser, h, fp);
2175 		}
2176 
2177 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2178 					   browser->min_pcnt);
2179 	}
2180 
2181 	return printed;
2182 }
2183 
2184 static int hist_browser__dump(struct hist_browser *browser)
2185 {
2186 	char filename[64];
2187 	FILE *fp;
2188 
2189 	while (1) {
2190 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2191 		if (access(filename, F_OK))
2192 			break;
2193 		/*
2194  		 * XXX: Just an arbitrary lazy upper limit
2195  		 */
2196 		if (++browser->print_seq == 8192) {
2197 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2198 			return -1;
2199 		}
2200 	}
2201 
2202 	fp = fopen(filename, "w");
2203 	if (fp == NULL) {
2204 		char bf[64];
2205 		const char *err = str_error_r(errno, bf, sizeof(bf));
2206 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2207 		return -1;
2208 	}
2209 
2210 	++browser->print_seq;
2211 	hist_browser__fprintf(browser, fp);
2212 	fclose(fp);
2213 	ui_helpline__fpush("%s written!", filename);
2214 
2215 	return 0;
2216 }
2217 
2218 void hist_browser__init(struct hist_browser *browser,
2219 			struct hists *hists)
2220 {
2221 	struct perf_hpp_fmt *fmt;
2222 
2223 	browser->hists			= hists;
2224 	browser->b.refresh		= hist_browser__refresh;
2225 	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
2226 	browser->b.seek			= ui_browser__hists_seek;
2227 	browser->b.use_navkeypressed	= true;
2228 	browser->show_headers		= symbol_conf.show_hist_headers;
2229 	hist_browser__set_title_space(browser);
2230 
2231 	if (symbol_conf.report_hierarchy) {
2232 		struct perf_hpp_list_node *fmt_node;
2233 
2234 		/* count overhead columns (in the first node) */
2235 		fmt_node = list_first_entry(&hists->hpp_formats,
2236 					    struct perf_hpp_list_node, list);
2237 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2238 			++browser->b.columns;
2239 
2240 		/* add a single column for whole hierarchy sort keys*/
2241 		++browser->b.columns;
2242 	} else {
2243 		hists__for_each_format(hists, fmt)
2244 			++browser->b.columns;
2245 	}
2246 
2247 	hists__reset_column_width(hists);
2248 }
2249 
2250 struct hist_browser *hist_browser__new(struct hists *hists)
2251 {
2252 	struct hist_browser *browser = zalloc(sizeof(*browser));
2253 
2254 	if (browser)
2255 		hist_browser__init(browser, hists);
2256 
2257 	return browser;
2258 }
2259 
2260 static struct hist_browser *
2261 perf_evsel_browser__new(struct evsel *evsel,
2262 			struct hist_browser_timer *hbt,
2263 			struct perf_env *env,
2264 			struct annotation_options *annotation_opts)
2265 {
2266 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2267 
2268 	if (browser) {
2269 		browser->hbt   = hbt;
2270 		browser->env   = env;
2271 		browser->title = hists_browser__scnprintf_title;
2272 		browser->annotation_opts = annotation_opts;
2273 	}
2274 	return browser;
2275 }
2276 
2277 void hist_browser__delete(struct hist_browser *browser)
2278 {
2279 	free(browser);
2280 }
2281 
2282 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2283 {
2284 	return browser->he_selection;
2285 }
2286 
2287 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2288 {
2289 	return browser->he_selection->thread;
2290 }
2291 
2292 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2293 {
2294 	return browser->he_selection ? browser->he_selection->res_samples : NULL;
2295 }
2296 
2297 /* Check whether the browser is for 'top' or 'report' */
2298 static inline bool is_report_browser(void *timer)
2299 {
2300 	return timer == NULL;
2301 }
2302 
2303 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2304 {
2305 	struct hist_browser_timer *hbt = browser->hbt;
2306 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2307 
2308 	if (!is_report_browser(hbt)) {
2309 		struct perf_top *top = hbt->arg;
2310 
2311 		printed += scnprintf(bf + printed, size - printed,
2312 				     " lost: %" PRIu64 "/%" PRIu64,
2313 				     top->lost, top->lost_total);
2314 
2315 		printed += scnprintf(bf + printed, size - printed,
2316 				     " drop: %" PRIu64 "/%" PRIu64,
2317 				     top->drop, top->drop_total);
2318 
2319 		if (top->zero)
2320 			printed += scnprintf(bf + printed, size - printed, " [z]");
2321 
2322 		perf_top__reset_sample_counters(top);
2323 	}
2324 
2325 
2326 	return printed;
2327 }
2328 
2329 static inline void free_popup_options(char **options, int n)
2330 {
2331 	int i;
2332 
2333 	for (i = 0; i < n; ++i)
2334 		zfree(&options[i]);
2335 }
2336 
2337 /*
2338  * Only runtime switching of perf data file will make "input_name" point
2339  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2340  * whether we need to call free() for current "input_name" during the switch.
2341  */
2342 static bool is_input_name_malloced = false;
2343 
2344 static int switch_data_file(void)
2345 {
2346 	char *pwd, *options[32], *abs_path[32], *tmp;
2347 	DIR *pwd_dir;
2348 	int nr_options = 0, choice = -1, ret = -1;
2349 	struct dirent *dent;
2350 
2351 	pwd = getenv("PWD");
2352 	if (!pwd)
2353 		return ret;
2354 
2355 	pwd_dir = opendir(pwd);
2356 	if (!pwd_dir)
2357 		return ret;
2358 
2359 	memset(options, 0, sizeof(options));
2360 	memset(abs_path, 0, sizeof(abs_path));
2361 
2362 	while ((dent = readdir(pwd_dir))) {
2363 		char path[PATH_MAX];
2364 		u64 magic;
2365 		char *name = dent->d_name;
2366 		FILE *file;
2367 
2368 		if (!(dent->d_type == DT_REG))
2369 			continue;
2370 
2371 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2372 
2373 		file = fopen(path, "r");
2374 		if (!file)
2375 			continue;
2376 
2377 		if (fread(&magic, 1, 8, file) < 8)
2378 			goto close_file_and_continue;
2379 
2380 		if (is_perf_magic(magic)) {
2381 			options[nr_options] = strdup(name);
2382 			if (!options[nr_options])
2383 				goto close_file_and_continue;
2384 
2385 			abs_path[nr_options] = strdup(path);
2386 			if (!abs_path[nr_options]) {
2387 				zfree(&options[nr_options]);
2388 				ui__warning("Can't search all data files due to memory shortage.\n");
2389 				fclose(file);
2390 				break;
2391 			}
2392 
2393 			nr_options++;
2394 		}
2395 
2396 close_file_and_continue:
2397 		fclose(file);
2398 		if (nr_options >= 32) {
2399 			ui__warning("Too many perf data files in PWD!\n"
2400 				    "Only the first 32 files will be listed.\n");
2401 			break;
2402 		}
2403 	}
2404 	closedir(pwd_dir);
2405 
2406 	if (nr_options) {
2407 		choice = ui__popup_menu(nr_options, options, NULL);
2408 		if (choice < nr_options && choice >= 0) {
2409 			tmp = strdup(abs_path[choice]);
2410 			if (tmp) {
2411 				if (is_input_name_malloced)
2412 					free((void *)input_name);
2413 				input_name = tmp;
2414 				is_input_name_malloced = true;
2415 				ret = 0;
2416 			} else
2417 				ui__warning("Data switch failed due to memory shortage!\n");
2418 		}
2419 	}
2420 
2421 	free_popup_options(options, nr_options);
2422 	free_popup_options(abs_path, nr_options);
2423 	return ret;
2424 }
2425 
2426 struct popup_action {
2427 	unsigned long		time;
2428 	struct thread 		*thread;
2429 	struct map_symbol 	ms;
2430 	int			socket;
2431 	struct evsel	*evsel;
2432 	enum rstype		rstype;
2433 
2434 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2435 };
2436 
2437 static int
2438 do_annotate(struct hist_browser *browser, struct popup_action *act)
2439 {
2440 	struct evsel *evsel;
2441 	struct annotation *notes;
2442 	struct hist_entry *he;
2443 	int err;
2444 
2445 	if (!browser->annotation_opts->objdump_path &&
2446 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2447 		return 0;
2448 
2449 	notes = symbol__annotation(act->ms.sym);
2450 	if (!notes->src)
2451 		return 0;
2452 
2453 	if (browser->block_evsel)
2454 		evsel = browser->block_evsel;
2455 	else
2456 		evsel = hists_to_evsel(browser->hists);
2457 
2458 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2459 				       browser->annotation_opts);
2460 	he = hist_browser__selected_entry(browser);
2461 	/*
2462 	 * offer option to annotate the other branch source or target
2463 	 * (if they exists) when returning from annotate
2464 	 */
2465 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2466 		return 1;
2467 
2468 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2469 	if (err)
2470 		ui_browser__handle_resize(&browser->b);
2471 	return 0;
2472 }
2473 
2474 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2475 {
2476 	struct annotated_source *src;
2477 	struct symbol *sym;
2478 	char name[64];
2479 
2480 	snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2481 
2482 	sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2483 	if (sym) {
2484 		src = symbol__hists(sym, 1);
2485 		if (!src) {
2486 			symbol__delete(sym);
2487 			return NULL;
2488 		}
2489 
2490 		dso__insert_symbol(map__dso(map), sym);
2491 	}
2492 
2493 	return sym;
2494 }
2495 
2496 static int
2497 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2498 		 struct popup_action *act, char **optstr,
2499 		 struct map_symbol *ms,
2500 		 u64 addr)
2501 {
2502 	struct dso *dso;
2503 
2504 	if (!ms->map || (dso = map__dso(ms->map)) == NULL || dso->annotate_warned)
2505 		return 0;
2506 
2507 	if (!ms->sym)
2508 		ms->sym = symbol__new_unresolved(addr, ms->map);
2509 
2510 	if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2511 		return 0;
2512 
2513 	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2514 		return 0;
2515 
2516 	act->ms = *ms;
2517 	act->fn = do_annotate;
2518 	return 1;
2519 }
2520 
2521 static int
2522 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2523 {
2524 	struct thread *thread = act->thread;
2525 
2526 	if ((!hists__has(browser->hists, thread) &&
2527 	     !hists__has(browser->hists, comm)) || thread == NULL)
2528 		return 0;
2529 
2530 	if (browser->hists->thread_filter) {
2531 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2532 		perf_hpp__set_elide(HISTC_THREAD, false);
2533 		thread__zput(browser->hists->thread_filter);
2534 		ui_helpline__pop();
2535 	} else {
2536 		const char *comm_set_str =
2537 			thread__comm_set(thread) ? thread__comm_str(thread) : "";
2538 
2539 		if (hists__has(browser->hists, thread)) {
2540 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2541 					   comm_set_str, thread__tid(thread));
2542 		} else {
2543 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2544 					   comm_set_str);
2545 		}
2546 
2547 		browser->hists->thread_filter = thread__get(thread);
2548 		perf_hpp__set_elide(HISTC_THREAD, false);
2549 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2550 	}
2551 
2552 	hists__filter_by_thread(browser->hists);
2553 	hist_browser__reset(browser);
2554 	return 0;
2555 }
2556 
2557 static int
2558 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2559 	       char **optstr, struct thread *thread)
2560 {
2561 	int ret;
2562 	const char *comm_set_str, *in_out;
2563 
2564 	if ((!hists__has(browser->hists, thread) &&
2565 	     !hists__has(browser->hists, comm)) || thread == NULL)
2566 		return 0;
2567 
2568 	in_out = browser->hists->thread_filter ? "out of" : "into";
2569 	comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : "";
2570 	if (hists__has(browser->hists, thread)) {
2571 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2572 			       in_out, comm_set_str, thread__tid(thread));
2573 	} else {
2574 		ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str);
2575 	}
2576 	if (ret < 0)
2577 		return 0;
2578 
2579 	act->thread = thread;
2580 	act->fn = do_zoom_thread;
2581 	return 1;
2582 }
2583 
2584 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2585 {
2586 	if (!hists__has(browser->hists, dso) || map == NULL)
2587 		return 0;
2588 
2589 	if (browser->hists->dso_filter) {
2590 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2591 		perf_hpp__set_elide(HISTC_DSO, false);
2592 		browser->hists->dso_filter = NULL;
2593 		ui_helpline__pop();
2594 	} else {
2595 		struct dso *dso = map__dso(map);
2596 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2597 				   __map__is_kernel(map) ? "the Kernel" : dso->short_name);
2598 		browser->hists->dso_filter = dso;
2599 		perf_hpp__set_elide(HISTC_DSO, true);
2600 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2601 	}
2602 
2603 	hists__filter_by_dso(browser->hists);
2604 	hist_browser__reset(browser);
2605 	return 0;
2606 }
2607 
2608 static int
2609 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2610 {
2611 	return hists_browser__zoom_map(browser, act->ms.map);
2612 }
2613 
2614 static int
2615 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2616 	    char **optstr, struct map *map)
2617 {
2618 	if (!hists__has(browser->hists, dso) || map == NULL)
2619 		return 0;
2620 
2621 	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2622 		     browser->hists->dso_filter ? "out of" : "into",
2623 		     __map__is_kernel(map) ? "the Kernel" : map__dso(map)->short_name) < 0)
2624 		return 0;
2625 
2626 	act->ms.map = map;
2627 	act->fn = do_zoom_dso;
2628 	return 1;
2629 }
2630 
2631 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2632 {
2633 	hist_browser__toggle_fold(browser);
2634 	return 0;
2635 }
2636 
2637 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2638 {
2639 	char sym_name[512];
2640 
2641         if (!hist_browser__selection_has_children(browser))
2642                 return 0;
2643 
2644 	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2645 		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2646 		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2647 		return 0;
2648 
2649 	act->fn = do_toggle_callchain;
2650 	return 1;
2651 }
2652 
2653 static int
2654 do_browse_map(struct hist_browser *browser __maybe_unused,
2655 	      struct popup_action *act)
2656 {
2657 	map__browse(act->ms.map);
2658 	return 0;
2659 }
2660 
2661 static int
2662 add_map_opt(struct hist_browser *browser,
2663 	    struct popup_action *act, char **optstr, struct map *map)
2664 {
2665 	if (!hists__has(browser->hists, dso) || map == NULL)
2666 		return 0;
2667 
2668 	if (asprintf(optstr, "Browse map details") < 0)
2669 		return 0;
2670 
2671 	act->ms.map = map;
2672 	act->fn = do_browse_map;
2673 	return 1;
2674 }
2675 
2676 static int
2677 do_run_script(struct hist_browser *browser __maybe_unused,
2678 	      struct popup_action *act)
2679 {
2680 	char *script_opt;
2681 	int len;
2682 	int n = 0;
2683 
2684 	len = 100;
2685 	if (act->thread)
2686 		len += strlen(thread__comm_str(act->thread));
2687 	else if (act->ms.sym)
2688 		len += strlen(act->ms.sym->name);
2689 	script_opt = malloc(len);
2690 	if (!script_opt)
2691 		return -1;
2692 
2693 	script_opt[0] = 0;
2694 	if (act->thread) {
2695 		n = scnprintf(script_opt, len, " -c %s ",
2696 			  thread__comm_str(act->thread));
2697 	} else if (act->ms.sym) {
2698 		n = scnprintf(script_opt, len, " -S %s ",
2699 			  act->ms.sym->name);
2700 	}
2701 
2702 	if (act->time) {
2703 		char start[32], end[32];
2704 		unsigned long starttime = act->time;
2705 		unsigned long endtime = act->time + symbol_conf.time_quantum;
2706 
2707 		if (starttime == endtime) { /* Display 1ms as fallback */
2708 			starttime -= 1*NSEC_PER_MSEC;
2709 			endtime += 1*NSEC_PER_MSEC;
2710 		}
2711 		timestamp__scnprintf_usec(starttime, start, sizeof start);
2712 		timestamp__scnprintf_usec(endtime, end, sizeof end);
2713 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2714 	}
2715 
2716 	script_browse(script_opt, act->evsel);
2717 	free(script_opt);
2718 	return 0;
2719 }
2720 
2721 static int
2722 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2723 		     struct popup_action *act)
2724 {
2725 	struct hist_entry *he;
2726 
2727 	he = hist_browser__selected_entry(browser);
2728 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2729 	return 0;
2730 }
2731 
2732 static int
2733 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2734 	       struct popup_action *act, char **optstr,
2735 	       struct thread *thread, struct symbol *sym,
2736 	       struct evsel *evsel, const char *tstr)
2737 {
2738 
2739 	if (thread) {
2740 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2741 			     thread__comm_str(thread), tstr) < 0)
2742 			return 0;
2743 	} else if (sym) {
2744 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2745 			     sym->name, tstr) < 0)
2746 			return 0;
2747 	} else {
2748 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2749 			return 0;
2750 	}
2751 
2752 	act->thread = thread;
2753 	act->ms.sym = sym;
2754 	act->evsel = evsel;
2755 	act->fn = do_run_script;
2756 	return 1;
2757 }
2758 
2759 static int
2760 add_script_opt(struct hist_browser *browser,
2761 	       struct popup_action *act, char **optstr,
2762 	       struct thread *thread, struct symbol *sym,
2763 	       struct evsel *evsel)
2764 {
2765 	int n, j;
2766 	struct hist_entry *he;
2767 
2768 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2769 
2770 	he = hist_browser__selected_entry(browser);
2771 	if (sort_order && strstr(sort_order, "time")) {
2772 		char tstr[128];
2773 
2774 		optstr++;
2775 		act++;
2776 		j = sprintf(tstr, " in ");
2777 		j += timestamp__scnprintf_usec(he->time, tstr + j,
2778 					       sizeof tstr - j);
2779 		j += sprintf(tstr + j, "-");
2780 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2781 				          tstr + j, sizeof tstr - j);
2782 		n += add_script_opt_2(browser, act, optstr, thread, sym,
2783 					  evsel, tstr);
2784 		act->time = he->time;
2785 	}
2786 	return n;
2787 }
2788 
2789 static int
2790 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2791 		   struct popup_action *act, char **optstr,
2792 		   struct res_sample *res_sample,
2793 		   struct evsel *evsel,
2794 		   enum rstype type)
2795 {
2796 	if (!res_sample)
2797 		return 0;
2798 
2799 	if (asprintf(optstr, "Show context for individual samples %s",
2800 		type == A_ASM ? "with assembler" :
2801 		type == A_SOURCE ? "with source" : "") < 0)
2802 		return 0;
2803 
2804 	act->fn = do_res_sample_script;
2805 	act->evsel = evsel;
2806 	act->rstype = type;
2807 	return 1;
2808 }
2809 
2810 static int
2811 do_switch_data(struct hist_browser *browser __maybe_unused,
2812 	       struct popup_action *act __maybe_unused)
2813 {
2814 	if (switch_data_file()) {
2815 		ui__warning("Won't switch the data files due to\n"
2816 			    "no valid data file get selected!\n");
2817 		return 0;
2818 	}
2819 
2820 	return K_SWITCH_INPUT_DATA;
2821 }
2822 
2823 static int
2824 add_switch_opt(struct hist_browser *browser,
2825 	       struct popup_action *act, char **optstr)
2826 {
2827 	if (!is_report_browser(browser->hbt))
2828 		return 0;
2829 
2830 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2831 		return 0;
2832 
2833 	act->fn = do_switch_data;
2834 	return 1;
2835 }
2836 
2837 static int
2838 do_exit_browser(struct hist_browser *browser __maybe_unused,
2839 		struct popup_action *act __maybe_unused)
2840 {
2841 	return 0;
2842 }
2843 
2844 static int
2845 add_exit_opt(struct hist_browser *browser __maybe_unused,
2846 	     struct popup_action *act, char **optstr)
2847 {
2848 	if (asprintf(optstr, "Exit") < 0)
2849 		return 0;
2850 
2851 	act->fn = do_exit_browser;
2852 	return 1;
2853 }
2854 
2855 static int
2856 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2857 {
2858 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2859 		return 0;
2860 
2861 	if (browser->hists->socket_filter > -1) {
2862 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2863 		browser->hists->socket_filter = -1;
2864 		perf_hpp__set_elide(HISTC_SOCKET, false);
2865 	} else {
2866 		browser->hists->socket_filter = act->socket;
2867 		perf_hpp__set_elide(HISTC_SOCKET, true);
2868 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2869 	}
2870 
2871 	hists__filter_by_socket(browser->hists);
2872 	hist_browser__reset(browser);
2873 	return 0;
2874 }
2875 
2876 static int
2877 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2878 	       char **optstr, int socket_id)
2879 {
2880 	if (!hists__has(browser->hists, socket) || socket_id < 0)
2881 		return 0;
2882 
2883 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2884 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2885 		     socket_id) < 0)
2886 		return 0;
2887 
2888 	act->socket = socket_id;
2889 	act->fn = do_zoom_socket;
2890 	return 1;
2891 }
2892 
2893 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2894 {
2895 	u64 nr_entries = 0;
2896 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2897 
2898 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2899 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2900 		return;
2901 	}
2902 
2903 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2904 		nr_entries++;
2905 		nd = rb_hierarchy_next(nd);
2906 	}
2907 
2908 	hb->nr_non_filtered_entries = nr_entries;
2909 	hb->nr_hierarchy_entries = nr_entries;
2910 }
2911 
2912 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2913 					       double percent)
2914 {
2915 	struct hist_entry *he;
2916 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2917 	u64 total = hists__total_period(hb->hists);
2918 	u64 min_callchain_hits = total * (percent / 100);
2919 
2920 	hb->min_pcnt = callchain_param.min_percent = percent;
2921 
2922 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2923 		he = rb_entry(nd, struct hist_entry, rb_node);
2924 
2925 		if (he->has_no_entry) {
2926 			he->has_no_entry = false;
2927 			he->nr_rows = 0;
2928 		}
2929 
2930 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2931 			goto next;
2932 
2933 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2934 			total = he->stat.period;
2935 
2936 			if (symbol_conf.cumulate_callchain)
2937 				total = he->stat_acc->period;
2938 
2939 			min_callchain_hits = total * (percent / 100);
2940 		}
2941 
2942 		callchain_param.sort(&he->sorted_chain, he->callchain,
2943 				     min_callchain_hits, &callchain_param);
2944 
2945 next:
2946 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2947 
2948 		/* force to re-evaluate folding state of callchains */
2949 		he->init_have_children = false;
2950 		hist_entry__set_folding(he, hb, false);
2951 	}
2952 }
2953 
2954 static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
2955 			       bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
2956 			       struct perf_env *env, bool warn_lost_event,
2957 			       struct annotation_options *annotation_opts)
2958 {
2959 	struct hists *hists = evsel__hists(evsel);
2960 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2961 	struct branch_info *bi = NULL;
2962 #define MAX_OPTIONS  16
2963 	char *options[MAX_OPTIONS];
2964 	struct popup_action actions[MAX_OPTIONS];
2965 	int nr_options = 0;
2966 	int key = -1;
2967 	char buf[128];
2968 	int delay_secs = hbt ? hbt->refresh : 0;
2969 
2970 #define HIST_BROWSER_HELP_COMMON					\
2971 	"h/?/F1        Show this window\n"				\
2972 	"UP/DOWN/PGUP\n"						\
2973 	"PGDN/SPACE    Navigate\n"					\
2974 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2975 	"For multiple event sessions:\n\n"				\
2976 	"TAB/UNTAB     Switch events\n\n"				\
2977 	"For symbolic views (--sort has sym):\n\n"			\
2978 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2979 	"ESC           Zoom out\n"					\
2980 	"+             Expand/Collapse one callchain level\n"		\
2981 	"a             Annotate current symbol\n"			\
2982 	"C             Collapse all callchains\n"			\
2983 	"d             Zoom into current DSO\n"				\
2984 	"e             Expand/Collapse main entry callchains\n"	\
2985 	"E             Expand all callchains\n"				\
2986 	"F             Toggle percentage of filtered entries\n"		\
2987 	"H             Display column headers\n"			\
2988 	"k             Zoom into the kernel map\n"			\
2989 	"L             Change percent limit\n"				\
2990 	"m             Display context menu\n"				\
2991 	"S             Zoom into current Processor Socket\n"		\
2992 
2993 	/* help messages are sorted by lexical order of the hotkey */
2994 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
2995 	"i             Show header information\n"
2996 	"P             Print histograms to perf.hist.N\n"
2997 	"r             Run available scripts\n"
2998 	"s             Switch to another data file in PWD\n"
2999 	"t             Zoom into current Thread\n"
3000 	"V             Verbose (DSO names in callchains, etc)\n"
3001 	"/             Filter symbol by name\n"
3002 	"0-9           Sort by event n in group";
3003 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
3004 	"P             Print histograms to perf.hist.N\n"
3005 	"t             Zoom into current Thread\n"
3006 	"V             Verbose (DSO names in callchains, etc)\n"
3007 	"z             Toggle zeroing of samples\n"
3008 	"f             Enable/Disable events\n"
3009 	"/             Filter symbol by name";
3010 
3011 	if (browser == NULL)
3012 		return -1;
3013 
3014 	/* reset abort key so that it can get Ctrl-C as a key */
3015 	SLang_reset_tty();
3016 	SLang_init_tty(0, 0, 0);
3017 
3018 	if (min_pcnt)
3019 		browser->min_pcnt = min_pcnt;
3020 	hist_browser__update_nr_entries(browser);
3021 
3022 	browser->pstack = pstack__new(3);
3023 	if (browser->pstack == NULL)
3024 		goto out;
3025 
3026 	ui_helpline__push(helpline);
3027 
3028 	memset(options, 0, sizeof(options));
3029 	memset(actions, 0, sizeof(actions));
3030 
3031 	if (symbol_conf.col_width_list_str)
3032 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3033 
3034 	if (!is_report_browser(hbt))
3035 		browser->b.no_samples_msg = "Collecting samples...";
3036 
3037 	while (1) {
3038 		struct thread *thread = NULL;
3039 		struct map *map = NULL;
3040 		int choice;
3041 		int socked_id = -1;
3042 
3043 		key = 0; // reset key
3044 do_hotkey:		 // key came straight from options ui__popup_menu()
3045 		choice = nr_options = 0;
3046 		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3047 
3048 		if (browser->he_selection != NULL) {
3049 			thread = hist_browser__selected_thread(browser);
3050 			map = browser->selection->map;
3051 			socked_id = browser->he_selection->socket;
3052 		}
3053 		switch (key) {
3054 		case K_TAB:
3055 		case K_UNTAB:
3056 			if (nr_events == 1)
3057 				continue;
3058 			/*
3059 			 * Exit the browser, let hists__browser_tree
3060 			 * go to the next or previous
3061 			 */
3062 			goto out_free_stack;
3063 		case '0' ... '9':
3064 			if (!symbol_conf.event_group ||
3065 			    evsel->core.nr_members < 2) {
3066 				snprintf(buf, sizeof(buf),
3067 					 "Sort by index only available with group events!");
3068 				helpline = buf;
3069 				continue;
3070 			}
3071 
3072 			if (key - '0' == symbol_conf.group_sort_idx)
3073 				continue;
3074 
3075 			symbol_conf.group_sort_idx = key - '0';
3076 
3077 			if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3078 				snprintf(buf, sizeof(buf),
3079 					 "Max event group index to sort is %d (index from 0 to %d)",
3080 					 evsel->core.nr_members - 1,
3081 					 evsel->core.nr_members - 1);
3082 				helpline = buf;
3083 				continue;
3084 			}
3085 
3086 			key = K_RELOAD;
3087 			goto out_free_stack;
3088 		case 'a':
3089 			if (!hists__has(hists, sym)) {
3090 				ui_browser__warning(&browser->b, delay_secs * 2,
3091 			"Annotation is only available for symbolic views, "
3092 			"include \"sym*\" in --sort to use it.");
3093 				continue;
3094 			}
3095 
3096 			if (!browser->selection ||
3097 			    !browser->selection->map ||
3098 			    !map__dso(browser->selection->map) ||
3099 			    map__dso(browser->selection->map)->annotate_warned) {
3100 				continue;
3101 			}
3102 
3103 			if (!browser->selection->sym) {
3104 				if (!browser->he_selection)
3105 					continue;
3106 
3107 				if (sort__mode == SORT_MODE__BRANCH) {
3108 					bi = browser->he_selection->branch_info;
3109 					if (!bi || !bi->to.ms.map)
3110 						continue;
3111 
3112 					actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3113 					actions->ms.map = bi->to.ms.map;
3114 				} else {
3115 					actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3116 										 browser->selection->map);
3117 					actions->ms.map = browser->selection->map;
3118 				}
3119 
3120 				if (!actions->ms.sym)
3121 					continue;
3122 			} else {
3123 				if (symbol__annotation(browser->selection->sym)->src == NULL) {
3124 					ui_browser__warning(&browser->b, delay_secs * 2,
3125 						"No samples for the \"%s\" symbol.\n\n"
3126 						"Probably appeared just in a callchain",
3127 						browser->selection->sym->name);
3128 					continue;
3129 				}
3130 
3131 				actions->ms.map = browser->selection->map;
3132 				actions->ms.sym = browser->selection->sym;
3133 			}
3134 
3135 			do_annotate(browser, actions);
3136 			continue;
3137 		case 'P':
3138 			hist_browser__dump(browser);
3139 			continue;
3140 		case 'd':
3141 			actions->ms.map = map;
3142 			do_zoom_dso(browser, actions);
3143 			continue;
3144 		case 'k':
3145 			if (browser->selection != NULL)
3146 				hists_browser__zoom_map(browser,
3147 					      maps__machine(browser->selection->maps)->vmlinux_map);
3148 			continue;
3149 		case 'V':
3150 			verbose = (verbose + 1) % 4;
3151 			browser->show_dso = verbose > 0;
3152 			ui_helpline__fpush("Verbosity level set to %d\n",
3153 					   verbose);
3154 			continue;
3155 		case 't':
3156 			actions->thread = thread;
3157 			do_zoom_thread(browser, actions);
3158 			continue;
3159 		case 'S':
3160 			actions->socket = socked_id;
3161 			do_zoom_socket(browser, actions);
3162 			continue;
3163 		case '/':
3164 			if (ui_browser__input_window("Symbol to show",
3165 					"Please enter the name of symbol you want to see.\n"
3166 					"To remove the filter later, press / + ENTER.",
3167 					buf, "ENTER: OK, ESC: Cancel",
3168 					delay_secs * 2) == K_ENTER) {
3169 				hists->symbol_filter_str = *buf ? buf : NULL;
3170 				hists__filter_by_symbol(hists);
3171 				hist_browser__reset(browser);
3172 			}
3173 			continue;
3174 		case 'r':
3175 			if (is_report_browser(hbt)) {
3176 				actions->thread = NULL;
3177 				actions->ms.sym = NULL;
3178 				do_run_script(browser, actions);
3179 			}
3180 			continue;
3181 		case 's':
3182 			if (is_report_browser(hbt)) {
3183 				key = do_switch_data(browser, actions);
3184 				if (key == K_SWITCH_INPUT_DATA)
3185 					goto out_free_stack;
3186 			}
3187 			continue;
3188 		case 'i':
3189 			/* env->arch is NULL for live-mode (i.e. perf top) */
3190 			if (env->arch)
3191 				tui__header_window(env);
3192 			continue;
3193 		case 'F':
3194 			symbol_conf.filter_relative ^= 1;
3195 			continue;
3196 		case 'z':
3197 			if (!is_report_browser(hbt)) {
3198 				struct perf_top *top = hbt->arg;
3199 
3200 				top->zero = !top->zero;
3201 			}
3202 			continue;
3203 		case 'L':
3204 			if (ui_browser__input_window("Percent Limit",
3205 					"Please enter the value you want to hide entries under that percent.",
3206 					buf, "ENTER: OK, ESC: Cancel",
3207 					delay_secs * 2) == K_ENTER) {
3208 				char *end;
3209 				double new_percent = strtod(buf, &end);
3210 
3211 				if (new_percent < 0 || new_percent > 100) {
3212 					ui_browser__warning(&browser->b, delay_secs * 2,
3213 						"Invalid percent: %.2f", new_percent);
3214 					continue;
3215 				}
3216 
3217 				hist_browser__update_percent_limit(browser, new_percent);
3218 				hist_browser__reset(browser);
3219 			}
3220 			continue;
3221 		case K_F1:
3222 		case 'h':
3223 		case '?':
3224 			ui_browser__help_window(&browser->b,
3225 				is_report_browser(hbt) ? report_help : top_help);
3226 			continue;
3227 		case K_ENTER:
3228 		case K_RIGHT:
3229 		case 'm':
3230 			/* menu */
3231 			break;
3232 		case K_ESC:
3233 		case K_LEFT: {
3234 			const void *top;
3235 
3236 			if (pstack__empty(browser->pstack)) {
3237 				/*
3238 				 * Go back to the perf_evsel_menu__run or other user
3239 				 */
3240 				if (left_exits)
3241 					goto out_free_stack;
3242 
3243 				if (key == K_ESC &&
3244 				    ui_browser__dialog_yesno(&browser->b,
3245 							     "Do you really want to exit?"))
3246 					goto out_free_stack;
3247 
3248 				continue;
3249 			}
3250 			actions->ms.map = map;
3251 			top = pstack__peek(browser->pstack);
3252 			if (top == &browser->hists->dso_filter) {
3253 				/*
3254 				 * No need to set actions->dso here since
3255 				 * it's just to remove the current filter.
3256 				 * Ditto for thread below.
3257 				 */
3258 				do_zoom_dso(browser, actions);
3259 			} else if (top == &browser->hists->thread_filter) {
3260 				do_zoom_thread(browser, actions);
3261 			} else if (top == &browser->hists->socket_filter) {
3262 				do_zoom_socket(browser, actions);
3263 			}
3264 			continue;
3265 		}
3266 		case 'q':
3267 		case CTRL('c'):
3268 			goto out_free_stack;
3269 		case 'f':
3270 			if (!is_report_browser(hbt)) {
3271 				struct perf_top *top = hbt->arg;
3272 
3273 				evlist__toggle_enable(top->evlist);
3274 				/*
3275 				 * No need to refresh, resort/decay histogram
3276 				 * entries if we are not collecting samples:
3277 				 */
3278 				if (top->evlist->enabled) {
3279 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3280 					hbt->refresh = delay_secs;
3281 				} else {
3282 					helpline = "Press 'f' again to re-enable the events";
3283 					hbt->refresh = 0;
3284 				}
3285 				continue;
3286 			}
3287 			/* Fall thru */
3288 		default:
3289 			helpline = "Press '?' for help on key bindings";
3290 			continue;
3291 		}
3292 
3293 		if (!hists__has(hists, sym) || browser->selection == NULL)
3294 			goto skip_annotation;
3295 
3296 		if (sort__mode == SORT_MODE__BRANCH) {
3297 
3298 			if (browser->he_selection)
3299 				bi = browser->he_selection->branch_info;
3300 
3301 			if (bi == NULL)
3302 				goto skip_annotation;
3303 
3304 			nr_options += add_annotate_opt(browser,
3305 						       &actions[nr_options],
3306 						       &options[nr_options],
3307 						       &bi->from.ms,
3308 						       bi->from.al_addr);
3309 			if (bi->to.ms.sym != bi->from.ms.sym)
3310 				nr_options += add_annotate_opt(browser,
3311 							&actions[nr_options],
3312 							&options[nr_options],
3313 							&bi->to.ms,
3314 							bi->to.al_addr);
3315 		} else {
3316 			nr_options += add_annotate_opt(browser,
3317 						       &actions[nr_options],
3318 						       &options[nr_options],
3319 						       browser->selection,
3320 						       browser->he_selection->ip);
3321 		}
3322 skip_annotation:
3323 		nr_options += add_thread_opt(browser, &actions[nr_options],
3324 					     &options[nr_options], thread);
3325 		nr_options += add_dso_opt(browser, &actions[nr_options],
3326 					  &options[nr_options], map);
3327 		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3328 		nr_options += add_map_opt(browser, &actions[nr_options],
3329 					  &options[nr_options],
3330 					  browser->selection ?
3331 						browser->selection->map : NULL);
3332 		nr_options += add_socket_opt(browser, &actions[nr_options],
3333 					     &options[nr_options],
3334 					     socked_id);
3335 		/* perf script support */
3336 		if (!is_report_browser(hbt))
3337 			goto skip_scripting;
3338 
3339 		if (browser->he_selection) {
3340 			if (hists__has(hists, thread) && thread) {
3341 				nr_options += add_script_opt(browser,
3342 							     &actions[nr_options],
3343 							     &options[nr_options],
3344 							     thread, NULL, evsel);
3345 			}
3346 			/*
3347 			 * Note that browser->selection != NULL
3348 			 * when browser->he_selection is not NULL,
3349 			 * so we don't need to check browser->selection
3350 			 * before fetching browser->selection->sym like what
3351 			 * we do before fetching browser->selection->map.
3352 			 *
3353 			 * See hist_browser__show_entry.
3354 			 */
3355 			if (hists__has(hists, sym) && browser->selection->sym) {
3356 				nr_options += add_script_opt(browser,
3357 							     &actions[nr_options],
3358 							     &options[nr_options],
3359 							     NULL, browser->selection->sym,
3360 							     evsel);
3361 			}
3362 		}
3363 		nr_options += add_script_opt(browser, &actions[nr_options],
3364 					     &options[nr_options], NULL, NULL, evsel);
3365 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3366 						 &options[nr_options],
3367 						 hist_browser__selected_res_sample(browser),
3368 						 evsel, A_NORMAL);
3369 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3370 						 &options[nr_options],
3371 						 hist_browser__selected_res_sample(browser),
3372 						 evsel, A_ASM);
3373 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3374 						 &options[nr_options],
3375 						 hist_browser__selected_res_sample(browser),
3376 						 evsel, A_SOURCE);
3377 		nr_options += add_switch_opt(browser, &actions[nr_options],
3378 					     &options[nr_options]);
3379 skip_scripting:
3380 		nr_options += add_exit_opt(browser, &actions[nr_options],
3381 					   &options[nr_options]);
3382 
3383 		do {
3384 			struct popup_action *act;
3385 
3386 			choice = ui__popup_menu(nr_options, options, &key);
3387 			if (choice == -1)
3388 				break;
3389 
3390 			if (choice == nr_options)
3391 				goto do_hotkey;
3392 
3393 			act = &actions[choice];
3394 			key = act->fn(browser, act);
3395 		} while (key == 1);
3396 
3397 		if (key == K_SWITCH_INPUT_DATA)
3398 			break;
3399 	}
3400 out_free_stack:
3401 	pstack__delete(browser->pstack);
3402 out:
3403 	hist_browser__delete(browser);
3404 	free_popup_options(options, MAX_OPTIONS);
3405 	return key;
3406 }
3407 
3408 struct evsel_menu {
3409 	struct ui_browser b;
3410 	struct evsel *selection;
3411 	struct annotation_options *annotation_opts;
3412 	bool lost_events, lost_events_warned;
3413 	float min_pcnt;
3414 	struct perf_env *env;
3415 };
3416 
3417 static void perf_evsel_menu__write(struct ui_browser *browser,
3418 				   void *entry, int row)
3419 {
3420 	struct evsel_menu *menu = container_of(browser,
3421 						    struct evsel_menu, b);
3422 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3423 	struct hists *hists = evsel__hists(evsel);
3424 	bool current_entry = ui_browser__is_current_entry(browser, row);
3425 	unsigned long nr_events = hists->stats.nr_samples;
3426 	const char *ev_name = evsel__name(evsel);
3427 	char bf[256], unit;
3428 	const char *warn = " ";
3429 	size_t printed;
3430 
3431 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3432 						       HE_COLORSET_NORMAL);
3433 
3434 	if (evsel__is_group_event(evsel)) {
3435 		struct evsel *pos;
3436 
3437 		ev_name = evsel__group_name(evsel);
3438 
3439 		for_each_group_member(pos, evsel) {
3440 			struct hists *pos_hists = evsel__hists(pos);
3441 			nr_events += pos_hists->stats.nr_samples;
3442 		}
3443 	}
3444 
3445 	nr_events = convert_unit(nr_events, &unit);
3446 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3447 			   unit, unit == ' ' ? "" : " ", ev_name);
3448 	ui_browser__printf(browser, "%s", bf);
3449 
3450 	nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3451 	if (nr_events != 0) {
3452 		menu->lost_events = true;
3453 		if (!current_entry)
3454 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3455 		nr_events = convert_unit(nr_events, &unit);
3456 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3457 				     nr_events, unit, unit == ' ' ? "" : " ");
3458 		warn = bf;
3459 	}
3460 
3461 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3462 
3463 	if (current_entry)
3464 		menu->selection = evsel;
3465 }
3466 
3467 static int perf_evsel_menu__run(struct evsel_menu *menu,
3468 				int nr_events, const char *help,
3469 				struct hist_browser_timer *hbt,
3470 				bool warn_lost_event)
3471 {
3472 	struct evlist *evlist = menu->b.priv;
3473 	struct evsel *pos;
3474 	const char *title = "Available samples";
3475 	int delay_secs = hbt ? hbt->refresh : 0;
3476 	int key;
3477 
3478 	if (ui_browser__show(&menu->b, title,
3479 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3480 		return -1;
3481 
3482 	while (1) {
3483 		key = ui_browser__run(&menu->b, delay_secs);
3484 
3485 		switch (key) {
3486 		case K_TIMER:
3487 			if (hbt)
3488 				hbt->timer(hbt->arg);
3489 
3490 			if (!menu->lost_events_warned &&
3491 			    menu->lost_events &&
3492 			    warn_lost_event) {
3493 				ui_browser__warn_lost_events(&menu->b);
3494 				menu->lost_events_warned = true;
3495 			}
3496 			continue;
3497 		case K_RIGHT:
3498 		case K_ENTER:
3499 			if (!menu->selection)
3500 				continue;
3501 			pos = menu->selection;
3502 browse_hists:
3503 			evlist__set_selected(evlist, pos);
3504 			/*
3505 			 * Give the calling tool a chance to populate the non
3506 			 * default evsel resorted hists tree.
3507 			 */
3508 			if (hbt)
3509 				hbt->timer(hbt->arg);
3510 			key = evsel__hists_browse(pos, nr_events, help, true, hbt,
3511 						  menu->min_pcnt, 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 __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3569 				      struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3570 				      bool warn_lost_event, struct annotation_options *annotation_opts)
3571 {
3572 	struct evsel *pos;
3573 	struct evsel_menu menu = {
3574 		.b = {
3575 			.entries    = &evlist->core.entries,
3576 			.refresh    = ui_browser__list_head_refresh,
3577 			.seek	    = ui_browser__list_head_seek,
3578 			.write	    = perf_evsel_menu__write,
3579 			.filter	    = filter_group_entries,
3580 			.nr_entries = nr_entries,
3581 			.priv	    = evlist,
3582 		},
3583 		.min_pcnt = min_pcnt,
3584 		.env = env,
3585 		.annotation_opts = annotation_opts,
3586 	};
3587 
3588 	ui_helpline__push("Press ESC to exit");
3589 
3590 	evlist__for_each_entry(evlist, pos) {
3591 		const char *ev_name = evsel__name(pos);
3592 		size_t line_len = strlen(ev_name) + 7;
3593 
3594 		if (menu.b.width < line_len)
3595 			menu.b.width = line_len;
3596 	}
3597 
3598 	return perf_evsel_menu__run(&menu, nr_entries, help,
3599 				    hbt, warn_lost_event);
3600 }
3601 
3602 static bool evlist__single_entry(struct evlist *evlist)
3603 {
3604 	int nr_entries = evlist->core.nr_entries;
3605 
3606 	if (nr_entries == 1)
3607 	       return true;
3608 
3609 	if (nr_entries == 2) {
3610 		struct evsel *last = evlist__last(evlist);
3611 
3612 		if (evsel__is_dummy_event(last))
3613 			return true;
3614 	}
3615 
3616 	return false;
3617 }
3618 
3619 int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3620 			     float min_pcnt, struct perf_env *env, bool warn_lost_event,
3621 			     struct annotation_options *annotation_opts)
3622 {
3623 	int nr_entries = evlist->core.nr_entries;
3624 
3625 	if (evlist__single_entry(evlist)) {
3626 single_entry: {
3627 		struct evsel *first = evlist__first(evlist);
3628 
3629 		return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
3630 					   env, warn_lost_event, annotation_opts);
3631 	}
3632 	}
3633 
3634 	if (symbol_conf.event_group) {
3635 		struct evsel *pos;
3636 
3637 		nr_entries = 0;
3638 		evlist__for_each_entry(evlist, pos) {
3639 			if (evsel__is_group_leader(pos))
3640 				nr_entries++;
3641 		}
3642 
3643 		if (nr_entries == 1)
3644 			goto single_entry;
3645 	}
3646 
3647 	return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3648 					  warn_lost_event, annotation_opts);
3649 }
3650 
3651 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3652 				      size_t size)
3653 {
3654 	struct hists *hists = evsel__hists(browser->block_evsel);
3655 	const char *evname = evsel__name(browser->block_evsel);
3656 	unsigned long nr_samples = hists->stats.nr_samples;
3657 	int ret;
3658 
3659 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3660 	if (evname)
3661 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3662 
3663 	return 0;
3664 }
3665 
3666 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3667 			   float min_percent, struct perf_env *env,
3668 			   struct annotation_options *annotation_opts)
3669 {
3670 	struct hists *hists = &bh->block_hists;
3671 	struct hist_browser *browser;
3672 	int key = -1;
3673 	struct popup_action action;
3674 	static const char help[] =
3675 	" q             Quit \n";
3676 
3677 	browser = hist_browser__new(hists);
3678 	if (!browser)
3679 		return -1;
3680 
3681 	browser->block_evsel = evsel;
3682 	browser->title = block_hists_browser__title;
3683 	browser->min_pcnt = min_percent;
3684 	browser->env = env;
3685 	browser->annotation_opts = annotation_opts;
3686 
3687 	/* reset abort key so that it can get Ctrl-C as a key */
3688 	SLang_reset_tty();
3689 	SLang_init_tty(0, 0, 0);
3690 
3691 	memset(&action, 0, sizeof(action));
3692 
3693 	while (1) {
3694 		key = hist_browser__run(browser, "? - help", true, 0);
3695 
3696 		switch (key) {
3697 		case 'q':
3698 			goto out;
3699 		case '?':
3700 			ui_browser__help_window(&browser->b, help);
3701 			break;
3702 		case 'a':
3703 		case K_ENTER:
3704 			if (!browser->selection ||
3705 			    !browser->selection->sym) {
3706 				continue;
3707 			}
3708 
3709 			action.ms.map = browser->selection->map;
3710 			action.ms.sym = browser->selection->sym;
3711 			do_annotate(browser, &action);
3712 			continue;
3713 		default:
3714 			break;
3715 		}
3716 	}
3717 
3718 out:
3719 	hist_browser__delete(browser);
3720 	return 0;
3721 }
3722