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