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