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