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