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