xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 3381df09)
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 /* Check whether the browser is for 'top' or 'report' */
2292 static inline bool is_report_browser(void *timer)
2293 {
2294 	return timer == NULL;
2295 }
2296 
2297 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2298 {
2299 	struct hist_browser_timer *hbt = browser->hbt;
2300 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2301 
2302 	if (!is_report_browser(hbt)) {
2303 		struct perf_top *top = hbt->arg;
2304 
2305 		printed += scnprintf(bf + printed, size - printed,
2306 				     " lost: %" PRIu64 "/%" PRIu64,
2307 				     top->lost, top->lost_total);
2308 
2309 		printed += scnprintf(bf + printed, size - printed,
2310 				     " drop: %" PRIu64 "/%" PRIu64,
2311 				     top->drop, top->drop_total);
2312 
2313 		if (top->zero)
2314 			printed += scnprintf(bf + printed, size - printed, " [z]");
2315 
2316 		perf_top__reset_sample_counters(top);
2317 	}
2318 
2319 
2320 	return printed;
2321 }
2322 
2323 static inline void free_popup_options(char **options, int n)
2324 {
2325 	int i;
2326 
2327 	for (i = 0; i < n; ++i)
2328 		zfree(&options[i]);
2329 }
2330 
2331 /*
2332  * Only runtime switching of perf data file will make "input_name" point
2333  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2334  * whether we need to call free() for current "input_name" during the switch.
2335  */
2336 static bool is_input_name_malloced = false;
2337 
2338 static int switch_data_file(void)
2339 {
2340 	char *pwd, *options[32], *abs_path[32], *tmp;
2341 	DIR *pwd_dir;
2342 	int nr_options = 0, choice = -1, ret = -1;
2343 	struct dirent *dent;
2344 
2345 	pwd = getenv("PWD");
2346 	if (!pwd)
2347 		return ret;
2348 
2349 	pwd_dir = opendir(pwd);
2350 	if (!pwd_dir)
2351 		return ret;
2352 
2353 	memset(options, 0, sizeof(options));
2354 	memset(abs_path, 0, sizeof(abs_path));
2355 
2356 	while ((dent = readdir(pwd_dir))) {
2357 		char path[PATH_MAX];
2358 		u64 magic;
2359 		char *name = dent->d_name;
2360 		FILE *file;
2361 
2362 		if (!(dent->d_type == DT_REG))
2363 			continue;
2364 
2365 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2366 
2367 		file = fopen(path, "r");
2368 		if (!file)
2369 			continue;
2370 
2371 		if (fread(&magic, 1, 8, file) < 8)
2372 			goto close_file_and_continue;
2373 
2374 		if (is_perf_magic(magic)) {
2375 			options[nr_options] = strdup(name);
2376 			if (!options[nr_options])
2377 				goto close_file_and_continue;
2378 
2379 			abs_path[nr_options] = strdup(path);
2380 			if (!abs_path[nr_options]) {
2381 				zfree(&options[nr_options]);
2382 				ui__warning("Can't search all data files due to memory shortage.\n");
2383 				fclose(file);
2384 				break;
2385 			}
2386 
2387 			nr_options++;
2388 		}
2389 
2390 close_file_and_continue:
2391 		fclose(file);
2392 		if (nr_options >= 32) {
2393 			ui__warning("Too many perf data files in PWD!\n"
2394 				    "Only the first 32 files will be listed.\n");
2395 			break;
2396 		}
2397 	}
2398 	closedir(pwd_dir);
2399 
2400 	if (nr_options) {
2401 		choice = ui__popup_menu(nr_options, options, NULL);
2402 		if (choice < nr_options && choice >= 0) {
2403 			tmp = strdup(abs_path[choice]);
2404 			if (tmp) {
2405 				if (is_input_name_malloced)
2406 					free((void *)input_name);
2407 				input_name = tmp;
2408 				is_input_name_malloced = true;
2409 				ret = 0;
2410 			} else
2411 				ui__warning("Data switch failed due to memory shortage!\n");
2412 		}
2413 	}
2414 
2415 	free_popup_options(options, nr_options);
2416 	free_popup_options(abs_path, nr_options);
2417 	return ret;
2418 }
2419 
2420 struct popup_action {
2421 	unsigned long		time;
2422 	struct thread 		*thread;
2423 	struct map_symbol 	ms;
2424 	int			socket;
2425 	struct evsel	*evsel;
2426 	enum rstype		rstype;
2427 
2428 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2429 };
2430 
2431 static int
2432 do_annotate(struct hist_browser *browser, struct popup_action *act)
2433 {
2434 	struct evsel *evsel;
2435 	struct annotation *notes;
2436 	struct hist_entry *he;
2437 	int err;
2438 
2439 	if (!browser->annotation_opts->objdump_path &&
2440 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2441 		return 0;
2442 
2443 	notes = symbol__annotation(act->ms.sym);
2444 	if (!notes->src)
2445 		return 0;
2446 
2447 	if (browser->block_evsel)
2448 		evsel = browser->block_evsel;
2449 	else
2450 		evsel = hists_to_evsel(browser->hists);
2451 
2452 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2453 				       browser->annotation_opts);
2454 	he = hist_browser__selected_entry(browser);
2455 	/*
2456 	 * offer option to annotate the other branch source or target
2457 	 * (if they exists) when returning from annotate
2458 	 */
2459 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2460 		return 1;
2461 
2462 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2463 	if (err)
2464 		ui_browser__handle_resize(&browser->b);
2465 	return 0;
2466 }
2467 
2468 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2469 {
2470 	struct annotated_source *src;
2471 	struct symbol *sym;
2472 	char name[64];
2473 
2474 	snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2475 
2476 	sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2477 	if (sym) {
2478 		src = symbol__hists(sym, 1);
2479 		if (!src) {
2480 			symbol__delete(sym);
2481 			return NULL;
2482 		}
2483 
2484 		dso__insert_symbol(map->dso, sym);
2485 	}
2486 
2487 	return sym;
2488 }
2489 
2490 static int
2491 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2492 		 struct popup_action *act, char **optstr,
2493 		 struct map_symbol *ms,
2494 		 u64 addr)
2495 {
2496 	if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned)
2497 		return 0;
2498 
2499 	if (!ms->sym)
2500 		ms->sym = symbol__new_unresolved(addr, ms->map);
2501 
2502 	if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2503 		return 0;
2504 
2505 	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2506 		return 0;
2507 
2508 	act->ms = *ms;
2509 	act->fn = do_annotate;
2510 	return 1;
2511 }
2512 
2513 static int
2514 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2515 {
2516 	struct thread *thread = act->thread;
2517 
2518 	if ((!hists__has(browser->hists, thread) &&
2519 	     !hists__has(browser->hists, comm)) || thread == NULL)
2520 		return 0;
2521 
2522 	if (browser->hists->thread_filter) {
2523 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2524 		perf_hpp__set_elide(HISTC_THREAD, false);
2525 		thread__zput(browser->hists->thread_filter);
2526 		ui_helpline__pop();
2527 	} else {
2528 		if (hists__has(browser->hists, thread)) {
2529 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2530 					   thread->comm_set ? thread__comm_str(thread) : "",
2531 					   thread->tid);
2532 		} else {
2533 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2534 					   thread->comm_set ? thread__comm_str(thread) : "");
2535 		}
2536 
2537 		browser->hists->thread_filter = thread__get(thread);
2538 		perf_hpp__set_elide(HISTC_THREAD, false);
2539 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2540 	}
2541 
2542 	hists__filter_by_thread(browser->hists);
2543 	hist_browser__reset(browser);
2544 	return 0;
2545 }
2546 
2547 static int
2548 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2549 	       char **optstr, struct thread *thread)
2550 {
2551 	int ret;
2552 
2553 	if ((!hists__has(browser->hists, thread) &&
2554 	     !hists__has(browser->hists, comm)) || thread == NULL)
2555 		return 0;
2556 
2557 	if (hists__has(browser->hists, thread)) {
2558 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2559 			       browser->hists->thread_filter ? "out of" : "into",
2560 			       thread->comm_set ? thread__comm_str(thread) : "",
2561 			       thread->tid);
2562 	} else {
2563 		ret = asprintf(optstr, "Zoom %s %s thread",
2564 			       browser->hists->thread_filter ? "out of" : "into",
2565 			       thread->comm_set ? thread__comm_str(thread) : "");
2566 	}
2567 	if (ret < 0)
2568 		return 0;
2569 
2570 	act->thread = thread;
2571 	act->fn = do_zoom_thread;
2572 	return 1;
2573 }
2574 
2575 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2576 {
2577 	if (!hists__has(browser->hists, dso) || map == NULL)
2578 		return 0;
2579 
2580 	if (browser->hists->dso_filter) {
2581 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2582 		perf_hpp__set_elide(HISTC_DSO, false);
2583 		browser->hists->dso_filter = NULL;
2584 		ui_helpline__pop();
2585 	} else {
2586 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2587 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2588 		browser->hists->dso_filter = map->dso;
2589 		perf_hpp__set_elide(HISTC_DSO, true);
2590 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2591 	}
2592 
2593 	hists__filter_by_dso(browser->hists);
2594 	hist_browser__reset(browser);
2595 	return 0;
2596 }
2597 
2598 static int
2599 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2600 {
2601 	return hists_browser__zoom_map(browser, act->ms.map);
2602 }
2603 
2604 static int
2605 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2606 	    char **optstr, struct map *map)
2607 {
2608 	if (!hists__has(browser->hists, dso) || map == NULL)
2609 		return 0;
2610 
2611 	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2612 		     browser->hists->dso_filter ? "out of" : "into",
2613 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2614 		return 0;
2615 
2616 	act->ms.map = map;
2617 	act->fn = do_zoom_dso;
2618 	return 1;
2619 }
2620 
2621 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2622 {
2623 	hist_browser__toggle_fold(browser);
2624 	return 0;
2625 }
2626 
2627 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2628 {
2629 	char sym_name[512];
2630 
2631         if (!hist_browser__selection_has_children(browser))
2632                 return 0;
2633 
2634 	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2635 		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2636 		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2637 		return 0;
2638 
2639 	act->fn = do_toggle_callchain;
2640 	return 1;
2641 }
2642 
2643 static int
2644 do_browse_map(struct hist_browser *browser __maybe_unused,
2645 	      struct popup_action *act)
2646 {
2647 	map__browse(act->ms.map);
2648 	return 0;
2649 }
2650 
2651 static int
2652 add_map_opt(struct hist_browser *browser,
2653 	    struct popup_action *act, char **optstr, struct map *map)
2654 {
2655 	if (!hists__has(browser->hists, dso) || map == NULL)
2656 		return 0;
2657 
2658 	if (asprintf(optstr, "Browse map details") < 0)
2659 		return 0;
2660 
2661 	act->ms.map = map;
2662 	act->fn = do_browse_map;
2663 	return 1;
2664 }
2665 
2666 static int
2667 do_run_script(struct hist_browser *browser __maybe_unused,
2668 	      struct popup_action *act)
2669 {
2670 	char *script_opt;
2671 	int len;
2672 	int n = 0;
2673 
2674 	len = 100;
2675 	if (act->thread)
2676 		len += strlen(thread__comm_str(act->thread));
2677 	else if (act->ms.sym)
2678 		len += strlen(act->ms.sym->name);
2679 	script_opt = malloc(len);
2680 	if (!script_opt)
2681 		return -1;
2682 
2683 	script_opt[0] = 0;
2684 	if (act->thread) {
2685 		n = scnprintf(script_opt, len, " -c %s ",
2686 			  thread__comm_str(act->thread));
2687 	} else if (act->ms.sym) {
2688 		n = scnprintf(script_opt, len, " -S %s ",
2689 			  act->ms.sym->name);
2690 	}
2691 
2692 	if (act->time) {
2693 		char start[32], end[32];
2694 		unsigned long starttime = act->time;
2695 		unsigned long endtime = act->time + symbol_conf.time_quantum;
2696 
2697 		if (starttime == endtime) { /* Display 1ms as fallback */
2698 			starttime -= 1*NSEC_PER_MSEC;
2699 			endtime += 1*NSEC_PER_MSEC;
2700 		}
2701 		timestamp__scnprintf_usec(starttime, start, sizeof start);
2702 		timestamp__scnprintf_usec(endtime, end, sizeof end);
2703 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2704 	}
2705 
2706 	script_browse(script_opt, act->evsel);
2707 	free(script_opt);
2708 	return 0;
2709 }
2710 
2711 static int
2712 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2713 		     struct popup_action *act)
2714 {
2715 	struct hist_entry *he;
2716 
2717 	he = hist_browser__selected_entry(browser);
2718 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2719 	return 0;
2720 }
2721 
2722 static int
2723 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2724 	       struct popup_action *act, char **optstr,
2725 	       struct thread *thread, struct symbol *sym,
2726 	       struct evsel *evsel, const char *tstr)
2727 {
2728 
2729 	if (thread) {
2730 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2731 			     thread__comm_str(thread), tstr) < 0)
2732 			return 0;
2733 	} else if (sym) {
2734 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2735 			     sym->name, tstr) < 0)
2736 			return 0;
2737 	} else {
2738 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2739 			return 0;
2740 	}
2741 
2742 	act->thread = thread;
2743 	act->ms.sym = sym;
2744 	act->evsel = evsel;
2745 	act->fn = do_run_script;
2746 	return 1;
2747 }
2748 
2749 static int
2750 add_script_opt(struct hist_browser *browser,
2751 	       struct popup_action *act, char **optstr,
2752 	       struct thread *thread, struct symbol *sym,
2753 	       struct evsel *evsel)
2754 {
2755 	int n, j;
2756 	struct hist_entry *he;
2757 
2758 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2759 
2760 	he = hist_browser__selected_entry(browser);
2761 	if (sort_order && strstr(sort_order, "time")) {
2762 		char tstr[128];
2763 
2764 		optstr++;
2765 		act++;
2766 		j = sprintf(tstr, " in ");
2767 		j += timestamp__scnprintf_usec(he->time, tstr + j,
2768 					       sizeof tstr - j);
2769 		j += sprintf(tstr + j, "-");
2770 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2771 				          tstr + j, sizeof tstr - j);
2772 		n += add_script_opt_2(browser, act, optstr, thread, sym,
2773 					  evsel, tstr);
2774 		act->time = he->time;
2775 	}
2776 	return n;
2777 }
2778 
2779 static int
2780 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2781 		   struct popup_action *act, char **optstr,
2782 		   struct res_sample *res_sample,
2783 		   struct evsel *evsel,
2784 		   enum rstype type)
2785 {
2786 	if (!res_sample)
2787 		return 0;
2788 
2789 	if (asprintf(optstr, "Show context for individual samples %s",
2790 		type == A_ASM ? "with assembler" :
2791 		type == A_SOURCE ? "with source" : "") < 0)
2792 		return 0;
2793 
2794 	act->fn = do_res_sample_script;
2795 	act->evsel = evsel;
2796 	act->rstype = type;
2797 	return 1;
2798 }
2799 
2800 static int
2801 do_switch_data(struct hist_browser *browser __maybe_unused,
2802 	       struct popup_action *act __maybe_unused)
2803 {
2804 	if (switch_data_file()) {
2805 		ui__warning("Won't switch the data files due to\n"
2806 			    "no valid data file get selected!\n");
2807 		return 0;
2808 	}
2809 
2810 	return K_SWITCH_INPUT_DATA;
2811 }
2812 
2813 static int
2814 add_switch_opt(struct hist_browser *browser,
2815 	       struct popup_action *act, char **optstr)
2816 {
2817 	if (!is_report_browser(browser->hbt))
2818 		return 0;
2819 
2820 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2821 		return 0;
2822 
2823 	act->fn = do_switch_data;
2824 	return 1;
2825 }
2826 
2827 static int
2828 do_exit_browser(struct hist_browser *browser __maybe_unused,
2829 		struct popup_action *act __maybe_unused)
2830 {
2831 	return 0;
2832 }
2833 
2834 static int
2835 add_exit_opt(struct hist_browser *browser __maybe_unused,
2836 	     struct popup_action *act, char **optstr)
2837 {
2838 	if (asprintf(optstr, "Exit") < 0)
2839 		return 0;
2840 
2841 	act->fn = do_exit_browser;
2842 	return 1;
2843 }
2844 
2845 static int
2846 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2847 {
2848 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2849 		return 0;
2850 
2851 	if (browser->hists->socket_filter > -1) {
2852 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2853 		browser->hists->socket_filter = -1;
2854 		perf_hpp__set_elide(HISTC_SOCKET, false);
2855 	} else {
2856 		browser->hists->socket_filter = act->socket;
2857 		perf_hpp__set_elide(HISTC_SOCKET, true);
2858 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2859 	}
2860 
2861 	hists__filter_by_socket(browser->hists);
2862 	hist_browser__reset(browser);
2863 	return 0;
2864 }
2865 
2866 static int
2867 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2868 	       char **optstr, int socket_id)
2869 {
2870 	if (!hists__has(browser->hists, socket) || socket_id < 0)
2871 		return 0;
2872 
2873 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2874 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2875 		     socket_id) < 0)
2876 		return 0;
2877 
2878 	act->socket = socket_id;
2879 	act->fn = do_zoom_socket;
2880 	return 1;
2881 }
2882 
2883 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2884 {
2885 	u64 nr_entries = 0;
2886 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2887 
2888 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2889 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2890 		return;
2891 	}
2892 
2893 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2894 		nr_entries++;
2895 		nd = rb_hierarchy_next(nd);
2896 	}
2897 
2898 	hb->nr_non_filtered_entries = nr_entries;
2899 	hb->nr_hierarchy_entries = nr_entries;
2900 }
2901 
2902 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2903 					       double percent)
2904 {
2905 	struct hist_entry *he;
2906 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2907 	u64 total = hists__total_period(hb->hists);
2908 	u64 min_callchain_hits = total * (percent / 100);
2909 
2910 	hb->min_pcnt = callchain_param.min_percent = percent;
2911 
2912 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2913 		he = rb_entry(nd, struct hist_entry, rb_node);
2914 
2915 		if (he->has_no_entry) {
2916 			he->has_no_entry = false;
2917 			he->nr_rows = 0;
2918 		}
2919 
2920 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2921 			goto next;
2922 
2923 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2924 			total = he->stat.period;
2925 
2926 			if (symbol_conf.cumulate_callchain)
2927 				total = he->stat_acc->period;
2928 
2929 			min_callchain_hits = total * (percent / 100);
2930 		}
2931 
2932 		callchain_param.sort(&he->sorted_chain, he->callchain,
2933 				     min_callchain_hits, &callchain_param);
2934 
2935 next:
2936 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2937 
2938 		/* force to re-evaluate folding state of callchains */
2939 		he->init_have_children = false;
2940 		hist_entry__set_folding(he, hb, false);
2941 	}
2942 }
2943 
2944 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2945 				    const char *helpline,
2946 				    bool left_exits,
2947 				    struct hist_browser_timer *hbt,
2948 				    float min_pcnt,
2949 				    struct perf_env *env,
2950 				    bool warn_lost_event,
2951 				    struct annotation_options *annotation_opts)
2952 {
2953 	struct hists *hists = evsel__hists(evsel);
2954 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2955 	struct branch_info *bi = NULL;
2956 #define MAX_OPTIONS  16
2957 	char *options[MAX_OPTIONS];
2958 	struct popup_action actions[MAX_OPTIONS];
2959 	int nr_options = 0;
2960 	int key = -1;
2961 	char buf[64];
2962 	int delay_secs = hbt ? hbt->refresh : 0;
2963 
2964 #define HIST_BROWSER_HELP_COMMON					\
2965 	"h/?/F1        Show this window\n"				\
2966 	"UP/DOWN/PGUP\n"						\
2967 	"PGDN/SPACE    Navigate\n"					\
2968 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2969 	"For multiple event sessions:\n\n"				\
2970 	"TAB/UNTAB     Switch events\n\n"				\
2971 	"For symbolic views (--sort has sym):\n\n"			\
2972 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2973 	"ESC           Zoom out\n"					\
2974 	"+             Expand/Collapse one callchain level\n"		\
2975 	"a             Annotate current symbol\n"			\
2976 	"C             Collapse all callchains\n"			\
2977 	"d             Zoom into current DSO\n"				\
2978 	"e             Expand/Collapse main entry callchains\n"	\
2979 	"E             Expand all callchains\n"				\
2980 	"F             Toggle percentage of filtered entries\n"		\
2981 	"H             Display column headers\n"			\
2982 	"k             Zoom into the kernel map\n"			\
2983 	"L             Change percent limit\n"				\
2984 	"m             Display context menu\n"				\
2985 	"S             Zoom into current Processor Socket\n"		\
2986 
2987 	/* help messages are sorted by lexical order of the hotkey */
2988 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
2989 	"i             Show header information\n"
2990 	"P             Print histograms to perf.hist.N\n"
2991 	"r             Run available scripts\n"
2992 	"s             Switch to another data file in PWD\n"
2993 	"t             Zoom into current Thread\n"
2994 	"V             Verbose (DSO names in callchains, etc)\n"
2995 	"/             Filter symbol by name\n"
2996 	"0-9           Sort by event n in group";
2997 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
2998 	"P             Print histograms to perf.hist.N\n"
2999 	"t             Zoom into current Thread\n"
3000 	"V             Verbose (DSO names in callchains, etc)\n"
3001 	"z             Toggle zeroing of samples\n"
3002 	"f             Enable/Disable events\n"
3003 	"/             Filter symbol by name";
3004 
3005 	if (browser == NULL)
3006 		return -1;
3007 
3008 	/* reset abort key so that it can get Ctrl-C as a key */
3009 	SLang_reset_tty();
3010 	SLang_init_tty(0, 0, 0);
3011 
3012 	if (min_pcnt)
3013 		browser->min_pcnt = min_pcnt;
3014 	hist_browser__update_nr_entries(browser);
3015 
3016 	browser->pstack = pstack__new(3);
3017 	if (browser->pstack == NULL)
3018 		goto out;
3019 
3020 	ui_helpline__push(helpline);
3021 
3022 	memset(options, 0, sizeof(options));
3023 	memset(actions, 0, sizeof(actions));
3024 
3025 	if (symbol_conf.col_width_list_str)
3026 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3027 
3028 	if (!is_report_browser(hbt))
3029 		browser->b.no_samples_msg = "Collecting samples...";
3030 
3031 	while (1) {
3032 		struct thread *thread = NULL;
3033 		struct map *map = NULL;
3034 		int choice;
3035 		int socked_id = -1;
3036 
3037 		key = 0; // reset key
3038 do_hotkey:		 // key came straight from options ui__popup_menu()
3039 		choice = nr_options = 0;
3040 		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3041 
3042 		if (browser->he_selection != NULL) {
3043 			thread = hist_browser__selected_thread(browser);
3044 			map = browser->selection->map;
3045 			socked_id = browser->he_selection->socket;
3046 		}
3047 		switch (key) {
3048 		case K_TAB:
3049 		case K_UNTAB:
3050 			if (nr_events == 1)
3051 				continue;
3052 			/*
3053 			 * Exit the browser, let hists__browser_tree
3054 			 * go to the next or previous
3055 			 */
3056 			goto out_free_stack;
3057 		case '0' ... '9':
3058 			if (!symbol_conf.event_group ||
3059 			    evsel->core.nr_members < 2) {
3060 				snprintf(buf, sizeof(buf),
3061 					 "Sort by index only available with group events!");
3062 				helpline = buf;
3063 				continue;
3064 			}
3065 
3066 			if (key - '0' == symbol_conf.group_sort_idx)
3067 				continue;
3068 
3069 			symbol_conf.group_sort_idx = key - '0';
3070 
3071 			if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3072 				snprintf(buf, sizeof(buf),
3073 					 "Max event group index to sort is %d (index from 0 to %d)",
3074 					 evsel->core.nr_members - 1,
3075 					 evsel->core.nr_members - 1);
3076 				helpline = buf;
3077 				continue;
3078 			}
3079 
3080 			key = K_RELOAD;
3081 			goto out_free_stack;
3082 		case 'a':
3083 			if (!hists__has(hists, sym)) {
3084 				ui_browser__warning(&browser->b, delay_secs * 2,
3085 			"Annotation is only available for symbolic views, "
3086 			"include \"sym*\" in --sort to use it.");
3087 				continue;
3088 			}
3089 
3090 			if (!browser->selection ||
3091 			    !browser->selection->map ||
3092 			    !browser->selection->map->dso ||
3093 			    browser->selection->map->dso->annotate_warned) {
3094 				continue;
3095 			}
3096 
3097 			if (!browser->selection->sym) {
3098 				if (!browser->he_selection)
3099 					continue;
3100 
3101 				if (sort__mode == SORT_MODE__BRANCH) {
3102 					bi = browser->he_selection->branch_info;
3103 					if (!bi || !bi->to.ms.map)
3104 						continue;
3105 
3106 					actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3107 					actions->ms.map = bi->to.ms.map;
3108 				} else {
3109 					actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3110 										 browser->selection->map);
3111 					actions->ms.map = browser->selection->map;
3112 				}
3113 
3114 				if (!actions->ms.sym)
3115 					continue;
3116 			} else {
3117 				if (symbol__annotation(browser->selection->sym)->src == NULL) {
3118 					ui_browser__warning(&browser->b, delay_secs * 2,
3119 						"No samples for the \"%s\" symbol.\n\n"
3120 						"Probably appeared just in a callchain",
3121 						browser->selection->sym->name);
3122 					continue;
3123 				}
3124 
3125 				actions->ms.map = browser->selection->map;
3126 				actions->ms.sym = browser->selection->sym;
3127 			}
3128 
3129 			do_annotate(browser, actions);
3130 			continue;
3131 		case 'P':
3132 			hist_browser__dump(browser);
3133 			continue;
3134 		case 'd':
3135 			actions->ms.map = map;
3136 			do_zoom_dso(browser, actions);
3137 			continue;
3138 		case 'k':
3139 			if (browser->selection != NULL)
3140 				hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3141 			continue;
3142 		case 'V':
3143 			verbose = (verbose + 1) % 4;
3144 			browser->show_dso = verbose > 0;
3145 			ui_helpline__fpush("Verbosity level set to %d\n",
3146 					   verbose);
3147 			continue;
3148 		case 't':
3149 			actions->thread = thread;
3150 			do_zoom_thread(browser, actions);
3151 			continue;
3152 		case 'S':
3153 			actions->socket = socked_id;
3154 			do_zoom_socket(browser, actions);
3155 			continue;
3156 		case '/':
3157 			if (ui_browser__input_window("Symbol to show",
3158 					"Please enter the name of symbol you want to see.\n"
3159 					"To remove the filter later, press / + ENTER.",
3160 					buf, "ENTER: OK, ESC: Cancel",
3161 					delay_secs * 2) == K_ENTER) {
3162 				hists->symbol_filter_str = *buf ? buf : NULL;
3163 				hists__filter_by_symbol(hists);
3164 				hist_browser__reset(browser);
3165 			}
3166 			continue;
3167 		case 'r':
3168 			if (is_report_browser(hbt)) {
3169 				actions->thread = NULL;
3170 				actions->ms.sym = NULL;
3171 				do_run_script(browser, actions);
3172 			}
3173 			continue;
3174 		case 's':
3175 			if (is_report_browser(hbt)) {
3176 				key = do_switch_data(browser, actions);
3177 				if (key == K_SWITCH_INPUT_DATA)
3178 					goto out_free_stack;
3179 			}
3180 			continue;
3181 		case 'i':
3182 			/* env->arch is NULL for live-mode (i.e. perf top) */
3183 			if (env->arch)
3184 				tui__header_window(env);
3185 			continue;
3186 		case 'F':
3187 			symbol_conf.filter_relative ^= 1;
3188 			continue;
3189 		case 'z':
3190 			if (!is_report_browser(hbt)) {
3191 				struct perf_top *top = hbt->arg;
3192 
3193 				top->zero = !top->zero;
3194 			}
3195 			continue;
3196 		case 'L':
3197 			if (ui_browser__input_window("Percent Limit",
3198 					"Please enter the value you want to hide entries under that percent.",
3199 					buf, "ENTER: OK, ESC: Cancel",
3200 					delay_secs * 2) == K_ENTER) {
3201 				char *end;
3202 				double new_percent = strtod(buf, &end);
3203 
3204 				if (new_percent < 0 || new_percent > 100) {
3205 					ui_browser__warning(&browser->b, delay_secs * 2,
3206 						"Invalid percent: %.2f", new_percent);
3207 					continue;
3208 				}
3209 
3210 				hist_browser__update_percent_limit(browser, new_percent);
3211 				hist_browser__reset(browser);
3212 			}
3213 			continue;
3214 		case K_F1:
3215 		case 'h':
3216 		case '?':
3217 			ui_browser__help_window(&browser->b,
3218 				is_report_browser(hbt) ? report_help : top_help);
3219 			continue;
3220 		case K_ENTER:
3221 		case K_RIGHT:
3222 		case 'm':
3223 			/* menu */
3224 			break;
3225 		case K_ESC:
3226 		case K_LEFT: {
3227 			const void *top;
3228 
3229 			if (pstack__empty(browser->pstack)) {
3230 				/*
3231 				 * Go back to the perf_evsel_menu__run or other user
3232 				 */
3233 				if (left_exits)
3234 					goto out_free_stack;
3235 
3236 				if (key == K_ESC &&
3237 				    ui_browser__dialog_yesno(&browser->b,
3238 							     "Do you really want to exit?"))
3239 					goto out_free_stack;
3240 
3241 				continue;
3242 			}
3243 			actions->ms.map = map;
3244 			top = pstack__peek(browser->pstack);
3245 			if (top == &browser->hists->dso_filter) {
3246 				/*
3247 				 * No need to set actions->dso here since
3248 				 * it's just to remove the current filter.
3249 				 * Ditto for thread below.
3250 				 */
3251 				do_zoom_dso(browser, actions);
3252 			} else if (top == &browser->hists->thread_filter) {
3253 				do_zoom_thread(browser, actions);
3254 			} else if (top == &browser->hists->socket_filter) {
3255 				do_zoom_socket(browser, actions);
3256 			}
3257 			continue;
3258 		}
3259 		case 'q':
3260 		case CTRL('c'):
3261 			goto out_free_stack;
3262 		case 'f':
3263 			if (!is_report_browser(hbt)) {
3264 				struct perf_top *top = hbt->arg;
3265 
3266 				perf_evlist__toggle_enable(top->evlist);
3267 				/*
3268 				 * No need to refresh, resort/decay histogram
3269 				 * entries if we are not collecting samples:
3270 				 */
3271 				if (top->evlist->enabled) {
3272 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3273 					hbt->refresh = delay_secs;
3274 				} else {
3275 					helpline = "Press 'f' again to re-enable the events";
3276 					hbt->refresh = 0;
3277 				}
3278 				continue;
3279 			}
3280 			/* Fall thru */
3281 		default:
3282 			helpline = "Press '?' for help on key bindings";
3283 			continue;
3284 		}
3285 
3286 		if (!hists__has(hists, sym) || browser->selection == NULL)
3287 			goto skip_annotation;
3288 
3289 		if (sort__mode == SORT_MODE__BRANCH) {
3290 
3291 			if (browser->he_selection)
3292 				bi = browser->he_selection->branch_info;
3293 
3294 			if (bi == NULL)
3295 				goto skip_annotation;
3296 
3297 			nr_options += add_annotate_opt(browser,
3298 						       &actions[nr_options],
3299 						       &options[nr_options],
3300 						       &bi->from.ms,
3301 						       bi->from.al_addr);
3302 			if (bi->to.ms.sym != bi->from.ms.sym)
3303 				nr_options += add_annotate_opt(browser,
3304 							&actions[nr_options],
3305 							&options[nr_options],
3306 							&bi->to.ms,
3307 							bi->to.al_addr);
3308 		} else {
3309 			nr_options += add_annotate_opt(browser,
3310 						       &actions[nr_options],
3311 						       &options[nr_options],
3312 						       browser->selection,
3313 						       browser->he_selection->ip);
3314 		}
3315 skip_annotation:
3316 		nr_options += add_thread_opt(browser, &actions[nr_options],
3317 					     &options[nr_options], thread);
3318 		nr_options += add_dso_opt(browser, &actions[nr_options],
3319 					  &options[nr_options], map);
3320 		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3321 		nr_options += add_map_opt(browser, &actions[nr_options],
3322 					  &options[nr_options],
3323 					  browser->selection ?
3324 						browser->selection->map : NULL);
3325 		nr_options += add_socket_opt(browser, &actions[nr_options],
3326 					     &options[nr_options],
3327 					     socked_id);
3328 		/* perf script support */
3329 		if (!is_report_browser(hbt))
3330 			goto skip_scripting;
3331 
3332 		if (browser->he_selection) {
3333 			if (hists__has(hists, thread) && thread) {
3334 				nr_options += add_script_opt(browser,
3335 							     &actions[nr_options],
3336 							     &options[nr_options],
3337 							     thread, NULL, evsel);
3338 			}
3339 			/*
3340 			 * Note that browser->selection != NULL
3341 			 * when browser->he_selection is not NULL,
3342 			 * so we don't need to check browser->selection
3343 			 * before fetching browser->selection->sym like what
3344 			 * we do before fetching browser->selection->map.
3345 			 *
3346 			 * See hist_browser__show_entry.
3347 			 */
3348 			if (hists__has(hists, sym) && browser->selection->sym) {
3349 				nr_options += add_script_opt(browser,
3350 							     &actions[nr_options],
3351 							     &options[nr_options],
3352 							     NULL, browser->selection->sym,
3353 							     evsel);
3354 			}
3355 		}
3356 		nr_options += add_script_opt(browser, &actions[nr_options],
3357 					     &options[nr_options], NULL, NULL, evsel);
3358 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3359 						 &options[nr_options],
3360 				 hist_browser__selected_entry(browser)->res_samples,
3361 				 evsel, A_NORMAL);
3362 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3363 						 &options[nr_options],
3364 				 hist_browser__selected_entry(browser)->res_samples,
3365 				 evsel, A_ASM);
3366 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3367 						 &options[nr_options],
3368 				 hist_browser__selected_entry(browser)->res_samples,
3369 				 evsel, A_SOURCE);
3370 		nr_options += add_switch_opt(browser, &actions[nr_options],
3371 					     &options[nr_options]);
3372 skip_scripting:
3373 		nr_options += add_exit_opt(browser, &actions[nr_options],
3374 					   &options[nr_options]);
3375 
3376 		do {
3377 			struct popup_action *act;
3378 
3379 			choice = ui__popup_menu(nr_options, options, &key);
3380 			if (choice == -1)
3381 				break;
3382 
3383 			if (choice == nr_options)
3384 				goto do_hotkey;
3385 
3386 			act = &actions[choice];
3387 			key = act->fn(browser, act);
3388 		} while (key == 1);
3389 
3390 		if (key == K_SWITCH_INPUT_DATA)
3391 			break;
3392 	}
3393 out_free_stack:
3394 	pstack__delete(browser->pstack);
3395 out:
3396 	hist_browser__delete(browser);
3397 	free_popup_options(options, MAX_OPTIONS);
3398 	return key;
3399 }
3400 
3401 struct evsel_menu {
3402 	struct ui_browser b;
3403 	struct evsel *selection;
3404 	struct annotation_options *annotation_opts;
3405 	bool lost_events, lost_events_warned;
3406 	float min_pcnt;
3407 	struct perf_env *env;
3408 };
3409 
3410 static void perf_evsel_menu__write(struct ui_browser *browser,
3411 				   void *entry, int row)
3412 {
3413 	struct evsel_menu *menu = container_of(browser,
3414 						    struct evsel_menu, b);
3415 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3416 	struct hists *hists = evsel__hists(evsel);
3417 	bool current_entry = ui_browser__is_current_entry(browser, row);
3418 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3419 	const char *ev_name = perf_evsel__name(evsel);
3420 	char bf[256], unit;
3421 	const char *warn = " ";
3422 	size_t printed;
3423 
3424 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3425 						       HE_COLORSET_NORMAL);
3426 
3427 	if (perf_evsel__is_group_event(evsel)) {
3428 		struct evsel *pos;
3429 
3430 		ev_name = perf_evsel__group_name(evsel);
3431 
3432 		for_each_group_member(pos, evsel) {
3433 			struct hists *pos_hists = evsel__hists(pos);
3434 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3435 		}
3436 	}
3437 
3438 	nr_events = convert_unit(nr_events, &unit);
3439 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3440 			   unit, unit == ' ' ? "" : " ", ev_name);
3441 	ui_browser__printf(browser, "%s", bf);
3442 
3443 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3444 	if (nr_events != 0) {
3445 		menu->lost_events = true;
3446 		if (!current_entry)
3447 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3448 		nr_events = convert_unit(nr_events, &unit);
3449 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3450 				     nr_events, unit, unit == ' ' ? "" : " ");
3451 		warn = bf;
3452 	}
3453 
3454 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3455 
3456 	if (current_entry)
3457 		menu->selection = evsel;
3458 }
3459 
3460 static int perf_evsel_menu__run(struct evsel_menu *menu,
3461 				int nr_events, const char *help,
3462 				struct hist_browser_timer *hbt,
3463 				bool warn_lost_event)
3464 {
3465 	struct evlist *evlist = menu->b.priv;
3466 	struct evsel *pos;
3467 	const char *title = "Available samples";
3468 	int delay_secs = hbt ? hbt->refresh : 0;
3469 	int key;
3470 
3471 	if (ui_browser__show(&menu->b, title,
3472 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3473 		return -1;
3474 
3475 	while (1) {
3476 		key = ui_browser__run(&menu->b, delay_secs);
3477 
3478 		switch (key) {
3479 		case K_TIMER:
3480 			if (hbt)
3481 				hbt->timer(hbt->arg);
3482 
3483 			if (!menu->lost_events_warned &&
3484 			    menu->lost_events &&
3485 			    warn_lost_event) {
3486 				ui_browser__warn_lost_events(&menu->b);
3487 				menu->lost_events_warned = true;
3488 			}
3489 			continue;
3490 		case K_RIGHT:
3491 		case K_ENTER:
3492 			if (!menu->selection)
3493 				continue;
3494 			pos = menu->selection;
3495 browse_hists:
3496 			perf_evlist__set_selected(evlist, pos);
3497 			/*
3498 			 * Give the calling tool a chance to populate the non
3499 			 * default evsel resorted hists tree.
3500 			 */
3501 			if (hbt)
3502 				hbt->timer(hbt->arg);
3503 			key = perf_evsel__hists_browse(pos, nr_events, help,
3504 						       true, hbt,
3505 						       menu->min_pcnt,
3506 						       menu->env,
3507 						       warn_lost_event,
3508 						       menu->annotation_opts);
3509 			ui_browser__show_title(&menu->b, title);
3510 			switch (key) {
3511 			case K_TAB:
3512 				if (pos->core.node.next == &evlist->core.entries)
3513 					pos = evlist__first(evlist);
3514 				else
3515 					pos = perf_evsel__next(pos);
3516 				goto browse_hists;
3517 			case K_UNTAB:
3518 				if (pos->core.node.prev == &evlist->core.entries)
3519 					pos = evlist__last(evlist);
3520 				else
3521 					pos = perf_evsel__prev(pos);
3522 				goto browse_hists;
3523 			case K_SWITCH_INPUT_DATA:
3524 			case K_RELOAD:
3525 			case 'q':
3526 			case CTRL('c'):
3527 				goto out;
3528 			case K_ESC:
3529 			default:
3530 				continue;
3531 			}
3532 		case K_LEFT:
3533 			continue;
3534 		case K_ESC:
3535 			if (!ui_browser__dialog_yesno(&menu->b,
3536 					       "Do you really want to exit?"))
3537 				continue;
3538 			/* Fall thru */
3539 		case 'q':
3540 		case CTRL('c'):
3541 			goto out;
3542 		default:
3543 			continue;
3544 		}
3545 	}
3546 
3547 out:
3548 	ui_browser__hide(&menu->b);
3549 	return key;
3550 }
3551 
3552 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3553 				 void *entry)
3554 {
3555 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3556 
3557 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3558 		return true;
3559 
3560 	return false;
3561 }
3562 
3563 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3564 					   int nr_entries, const char *help,
3565 					   struct hist_browser_timer *hbt,
3566 					   float min_pcnt,
3567 					   struct perf_env *env,
3568 					   bool warn_lost_event,
3569 					   struct annotation_options *annotation_opts)
3570 {
3571 	struct evsel *pos;
3572 	struct evsel_menu menu = {
3573 		.b = {
3574 			.entries    = &evlist->core.entries,
3575 			.refresh    = ui_browser__list_head_refresh,
3576 			.seek	    = ui_browser__list_head_seek,
3577 			.write	    = perf_evsel_menu__write,
3578 			.filter	    = filter_group_entries,
3579 			.nr_entries = nr_entries,
3580 			.priv	    = evlist,
3581 		},
3582 		.min_pcnt = min_pcnt,
3583 		.env = env,
3584 		.annotation_opts = annotation_opts,
3585 	};
3586 
3587 	ui_helpline__push("Press ESC to exit");
3588 
3589 	evlist__for_each_entry(evlist, pos) {
3590 		const char *ev_name = perf_evsel__name(pos);
3591 		size_t line_len = strlen(ev_name) + 7;
3592 
3593 		if (menu.b.width < line_len)
3594 			menu.b.width = line_len;
3595 	}
3596 
3597 	return perf_evsel_menu__run(&menu, nr_entries, help,
3598 				    hbt, warn_lost_event);
3599 }
3600 
3601 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3602 				  struct hist_browser_timer *hbt,
3603 				  float min_pcnt,
3604 				  struct perf_env *env,
3605 				  bool warn_lost_event,
3606 				  struct annotation_options *annotation_opts)
3607 {
3608 	int nr_entries = evlist->core.nr_entries;
3609 
3610 single_entry:
3611 	if (nr_entries == 1) {
3612 		struct evsel *first = evlist__first(evlist);
3613 
3614 		return perf_evsel__hists_browse(first, nr_entries, help,
3615 						false, hbt, min_pcnt,
3616 						env, warn_lost_event,
3617 						annotation_opts);
3618 	}
3619 
3620 	if (symbol_conf.event_group) {
3621 		struct evsel *pos;
3622 
3623 		nr_entries = 0;
3624 		evlist__for_each_entry(evlist, pos) {
3625 			if (perf_evsel__is_group_leader(pos))
3626 				nr_entries++;
3627 		}
3628 
3629 		if (nr_entries == 1)
3630 			goto single_entry;
3631 	}
3632 
3633 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3634 					       hbt, min_pcnt, env,
3635 					       warn_lost_event,
3636 					       annotation_opts);
3637 }
3638 
3639 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3640 				      size_t size)
3641 {
3642 	struct hists *hists = evsel__hists(browser->block_evsel);
3643 	const char *evname = perf_evsel__name(browser->block_evsel);
3644 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3645 	int ret;
3646 
3647 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3648 	if (evname)
3649 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3650 
3651 	return 0;
3652 }
3653 
3654 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3655 			   float min_percent, struct perf_env *env,
3656 			   struct annotation_options *annotation_opts)
3657 {
3658 	struct hists *hists = &bh->block_hists;
3659 	struct hist_browser *browser;
3660 	int key = -1;
3661 	struct popup_action action;
3662 	static const char help[] =
3663 	" q             Quit \n";
3664 
3665 	browser = hist_browser__new(hists);
3666 	if (!browser)
3667 		return -1;
3668 
3669 	browser->block_evsel = evsel;
3670 	browser->title = block_hists_browser__title;
3671 	browser->min_pcnt = min_percent;
3672 	browser->env = env;
3673 	browser->annotation_opts = annotation_opts;
3674 
3675 	/* reset abort key so that it can get Ctrl-C as a key */
3676 	SLang_reset_tty();
3677 	SLang_init_tty(0, 0, 0);
3678 
3679 	memset(&action, 0, sizeof(action));
3680 
3681 	while (1) {
3682 		key = hist_browser__run(browser, "? - help", true, 0);
3683 
3684 		switch (key) {
3685 		case 'q':
3686 			goto out;
3687 		case '?':
3688 			ui_browser__help_window(&browser->b, help);
3689 			break;
3690 		case 'a':
3691 		case K_ENTER:
3692 			if (!browser->selection ||
3693 			    !browser->selection->sym) {
3694 				continue;
3695 			}
3696 
3697 			action.ms.map = browser->selection->map;
3698 			action.ms.sym = browser->selection->sym;
3699 			do_annotate(browser, &action);
3700 			continue;
3701 		default:
3702 			break;
3703 		}
3704 	}
3705 
3706 out:
3707 	hist_browser__delete(browser);
3708 	return 0;
3709 }
3710