xref: /linux/tools/perf/ui/browsers/hists.c (revision 4949009eb8d40a441dcddcd96e101e77d31cf1b2)
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <linux/rbtree.h>
6 
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../util/top.h"
14 #include "../../arch/common.h"
15 
16 #include "../browser.h"
17 #include "../helpline.h"
18 #include "../util.h"
19 #include "../ui.h"
20 #include "map.h"
21 #include "annotate.h"
22 
23 struct hist_browser {
24 	struct ui_browser   b;
25 	struct hists	    *hists;
26 	struct hist_entry   *he_selection;
27 	struct map_symbol   *selection;
28 	int		     print_seq;
29 	bool		     show_dso;
30 	bool		     show_headers;
31 	float		     min_pcnt;
32 	u64		     nr_non_filtered_entries;
33 	u64		     nr_callchain_rows;
34 };
35 
36 extern void hist_browser__init_hpp(void);
37 
38 static int hists__browser_title(struct hists *hists,
39 				struct hist_browser_timer *hbt,
40 				char *bf, size_t size);
41 static void hist_browser__update_nr_entries(struct hist_browser *hb);
42 
43 static struct rb_node *hists__filter_entries(struct rb_node *nd,
44 					     float min_pcnt);
45 
46 static bool hist_browser__has_filter(struct hist_browser *hb)
47 {
48 	return hists__has_filter(hb->hists) || hb->min_pcnt;
49 }
50 
51 static u32 hist_browser__nr_entries(struct hist_browser *hb)
52 {
53 	u32 nr_entries;
54 
55 	if (hist_browser__has_filter(hb))
56 		nr_entries = hb->nr_non_filtered_entries;
57 	else
58 		nr_entries = hb->hists->nr_entries;
59 
60 	return nr_entries + hb->nr_callchain_rows;
61 }
62 
63 static void hist_browser__update_rows(struct hist_browser *hb)
64 {
65 	struct ui_browser *browser = &hb->b;
66 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
67 
68 	browser->rows = browser->height - header_offset;
69 	/*
70 	 * Verify if we were at the last line and that line isn't
71 	 * visibe because we now show the header line(s).
72 	 */
73 	index_row = browser->index - browser->top_idx;
74 	if (index_row >= browser->rows)
75 		browser->index -= index_row - browser->rows + 1;
76 }
77 
78 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
79 {
80 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
81 
82 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
83 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
84 	/*
85  	 * FIXME: Just keeping existing behaviour, but this really should be
86  	 *	  before updating browser->width, as it will invalidate the
87  	 *	  calculation above. Fix this and the fallout in another
88  	 *	  changeset.
89  	 */
90 	ui_browser__refresh_dimensions(browser);
91 	hist_browser__update_rows(hb);
92 }
93 
94 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
95 {
96 	u16 header_offset = browser->show_headers ? 1 : 0;
97 
98 	ui_browser__gotorc(&browser->b, row + header_offset, column);
99 }
100 
101 static void hist_browser__reset(struct hist_browser *browser)
102 {
103 	/*
104 	 * The hists__remove_entry_filter() already folds non-filtered
105 	 * entries so we can assume it has 0 callchain rows.
106 	 */
107 	browser->nr_callchain_rows = 0;
108 
109 	hist_browser__update_nr_entries(browser);
110 	browser->b.nr_entries = hist_browser__nr_entries(browser);
111 	hist_browser__refresh_dimensions(&browser->b);
112 	ui_browser__reset_index(&browser->b);
113 }
114 
115 static char tree__folded_sign(bool unfolded)
116 {
117 	return unfolded ? '-' : '+';
118 }
119 
120 static char map_symbol__folded(const struct map_symbol *ms)
121 {
122 	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
123 }
124 
125 static char hist_entry__folded(const struct hist_entry *he)
126 {
127 	return map_symbol__folded(&he->ms);
128 }
129 
130 static char callchain_list__folded(const struct callchain_list *cl)
131 {
132 	return map_symbol__folded(&cl->ms);
133 }
134 
135 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
136 {
137 	ms->unfolded = unfold ? ms->has_children : false;
138 }
139 
140 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
141 {
142 	int n = 0;
143 	struct rb_node *nd;
144 
145 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
146 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
147 		struct callchain_list *chain;
148 		char folded_sign = ' '; /* No children */
149 
150 		list_for_each_entry(chain, &child->val, list) {
151 			++n;
152 			/* We need this because we may not have children */
153 			folded_sign = callchain_list__folded(chain);
154 			if (folded_sign == '+')
155 				break;
156 		}
157 
158 		if (folded_sign == '-') /* Have children and they're unfolded */
159 			n += callchain_node__count_rows_rb_tree(child);
160 	}
161 
162 	return n;
163 }
164 
165 static int callchain_node__count_rows(struct callchain_node *node)
166 {
167 	struct callchain_list *chain;
168 	bool unfolded = false;
169 	int n = 0;
170 
171 	list_for_each_entry(chain, &node->val, list) {
172 		++n;
173 		unfolded = chain->ms.unfolded;
174 	}
175 
176 	if (unfolded)
177 		n += callchain_node__count_rows_rb_tree(node);
178 
179 	return n;
180 }
181 
182 static int callchain__count_rows(struct rb_root *chain)
183 {
184 	struct rb_node *nd;
185 	int n = 0;
186 
187 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
188 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
189 		n += callchain_node__count_rows(node);
190 	}
191 
192 	return n;
193 }
194 
195 static bool map_symbol__toggle_fold(struct map_symbol *ms)
196 {
197 	if (!ms)
198 		return false;
199 
200 	if (!ms->has_children)
201 		return false;
202 
203 	ms->unfolded = !ms->unfolded;
204 	return true;
205 }
206 
207 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
208 {
209 	struct rb_node *nd = rb_first(&node->rb_root);
210 
211 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
212 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
213 		struct callchain_list *chain;
214 		bool first = true;
215 
216 		list_for_each_entry(chain, &child->val, list) {
217 			if (first) {
218 				first = false;
219 				chain->ms.has_children = chain->list.next != &child->val ||
220 							 !RB_EMPTY_ROOT(&child->rb_root);
221 			} else
222 				chain->ms.has_children = chain->list.next == &child->val &&
223 							 !RB_EMPTY_ROOT(&child->rb_root);
224 		}
225 
226 		callchain_node__init_have_children_rb_tree(child);
227 	}
228 }
229 
230 static void callchain_node__init_have_children(struct callchain_node *node,
231 					       bool has_sibling)
232 {
233 	struct callchain_list *chain;
234 
235 	chain = list_entry(node->val.next, struct callchain_list, list);
236 	chain->ms.has_children = has_sibling;
237 
238 	if (!list_empty(&node->val)) {
239 		chain = list_entry(node->val.prev, struct callchain_list, list);
240 		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
241 	}
242 
243 	callchain_node__init_have_children_rb_tree(node);
244 }
245 
246 static void callchain__init_have_children(struct rb_root *root)
247 {
248 	struct rb_node *nd = rb_first(root);
249 	bool has_sibling = nd && rb_next(nd);
250 
251 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
252 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
253 		callchain_node__init_have_children(node, has_sibling);
254 	}
255 }
256 
257 static void hist_entry__init_have_children(struct hist_entry *he)
258 {
259 	if (!he->init_have_children) {
260 		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
261 		callchain__init_have_children(&he->sorted_chain);
262 		he->init_have_children = true;
263 	}
264 }
265 
266 static bool hist_browser__toggle_fold(struct hist_browser *browser)
267 {
268 	if (map_symbol__toggle_fold(browser->selection)) {
269 		struct hist_entry *he = browser->he_selection;
270 
271 		hist_entry__init_have_children(he);
272 		browser->b.nr_entries -= he->nr_rows;
273 		browser->nr_callchain_rows -= he->nr_rows;
274 
275 		if (he->ms.unfolded)
276 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
277 		else
278 			he->nr_rows = 0;
279 
280 		browser->b.nr_entries += he->nr_rows;
281 		browser->nr_callchain_rows += he->nr_rows;
282 
283 		return true;
284 	}
285 
286 	/* If it doesn't have children, no toggling performed */
287 	return false;
288 }
289 
290 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
291 {
292 	int n = 0;
293 	struct rb_node *nd;
294 
295 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
296 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
297 		struct callchain_list *chain;
298 		bool has_children = false;
299 
300 		list_for_each_entry(chain, &child->val, list) {
301 			++n;
302 			map_symbol__set_folding(&chain->ms, unfold);
303 			has_children = chain->ms.has_children;
304 		}
305 
306 		if (has_children)
307 			n += callchain_node__set_folding_rb_tree(child, unfold);
308 	}
309 
310 	return n;
311 }
312 
313 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
314 {
315 	struct callchain_list *chain;
316 	bool has_children = false;
317 	int n = 0;
318 
319 	list_for_each_entry(chain, &node->val, list) {
320 		++n;
321 		map_symbol__set_folding(&chain->ms, unfold);
322 		has_children = chain->ms.has_children;
323 	}
324 
325 	if (has_children)
326 		n += callchain_node__set_folding_rb_tree(node, unfold);
327 
328 	return n;
329 }
330 
331 static int callchain__set_folding(struct rb_root *chain, bool unfold)
332 {
333 	struct rb_node *nd;
334 	int n = 0;
335 
336 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
337 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
338 		n += callchain_node__set_folding(node, unfold);
339 	}
340 
341 	return n;
342 }
343 
344 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
345 {
346 	hist_entry__init_have_children(he);
347 	map_symbol__set_folding(&he->ms, unfold);
348 
349 	if (he->ms.has_children) {
350 		int n = callchain__set_folding(&he->sorted_chain, unfold);
351 		he->nr_rows = unfold ? n : 0;
352 	} else
353 		he->nr_rows = 0;
354 }
355 
356 static void
357 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
358 {
359 	struct rb_node *nd;
360 	struct hists *hists = browser->hists;
361 
362 	for (nd = rb_first(&hists->entries);
363 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
364 	     nd = rb_next(nd)) {
365 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
366 		hist_entry__set_folding(he, unfold);
367 		browser->nr_callchain_rows += he->nr_rows;
368 	}
369 }
370 
371 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
372 {
373 	browser->nr_callchain_rows = 0;
374 	__hist_browser__set_folding(browser, unfold);
375 
376 	browser->b.nr_entries = hist_browser__nr_entries(browser);
377 	/* Go to the start, we may be way after valid entries after a collapse */
378 	ui_browser__reset_index(&browser->b);
379 }
380 
381 static void ui_browser__warn_lost_events(struct ui_browser *browser)
382 {
383 	ui_browser__warning(browser, 4,
384 		"Events are being lost, check IO/CPU overload!\n\n"
385 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
386 		" perf top -r 80\n\n"
387 		"Or reduce the sampling frequency.");
388 }
389 
390 static int hist_browser__run(struct hist_browser *browser,
391 			     struct hist_browser_timer *hbt)
392 {
393 	int key;
394 	char title[160];
395 	int delay_secs = hbt ? hbt->refresh : 0;
396 
397 	browser->b.entries = &browser->hists->entries;
398 	browser->b.nr_entries = hist_browser__nr_entries(browser);
399 
400 	hists__browser_title(browser->hists, hbt, title, sizeof(title));
401 
402 	if (ui_browser__show(&browser->b, title,
403 			     "Press '?' for help on key bindings") < 0)
404 		return -1;
405 
406 	while (1) {
407 		key = ui_browser__run(&browser->b, delay_secs);
408 
409 		switch (key) {
410 		case K_TIMER: {
411 			u64 nr_entries;
412 			hbt->timer(hbt->arg);
413 
414 			if (hist_browser__has_filter(browser))
415 				hist_browser__update_nr_entries(browser);
416 
417 			nr_entries = hist_browser__nr_entries(browser);
418 			ui_browser__update_nr_entries(&browser->b, nr_entries);
419 
420 			if (browser->hists->stats.nr_lost_warned !=
421 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
422 				browser->hists->stats.nr_lost_warned =
423 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
424 				ui_browser__warn_lost_events(&browser->b);
425 			}
426 
427 			hists__browser_title(browser->hists,
428 					     hbt, title, sizeof(title));
429 			ui_browser__show_title(&browser->b, title);
430 			continue;
431 		}
432 		case 'D': { /* Debug */
433 			static int seq;
434 			struct hist_entry *h = rb_entry(browser->b.top,
435 							struct hist_entry, rb_node);
436 			ui_helpline__pop();
437 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
438 					   seq++, browser->b.nr_entries,
439 					   browser->hists->nr_entries,
440 					   browser->b.rows,
441 					   browser->b.index,
442 					   browser->b.top_idx,
443 					   h->row_offset, h->nr_rows);
444 		}
445 			break;
446 		case 'C':
447 			/* Collapse the whole world. */
448 			hist_browser__set_folding(browser, false);
449 			break;
450 		case 'E':
451 			/* Expand the whole world. */
452 			hist_browser__set_folding(browser, true);
453 			break;
454 		case 'H':
455 			browser->show_headers = !browser->show_headers;
456 			hist_browser__update_rows(browser);
457 			break;
458 		case K_ENTER:
459 			if (hist_browser__toggle_fold(browser))
460 				break;
461 			/* fall thru */
462 		default:
463 			goto out;
464 		}
465 	}
466 out:
467 	ui_browser__hide(&browser->b);
468 	return key;
469 }
470 
471 struct callchain_print_arg {
472 	/* for hists browser */
473 	off_t	row_offset;
474 	bool	is_current_entry;
475 
476 	/* for file dump */
477 	FILE	*fp;
478 	int	printed;
479 };
480 
481 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
482 					 struct callchain_list *chain,
483 					 const char *str, int offset,
484 					 unsigned short row,
485 					 struct callchain_print_arg *arg);
486 
487 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
488 					       struct callchain_list *chain,
489 					       const char *str, int offset,
490 					       unsigned short row,
491 					       struct callchain_print_arg *arg)
492 {
493 	int color, width;
494 	char folded_sign = callchain_list__folded(chain);
495 
496 	color = HE_COLORSET_NORMAL;
497 	width = browser->b.width - (offset + 2);
498 	if (ui_browser__is_current_entry(&browser->b, row)) {
499 		browser->selection = &chain->ms;
500 		color = HE_COLORSET_SELECTED;
501 		arg->is_current_entry = true;
502 	}
503 
504 	ui_browser__set_color(&browser->b, color);
505 	hist_browser__gotorc(browser, row, 0);
506 	slsmg_write_nstring(" ", offset);
507 	slsmg_printf("%c ", folded_sign);
508 	slsmg_write_nstring(str, width);
509 }
510 
511 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
512 						  struct callchain_list *chain,
513 						  const char *str, int offset,
514 						  unsigned short row __maybe_unused,
515 						  struct callchain_print_arg *arg)
516 {
517 	char folded_sign = callchain_list__folded(chain);
518 
519 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
520 				folded_sign, str);
521 }
522 
523 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
524 				     unsigned short row);
525 
526 static bool hist_browser__check_output_full(struct hist_browser *browser,
527 					    unsigned short row)
528 {
529 	return browser->b.rows == row;
530 }
531 
532 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
533 					  unsigned short row __maybe_unused)
534 {
535 	return false;
536 }
537 
538 #define LEVEL_OFFSET_STEP 3
539 
540 static int hist_browser__show_callchain(struct hist_browser *browser,
541 					struct rb_root *root, int level,
542 					unsigned short row, u64 total,
543 					print_callchain_entry_fn print,
544 					struct callchain_print_arg *arg,
545 					check_output_full_fn is_output_full)
546 {
547 	struct rb_node *node;
548 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
549 	u64 new_total;
550 	bool need_percent;
551 
552 	node = rb_first(root);
553 	need_percent = node && rb_next(node);
554 
555 	while (node) {
556 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
557 		struct rb_node *next = rb_next(node);
558 		u64 cumul = callchain_cumul_hits(child);
559 		struct callchain_list *chain;
560 		char folded_sign = ' ';
561 		int first = true;
562 		int extra_offset = 0;
563 
564 		list_for_each_entry(chain, &child->val, list) {
565 			char bf[1024], *alloc_str;
566 			const char *str;
567 			bool was_first = first;
568 
569 			if (first)
570 				first = false;
571 			else if (need_percent)
572 				extra_offset = LEVEL_OFFSET_STEP;
573 
574 			folded_sign = callchain_list__folded(chain);
575 			if (arg->row_offset != 0) {
576 				arg->row_offset--;
577 				goto do_next;
578 			}
579 
580 			alloc_str = NULL;
581 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
582 						       browser->show_dso);
583 
584 			if (was_first && need_percent) {
585 				double percent = cumul * 100.0 / total;
586 
587 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
588 					str = "Not enough memory!";
589 				else
590 					str = alloc_str;
591 			}
592 
593 			print(browser, chain, str, offset + extra_offset, row, arg);
594 
595 			free(alloc_str);
596 
597 			if (is_output_full(browser, ++row))
598 				goto out;
599 do_next:
600 			if (folded_sign == '+')
601 				break;
602 		}
603 
604 		if (folded_sign == '-') {
605 			const int new_level = level + (extra_offset ? 2 : 1);
606 
607 			if (callchain_param.mode == CHAIN_GRAPH_REL)
608 				new_total = child->children_hit;
609 			else
610 				new_total = total;
611 
612 			row += hist_browser__show_callchain(browser, &child->rb_root,
613 							    new_level, row, new_total,
614 							    print, arg, is_output_full);
615 		}
616 		if (is_output_full(browser, row))
617 			break;
618 		node = next;
619 	}
620 out:
621 	return row - first_row;
622 }
623 
624 struct hpp_arg {
625 	struct ui_browser *b;
626 	char folded_sign;
627 	bool current_entry;
628 };
629 
630 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
631 {
632 	struct hpp_arg *arg = hpp->ptr;
633 	int ret, len;
634 	va_list args;
635 	double percent;
636 
637 	va_start(args, fmt);
638 	len = va_arg(args, int);
639 	percent = va_arg(args, double);
640 	va_end(args);
641 
642 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
643 
644 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
645 	slsmg_printf("%s", hpp->buf);
646 
647 	advance_hpp(hpp, ret);
648 	return ret;
649 }
650 
651 #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
652 static u64 __hpp_get_##_field(struct hist_entry *he)			\
653 {									\
654 	return he->stat._field;						\
655 }									\
656 									\
657 static int								\
658 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
659 				struct perf_hpp *hpp,			\
660 				struct hist_entry *he)			\
661 {									\
662 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
663 			__hpp__slsmg_color_printf, true);		\
664 }
665 
666 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
667 static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
668 {									\
669 	return he->stat_acc->_field;					\
670 }									\
671 									\
672 static int								\
673 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
674 				struct perf_hpp *hpp,			\
675 				struct hist_entry *he)			\
676 {									\
677 	if (!symbol_conf.cumulate_callchain) {				\
678 		int len = fmt->user_len ?: fmt->len;			\
679 		int ret = scnprintf(hpp->buf, hpp->size,		\
680 				    "%*s", len, "N/A");			\
681 		slsmg_printf("%s", hpp->buf);				\
682 									\
683 		return ret;						\
684 	}								\
685 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
686 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
687 }
688 
689 __HPP_COLOR_PERCENT_FN(overhead, period)
690 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
691 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
692 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
693 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
694 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
695 
696 #undef __HPP_COLOR_PERCENT_FN
697 #undef __HPP_COLOR_ACC_PERCENT_FN
698 
699 void hist_browser__init_hpp(void)
700 {
701 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
702 				hist_browser__hpp_color_overhead;
703 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
704 				hist_browser__hpp_color_overhead_sys;
705 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
706 				hist_browser__hpp_color_overhead_us;
707 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
708 				hist_browser__hpp_color_overhead_guest_sys;
709 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
710 				hist_browser__hpp_color_overhead_guest_us;
711 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
712 				hist_browser__hpp_color_overhead_acc;
713 }
714 
715 static int hist_browser__show_entry(struct hist_browser *browser,
716 				    struct hist_entry *entry,
717 				    unsigned short row)
718 {
719 	char s[256];
720 	int printed = 0;
721 	int width = browser->b.width;
722 	char folded_sign = ' ';
723 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
724 	off_t row_offset = entry->row_offset;
725 	bool first = true;
726 	struct perf_hpp_fmt *fmt;
727 
728 	if (current_entry) {
729 		browser->he_selection = entry;
730 		browser->selection = &entry->ms;
731 	}
732 
733 	if (symbol_conf.use_callchain) {
734 		hist_entry__init_have_children(entry);
735 		folded_sign = hist_entry__folded(entry);
736 	}
737 
738 	if (row_offset == 0) {
739 		struct hpp_arg arg = {
740 			.b		= &browser->b,
741 			.folded_sign	= folded_sign,
742 			.current_entry	= current_entry,
743 		};
744 		struct perf_hpp hpp = {
745 			.buf		= s,
746 			.size		= sizeof(s),
747 			.ptr		= &arg,
748 		};
749 
750 		hist_browser__gotorc(browser, row, 0);
751 
752 		perf_hpp__for_each_format(fmt) {
753 			if (perf_hpp__should_skip(fmt))
754 				continue;
755 
756 			if (current_entry && browser->b.navkeypressed) {
757 				ui_browser__set_color(&browser->b,
758 						      HE_COLORSET_SELECTED);
759 			} else {
760 				ui_browser__set_color(&browser->b,
761 						      HE_COLORSET_NORMAL);
762 			}
763 
764 			if (first) {
765 				if (symbol_conf.use_callchain) {
766 					slsmg_printf("%c ", folded_sign);
767 					width -= 2;
768 				}
769 				first = false;
770 			} else {
771 				slsmg_printf("  ");
772 				width -= 2;
773 			}
774 
775 			if (fmt->color) {
776 				width -= fmt->color(fmt, &hpp, entry);
777 			} else {
778 				width -= fmt->entry(fmt, &hpp, entry);
779 				slsmg_printf("%s", s);
780 			}
781 		}
782 
783 		/* The scroll bar isn't being used */
784 		if (!browser->b.navkeypressed)
785 			width += 1;
786 
787 		slsmg_write_nstring("", width);
788 
789 		++row;
790 		++printed;
791 	} else
792 		--row_offset;
793 
794 	if (folded_sign == '-' && row != browser->b.rows) {
795 		u64 total = hists__total_period(entry->hists);
796 		struct callchain_print_arg arg = {
797 			.row_offset = row_offset,
798 			.is_current_entry = current_entry,
799 		};
800 
801 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
802 			if (symbol_conf.cumulate_callchain)
803 				total = entry->stat_acc->period;
804 			else
805 				total = entry->stat.period;
806 		}
807 
808 		printed += hist_browser__show_callchain(browser,
809 					&entry->sorted_chain, 1, row, total,
810 					hist_browser__show_callchain_entry, &arg,
811 					hist_browser__check_output_full);
812 
813 		if (arg.is_current_entry)
814 			browser->he_selection = entry;
815 	}
816 
817 	return printed;
818 }
819 
820 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
821 {
822 	advance_hpp(hpp, inc);
823 	return hpp->size <= 0;
824 }
825 
826 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
827 {
828 	struct perf_hpp dummy_hpp = {
829 		.buf    = buf,
830 		.size   = size,
831 	};
832 	struct perf_hpp_fmt *fmt;
833 	size_t ret = 0;
834 
835 	if (symbol_conf.use_callchain) {
836 		ret = scnprintf(buf, size, "  ");
837 		if (advance_hpp_check(&dummy_hpp, ret))
838 			return ret;
839 	}
840 
841 	perf_hpp__for_each_format(fmt) {
842 		if (perf_hpp__should_skip(fmt))
843 			continue;
844 
845 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
846 		if (advance_hpp_check(&dummy_hpp, ret))
847 			break;
848 
849 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
850 		if (advance_hpp_check(&dummy_hpp, ret))
851 			break;
852 	}
853 
854 	return ret;
855 }
856 
857 static void hist_browser__show_headers(struct hist_browser *browser)
858 {
859 	char headers[1024];
860 
861 	hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
862 	ui_browser__gotorc(&browser->b, 0, 0);
863 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
864 	slsmg_write_nstring(headers, browser->b.width + 1);
865 }
866 
867 static void ui_browser__hists_init_top(struct ui_browser *browser)
868 {
869 	if (browser->top == NULL) {
870 		struct hist_browser *hb;
871 
872 		hb = container_of(browser, struct hist_browser, b);
873 		browser->top = rb_first(&hb->hists->entries);
874 	}
875 }
876 
877 static unsigned int hist_browser__refresh(struct ui_browser *browser)
878 {
879 	unsigned row = 0;
880 	u16 header_offset = 0;
881 	struct rb_node *nd;
882 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
883 
884 	if (hb->show_headers) {
885 		hist_browser__show_headers(hb);
886 		header_offset = 1;
887 	}
888 
889 	ui_browser__hists_init_top(browser);
890 
891 	for (nd = browser->top; nd; nd = rb_next(nd)) {
892 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
893 		float percent;
894 
895 		if (h->filtered)
896 			continue;
897 
898 		percent = hist_entry__get_percent_limit(h);
899 		if (percent < hb->min_pcnt)
900 			continue;
901 
902 		row += hist_browser__show_entry(hb, h, row);
903 		if (row == browser->rows)
904 			break;
905 	}
906 
907 	return row + header_offset;
908 }
909 
910 static struct rb_node *hists__filter_entries(struct rb_node *nd,
911 					     float min_pcnt)
912 {
913 	while (nd != NULL) {
914 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
915 		float percent = hist_entry__get_percent_limit(h);
916 
917 		if (!h->filtered && percent >= min_pcnt)
918 			return nd;
919 
920 		nd = rb_next(nd);
921 	}
922 
923 	return NULL;
924 }
925 
926 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
927 						  float min_pcnt)
928 {
929 	while (nd != NULL) {
930 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
931 		float percent = hist_entry__get_percent_limit(h);
932 
933 		if (!h->filtered && percent >= min_pcnt)
934 			return nd;
935 
936 		nd = rb_prev(nd);
937 	}
938 
939 	return NULL;
940 }
941 
942 static void ui_browser__hists_seek(struct ui_browser *browser,
943 				   off_t offset, int whence)
944 {
945 	struct hist_entry *h;
946 	struct rb_node *nd;
947 	bool first = true;
948 	struct hist_browser *hb;
949 
950 	hb = container_of(browser, struct hist_browser, b);
951 
952 	if (browser->nr_entries == 0)
953 		return;
954 
955 	ui_browser__hists_init_top(browser);
956 
957 	switch (whence) {
958 	case SEEK_SET:
959 		nd = hists__filter_entries(rb_first(browser->entries),
960 					   hb->min_pcnt);
961 		break;
962 	case SEEK_CUR:
963 		nd = browser->top;
964 		goto do_offset;
965 	case SEEK_END:
966 		nd = hists__filter_prev_entries(rb_last(browser->entries),
967 						hb->min_pcnt);
968 		first = false;
969 		break;
970 	default:
971 		return;
972 	}
973 
974 	/*
975 	 * Moves not relative to the first visible entry invalidates its
976 	 * row_offset:
977 	 */
978 	h = rb_entry(browser->top, struct hist_entry, rb_node);
979 	h->row_offset = 0;
980 
981 	/*
982 	 * Here we have to check if nd is expanded (+), if it is we can't go
983 	 * the next top level hist_entry, instead we must compute an offset of
984 	 * what _not_ to show and not change the first visible entry.
985 	 *
986 	 * This offset increments when we are going from top to bottom and
987 	 * decreases when we're going from bottom to top.
988 	 *
989 	 * As we don't have backpointers to the top level in the callchains
990 	 * structure, we need to always print the whole hist_entry callchain,
991 	 * skipping the first ones that are before the first visible entry
992 	 * and stop when we printed enough lines to fill the screen.
993 	 */
994 do_offset:
995 	if (offset > 0) {
996 		do {
997 			h = rb_entry(nd, struct hist_entry, rb_node);
998 			if (h->ms.unfolded) {
999 				u16 remaining = h->nr_rows - h->row_offset;
1000 				if (offset > remaining) {
1001 					offset -= remaining;
1002 					h->row_offset = 0;
1003 				} else {
1004 					h->row_offset += offset;
1005 					offset = 0;
1006 					browser->top = nd;
1007 					break;
1008 				}
1009 			}
1010 			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1011 			if (nd == NULL)
1012 				break;
1013 			--offset;
1014 			browser->top = nd;
1015 		} while (offset != 0);
1016 	} else if (offset < 0) {
1017 		while (1) {
1018 			h = rb_entry(nd, struct hist_entry, rb_node);
1019 			if (h->ms.unfolded) {
1020 				if (first) {
1021 					if (-offset > h->row_offset) {
1022 						offset += h->row_offset;
1023 						h->row_offset = 0;
1024 					} else {
1025 						h->row_offset += offset;
1026 						offset = 0;
1027 						browser->top = nd;
1028 						break;
1029 					}
1030 				} else {
1031 					if (-offset > h->nr_rows) {
1032 						offset += h->nr_rows;
1033 						h->row_offset = 0;
1034 					} else {
1035 						h->row_offset = h->nr_rows + offset;
1036 						offset = 0;
1037 						browser->top = nd;
1038 						break;
1039 					}
1040 				}
1041 			}
1042 
1043 			nd = hists__filter_prev_entries(rb_prev(nd),
1044 							hb->min_pcnt);
1045 			if (nd == NULL)
1046 				break;
1047 			++offset;
1048 			browser->top = nd;
1049 			if (offset == 0) {
1050 				/*
1051 				 * Last unfiltered hist_entry, check if it is
1052 				 * unfolded, if it is then we should have
1053 				 * row_offset at its last entry.
1054 				 */
1055 				h = rb_entry(nd, struct hist_entry, rb_node);
1056 				if (h->ms.unfolded)
1057 					h->row_offset = h->nr_rows;
1058 				break;
1059 			}
1060 			first = false;
1061 		}
1062 	} else {
1063 		browser->top = nd;
1064 		h = rb_entry(nd, struct hist_entry, rb_node);
1065 		h->row_offset = 0;
1066 	}
1067 }
1068 
1069 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1070 					   struct hist_entry *he, FILE *fp)
1071 {
1072 	u64 total = hists__total_period(he->hists);
1073 	struct callchain_print_arg arg  = {
1074 		.fp = fp,
1075 	};
1076 
1077 	if (symbol_conf.cumulate_callchain)
1078 		total = he->stat_acc->period;
1079 
1080 	hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1081 				     hist_browser__fprintf_callchain_entry, &arg,
1082 				     hist_browser__check_dump_full);
1083 	return arg.printed;
1084 }
1085 
1086 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1087 				       struct hist_entry *he, FILE *fp)
1088 {
1089 	char s[8192];
1090 	int printed = 0;
1091 	char folded_sign = ' ';
1092 	struct perf_hpp hpp = {
1093 		.buf = s,
1094 		.size = sizeof(s),
1095 	};
1096 	struct perf_hpp_fmt *fmt;
1097 	bool first = true;
1098 	int ret;
1099 
1100 	if (symbol_conf.use_callchain)
1101 		folded_sign = hist_entry__folded(he);
1102 
1103 	if (symbol_conf.use_callchain)
1104 		printed += fprintf(fp, "%c ", folded_sign);
1105 
1106 	perf_hpp__for_each_format(fmt) {
1107 		if (perf_hpp__should_skip(fmt))
1108 			continue;
1109 
1110 		if (!first) {
1111 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1112 			advance_hpp(&hpp, ret);
1113 		} else
1114 			first = false;
1115 
1116 		ret = fmt->entry(fmt, &hpp, he);
1117 		advance_hpp(&hpp, ret);
1118 	}
1119 	printed += fprintf(fp, "%s\n", rtrim(s));
1120 
1121 	if (folded_sign == '-')
1122 		printed += hist_browser__fprintf_callchain(browser, he, fp);
1123 
1124 	return printed;
1125 }
1126 
1127 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1128 {
1129 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1130 						   browser->min_pcnt);
1131 	int printed = 0;
1132 
1133 	while (nd) {
1134 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1135 
1136 		printed += hist_browser__fprintf_entry(browser, h, fp);
1137 		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1138 	}
1139 
1140 	return printed;
1141 }
1142 
1143 static int hist_browser__dump(struct hist_browser *browser)
1144 {
1145 	char filename[64];
1146 	FILE *fp;
1147 
1148 	while (1) {
1149 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1150 		if (access(filename, F_OK))
1151 			break;
1152 		/*
1153  		 * XXX: Just an arbitrary lazy upper limit
1154  		 */
1155 		if (++browser->print_seq == 8192) {
1156 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1157 			return -1;
1158 		}
1159 	}
1160 
1161 	fp = fopen(filename, "w");
1162 	if (fp == NULL) {
1163 		char bf[64];
1164 		const char *err = strerror_r(errno, bf, sizeof(bf));
1165 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1166 		return -1;
1167 	}
1168 
1169 	++browser->print_seq;
1170 	hist_browser__fprintf(browser, fp);
1171 	fclose(fp);
1172 	ui_helpline__fpush("%s written!", filename);
1173 
1174 	return 0;
1175 }
1176 
1177 static struct hist_browser *hist_browser__new(struct hists *hists)
1178 {
1179 	struct hist_browser *browser = zalloc(sizeof(*browser));
1180 
1181 	if (browser) {
1182 		browser->hists = hists;
1183 		browser->b.refresh = hist_browser__refresh;
1184 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1185 		browser->b.seek = ui_browser__hists_seek;
1186 		browser->b.use_navkeypressed = true;
1187 		browser->show_headers = symbol_conf.show_hist_headers;
1188 	}
1189 
1190 	return browser;
1191 }
1192 
1193 static void hist_browser__delete(struct hist_browser *browser)
1194 {
1195 	free(browser);
1196 }
1197 
1198 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1199 {
1200 	return browser->he_selection;
1201 }
1202 
1203 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1204 {
1205 	return browser->he_selection->thread;
1206 }
1207 
1208 /* Check whether the browser is for 'top' or 'report' */
1209 static inline bool is_report_browser(void *timer)
1210 {
1211 	return timer == NULL;
1212 }
1213 
1214 static int hists__browser_title(struct hists *hists,
1215 				struct hist_browser_timer *hbt,
1216 				char *bf, size_t size)
1217 {
1218 	char unit;
1219 	int printed;
1220 	const struct dso *dso = hists->dso_filter;
1221 	const struct thread *thread = hists->thread_filter;
1222 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1223 	u64 nr_events = hists->stats.total_period;
1224 	struct perf_evsel *evsel = hists_to_evsel(hists);
1225 	const char *ev_name = perf_evsel__name(evsel);
1226 	char buf[512];
1227 	size_t buflen = sizeof(buf);
1228 
1229 	if (symbol_conf.filter_relative) {
1230 		nr_samples = hists->stats.nr_non_filtered_samples;
1231 		nr_events = hists->stats.total_non_filtered_period;
1232 	}
1233 
1234 	if (perf_evsel__is_group_event(evsel)) {
1235 		struct perf_evsel *pos;
1236 
1237 		perf_evsel__group_desc(evsel, buf, buflen);
1238 		ev_name = buf;
1239 
1240 		for_each_group_member(pos, evsel) {
1241 			struct hists *pos_hists = evsel__hists(pos);
1242 
1243 			if (symbol_conf.filter_relative) {
1244 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
1245 				nr_events += pos_hists->stats.total_non_filtered_period;
1246 			} else {
1247 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1248 				nr_events += pos_hists->stats.total_period;
1249 			}
1250 		}
1251 	}
1252 
1253 	nr_samples = convert_unit(nr_samples, &unit);
1254 	printed = scnprintf(bf, size,
1255 			   "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64,
1256 			   nr_samples, unit, ev_name, nr_events);
1257 
1258 
1259 	if (hists->uid_filter_str)
1260 		printed += snprintf(bf + printed, size - printed,
1261 				    ", UID: %s", hists->uid_filter_str);
1262 	if (thread)
1263 		printed += scnprintf(bf + printed, size - printed,
1264 				    ", Thread: %s(%d)",
1265 				     (thread->comm_set ? thread__comm_str(thread) : ""),
1266 				    thread->tid);
1267 	if (dso)
1268 		printed += scnprintf(bf + printed, size - printed,
1269 				    ", DSO: %s", dso->short_name);
1270 	if (!is_report_browser(hbt)) {
1271 		struct perf_top *top = hbt->arg;
1272 
1273 		if (top->zero)
1274 			printed += scnprintf(bf + printed, size - printed, " [z]");
1275 	}
1276 
1277 	return printed;
1278 }
1279 
1280 static inline void free_popup_options(char **options, int n)
1281 {
1282 	int i;
1283 
1284 	for (i = 0; i < n; ++i)
1285 		zfree(&options[i]);
1286 }
1287 
1288 /*
1289  * Only runtime switching of perf data file will make "input_name" point
1290  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1291  * whether we need to call free() for current "input_name" during the switch.
1292  */
1293 static bool is_input_name_malloced = false;
1294 
1295 static int switch_data_file(void)
1296 {
1297 	char *pwd, *options[32], *abs_path[32], *tmp;
1298 	DIR *pwd_dir;
1299 	int nr_options = 0, choice = -1, ret = -1;
1300 	struct dirent *dent;
1301 
1302 	pwd = getenv("PWD");
1303 	if (!pwd)
1304 		return ret;
1305 
1306 	pwd_dir = opendir(pwd);
1307 	if (!pwd_dir)
1308 		return ret;
1309 
1310 	memset(options, 0, sizeof(options));
1311 	memset(options, 0, sizeof(abs_path));
1312 
1313 	while ((dent = readdir(pwd_dir))) {
1314 		char path[PATH_MAX];
1315 		u64 magic;
1316 		char *name = dent->d_name;
1317 		FILE *file;
1318 
1319 		if (!(dent->d_type == DT_REG))
1320 			continue;
1321 
1322 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1323 
1324 		file = fopen(path, "r");
1325 		if (!file)
1326 			continue;
1327 
1328 		if (fread(&magic, 1, 8, file) < 8)
1329 			goto close_file_and_continue;
1330 
1331 		if (is_perf_magic(magic)) {
1332 			options[nr_options] = strdup(name);
1333 			if (!options[nr_options])
1334 				goto close_file_and_continue;
1335 
1336 			abs_path[nr_options] = strdup(path);
1337 			if (!abs_path[nr_options]) {
1338 				zfree(&options[nr_options]);
1339 				ui__warning("Can't search all data files due to memory shortage.\n");
1340 				fclose(file);
1341 				break;
1342 			}
1343 
1344 			nr_options++;
1345 		}
1346 
1347 close_file_and_continue:
1348 		fclose(file);
1349 		if (nr_options >= 32) {
1350 			ui__warning("Too many perf data files in PWD!\n"
1351 				    "Only the first 32 files will be listed.\n");
1352 			break;
1353 		}
1354 	}
1355 	closedir(pwd_dir);
1356 
1357 	if (nr_options) {
1358 		choice = ui__popup_menu(nr_options, options);
1359 		if (choice < nr_options && choice >= 0) {
1360 			tmp = strdup(abs_path[choice]);
1361 			if (tmp) {
1362 				if (is_input_name_malloced)
1363 					free((void *)input_name);
1364 				input_name = tmp;
1365 				is_input_name_malloced = true;
1366 				ret = 0;
1367 			} else
1368 				ui__warning("Data switch failed due to memory shortage!\n");
1369 		}
1370 	}
1371 
1372 	free_popup_options(options, nr_options);
1373 	free_popup_options(abs_path, nr_options);
1374 	return ret;
1375 }
1376 
1377 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1378 {
1379 	u64 nr_entries = 0;
1380 	struct rb_node *nd = rb_first(&hb->hists->entries);
1381 
1382 	if (hb->min_pcnt == 0) {
1383 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1384 		return;
1385 	}
1386 
1387 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1388 		nr_entries++;
1389 		nd = rb_next(nd);
1390 	}
1391 
1392 	hb->nr_non_filtered_entries = nr_entries;
1393 }
1394 
1395 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1396 				    const char *helpline,
1397 				    bool left_exits,
1398 				    struct hist_browser_timer *hbt,
1399 				    float min_pcnt,
1400 				    struct perf_session_env *env)
1401 {
1402 	struct hists *hists = evsel__hists(evsel);
1403 	struct hist_browser *browser = hist_browser__new(hists);
1404 	struct branch_info *bi;
1405 	struct pstack *fstack;
1406 	char *options[16];
1407 	int nr_options = 0;
1408 	int key = -1;
1409 	char buf[64];
1410 	char script_opt[64];
1411 	int delay_secs = hbt ? hbt->refresh : 0;
1412 	struct perf_hpp_fmt *fmt;
1413 
1414 #define HIST_BROWSER_HELP_COMMON					\
1415 	"h/?/F1        Show this window\n"				\
1416 	"UP/DOWN/PGUP\n"						\
1417 	"PGDN/SPACE    Navigate\n"					\
1418 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1419 	"For multiple event sessions:\n\n"				\
1420 	"TAB/UNTAB     Switch events\n\n"				\
1421 	"For symbolic views (--sort has sym):\n\n"			\
1422 	"->            Zoom into DSO/Threads & Annotate current symbol\n" \
1423 	"<-            Zoom out\n"					\
1424 	"a             Annotate current symbol\n"			\
1425 	"C             Collapse all callchains\n"			\
1426 	"d             Zoom into current DSO\n"				\
1427 	"E             Expand all callchains\n"				\
1428 	"F             Toggle percentage of filtered entries\n"		\
1429 	"H             Display column headers\n"			\
1430 
1431 	/* help messages are sorted by lexical order of the hotkey */
1432 	const char report_help[] = HIST_BROWSER_HELP_COMMON
1433 	"i             Show header information\n"
1434 	"P             Print histograms to perf.hist.N\n"
1435 	"r             Run available scripts\n"
1436 	"s             Switch to another data file in PWD\n"
1437 	"t             Zoom into current Thread\n"
1438 	"V             Verbose (DSO names in callchains, etc)\n"
1439 	"/             Filter symbol by name";
1440 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1441 	"P             Print histograms to perf.hist.N\n"
1442 	"t             Zoom into current Thread\n"
1443 	"V             Verbose (DSO names in callchains, etc)\n"
1444 	"z             Toggle zeroing of samples\n"
1445 	"/             Filter symbol by name";
1446 
1447 	if (browser == NULL)
1448 		return -1;
1449 
1450 	if (min_pcnt) {
1451 		browser->min_pcnt = min_pcnt;
1452 		hist_browser__update_nr_entries(browser);
1453 	}
1454 
1455 	fstack = pstack__new(2);
1456 	if (fstack == NULL)
1457 		goto out;
1458 
1459 	ui_helpline__push(helpline);
1460 
1461 	memset(options, 0, sizeof(options));
1462 
1463 	perf_hpp__for_each_format(fmt)
1464 		perf_hpp__reset_width(fmt, hists);
1465 
1466 	if (symbol_conf.col_width_list_str)
1467 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1468 
1469 	while (1) {
1470 		const struct thread *thread = NULL;
1471 		const struct dso *dso = NULL;
1472 		int choice = 0,
1473 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1474 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1475 		int scripts_comm = -2, scripts_symbol = -2,
1476 		    scripts_all = -2, switch_data = -2;
1477 
1478 		nr_options = 0;
1479 
1480 		key = hist_browser__run(browser, hbt);
1481 
1482 		if (browser->he_selection != NULL) {
1483 			thread = hist_browser__selected_thread(browser);
1484 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1485 		}
1486 		switch (key) {
1487 		case K_TAB:
1488 		case K_UNTAB:
1489 			if (nr_events == 1)
1490 				continue;
1491 			/*
1492 			 * Exit the browser, let hists__browser_tree
1493 			 * go to the next or previous
1494 			 */
1495 			goto out_free_stack;
1496 		case 'a':
1497 			if (!sort__has_sym) {
1498 				ui_browser__warning(&browser->b, delay_secs * 2,
1499 			"Annotation is only available for symbolic views, "
1500 			"include \"sym*\" in --sort to use it.");
1501 				continue;
1502 			}
1503 
1504 			if (browser->selection == NULL ||
1505 			    browser->selection->sym == NULL ||
1506 			    browser->selection->map->dso->annotate_warned)
1507 				continue;
1508 			goto do_annotate;
1509 		case 'P':
1510 			hist_browser__dump(browser);
1511 			continue;
1512 		case 'd':
1513 			goto zoom_dso;
1514 		case 'V':
1515 			browser->show_dso = !browser->show_dso;
1516 			continue;
1517 		case 't':
1518 			goto zoom_thread;
1519 		case '/':
1520 			if (ui_browser__input_window("Symbol to show",
1521 					"Please enter the name of symbol you want to see",
1522 					buf, "ENTER: OK, ESC: Cancel",
1523 					delay_secs * 2) == K_ENTER) {
1524 				hists->symbol_filter_str = *buf ? buf : NULL;
1525 				hists__filter_by_symbol(hists);
1526 				hist_browser__reset(browser);
1527 			}
1528 			continue;
1529 		case 'r':
1530 			if (is_report_browser(hbt))
1531 				goto do_scripts;
1532 			continue;
1533 		case 's':
1534 			if (is_report_browser(hbt))
1535 				goto do_data_switch;
1536 			continue;
1537 		case 'i':
1538 			/* env->arch is NULL for live-mode (i.e. perf top) */
1539 			if (env->arch)
1540 				tui__header_window(env);
1541 			continue;
1542 		case 'F':
1543 			symbol_conf.filter_relative ^= 1;
1544 			continue;
1545 		case 'z':
1546 			if (!is_report_browser(hbt)) {
1547 				struct perf_top *top = hbt->arg;
1548 
1549 				top->zero = !top->zero;
1550 			}
1551 			continue;
1552 		case K_F1:
1553 		case 'h':
1554 		case '?':
1555 			ui_browser__help_window(&browser->b,
1556 				is_report_browser(hbt) ? report_help : top_help);
1557 			continue;
1558 		case K_ENTER:
1559 		case K_RIGHT:
1560 			/* menu */
1561 			break;
1562 		case K_LEFT: {
1563 			const void *top;
1564 
1565 			if (pstack__empty(fstack)) {
1566 				/*
1567 				 * Go back to the perf_evsel_menu__run or other user
1568 				 */
1569 				if (left_exits)
1570 					goto out_free_stack;
1571 				continue;
1572 			}
1573 			top = pstack__pop(fstack);
1574 			if (top == &browser->hists->dso_filter)
1575 				goto zoom_out_dso;
1576 			if (top == &browser->hists->thread_filter)
1577 				goto zoom_out_thread;
1578 			continue;
1579 		}
1580 		case K_ESC:
1581 			if (!left_exits &&
1582 			    !ui_browser__dialog_yesno(&browser->b,
1583 					       "Do you really want to exit?"))
1584 				continue;
1585 			/* Fall thru */
1586 		case 'q':
1587 		case CTRL('c'):
1588 			goto out_free_stack;
1589 		default:
1590 			continue;
1591 		}
1592 
1593 		if (!sort__has_sym)
1594 			goto add_exit_option;
1595 
1596 		if (sort__mode == SORT_MODE__BRANCH) {
1597 			bi = browser->he_selection->branch_info;
1598 			if (browser->selection != NULL &&
1599 			    bi &&
1600 			    bi->from.sym != NULL &&
1601 			    !bi->from.map->dso->annotate_warned &&
1602 				asprintf(&options[nr_options], "Annotate %s",
1603 					 bi->from.sym->name) > 0)
1604 				annotate_f = nr_options++;
1605 
1606 			if (browser->selection != NULL &&
1607 			    bi &&
1608 			    bi->to.sym != NULL &&
1609 			    !bi->to.map->dso->annotate_warned &&
1610 			    (bi->to.sym != bi->from.sym ||
1611 			     bi->to.map->dso != bi->from.map->dso) &&
1612 				asprintf(&options[nr_options], "Annotate %s",
1613 					 bi->to.sym->name) > 0)
1614 				annotate_t = nr_options++;
1615 		} else {
1616 			if (browser->selection != NULL &&
1617 			    browser->selection->sym != NULL &&
1618 			    !browser->selection->map->dso->annotate_warned) {
1619 				struct annotation *notes;
1620 
1621 				notes = symbol__annotation(browser->selection->sym);
1622 
1623 				if (notes->src &&
1624 				    asprintf(&options[nr_options], "Annotate %s",
1625 						 browser->selection->sym->name) > 0)
1626 					annotate = nr_options++;
1627 			}
1628 		}
1629 
1630 		if (thread != NULL &&
1631 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1632 			     (browser->hists->thread_filter ? "out of" : "into"),
1633 			     (thread->comm_set ? thread__comm_str(thread) : ""),
1634 			     thread->tid) > 0)
1635 			zoom_thread = nr_options++;
1636 
1637 		if (dso != NULL &&
1638 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1639 			     (browser->hists->dso_filter ? "out of" : "into"),
1640 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1641 			zoom_dso = nr_options++;
1642 
1643 		if (browser->selection != NULL &&
1644 		    browser->selection->map != NULL &&
1645 		    asprintf(&options[nr_options], "Browse map details") > 0)
1646 			browse_map = nr_options++;
1647 
1648 		/* perf script support */
1649 		if (browser->he_selection) {
1650 			struct symbol *sym;
1651 
1652 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1653 				     thread__comm_str(browser->he_selection->thread)) > 0)
1654 				scripts_comm = nr_options++;
1655 
1656 			sym = browser->he_selection->ms.sym;
1657 			if (sym && sym->namelen &&
1658 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1659 						sym->name) > 0)
1660 				scripts_symbol = nr_options++;
1661 		}
1662 
1663 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1664 			scripts_all = nr_options++;
1665 
1666 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1667 				"Switch to another data file in PWD") > 0)
1668 			switch_data = nr_options++;
1669 add_exit_option:
1670 		options[nr_options++] = (char *)"Exit";
1671 retry_popup_menu:
1672 		choice = ui__popup_menu(nr_options, options);
1673 
1674 		if (choice == nr_options - 1)
1675 			break;
1676 
1677 		if (choice == -1) {
1678 			free_popup_options(options, nr_options - 1);
1679 			continue;
1680 		}
1681 
1682 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1683 			struct hist_entry *he;
1684 			struct annotation *notes;
1685 			int err;
1686 do_annotate:
1687 			if (!objdump_path && perf_session_env__lookup_objdump(env))
1688 				continue;
1689 
1690 			he = hist_browser__selected_entry(browser);
1691 			if (he == NULL)
1692 				continue;
1693 
1694 			/*
1695 			 * we stash the branch_info symbol + map into the
1696 			 * the ms so we don't have to rewrite all the annotation
1697 			 * code to use branch_info.
1698 			 * in branch mode, the ms struct is not used
1699 			 */
1700 			if (choice == annotate_f) {
1701 				he->ms.sym = he->branch_info->from.sym;
1702 				he->ms.map = he->branch_info->from.map;
1703 			}  else if (choice == annotate_t) {
1704 				he->ms.sym = he->branch_info->to.sym;
1705 				he->ms.map = he->branch_info->to.map;
1706 			}
1707 
1708 			notes = symbol__annotation(he->ms.sym);
1709 			if (!notes->src)
1710 				continue;
1711 
1712 			/*
1713 			 * Don't let this be freed, say, by hists__decay_entry.
1714 			 */
1715 			he->used = true;
1716 			err = hist_entry__tui_annotate(he, evsel, hbt);
1717 			he->used = false;
1718 			/*
1719 			 * offer option to annotate the other branch source or target
1720 			 * (if they exists) when returning from annotate
1721 			 */
1722 			if ((err == 'q' || err == CTRL('c'))
1723 			    && annotate_t != -2 && annotate_f != -2)
1724 				goto retry_popup_menu;
1725 
1726 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1727 			if (err)
1728 				ui_browser__handle_resize(&browser->b);
1729 
1730 		} else if (choice == browse_map)
1731 			map__browse(browser->selection->map);
1732 		else if (choice == zoom_dso) {
1733 zoom_dso:
1734 			if (browser->hists->dso_filter) {
1735 				pstack__remove(fstack, &browser->hists->dso_filter);
1736 zoom_out_dso:
1737 				ui_helpline__pop();
1738 				browser->hists->dso_filter = NULL;
1739 				perf_hpp__set_elide(HISTC_DSO, false);
1740 			} else {
1741 				if (dso == NULL)
1742 					continue;
1743 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1744 						   dso->kernel ? "the Kernel" : dso->short_name);
1745 				browser->hists->dso_filter = dso;
1746 				perf_hpp__set_elide(HISTC_DSO, true);
1747 				pstack__push(fstack, &browser->hists->dso_filter);
1748 			}
1749 			hists__filter_by_dso(hists);
1750 			hist_browser__reset(browser);
1751 		} else if (choice == zoom_thread) {
1752 zoom_thread:
1753 			if (browser->hists->thread_filter) {
1754 				pstack__remove(fstack, &browser->hists->thread_filter);
1755 zoom_out_thread:
1756 				ui_helpline__pop();
1757 				browser->hists->thread_filter = NULL;
1758 				perf_hpp__set_elide(HISTC_THREAD, false);
1759 			} else {
1760 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1761 						   thread->comm_set ? thread__comm_str(thread) : "",
1762 						   thread->tid);
1763 				browser->hists->thread_filter = thread;
1764 				perf_hpp__set_elide(HISTC_THREAD, false);
1765 				pstack__push(fstack, &browser->hists->thread_filter);
1766 			}
1767 			hists__filter_by_thread(hists);
1768 			hist_browser__reset(browser);
1769 		}
1770 		/* perf scripts support */
1771 		else if (choice == scripts_all || choice == scripts_comm ||
1772 				choice == scripts_symbol) {
1773 do_scripts:
1774 			memset(script_opt, 0, 64);
1775 
1776 			if (choice == scripts_comm)
1777 				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1778 
1779 			if (choice == scripts_symbol)
1780 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1781 
1782 			script_browse(script_opt);
1783 		}
1784 		/* Switch to another data file */
1785 		else if (choice == switch_data) {
1786 do_data_switch:
1787 			if (!switch_data_file()) {
1788 				key = K_SWITCH_INPUT_DATA;
1789 				break;
1790 			} else
1791 				ui__warning("Won't switch the data files due to\n"
1792 					"no valid data file get selected!\n");
1793 		}
1794 	}
1795 out_free_stack:
1796 	pstack__delete(fstack);
1797 out:
1798 	hist_browser__delete(browser);
1799 	free_popup_options(options, nr_options - 1);
1800 	return key;
1801 }
1802 
1803 struct perf_evsel_menu {
1804 	struct ui_browser b;
1805 	struct perf_evsel *selection;
1806 	bool lost_events, lost_events_warned;
1807 	float min_pcnt;
1808 	struct perf_session_env *env;
1809 };
1810 
1811 static void perf_evsel_menu__write(struct ui_browser *browser,
1812 				   void *entry, int row)
1813 {
1814 	struct perf_evsel_menu *menu = container_of(browser,
1815 						    struct perf_evsel_menu, b);
1816 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1817 	struct hists *hists = evsel__hists(evsel);
1818 	bool current_entry = ui_browser__is_current_entry(browser, row);
1819 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1820 	const char *ev_name = perf_evsel__name(evsel);
1821 	char bf[256], unit;
1822 	const char *warn = " ";
1823 	size_t printed;
1824 
1825 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1826 						       HE_COLORSET_NORMAL);
1827 
1828 	if (perf_evsel__is_group_event(evsel)) {
1829 		struct perf_evsel *pos;
1830 
1831 		ev_name = perf_evsel__group_name(evsel);
1832 
1833 		for_each_group_member(pos, evsel) {
1834 			struct hists *pos_hists = evsel__hists(pos);
1835 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1836 		}
1837 	}
1838 
1839 	nr_events = convert_unit(nr_events, &unit);
1840 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1841 			   unit, unit == ' ' ? "" : " ", ev_name);
1842 	slsmg_printf("%s", bf);
1843 
1844 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
1845 	if (nr_events != 0) {
1846 		menu->lost_events = true;
1847 		if (!current_entry)
1848 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1849 		nr_events = convert_unit(nr_events, &unit);
1850 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1851 				     nr_events, unit, unit == ' ' ? "" : " ");
1852 		warn = bf;
1853 	}
1854 
1855 	slsmg_write_nstring(warn, browser->width - printed);
1856 
1857 	if (current_entry)
1858 		menu->selection = evsel;
1859 }
1860 
1861 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1862 				int nr_events, const char *help,
1863 				struct hist_browser_timer *hbt)
1864 {
1865 	struct perf_evlist *evlist = menu->b.priv;
1866 	struct perf_evsel *pos;
1867 	const char *title = "Available samples";
1868 	int delay_secs = hbt ? hbt->refresh : 0;
1869 	int key;
1870 
1871 	if (ui_browser__show(&menu->b, title,
1872 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1873 		return -1;
1874 
1875 	while (1) {
1876 		key = ui_browser__run(&menu->b, delay_secs);
1877 
1878 		switch (key) {
1879 		case K_TIMER:
1880 			hbt->timer(hbt->arg);
1881 
1882 			if (!menu->lost_events_warned && menu->lost_events) {
1883 				ui_browser__warn_lost_events(&menu->b);
1884 				menu->lost_events_warned = true;
1885 			}
1886 			continue;
1887 		case K_RIGHT:
1888 		case K_ENTER:
1889 			if (!menu->selection)
1890 				continue;
1891 			pos = menu->selection;
1892 browse_hists:
1893 			perf_evlist__set_selected(evlist, pos);
1894 			/*
1895 			 * Give the calling tool a chance to populate the non
1896 			 * default evsel resorted hists tree.
1897 			 */
1898 			if (hbt)
1899 				hbt->timer(hbt->arg);
1900 			key = perf_evsel__hists_browse(pos, nr_events, help,
1901 						       true, hbt,
1902 						       menu->min_pcnt,
1903 						       menu->env);
1904 			ui_browser__show_title(&menu->b, title);
1905 			switch (key) {
1906 			case K_TAB:
1907 				if (pos->node.next == &evlist->entries)
1908 					pos = perf_evlist__first(evlist);
1909 				else
1910 					pos = perf_evsel__next(pos);
1911 				goto browse_hists;
1912 			case K_UNTAB:
1913 				if (pos->node.prev == &evlist->entries)
1914 					pos = perf_evlist__last(evlist);
1915 				else
1916 					pos = perf_evsel__prev(pos);
1917 				goto browse_hists;
1918 			case K_ESC:
1919 				if (!ui_browser__dialog_yesno(&menu->b,
1920 						"Do you really want to exit?"))
1921 					continue;
1922 				/* Fall thru */
1923 			case K_SWITCH_INPUT_DATA:
1924 			case 'q':
1925 			case CTRL('c'):
1926 				goto out;
1927 			default:
1928 				continue;
1929 			}
1930 		case K_LEFT:
1931 			continue;
1932 		case K_ESC:
1933 			if (!ui_browser__dialog_yesno(&menu->b,
1934 					       "Do you really want to exit?"))
1935 				continue;
1936 			/* Fall thru */
1937 		case 'q':
1938 		case CTRL('c'):
1939 			goto out;
1940 		default:
1941 			continue;
1942 		}
1943 	}
1944 
1945 out:
1946 	ui_browser__hide(&menu->b);
1947 	return key;
1948 }
1949 
1950 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1951 				 void *entry)
1952 {
1953 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1954 
1955 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1956 		return true;
1957 
1958 	return false;
1959 }
1960 
1961 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1962 					   int nr_entries, const char *help,
1963 					   struct hist_browser_timer *hbt,
1964 					   float min_pcnt,
1965 					   struct perf_session_env *env)
1966 {
1967 	struct perf_evsel *pos;
1968 	struct perf_evsel_menu menu = {
1969 		.b = {
1970 			.entries    = &evlist->entries,
1971 			.refresh    = ui_browser__list_head_refresh,
1972 			.seek	    = ui_browser__list_head_seek,
1973 			.write	    = perf_evsel_menu__write,
1974 			.filter	    = filter_group_entries,
1975 			.nr_entries = nr_entries,
1976 			.priv	    = evlist,
1977 		},
1978 		.min_pcnt = min_pcnt,
1979 		.env = env,
1980 	};
1981 
1982 	ui_helpline__push("Press ESC to exit");
1983 
1984 	evlist__for_each(evlist, pos) {
1985 		const char *ev_name = perf_evsel__name(pos);
1986 		size_t line_len = strlen(ev_name) + 7;
1987 
1988 		if (menu.b.width < line_len)
1989 			menu.b.width = line_len;
1990 	}
1991 
1992 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1993 }
1994 
1995 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1996 				  struct hist_browser_timer *hbt,
1997 				  float min_pcnt,
1998 				  struct perf_session_env *env)
1999 {
2000 	int nr_entries = evlist->nr_entries;
2001 
2002 single_entry:
2003 	if (nr_entries == 1) {
2004 		struct perf_evsel *first = perf_evlist__first(evlist);
2005 
2006 		return perf_evsel__hists_browse(first, nr_entries, help,
2007 						false, hbt, min_pcnt,
2008 						env);
2009 	}
2010 
2011 	if (symbol_conf.event_group) {
2012 		struct perf_evsel *pos;
2013 
2014 		nr_entries = 0;
2015 		evlist__for_each(evlist, pos) {
2016 			if (perf_evsel__is_group_leader(pos))
2017 				nr_entries++;
2018 		}
2019 
2020 		if (nr_entries == 1)
2021 			goto single_entry;
2022 	}
2023 
2024 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2025 					       hbt, min_pcnt, env);
2026 }
2027