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