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