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