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