xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision dabd2012)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <linux/rbtree.h>
5 
6 #include "../../util/evsel.h"
7 #include "../../util/evlist.h"
8 #include "../../util/hist.h"
9 #include "../../util/pstack.h"
10 #include "../../util/sort.h"
11 #include "../../util/util.h"
12 #include "../../util/top.h"
13 #include "../../arch/common.h"
14 
15 #include "../browsers/hists.h"
16 #include "../helpline.h"
17 #include "../util.h"
18 #include "../ui.h"
19 #include "map.h"
20 #include "annotate.h"
21 
22 extern void hist_browser__init_hpp(void);
23 
24 static int hists__browser_title(struct hists *hists,
25 				struct hist_browser_timer *hbt,
26 				char *bf, size_t size);
27 static void hist_browser__update_nr_entries(struct hist_browser *hb);
28 
29 static struct rb_node *hists__filter_entries(struct rb_node *nd,
30 					     float min_pcnt);
31 
32 static bool hist_browser__has_filter(struct hist_browser *hb)
33 {
34 	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
35 }
36 
37 static int hist_browser__get_folding(struct hist_browser *browser)
38 {
39 	struct rb_node *nd;
40 	struct hists *hists = browser->hists;
41 	int unfolded_rows = 0;
42 
43 	for (nd = rb_first(&hists->entries);
44 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
45 	     nd = rb_hierarchy_next(nd)) {
46 		struct hist_entry *he =
47 			rb_entry(nd, struct hist_entry, rb_node);
48 
49 		if (he->leaf && he->unfolded)
50 			unfolded_rows += he->nr_rows;
51 	}
52 	return unfolded_rows;
53 }
54 
55 static u32 hist_browser__nr_entries(struct hist_browser *hb)
56 {
57 	u32 nr_entries;
58 
59 	if (symbol_conf.report_hierarchy)
60 		nr_entries = hb->nr_hierarchy_entries;
61 	else if (hist_browser__has_filter(hb))
62 		nr_entries = hb->nr_non_filtered_entries;
63 	else
64 		nr_entries = hb->hists->nr_entries;
65 
66 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
67 	return nr_entries + hb->nr_callchain_rows;
68 }
69 
70 static void hist_browser__update_rows(struct hist_browser *hb)
71 {
72 	struct ui_browser *browser = &hb->b;
73 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
74 
75 	browser->rows = browser->height - header_offset;
76 	/*
77 	 * Verify if we were at the last line and that line isn't
78 	 * visibe because we now show the header line(s).
79 	 */
80 	index_row = browser->index - browser->top_idx;
81 	if (index_row >= browser->rows)
82 		browser->index -= index_row - browser->rows + 1;
83 }
84 
85 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
86 {
87 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
88 
89 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
90 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
91 	/*
92  	 * FIXME: Just keeping existing behaviour, but this really should be
93  	 *	  before updating browser->width, as it will invalidate the
94  	 *	  calculation above. Fix this and the fallout in another
95  	 *	  changeset.
96  	 */
97 	ui_browser__refresh_dimensions(browser);
98 	hist_browser__update_rows(hb);
99 }
100 
101 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
102 {
103 	u16 header_offset = browser->show_headers ? 1 : 0;
104 
105 	ui_browser__gotorc(&browser->b, row + header_offset, column);
106 }
107 
108 static void hist_browser__reset(struct hist_browser *browser)
109 {
110 	/*
111 	 * The hists__remove_entry_filter() already folds non-filtered
112 	 * entries so we can assume it has 0 callchain rows.
113 	 */
114 	browser->nr_callchain_rows = 0;
115 
116 	hist_browser__update_nr_entries(browser);
117 	browser->b.nr_entries = hist_browser__nr_entries(browser);
118 	hist_browser__refresh_dimensions(&browser->b);
119 	ui_browser__reset_index(&browser->b);
120 }
121 
122 static char tree__folded_sign(bool unfolded)
123 {
124 	return unfolded ? '-' : '+';
125 }
126 
127 static char hist_entry__folded(const struct hist_entry *he)
128 {
129 	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
130 }
131 
132 static char callchain_list__folded(const struct callchain_list *cl)
133 {
134 	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
135 }
136 
137 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
138 {
139 	cl->unfolded = unfold ? cl->has_children : false;
140 }
141 
142 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
143 {
144 	int n = 0;
145 	struct rb_node *nd;
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 		char folded_sign = ' '; /* No children */
151 
152 		list_for_each_entry(chain, &child->val, list) {
153 			++n;
154 			/* We need this because we may not have children */
155 			folded_sign = callchain_list__folded(chain);
156 			if (folded_sign == '+')
157 				break;
158 		}
159 
160 		if (folded_sign == '-') /* Have children and they're unfolded */
161 			n += callchain_node__count_rows_rb_tree(child);
162 	}
163 
164 	return n;
165 }
166 
167 static int callchain_node__count_flat_rows(struct callchain_node *node)
168 {
169 	struct callchain_list *chain;
170 	char folded_sign = 0;
171 	int n = 0;
172 
173 	list_for_each_entry(chain, &node->parent_val, list) {
174 		if (!folded_sign) {
175 			/* only check first chain list entry */
176 			folded_sign = callchain_list__folded(chain);
177 			if (folded_sign == '+')
178 				return 1;
179 		}
180 		n++;
181 	}
182 
183 	list_for_each_entry(chain, &node->val, list) {
184 		if (!folded_sign) {
185 			/* node->parent_val list might be empty */
186 			folded_sign = callchain_list__folded(chain);
187 			if (folded_sign == '+')
188 				return 1;
189 		}
190 		n++;
191 	}
192 
193 	return n;
194 }
195 
196 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
197 {
198 	return 1;
199 }
200 
201 static int callchain_node__count_rows(struct callchain_node *node)
202 {
203 	struct callchain_list *chain;
204 	bool unfolded = false;
205 	int n = 0;
206 
207 	if (callchain_param.mode == CHAIN_FLAT)
208 		return callchain_node__count_flat_rows(node);
209 	else if (callchain_param.mode == CHAIN_FOLDED)
210 		return callchain_node__count_folded_rows(node);
211 
212 	list_for_each_entry(chain, &node->val, list) {
213 		++n;
214 		unfolded = chain->unfolded;
215 	}
216 
217 	if (unfolded)
218 		n += callchain_node__count_rows_rb_tree(node);
219 
220 	return n;
221 }
222 
223 static int callchain__count_rows(struct rb_root *chain)
224 {
225 	struct rb_node *nd;
226 	int n = 0;
227 
228 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
229 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
230 		n += callchain_node__count_rows(node);
231 	}
232 
233 	return n;
234 }
235 
236 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
237 				bool include_children)
238 {
239 	int count = 0;
240 	struct rb_node *node;
241 	struct hist_entry *child;
242 
243 	if (he->leaf)
244 		return callchain__count_rows(&he->sorted_chain);
245 
246 	if (he->has_no_entry)
247 		return 1;
248 
249 	node = rb_first(&he->hroot_out);
250 	while (node) {
251 		float percent;
252 
253 		child = rb_entry(node, struct hist_entry, rb_node);
254 		percent = hist_entry__get_percent_limit(child);
255 
256 		if (!child->filtered && percent >= hb->min_pcnt) {
257 			count++;
258 
259 			if (include_children && child->unfolded)
260 				count += hierarchy_count_rows(hb, child, true);
261 		}
262 
263 		node = rb_next(node);
264 	}
265 	return count;
266 }
267 
268 static bool hist_entry__toggle_fold(struct hist_entry *he)
269 {
270 	if (!he)
271 		return false;
272 
273 	if (!he->has_children)
274 		return false;
275 
276 	he->unfolded = !he->unfolded;
277 	return true;
278 }
279 
280 static bool callchain_list__toggle_fold(struct callchain_list *cl)
281 {
282 	if (!cl)
283 		return false;
284 
285 	if (!cl->has_children)
286 		return false;
287 
288 	cl->unfolded = !cl->unfolded;
289 	return true;
290 }
291 
292 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
293 {
294 	struct rb_node *nd = rb_first(&node->rb_root);
295 
296 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
297 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
298 		struct callchain_list *chain;
299 		bool first = true;
300 
301 		list_for_each_entry(chain, &child->val, list) {
302 			if (first) {
303 				first = false;
304 				chain->has_children = chain->list.next != &child->val ||
305 							 !RB_EMPTY_ROOT(&child->rb_root);
306 			} else
307 				chain->has_children = chain->list.next == &child->val &&
308 							 !RB_EMPTY_ROOT(&child->rb_root);
309 		}
310 
311 		callchain_node__init_have_children_rb_tree(child);
312 	}
313 }
314 
315 static void callchain_node__init_have_children(struct callchain_node *node,
316 					       bool has_sibling)
317 {
318 	struct callchain_list *chain;
319 
320 	chain = list_entry(node->val.next, struct callchain_list, list);
321 	chain->has_children = has_sibling;
322 
323 	if (!list_empty(&node->val)) {
324 		chain = list_entry(node->val.prev, struct callchain_list, list);
325 		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
326 	}
327 
328 	callchain_node__init_have_children_rb_tree(node);
329 }
330 
331 static void callchain__init_have_children(struct rb_root *root)
332 {
333 	struct rb_node *nd = rb_first(root);
334 	bool has_sibling = nd && rb_next(nd);
335 
336 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
337 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
338 		callchain_node__init_have_children(node, has_sibling);
339 		if (callchain_param.mode == CHAIN_FLAT ||
340 		    callchain_param.mode == CHAIN_FOLDED)
341 			callchain_node__make_parent_list(node);
342 	}
343 }
344 
345 static void hist_entry__init_have_children(struct hist_entry *he)
346 {
347 	if (he->init_have_children)
348 		return;
349 
350 	if (he->leaf) {
351 		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
352 		callchain__init_have_children(&he->sorted_chain);
353 	} else {
354 		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
355 	}
356 
357 	he->init_have_children = true;
358 }
359 
360 static bool hist_browser__toggle_fold(struct hist_browser *browser)
361 {
362 	struct hist_entry *he = browser->he_selection;
363 	struct map_symbol *ms = browser->selection;
364 	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
365 	bool has_children;
366 
367 	if (!he || !ms)
368 		return false;
369 
370 	if (ms == &he->ms)
371 		has_children = hist_entry__toggle_fold(he);
372 	else
373 		has_children = callchain_list__toggle_fold(cl);
374 
375 	if (has_children) {
376 		int child_rows = 0;
377 
378 		hist_entry__init_have_children(he);
379 		browser->b.nr_entries -= he->nr_rows;
380 
381 		if (he->leaf)
382 			browser->nr_callchain_rows -= he->nr_rows;
383 		else
384 			browser->nr_hierarchy_entries -= he->nr_rows;
385 
386 		if (symbol_conf.report_hierarchy)
387 			child_rows = hierarchy_count_rows(browser, he, true);
388 
389 		if (he->unfolded) {
390 			if (he->leaf)
391 				he->nr_rows = callchain__count_rows(&he->sorted_chain);
392 			else
393 				he->nr_rows = hierarchy_count_rows(browser, he, false);
394 
395 			/* account grand children */
396 			if (symbol_conf.report_hierarchy)
397 				browser->b.nr_entries += child_rows - he->nr_rows;
398 
399 			if (!he->leaf && he->nr_rows == 0) {
400 				he->has_no_entry = true;
401 				he->nr_rows = 1;
402 			}
403 		} else {
404 			if (symbol_conf.report_hierarchy)
405 				browser->b.nr_entries -= child_rows - he->nr_rows;
406 
407 			if (he->has_no_entry)
408 				he->has_no_entry = false;
409 
410 			he->nr_rows = 0;
411 		}
412 
413 		browser->b.nr_entries += he->nr_rows;
414 
415 		if (he->leaf)
416 			browser->nr_callchain_rows += he->nr_rows;
417 		else
418 			browser->nr_hierarchy_entries += he->nr_rows;
419 
420 		return true;
421 	}
422 
423 	/* If it doesn't have children, no toggling performed */
424 	return false;
425 }
426 
427 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
428 {
429 	int n = 0;
430 	struct rb_node *nd;
431 
432 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
433 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
434 		struct callchain_list *chain;
435 		bool has_children = false;
436 
437 		list_for_each_entry(chain, &child->val, list) {
438 			++n;
439 			callchain_list__set_folding(chain, unfold);
440 			has_children = chain->has_children;
441 		}
442 
443 		if (has_children)
444 			n += callchain_node__set_folding_rb_tree(child, unfold);
445 	}
446 
447 	return n;
448 }
449 
450 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
451 {
452 	struct callchain_list *chain;
453 	bool has_children = false;
454 	int n = 0;
455 
456 	list_for_each_entry(chain, &node->val, list) {
457 		++n;
458 		callchain_list__set_folding(chain, unfold);
459 		has_children = chain->has_children;
460 	}
461 
462 	if (has_children)
463 		n += callchain_node__set_folding_rb_tree(node, unfold);
464 
465 	return n;
466 }
467 
468 static int callchain__set_folding(struct rb_root *chain, bool unfold)
469 {
470 	struct rb_node *nd;
471 	int n = 0;
472 
473 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
474 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
475 		n += callchain_node__set_folding(node, unfold);
476 	}
477 
478 	return n;
479 }
480 
481 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
482 				 bool unfold __maybe_unused)
483 {
484 	float percent;
485 	struct rb_node *nd;
486 	struct hist_entry *child;
487 	int n = 0;
488 
489 	for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
490 		child = rb_entry(nd, struct hist_entry, rb_node);
491 		percent = hist_entry__get_percent_limit(child);
492 		if (!child->filtered && percent >= hb->min_pcnt)
493 			n++;
494 	}
495 
496 	return n;
497 }
498 
499 static void hist_entry__set_folding(struct hist_entry *he,
500 				    struct hist_browser *hb, bool unfold)
501 {
502 	hist_entry__init_have_children(he);
503 	he->unfolded = unfold ? he->has_children : false;
504 
505 	if (he->has_children) {
506 		int n;
507 
508 		if (he->leaf)
509 			n = callchain__set_folding(&he->sorted_chain, unfold);
510 		else
511 			n = hierarchy_set_folding(hb, he, unfold);
512 
513 		he->nr_rows = unfold ? n : 0;
514 	} else
515 		he->nr_rows = 0;
516 }
517 
518 static void
519 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
520 {
521 	struct rb_node *nd;
522 	struct hist_entry *he;
523 	double percent;
524 
525 	nd = rb_first(&browser->hists->entries);
526 	while (nd) {
527 		he = rb_entry(nd, struct hist_entry, rb_node);
528 
529 		/* set folding state even if it's currently folded */
530 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
531 
532 		hist_entry__set_folding(he, browser, unfold);
533 
534 		percent = hist_entry__get_percent_limit(he);
535 		if (he->filtered || percent < browser->min_pcnt)
536 			continue;
537 
538 		if (!he->depth || unfold)
539 			browser->nr_hierarchy_entries++;
540 		if (he->leaf)
541 			browser->nr_callchain_rows += he->nr_rows;
542 		else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
543 			browser->nr_hierarchy_entries++;
544 			he->has_no_entry = true;
545 			he->nr_rows = 1;
546 		} else
547 			he->has_no_entry = false;
548 	}
549 }
550 
551 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
552 {
553 	browser->nr_hierarchy_entries = 0;
554 	browser->nr_callchain_rows = 0;
555 	__hist_browser__set_folding(browser, unfold);
556 
557 	browser->b.nr_entries = hist_browser__nr_entries(browser);
558 	/* Go to the start, we may be way after valid entries after a collapse */
559 	ui_browser__reset_index(&browser->b);
560 }
561 
562 static void ui_browser__warn_lost_events(struct ui_browser *browser)
563 {
564 	ui_browser__warning(browser, 4,
565 		"Events are being lost, check IO/CPU overload!\n\n"
566 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
567 		" perf top -r 80\n\n"
568 		"Or reduce the sampling frequency.");
569 }
570 
571 int hist_browser__run(struct hist_browser *browser, const char *help)
572 {
573 	int key;
574 	char title[160];
575 	struct hist_browser_timer *hbt = browser->hbt;
576 	int delay_secs = hbt ? hbt->refresh : 0;
577 
578 	browser->b.entries = &browser->hists->entries;
579 	browser->b.nr_entries = hist_browser__nr_entries(browser);
580 
581 	hists__browser_title(browser->hists, hbt, title, sizeof(title));
582 
583 	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
584 		return -1;
585 
586 	while (1) {
587 		key = ui_browser__run(&browser->b, delay_secs);
588 
589 		switch (key) {
590 		case K_TIMER: {
591 			u64 nr_entries;
592 			hbt->timer(hbt->arg);
593 
594 			if (hist_browser__has_filter(browser))
595 				hist_browser__update_nr_entries(browser);
596 
597 			nr_entries = hist_browser__nr_entries(browser);
598 			ui_browser__update_nr_entries(&browser->b, nr_entries);
599 
600 			if (browser->hists->stats.nr_lost_warned !=
601 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
602 				browser->hists->stats.nr_lost_warned =
603 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
604 				ui_browser__warn_lost_events(&browser->b);
605 			}
606 
607 			hists__browser_title(browser->hists,
608 					     hbt, title, sizeof(title));
609 			ui_browser__show_title(&browser->b, title);
610 			continue;
611 		}
612 		case 'D': { /* Debug */
613 			static int seq;
614 			struct hist_entry *h = rb_entry(browser->b.top,
615 							struct hist_entry, rb_node);
616 			ui_helpline__pop();
617 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
618 					   seq++, browser->b.nr_entries,
619 					   browser->hists->nr_entries,
620 					   browser->b.rows,
621 					   browser->b.index,
622 					   browser->b.top_idx,
623 					   h->row_offset, h->nr_rows);
624 		}
625 			break;
626 		case 'C':
627 			/* Collapse the whole world. */
628 			hist_browser__set_folding(browser, false);
629 			break;
630 		case 'E':
631 			/* Expand the whole world. */
632 			hist_browser__set_folding(browser, true);
633 			break;
634 		case 'H':
635 			browser->show_headers = !browser->show_headers;
636 			hist_browser__update_rows(browser);
637 			break;
638 		case K_ENTER:
639 			if (hist_browser__toggle_fold(browser))
640 				break;
641 			/* fall thru */
642 		default:
643 			goto out;
644 		}
645 	}
646 out:
647 	ui_browser__hide(&browser->b);
648 	return key;
649 }
650 
651 struct callchain_print_arg {
652 	/* for hists browser */
653 	off_t	row_offset;
654 	bool	is_current_entry;
655 
656 	/* for file dump */
657 	FILE	*fp;
658 	int	printed;
659 };
660 
661 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
662 					 struct callchain_list *chain,
663 					 const char *str, int offset,
664 					 unsigned short row,
665 					 struct callchain_print_arg *arg);
666 
667 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
668 					       struct callchain_list *chain,
669 					       const char *str, int offset,
670 					       unsigned short row,
671 					       struct callchain_print_arg *arg)
672 {
673 	int color, width;
674 	char folded_sign = callchain_list__folded(chain);
675 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
676 
677 	color = HE_COLORSET_NORMAL;
678 	width = browser->b.width - (offset + 2);
679 	if (ui_browser__is_current_entry(&browser->b, row)) {
680 		browser->selection = &chain->ms;
681 		color = HE_COLORSET_SELECTED;
682 		arg->is_current_entry = true;
683 	}
684 
685 	ui_browser__set_color(&browser->b, color);
686 	hist_browser__gotorc(browser, row, 0);
687 	ui_browser__write_nstring(&browser->b, " ", offset);
688 	ui_browser__printf(&browser->b, "%c", folded_sign);
689 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
690 	ui_browser__write_nstring(&browser->b, str, width);
691 }
692 
693 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
694 						  struct callchain_list *chain,
695 						  const char *str, int offset,
696 						  unsigned short row __maybe_unused,
697 						  struct callchain_print_arg *arg)
698 {
699 	char folded_sign = callchain_list__folded(chain);
700 
701 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
702 				folded_sign, str);
703 }
704 
705 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
706 				     unsigned short row);
707 
708 static bool hist_browser__check_output_full(struct hist_browser *browser,
709 					    unsigned short row)
710 {
711 	return browser->b.rows == row;
712 }
713 
714 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
715 					  unsigned short row __maybe_unused)
716 {
717 	return false;
718 }
719 
720 #define LEVEL_OFFSET_STEP 3
721 
722 static int hist_browser__show_callchain_list(struct hist_browser *browser,
723 					     struct callchain_node *node,
724 					     struct callchain_list *chain,
725 					     unsigned short row, u64 total,
726 					     bool need_percent, int offset,
727 					     print_callchain_entry_fn print,
728 					     struct callchain_print_arg *arg)
729 {
730 	char bf[1024], *alloc_str;
731 	const char *str;
732 
733 	if (arg->row_offset != 0) {
734 		arg->row_offset--;
735 		return 0;
736 	}
737 
738 	alloc_str = NULL;
739 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
740 				       browser->show_dso);
741 
742 	if (need_percent) {
743 		char buf[64];
744 
745 		callchain_node__scnprintf_value(node, buf, sizeof(buf),
746 						total);
747 
748 		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
749 			str = "Not enough memory!";
750 		else
751 			str = alloc_str;
752 	}
753 
754 	print(browser, chain, str, offset, row, arg);
755 
756 	free(alloc_str);
757 	return 1;
758 }
759 
760 static bool check_percent_display(struct rb_node *node, u64 parent_total)
761 {
762 	struct callchain_node *child;
763 
764 	if (node == NULL)
765 		return false;
766 
767 	if (rb_next(node))
768 		return true;
769 
770 	child = rb_entry(node, struct callchain_node, rb_node);
771 	return callchain_cumul_hits(child) != parent_total;
772 }
773 
774 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
775 					     struct rb_root *root,
776 					     unsigned short row, u64 total,
777 					     u64 parent_total,
778 					     print_callchain_entry_fn print,
779 					     struct callchain_print_arg *arg,
780 					     check_output_full_fn is_output_full)
781 {
782 	struct rb_node *node;
783 	int first_row = row, offset = LEVEL_OFFSET_STEP;
784 	bool need_percent;
785 
786 	node = rb_first(root);
787 	need_percent = check_percent_display(node, parent_total);
788 
789 	while (node) {
790 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
791 		struct rb_node *next = rb_next(node);
792 		struct callchain_list *chain;
793 		char folded_sign = ' ';
794 		int first = true;
795 		int extra_offset = 0;
796 
797 		list_for_each_entry(chain, &child->parent_val, list) {
798 			bool was_first = first;
799 
800 			if (first)
801 				first = false;
802 			else if (need_percent)
803 				extra_offset = LEVEL_OFFSET_STEP;
804 
805 			folded_sign = callchain_list__folded(chain);
806 
807 			row += hist_browser__show_callchain_list(browser, child,
808 							chain, row, total,
809 							was_first && need_percent,
810 							offset + extra_offset,
811 							print, arg);
812 
813 			if (is_output_full(browser, row))
814 				goto out;
815 
816 			if (folded_sign == '+')
817 				goto next;
818 		}
819 
820 		list_for_each_entry(chain, &child->val, list) {
821 			bool was_first = first;
822 
823 			if (first)
824 				first = false;
825 			else if (need_percent)
826 				extra_offset = LEVEL_OFFSET_STEP;
827 
828 			folded_sign = callchain_list__folded(chain);
829 
830 			row += hist_browser__show_callchain_list(browser, child,
831 							chain, row, total,
832 							was_first && need_percent,
833 							offset + extra_offset,
834 							print, arg);
835 
836 			if (is_output_full(browser, row))
837 				goto out;
838 
839 			if (folded_sign == '+')
840 				break;
841 		}
842 
843 next:
844 		if (is_output_full(browser, row))
845 			break;
846 		node = next;
847 	}
848 out:
849 	return row - first_row;
850 }
851 
852 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
853 						struct callchain_list *chain,
854 						char *value_str, char *old_str)
855 {
856 	char bf[1024];
857 	const char *str;
858 	char *new;
859 
860 	str = callchain_list__sym_name(chain, bf, sizeof(bf),
861 				       browser->show_dso);
862 	if (old_str) {
863 		if (asprintf(&new, "%s%s%s", old_str,
864 			     symbol_conf.field_sep ?: ";", str) < 0)
865 			new = NULL;
866 	} else {
867 		if (value_str) {
868 			if (asprintf(&new, "%s %s", value_str, str) < 0)
869 				new = NULL;
870 		} else {
871 			if (asprintf(&new, "%s", str) < 0)
872 				new = NULL;
873 		}
874 	}
875 	return new;
876 }
877 
878 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
879 					       struct rb_root *root,
880 					       unsigned short row, u64 total,
881 					       u64 parent_total,
882 					       print_callchain_entry_fn print,
883 					       struct callchain_print_arg *arg,
884 					       check_output_full_fn is_output_full)
885 {
886 	struct rb_node *node;
887 	int first_row = row, offset = LEVEL_OFFSET_STEP;
888 	bool need_percent;
889 
890 	node = rb_first(root);
891 	need_percent = check_percent_display(node, parent_total);
892 
893 	while (node) {
894 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
895 		struct rb_node *next = rb_next(node);
896 		struct callchain_list *chain, *first_chain = NULL;
897 		int first = true;
898 		char *value_str = NULL, *value_str_alloc = NULL;
899 		char *chain_str = NULL, *chain_str_alloc = NULL;
900 
901 		if (arg->row_offset != 0) {
902 			arg->row_offset--;
903 			goto next;
904 		}
905 
906 		if (need_percent) {
907 			char buf[64];
908 
909 			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
910 			if (asprintf(&value_str, "%s", buf) < 0) {
911 				value_str = (char *)"<...>";
912 				goto do_print;
913 			}
914 			value_str_alloc = value_str;
915 		}
916 
917 		list_for_each_entry(chain, &child->parent_val, list) {
918 			chain_str = hist_browser__folded_callchain_str(browser,
919 						chain, value_str, chain_str);
920 			if (first) {
921 				first = false;
922 				first_chain = chain;
923 			}
924 
925 			if (chain_str == NULL) {
926 				chain_str = (char *)"Not enough memory!";
927 				goto do_print;
928 			}
929 
930 			chain_str_alloc = chain_str;
931 		}
932 
933 		list_for_each_entry(chain, &child->val, list) {
934 			chain_str = hist_browser__folded_callchain_str(browser,
935 						chain, value_str, chain_str);
936 			if (first) {
937 				first = false;
938 				first_chain = chain;
939 			}
940 
941 			if (chain_str == NULL) {
942 				chain_str = (char *)"Not enough memory!";
943 				goto do_print;
944 			}
945 
946 			chain_str_alloc = chain_str;
947 		}
948 
949 do_print:
950 		print(browser, first_chain, chain_str, offset, row++, arg);
951 		free(value_str_alloc);
952 		free(chain_str_alloc);
953 
954 next:
955 		if (is_output_full(browser, row))
956 			break;
957 		node = next;
958 	}
959 
960 	return row - first_row;
961 }
962 
963 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
964 					struct rb_root *root, int level,
965 					unsigned short row, u64 total,
966 					u64 parent_total,
967 					print_callchain_entry_fn print,
968 					struct callchain_print_arg *arg,
969 					check_output_full_fn is_output_full)
970 {
971 	struct rb_node *node;
972 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
973 	bool need_percent;
974 	u64 percent_total = total;
975 
976 	if (callchain_param.mode == CHAIN_GRAPH_REL)
977 		percent_total = parent_total;
978 
979 	node = rb_first(root);
980 	need_percent = check_percent_display(node, parent_total);
981 
982 	while (node) {
983 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
984 		struct rb_node *next = rb_next(node);
985 		struct callchain_list *chain;
986 		char folded_sign = ' ';
987 		int first = true;
988 		int extra_offset = 0;
989 
990 		list_for_each_entry(chain, &child->val, list) {
991 			bool was_first = first;
992 
993 			if (first)
994 				first = false;
995 			else if (need_percent)
996 				extra_offset = LEVEL_OFFSET_STEP;
997 
998 			folded_sign = callchain_list__folded(chain);
999 
1000 			row += hist_browser__show_callchain_list(browser, child,
1001 							chain, row, percent_total,
1002 							was_first && need_percent,
1003 							offset + extra_offset,
1004 							print, arg);
1005 
1006 			if (is_output_full(browser, row))
1007 				goto out;
1008 
1009 			if (folded_sign == '+')
1010 				break;
1011 		}
1012 
1013 		if (folded_sign == '-') {
1014 			const int new_level = level + (extra_offset ? 2 : 1);
1015 
1016 			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1017 							    new_level, row, total,
1018 							    child->children_hit,
1019 							    print, arg, is_output_full);
1020 		}
1021 		if (is_output_full(browser, row))
1022 			break;
1023 		node = next;
1024 	}
1025 out:
1026 	return row - first_row;
1027 }
1028 
1029 static int hist_browser__show_callchain(struct hist_browser *browser,
1030 					struct hist_entry *entry, int level,
1031 					unsigned short row,
1032 					print_callchain_entry_fn print,
1033 					struct callchain_print_arg *arg,
1034 					check_output_full_fn is_output_full)
1035 {
1036 	u64 total = hists__total_period(entry->hists);
1037 	u64 parent_total;
1038 	int printed;
1039 
1040 	if (symbol_conf.cumulate_callchain)
1041 		parent_total = entry->stat_acc->period;
1042 	else
1043 		parent_total = entry->stat.period;
1044 
1045 	if (callchain_param.mode == CHAIN_FLAT) {
1046 		printed = hist_browser__show_callchain_flat(browser,
1047 						&entry->sorted_chain, row,
1048 						total, parent_total, print, arg,
1049 						is_output_full);
1050 	} else if (callchain_param.mode == CHAIN_FOLDED) {
1051 		printed = hist_browser__show_callchain_folded(browser,
1052 						&entry->sorted_chain, row,
1053 						total, parent_total, print, arg,
1054 						is_output_full);
1055 	} else {
1056 		printed = hist_browser__show_callchain_graph(browser,
1057 						&entry->sorted_chain, level, row,
1058 						total, parent_total, print, arg,
1059 						is_output_full);
1060 	}
1061 
1062 	if (arg->is_current_entry)
1063 		browser->he_selection = entry;
1064 
1065 	return printed;
1066 }
1067 
1068 struct hpp_arg {
1069 	struct ui_browser *b;
1070 	char folded_sign;
1071 	bool current_entry;
1072 };
1073 
1074 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1075 {
1076 	struct hpp_arg *arg = hpp->ptr;
1077 	int ret, len;
1078 	va_list args;
1079 	double percent;
1080 
1081 	va_start(args, fmt);
1082 	len = va_arg(args, int);
1083 	percent = va_arg(args, double);
1084 	va_end(args);
1085 
1086 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1087 
1088 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1089 	ui_browser__printf(arg->b, "%s", hpp->buf);
1090 
1091 	advance_hpp(hpp, ret);
1092 	return ret;
1093 }
1094 
1095 #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
1096 static u64 __hpp_get_##_field(struct hist_entry *he)			\
1097 {									\
1098 	return he->stat._field;						\
1099 }									\
1100 									\
1101 static int								\
1102 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1103 				struct perf_hpp *hpp,			\
1104 				struct hist_entry *he)			\
1105 {									\
1106 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
1107 			__hpp__slsmg_color_printf, true);		\
1108 }
1109 
1110 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
1111 static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
1112 {									\
1113 	return he->stat_acc->_field;					\
1114 }									\
1115 									\
1116 static int								\
1117 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1118 				struct perf_hpp *hpp,			\
1119 				struct hist_entry *he)			\
1120 {									\
1121 	if (!symbol_conf.cumulate_callchain) {				\
1122 		struct hpp_arg *arg = hpp->ptr;				\
1123 		int len = fmt->user_len ?: fmt->len;			\
1124 		int ret = scnprintf(hpp->buf, hpp->size,		\
1125 				    "%*s", len, "N/A");			\
1126 		ui_browser__printf(arg->b, "%s", hpp->buf);		\
1127 									\
1128 		return ret;						\
1129 	}								\
1130 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
1131 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
1132 }
1133 
1134 __HPP_COLOR_PERCENT_FN(overhead, period)
1135 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1136 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1137 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1138 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1139 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1140 
1141 #undef __HPP_COLOR_PERCENT_FN
1142 #undef __HPP_COLOR_ACC_PERCENT_FN
1143 
1144 void hist_browser__init_hpp(void)
1145 {
1146 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1147 				hist_browser__hpp_color_overhead;
1148 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1149 				hist_browser__hpp_color_overhead_sys;
1150 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1151 				hist_browser__hpp_color_overhead_us;
1152 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1153 				hist_browser__hpp_color_overhead_guest_sys;
1154 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1155 				hist_browser__hpp_color_overhead_guest_us;
1156 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1157 				hist_browser__hpp_color_overhead_acc;
1158 }
1159 
1160 static int hist_browser__show_entry(struct hist_browser *browser,
1161 				    struct hist_entry *entry,
1162 				    unsigned short row)
1163 {
1164 	int printed = 0;
1165 	int width = browser->b.width;
1166 	char folded_sign = ' ';
1167 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1168 	off_t row_offset = entry->row_offset;
1169 	bool first = true;
1170 	struct perf_hpp_fmt *fmt;
1171 
1172 	if (current_entry) {
1173 		browser->he_selection = entry;
1174 		browser->selection = &entry->ms;
1175 	}
1176 
1177 	if (symbol_conf.use_callchain) {
1178 		hist_entry__init_have_children(entry);
1179 		folded_sign = hist_entry__folded(entry);
1180 	}
1181 
1182 	if (row_offset == 0) {
1183 		struct hpp_arg arg = {
1184 			.b		= &browser->b,
1185 			.folded_sign	= folded_sign,
1186 			.current_entry	= current_entry,
1187 		};
1188 		int column = 0;
1189 
1190 		hist_browser__gotorc(browser, row, 0);
1191 
1192 		hists__for_each_format(browser->hists, fmt) {
1193 			char s[2048];
1194 			struct perf_hpp hpp = {
1195 				.buf	= s,
1196 				.size	= sizeof(s),
1197 				.ptr	= &arg,
1198 			};
1199 
1200 			if (perf_hpp__should_skip(fmt, entry->hists) ||
1201 			    column++ < browser->b.horiz_scroll)
1202 				continue;
1203 
1204 			if (current_entry && browser->b.navkeypressed) {
1205 				ui_browser__set_color(&browser->b,
1206 						      HE_COLORSET_SELECTED);
1207 			} else {
1208 				ui_browser__set_color(&browser->b,
1209 						      HE_COLORSET_NORMAL);
1210 			}
1211 
1212 			if (first) {
1213 				if (symbol_conf.use_callchain) {
1214 					ui_browser__printf(&browser->b, "%c ", folded_sign);
1215 					width -= 2;
1216 				}
1217 				first = false;
1218 			} else {
1219 				ui_browser__printf(&browser->b, "  ");
1220 				width -= 2;
1221 			}
1222 
1223 			if (fmt->color) {
1224 				int ret = fmt->color(fmt, &hpp, entry);
1225 				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1226 				/*
1227 				 * fmt->color() already used ui_browser to
1228 				 * print the non alignment bits, skip it (+ret):
1229 				 */
1230 				ui_browser__printf(&browser->b, "%s", s + ret);
1231 			} else {
1232 				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1233 				ui_browser__printf(&browser->b, "%s", s);
1234 			}
1235 			width -= hpp.buf - s;
1236 		}
1237 
1238 		/* The scroll bar isn't being used */
1239 		if (!browser->b.navkeypressed)
1240 			width += 1;
1241 
1242 		ui_browser__write_nstring(&browser->b, "", width);
1243 
1244 		++row;
1245 		++printed;
1246 	} else
1247 		--row_offset;
1248 
1249 	if (folded_sign == '-' && row != browser->b.rows) {
1250 		struct callchain_print_arg arg = {
1251 			.row_offset = row_offset,
1252 			.is_current_entry = current_entry,
1253 		};
1254 
1255 		printed += hist_browser__show_callchain(browser, entry, 1, row,
1256 					hist_browser__show_callchain_entry, &arg,
1257 					hist_browser__check_output_full);
1258 	}
1259 
1260 	return printed;
1261 }
1262 
1263 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1264 					      struct hist_entry *entry,
1265 					      unsigned short row,
1266 					      int level)
1267 {
1268 	int printed = 0;
1269 	int width = browser->b.width;
1270 	char folded_sign = ' ';
1271 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1272 	off_t row_offset = entry->row_offset;
1273 	bool first = true;
1274 	struct perf_hpp_fmt *fmt;
1275 	struct perf_hpp_list_node *fmt_node;
1276 	struct hpp_arg arg = {
1277 		.b		= &browser->b,
1278 		.current_entry	= current_entry,
1279 	};
1280 	int column = 0;
1281 	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1282 
1283 	if (current_entry) {
1284 		browser->he_selection = entry;
1285 		browser->selection = &entry->ms;
1286 	}
1287 
1288 	hist_entry__init_have_children(entry);
1289 	folded_sign = hist_entry__folded(entry);
1290 	arg.folded_sign = folded_sign;
1291 
1292 	if (entry->leaf && row_offset) {
1293 		row_offset--;
1294 		goto show_callchain;
1295 	}
1296 
1297 	hist_browser__gotorc(browser, row, 0);
1298 
1299 	if (current_entry && browser->b.navkeypressed)
1300 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1301 	else
1302 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1303 
1304 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1305 	width -= level * HIERARCHY_INDENT;
1306 
1307 	/* the first hpp_list_node is for overhead columns */
1308 	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1309 				    struct perf_hpp_list_node, list);
1310 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1311 		char s[2048];
1312 		struct perf_hpp hpp = {
1313 			.buf		= s,
1314 			.size		= sizeof(s),
1315 			.ptr		= &arg,
1316 		};
1317 
1318 		if (perf_hpp__should_skip(fmt, entry->hists) ||
1319 		    column++ < browser->b.horiz_scroll)
1320 			continue;
1321 
1322 		if (current_entry && browser->b.navkeypressed) {
1323 			ui_browser__set_color(&browser->b,
1324 					      HE_COLORSET_SELECTED);
1325 		} else {
1326 			ui_browser__set_color(&browser->b,
1327 					      HE_COLORSET_NORMAL);
1328 		}
1329 
1330 		if (first) {
1331 			ui_browser__printf(&browser->b, "%c", folded_sign);
1332 			width--;
1333 			first = false;
1334 		} else {
1335 			ui_browser__printf(&browser->b, "  ");
1336 			width -= 2;
1337 		}
1338 
1339 		if (fmt->color) {
1340 			int ret = fmt->color(fmt, &hpp, entry);
1341 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1342 			/*
1343 			 * fmt->color() already used ui_browser to
1344 			 * print the non alignment bits, skip it (+ret):
1345 			 */
1346 			ui_browser__printf(&browser->b, "%s", s + ret);
1347 		} else {
1348 			int ret = fmt->entry(fmt, &hpp, entry);
1349 			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1350 			ui_browser__printf(&browser->b, "%s", s);
1351 		}
1352 		width -= hpp.buf - s;
1353 	}
1354 
1355 	ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1356 	width -= hierarchy_indent;
1357 
1358 	if (column >= browser->b.horiz_scroll) {
1359 		char s[2048];
1360 		struct perf_hpp hpp = {
1361 			.buf		= s,
1362 			.size		= sizeof(s),
1363 			.ptr		= &arg,
1364 		};
1365 
1366 		if (current_entry && browser->b.navkeypressed) {
1367 			ui_browser__set_color(&browser->b,
1368 					      HE_COLORSET_SELECTED);
1369 		} else {
1370 			ui_browser__set_color(&browser->b,
1371 					      HE_COLORSET_NORMAL);
1372 		}
1373 
1374 		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1375 			ui_browser__write_nstring(&browser->b, "", 2);
1376 			width -= 2;
1377 
1378 			/*
1379 			 * No need to call hist_entry__snprintf_alignment()
1380 			 * since this fmt is always the last column in the
1381 			 * hierarchy mode.
1382 			 */
1383 			if (fmt->color) {
1384 				width -= fmt->color(fmt, &hpp, entry);
1385 			} else {
1386 				int i = 0;
1387 
1388 				width -= fmt->entry(fmt, &hpp, entry);
1389 				ui_browser__printf(&browser->b, "%s", ltrim(s));
1390 
1391 				while (isspace(s[i++]))
1392 					width++;
1393 			}
1394 		}
1395 	}
1396 
1397 	/* The scroll bar isn't being used */
1398 	if (!browser->b.navkeypressed)
1399 		width += 1;
1400 
1401 	ui_browser__write_nstring(&browser->b, "", width);
1402 
1403 	++row;
1404 	++printed;
1405 
1406 show_callchain:
1407 	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1408 		struct callchain_print_arg carg = {
1409 			.row_offset = row_offset,
1410 		};
1411 
1412 		printed += hist_browser__show_callchain(browser, entry,
1413 					level + 1, row,
1414 					hist_browser__show_callchain_entry, &carg,
1415 					hist_browser__check_output_full);
1416 	}
1417 
1418 	return printed;
1419 }
1420 
1421 static int hist_browser__show_no_entry(struct hist_browser *browser,
1422 				       unsigned short row, int level)
1423 {
1424 	int width = browser->b.width;
1425 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1426 	bool first = true;
1427 	int column = 0;
1428 	int ret;
1429 	struct perf_hpp_fmt *fmt;
1430 	struct perf_hpp_list_node *fmt_node;
1431 	int indent = browser->hists->nr_hpp_node - 2;
1432 
1433 	if (current_entry) {
1434 		browser->he_selection = NULL;
1435 		browser->selection = NULL;
1436 	}
1437 
1438 	hist_browser__gotorc(browser, row, 0);
1439 
1440 	if (current_entry && browser->b.navkeypressed)
1441 		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1442 	else
1443 		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1444 
1445 	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1446 	width -= level * HIERARCHY_INDENT;
1447 
1448 	/* the first hpp_list_node is for overhead columns */
1449 	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1450 				    struct perf_hpp_list_node, list);
1451 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1452 		if (perf_hpp__should_skip(fmt, browser->hists) ||
1453 		    column++ < browser->b.horiz_scroll)
1454 			continue;
1455 
1456 		ret = fmt->width(fmt, NULL, browser->hists);
1457 
1458 		if (first) {
1459 			/* for folded sign */
1460 			first = false;
1461 			ret++;
1462 		} else {
1463 			/* space between columns */
1464 			ret += 2;
1465 		}
1466 
1467 		ui_browser__write_nstring(&browser->b, "", ret);
1468 		width -= ret;
1469 	}
1470 
1471 	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1472 	width -= indent * HIERARCHY_INDENT;
1473 
1474 	if (column >= browser->b.horiz_scroll) {
1475 		char buf[32];
1476 
1477 		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1478 		ui_browser__printf(&browser->b, "  %s", buf);
1479 		width -= ret + 2;
1480 	}
1481 
1482 	/* The scroll bar isn't being used */
1483 	if (!browser->b.navkeypressed)
1484 		width += 1;
1485 
1486 	ui_browser__write_nstring(&browser->b, "", width);
1487 	return 1;
1488 }
1489 
1490 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1491 {
1492 	advance_hpp(hpp, inc);
1493 	return hpp->size <= 0;
1494 }
1495 
1496 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
1497 {
1498 	struct hists *hists = browser->hists;
1499 	struct perf_hpp dummy_hpp = {
1500 		.buf    = buf,
1501 		.size   = size,
1502 	};
1503 	struct perf_hpp_fmt *fmt;
1504 	size_t ret = 0;
1505 	int column = 0;
1506 
1507 	if (symbol_conf.use_callchain) {
1508 		ret = scnprintf(buf, size, "  ");
1509 		if (advance_hpp_check(&dummy_hpp, ret))
1510 			return ret;
1511 	}
1512 
1513 	hists__for_each_format(browser->hists, fmt) {
1514 		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1515 			continue;
1516 
1517 		ret = fmt->header(fmt, &dummy_hpp, hists);
1518 		if (advance_hpp_check(&dummy_hpp, ret))
1519 			break;
1520 
1521 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1522 		if (advance_hpp_check(&dummy_hpp, ret))
1523 			break;
1524 	}
1525 
1526 	return ret;
1527 }
1528 
1529 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1530 {
1531 	struct hists *hists = browser->hists;
1532 	struct perf_hpp dummy_hpp = {
1533 		.buf    = buf,
1534 		.size   = size,
1535 	};
1536 	struct perf_hpp_fmt *fmt;
1537 	struct perf_hpp_list_node *fmt_node;
1538 	size_t ret = 0;
1539 	int column = 0;
1540 	int indent = hists->nr_hpp_node - 2;
1541 	bool first_node, first_col;
1542 
1543 	ret = scnprintf(buf, size, " ");
1544 	if (advance_hpp_check(&dummy_hpp, ret))
1545 		return ret;
1546 
1547 	/* the first hpp_list_node is for overhead columns */
1548 	fmt_node = list_first_entry(&hists->hpp_formats,
1549 				    struct perf_hpp_list_node, list);
1550 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1551 		if (column++ < browser->b.horiz_scroll)
1552 			continue;
1553 
1554 		ret = fmt->header(fmt, &dummy_hpp, hists);
1555 		if (advance_hpp_check(&dummy_hpp, ret))
1556 			break;
1557 
1558 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1559 		if (advance_hpp_check(&dummy_hpp, ret))
1560 			break;
1561 	}
1562 
1563 	ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1564 			indent * HIERARCHY_INDENT, "");
1565 	if (advance_hpp_check(&dummy_hpp, ret))
1566 		return ret;
1567 
1568 	first_node = true;
1569 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1570 		if (!first_node) {
1571 			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1572 			if (advance_hpp_check(&dummy_hpp, ret))
1573 				break;
1574 		}
1575 		first_node = false;
1576 
1577 		first_col = true;
1578 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1579 			char *start;
1580 
1581 			if (perf_hpp__should_skip(fmt, hists))
1582 				continue;
1583 
1584 			if (!first_col) {
1585 				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1586 				if (advance_hpp_check(&dummy_hpp, ret))
1587 					break;
1588 			}
1589 			first_col = false;
1590 
1591 			ret = fmt->header(fmt, &dummy_hpp, hists);
1592 			dummy_hpp.buf[ret] = '\0';
1593 
1594 			start = trim(dummy_hpp.buf);
1595 			ret = strlen(start);
1596 
1597 			if (start != dummy_hpp.buf)
1598 				memmove(dummy_hpp.buf, start, ret + 1);
1599 
1600 			if (advance_hpp_check(&dummy_hpp, ret))
1601 				break;
1602 		}
1603 	}
1604 
1605 	return ret;
1606 }
1607 
1608 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1609 {
1610 	char headers[1024];
1611 
1612 	hists_browser__scnprintf_hierarchy_headers(browser, headers,
1613 						   sizeof(headers));
1614 
1615 	ui_browser__gotorc(&browser->b, 0, 0);
1616 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1617 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1618 }
1619 
1620 static void hists_browser__headers(struct hist_browser *browser)
1621 {
1622 	char headers[1024];
1623 
1624 	hists_browser__scnprintf_headers(browser, headers,
1625 					 sizeof(headers));
1626 
1627 	ui_browser__gotorc(&browser->b, 0, 0);
1628 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1629 	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1630 }
1631 
1632 static void hist_browser__show_headers(struct hist_browser *browser)
1633 {
1634 	if (symbol_conf.report_hierarchy)
1635 		hists_browser__hierarchy_headers(browser);
1636 	else
1637 		hists_browser__headers(browser);
1638 }
1639 
1640 static void ui_browser__hists_init_top(struct ui_browser *browser)
1641 {
1642 	if (browser->top == NULL) {
1643 		struct hist_browser *hb;
1644 
1645 		hb = container_of(browser, struct hist_browser, b);
1646 		browser->top = rb_first(&hb->hists->entries);
1647 	}
1648 }
1649 
1650 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1651 {
1652 	unsigned row = 0;
1653 	u16 header_offset = 0;
1654 	struct rb_node *nd;
1655 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1656 
1657 	if (hb->show_headers) {
1658 		hist_browser__show_headers(hb);
1659 		header_offset = 1;
1660 	}
1661 
1662 	ui_browser__hists_init_top(browser);
1663 	hb->he_selection = NULL;
1664 	hb->selection = NULL;
1665 
1666 	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1667 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1668 		float percent;
1669 
1670 		if (h->filtered) {
1671 			/* let it move to sibling */
1672 			h->unfolded = false;
1673 			continue;
1674 		}
1675 
1676 		percent = hist_entry__get_percent_limit(h);
1677 		if (percent < hb->min_pcnt)
1678 			continue;
1679 
1680 		if (symbol_conf.report_hierarchy) {
1681 			row += hist_browser__show_hierarchy_entry(hb, h, row,
1682 								  h->depth);
1683 			if (row == browser->rows)
1684 				break;
1685 
1686 			if (h->has_no_entry) {
1687 				hist_browser__show_no_entry(hb, row, h->depth + 1);
1688 				row++;
1689 			}
1690 		} else {
1691 			row += hist_browser__show_entry(hb, h, row);
1692 		}
1693 
1694 		if (row == browser->rows)
1695 			break;
1696 	}
1697 
1698 	return row + header_offset;
1699 }
1700 
1701 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1702 					     float min_pcnt)
1703 {
1704 	while (nd != NULL) {
1705 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1706 		float percent = hist_entry__get_percent_limit(h);
1707 
1708 		if (!h->filtered && percent >= min_pcnt)
1709 			return nd;
1710 
1711 		/*
1712 		 * If it's filtered, its all children also were filtered.
1713 		 * So move to sibling node.
1714 		 */
1715 		if (rb_next(nd))
1716 			nd = rb_next(nd);
1717 		else
1718 			nd = rb_hierarchy_next(nd);
1719 	}
1720 
1721 	return NULL;
1722 }
1723 
1724 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1725 						  float min_pcnt)
1726 {
1727 	while (nd != NULL) {
1728 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1729 		float percent = hist_entry__get_percent_limit(h);
1730 
1731 		if (!h->filtered && percent >= min_pcnt)
1732 			return nd;
1733 
1734 		nd = rb_hierarchy_prev(nd);
1735 	}
1736 
1737 	return NULL;
1738 }
1739 
1740 static void ui_browser__hists_seek(struct ui_browser *browser,
1741 				   off_t offset, int whence)
1742 {
1743 	struct hist_entry *h;
1744 	struct rb_node *nd;
1745 	bool first = true;
1746 	struct hist_browser *hb;
1747 
1748 	hb = container_of(browser, struct hist_browser, b);
1749 
1750 	if (browser->nr_entries == 0)
1751 		return;
1752 
1753 	ui_browser__hists_init_top(browser);
1754 
1755 	switch (whence) {
1756 	case SEEK_SET:
1757 		nd = hists__filter_entries(rb_first(browser->entries),
1758 					   hb->min_pcnt);
1759 		break;
1760 	case SEEK_CUR:
1761 		nd = browser->top;
1762 		goto do_offset;
1763 	case SEEK_END:
1764 		nd = rb_hierarchy_last(rb_last(browser->entries));
1765 		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1766 		first = false;
1767 		break;
1768 	default:
1769 		return;
1770 	}
1771 
1772 	/*
1773 	 * Moves not relative to the first visible entry invalidates its
1774 	 * row_offset:
1775 	 */
1776 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1777 	h->row_offset = 0;
1778 
1779 	/*
1780 	 * Here we have to check if nd is expanded (+), if it is we can't go
1781 	 * the next top level hist_entry, instead we must compute an offset of
1782 	 * what _not_ to show and not change the first visible entry.
1783 	 *
1784 	 * This offset increments when we are going from top to bottom and
1785 	 * decreases when we're going from bottom to top.
1786 	 *
1787 	 * As we don't have backpointers to the top level in the callchains
1788 	 * structure, we need to always print the whole hist_entry callchain,
1789 	 * skipping the first ones that are before the first visible entry
1790 	 * and stop when we printed enough lines to fill the screen.
1791 	 */
1792 do_offset:
1793 	if (!nd)
1794 		return;
1795 
1796 	if (offset > 0) {
1797 		do {
1798 			h = rb_entry(nd, struct hist_entry, rb_node);
1799 			if (h->unfolded && h->leaf) {
1800 				u16 remaining = h->nr_rows - h->row_offset;
1801 				if (offset > remaining) {
1802 					offset -= remaining;
1803 					h->row_offset = 0;
1804 				} else {
1805 					h->row_offset += offset;
1806 					offset = 0;
1807 					browser->top = nd;
1808 					break;
1809 				}
1810 			}
1811 			nd = hists__filter_entries(rb_hierarchy_next(nd),
1812 						   hb->min_pcnt);
1813 			if (nd == NULL)
1814 				break;
1815 			--offset;
1816 			browser->top = nd;
1817 		} while (offset != 0);
1818 	} else if (offset < 0) {
1819 		while (1) {
1820 			h = rb_entry(nd, struct hist_entry, rb_node);
1821 			if (h->unfolded && h->leaf) {
1822 				if (first) {
1823 					if (-offset > h->row_offset) {
1824 						offset += h->row_offset;
1825 						h->row_offset = 0;
1826 					} else {
1827 						h->row_offset += offset;
1828 						offset = 0;
1829 						browser->top = nd;
1830 						break;
1831 					}
1832 				} else {
1833 					if (-offset > h->nr_rows) {
1834 						offset += h->nr_rows;
1835 						h->row_offset = 0;
1836 					} else {
1837 						h->row_offset = h->nr_rows + offset;
1838 						offset = 0;
1839 						browser->top = nd;
1840 						break;
1841 					}
1842 				}
1843 			}
1844 
1845 			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1846 							hb->min_pcnt);
1847 			if (nd == NULL)
1848 				break;
1849 			++offset;
1850 			browser->top = nd;
1851 			if (offset == 0) {
1852 				/*
1853 				 * Last unfiltered hist_entry, check if it is
1854 				 * unfolded, if it is then we should have
1855 				 * row_offset at its last entry.
1856 				 */
1857 				h = rb_entry(nd, struct hist_entry, rb_node);
1858 				if (h->unfolded && h->leaf)
1859 					h->row_offset = h->nr_rows;
1860 				break;
1861 			}
1862 			first = false;
1863 		}
1864 	} else {
1865 		browser->top = nd;
1866 		h = rb_entry(nd, struct hist_entry, rb_node);
1867 		h->row_offset = 0;
1868 	}
1869 }
1870 
1871 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1872 					   struct hist_entry *he, FILE *fp,
1873 					   int level)
1874 {
1875 	struct callchain_print_arg arg  = {
1876 		.fp = fp,
1877 	};
1878 
1879 	hist_browser__show_callchain(browser, he, level, 0,
1880 				     hist_browser__fprintf_callchain_entry, &arg,
1881 				     hist_browser__check_dump_full);
1882 	return arg.printed;
1883 }
1884 
1885 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1886 				       struct hist_entry *he, FILE *fp)
1887 {
1888 	char s[8192];
1889 	int printed = 0;
1890 	char folded_sign = ' ';
1891 	struct perf_hpp hpp = {
1892 		.buf = s,
1893 		.size = sizeof(s),
1894 	};
1895 	struct perf_hpp_fmt *fmt;
1896 	bool first = true;
1897 	int ret;
1898 
1899 	if (symbol_conf.use_callchain) {
1900 		folded_sign = hist_entry__folded(he);
1901 		printed += fprintf(fp, "%c ", folded_sign);
1902 	}
1903 
1904 	hists__for_each_format(browser->hists, fmt) {
1905 		if (perf_hpp__should_skip(fmt, he->hists))
1906 			continue;
1907 
1908 		if (!first) {
1909 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1910 			advance_hpp(&hpp, ret);
1911 		} else
1912 			first = false;
1913 
1914 		ret = fmt->entry(fmt, &hpp, he);
1915 		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1916 		advance_hpp(&hpp, ret);
1917 	}
1918 	printed += fprintf(fp, "%s\n", s);
1919 
1920 	if (folded_sign == '-')
1921 		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1922 
1923 	return printed;
1924 }
1925 
1926 
1927 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1928 						 struct hist_entry *he,
1929 						 FILE *fp, int level)
1930 {
1931 	char s[8192];
1932 	int printed = 0;
1933 	char folded_sign = ' ';
1934 	struct perf_hpp hpp = {
1935 		.buf = s,
1936 		.size = sizeof(s),
1937 	};
1938 	struct perf_hpp_fmt *fmt;
1939 	struct perf_hpp_list_node *fmt_node;
1940 	bool first = true;
1941 	int ret;
1942 	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1943 
1944 	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1945 
1946 	folded_sign = hist_entry__folded(he);
1947 	printed += fprintf(fp, "%c", folded_sign);
1948 
1949 	/* the first hpp_list_node is for overhead columns */
1950 	fmt_node = list_first_entry(&he->hists->hpp_formats,
1951 				    struct perf_hpp_list_node, list);
1952 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1953 		if (!first) {
1954 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1955 			advance_hpp(&hpp, ret);
1956 		} else
1957 			first = false;
1958 
1959 		ret = fmt->entry(fmt, &hpp, he);
1960 		advance_hpp(&hpp, ret);
1961 	}
1962 
1963 	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1964 	advance_hpp(&hpp, ret);
1965 
1966 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1967 		ret = scnprintf(hpp.buf, hpp.size, "  ");
1968 		advance_hpp(&hpp, ret);
1969 
1970 		ret = fmt->entry(fmt, &hpp, he);
1971 		advance_hpp(&hpp, ret);
1972 	}
1973 
1974 	printed += fprintf(fp, "%s\n", rtrim(s));
1975 
1976 	if (he->leaf && folded_sign == '-') {
1977 		printed += hist_browser__fprintf_callchain(browser, he, fp,
1978 							   he->depth + 1);
1979 	}
1980 
1981 	return printed;
1982 }
1983 
1984 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1985 {
1986 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1987 						   browser->min_pcnt);
1988 	int printed = 0;
1989 
1990 	while (nd) {
1991 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1992 
1993 		if (symbol_conf.report_hierarchy) {
1994 			printed += hist_browser__fprintf_hierarchy_entry(browser,
1995 									 h, fp,
1996 									 h->depth);
1997 		} else {
1998 			printed += hist_browser__fprintf_entry(browser, h, fp);
1999 		}
2000 
2001 		nd = hists__filter_entries(rb_hierarchy_next(nd),
2002 					   browser->min_pcnt);
2003 	}
2004 
2005 	return printed;
2006 }
2007 
2008 static int hist_browser__dump(struct hist_browser *browser)
2009 {
2010 	char filename[64];
2011 	FILE *fp;
2012 
2013 	while (1) {
2014 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2015 		if (access(filename, F_OK))
2016 			break;
2017 		/*
2018  		 * XXX: Just an arbitrary lazy upper limit
2019  		 */
2020 		if (++browser->print_seq == 8192) {
2021 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2022 			return -1;
2023 		}
2024 	}
2025 
2026 	fp = fopen(filename, "w");
2027 	if (fp == NULL) {
2028 		char bf[64];
2029 		const char *err = strerror_r(errno, bf, sizeof(bf));
2030 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2031 		return -1;
2032 	}
2033 
2034 	++browser->print_seq;
2035 	hist_browser__fprintf(browser, fp);
2036 	fclose(fp);
2037 	ui_helpline__fpush("%s written!", filename);
2038 
2039 	return 0;
2040 }
2041 
2042 struct hist_browser *hist_browser__new(struct hists *hists,
2043 				       struct hist_browser_timer *hbt,
2044 				       struct perf_env *env)
2045 {
2046 	struct hist_browser *browser = zalloc(sizeof(*browser));
2047 
2048 	if (browser) {
2049 		browser->hists = hists;
2050 		browser->b.refresh = hist_browser__refresh;
2051 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2052 		browser->b.seek = ui_browser__hists_seek;
2053 		browser->b.use_navkeypressed = true;
2054 		browser->show_headers = symbol_conf.show_hist_headers;
2055 		browser->hbt = hbt;
2056 		browser->env = env;
2057 	}
2058 
2059 	return browser;
2060 }
2061 
2062 void hist_browser__delete(struct hist_browser *browser)
2063 {
2064 	free(browser);
2065 }
2066 
2067 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2068 {
2069 	return browser->he_selection;
2070 }
2071 
2072 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2073 {
2074 	return browser->he_selection->thread;
2075 }
2076 
2077 /* Check whether the browser is for 'top' or 'report' */
2078 static inline bool is_report_browser(void *timer)
2079 {
2080 	return timer == NULL;
2081 }
2082 
2083 static int hists__browser_title(struct hists *hists,
2084 				struct hist_browser_timer *hbt,
2085 				char *bf, size_t size)
2086 {
2087 	char unit;
2088 	int printed;
2089 	const struct dso *dso = hists->dso_filter;
2090 	const struct thread *thread = hists->thread_filter;
2091 	int socket_id = hists->socket_filter;
2092 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2093 	u64 nr_events = hists->stats.total_period;
2094 	struct perf_evsel *evsel = hists_to_evsel(hists);
2095 	const char *ev_name = perf_evsel__name(evsel);
2096 	char buf[512];
2097 	size_t buflen = sizeof(buf);
2098 	char ref[30] = " show reference callgraph, ";
2099 	bool enable_ref = false;
2100 
2101 	if (symbol_conf.filter_relative) {
2102 		nr_samples = hists->stats.nr_non_filtered_samples;
2103 		nr_events = hists->stats.total_non_filtered_period;
2104 	}
2105 
2106 	if (perf_evsel__is_group_event(evsel)) {
2107 		struct perf_evsel *pos;
2108 
2109 		perf_evsel__group_desc(evsel, buf, buflen);
2110 		ev_name = buf;
2111 
2112 		for_each_group_member(pos, evsel) {
2113 			struct hists *pos_hists = evsel__hists(pos);
2114 
2115 			if (symbol_conf.filter_relative) {
2116 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
2117 				nr_events += pos_hists->stats.total_non_filtered_period;
2118 			} else {
2119 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2120 				nr_events += pos_hists->stats.total_period;
2121 			}
2122 		}
2123 	}
2124 
2125 	if (symbol_conf.show_ref_callgraph &&
2126 	    strstr(ev_name, "call-graph=no"))
2127 		enable_ref = true;
2128 	nr_samples = convert_unit(nr_samples, &unit);
2129 	printed = scnprintf(bf, size,
2130 			   "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2131 			   nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2132 
2133 
2134 	if (hists->uid_filter_str)
2135 		printed += snprintf(bf + printed, size - printed,
2136 				    ", UID: %s", hists->uid_filter_str);
2137 	if (thread) {
2138 		if (hists__has(hists, thread)) {
2139 			printed += scnprintf(bf + printed, size - printed,
2140 				    ", Thread: %s(%d)",
2141 				     (thread->comm_set ? thread__comm_str(thread) : ""),
2142 				    thread->tid);
2143 		} else {
2144 			printed += scnprintf(bf + printed, size - printed,
2145 				    ", Thread: %s",
2146 				     (thread->comm_set ? thread__comm_str(thread) : ""));
2147 		}
2148 	}
2149 	if (dso)
2150 		printed += scnprintf(bf + printed, size - printed,
2151 				    ", DSO: %s", dso->short_name);
2152 	if (socket_id > -1)
2153 		printed += scnprintf(bf + printed, size - printed,
2154 				    ", Processor Socket: %d", socket_id);
2155 	if (!is_report_browser(hbt)) {
2156 		struct perf_top *top = hbt->arg;
2157 
2158 		if (top->zero)
2159 			printed += scnprintf(bf + printed, size - printed, " [z]");
2160 	}
2161 
2162 	return printed;
2163 }
2164 
2165 static inline void free_popup_options(char **options, int n)
2166 {
2167 	int i;
2168 
2169 	for (i = 0; i < n; ++i)
2170 		zfree(&options[i]);
2171 }
2172 
2173 /*
2174  * Only runtime switching of perf data file will make "input_name" point
2175  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2176  * whether we need to call free() for current "input_name" during the switch.
2177  */
2178 static bool is_input_name_malloced = false;
2179 
2180 static int switch_data_file(void)
2181 {
2182 	char *pwd, *options[32], *abs_path[32], *tmp;
2183 	DIR *pwd_dir;
2184 	int nr_options = 0, choice = -1, ret = -1;
2185 	struct dirent *dent;
2186 
2187 	pwd = getenv("PWD");
2188 	if (!pwd)
2189 		return ret;
2190 
2191 	pwd_dir = opendir(pwd);
2192 	if (!pwd_dir)
2193 		return ret;
2194 
2195 	memset(options, 0, sizeof(options));
2196 	memset(options, 0, sizeof(abs_path));
2197 
2198 	while ((dent = readdir(pwd_dir))) {
2199 		char path[PATH_MAX];
2200 		u64 magic;
2201 		char *name = dent->d_name;
2202 		FILE *file;
2203 
2204 		if (!(dent->d_type == DT_REG))
2205 			continue;
2206 
2207 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2208 
2209 		file = fopen(path, "r");
2210 		if (!file)
2211 			continue;
2212 
2213 		if (fread(&magic, 1, 8, file) < 8)
2214 			goto close_file_and_continue;
2215 
2216 		if (is_perf_magic(magic)) {
2217 			options[nr_options] = strdup(name);
2218 			if (!options[nr_options])
2219 				goto close_file_and_continue;
2220 
2221 			abs_path[nr_options] = strdup(path);
2222 			if (!abs_path[nr_options]) {
2223 				zfree(&options[nr_options]);
2224 				ui__warning("Can't search all data files due to memory shortage.\n");
2225 				fclose(file);
2226 				break;
2227 			}
2228 
2229 			nr_options++;
2230 		}
2231 
2232 close_file_and_continue:
2233 		fclose(file);
2234 		if (nr_options >= 32) {
2235 			ui__warning("Too many perf data files in PWD!\n"
2236 				    "Only the first 32 files will be listed.\n");
2237 			break;
2238 		}
2239 	}
2240 	closedir(pwd_dir);
2241 
2242 	if (nr_options) {
2243 		choice = ui__popup_menu(nr_options, options);
2244 		if (choice < nr_options && choice >= 0) {
2245 			tmp = strdup(abs_path[choice]);
2246 			if (tmp) {
2247 				if (is_input_name_malloced)
2248 					free((void *)input_name);
2249 				input_name = tmp;
2250 				is_input_name_malloced = true;
2251 				ret = 0;
2252 			} else
2253 				ui__warning("Data switch failed due to memory shortage!\n");
2254 		}
2255 	}
2256 
2257 	free_popup_options(options, nr_options);
2258 	free_popup_options(abs_path, nr_options);
2259 	return ret;
2260 }
2261 
2262 struct popup_action {
2263 	struct thread 		*thread;
2264 	struct map_symbol 	ms;
2265 	int			socket;
2266 
2267 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2268 };
2269 
2270 static int
2271 do_annotate(struct hist_browser *browser, struct popup_action *act)
2272 {
2273 	struct perf_evsel *evsel;
2274 	struct annotation *notes;
2275 	struct hist_entry *he;
2276 	int err;
2277 
2278 	if (!objdump_path && perf_env__lookup_objdump(browser->env))
2279 		return 0;
2280 
2281 	notes = symbol__annotation(act->ms.sym);
2282 	if (!notes->src)
2283 		return 0;
2284 
2285 	evsel = hists_to_evsel(browser->hists);
2286 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2287 	he = hist_browser__selected_entry(browser);
2288 	/*
2289 	 * offer option to annotate the other branch source or target
2290 	 * (if they exists) when returning from annotate
2291 	 */
2292 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2293 		return 1;
2294 
2295 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2296 	if (err)
2297 		ui_browser__handle_resize(&browser->b);
2298 	return 0;
2299 }
2300 
2301 static int
2302 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2303 		 struct popup_action *act, char **optstr,
2304 		 struct map *map, struct symbol *sym)
2305 {
2306 	if (sym == NULL || map->dso->annotate_warned)
2307 		return 0;
2308 
2309 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2310 		return 0;
2311 
2312 	act->ms.map = map;
2313 	act->ms.sym = sym;
2314 	act->fn = do_annotate;
2315 	return 1;
2316 }
2317 
2318 static int
2319 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2320 {
2321 	struct thread *thread = act->thread;
2322 
2323 	if ((!hists__has(browser->hists, thread) &&
2324 	     !hists__has(browser->hists, comm)) || thread == NULL)
2325 		return 0;
2326 
2327 	if (browser->hists->thread_filter) {
2328 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2329 		perf_hpp__set_elide(HISTC_THREAD, false);
2330 		thread__zput(browser->hists->thread_filter);
2331 		ui_helpline__pop();
2332 	} else {
2333 		if (hists__has(browser->hists, thread)) {
2334 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2335 					   thread->comm_set ? thread__comm_str(thread) : "",
2336 					   thread->tid);
2337 		} else {
2338 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2339 					   thread->comm_set ? thread__comm_str(thread) : "");
2340 		}
2341 
2342 		browser->hists->thread_filter = thread__get(thread);
2343 		perf_hpp__set_elide(HISTC_THREAD, false);
2344 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2345 	}
2346 
2347 	hists__filter_by_thread(browser->hists);
2348 	hist_browser__reset(browser);
2349 	return 0;
2350 }
2351 
2352 static int
2353 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2354 	       char **optstr, struct thread *thread)
2355 {
2356 	int ret;
2357 
2358 	if ((!hists__has(browser->hists, thread) &&
2359 	     !hists__has(browser->hists, comm)) || thread == NULL)
2360 		return 0;
2361 
2362 	if (hists__has(browser->hists, thread)) {
2363 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2364 			       browser->hists->thread_filter ? "out of" : "into",
2365 			       thread->comm_set ? thread__comm_str(thread) : "",
2366 			       thread->tid);
2367 	} else {
2368 		ret = asprintf(optstr, "Zoom %s %s thread",
2369 			       browser->hists->thread_filter ? "out of" : "into",
2370 			       thread->comm_set ? thread__comm_str(thread) : "");
2371 	}
2372 	if (ret < 0)
2373 		return 0;
2374 
2375 	act->thread = thread;
2376 	act->fn = do_zoom_thread;
2377 	return 1;
2378 }
2379 
2380 static int
2381 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2382 {
2383 	struct map *map = act->ms.map;
2384 
2385 	if (!hists__has(browser->hists, dso) || map == NULL)
2386 		return 0;
2387 
2388 	if (browser->hists->dso_filter) {
2389 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2390 		perf_hpp__set_elide(HISTC_DSO, false);
2391 		browser->hists->dso_filter = NULL;
2392 		ui_helpline__pop();
2393 	} else {
2394 		if (map == NULL)
2395 			return 0;
2396 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2397 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2398 		browser->hists->dso_filter = map->dso;
2399 		perf_hpp__set_elide(HISTC_DSO, true);
2400 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2401 	}
2402 
2403 	hists__filter_by_dso(browser->hists);
2404 	hist_browser__reset(browser);
2405 	return 0;
2406 }
2407 
2408 static int
2409 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2410 	    char **optstr, struct map *map)
2411 {
2412 	if (!hists__has(browser->hists, dso) || map == NULL)
2413 		return 0;
2414 
2415 	if (asprintf(optstr, "Zoom %s %s DSO",
2416 		     browser->hists->dso_filter ? "out of" : "into",
2417 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2418 		return 0;
2419 
2420 	act->ms.map = map;
2421 	act->fn = do_zoom_dso;
2422 	return 1;
2423 }
2424 
2425 static int
2426 do_browse_map(struct hist_browser *browser __maybe_unused,
2427 	      struct popup_action *act)
2428 {
2429 	map__browse(act->ms.map);
2430 	return 0;
2431 }
2432 
2433 static int
2434 add_map_opt(struct hist_browser *browser,
2435 	    struct popup_action *act, char **optstr, struct map *map)
2436 {
2437 	if (!hists__has(browser->hists, dso) || map == NULL)
2438 		return 0;
2439 
2440 	if (asprintf(optstr, "Browse map details") < 0)
2441 		return 0;
2442 
2443 	act->ms.map = map;
2444 	act->fn = do_browse_map;
2445 	return 1;
2446 }
2447 
2448 static int
2449 do_run_script(struct hist_browser *browser __maybe_unused,
2450 	      struct popup_action *act)
2451 {
2452 	char script_opt[64];
2453 	memset(script_opt, 0, sizeof(script_opt));
2454 
2455 	if (act->thread) {
2456 		scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2457 			  thread__comm_str(act->thread));
2458 	} else if (act->ms.sym) {
2459 		scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2460 			  act->ms.sym->name);
2461 	}
2462 
2463 	script_browse(script_opt);
2464 	return 0;
2465 }
2466 
2467 static int
2468 add_script_opt(struct hist_browser *browser __maybe_unused,
2469 	       struct popup_action *act, char **optstr,
2470 	       struct thread *thread, struct symbol *sym)
2471 {
2472 	if (thread) {
2473 		if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2474 			     thread__comm_str(thread)) < 0)
2475 			return 0;
2476 	} else if (sym) {
2477 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2478 			     sym->name) < 0)
2479 			return 0;
2480 	} else {
2481 		if (asprintf(optstr, "Run scripts for all samples") < 0)
2482 			return 0;
2483 	}
2484 
2485 	act->thread = thread;
2486 	act->ms.sym = sym;
2487 	act->fn = do_run_script;
2488 	return 1;
2489 }
2490 
2491 static int
2492 do_switch_data(struct hist_browser *browser __maybe_unused,
2493 	       struct popup_action *act __maybe_unused)
2494 {
2495 	if (switch_data_file()) {
2496 		ui__warning("Won't switch the data files due to\n"
2497 			    "no valid data file get selected!\n");
2498 		return 0;
2499 	}
2500 
2501 	return K_SWITCH_INPUT_DATA;
2502 }
2503 
2504 static int
2505 add_switch_opt(struct hist_browser *browser,
2506 	       struct popup_action *act, char **optstr)
2507 {
2508 	if (!is_report_browser(browser->hbt))
2509 		return 0;
2510 
2511 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2512 		return 0;
2513 
2514 	act->fn = do_switch_data;
2515 	return 1;
2516 }
2517 
2518 static int
2519 do_exit_browser(struct hist_browser *browser __maybe_unused,
2520 		struct popup_action *act __maybe_unused)
2521 {
2522 	return 0;
2523 }
2524 
2525 static int
2526 add_exit_opt(struct hist_browser *browser __maybe_unused,
2527 	     struct popup_action *act, char **optstr)
2528 {
2529 	if (asprintf(optstr, "Exit") < 0)
2530 		return 0;
2531 
2532 	act->fn = do_exit_browser;
2533 	return 1;
2534 }
2535 
2536 static int
2537 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2538 {
2539 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2540 		return 0;
2541 
2542 	if (browser->hists->socket_filter > -1) {
2543 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2544 		browser->hists->socket_filter = -1;
2545 		perf_hpp__set_elide(HISTC_SOCKET, false);
2546 	} else {
2547 		browser->hists->socket_filter = act->socket;
2548 		perf_hpp__set_elide(HISTC_SOCKET, true);
2549 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2550 	}
2551 
2552 	hists__filter_by_socket(browser->hists);
2553 	hist_browser__reset(browser);
2554 	return 0;
2555 }
2556 
2557 static int
2558 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2559 	       char **optstr, int socket_id)
2560 {
2561 	if (!hists__has(browser->hists, socket) || socket_id < 0)
2562 		return 0;
2563 
2564 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2565 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2566 		     socket_id) < 0)
2567 		return 0;
2568 
2569 	act->socket = socket_id;
2570 	act->fn = do_zoom_socket;
2571 	return 1;
2572 }
2573 
2574 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2575 {
2576 	u64 nr_entries = 0;
2577 	struct rb_node *nd = rb_first(&hb->hists->entries);
2578 
2579 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2580 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2581 		return;
2582 	}
2583 
2584 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2585 		nr_entries++;
2586 		nd = rb_hierarchy_next(nd);
2587 	}
2588 
2589 	hb->nr_non_filtered_entries = nr_entries;
2590 	hb->nr_hierarchy_entries = nr_entries;
2591 }
2592 
2593 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2594 					       double percent)
2595 {
2596 	struct hist_entry *he;
2597 	struct rb_node *nd = rb_first(&hb->hists->entries);
2598 	u64 total = hists__total_period(hb->hists);
2599 	u64 min_callchain_hits = total * (percent / 100);
2600 
2601 	hb->min_pcnt = callchain_param.min_percent = percent;
2602 
2603 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2604 		he = rb_entry(nd, struct hist_entry, rb_node);
2605 
2606 		if (he->has_no_entry) {
2607 			he->has_no_entry = false;
2608 			he->nr_rows = 0;
2609 		}
2610 
2611 		if (!he->leaf || !symbol_conf.use_callchain)
2612 			goto next;
2613 
2614 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2615 			total = he->stat.period;
2616 
2617 			if (symbol_conf.cumulate_callchain)
2618 				total = he->stat_acc->period;
2619 
2620 			min_callchain_hits = total * (percent / 100);
2621 		}
2622 
2623 		callchain_param.sort(&he->sorted_chain, he->callchain,
2624 				     min_callchain_hits, &callchain_param);
2625 
2626 next:
2627 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2628 
2629 		/* force to re-evaluate folding state of callchains */
2630 		he->init_have_children = false;
2631 		hist_entry__set_folding(he, hb, false);
2632 	}
2633 }
2634 
2635 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2636 				    const char *helpline,
2637 				    bool left_exits,
2638 				    struct hist_browser_timer *hbt,
2639 				    float min_pcnt,
2640 				    struct perf_env *env)
2641 {
2642 	struct hists *hists = evsel__hists(evsel);
2643 	struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2644 	struct branch_info *bi;
2645 #define MAX_OPTIONS  16
2646 	char *options[MAX_OPTIONS];
2647 	struct popup_action actions[MAX_OPTIONS];
2648 	int nr_options = 0;
2649 	int key = -1;
2650 	char buf[64];
2651 	int delay_secs = hbt ? hbt->refresh : 0;
2652 	struct perf_hpp_fmt *fmt;
2653 
2654 #define HIST_BROWSER_HELP_COMMON					\
2655 	"h/?/F1        Show this window\n"				\
2656 	"UP/DOWN/PGUP\n"						\
2657 	"PGDN/SPACE    Navigate\n"					\
2658 	"q/ESC/CTRL+C  Exit browser\n\n"				\
2659 	"For multiple event sessions:\n\n"				\
2660 	"TAB/UNTAB     Switch events\n\n"				\
2661 	"For symbolic views (--sort has sym):\n\n"			\
2662 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2663 	"ESC           Zoom out\n"					\
2664 	"a             Annotate current symbol\n"			\
2665 	"C             Collapse all callchains\n"			\
2666 	"d             Zoom into current DSO\n"				\
2667 	"E             Expand all callchains\n"				\
2668 	"F             Toggle percentage of filtered entries\n"		\
2669 	"H             Display column headers\n"			\
2670 	"L             Change percent limit\n"				\
2671 	"m             Display context menu\n"				\
2672 	"S             Zoom into current Processor Socket\n"		\
2673 
2674 	/* help messages are sorted by lexical order of the hotkey */
2675 	const char report_help[] = HIST_BROWSER_HELP_COMMON
2676 	"i             Show header information\n"
2677 	"P             Print histograms to perf.hist.N\n"
2678 	"r             Run available scripts\n"
2679 	"s             Switch to another data file in PWD\n"
2680 	"t             Zoom into current Thread\n"
2681 	"V             Verbose (DSO names in callchains, etc)\n"
2682 	"/             Filter symbol by name";
2683 	const char top_help[] = HIST_BROWSER_HELP_COMMON
2684 	"P             Print histograms to perf.hist.N\n"
2685 	"t             Zoom into current Thread\n"
2686 	"V             Verbose (DSO names in callchains, etc)\n"
2687 	"z             Toggle zeroing of samples\n"
2688 	"f             Enable/Disable events\n"
2689 	"/             Filter symbol by name";
2690 
2691 	if (browser == NULL)
2692 		return -1;
2693 
2694 	/* reset abort key so that it can get Ctrl-C as a key */
2695 	SLang_reset_tty();
2696 	SLang_init_tty(0, 0, 0);
2697 
2698 	if (min_pcnt)
2699 		browser->min_pcnt = min_pcnt;
2700 	hist_browser__update_nr_entries(browser);
2701 
2702 	browser->pstack = pstack__new(3);
2703 	if (browser->pstack == NULL)
2704 		goto out;
2705 
2706 	ui_helpline__push(helpline);
2707 
2708 	memset(options, 0, sizeof(options));
2709 	memset(actions, 0, sizeof(actions));
2710 
2711 	hists__for_each_format(browser->hists, fmt) {
2712 		perf_hpp__reset_width(fmt, hists);
2713 		/*
2714 		 * This is done just once, and activates the horizontal scrolling
2715 		 * code in the ui_browser code, it would be better to have a the
2716 		 * counter in the perf_hpp code, but I couldn't find doing it here
2717 		 * works, FIXME by setting this in hist_browser__new, for now, be
2718 		 * clever 8-)
2719 		 */
2720 		++browser->b.columns;
2721 	}
2722 
2723 	if (symbol_conf.col_width_list_str)
2724 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2725 
2726 	while (1) {
2727 		struct thread *thread = NULL;
2728 		struct map *map = NULL;
2729 		int choice = 0;
2730 		int socked_id = -1;
2731 
2732 		nr_options = 0;
2733 
2734 		key = hist_browser__run(browser, helpline);
2735 
2736 		if (browser->he_selection != NULL) {
2737 			thread = hist_browser__selected_thread(browser);
2738 			map = browser->selection->map;
2739 			socked_id = browser->he_selection->socket;
2740 		}
2741 		switch (key) {
2742 		case K_TAB:
2743 		case K_UNTAB:
2744 			if (nr_events == 1)
2745 				continue;
2746 			/*
2747 			 * Exit the browser, let hists__browser_tree
2748 			 * go to the next or previous
2749 			 */
2750 			goto out_free_stack;
2751 		case 'a':
2752 			if (!hists__has(hists, sym)) {
2753 				ui_browser__warning(&browser->b, delay_secs * 2,
2754 			"Annotation is only available for symbolic views, "
2755 			"include \"sym*\" in --sort to use it.");
2756 				continue;
2757 			}
2758 
2759 			if (browser->selection == NULL ||
2760 			    browser->selection->sym == NULL ||
2761 			    browser->selection->map->dso->annotate_warned)
2762 				continue;
2763 
2764 			actions->ms.map = browser->selection->map;
2765 			actions->ms.sym = browser->selection->sym;
2766 			do_annotate(browser, actions);
2767 			continue;
2768 		case 'P':
2769 			hist_browser__dump(browser);
2770 			continue;
2771 		case 'd':
2772 			actions->ms.map = map;
2773 			do_zoom_dso(browser, actions);
2774 			continue;
2775 		case 'V':
2776 			browser->show_dso = !browser->show_dso;
2777 			continue;
2778 		case 't':
2779 			actions->thread = thread;
2780 			do_zoom_thread(browser, actions);
2781 			continue;
2782 		case 'S':
2783 			actions->socket = socked_id;
2784 			do_zoom_socket(browser, actions);
2785 			continue;
2786 		case '/':
2787 			if (ui_browser__input_window("Symbol to show",
2788 					"Please enter the name of symbol you want to see.\n"
2789 					"To remove the filter later, press / + ENTER.",
2790 					buf, "ENTER: OK, ESC: Cancel",
2791 					delay_secs * 2) == K_ENTER) {
2792 				hists->symbol_filter_str = *buf ? buf : NULL;
2793 				hists__filter_by_symbol(hists);
2794 				hist_browser__reset(browser);
2795 			}
2796 			continue;
2797 		case 'r':
2798 			if (is_report_browser(hbt)) {
2799 				actions->thread = NULL;
2800 				actions->ms.sym = NULL;
2801 				do_run_script(browser, actions);
2802 			}
2803 			continue;
2804 		case 's':
2805 			if (is_report_browser(hbt)) {
2806 				key = do_switch_data(browser, actions);
2807 				if (key == K_SWITCH_INPUT_DATA)
2808 					goto out_free_stack;
2809 			}
2810 			continue;
2811 		case 'i':
2812 			/* env->arch is NULL for live-mode (i.e. perf top) */
2813 			if (env->arch)
2814 				tui__header_window(env);
2815 			continue;
2816 		case 'F':
2817 			symbol_conf.filter_relative ^= 1;
2818 			continue;
2819 		case 'z':
2820 			if (!is_report_browser(hbt)) {
2821 				struct perf_top *top = hbt->arg;
2822 
2823 				top->zero = !top->zero;
2824 			}
2825 			continue;
2826 		case 'L':
2827 			if (ui_browser__input_window("Percent Limit",
2828 					"Please enter the value you want to hide entries under that percent.",
2829 					buf, "ENTER: OK, ESC: Cancel",
2830 					delay_secs * 2) == K_ENTER) {
2831 				char *end;
2832 				double new_percent = strtod(buf, &end);
2833 
2834 				if (new_percent < 0 || new_percent > 100) {
2835 					ui_browser__warning(&browser->b, delay_secs * 2,
2836 						"Invalid percent: %.2f", new_percent);
2837 					continue;
2838 				}
2839 
2840 				hist_browser__update_percent_limit(browser, new_percent);
2841 				hist_browser__reset(browser);
2842 			}
2843 			continue;
2844 		case K_F1:
2845 		case 'h':
2846 		case '?':
2847 			ui_browser__help_window(&browser->b,
2848 				is_report_browser(hbt) ? report_help : top_help);
2849 			continue;
2850 		case K_ENTER:
2851 		case K_RIGHT:
2852 		case 'm':
2853 			/* menu */
2854 			break;
2855 		case K_ESC:
2856 		case K_LEFT: {
2857 			const void *top;
2858 
2859 			if (pstack__empty(browser->pstack)) {
2860 				/*
2861 				 * Go back to the perf_evsel_menu__run or other user
2862 				 */
2863 				if (left_exits)
2864 					goto out_free_stack;
2865 
2866 				if (key == K_ESC &&
2867 				    ui_browser__dialog_yesno(&browser->b,
2868 							     "Do you really want to exit?"))
2869 					goto out_free_stack;
2870 
2871 				continue;
2872 			}
2873 			top = pstack__peek(browser->pstack);
2874 			if (top == &browser->hists->dso_filter) {
2875 				/*
2876 				 * No need to set actions->dso here since
2877 				 * it's just to remove the current filter.
2878 				 * Ditto for thread below.
2879 				 */
2880 				do_zoom_dso(browser, actions);
2881 			} else if (top == &browser->hists->thread_filter) {
2882 				do_zoom_thread(browser, actions);
2883 			} else if (top == &browser->hists->socket_filter) {
2884 				do_zoom_socket(browser, actions);
2885 			}
2886 			continue;
2887 		}
2888 		case 'q':
2889 		case CTRL('c'):
2890 			goto out_free_stack;
2891 		case 'f':
2892 			if (!is_report_browser(hbt)) {
2893 				struct perf_top *top = hbt->arg;
2894 
2895 				perf_evlist__toggle_enable(top->evlist);
2896 				/*
2897 				 * No need to refresh, resort/decay histogram
2898 				 * entries if we are not collecting samples:
2899 				 */
2900 				if (top->evlist->enabled) {
2901 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2902 					hbt->refresh = delay_secs;
2903 				} else {
2904 					helpline = "Press 'f' again to re-enable the events";
2905 					hbt->refresh = 0;
2906 				}
2907 				continue;
2908 			}
2909 			/* Fall thru */
2910 		default:
2911 			helpline = "Press '?' for help on key bindings";
2912 			continue;
2913 		}
2914 
2915 		if (!hists__has(hists, sym) || browser->selection == NULL)
2916 			goto skip_annotation;
2917 
2918 		if (sort__mode == SORT_MODE__BRANCH) {
2919 			bi = browser->he_selection->branch_info;
2920 
2921 			if (bi == NULL)
2922 				goto skip_annotation;
2923 
2924 			nr_options += add_annotate_opt(browser,
2925 						       &actions[nr_options],
2926 						       &options[nr_options],
2927 						       bi->from.map,
2928 						       bi->from.sym);
2929 			if (bi->to.sym != bi->from.sym)
2930 				nr_options += add_annotate_opt(browser,
2931 							&actions[nr_options],
2932 							&options[nr_options],
2933 							bi->to.map,
2934 							bi->to.sym);
2935 		} else {
2936 			nr_options += add_annotate_opt(browser,
2937 						       &actions[nr_options],
2938 						       &options[nr_options],
2939 						       browser->selection->map,
2940 						       browser->selection->sym);
2941 		}
2942 skip_annotation:
2943 		nr_options += add_thread_opt(browser, &actions[nr_options],
2944 					     &options[nr_options], thread);
2945 		nr_options += add_dso_opt(browser, &actions[nr_options],
2946 					  &options[nr_options], map);
2947 		nr_options += add_map_opt(browser, &actions[nr_options],
2948 					  &options[nr_options],
2949 					  browser->selection ?
2950 						browser->selection->map : NULL);
2951 		nr_options += add_socket_opt(browser, &actions[nr_options],
2952 					     &options[nr_options],
2953 					     socked_id);
2954 		/* perf script support */
2955 		if (!is_report_browser(hbt))
2956 			goto skip_scripting;
2957 
2958 		if (browser->he_selection) {
2959 			if (hists__has(hists, thread) && thread) {
2960 				nr_options += add_script_opt(browser,
2961 							     &actions[nr_options],
2962 							     &options[nr_options],
2963 							     thread, NULL);
2964 			}
2965 			/*
2966 			 * Note that browser->selection != NULL
2967 			 * when browser->he_selection is not NULL,
2968 			 * so we don't need to check browser->selection
2969 			 * before fetching browser->selection->sym like what
2970 			 * we do before fetching browser->selection->map.
2971 			 *
2972 			 * See hist_browser__show_entry.
2973 			 */
2974 			if (hists__has(hists, sym) && browser->selection->sym) {
2975 				nr_options += add_script_opt(browser,
2976 							     &actions[nr_options],
2977 							     &options[nr_options],
2978 							     NULL, browser->selection->sym);
2979 			}
2980 		}
2981 		nr_options += add_script_opt(browser, &actions[nr_options],
2982 					     &options[nr_options], NULL, NULL);
2983 		nr_options += add_switch_opt(browser, &actions[nr_options],
2984 					     &options[nr_options]);
2985 skip_scripting:
2986 		nr_options += add_exit_opt(browser, &actions[nr_options],
2987 					   &options[nr_options]);
2988 
2989 		do {
2990 			struct popup_action *act;
2991 
2992 			choice = ui__popup_menu(nr_options, options);
2993 			if (choice == -1 || choice >= nr_options)
2994 				break;
2995 
2996 			act = &actions[choice];
2997 			key = act->fn(browser, act);
2998 		} while (key == 1);
2999 
3000 		if (key == K_SWITCH_INPUT_DATA)
3001 			break;
3002 	}
3003 out_free_stack:
3004 	pstack__delete(browser->pstack);
3005 out:
3006 	hist_browser__delete(browser);
3007 	free_popup_options(options, MAX_OPTIONS);
3008 	return key;
3009 }
3010 
3011 struct perf_evsel_menu {
3012 	struct ui_browser b;
3013 	struct perf_evsel *selection;
3014 	bool lost_events, lost_events_warned;
3015 	float min_pcnt;
3016 	struct perf_env *env;
3017 };
3018 
3019 static void perf_evsel_menu__write(struct ui_browser *browser,
3020 				   void *entry, int row)
3021 {
3022 	struct perf_evsel_menu *menu = container_of(browser,
3023 						    struct perf_evsel_menu, b);
3024 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3025 	struct hists *hists = evsel__hists(evsel);
3026 	bool current_entry = ui_browser__is_current_entry(browser, row);
3027 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3028 	const char *ev_name = perf_evsel__name(evsel);
3029 	char bf[256], unit;
3030 	const char *warn = " ";
3031 	size_t printed;
3032 
3033 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3034 						       HE_COLORSET_NORMAL);
3035 
3036 	if (perf_evsel__is_group_event(evsel)) {
3037 		struct perf_evsel *pos;
3038 
3039 		ev_name = perf_evsel__group_name(evsel);
3040 
3041 		for_each_group_member(pos, evsel) {
3042 			struct hists *pos_hists = evsel__hists(pos);
3043 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3044 		}
3045 	}
3046 
3047 	nr_events = convert_unit(nr_events, &unit);
3048 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3049 			   unit, unit == ' ' ? "" : " ", ev_name);
3050 	ui_browser__printf(browser, "%s", bf);
3051 
3052 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3053 	if (nr_events != 0) {
3054 		menu->lost_events = true;
3055 		if (!current_entry)
3056 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3057 		nr_events = convert_unit(nr_events, &unit);
3058 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3059 				     nr_events, unit, unit == ' ' ? "" : " ");
3060 		warn = bf;
3061 	}
3062 
3063 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3064 
3065 	if (current_entry)
3066 		menu->selection = evsel;
3067 }
3068 
3069 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3070 				int nr_events, const char *help,
3071 				struct hist_browser_timer *hbt)
3072 {
3073 	struct perf_evlist *evlist = menu->b.priv;
3074 	struct perf_evsel *pos;
3075 	const char *title = "Available samples";
3076 	int delay_secs = hbt ? hbt->refresh : 0;
3077 	int key;
3078 
3079 	if (ui_browser__show(&menu->b, title,
3080 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3081 		return -1;
3082 
3083 	while (1) {
3084 		key = ui_browser__run(&menu->b, delay_secs);
3085 
3086 		switch (key) {
3087 		case K_TIMER:
3088 			hbt->timer(hbt->arg);
3089 
3090 			if (!menu->lost_events_warned && menu->lost_events) {
3091 				ui_browser__warn_lost_events(&menu->b);
3092 				menu->lost_events_warned = true;
3093 			}
3094 			continue;
3095 		case K_RIGHT:
3096 		case K_ENTER:
3097 			if (!menu->selection)
3098 				continue;
3099 			pos = menu->selection;
3100 browse_hists:
3101 			perf_evlist__set_selected(evlist, pos);
3102 			/*
3103 			 * Give the calling tool a chance to populate the non
3104 			 * default evsel resorted hists tree.
3105 			 */
3106 			if (hbt)
3107 				hbt->timer(hbt->arg);
3108 			key = perf_evsel__hists_browse(pos, nr_events, help,
3109 						       true, hbt,
3110 						       menu->min_pcnt,
3111 						       menu->env);
3112 			ui_browser__show_title(&menu->b, title);
3113 			switch (key) {
3114 			case K_TAB:
3115 				if (pos->node.next == &evlist->entries)
3116 					pos = perf_evlist__first(evlist);
3117 				else
3118 					pos = perf_evsel__next(pos);
3119 				goto browse_hists;
3120 			case K_UNTAB:
3121 				if (pos->node.prev == &evlist->entries)
3122 					pos = perf_evlist__last(evlist);
3123 				else
3124 					pos = perf_evsel__prev(pos);
3125 				goto browse_hists;
3126 			case K_SWITCH_INPUT_DATA:
3127 			case 'q':
3128 			case CTRL('c'):
3129 				goto out;
3130 			case K_ESC:
3131 			default:
3132 				continue;
3133 			}
3134 		case K_LEFT:
3135 			continue;
3136 		case K_ESC:
3137 			if (!ui_browser__dialog_yesno(&menu->b,
3138 					       "Do you really want to exit?"))
3139 				continue;
3140 			/* Fall thru */
3141 		case 'q':
3142 		case CTRL('c'):
3143 			goto out;
3144 		default:
3145 			continue;
3146 		}
3147 	}
3148 
3149 out:
3150 	ui_browser__hide(&menu->b);
3151 	return key;
3152 }
3153 
3154 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3155 				 void *entry)
3156 {
3157 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3158 
3159 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3160 		return true;
3161 
3162 	return false;
3163 }
3164 
3165 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3166 					   int nr_entries, const char *help,
3167 					   struct hist_browser_timer *hbt,
3168 					   float min_pcnt,
3169 					   struct perf_env *env)
3170 {
3171 	struct perf_evsel *pos;
3172 	struct perf_evsel_menu menu = {
3173 		.b = {
3174 			.entries    = &evlist->entries,
3175 			.refresh    = ui_browser__list_head_refresh,
3176 			.seek	    = ui_browser__list_head_seek,
3177 			.write	    = perf_evsel_menu__write,
3178 			.filter	    = filter_group_entries,
3179 			.nr_entries = nr_entries,
3180 			.priv	    = evlist,
3181 		},
3182 		.min_pcnt = min_pcnt,
3183 		.env = env,
3184 	};
3185 
3186 	ui_helpline__push("Press ESC to exit");
3187 
3188 	evlist__for_each(evlist, pos) {
3189 		const char *ev_name = perf_evsel__name(pos);
3190 		size_t line_len = strlen(ev_name) + 7;
3191 
3192 		if (menu.b.width < line_len)
3193 			menu.b.width = line_len;
3194 	}
3195 
3196 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3197 }
3198 
3199 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3200 				  struct hist_browser_timer *hbt,
3201 				  float min_pcnt,
3202 				  struct perf_env *env)
3203 {
3204 	int nr_entries = evlist->nr_entries;
3205 
3206 single_entry:
3207 	if (nr_entries == 1) {
3208 		struct perf_evsel *first = perf_evlist__first(evlist);
3209 
3210 		return perf_evsel__hists_browse(first, nr_entries, help,
3211 						false, hbt, min_pcnt,
3212 						env);
3213 	}
3214 
3215 	if (symbol_conf.event_group) {
3216 		struct perf_evsel *pos;
3217 
3218 		nr_entries = 0;
3219 		evlist__for_each(evlist, pos) {
3220 			if (perf_evsel__is_group_leader(pos))
3221 				nr_entries++;
3222 		}
3223 
3224 		if (nr_entries == 1)
3225 			goto single_entry;
3226 	}
3227 
3228 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3229 					       hbt, min_pcnt, env);
3230 }
3231