xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 5f32c314)
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__color_callchain(struct hpp_arg *arg)
591 {
592 	if (!symbol_conf.use_callchain)
593 		return 0;
594 
595 	slsmg_printf("%c ", arg->folded_sign);
596 	return 2;
597 }
598 
599 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
600 			    u64 (*get_field)(struct hist_entry *),
601 			    int (*callchain_cb)(struct hpp_arg *))
602 {
603 	int ret = 0;
604 	double percent = 0.0;
605 	struct hists *hists = he->hists;
606 	struct hpp_arg *arg = hpp->ptr;
607 
608 	if (hists->stats.total_period)
609 		percent = 100.0 * get_field(he) / hists->stats.total_period;
610 
611 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
612 
613 	if (callchain_cb)
614 		ret += callchain_cb(arg);
615 
616 	ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
617 	slsmg_printf("%s", hpp->buf);
618 
619 	if (symbol_conf.event_group) {
620 		int prev_idx, idx_delta;
621 		struct perf_evsel *evsel = hists_to_evsel(hists);
622 		struct hist_entry *pair;
623 		int nr_members = evsel->nr_members;
624 
625 		if (nr_members <= 1)
626 			goto out;
627 
628 		prev_idx = perf_evsel__group_idx(evsel);
629 
630 		list_for_each_entry(pair, &he->pairs.head, pairs.node) {
631 			u64 period = get_field(pair);
632 			u64 total = pair->hists->stats.total_period;
633 
634 			if (!total)
635 				continue;
636 
637 			evsel = hists_to_evsel(pair->hists);
638 			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
639 
640 			while (idx_delta--) {
641 				/*
642 				 * zero-fill group members in the middle which
643 				 * have no sample
644 				 */
645 				ui_browser__set_percent_color(arg->b, 0.0,
646 							arg->current_entry);
647 				ret += scnprintf(hpp->buf, hpp->size,
648 						 " %6.2f%%", 0.0);
649 				slsmg_printf("%s", hpp->buf);
650 			}
651 
652 			percent = 100.0 * period / total;
653 			ui_browser__set_percent_color(arg->b, percent,
654 						      arg->current_entry);
655 			ret += scnprintf(hpp->buf, hpp->size,
656 					 " %6.2f%%", percent);
657 			slsmg_printf("%s", hpp->buf);
658 
659 			prev_idx = perf_evsel__group_idx(evsel);
660 		}
661 
662 		idx_delta = nr_members - prev_idx - 1;
663 
664 		while (idx_delta--) {
665 			/*
666 			 * zero-fill group members at last which have no sample
667 			 */
668 			ui_browser__set_percent_color(arg->b, 0.0,
669 						      arg->current_entry);
670 			ret += scnprintf(hpp->buf, hpp->size,
671 					 " %6.2f%%", 0.0);
672 			slsmg_printf("%s", hpp->buf);
673 		}
674 	}
675 out:
676 	if (!arg->current_entry || !arg->b->navkeypressed)
677 		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
678 
679 	return ret;
680 }
681 
682 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)			\
683 static u64 __hpp_get_##_field(struct hist_entry *he)			\
684 {									\
685 	return he->stat._field;						\
686 }									\
687 									\
688 static int								\
689 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
690 				struct perf_hpp *hpp,			\
691 				struct hist_entry *he)			\
692 {									\
693 	return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);	\
694 }
695 
696 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
697 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
698 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
699 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
700 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
701 
702 #undef __HPP_COLOR_PERCENT_FN
703 
704 void hist_browser__init_hpp(void)
705 {
706 	perf_hpp__init();
707 
708 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
709 				hist_browser__hpp_color_overhead;
710 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
711 				hist_browser__hpp_color_overhead_sys;
712 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
713 				hist_browser__hpp_color_overhead_us;
714 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
715 				hist_browser__hpp_color_overhead_guest_sys;
716 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
717 				hist_browser__hpp_color_overhead_guest_us;
718 }
719 
720 static int hist_browser__show_entry(struct hist_browser *browser,
721 				    struct hist_entry *entry,
722 				    unsigned short row)
723 {
724 	char s[256];
725 	int printed = 0;
726 	int width = browser->b.width;
727 	char folded_sign = ' ';
728 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
729 	off_t row_offset = entry->row_offset;
730 	bool first = true;
731 	struct perf_hpp_fmt *fmt;
732 
733 	if (current_entry) {
734 		browser->he_selection = entry;
735 		browser->selection = &entry->ms;
736 	}
737 
738 	if (symbol_conf.use_callchain) {
739 		hist_entry__init_have_children(entry);
740 		folded_sign = hist_entry__folded(entry);
741 	}
742 
743 	if (row_offset == 0) {
744 		struct hpp_arg arg = {
745 			.b 		= &browser->b,
746 			.folded_sign	= folded_sign,
747 			.current_entry	= current_entry,
748 		};
749 		struct perf_hpp hpp = {
750 			.buf		= s,
751 			.size		= sizeof(s),
752 			.ptr		= &arg,
753 		};
754 
755 		ui_browser__gotorc(&browser->b, row, 0);
756 
757 		perf_hpp__for_each_format(fmt) {
758 			if (!first) {
759 				slsmg_printf("  ");
760 				width -= 2;
761 			}
762 			first = false;
763 
764 			if (fmt->color) {
765 				width -= fmt->color(fmt, &hpp, entry);
766 			} else {
767 				width -= fmt->entry(fmt, &hpp, entry);
768 				slsmg_printf("%s", s);
769 			}
770 		}
771 
772 		/* The scroll bar isn't being used */
773 		if (!browser->b.navkeypressed)
774 			width += 1;
775 
776 		hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
777 		slsmg_write_nstring(s, width);
778 		++row;
779 		++printed;
780 	} else
781 		--row_offset;
782 
783 	if (folded_sign == '-' && row != browser->b.height) {
784 		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
785 							1, row, &row_offset,
786 							&current_entry);
787 		if (current_entry)
788 			browser->he_selection = entry;
789 	}
790 
791 	return printed;
792 }
793 
794 static void ui_browser__hists_init_top(struct ui_browser *browser)
795 {
796 	if (browser->top == NULL) {
797 		struct hist_browser *hb;
798 
799 		hb = container_of(browser, struct hist_browser, b);
800 		browser->top = rb_first(&hb->hists->entries);
801 	}
802 }
803 
804 static unsigned int hist_browser__refresh(struct ui_browser *browser)
805 {
806 	unsigned row = 0;
807 	struct rb_node *nd;
808 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
809 
810 	ui_browser__hists_init_top(browser);
811 
812 	for (nd = browser->top; nd; nd = rb_next(nd)) {
813 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
814 		float percent = h->stat.period * 100.0 /
815 					hb->hists->stats.total_period;
816 
817 		if (h->filtered)
818 			continue;
819 
820 		if (percent < hb->min_pcnt)
821 			continue;
822 
823 		row += hist_browser__show_entry(hb, h, row);
824 		if (row == browser->height)
825 			break;
826 	}
827 
828 	return row;
829 }
830 
831 static struct rb_node *hists__filter_entries(struct rb_node *nd,
832 					     struct hists *hists,
833 					     float min_pcnt)
834 {
835 	while (nd != NULL) {
836 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
837 		float percent = h->stat.period * 100.0 /
838 					hists->stats.total_period;
839 
840 		if (percent < min_pcnt)
841 			return NULL;
842 
843 		if (!h->filtered)
844 			return nd;
845 
846 		nd = rb_next(nd);
847 	}
848 
849 	return NULL;
850 }
851 
852 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
853 						  struct hists *hists,
854 						  float min_pcnt)
855 {
856 	while (nd != NULL) {
857 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
858 		float percent = h->stat.period * 100.0 /
859 					hists->stats.total_period;
860 
861 		if (!h->filtered && percent >= min_pcnt)
862 			return nd;
863 
864 		nd = rb_prev(nd);
865 	}
866 
867 	return NULL;
868 }
869 
870 static void ui_browser__hists_seek(struct ui_browser *browser,
871 				   off_t offset, int whence)
872 {
873 	struct hist_entry *h;
874 	struct rb_node *nd;
875 	bool first = true;
876 	struct hist_browser *hb;
877 
878 	hb = container_of(browser, struct hist_browser, b);
879 
880 	if (browser->nr_entries == 0)
881 		return;
882 
883 	ui_browser__hists_init_top(browser);
884 
885 	switch (whence) {
886 	case SEEK_SET:
887 		nd = hists__filter_entries(rb_first(browser->entries),
888 					   hb->hists, hb->min_pcnt);
889 		break;
890 	case SEEK_CUR:
891 		nd = browser->top;
892 		goto do_offset;
893 	case SEEK_END:
894 		nd = hists__filter_prev_entries(rb_last(browser->entries),
895 						hb->hists, hb->min_pcnt);
896 		first = false;
897 		break;
898 	default:
899 		return;
900 	}
901 
902 	/*
903 	 * Moves not relative to the first visible entry invalidates its
904 	 * row_offset:
905 	 */
906 	h = rb_entry(browser->top, struct hist_entry, rb_node);
907 	h->row_offset = 0;
908 
909 	/*
910 	 * Here we have to check if nd is expanded (+), if it is we can't go
911 	 * the next top level hist_entry, instead we must compute an offset of
912 	 * what _not_ to show and not change the first visible entry.
913 	 *
914 	 * This offset increments when we are going from top to bottom and
915 	 * decreases when we're going from bottom to top.
916 	 *
917 	 * As we don't have backpointers to the top level in the callchains
918 	 * structure, we need to always print the whole hist_entry callchain,
919 	 * skipping the first ones that are before the first visible entry
920 	 * and stop when we printed enough lines to fill the screen.
921 	 */
922 do_offset:
923 	if (offset > 0) {
924 		do {
925 			h = rb_entry(nd, struct hist_entry, rb_node);
926 			if (h->ms.unfolded) {
927 				u16 remaining = h->nr_rows - h->row_offset;
928 				if (offset > remaining) {
929 					offset -= remaining;
930 					h->row_offset = 0;
931 				} else {
932 					h->row_offset += offset;
933 					offset = 0;
934 					browser->top = nd;
935 					break;
936 				}
937 			}
938 			nd = hists__filter_entries(rb_next(nd), hb->hists,
939 						   hb->min_pcnt);
940 			if (nd == NULL)
941 				break;
942 			--offset;
943 			browser->top = nd;
944 		} while (offset != 0);
945 	} else if (offset < 0) {
946 		while (1) {
947 			h = rb_entry(nd, struct hist_entry, rb_node);
948 			if (h->ms.unfolded) {
949 				if (first) {
950 					if (-offset > h->row_offset) {
951 						offset += h->row_offset;
952 						h->row_offset = 0;
953 					} else {
954 						h->row_offset += offset;
955 						offset = 0;
956 						browser->top = nd;
957 						break;
958 					}
959 				} else {
960 					if (-offset > h->nr_rows) {
961 						offset += h->nr_rows;
962 						h->row_offset = 0;
963 					} else {
964 						h->row_offset = h->nr_rows + offset;
965 						offset = 0;
966 						browser->top = nd;
967 						break;
968 					}
969 				}
970 			}
971 
972 			nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
973 							hb->min_pcnt);
974 			if (nd == NULL)
975 				break;
976 			++offset;
977 			browser->top = nd;
978 			if (offset == 0) {
979 				/*
980 				 * Last unfiltered hist_entry, check if it is
981 				 * unfolded, if it is then we should have
982 				 * row_offset at its last entry.
983 				 */
984 				h = rb_entry(nd, struct hist_entry, rb_node);
985 				if (h->ms.unfolded)
986 					h->row_offset = h->nr_rows;
987 				break;
988 			}
989 			first = false;
990 		}
991 	} else {
992 		browser->top = nd;
993 		h = rb_entry(nd, struct hist_entry, rb_node);
994 		h->row_offset = 0;
995 	}
996 }
997 
998 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
999 							struct callchain_node *chain_node,
1000 							u64 total, int level,
1001 							FILE *fp)
1002 {
1003 	struct rb_node *node;
1004 	int offset = level * LEVEL_OFFSET_STEP;
1005 	u64 new_total, remaining;
1006 	int printed = 0;
1007 
1008 	if (callchain_param.mode == CHAIN_GRAPH_REL)
1009 		new_total = chain_node->children_hit;
1010 	else
1011 		new_total = total;
1012 
1013 	remaining = new_total;
1014 	node = rb_first(&chain_node->rb_root);
1015 	while (node) {
1016 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1017 		struct rb_node *next = rb_next(node);
1018 		u64 cumul = callchain_cumul_hits(child);
1019 		struct callchain_list *chain;
1020 		char folded_sign = ' ';
1021 		int first = true;
1022 		int extra_offset = 0;
1023 
1024 		remaining -= cumul;
1025 
1026 		list_for_each_entry(chain, &child->val, list) {
1027 			char bf[1024], *alloc_str;
1028 			const char *str;
1029 			bool was_first = first;
1030 
1031 			if (first)
1032 				first = false;
1033 			else
1034 				extra_offset = LEVEL_OFFSET_STEP;
1035 
1036 			folded_sign = callchain_list__folded(chain);
1037 
1038 			alloc_str = NULL;
1039 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
1040 						       browser->show_dso);
1041 			if (was_first) {
1042 				double percent = cumul * 100.0 / new_total;
1043 
1044 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1045 					str = "Not enough memory!";
1046 				else
1047 					str = alloc_str;
1048 			}
1049 
1050 			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1051 			free(alloc_str);
1052 			if (folded_sign == '+')
1053 				break;
1054 		}
1055 
1056 		if (folded_sign == '-') {
1057 			const int new_level = level + (extra_offset ? 2 : 1);
1058 			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1059 										new_level, fp);
1060 		}
1061 
1062 		node = next;
1063 	}
1064 
1065 	return printed;
1066 }
1067 
1068 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1069 						struct callchain_node *node,
1070 						int level, FILE *fp)
1071 {
1072 	struct callchain_list *chain;
1073 	int offset = level * LEVEL_OFFSET_STEP;
1074 	char folded_sign = ' ';
1075 	int printed = 0;
1076 
1077 	list_for_each_entry(chain, &node->val, list) {
1078 		char bf[1024], *s;
1079 
1080 		folded_sign = callchain_list__folded(chain);
1081 		s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1082 		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1083 	}
1084 
1085 	if (folded_sign == '-')
1086 		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1087 									browser->hists->stats.total_period,
1088 									level + 1,  fp);
1089 	return printed;
1090 }
1091 
1092 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1093 					   struct rb_root *chain, int level, FILE *fp)
1094 {
1095 	struct rb_node *nd;
1096 	int printed = 0;
1097 
1098 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1099 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1100 
1101 		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1102 	}
1103 
1104 	return printed;
1105 }
1106 
1107 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1108 				       struct hist_entry *he, FILE *fp)
1109 {
1110 	char s[8192];
1111 	double percent;
1112 	int printed = 0;
1113 	char folded_sign = ' ';
1114 
1115 	if (symbol_conf.use_callchain)
1116 		folded_sign = hist_entry__folded(he);
1117 
1118 	hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1119 	percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1120 
1121 	if (symbol_conf.use_callchain)
1122 		printed += fprintf(fp, "%c ", folded_sign);
1123 
1124 	printed += fprintf(fp, " %5.2f%%", percent);
1125 
1126 	if (symbol_conf.show_nr_samples)
1127 		printed += fprintf(fp, " %11u", he->stat.nr_events);
1128 
1129 	if (symbol_conf.show_total_period)
1130 		printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1131 
1132 	printed += fprintf(fp, "%s\n", rtrim(s));
1133 
1134 	if (folded_sign == '-')
1135 		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1136 
1137 	return printed;
1138 }
1139 
1140 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1141 {
1142 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1143 						   browser->hists,
1144 						   browser->min_pcnt);
1145 	int printed = 0;
1146 
1147 	while (nd) {
1148 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1149 
1150 		printed += hist_browser__fprintf_entry(browser, h, fp);
1151 		nd = hists__filter_entries(rb_next(nd), browser->hists,
1152 					   browser->min_pcnt);
1153 	}
1154 
1155 	return printed;
1156 }
1157 
1158 static int hist_browser__dump(struct hist_browser *browser)
1159 {
1160 	char filename[64];
1161 	FILE *fp;
1162 
1163 	while (1) {
1164 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1165 		if (access(filename, F_OK))
1166 			break;
1167 		/*
1168  		 * XXX: Just an arbitrary lazy upper limit
1169  		 */
1170 		if (++browser->print_seq == 8192) {
1171 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1172 			return -1;
1173 		}
1174 	}
1175 
1176 	fp = fopen(filename, "w");
1177 	if (fp == NULL) {
1178 		char bf[64];
1179 		const char *err = strerror_r(errno, bf, sizeof(bf));
1180 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1181 		return -1;
1182 	}
1183 
1184 	++browser->print_seq;
1185 	hist_browser__fprintf(browser, fp);
1186 	fclose(fp);
1187 	ui_helpline__fpush("%s written!", filename);
1188 
1189 	return 0;
1190 }
1191 
1192 static struct hist_browser *hist_browser__new(struct hists *hists)
1193 {
1194 	struct hist_browser *browser = zalloc(sizeof(*browser));
1195 
1196 	if (browser) {
1197 		browser->hists = hists;
1198 		browser->b.refresh = hist_browser__refresh;
1199 		browser->b.seek = ui_browser__hists_seek;
1200 		browser->b.use_navkeypressed = true;
1201 	}
1202 
1203 	return browser;
1204 }
1205 
1206 static void hist_browser__delete(struct hist_browser *browser)
1207 {
1208 	free(browser);
1209 }
1210 
1211 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1212 {
1213 	return browser->he_selection;
1214 }
1215 
1216 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1217 {
1218 	return browser->he_selection->thread;
1219 }
1220 
1221 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1222 				const char *ev_name)
1223 {
1224 	char unit;
1225 	int printed;
1226 	const struct dso *dso = hists->dso_filter;
1227 	const struct thread *thread = hists->thread_filter;
1228 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1229 	u64 nr_events = hists->stats.total_period;
1230 	struct perf_evsel *evsel = hists_to_evsel(hists);
1231 	char buf[512];
1232 	size_t buflen = sizeof(buf);
1233 
1234 	if (perf_evsel__is_group_event(evsel)) {
1235 		struct perf_evsel *pos;
1236 
1237 		perf_evsel__group_desc(evsel, buf, buflen);
1238 		ev_name = buf;
1239 
1240 		for_each_group_member(pos, evsel) {
1241 			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1242 			nr_events += pos->hists.stats.total_period;
1243 		}
1244 	}
1245 
1246 	nr_samples = convert_unit(nr_samples, &unit);
1247 	printed = scnprintf(bf, size,
1248 			   "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1249 			   nr_samples, unit, ev_name, nr_events);
1250 
1251 
1252 	if (hists->uid_filter_str)
1253 		printed += snprintf(bf + printed, size - printed,
1254 				    ", UID: %s", hists->uid_filter_str);
1255 	if (thread)
1256 		printed += scnprintf(bf + printed, size - printed,
1257 				    ", Thread: %s(%d)",
1258 				     (thread->comm_set ? thread__comm_str(thread) : ""),
1259 				    thread->tid);
1260 	if (dso)
1261 		printed += scnprintf(bf + printed, size - printed,
1262 				    ", DSO: %s", dso->short_name);
1263 	return printed;
1264 }
1265 
1266 static inline void free_popup_options(char **options, int n)
1267 {
1268 	int i;
1269 
1270 	for (i = 0; i < n; ++i)
1271 		zfree(&options[i]);
1272 }
1273 
1274 /* Check whether the browser is for 'top' or 'report' */
1275 static inline bool is_report_browser(void *timer)
1276 {
1277 	return timer == NULL;
1278 }
1279 
1280 /*
1281  * Only runtime switching of perf data file will make "input_name" point
1282  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1283  * whether we need to call free() for current "input_name" during the switch.
1284  */
1285 static bool is_input_name_malloced = false;
1286 
1287 static int switch_data_file(void)
1288 {
1289 	char *pwd, *options[32], *abs_path[32], *tmp;
1290 	DIR *pwd_dir;
1291 	int nr_options = 0, choice = -1, ret = -1;
1292 	struct dirent *dent;
1293 
1294 	pwd = getenv("PWD");
1295 	if (!pwd)
1296 		return ret;
1297 
1298 	pwd_dir = opendir(pwd);
1299 	if (!pwd_dir)
1300 		return ret;
1301 
1302 	memset(options, 0, sizeof(options));
1303 	memset(options, 0, sizeof(abs_path));
1304 
1305 	while ((dent = readdir(pwd_dir))) {
1306 		char path[PATH_MAX];
1307 		u64 magic;
1308 		char *name = dent->d_name;
1309 		FILE *file;
1310 
1311 		if (!(dent->d_type == DT_REG))
1312 			continue;
1313 
1314 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1315 
1316 		file = fopen(path, "r");
1317 		if (!file)
1318 			continue;
1319 
1320 		if (fread(&magic, 1, 8, file) < 8)
1321 			goto close_file_and_continue;
1322 
1323 		if (is_perf_magic(magic)) {
1324 			options[nr_options] = strdup(name);
1325 			if (!options[nr_options])
1326 				goto close_file_and_continue;
1327 
1328 			abs_path[nr_options] = strdup(path);
1329 			if (!abs_path[nr_options]) {
1330 				zfree(&options[nr_options]);
1331 				ui__warning("Can't search all data files due to memory shortage.\n");
1332 				fclose(file);
1333 				break;
1334 			}
1335 
1336 			nr_options++;
1337 		}
1338 
1339 close_file_and_continue:
1340 		fclose(file);
1341 		if (nr_options >= 32) {
1342 			ui__warning("Too many perf data files in PWD!\n"
1343 				    "Only the first 32 files will be listed.\n");
1344 			break;
1345 		}
1346 	}
1347 	closedir(pwd_dir);
1348 
1349 	if (nr_options) {
1350 		choice = ui__popup_menu(nr_options, options);
1351 		if (choice < nr_options && choice >= 0) {
1352 			tmp = strdup(abs_path[choice]);
1353 			if (tmp) {
1354 				if (is_input_name_malloced)
1355 					free((void *)input_name);
1356 				input_name = tmp;
1357 				is_input_name_malloced = true;
1358 				ret = 0;
1359 			} else
1360 				ui__warning("Data switch failed due to memory shortage!\n");
1361 		}
1362 	}
1363 
1364 	free_popup_options(options, nr_options);
1365 	free_popup_options(abs_path, nr_options);
1366 	return ret;
1367 }
1368 
1369 static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1370 {
1371 	u64 nr_entries = 0;
1372 	struct rb_node *nd = rb_first(&hb->hists->entries);
1373 
1374 	while (nd) {
1375 		nr_entries++;
1376 		nd = hists__filter_entries(rb_next(nd), hb->hists,
1377 					   hb->min_pcnt);
1378 	}
1379 
1380 	hb->nr_pcnt_entries = nr_entries;
1381 }
1382 
1383 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1384 				    const char *helpline, const char *ev_name,
1385 				    bool left_exits,
1386 				    struct hist_browser_timer *hbt,
1387 				    float min_pcnt,
1388 				    struct perf_session_env *env)
1389 {
1390 	struct hists *hists = &evsel->hists;
1391 	struct hist_browser *browser = hist_browser__new(hists);
1392 	struct branch_info *bi;
1393 	struct pstack *fstack;
1394 	char *options[16];
1395 	int nr_options = 0;
1396 	int key = -1;
1397 	char buf[64];
1398 	char script_opt[64];
1399 	int delay_secs = hbt ? hbt->refresh : 0;
1400 
1401 #define HIST_BROWSER_HELP_COMMON					\
1402 	"h/?/F1        Show this window\n"				\
1403 	"UP/DOWN/PGUP\n"						\
1404 	"PGDN/SPACE    Navigate\n"					\
1405 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1406 	"For multiple event sessions:\n\n"				\
1407 	"TAB/UNTAB     Switch events\n\n"				\
1408 	"For symbolic views (--sort has sym):\n\n"			\
1409 	"->            Zoom into DSO/Threads & Annotate current symbol\n" \
1410 	"<-            Zoom out\n"					\
1411 	"a             Annotate current symbol\n"			\
1412 	"C             Collapse all callchains\n"			\
1413 	"d             Zoom into current DSO\n"				\
1414 	"E             Expand all callchains\n"				\
1415 
1416 	/* help messages are sorted by lexical order of the hotkey */
1417 	const char report_help[] = HIST_BROWSER_HELP_COMMON
1418 	"i             Show header information\n"
1419 	"P             Print histograms to perf.hist.N\n"
1420 	"r             Run available scripts\n"
1421 	"s             Switch to another data file in PWD\n"
1422 	"t             Zoom into current Thread\n"
1423 	"V             Verbose (DSO names in callchains, etc)\n"
1424 	"/             Filter symbol by name";
1425 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1426 	"P             Print histograms to perf.hist.N\n"
1427 	"t             Zoom into current Thread\n"
1428 	"V             Verbose (DSO names in callchains, etc)\n"
1429 	"/             Filter symbol by name";
1430 
1431 	if (browser == NULL)
1432 		return -1;
1433 
1434 	if (min_pcnt) {
1435 		browser->min_pcnt = min_pcnt;
1436 		hist_browser__update_pcnt_entries(browser);
1437 	}
1438 
1439 	fstack = pstack__new(2);
1440 	if (fstack == NULL)
1441 		goto out;
1442 
1443 	ui_helpline__push(helpline);
1444 
1445 	memset(options, 0, sizeof(options));
1446 
1447 	while (1) {
1448 		const struct thread *thread = NULL;
1449 		const struct dso *dso = NULL;
1450 		int choice = 0,
1451 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1452 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1453 		int scripts_comm = -2, scripts_symbol = -2,
1454 		    scripts_all = -2, switch_data = -2;
1455 
1456 		nr_options = 0;
1457 
1458 		key = hist_browser__run(browser, ev_name, hbt);
1459 
1460 		if (browser->he_selection != NULL) {
1461 			thread = hist_browser__selected_thread(browser);
1462 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1463 		}
1464 		switch (key) {
1465 		case K_TAB:
1466 		case K_UNTAB:
1467 			if (nr_events == 1)
1468 				continue;
1469 			/*
1470 			 * Exit the browser, let hists__browser_tree
1471 			 * go to the next or previous
1472 			 */
1473 			goto out_free_stack;
1474 		case 'a':
1475 			if (!sort__has_sym) {
1476 				ui_browser__warning(&browser->b, delay_secs * 2,
1477 			"Annotation is only available for symbolic views, "
1478 			"include \"sym*\" in --sort to use it.");
1479 				continue;
1480 			}
1481 
1482 			if (browser->selection == NULL ||
1483 			    browser->selection->sym == NULL ||
1484 			    browser->selection->map->dso->annotate_warned)
1485 				continue;
1486 			goto do_annotate;
1487 		case 'P':
1488 			hist_browser__dump(browser);
1489 			continue;
1490 		case 'd':
1491 			goto zoom_dso;
1492 		case 'V':
1493 			browser->show_dso = !browser->show_dso;
1494 			continue;
1495 		case 't':
1496 			goto zoom_thread;
1497 		case '/':
1498 			if (ui_browser__input_window("Symbol to show",
1499 					"Please enter the name of symbol you want to see",
1500 					buf, "ENTER: OK, ESC: Cancel",
1501 					delay_secs * 2) == K_ENTER) {
1502 				hists->symbol_filter_str = *buf ? buf : NULL;
1503 				hists__filter_by_symbol(hists);
1504 				hist_browser__reset(browser);
1505 			}
1506 			continue;
1507 		case 'r':
1508 			if (is_report_browser(hbt))
1509 				goto do_scripts;
1510 			continue;
1511 		case 's':
1512 			if (is_report_browser(hbt))
1513 				goto do_data_switch;
1514 			continue;
1515 		case 'i':
1516 			/* env->arch is NULL for live-mode (i.e. perf top) */
1517 			if (env->arch)
1518 				tui__header_window(env);
1519 			continue;
1520 		case K_F1:
1521 		case 'h':
1522 		case '?':
1523 			ui_browser__help_window(&browser->b,
1524 				is_report_browser(hbt) ? report_help : top_help);
1525 			continue;
1526 		case K_ENTER:
1527 		case K_RIGHT:
1528 			/* menu */
1529 			break;
1530 		case K_LEFT: {
1531 			const void *top;
1532 
1533 			if (pstack__empty(fstack)) {
1534 				/*
1535 				 * Go back to the perf_evsel_menu__run or other user
1536 				 */
1537 				if (left_exits)
1538 					goto out_free_stack;
1539 				continue;
1540 			}
1541 			top = pstack__pop(fstack);
1542 			if (top == &browser->hists->dso_filter)
1543 				goto zoom_out_dso;
1544 			if (top == &browser->hists->thread_filter)
1545 				goto zoom_out_thread;
1546 			continue;
1547 		}
1548 		case K_ESC:
1549 			if (!left_exits &&
1550 			    !ui_browser__dialog_yesno(&browser->b,
1551 					       "Do you really want to exit?"))
1552 				continue;
1553 			/* Fall thru */
1554 		case 'q':
1555 		case CTRL('c'):
1556 			goto out_free_stack;
1557 		default:
1558 			continue;
1559 		}
1560 
1561 		if (!sort__has_sym)
1562 			goto add_exit_option;
1563 
1564 		if (sort__mode == SORT_MODE__BRANCH) {
1565 			bi = browser->he_selection->branch_info;
1566 			if (browser->selection != NULL &&
1567 			    bi &&
1568 			    bi->from.sym != NULL &&
1569 			    !bi->from.map->dso->annotate_warned &&
1570 				asprintf(&options[nr_options], "Annotate %s",
1571 					 bi->from.sym->name) > 0)
1572 				annotate_f = nr_options++;
1573 
1574 			if (browser->selection != NULL &&
1575 			    bi &&
1576 			    bi->to.sym != NULL &&
1577 			    !bi->to.map->dso->annotate_warned &&
1578 			    (bi->to.sym != bi->from.sym ||
1579 			     bi->to.map->dso != bi->from.map->dso) &&
1580 				asprintf(&options[nr_options], "Annotate %s",
1581 					 bi->to.sym->name) > 0)
1582 				annotate_t = nr_options++;
1583 		} else {
1584 
1585 			if (browser->selection != NULL &&
1586 			    browser->selection->sym != NULL &&
1587 			    !browser->selection->map->dso->annotate_warned &&
1588 				asprintf(&options[nr_options], "Annotate %s",
1589 					 browser->selection->sym->name) > 0)
1590 				annotate = nr_options++;
1591 		}
1592 
1593 		if (thread != NULL &&
1594 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1595 			     (browser->hists->thread_filter ? "out of" : "into"),
1596 			     (thread->comm_set ? thread__comm_str(thread) : ""),
1597 			     thread->tid) > 0)
1598 			zoom_thread = nr_options++;
1599 
1600 		if (dso != NULL &&
1601 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1602 			     (browser->hists->dso_filter ? "out of" : "into"),
1603 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1604 			zoom_dso = nr_options++;
1605 
1606 		if (browser->selection != NULL &&
1607 		    browser->selection->map != NULL &&
1608 		    asprintf(&options[nr_options], "Browse map details") > 0)
1609 			browse_map = nr_options++;
1610 
1611 		/* perf script support */
1612 		if (browser->he_selection) {
1613 			struct symbol *sym;
1614 
1615 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1616 				     thread__comm_str(browser->he_selection->thread)) > 0)
1617 				scripts_comm = nr_options++;
1618 
1619 			sym = browser->he_selection->ms.sym;
1620 			if (sym && sym->namelen &&
1621 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1622 						sym->name) > 0)
1623 				scripts_symbol = nr_options++;
1624 		}
1625 
1626 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1627 			scripts_all = nr_options++;
1628 
1629 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1630 				"Switch to another data file in PWD") > 0)
1631 			switch_data = nr_options++;
1632 add_exit_option:
1633 		options[nr_options++] = (char *)"Exit";
1634 retry_popup_menu:
1635 		choice = ui__popup_menu(nr_options, options);
1636 
1637 		if (choice == nr_options - 1)
1638 			break;
1639 
1640 		if (choice == -1) {
1641 			free_popup_options(options, nr_options - 1);
1642 			continue;
1643 		}
1644 
1645 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1646 			struct hist_entry *he;
1647 			int err;
1648 do_annotate:
1649 			if (!objdump_path && perf_session_env__lookup_objdump(env))
1650 				continue;
1651 
1652 			he = hist_browser__selected_entry(browser);
1653 			if (he == NULL)
1654 				continue;
1655 
1656 			/*
1657 			 * we stash the branch_info symbol + map into the
1658 			 * the ms so we don't have to rewrite all the annotation
1659 			 * code to use branch_info.
1660 			 * in branch mode, the ms struct is not used
1661 			 */
1662 			if (choice == annotate_f) {
1663 				he->ms.sym = he->branch_info->from.sym;
1664 				he->ms.map = he->branch_info->from.map;
1665 			}  else if (choice == annotate_t) {
1666 				he->ms.sym = he->branch_info->to.sym;
1667 				he->ms.map = he->branch_info->to.map;
1668 			}
1669 
1670 			/*
1671 			 * Don't let this be freed, say, by hists__decay_entry.
1672 			 */
1673 			he->used = true;
1674 			err = hist_entry__tui_annotate(he, evsel, hbt);
1675 			he->used = false;
1676 			/*
1677 			 * offer option to annotate the other branch source or target
1678 			 * (if they exists) when returning from annotate
1679 			 */
1680 			if ((err == 'q' || err == CTRL('c'))
1681 			    && annotate_t != -2 && annotate_f != -2)
1682 				goto retry_popup_menu;
1683 
1684 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1685 			if (err)
1686 				ui_browser__handle_resize(&browser->b);
1687 
1688 		} else if (choice == browse_map)
1689 			map__browse(browser->selection->map);
1690 		else if (choice == zoom_dso) {
1691 zoom_dso:
1692 			if (browser->hists->dso_filter) {
1693 				pstack__remove(fstack, &browser->hists->dso_filter);
1694 zoom_out_dso:
1695 				ui_helpline__pop();
1696 				browser->hists->dso_filter = NULL;
1697 				sort_dso.elide = false;
1698 			} else {
1699 				if (dso == NULL)
1700 					continue;
1701 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1702 						   dso->kernel ? "the Kernel" : dso->short_name);
1703 				browser->hists->dso_filter = dso;
1704 				sort_dso.elide = true;
1705 				pstack__push(fstack, &browser->hists->dso_filter);
1706 			}
1707 			hists__filter_by_dso(hists);
1708 			hist_browser__reset(browser);
1709 		} else if (choice == zoom_thread) {
1710 zoom_thread:
1711 			if (browser->hists->thread_filter) {
1712 				pstack__remove(fstack, &browser->hists->thread_filter);
1713 zoom_out_thread:
1714 				ui_helpline__pop();
1715 				browser->hists->thread_filter = NULL;
1716 				sort_thread.elide = false;
1717 			} else {
1718 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1719 						   thread->comm_set ? thread__comm_str(thread) : "",
1720 						   thread->tid);
1721 				browser->hists->thread_filter = thread;
1722 				sort_thread.elide = true;
1723 				pstack__push(fstack, &browser->hists->thread_filter);
1724 			}
1725 			hists__filter_by_thread(hists);
1726 			hist_browser__reset(browser);
1727 		}
1728 		/* perf scripts support */
1729 		else if (choice == scripts_all || choice == scripts_comm ||
1730 				choice == scripts_symbol) {
1731 do_scripts:
1732 			memset(script_opt, 0, 64);
1733 
1734 			if (choice == scripts_comm)
1735 				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1736 
1737 			if (choice == scripts_symbol)
1738 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1739 
1740 			script_browse(script_opt);
1741 		}
1742 		/* Switch to another data file */
1743 		else if (choice == switch_data) {
1744 do_data_switch:
1745 			if (!switch_data_file()) {
1746 				key = K_SWITCH_INPUT_DATA;
1747 				break;
1748 			} else
1749 				ui__warning("Won't switch the data files due to\n"
1750 					"no valid data file get selected!\n");
1751 		}
1752 	}
1753 out_free_stack:
1754 	pstack__delete(fstack);
1755 out:
1756 	hist_browser__delete(browser);
1757 	free_popup_options(options, nr_options - 1);
1758 	return key;
1759 }
1760 
1761 struct perf_evsel_menu {
1762 	struct ui_browser b;
1763 	struct perf_evsel *selection;
1764 	bool lost_events, lost_events_warned;
1765 	float min_pcnt;
1766 	struct perf_session_env *env;
1767 };
1768 
1769 static void perf_evsel_menu__write(struct ui_browser *browser,
1770 				   void *entry, int row)
1771 {
1772 	struct perf_evsel_menu *menu = container_of(browser,
1773 						    struct perf_evsel_menu, b);
1774 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1775 	bool current_entry = ui_browser__is_current_entry(browser, row);
1776 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1777 	const char *ev_name = perf_evsel__name(evsel);
1778 	char bf[256], unit;
1779 	const char *warn = " ";
1780 	size_t printed;
1781 
1782 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1783 						       HE_COLORSET_NORMAL);
1784 
1785 	if (perf_evsel__is_group_event(evsel)) {
1786 		struct perf_evsel *pos;
1787 
1788 		ev_name = perf_evsel__group_name(evsel);
1789 
1790 		for_each_group_member(pos, evsel) {
1791 			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1792 		}
1793 	}
1794 
1795 	nr_events = convert_unit(nr_events, &unit);
1796 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1797 			   unit, unit == ' ' ? "" : " ", ev_name);
1798 	slsmg_printf("%s", bf);
1799 
1800 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1801 	if (nr_events != 0) {
1802 		menu->lost_events = true;
1803 		if (!current_entry)
1804 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1805 		nr_events = convert_unit(nr_events, &unit);
1806 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1807 				     nr_events, unit, unit == ' ' ? "" : " ");
1808 		warn = bf;
1809 	}
1810 
1811 	slsmg_write_nstring(warn, browser->width - printed);
1812 
1813 	if (current_entry)
1814 		menu->selection = evsel;
1815 }
1816 
1817 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1818 				int nr_events, const char *help,
1819 				struct hist_browser_timer *hbt)
1820 {
1821 	struct perf_evlist *evlist = menu->b.priv;
1822 	struct perf_evsel *pos;
1823 	const char *ev_name, *title = "Available samples";
1824 	int delay_secs = hbt ? hbt->refresh : 0;
1825 	int key;
1826 
1827 	if (ui_browser__show(&menu->b, title,
1828 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1829 		return -1;
1830 
1831 	while (1) {
1832 		key = ui_browser__run(&menu->b, delay_secs);
1833 
1834 		switch (key) {
1835 		case K_TIMER:
1836 			hbt->timer(hbt->arg);
1837 
1838 			if (!menu->lost_events_warned && menu->lost_events) {
1839 				ui_browser__warn_lost_events(&menu->b);
1840 				menu->lost_events_warned = true;
1841 			}
1842 			continue;
1843 		case K_RIGHT:
1844 		case K_ENTER:
1845 			if (!menu->selection)
1846 				continue;
1847 			pos = menu->selection;
1848 browse_hists:
1849 			perf_evlist__set_selected(evlist, pos);
1850 			/*
1851 			 * Give the calling tool a chance to populate the non
1852 			 * default evsel resorted hists tree.
1853 			 */
1854 			if (hbt)
1855 				hbt->timer(hbt->arg);
1856 			ev_name = perf_evsel__name(pos);
1857 			key = perf_evsel__hists_browse(pos, nr_events, help,
1858 						       ev_name, true, hbt,
1859 						       menu->min_pcnt,
1860 						       menu->env);
1861 			ui_browser__show_title(&menu->b, title);
1862 			switch (key) {
1863 			case K_TAB:
1864 				if (pos->node.next == &evlist->entries)
1865 					pos = perf_evlist__first(evlist);
1866 				else
1867 					pos = perf_evsel__next(pos);
1868 				goto browse_hists;
1869 			case K_UNTAB:
1870 				if (pos->node.prev == &evlist->entries)
1871 					pos = perf_evlist__last(evlist);
1872 				else
1873 					pos = perf_evsel__prev(pos);
1874 				goto browse_hists;
1875 			case K_ESC:
1876 				if (!ui_browser__dialog_yesno(&menu->b,
1877 						"Do you really want to exit?"))
1878 					continue;
1879 				/* Fall thru */
1880 			case K_SWITCH_INPUT_DATA:
1881 			case 'q':
1882 			case CTRL('c'):
1883 				goto out;
1884 			default:
1885 				continue;
1886 			}
1887 		case K_LEFT:
1888 			continue;
1889 		case K_ESC:
1890 			if (!ui_browser__dialog_yesno(&menu->b,
1891 					       "Do you really want to exit?"))
1892 				continue;
1893 			/* Fall thru */
1894 		case 'q':
1895 		case CTRL('c'):
1896 			goto out;
1897 		default:
1898 			continue;
1899 		}
1900 	}
1901 
1902 out:
1903 	ui_browser__hide(&menu->b);
1904 	return key;
1905 }
1906 
1907 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1908 				 void *entry)
1909 {
1910 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1911 
1912 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1913 		return true;
1914 
1915 	return false;
1916 }
1917 
1918 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1919 					   int nr_entries, const char *help,
1920 					   struct hist_browser_timer *hbt,
1921 					   float min_pcnt,
1922 					   struct perf_session_env *env)
1923 {
1924 	struct perf_evsel *pos;
1925 	struct perf_evsel_menu menu = {
1926 		.b = {
1927 			.entries    = &evlist->entries,
1928 			.refresh    = ui_browser__list_head_refresh,
1929 			.seek	    = ui_browser__list_head_seek,
1930 			.write	    = perf_evsel_menu__write,
1931 			.filter	    = filter_group_entries,
1932 			.nr_entries = nr_entries,
1933 			.priv	    = evlist,
1934 		},
1935 		.min_pcnt = min_pcnt,
1936 		.env = env,
1937 	};
1938 
1939 	ui_helpline__push("Press ESC to exit");
1940 
1941 	evlist__for_each(evlist, pos) {
1942 		const char *ev_name = perf_evsel__name(pos);
1943 		size_t line_len = strlen(ev_name) + 7;
1944 
1945 		if (menu.b.width < line_len)
1946 			menu.b.width = line_len;
1947 	}
1948 
1949 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1950 }
1951 
1952 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1953 				  struct hist_browser_timer *hbt,
1954 				  float min_pcnt,
1955 				  struct perf_session_env *env)
1956 {
1957 	int nr_entries = evlist->nr_entries;
1958 
1959 single_entry:
1960 	if (nr_entries == 1) {
1961 		struct perf_evsel *first = perf_evlist__first(evlist);
1962 		const char *ev_name = perf_evsel__name(first);
1963 
1964 		return perf_evsel__hists_browse(first, nr_entries, help,
1965 						ev_name, false, hbt, min_pcnt,
1966 						env);
1967 	}
1968 
1969 	if (symbol_conf.event_group) {
1970 		struct perf_evsel *pos;
1971 
1972 		nr_entries = 0;
1973 		evlist__for_each(evlist, pos) {
1974 			if (perf_evsel__is_group_leader(pos))
1975 				nr_entries++;
1976 		}
1977 
1978 		if (nr_entries == 1)
1979 			goto single_entry;
1980 	}
1981 
1982 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1983 					       hbt, min_pcnt, env);
1984 }
1985