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