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