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