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