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