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