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