xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 2dbbe9f2)
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 (node->val.next != node->val.prev) {
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 hpp_arg arg = {
1293 		.b		= &browser->b,
1294 		.current_entry	= current_entry,
1295 	};
1296 	int column = 0;
1297 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1298 
1299 	if (current_entry) {
1300 		browser->he_selection = entry;
1301 		browser->selection = &entry->ms;
1302 	}
1303 
1304 	hist_entry__init_have_children(entry);
1305 	folded_sign = hist_entry__folded(entry);
1306 	arg.folded_sign = folded_sign;
1307 
1308 	if (entry->leaf && row_offset) {
1309 		row_offset--;
1310 		goto show_callchain;
1311 	}
1312 
1313 	hist_browser__gotorc(browser, row, 0);
1314 
1315 	if (current_entry && browser->b.navkeypressed)
1316 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1317 	else
1318 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1319 
1320 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1321 	width -= level * HIERARCHY_INDENT;
1322 
1323 	hists__for_each_format(entry->hists, fmt) {
1324 		char s[2048];
1325 		struct perf_hpp hpp = {
1326 			.buf		= s,
1327 			.size		= sizeof(s),
1328 			.ptr		= &arg,
1329 		};
1330 
1331 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1332 		    column++ < browser->b.horiz_scroll)
1333 			continue;
1334 
1335 		if (perf_hpp__is_sort_entry(fmt) ||
1336 		    perf_hpp__is_dynamic_entry(fmt))
1337 			break;
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 	int indent = browser->hists->nr_hpp_node - 2;
1448 
1449 	if (current_entry) {
1450 		browser->he_selection = NULL;
1451 		browser->selection = NULL;
1452 	}
1453 
1454 	hist_browser__gotorc(browser, row, 0);
1455 
1456 	if (current_entry && browser->b.navkeypressed)
1457 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1458 	else
1459 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1460 
1461 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1462 	width -= level * HIERARCHY_INDENT;
1463 
1464 	hists__for_each_format(browser->hists, fmt) {
1465 		if (perf_hpp__should_skip(fmt, browser->hists) ||
1466 		    column++ < browser->b.horiz_scroll)
1467 			continue;
1468 
1469 		if (perf_hpp__is_sort_entry(fmt) ||
1470 		    perf_hpp__is_dynamic_entry(fmt))
1471 			break;
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 	size_t ret = 0;
1555 	int column = 0;
1556 	int indent = hists->nr_hpp_node - 2;
1557 	bool first = true;
1558 
1559 	ret = scnprintf(buf, size, " ");
1560 	if (advance_hpp_check(&dummy_hpp, ret))
1561 		return ret;
1562 
1563 	hists__for_each_format(hists, fmt) {
1564 		if (column++ < browser->b.horiz_scroll)
1565 			continue;
1566 
1567 		if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
1568 			break;
1569 
1570 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1571 		if (advance_hpp_check(&dummy_hpp, ret))
1572 			break;
1573 
1574 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1575 		if (advance_hpp_check(&dummy_hpp, ret))
1576 			break;
1577 	}
1578 
1579 	ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1580 			indent * HIERARCHY_INDENT, "");
1581 	if (advance_hpp_check(&dummy_hpp, ret))
1582 		return ret;
1583 
1584 	hists__for_each_format(hists, fmt) {
1585 		char *start;
1586 
1587 		if (!perf_hpp__is_sort_entry(fmt) && !perf_hpp__is_dynamic_entry(fmt))
1588 			continue;
1589 		if (perf_hpp__should_skip(fmt, hists))
1590 			continue;
1591 
1592 		if (first) {
1593 			first = false;
1594 		} else {
1595 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1596 			if (advance_hpp_check(&dummy_hpp, ret))
1597 				break;
1598 		}
1599 
1600 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1601 		dummy_hpp.buf[ret] = '\0';
1602 		rtrim(dummy_hpp.buf);
1603 
1604 		start = ltrim(dummy_hpp.buf);
1605 		ret = strlen(start);
1606 
1607 		if (start != dummy_hpp.buf)
1608 			memmove(dummy_hpp.buf, start, ret + 1);
1609 
1610 		if (advance_hpp_check(&dummy_hpp, ret))
1611 			break;
1612 	}
1613 
1614 	return ret;
1615 }
1616 
1617 static void hist_browser__show_headers(struct hist_browser *browser)
1618 {
1619 	char headers[1024];
1620 
1621 	if (symbol_conf.report_hierarchy)
1622 		hists_browser__scnprintf_hierarchy_headers(browser, headers,
1623 							   sizeof(headers));
1624 	else
1625 		hists_browser__scnprintf_headers(browser, headers,
1626 						 sizeof(headers));
1627 	ui_browser__gotorc(&browser->b, 0, 0);
1628 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1629 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1630 }
1631 
1632 static void ui_browser__hists_init_top(struct ui_browser *browser)
1633 {
1634 	if (browser->top == NULL) {
1635 		struct hist_browser *hb;
1636 
1637 		hb = container_of(browser, struct hist_browser, b);
1638 		browser->top = rb_first(&hb->hists->entries);
1639 	}
1640 }
1641 
1642 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1643 {
1644 	unsigned row = 0;
1645 	u16 header_offset = 0;
1646 	struct rb_node *nd;
1647 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1648 
1649 	if (hb->show_headers) {
1650 		hist_browser__show_headers(hb);
1651 		header_offset = 1;
1652 	}
1653 
1654 	ui_browser__hists_init_top(browser);
1655 	hb->he_selection = NULL;
1656 	hb->selection = NULL;
1657 
1658 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1659 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1660 		float percent;
1661 
1662 		if (h->filtered) {
1663 			/* let it move to sibling */
1664 			h->unfolded = false;
1665 			continue;
1666 		}
1667 
1668 		percent = hist_entry__get_percent_limit(h);
1669 		if (percent < hb->min_pcnt)
1670 			continue;
1671 
1672 		if (symbol_conf.report_hierarchy) {
1673 			row += hist_browser__show_hierarchy_entry(hb, h, row,
1674 								  h->depth);
1675 			if (row == browser->rows)
1676 				break;
1677 
1678 			if (h->has_no_entry) {
1679 				hist_browser__show_no_entry(hb, row, h->depth);
1680 				row++;
1681 			}
1682 		} else {
1683 			row += hist_browser__show_entry(hb, h, row);
1684 		}
1685 
1686 		if (row == browser->rows)
1687 			break;
1688 	}
1689 
1690 	return row + header_offset;
1691 }
1692 
1693 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1694 					     float min_pcnt)
1695 {
1696 	while (nd != NULL) {
1697 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1698 		float percent = hist_entry__get_percent_limit(h);
1699 
1700 		if (!h->filtered && percent >= min_pcnt)
1701 			return nd;
1702 
1703 		/*
1704 		 * If it's filtered, its all children also were filtered.
1705 		 * So move to sibling node.
1706 		 */
1707 		if (rb_next(nd))
1708 			nd = rb_next(nd);
1709 		else
1710 			nd = rb_hierarchy_next(nd);
1711 	}
1712 
1713 	return NULL;
1714 }
1715 
1716 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1717 						  float min_pcnt)
1718 {
1719 	while (nd != NULL) {
1720 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1721 		float percent = hist_entry__get_percent_limit(h);
1722 
1723 		if (!h->filtered && percent >= min_pcnt)
1724 			return nd;
1725 
1726 		nd = rb_hierarchy_prev(nd);
1727 	}
1728 
1729 	return NULL;
1730 }
1731 
1732 static void ui_browser__hists_seek(struct ui_browser *browser,
1733 				   off_t offset, int whence)
1734 {
1735 	struct hist_entry *h;
1736 	struct rb_node *nd;
1737 	bool first = true;
1738 	struct hist_browser *hb;
1739 
1740 	hb = container_of(browser, struct hist_browser, b);
1741 
1742 	if (browser->nr_entries == 0)
1743 		return;
1744 
1745 	ui_browser__hists_init_top(browser);
1746 
1747 	switch (whence) {
1748 	case SEEK_SET:
1749 		nd = hists__filter_entries(rb_first(browser->entries),
1750 					   hb->min_pcnt);
1751 		break;
1752 	case SEEK_CUR:
1753 		nd = browser->top;
1754 		goto do_offset;
1755 	case SEEK_END:
1756 		nd = rb_hierarchy_last(rb_last(browser->entries));
1757 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1758 		first = false;
1759 		break;
1760 	default:
1761 		return;
1762 	}
1763 
1764 	/*
1765 	 * Moves not relative to the first visible entry invalidates its
1766 	 * row_offset:
1767 	 */
1768 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1769 	h->row_offset = 0;
1770 
1771 	/*
1772 	 * Here we have to check if nd is expanded (+), if it is we can't go
1773 	 * the next top level hist_entry, instead we must compute an offset of
1774 	 * what _not_ to show and not change the first visible entry.
1775 	 *
1776 	 * This offset increments when we are going from top to bottom and
1777 	 * decreases when we're going from bottom to top.
1778 	 *
1779 	 * As we don't have backpointers to the top level in the callchains
1780 	 * structure, we need to always print the whole hist_entry callchain,
1781 	 * skipping the first ones that are before the first visible entry
1782 	 * and stop when we printed enough lines to fill the screen.
1783 	 */
1784 do_offset:
1785 	if (!nd)
1786 		return;
1787 
1788 	if (offset > 0) {
1789 		do {
1790 			h = rb_entry(nd, struct hist_entry, rb_node);
1791 			if (h->unfolded && h->leaf) {
1792 				u16 remaining = h->nr_rows - h->row_offset;
1793 				if (offset > remaining) {
1794 					offset -= remaining;
1795 					h->row_offset = 0;
1796 				} else {
1797 					h->row_offset += offset;
1798 					offset = 0;
1799 					browser->top = nd;
1800 					break;
1801 				}
1802 			}
1803 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1804 						   hb->min_pcnt);
1805 			if (nd == NULL)
1806 				break;
1807 			--offset;
1808 			browser->top = nd;
1809 		} while (offset != 0);
1810 	} else if (offset < 0) {
1811 		while (1) {
1812 			h = rb_entry(nd, struct hist_entry, rb_node);
1813 			if (h->unfolded && h->leaf) {
1814 				if (first) {
1815 					if (-offset > h->row_offset) {
1816 						offset += h->row_offset;
1817 						h->row_offset = 0;
1818 					} else {
1819 						h->row_offset += offset;
1820 						offset = 0;
1821 						browser->top = nd;
1822 						break;
1823 					}
1824 				} else {
1825 					if (-offset > h->nr_rows) {
1826 						offset += h->nr_rows;
1827 						h->row_offset = 0;
1828 					} else {
1829 						h->row_offset = h->nr_rows + offset;
1830 						offset = 0;
1831 						browser->top = nd;
1832 						break;
1833 					}
1834 				}
1835 			}
1836 
1837 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1838 							hb->min_pcnt);
1839 			if (nd == NULL)
1840 				break;
1841 			++offset;
1842 			browser->top = nd;
1843 			if (offset == 0) {
1844 				/*
1845 				 * Last unfiltered hist_entry, check if it is
1846 				 * unfolded, if it is then we should have
1847 				 * row_offset at its last entry.
1848 				 */
1849 				h = rb_entry(nd, struct hist_entry, rb_node);
1850 				if (h->unfolded && h->leaf)
1851 					h->row_offset = h->nr_rows;
1852 				break;
1853 			}
1854 			first = false;
1855 		}
1856 	} else {
1857 		browser->top = nd;
1858 		h = rb_entry(nd, struct hist_entry, rb_node);
1859 		h->row_offset = 0;
1860 	}
1861 }
1862 
1863 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1864 					   struct hist_entry *he, FILE *fp,
1865 					   int level)
1866 {
1867 	struct callchain_print_arg arg  = {
1868 		.fp = fp,
1869 	};
1870 
1871 	hist_browser__show_callchain(browser, he, level, 0,
1872 				     hist_browser__fprintf_callchain_entry, &arg,
1873 				     hist_browser__check_dump_full);
1874 	return arg.printed;
1875 }
1876 
1877 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1878 				       struct hist_entry *he, FILE *fp)
1879 {
1880 	char s[8192];
1881 	int printed = 0;
1882 	char folded_sign = ' ';
1883 	struct perf_hpp hpp = {
1884 		.buf = s,
1885 		.size = sizeof(s),
1886 	};
1887 	struct perf_hpp_fmt *fmt;
1888 	bool first = true;
1889 	int ret;
1890 
1891 	if (symbol_conf.use_callchain)
1892 		folded_sign = hist_entry__folded(he);
1893 
1894 	if (symbol_conf.use_callchain)
1895 		printed += fprintf(fp, "%c ", folded_sign);
1896 
1897 	hists__for_each_format(browser->hists, fmt) {
1898 		if (perf_hpp__should_skip(fmt, he->hists))
1899 			continue;
1900 
1901 		if (!first) {
1902 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1903 			advance_hpp(&hpp, ret);
1904 		} else
1905 			first = false;
1906 
1907 		ret = fmt->entry(fmt, &hpp, he);
1908 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1909 		advance_hpp(&hpp, ret);
1910 	}
1911 	printed += fprintf(fp, "%s\n", s);
1912 
1913 	if (folded_sign == '-')
1914 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1915 
1916 	return printed;
1917 }
1918 
1919 
1920 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1921 						 struct hist_entry *he,
1922 						 FILE *fp, int level,
1923 						 int nr_sort_keys)
1924 {
1925 	char s[8192];
1926 	int printed = 0;
1927 	char folded_sign = ' ';
1928 	struct perf_hpp hpp = {
1929 		.buf = s,
1930 		.size = sizeof(s),
1931 	};
1932 	struct perf_hpp_fmt *fmt;
1933 	bool first = true;
1934 	int ret;
1935 	int hierarchy_indent = nr_sort_keys * HIERARCHY_INDENT;
1936 
1937 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1938 
1939 	folded_sign = hist_entry__folded(he);
1940 	printed += fprintf(fp, "%c", folded_sign);
1941 
1942 	hists__for_each_format(he->hists, fmt) {
1943 		if (perf_hpp__should_skip(fmt, he->hists))
1944 			continue;
1945 
1946 		if (perf_hpp__is_sort_entry(fmt) ||
1947 		    perf_hpp__is_dynamic_entry(fmt))
1948 			break;
1949 
1950 		if (!first) {
1951 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1952 			advance_hpp(&hpp, ret);
1953 		} else
1954 			first = false;
1955 
1956 		ret = fmt->entry(fmt, &hpp, he);
1957 		advance_hpp(&hpp, ret);
1958 	}
1959 
1960 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1961 	advance_hpp(&hpp, ret);
1962 
1963 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1964 		ret = scnprintf(hpp.buf, hpp.size, "  ");
1965 		advance_hpp(&hpp, ret);
1966 
1967 		ret = fmt->entry(fmt, &hpp, he);
1968 		advance_hpp(&hpp, ret);
1969 	}
1970 
1971 	printed += fprintf(fp, "%s\n", rtrim(s));
1972 
1973 	if (he->leaf && folded_sign == '-') {
1974 		printed += hist_browser__fprintf_callchain(browser, he, fp,
1975 							   he->depth + 1);
1976 	}
1977 
1978 	return printed;
1979 }
1980 
1981 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1982 {
1983 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1984 						   browser->min_pcnt);
1985 	int printed = 0;
1986 	int nr_sort = browser->hists->nr_sort_keys;
1987 
1988 	while (nd) {
1989 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1990 
1991 		if (symbol_conf.report_hierarchy) {
1992 			printed += hist_browser__fprintf_hierarchy_entry(browser,
1993 									 h, fp,
1994 									 h->depth,
1995 									 nr_sort);
1996 		} else {
1997 			printed += hist_browser__fprintf_entry(browser, h, fp);
1998 		}
1999 
2000 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2001 					   browser->min_pcnt);
2002 	}
2003 
2004 	return printed;
2005 }
2006 
2007 static int hist_browser__dump(struct hist_browser *browser)
2008 {
2009 	char filename[64];
2010 	FILE *fp;
2011 
2012 	while (1) {
2013 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2014 		if (access(filename, F_OK))
2015 			break;
2016 		/*
2017  		 * XXX: Just an arbitrary lazy upper limit
2018  		 */
2019 		if (++browser->print_seq == 8192) {
2020 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2021 			return -1;
2022 		}
2023 	}
2024 
2025 	fp = fopen(filename, "w");
2026 	if (fp == NULL) {
2027 		char bf[64];
2028 		const char *err = strerror_r(errno, bf, sizeof(bf));
2029 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2030 		return -1;
2031 	}
2032 
2033 	++browser->print_seq;
2034 	hist_browser__fprintf(browser, fp);
2035 	fclose(fp);
2036 	ui_helpline__fpush("%s written!", filename);
2037 
2038 	return 0;
2039 }
2040 
2041 static struct hist_browser *hist_browser__new(struct hists *hists,
2042 					      struct hist_browser_timer *hbt,
2043 					      struct perf_env *env)
2044 {
2045 	struct hist_browser *browser = zalloc(sizeof(*browser));
2046 
2047 	if (browser) {
2048 		browser->hists = hists;
2049 		browser->b.refresh = hist_browser__refresh;
2050 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2051 		browser->b.seek = ui_browser__hists_seek;
2052 		browser->b.use_navkeypressed = true;
2053 		browser->show_headers = symbol_conf.show_hist_headers;
2054 		browser->hbt = hbt;
2055 		browser->env = env;
2056 	}
2057 
2058 	return browser;
2059 }
2060 
2061 static void hist_browser__delete(struct hist_browser *browser)
2062 {
2063 	free(browser);
2064 }
2065 
2066 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2067 {
2068 	return browser->he_selection;
2069 }
2070 
2071 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2072 {
2073 	return browser->he_selection->thread;
2074 }
2075 
2076 /* Check whether the browser is for 'top' or 'report' */
2077 static inline bool is_report_browser(void *timer)
2078 {
2079 	return timer == NULL;
2080 }
2081 
2082 static int hists__browser_title(struct hists *hists,
2083 				struct hist_browser_timer *hbt,
2084 				char *bf, size_t size)
2085 {
2086 	char unit;
2087 	int printed;
2088 	const struct dso *dso = hists->dso_filter;
2089 	const struct thread *thread = hists->thread_filter;
2090 	int socket_id = hists->socket_filter;
2091 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2092 	u64 nr_events = hists->stats.total_period;
2093 	struct perf_evsel *evsel = hists_to_evsel(hists);
2094 	const char *ev_name = perf_evsel__name(evsel);
2095 	char buf[512];
2096 	size_t buflen = sizeof(buf);
2097 	char ref[30] = " show reference callgraph, ";
2098 	bool enable_ref = false;
2099 
2100 	if (symbol_conf.filter_relative) {
2101 		nr_samples = hists->stats.nr_non_filtered_samples;
2102 		nr_events = hists->stats.total_non_filtered_period;
2103 	}
2104 
2105 	if (perf_evsel__is_group_event(evsel)) {
2106 		struct perf_evsel *pos;
2107 
2108 		perf_evsel__group_desc(evsel, buf, buflen);
2109 		ev_name = buf;
2110 
2111 		for_each_group_member(pos, evsel) {
2112 			struct hists *pos_hists = evsel__hists(pos);
2113 
2114 			if (symbol_conf.filter_relative) {
2115 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
2116 				nr_events += pos_hists->stats.total_non_filtered_period;
2117 			} else {
2118 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2119 				nr_events += pos_hists->stats.total_period;
2120 			}
2121 		}
2122 	}
2123 
2124 	if (symbol_conf.show_ref_callgraph &&
2125 	    strstr(ev_name, "call-graph=no"))
2126 		enable_ref = true;
2127 	nr_samples = convert_unit(nr_samples, &unit);
2128 	printed = scnprintf(bf, size,
2129 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2130 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2131 
2132 
2133 	if (hists->uid_filter_str)
2134 		printed += snprintf(bf + printed, size - printed,
2135 				    ", UID: %s", hists->uid_filter_str);
2136 	if (thread)
2137 		printed += scnprintf(bf + printed, size - printed,
2138 				    ", Thread: %s(%d)",
2139 				     (thread->comm_set ? thread__comm_str(thread) : ""),
2140 				    thread->tid);
2141 	if (dso)
2142 		printed += scnprintf(bf + printed, size - printed,
2143 				    ", DSO: %s", dso->short_name);
2144 	if (socket_id > -1)
2145 		printed += scnprintf(bf + printed, size - printed,
2146 				    ", Processor Socket: %d", socket_id);
2147 	if (!is_report_browser(hbt)) {
2148 		struct perf_top *top = hbt->arg;
2149 
2150 		if (top->zero)
2151 			printed += scnprintf(bf + printed, size - printed, " [z]");
2152 	}
2153 
2154 	return printed;
2155 }
2156 
2157 static inline void free_popup_options(char **options, int n)
2158 {
2159 	int i;
2160 
2161 	for (i = 0; i < n; ++i)
2162 		zfree(&options[i]);
2163 }
2164 
2165 /*
2166  * Only runtime switching of perf data file will make "input_name" point
2167  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2168  * whether we need to call free() for current "input_name" during the switch.
2169  */
2170 static bool is_input_name_malloced = false;
2171 
2172 static int switch_data_file(void)
2173 {
2174 	char *pwd, *options[32], *abs_path[32], *tmp;
2175 	DIR *pwd_dir;
2176 	int nr_options = 0, choice = -1, ret = -1;
2177 	struct dirent *dent;
2178 
2179 	pwd = getenv("PWD");
2180 	if (!pwd)
2181 		return ret;
2182 
2183 	pwd_dir = opendir(pwd);
2184 	if (!pwd_dir)
2185 		return ret;
2186 
2187 	memset(options, 0, sizeof(options));
2188 	memset(options, 0, sizeof(abs_path));
2189 
2190 	while ((dent = readdir(pwd_dir))) {
2191 		char path[PATH_MAX];
2192 		u64 magic;
2193 		char *name = dent->d_name;
2194 		FILE *file;
2195 
2196 		if (!(dent->d_type == DT_REG))
2197 			continue;
2198 
2199 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2200 
2201 		file = fopen(path, "r");
2202 		if (!file)
2203 			continue;
2204 
2205 		if (fread(&magic, 1, 8, file) < 8)
2206 			goto close_file_and_continue;
2207 
2208 		if (is_perf_magic(magic)) {
2209 			options[nr_options] = strdup(name);
2210 			if (!options[nr_options])
2211 				goto close_file_and_continue;
2212 
2213 			abs_path[nr_options] = strdup(path);
2214 			if (!abs_path[nr_options]) {
2215 				zfree(&options[nr_options]);
2216 				ui__warning("Can't search all data files due to memory shortage.\n");
2217 				fclose(file);
2218 				break;
2219 			}
2220 
2221 			nr_options++;
2222 		}
2223 
2224 close_file_and_continue:
2225 		fclose(file);
2226 		if (nr_options >= 32) {
2227 			ui__warning("Too many perf data files in PWD!\n"
2228 				    "Only the first 32 files will be listed.\n");
2229 			break;
2230 		}
2231 	}
2232 	closedir(pwd_dir);
2233 
2234 	if (nr_options) {
2235 		choice = ui__popup_menu(nr_options, options);
2236 		if (choice < nr_options && choice >= 0) {
2237 			tmp = strdup(abs_path[choice]);
2238 			if (tmp) {
2239 				if (is_input_name_malloced)
2240 					free((void *)input_name);
2241 				input_name = tmp;
2242 				is_input_name_malloced = true;
2243 				ret = 0;
2244 			} else
2245 				ui__warning("Data switch failed due to memory shortage!\n");
2246 		}
2247 	}
2248 
2249 	free_popup_options(options, nr_options);
2250 	free_popup_options(abs_path, nr_options);
2251 	return ret;
2252 }
2253 
2254 struct popup_action {
2255 	struct thread 		*thread;
2256 	struct map_symbol 	ms;
2257 	int			socket;
2258 
2259 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2260 };
2261 
2262 static int
2263 do_annotate(struct hist_browser *browser, struct popup_action *act)
2264 {
2265 	struct perf_evsel *evsel;
2266 	struct annotation *notes;
2267 	struct hist_entry *he;
2268 	int err;
2269 
2270 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
2271 		return 0;
2272 
2273 	notes = symbol__annotation(act->ms.sym);
2274 	if (!notes->src)
2275 		return 0;
2276 
2277 	evsel = hists_to_evsel(browser->hists);
2278 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2279 	he = hist_browser__selected_entry(browser);
2280 	/*
2281 	 * offer option to annotate the other branch source or target
2282 	 * (if they exists) when returning from annotate
2283 	 */
2284 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2285 		return 1;
2286 
2287 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2288 	if (err)
2289 		ui_browser__handle_resize(&browser->b);
2290 	return 0;
2291 }
2292 
2293 static int
2294 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2295 		 struct popup_action *act, char **optstr,
2296 		 struct map *map, struct symbol *sym)
2297 {
2298 	if (sym == NULL || map->dso->annotate_warned)
2299 		return 0;
2300 
2301 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2302 		return 0;
2303 
2304 	act->ms.map = map;
2305 	act->ms.sym = sym;
2306 	act->fn = do_annotate;
2307 	return 1;
2308 }
2309 
2310 static int
2311 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2312 {
2313 	struct thread *thread = act->thread;
2314 
2315 	if (browser->hists->thread_filter) {
2316 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2317 		perf_hpp__set_elide(HISTC_THREAD, false);
2318 		thread__zput(browser->hists->thread_filter);
2319 		ui_helpline__pop();
2320 	} else {
2321 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2322 				   thread->comm_set ? thread__comm_str(thread) : "",
2323 				   thread->tid);
2324 		browser->hists->thread_filter = thread__get(thread);
2325 		perf_hpp__set_elide(HISTC_THREAD, false);
2326 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2327 	}
2328 
2329 	hists__filter_by_thread(browser->hists);
2330 	hist_browser__reset(browser);
2331 	return 0;
2332 }
2333 
2334 static int
2335 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2336 	       char **optstr, struct thread *thread)
2337 {
2338 	if (!sort__has_thread || thread == NULL)
2339 		return 0;
2340 
2341 	if (asprintf(optstr, "Zoom %s %s(%d) thread",
2342 		     browser->hists->thread_filter ? "out of" : "into",
2343 		     thread->comm_set ? thread__comm_str(thread) : "",
2344 		     thread->tid) < 0)
2345 		return 0;
2346 
2347 	act->thread = thread;
2348 	act->fn = do_zoom_thread;
2349 	return 1;
2350 }
2351 
2352 static int
2353 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2354 {
2355 	struct map *map = act->ms.map;
2356 
2357 	if (browser->hists->dso_filter) {
2358 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2359 		perf_hpp__set_elide(HISTC_DSO, false);
2360 		browser->hists->dso_filter = NULL;
2361 		ui_helpline__pop();
2362 	} else {
2363 		if (map == NULL)
2364 			return 0;
2365 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2366 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2367 		browser->hists->dso_filter = map->dso;
2368 		perf_hpp__set_elide(HISTC_DSO, true);
2369 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2370 	}
2371 
2372 	hists__filter_by_dso(browser->hists);
2373 	hist_browser__reset(browser);
2374 	return 0;
2375 }
2376 
2377 static int
2378 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2379 	    char **optstr, struct map *map)
2380 {
2381 	if (!sort__has_dso || map == NULL)
2382 		return 0;
2383 
2384 	if (asprintf(optstr, "Zoom %s %s DSO",
2385 		     browser->hists->dso_filter ? "out of" : "into",
2386 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2387 		return 0;
2388 
2389 	act->ms.map = map;
2390 	act->fn = do_zoom_dso;
2391 	return 1;
2392 }
2393 
2394 static int
2395 do_browse_map(struct hist_browser *browser __maybe_unused,
2396 	      struct popup_action *act)
2397 {
2398 	map__browse(act->ms.map);
2399 	return 0;
2400 }
2401 
2402 static int
2403 add_map_opt(struct hist_browser *browser __maybe_unused,
2404 	    struct popup_action *act, char **optstr, struct map *map)
2405 {
2406 	if (!sort__has_dso || map == NULL)
2407 		return 0;
2408 
2409 	if (asprintf(optstr, "Browse map details") < 0)
2410 		return 0;
2411 
2412 	act->ms.map = map;
2413 	act->fn = do_browse_map;
2414 	return 1;
2415 }
2416 
2417 static int
2418 do_run_script(struct hist_browser *browser __maybe_unused,
2419 	      struct popup_action *act)
2420 {
2421 	char script_opt[64];
2422 	memset(script_opt, 0, sizeof(script_opt));
2423 
2424 	if (act->thread) {
2425 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2426 			  thread__comm_str(act->thread));
2427 	} else if (act->ms.sym) {
2428 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2429 			  act->ms.sym->name);
2430 	}
2431 
2432 	script_browse(script_opt);
2433 	return 0;
2434 }
2435 
2436 static int
2437 add_script_opt(struct hist_browser *browser __maybe_unused,
2438 	       struct popup_action *act, char **optstr,
2439 	       struct thread *thread, struct symbol *sym)
2440 {
2441 	if (thread) {
2442 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2443 			     thread__comm_str(thread)) < 0)
2444 			return 0;
2445 	} else if (sym) {
2446 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2447 			     sym->name) < 0)
2448 			return 0;
2449 	} else {
2450 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2451 			return 0;
2452 	}
2453 
2454 	act->thread = thread;
2455 	act->ms.sym = sym;
2456 	act->fn = do_run_script;
2457 	return 1;
2458 }
2459 
2460 static int
2461 do_switch_data(struct hist_browser *browser __maybe_unused,
2462 	       struct popup_action *act __maybe_unused)
2463 {
2464 	if (switch_data_file()) {
2465 		ui__warning("Won't switch the data files due to\n"
2466 			    "no valid data file get selected!\n");
2467 		return 0;
2468 	}
2469 
2470 	return K_SWITCH_INPUT_DATA;
2471 }
2472 
2473 static int
2474 add_switch_opt(struct hist_browser *browser,
2475 	       struct popup_action *act, char **optstr)
2476 {
2477 	if (!is_report_browser(browser->hbt))
2478 		return 0;
2479 
2480 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2481 		return 0;
2482 
2483 	act->fn = do_switch_data;
2484 	return 1;
2485 }
2486 
2487 static int
2488 do_exit_browser(struct hist_browser *browser __maybe_unused,
2489 		struct popup_action *act __maybe_unused)
2490 {
2491 	return 0;
2492 }
2493 
2494 static int
2495 add_exit_opt(struct hist_browser *browser __maybe_unused,
2496 	     struct popup_action *act, char **optstr)
2497 {
2498 	if (asprintf(optstr, "Exit") < 0)
2499 		return 0;
2500 
2501 	act->fn = do_exit_browser;
2502 	return 1;
2503 }
2504 
2505 static int
2506 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2507 {
2508 	if (browser->hists->socket_filter > -1) {
2509 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2510 		browser->hists->socket_filter = -1;
2511 		perf_hpp__set_elide(HISTC_SOCKET, false);
2512 	} else {
2513 		browser->hists->socket_filter = act->socket;
2514 		perf_hpp__set_elide(HISTC_SOCKET, true);
2515 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2516 	}
2517 
2518 	hists__filter_by_socket(browser->hists);
2519 	hist_browser__reset(browser);
2520 	return 0;
2521 }
2522 
2523 static int
2524 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2525 	       char **optstr, int socket_id)
2526 {
2527 	if (!sort__has_socket || socket_id < 0)
2528 		return 0;
2529 
2530 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2531 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2532 		     socket_id) < 0)
2533 		return 0;
2534 
2535 	act->socket = socket_id;
2536 	act->fn = do_zoom_socket;
2537 	return 1;
2538 }
2539 
2540 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2541 {
2542 	u64 nr_entries = 0;
2543 	struct rb_node *nd = rb_first(&hb->hists->entries);
2544 
2545 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2546 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2547 		return;
2548 	}
2549 
2550 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2551 		nr_entries++;
2552 		nd = rb_hierarchy_next(nd);
2553 	}
2554 
2555 	hb->nr_non_filtered_entries = nr_entries;
2556 	hb->nr_hierarchy_entries = nr_entries;
2557 }
2558 
2559 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2560 					       double percent)
2561 {
2562 	struct hist_entry *he;
2563 	struct rb_node *nd = rb_first(&hb->hists->entries);
2564 	u64 total = hists__total_period(hb->hists);
2565 	u64 min_callchain_hits = total * (percent / 100);
2566 
2567 	hb->min_pcnt = callchain_param.min_percent = percent;
2568 
2569 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2570 		he = rb_entry(nd, struct hist_entry, rb_node);
2571 
2572 		if (he->has_no_entry) {
2573 			he->has_no_entry = false;
2574 			he->nr_rows = 0;
2575 		}
2576 
2577 		if (!he->leaf || !symbol_conf.use_callchain)
2578 			goto next;
2579 
2580 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2581 			total = he->stat.period;
2582 
2583 			if (symbol_conf.cumulate_callchain)
2584 				total = he->stat_acc->period;
2585 
2586 			min_callchain_hits = total * (percent / 100);
2587 		}
2588 
2589 		callchain_param.sort(&he->sorted_chain, he->callchain,
2590 				     min_callchain_hits, &callchain_param);
2591 
2592 next:
2593 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2594 
2595 		/* force to re-evaluate folding state of callchains */
2596 		he->init_have_children = false;
2597 		hist_entry__set_folding(he, hb, false);
2598 	}
2599 }
2600 
2601 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2602 				    const char *helpline,
2603 				    bool left_exits,
2604 				    struct hist_browser_timer *hbt,
2605 				    float min_pcnt,
2606 				    struct perf_env *env)
2607 {
2608 	struct hists *hists = evsel__hists(evsel);
2609 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2610 	struct branch_info *bi;
2611 #define MAX_OPTIONS  16
2612 	char *options[MAX_OPTIONS];
2613 	struct popup_action actions[MAX_OPTIONS];
2614 	int nr_options = 0;
2615 	int key = -1;
2616 	char buf[64];
2617 	int delay_secs = hbt ? hbt->refresh : 0;
2618 	struct perf_hpp_fmt *fmt;
2619 
2620 #define HIST_BROWSER_HELP_COMMON					\
2621 	"h/?/F1        Show this window\n"				\
2622 	"UP/DOWN/PGUP\n"						\
2623 	"PGDN/SPACE    Navigate\n"					\
2624 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2625 	"For multiple event sessions:\n\n"				\
2626 	"TAB/UNTAB     Switch events\n\n"				\
2627 	"For symbolic views (--sort has sym):\n\n"			\
2628 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2629 	"ESC           Zoom out\n"					\
2630 	"a             Annotate current symbol\n"			\
2631 	"C             Collapse all callchains\n"			\
2632 	"d             Zoom into current DSO\n"				\
2633 	"E             Expand all callchains\n"				\
2634 	"F             Toggle percentage of filtered entries\n"		\
2635 	"H             Display column headers\n"			\
2636 	"L             Change percent limit\n"				\
2637 	"m             Display context menu\n"				\
2638 	"S             Zoom into current Processor Socket\n"		\
2639 
2640 	/* help messages are sorted by lexical order of the hotkey */
2641 	const char report_help[] = HIST_BROWSER_HELP_COMMON
2642 	"i             Show header information\n"
2643 	"P             Print histograms to perf.hist.N\n"
2644 	"r             Run available scripts\n"
2645 	"s             Switch to another data file in PWD\n"
2646 	"t             Zoom into current Thread\n"
2647 	"V             Verbose (DSO names in callchains, etc)\n"
2648 	"/             Filter symbol by name";
2649 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2650 	"P             Print histograms to perf.hist.N\n"
2651 	"t             Zoom into current Thread\n"
2652 	"V             Verbose (DSO names in callchains, etc)\n"
2653 	"z             Toggle zeroing of samples\n"
2654 	"f             Enable/Disable events\n"
2655 	"/             Filter symbol by name";
2656 
2657 	if (browser == NULL)
2658 		return -1;
2659 
2660 	/* reset abort key so that it can get Ctrl-C as a key */
2661 	SLang_reset_tty();
2662 	SLang_init_tty(0, 0, 0);
2663 
2664 	if (min_pcnt)
2665 		browser->min_pcnt = min_pcnt;
2666 	hist_browser__update_nr_entries(browser);
2667 
2668 	browser->pstack = pstack__new(3);
2669 	if (browser->pstack == NULL)
2670 		goto out;
2671 
2672 	ui_helpline__push(helpline);
2673 
2674 	memset(options, 0, sizeof(options));
2675 	memset(actions, 0, sizeof(actions));
2676 
2677 	hists__for_each_format(browser->hists, fmt) {
2678 		perf_hpp__reset_width(fmt, hists);
2679 		/*
2680 		 * This is done just once, and activates the horizontal scrolling
2681 		 * code in the ui_browser code, it would be better to have a the
2682 		 * counter in the perf_hpp code, but I couldn't find doing it here
2683 		 * works, FIXME by setting this in hist_browser__new, for now, be
2684 		 * clever 8-)
2685 		 */
2686 		++browser->b.columns;
2687 	}
2688 
2689 	if (symbol_conf.col_width_list_str)
2690 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2691 
2692 	while (1) {
2693 		struct thread *thread = NULL;
2694 		struct map *map = NULL;
2695 		int choice = 0;
2696 		int socked_id = -1;
2697 
2698 		nr_options = 0;
2699 
2700 		key = hist_browser__run(browser, helpline);
2701 
2702 		if (browser->he_selection != NULL) {
2703 			thread = hist_browser__selected_thread(browser);
2704 			map = browser->selection->map;
2705 			socked_id = browser->he_selection->socket;
2706 		}
2707 		switch (key) {
2708 		case K_TAB:
2709 		case K_UNTAB:
2710 			if (nr_events == 1)
2711 				continue;
2712 			/*
2713 			 * Exit the browser, let hists__browser_tree
2714 			 * go to the next or previous
2715 			 */
2716 			goto out_free_stack;
2717 		case 'a':
2718 			if (!sort__has_sym) {
2719 				ui_browser__warning(&browser->b, delay_secs * 2,
2720 			"Annotation is only available for symbolic views, "
2721 			"include \"sym*\" in --sort to use it.");
2722 				continue;
2723 			}
2724 
2725 			if (browser->selection == NULL ||
2726 			    browser->selection->sym == NULL ||
2727 			    browser->selection->map->dso->annotate_warned)
2728 				continue;
2729 
2730 			actions->ms.map = browser->selection->map;
2731 			actions->ms.sym = browser->selection->sym;
2732 			do_annotate(browser, actions);
2733 			continue;
2734 		case 'P':
2735 			hist_browser__dump(browser);
2736 			continue;
2737 		case 'd':
2738 			actions->ms.map = map;
2739 			do_zoom_dso(browser, actions);
2740 			continue;
2741 		case 'V':
2742 			browser->show_dso = !browser->show_dso;
2743 			continue;
2744 		case 't':
2745 			actions->thread = thread;
2746 			do_zoom_thread(browser, actions);
2747 			continue;
2748 		case 'S':
2749 			actions->socket = socked_id;
2750 			do_zoom_socket(browser, actions);
2751 			continue;
2752 		case '/':
2753 			if (ui_browser__input_window("Symbol to show",
2754 					"Please enter the name of symbol you want to see.\n"
2755 					"To remove the filter later, press / + ENTER.",
2756 					buf, "ENTER: OK, ESC: Cancel",
2757 					delay_secs * 2) == K_ENTER) {
2758 				hists->symbol_filter_str = *buf ? buf : NULL;
2759 				hists__filter_by_symbol(hists);
2760 				hist_browser__reset(browser);
2761 			}
2762 			continue;
2763 		case 'r':
2764 			if (is_report_browser(hbt)) {
2765 				actions->thread = NULL;
2766 				actions->ms.sym = NULL;
2767 				do_run_script(browser, actions);
2768 			}
2769 			continue;
2770 		case 's':
2771 			if (is_report_browser(hbt)) {
2772 				key = do_switch_data(browser, actions);
2773 				if (key == K_SWITCH_INPUT_DATA)
2774 					goto out_free_stack;
2775 			}
2776 			continue;
2777 		case 'i':
2778 			/* env->arch is NULL for live-mode (i.e. perf top) */
2779 			if (env->arch)
2780 				tui__header_window(env);
2781 			continue;
2782 		case 'F':
2783 			symbol_conf.filter_relative ^= 1;
2784 			continue;
2785 		case 'z':
2786 			if (!is_report_browser(hbt)) {
2787 				struct perf_top *top = hbt->arg;
2788 
2789 				top->zero = !top->zero;
2790 			}
2791 			continue;
2792 		case 'L':
2793 			if (ui_browser__input_window("Percent Limit",
2794 					"Please enter the value you want to hide entries under that percent.",
2795 					buf, "ENTER: OK, ESC: Cancel",
2796 					delay_secs * 2) == K_ENTER) {
2797 				char *end;
2798 				double new_percent = strtod(buf, &end);
2799 
2800 				if (new_percent < 0 || new_percent > 100) {
2801 					ui_browser__warning(&browser->b, delay_secs * 2,
2802 						"Invalid percent: %.2f", new_percent);
2803 					continue;
2804 				}
2805 
2806 				hist_browser__update_percent_limit(browser, new_percent);
2807 				hist_browser__reset(browser);
2808 			}
2809 			continue;
2810 		case K_F1:
2811 		case 'h':
2812 		case '?':
2813 			ui_browser__help_window(&browser->b,
2814 				is_report_browser(hbt) ? report_help : top_help);
2815 			continue;
2816 		case K_ENTER:
2817 		case K_RIGHT:
2818 		case 'm':
2819 			/* menu */
2820 			break;
2821 		case K_ESC:
2822 		case K_LEFT: {
2823 			const void *top;
2824 
2825 			if (pstack__empty(browser->pstack)) {
2826 				/*
2827 				 * Go back to the perf_evsel_menu__run or other user
2828 				 */
2829 				if (left_exits)
2830 					goto out_free_stack;
2831 
2832 				if (key == K_ESC &&
2833 				    ui_browser__dialog_yesno(&browser->b,
2834 							     "Do you really want to exit?"))
2835 					goto out_free_stack;
2836 
2837 				continue;
2838 			}
2839 			top = pstack__peek(browser->pstack);
2840 			if (top == &browser->hists->dso_filter) {
2841 				/*
2842 				 * No need to set actions->dso here since
2843 				 * it's just to remove the current filter.
2844 				 * Ditto for thread below.
2845 				 */
2846 				do_zoom_dso(browser, actions);
2847 			} else if (top == &browser->hists->thread_filter) {
2848 				do_zoom_thread(browser, actions);
2849 			} else if (top == &browser->hists->socket_filter) {
2850 				do_zoom_socket(browser, actions);
2851 			}
2852 			continue;
2853 		}
2854 		case 'q':
2855 		case CTRL('c'):
2856 			goto out_free_stack;
2857 		case 'f':
2858 			if (!is_report_browser(hbt)) {
2859 				struct perf_top *top = hbt->arg;
2860 
2861 				perf_evlist__toggle_enable(top->evlist);
2862 				/*
2863 				 * No need to refresh, resort/decay histogram
2864 				 * entries if we are not collecting samples:
2865 				 */
2866 				if (top->evlist->enabled) {
2867 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2868 					hbt->refresh = delay_secs;
2869 				} else {
2870 					helpline = "Press 'f' again to re-enable the events";
2871 					hbt->refresh = 0;
2872 				}
2873 				continue;
2874 			}
2875 			/* Fall thru */
2876 		default:
2877 			helpline = "Press '?' for help on key bindings";
2878 			continue;
2879 		}
2880 
2881 		if (!sort__has_sym || browser->selection == NULL)
2882 			goto skip_annotation;
2883 
2884 		if (sort__mode == SORT_MODE__BRANCH) {
2885 			bi = browser->he_selection->branch_info;
2886 
2887 			if (bi == NULL)
2888 				goto skip_annotation;
2889 
2890 			nr_options += add_annotate_opt(browser,
2891 						       &actions[nr_options],
2892 						       &options[nr_options],
2893 						       bi->from.map,
2894 						       bi->from.sym);
2895 			if (bi->to.sym != bi->from.sym)
2896 				nr_options += add_annotate_opt(browser,
2897 							&actions[nr_options],
2898 							&options[nr_options],
2899 							bi->to.map,
2900 							bi->to.sym);
2901 		} else {
2902 			nr_options += add_annotate_opt(browser,
2903 						       &actions[nr_options],
2904 						       &options[nr_options],
2905 						       browser->selection->map,
2906 						       browser->selection->sym);
2907 		}
2908 skip_annotation:
2909 		nr_options += add_thread_opt(browser, &actions[nr_options],
2910 					     &options[nr_options], thread);
2911 		nr_options += add_dso_opt(browser, &actions[nr_options],
2912 					  &options[nr_options], map);
2913 		nr_options += add_map_opt(browser, &actions[nr_options],
2914 					  &options[nr_options],
2915 					  browser->selection ?
2916 						browser->selection->map : NULL);
2917 		nr_options += add_socket_opt(browser, &actions[nr_options],
2918 					     &options[nr_options],
2919 					     socked_id);
2920 		/* perf script support */
2921 		if (!is_report_browser(hbt))
2922 			goto skip_scripting;
2923 
2924 		if (browser->he_selection) {
2925 			if (sort__has_thread && thread) {
2926 				nr_options += add_script_opt(browser,
2927 							     &actions[nr_options],
2928 							     &options[nr_options],
2929 							     thread, NULL);
2930 			}
2931 			/*
2932 			 * Note that browser->selection != NULL
2933 			 * when browser->he_selection is not NULL,
2934 			 * so we don't need to check browser->selection
2935 			 * before fetching browser->selection->sym like what
2936 			 * we do before fetching browser->selection->map.
2937 			 *
2938 			 * See hist_browser__show_entry.
2939 			 */
2940 			if (sort__has_sym && browser->selection->sym) {
2941 				nr_options += add_script_opt(browser,
2942 							     &actions[nr_options],
2943 							     &options[nr_options],
2944 							     NULL, browser->selection->sym);
2945 			}
2946 		}
2947 		nr_options += add_script_opt(browser, &actions[nr_options],
2948 					     &options[nr_options], NULL, NULL);
2949 		nr_options += add_switch_opt(browser, &actions[nr_options],
2950 					     &options[nr_options]);
2951 skip_scripting:
2952 		nr_options += add_exit_opt(browser, &actions[nr_options],
2953 					   &options[nr_options]);
2954 
2955 		do {
2956 			struct popup_action *act;
2957 
2958 			choice = ui__popup_menu(nr_options, options);
2959 			if (choice == -1 || choice >= nr_options)
2960 				break;
2961 
2962 			act = &actions[choice];
2963 			key = act->fn(browser, act);
2964 		} while (key == 1);
2965 
2966 		if (key == K_SWITCH_INPUT_DATA)
2967 			break;
2968 	}
2969 out_free_stack:
2970 	pstack__delete(browser->pstack);
2971 out:
2972 	hist_browser__delete(browser);
2973 	free_popup_options(options, MAX_OPTIONS);
2974 	return key;
2975 }
2976 
2977 struct perf_evsel_menu {
2978 	struct ui_browser b;
2979 	struct perf_evsel *selection;
2980 	bool lost_events, lost_events_warned;
2981 	float min_pcnt;
2982 	struct perf_env *env;
2983 };
2984 
2985 static void perf_evsel_menu__write(struct ui_browser *browser,
2986 				   void *entry, int row)
2987 {
2988 	struct perf_evsel_menu *menu = container_of(browser,
2989 						    struct perf_evsel_menu, b);
2990 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2991 	struct hists *hists = evsel__hists(evsel);
2992 	bool current_entry = ui_browser__is_current_entry(browser, row);
2993 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2994 	const char *ev_name = perf_evsel__name(evsel);
2995 	char bf[256], unit;
2996 	const char *warn = " ";
2997 	size_t printed;
2998 
2999 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3000 						       HE_COLORSET_NORMAL);
3001 
3002 	if (perf_evsel__is_group_event(evsel)) {
3003 		struct perf_evsel *pos;
3004 
3005 		ev_name = perf_evsel__group_name(evsel);
3006 
3007 		for_each_group_member(pos, evsel) {
3008 			struct hists *pos_hists = evsel__hists(pos);
3009 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3010 		}
3011 	}
3012 
3013 	nr_events = convert_unit(nr_events, &unit);
3014 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3015 			   unit, unit == ' ' ? "" : " ", ev_name);
3016 	ui_browser__printf(browser, "%s", bf);
3017 
3018 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3019 	if (nr_events != 0) {
3020 		menu->lost_events = true;
3021 		if (!current_entry)
3022 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3023 		nr_events = convert_unit(nr_events, &unit);
3024 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3025 				     nr_events, unit, unit == ' ' ? "" : " ");
3026 		warn = bf;
3027 	}
3028 
3029 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3030 
3031 	if (current_entry)
3032 		menu->selection = evsel;
3033 }
3034 
3035 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3036 				int nr_events, const char *help,
3037 				struct hist_browser_timer *hbt)
3038 {
3039 	struct perf_evlist *evlist = menu->b.priv;
3040 	struct perf_evsel *pos;
3041 	const char *title = "Available samples";
3042 	int delay_secs = hbt ? hbt->refresh : 0;
3043 	int key;
3044 
3045 	if (ui_browser__show(&menu->b, title,
3046 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3047 		return -1;
3048 
3049 	while (1) {
3050 		key = ui_browser__run(&menu->b, delay_secs);
3051 
3052 		switch (key) {
3053 		case K_TIMER:
3054 			hbt->timer(hbt->arg);
3055 
3056 			if (!menu->lost_events_warned && menu->lost_events) {
3057 				ui_browser__warn_lost_events(&menu->b);
3058 				menu->lost_events_warned = true;
3059 			}
3060 			continue;
3061 		case K_RIGHT:
3062 		case K_ENTER:
3063 			if (!menu->selection)
3064 				continue;
3065 			pos = menu->selection;
3066 browse_hists:
3067 			perf_evlist__set_selected(evlist, pos);
3068 			/*
3069 			 * Give the calling tool a chance to populate the non
3070 			 * default evsel resorted hists tree.
3071 			 */
3072 			if (hbt)
3073 				hbt->timer(hbt->arg);
3074 			key = perf_evsel__hists_browse(pos, nr_events, help,
3075 						       true, hbt,
3076 						       menu->min_pcnt,
3077 						       menu->env);
3078 			ui_browser__show_title(&menu->b, title);
3079 			switch (key) {
3080 			case K_TAB:
3081 				if (pos->node.next == &evlist->entries)
3082 					pos = perf_evlist__first(evlist);
3083 				else
3084 					pos = perf_evsel__next(pos);
3085 				goto browse_hists;
3086 			case K_UNTAB:
3087 				if (pos->node.prev == &evlist->entries)
3088 					pos = perf_evlist__last(evlist);
3089 				else
3090 					pos = perf_evsel__prev(pos);
3091 				goto browse_hists;
3092 			case K_SWITCH_INPUT_DATA:
3093 			case 'q':
3094 			case CTRL('c'):
3095 				goto out;
3096 			case K_ESC:
3097 			default:
3098 				continue;
3099 			}
3100 		case K_LEFT:
3101 			continue;
3102 		case K_ESC:
3103 			if (!ui_browser__dialog_yesno(&menu->b,
3104 					       "Do you really want to exit?"))
3105 				continue;
3106 			/* Fall thru */
3107 		case 'q':
3108 		case CTRL('c'):
3109 			goto out;
3110 		default:
3111 			continue;
3112 		}
3113 	}
3114 
3115 out:
3116 	ui_browser__hide(&menu->b);
3117 	return key;
3118 }
3119 
3120 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3121 				 void *entry)
3122 {
3123 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3124 
3125 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3126 		return true;
3127 
3128 	return false;
3129 }
3130 
3131 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3132 					   int nr_entries, const char *help,
3133 					   struct hist_browser_timer *hbt,
3134 					   float min_pcnt,
3135 					   struct perf_env *env)
3136 {
3137 	struct perf_evsel *pos;
3138 	struct perf_evsel_menu menu = {
3139 		.b = {
3140 			.entries    = &evlist->entries,
3141 			.refresh    = ui_browser__list_head_refresh,
3142 			.seek	    = ui_browser__list_head_seek,
3143 			.write	    = perf_evsel_menu__write,
3144 			.filter	    = filter_group_entries,
3145 			.nr_entries = nr_entries,
3146 			.priv	    = evlist,
3147 		},
3148 		.min_pcnt = min_pcnt,
3149 		.env = env,
3150 	};
3151 
3152 	ui_helpline__push("Press ESC to exit");
3153 
3154 	evlist__for_each(evlist, pos) {
3155 		const char *ev_name = perf_evsel__name(pos);
3156 		size_t line_len = strlen(ev_name) + 7;
3157 
3158 		if (menu.b.width < line_len)
3159 			menu.b.width = line_len;
3160 	}
3161 
3162 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3163 }
3164 
3165 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3166 				  struct hist_browser_timer *hbt,
3167 				  float min_pcnt,
3168 				  struct perf_env *env)
3169 {
3170 	int nr_entries = evlist->nr_entries;
3171 
3172 single_entry:
3173 	if (nr_entries == 1) {
3174 		struct perf_evsel *first = perf_evlist__first(evlist);
3175 
3176 		return perf_evsel__hists_browse(first, nr_entries, help,
3177 						false, hbt, min_pcnt,
3178 						env);
3179 	}
3180 
3181 	if (symbol_conf.event_group) {
3182 		struct perf_evsel *pos;
3183 
3184 		nr_entries = 0;
3185 		evlist__for_each(evlist, pos) {
3186 			if (perf_evsel__is_group_leader(pos))
3187 				nr_entries++;
3188 		}
3189 
3190 		if (nr_entries == 1)
3191 			goto single_entry;
3192 	}
3193 
3194 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3195 					       hbt, min_pcnt, env);
3196 }
3197