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