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