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