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