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