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