xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 99b7e93c)
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <linux/rbtree.h>
6 
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../util/top.h"
14 #include "../../arch/common.h"
15 
16 #include "../browser.h"
17 #include "../helpline.h"
18 #include "../util.h"
19 #include "../ui.h"
20 #include "map.h"
21 #include "annotate.h"
22 
23 struct hist_browser {
24 	struct ui_browser   b;
25 	struct hists	    *hists;
26 	struct hist_entry   *he_selection;
27 	struct map_symbol   *selection;
28 	int		     print_seq;
29 	bool		     show_dso;
30 	bool		     show_headers;
31 	float		     min_pcnt;
32 	u64		     nr_non_filtered_entries;
33 	u64		     nr_callchain_rows;
34 };
35 
36 extern void hist_browser__init_hpp(void);
37 
38 static int hists__browser_title(struct hists *hists,
39 				struct hist_browser_timer *hbt,
40 				char *bf, size_t size);
41 static void hist_browser__update_nr_entries(struct hist_browser *hb);
42 
43 static struct rb_node *hists__filter_entries(struct rb_node *nd,
44 					     float min_pcnt);
45 
46 static bool hist_browser__has_filter(struct hist_browser *hb)
47 {
48 	return hists__has_filter(hb->hists) || hb->min_pcnt;
49 }
50 
51 static int hist_browser__get_folding(struct hist_browser *browser)
52 {
53 	struct rb_node *nd;
54 	struct hists *hists = browser->hists;
55 	int unfolded_rows = 0;
56 
57 	for (nd = rb_first(&hists->entries);
58 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
59 	     nd = rb_next(nd)) {
60 		struct hist_entry *he =
61 			rb_entry(nd, struct hist_entry, rb_node);
62 
63 		if (he->ms.unfolded)
64 			unfolded_rows += he->nr_rows;
65 	}
66 	return unfolded_rows;
67 }
68 
69 static u32 hist_browser__nr_entries(struct hist_browser *hb)
70 {
71 	u32 nr_entries;
72 
73 	if (hist_browser__has_filter(hb))
74 		nr_entries = hb->nr_non_filtered_entries;
75 	else
76 		nr_entries = hb->hists->nr_entries;
77 
78 	hb->nr_callchain_rows = hist_browser__get_folding(hb);
79 	return nr_entries + hb->nr_callchain_rows;
80 }
81 
82 static void hist_browser__update_rows(struct hist_browser *hb)
83 {
84 	struct ui_browser *browser = &hb->b;
85 	u16 header_offset = hb->show_headers ? 1 : 0, index_row;
86 
87 	browser->rows = browser->height - header_offset;
88 	/*
89 	 * Verify if we were at the last line and that line isn't
90 	 * visibe because we now show the header line(s).
91 	 */
92 	index_row = browser->index - browser->top_idx;
93 	if (index_row >= browser->rows)
94 		browser->index -= index_row - browser->rows + 1;
95 }
96 
97 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
98 {
99 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
100 
101 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
102 	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
103 	/*
104  	 * FIXME: Just keeping existing behaviour, but this really should be
105  	 *	  before updating browser->width, as it will invalidate the
106  	 *	  calculation above. Fix this and the fallout in another
107  	 *	  changeset.
108  	 */
109 	ui_browser__refresh_dimensions(browser);
110 	hist_browser__update_rows(hb);
111 }
112 
113 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
114 {
115 	u16 header_offset = browser->show_headers ? 1 : 0;
116 
117 	ui_browser__gotorc(&browser->b, row + header_offset, column);
118 }
119 
120 static void hist_browser__reset(struct hist_browser *browser)
121 {
122 	/*
123 	 * The hists__remove_entry_filter() already folds non-filtered
124 	 * entries so we can assume it has 0 callchain rows.
125 	 */
126 	browser->nr_callchain_rows = 0;
127 
128 	hist_browser__update_nr_entries(browser);
129 	browser->b.nr_entries = hist_browser__nr_entries(browser);
130 	hist_browser__refresh_dimensions(&browser->b);
131 	ui_browser__reset_index(&browser->b);
132 }
133 
134 static char tree__folded_sign(bool unfolded)
135 {
136 	return unfolded ? '-' : '+';
137 }
138 
139 static char map_symbol__folded(const struct map_symbol *ms)
140 {
141 	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
142 }
143 
144 static char hist_entry__folded(const struct hist_entry *he)
145 {
146 	return map_symbol__folded(&he->ms);
147 }
148 
149 static char callchain_list__folded(const struct callchain_list *cl)
150 {
151 	return map_symbol__folded(&cl->ms);
152 }
153 
154 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
155 {
156 	ms->unfolded = unfold ? ms->has_children : false;
157 }
158 
159 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
160 {
161 	int n = 0;
162 	struct rb_node *nd;
163 
164 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
165 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
166 		struct callchain_list *chain;
167 		char folded_sign = ' '; /* No children */
168 
169 		list_for_each_entry(chain, &child->val, list) {
170 			++n;
171 			/* We need this because we may not have children */
172 			folded_sign = callchain_list__folded(chain);
173 			if (folded_sign == '+')
174 				break;
175 		}
176 
177 		if (folded_sign == '-') /* Have children and they're unfolded */
178 			n += callchain_node__count_rows_rb_tree(child);
179 	}
180 
181 	return n;
182 }
183 
184 static int callchain_node__count_rows(struct callchain_node *node)
185 {
186 	struct callchain_list *chain;
187 	bool unfolded = false;
188 	int n = 0;
189 
190 	list_for_each_entry(chain, &node->val, list) {
191 		++n;
192 		unfolded = chain->ms.unfolded;
193 	}
194 
195 	if (unfolded)
196 		n += callchain_node__count_rows_rb_tree(node);
197 
198 	return n;
199 }
200 
201 static int callchain__count_rows(struct rb_root *chain)
202 {
203 	struct rb_node *nd;
204 	int n = 0;
205 
206 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
207 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
208 		n += callchain_node__count_rows(node);
209 	}
210 
211 	return n;
212 }
213 
214 static bool map_symbol__toggle_fold(struct map_symbol *ms)
215 {
216 	if (!ms)
217 		return false;
218 
219 	if (!ms->has_children)
220 		return false;
221 
222 	ms->unfolded = !ms->unfolded;
223 	return true;
224 }
225 
226 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
227 {
228 	struct rb_node *nd = rb_first(&node->rb_root);
229 
230 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
231 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
232 		struct callchain_list *chain;
233 		bool first = true;
234 
235 		list_for_each_entry(chain, &child->val, list) {
236 			if (first) {
237 				first = false;
238 				chain->ms.has_children = chain->list.next != &child->val ||
239 							 !RB_EMPTY_ROOT(&child->rb_root);
240 			} else
241 				chain->ms.has_children = chain->list.next == &child->val &&
242 							 !RB_EMPTY_ROOT(&child->rb_root);
243 		}
244 
245 		callchain_node__init_have_children_rb_tree(child);
246 	}
247 }
248 
249 static void callchain_node__init_have_children(struct callchain_node *node,
250 					       bool has_sibling)
251 {
252 	struct callchain_list *chain;
253 
254 	chain = list_entry(node->val.next, struct callchain_list, list);
255 	chain->ms.has_children = has_sibling;
256 
257 	if (!list_empty(&node->val)) {
258 		chain = list_entry(node->val.prev, struct callchain_list, list);
259 		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
260 	}
261 
262 	callchain_node__init_have_children_rb_tree(node);
263 }
264 
265 static void callchain__init_have_children(struct rb_root *root)
266 {
267 	struct rb_node *nd = rb_first(root);
268 	bool has_sibling = nd && rb_next(nd);
269 
270 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
271 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
272 		callchain_node__init_have_children(node, has_sibling);
273 	}
274 }
275 
276 static void hist_entry__init_have_children(struct hist_entry *he)
277 {
278 	if (!he->init_have_children) {
279 		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
280 		callchain__init_have_children(&he->sorted_chain);
281 		he->init_have_children = true;
282 	}
283 }
284 
285 static bool hist_browser__toggle_fold(struct hist_browser *browser)
286 {
287 	if (map_symbol__toggle_fold(browser->selection)) {
288 		struct hist_entry *he = browser->he_selection;
289 
290 		hist_entry__init_have_children(he);
291 		browser->b.nr_entries -= he->nr_rows;
292 		browser->nr_callchain_rows -= he->nr_rows;
293 
294 		if (he->ms.unfolded)
295 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
296 		else
297 			he->nr_rows = 0;
298 
299 		browser->b.nr_entries += he->nr_rows;
300 		browser->nr_callchain_rows += he->nr_rows;
301 
302 		return true;
303 	}
304 
305 	/* If it doesn't have children, no toggling performed */
306 	return false;
307 }
308 
309 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
310 {
311 	int n = 0;
312 	struct rb_node *nd;
313 
314 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
315 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
316 		struct callchain_list *chain;
317 		bool has_children = false;
318 
319 		list_for_each_entry(chain, &child->val, list) {
320 			++n;
321 			map_symbol__set_folding(&chain->ms, unfold);
322 			has_children = chain->ms.has_children;
323 		}
324 
325 		if (has_children)
326 			n += callchain_node__set_folding_rb_tree(child, unfold);
327 	}
328 
329 	return n;
330 }
331 
332 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
333 {
334 	struct callchain_list *chain;
335 	bool has_children = false;
336 	int n = 0;
337 
338 	list_for_each_entry(chain, &node->val, list) {
339 		++n;
340 		map_symbol__set_folding(&chain->ms, unfold);
341 		has_children = chain->ms.has_children;
342 	}
343 
344 	if (has_children)
345 		n += callchain_node__set_folding_rb_tree(node, unfold);
346 
347 	return n;
348 }
349 
350 static int callchain__set_folding(struct rb_root *chain, bool unfold)
351 {
352 	struct rb_node *nd;
353 	int n = 0;
354 
355 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
356 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
357 		n += callchain_node__set_folding(node, unfold);
358 	}
359 
360 	return n;
361 }
362 
363 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
364 {
365 	hist_entry__init_have_children(he);
366 	map_symbol__set_folding(&he->ms, unfold);
367 
368 	if (he->ms.has_children) {
369 		int n = callchain__set_folding(&he->sorted_chain, unfold);
370 		he->nr_rows = unfold ? n : 0;
371 	} else
372 		he->nr_rows = 0;
373 }
374 
375 static void
376 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
377 {
378 	struct rb_node *nd;
379 	struct hists *hists = browser->hists;
380 
381 	for (nd = rb_first(&hists->entries);
382 	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
383 	     nd = rb_next(nd)) {
384 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
385 		hist_entry__set_folding(he, unfold);
386 		browser->nr_callchain_rows += he->nr_rows;
387 	}
388 }
389 
390 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
391 {
392 	browser->nr_callchain_rows = 0;
393 	__hist_browser__set_folding(browser, unfold);
394 
395 	browser->b.nr_entries = hist_browser__nr_entries(browser);
396 	/* Go to the start, we may be way after valid entries after a collapse */
397 	ui_browser__reset_index(&browser->b);
398 }
399 
400 static void ui_browser__warn_lost_events(struct ui_browser *browser)
401 {
402 	ui_browser__warning(browser, 4,
403 		"Events are being lost, check IO/CPU overload!\n\n"
404 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
405 		" perf top -r 80\n\n"
406 		"Or reduce the sampling frequency.");
407 }
408 
409 static int hist_browser__run(struct hist_browser *browser,
410 			     struct hist_browser_timer *hbt)
411 {
412 	int key;
413 	char title[160];
414 	int delay_secs = hbt ? hbt->refresh : 0;
415 
416 	browser->b.entries = &browser->hists->entries;
417 	browser->b.nr_entries = hist_browser__nr_entries(browser);
418 
419 	hists__browser_title(browser->hists, hbt, title, sizeof(title));
420 
421 	if (ui_browser__show(&browser->b, title,
422 			     "Press '?' for help on key bindings") < 0)
423 		return -1;
424 
425 	while (1) {
426 		key = ui_browser__run(&browser->b, delay_secs);
427 
428 		switch (key) {
429 		case K_TIMER: {
430 			u64 nr_entries;
431 			hbt->timer(hbt->arg);
432 
433 			if (hist_browser__has_filter(browser))
434 				hist_browser__update_nr_entries(browser);
435 
436 			nr_entries = hist_browser__nr_entries(browser);
437 			ui_browser__update_nr_entries(&browser->b, nr_entries);
438 
439 			if (browser->hists->stats.nr_lost_warned !=
440 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
441 				browser->hists->stats.nr_lost_warned =
442 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
443 				ui_browser__warn_lost_events(&browser->b);
444 			}
445 
446 			hists__browser_title(browser->hists,
447 					     hbt, title, sizeof(title));
448 			ui_browser__show_title(&browser->b, title);
449 			continue;
450 		}
451 		case 'D': { /* Debug */
452 			static int seq;
453 			struct hist_entry *h = rb_entry(browser->b.top,
454 							struct hist_entry, rb_node);
455 			ui_helpline__pop();
456 			ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
457 					   seq++, browser->b.nr_entries,
458 					   browser->hists->nr_entries,
459 					   browser->b.rows,
460 					   browser->b.index,
461 					   browser->b.top_idx,
462 					   h->row_offset, h->nr_rows);
463 		}
464 			break;
465 		case 'C':
466 			/* Collapse the whole world. */
467 			hist_browser__set_folding(browser, false);
468 			break;
469 		case 'E':
470 			/* Expand the whole world. */
471 			hist_browser__set_folding(browser, true);
472 			break;
473 		case 'H':
474 			browser->show_headers = !browser->show_headers;
475 			hist_browser__update_rows(browser);
476 			break;
477 		case K_ENTER:
478 			if (hist_browser__toggle_fold(browser))
479 				break;
480 			/* fall thru */
481 		default:
482 			goto out;
483 		}
484 	}
485 out:
486 	ui_browser__hide(&browser->b);
487 	return key;
488 }
489 
490 struct callchain_print_arg {
491 	/* for hists browser */
492 	off_t	row_offset;
493 	bool	is_current_entry;
494 
495 	/* for file dump */
496 	FILE	*fp;
497 	int	printed;
498 };
499 
500 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
501 					 struct callchain_list *chain,
502 					 const char *str, int offset,
503 					 unsigned short row,
504 					 struct callchain_print_arg *arg);
505 
506 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
507 					       struct callchain_list *chain,
508 					       const char *str, int offset,
509 					       unsigned short row,
510 					       struct callchain_print_arg *arg)
511 {
512 	int color, width;
513 	char folded_sign = callchain_list__folded(chain);
514 	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
515 
516 	color = HE_COLORSET_NORMAL;
517 	width = browser->b.width - (offset + 2);
518 	if (ui_browser__is_current_entry(&browser->b, row)) {
519 		browser->selection = &chain->ms;
520 		color = HE_COLORSET_SELECTED;
521 		arg->is_current_entry = true;
522 	}
523 
524 	ui_browser__set_color(&browser->b, color);
525 	hist_browser__gotorc(browser, row, 0);
526 	slsmg_write_nstring(" ", offset);
527 	slsmg_printf("%c", folded_sign);
528 	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
529 	slsmg_write_nstring(str, width);
530 }
531 
532 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
533 						  struct callchain_list *chain,
534 						  const char *str, int offset,
535 						  unsigned short row __maybe_unused,
536 						  struct callchain_print_arg *arg)
537 {
538 	char folded_sign = callchain_list__folded(chain);
539 
540 	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
541 				folded_sign, str);
542 }
543 
544 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
545 				     unsigned short row);
546 
547 static bool hist_browser__check_output_full(struct hist_browser *browser,
548 					    unsigned short row)
549 {
550 	return browser->b.rows == row;
551 }
552 
553 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
554 					  unsigned short row __maybe_unused)
555 {
556 	return false;
557 }
558 
559 #define LEVEL_OFFSET_STEP 3
560 
561 static int hist_browser__show_callchain(struct hist_browser *browser,
562 					struct rb_root *root, int level,
563 					unsigned short row, u64 total,
564 					print_callchain_entry_fn print,
565 					struct callchain_print_arg *arg,
566 					check_output_full_fn is_output_full)
567 {
568 	struct rb_node *node;
569 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
570 	u64 new_total;
571 	bool need_percent;
572 
573 	node = rb_first(root);
574 	need_percent = node && rb_next(node);
575 
576 	while (node) {
577 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
578 		struct rb_node *next = rb_next(node);
579 		u64 cumul = callchain_cumul_hits(child);
580 		struct callchain_list *chain;
581 		char folded_sign = ' ';
582 		int first = true;
583 		int extra_offset = 0;
584 
585 		list_for_each_entry(chain, &child->val, list) {
586 			char bf[1024], *alloc_str;
587 			const char *str;
588 			bool was_first = first;
589 
590 			if (first)
591 				first = false;
592 			else if (need_percent)
593 				extra_offset = LEVEL_OFFSET_STEP;
594 
595 			folded_sign = callchain_list__folded(chain);
596 			if (arg->row_offset != 0) {
597 				arg->row_offset--;
598 				goto do_next;
599 			}
600 
601 			alloc_str = NULL;
602 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
603 						       browser->show_dso);
604 
605 			if (was_first && need_percent) {
606 				double percent = cumul * 100.0 / total;
607 
608 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
609 					str = "Not enough memory!";
610 				else
611 					str = alloc_str;
612 			}
613 
614 			print(browser, chain, str, offset + extra_offset, row, arg);
615 
616 			free(alloc_str);
617 
618 			if (is_output_full(browser, ++row))
619 				goto out;
620 do_next:
621 			if (folded_sign == '+')
622 				break;
623 		}
624 
625 		if (folded_sign == '-') {
626 			const int new_level = level + (extra_offset ? 2 : 1);
627 
628 			if (callchain_param.mode == CHAIN_GRAPH_REL)
629 				new_total = child->children_hit;
630 			else
631 				new_total = total;
632 
633 			row += hist_browser__show_callchain(browser, &child->rb_root,
634 							    new_level, row, new_total,
635 							    print, arg, is_output_full);
636 		}
637 		if (is_output_full(browser, row))
638 			break;
639 		node = next;
640 	}
641 out:
642 	return row - first_row;
643 }
644 
645 struct hpp_arg {
646 	struct ui_browser *b;
647 	char folded_sign;
648 	bool current_entry;
649 };
650 
651 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
652 {
653 	struct hpp_arg *arg = hpp->ptr;
654 	int ret, len;
655 	va_list args;
656 	double percent;
657 
658 	va_start(args, fmt);
659 	len = va_arg(args, int);
660 	percent = va_arg(args, double);
661 	va_end(args);
662 
663 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
664 
665 	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
666 	slsmg_printf("%s", hpp->buf);
667 
668 	advance_hpp(hpp, ret);
669 	return ret;
670 }
671 
672 #define __HPP_COLOR_PERCENT_FN(_type, _field)				\
673 static u64 __hpp_get_##_field(struct hist_entry *he)			\
674 {									\
675 	return he->stat._field;						\
676 }									\
677 									\
678 static int								\
679 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
680 				struct perf_hpp *hpp,			\
681 				struct hist_entry *he)			\
682 {									\
683 	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
684 			__hpp__slsmg_color_printf, true);		\
685 }
686 
687 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
688 static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
689 {									\
690 	return he->stat_acc->_field;					\
691 }									\
692 									\
693 static int								\
694 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
695 				struct perf_hpp *hpp,			\
696 				struct hist_entry *he)			\
697 {									\
698 	if (!symbol_conf.cumulate_callchain) {				\
699 		int len = fmt->user_len ?: fmt->len;			\
700 		int ret = scnprintf(hpp->buf, hpp->size,		\
701 				    "%*s", len, "N/A");			\
702 		slsmg_printf("%s", hpp->buf);				\
703 									\
704 		return ret;						\
705 	}								\
706 	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
707 			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
708 }
709 
710 __HPP_COLOR_PERCENT_FN(overhead, period)
711 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
712 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
713 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
714 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
715 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
716 
717 #undef __HPP_COLOR_PERCENT_FN
718 #undef __HPP_COLOR_ACC_PERCENT_FN
719 
720 void hist_browser__init_hpp(void)
721 {
722 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
723 				hist_browser__hpp_color_overhead;
724 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
725 				hist_browser__hpp_color_overhead_sys;
726 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
727 				hist_browser__hpp_color_overhead_us;
728 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
729 				hist_browser__hpp_color_overhead_guest_sys;
730 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
731 				hist_browser__hpp_color_overhead_guest_us;
732 	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
733 				hist_browser__hpp_color_overhead_acc;
734 }
735 
736 static int hist_browser__show_entry(struct hist_browser *browser,
737 				    struct hist_entry *entry,
738 				    unsigned short row)
739 {
740 	char s[256];
741 	int printed = 0;
742 	int width = browser->b.width;
743 	char folded_sign = ' ';
744 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
745 	off_t row_offset = entry->row_offset;
746 	bool first = true;
747 	struct perf_hpp_fmt *fmt;
748 
749 	if (current_entry) {
750 		browser->he_selection = entry;
751 		browser->selection = &entry->ms;
752 	}
753 
754 	if (symbol_conf.use_callchain) {
755 		hist_entry__init_have_children(entry);
756 		folded_sign = hist_entry__folded(entry);
757 	}
758 
759 	if (row_offset == 0) {
760 		struct hpp_arg arg = {
761 			.b		= &browser->b,
762 			.folded_sign	= folded_sign,
763 			.current_entry	= current_entry,
764 		};
765 		struct perf_hpp hpp = {
766 			.buf		= s,
767 			.size		= sizeof(s),
768 			.ptr		= &arg,
769 		};
770 
771 		hist_browser__gotorc(browser, row, 0);
772 
773 		perf_hpp__for_each_format(fmt) {
774 			if (perf_hpp__should_skip(fmt))
775 				continue;
776 
777 			if (current_entry && browser->b.navkeypressed) {
778 				ui_browser__set_color(&browser->b,
779 						      HE_COLORSET_SELECTED);
780 			} else {
781 				ui_browser__set_color(&browser->b,
782 						      HE_COLORSET_NORMAL);
783 			}
784 
785 			if (first) {
786 				if (symbol_conf.use_callchain) {
787 					slsmg_printf("%c ", folded_sign);
788 					width -= 2;
789 				}
790 				first = false;
791 			} else {
792 				slsmg_printf("  ");
793 				width -= 2;
794 			}
795 
796 			if (fmt->color) {
797 				width -= fmt->color(fmt, &hpp, entry);
798 			} else {
799 				width -= fmt->entry(fmt, &hpp, entry);
800 				slsmg_printf("%s", s);
801 			}
802 		}
803 
804 		/* The scroll bar isn't being used */
805 		if (!browser->b.navkeypressed)
806 			width += 1;
807 
808 		slsmg_write_nstring("", width);
809 
810 		++row;
811 		++printed;
812 	} else
813 		--row_offset;
814 
815 	if (folded_sign == '-' && row != browser->b.rows) {
816 		u64 total = hists__total_period(entry->hists);
817 		struct callchain_print_arg arg = {
818 			.row_offset = row_offset,
819 			.is_current_entry = current_entry,
820 		};
821 
822 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
823 			if (symbol_conf.cumulate_callchain)
824 				total = entry->stat_acc->period;
825 			else
826 				total = entry->stat.period;
827 		}
828 
829 		printed += hist_browser__show_callchain(browser,
830 					&entry->sorted_chain, 1, row, total,
831 					hist_browser__show_callchain_entry, &arg,
832 					hist_browser__check_output_full);
833 
834 		if (arg.is_current_entry)
835 			browser->he_selection = entry;
836 	}
837 
838 	return printed;
839 }
840 
841 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
842 {
843 	advance_hpp(hpp, inc);
844 	return hpp->size <= 0;
845 }
846 
847 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
848 {
849 	struct perf_hpp dummy_hpp = {
850 		.buf    = buf,
851 		.size   = size,
852 	};
853 	struct perf_hpp_fmt *fmt;
854 	size_t ret = 0;
855 
856 	if (symbol_conf.use_callchain) {
857 		ret = scnprintf(buf, size, "  ");
858 		if (advance_hpp_check(&dummy_hpp, ret))
859 			return ret;
860 	}
861 
862 	perf_hpp__for_each_format(fmt) {
863 		if (perf_hpp__should_skip(fmt))
864 			continue;
865 
866 		ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
867 		if (advance_hpp_check(&dummy_hpp, ret))
868 			break;
869 
870 		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
871 		if (advance_hpp_check(&dummy_hpp, ret))
872 			break;
873 	}
874 
875 	return ret;
876 }
877 
878 static void hist_browser__show_headers(struct hist_browser *browser)
879 {
880 	char headers[1024];
881 
882 	hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
883 	ui_browser__gotorc(&browser->b, 0, 0);
884 	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
885 	slsmg_write_nstring(headers, browser->b.width + 1);
886 }
887 
888 static void ui_browser__hists_init_top(struct ui_browser *browser)
889 {
890 	if (browser->top == NULL) {
891 		struct hist_browser *hb;
892 
893 		hb = container_of(browser, struct hist_browser, b);
894 		browser->top = rb_first(&hb->hists->entries);
895 	}
896 }
897 
898 static unsigned int hist_browser__refresh(struct ui_browser *browser)
899 {
900 	unsigned row = 0;
901 	u16 header_offset = 0;
902 	struct rb_node *nd;
903 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
904 
905 	if (hb->show_headers) {
906 		hist_browser__show_headers(hb);
907 		header_offset = 1;
908 	}
909 
910 	ui_browser__hists_init_top(browser);
911 
912 	for (nd = browser->top; nd; nd = rb_next(nd)) {
913 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
914 		float percent;
915 
916 		if (h->filtered)
917 			continue;
918 
919 		percent = hist_entry__get_percent_limit(h);
920 		if (percent < hb->min_pcnt)
921 			continue;
922 
923 		row += hist_browser__show_entry(hb, h, row);
924 		if (row == browser->rows)
925 			break;
926 	}
927 
928 	return row + header_offset;
929 }
930 
931 static struct rb_node *hists__filter_entries(struct rb_node *nd,
932 					     float min_pcnt)
933 {
934 	while (nd != NULL) {
935 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
936 		float percent = hist_entry__get_percent_limit(h);
937 
938 		if (!h->filtered && percent >= min_pcnt)
939 			return nd;
940 
941 		nd = rb_next(nd);
942 	}
943 
944 	return NULL;
945 }
946 
947 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
948 						  float min_pcnt)
949 {
950 	while (nd != NULL) {
951 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
952 		float percent = hist_entry__get_percent_limit(h);
953 
954 		if (!h->filtered && percent >= min_pcnt)
955 			return nd;
956 
957 		nd = rb_prev(nd);
958 	}
959 
960 	return NULL;
961 }
962 
963 static void ui_browser__hists_seek(struct ui_browser *browser,
964 				   off_t offset, int whence)
965 {
966 	struct hist_entry *h;
967 	struct rb_node *nd;
968 	bool first = true;
969 	struct hist_browser *hb;
970 
971 	hb = container_of(browser, struct hist_browser, b);
972 
973 	if (browser->nr_entries == 0)
974 		return;
975 
976 	ui_browser__hists_init_top(browser);
977 
978 	switch (whence) {
979 	case SEEK_SET:
980 		nd = hists__filter_entries(rb_first(browser->entries),
981 					   hb->min_pcnt);
982 		break;
983 	case SEEK_CUR:
984 		nd = browser->top;
985 		goto do_offset;
986 	case SEEK_END:
987 		nd = hists__filter_prev_entries(rb_last(browser->entries),
988 						hb->min_pcnt);
989 		first = false;
990 		break;
991 	default:
992 		return;
993 	}
994 
995 	/*
996 	 * Moves not relative to the first visible entry invalidates its
997 	 * row_offset:
998 	 */
999 	h = rb_entry(browser->top, struct hist_entry, rb_node);
1000 	h->row_offset = 0;
1001 
1002 	/*
1003 	 * Here we have to check if nd is expanded (+), if it is we can't go
1004 	 * the next top level hist_entry, instead we must compute an offset of
1005 	 * what _not_ to show and not change the first visible entry.
1006 	 *
1007 	 * This offset increments when we are going from top to bottom and
1008 	 * decreases when we're going from bottom to top.
1009 	 *
1010 	 * As we don't have backpointers to the top level in the callchains
1011 	 * structure, we need to always print the whole hist_entry callchain,
1012 	 * skipping the first ones that are before the first visible entry
1013 	 * and stop when we printed enough lines to fill the screen.
1014 	 */
1015 do_offset:
1016 	if (offset > 0) {
1017 		do {
1018 			h = rb_entry(nd, struct hist_entry, rb_node);
1019 			if (h->ms.unfolded) {
1020 				u16 remaining = h->nr_rows - h->row_offset;
1021 				if (offset > remaining) {
1022 					offset -= remaining;
1023 					h->row_offset = 0;
1024 				} else {
1025 					h->row_offset += offset;
1026 					offset = 0;
1027 					browser->top = nd;
1028 					break;
1029 				}
1030 			}
1031 			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1032 			if (nd == NULL)
1033 				break;
1034 			--offset;
1035 			browser->top = nd;
1036 		} while (offset != 0);
1037 	} else if (offset < 0) {
1038 		while (1) {
1039 			h = rb_entry(nd, struct hist_entry, rb_node);
1040 			if (h->ms.unfolded) {
1041 				if (first) {
1042 					if (-offset > h->row_offset) {
1043 						offset += h->row_offset;
1044 						h->row_offset = 0;
1045 					} else {
1046 						h->row_offset += offset;
1047 						offset = 0;
1048 						browser->top = nd;
1049 						break;
1050 					}
1051 				} else {
1052 					if (-offset > h->nr_rows) {
1053 						offset += h->nr_rows;
1054 						h->row_offset = 0;
1055 					} else {
1056 						h->row_offset = h->nr_rows + offset;
1057 						offset = 0;
1058 						browser->top = nd;
1059 						break;
1060 					}
1061 				}
1062 			}
1063 
1064 			nd = hists__filter_prev_entries(rb_prev(nd),
1065 							hb->min_pcnt);
1066 			if (nd == NULL)
1067 				break;
1068 			++offset;
1069 			browser->top = nd;
1070 			if (offset == 0) {
1071 				/*
1072 				 * Last unfiltered hist_entry, check if it is
1073 				 * unfolded, if it is then we should have
1074 				 * row_offset at its last entry.
1075 				 */
1076 				h = rb_entry(nd, struct hist_entry, rb_node);
1077 				if (h->ms.unfolded)
1078 					h->row_offset = h->nr_rows;
1079 				break;
1080 			}
1081 			first = false;
1082 		}
1083 	} else {
1084 		browser->top = nd;
1085 		h = rb_entry(nd, struct hist_entry, rb_node);
1086 		h->row_offset = 0;
1087 	}
1088 }
1089 
1090 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1091 					   struct hist_entry *he, FILE *fp)
1092 {
1093 	u64 total = hists__total_period(he->hists);
1094 	struct callchain_print_arg arg  = {
1095 		.fp = fp,
1096 	};
1097 
1098 	if (symbol_conf.cumulate_callchain)
1099 		total = he->stat_acc->period;
1100 
1101 	hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1102 				     hist_browser__fprintf_callchain_entry, &arg,
1103 				     hist_browser__check_dump_full);
1104 	return arg.printed;
1105 }
1106 
1107 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1108 				       struct hist_entry *he, FILE *fp)
1109 {
1110 	char s[8192];
1111 	int printed = 0;
1112 	char folded_sign = ' ';
1113 	struct perf_hpp hpp = {
1114 		.buf = s,
1115 		.size = sizeof(s),
1116 	};
1117 	struct perf_hpp_fmt *fmt;
1118 	bool first = true;
1119 	int ret;
1120 
1121 	if (symbol_conf.use_callchain)
1122 		folded_sign = hist_entry__folded(he);
1123 
1124 	if (symbol_conf.use_callchain)
1125 		printed += fprintf(fp, "%c ", folded_sign);
1126 
1127 	perf_hpp__for_each_format(fmt) {
1128 		if (perf_hpp__should_skip(fmt))
1129 			continue;
1130 
1131 		if (!first) {
1132 			ret = scnprintf(hpp.buf, hpp.size, "  ");
1133 			advance_hpp(&hpp, ret);
1134 		} else
1135 			first = false;
1136 
1137 		ret = fmt->entry(fmt, &hpp, he);
1138 		advance_hpp(&hpp, ret);
1139 	}
1140 	printed += fprintf(fp, "%s\n", rtrim(s));
1141 
1142 	if (folded_sign == '-')
1143 		printed += hist_browser__fprintf_callchain(browser, he, fp);
1144 
1145 	return printed;
1146 }
1147 
1148 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1149 {
1150 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1151 						   browser->min_pcnt);
1152 	int printed = 0;
1153 
1154 	while (nd) {
1155 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1156 
1157 		printed += hist_browser__fprintf_entry(browser, h, fp);
1158 		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1159 	}
1160 
1161 	return printed;
1162 }
1163 
1164 static int hist_browser__dump(struct hist_browser *browser)
1165 {
1166 	char filename[64];
1167 	FILE *fp;
1168 
1169 	while (1) {
1170 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1171 		if (access(filename, F_OK))
1172 			break;
1173 		/*
1174  		 * XXX: Just an arbitrary lazy upper limit
1175  		 */
1176 		if (++browser->print_seq == 8192) {
1177 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1178 			return -1;
1179 		}
1180 	}
1181 
1182 	fp = fopen(filename, "w");
1183 	if (fp == NULL) {
1184 		char bf[64];
1185 		const char *err = strerror_r(errno, bf, sizeof(bf));
1186 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1187 		return -1;
1188 	}
1189 
1190 	++browser->print_seq;
1191 	hist_browser__fprintf(browser, fp);
1192 	fclose(fp);
1193 	ui_helpline__fpush("%s written!", filename);
1194 
1195 	return 0;
1196 }
1197 
1198 static struct hist_browser *hist_browser__new(struct hists *hists)
1199 {
1200 	struct hist_browser *browser = zalloc(sizeof(*browser));
1201 
1202 	if (browser) {
1203 		browser->hists = hists;
1204 		browser->b.refresh = hist_browser__refresh;
1205 		browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1206 		browser->b.seek = ui_browser__hists_seek;
1207 		browser->b.use_navkeypressed = true;
1208 		browser->show_headers = symbol_conf.show_hist_headers;
1209 	}
1210 
1211 	return browser;
1212 }
1213 
1214 static void hist_browser__delete(struct hist_browser *browser)
1215 {
1216 	free(browser);
1217 }
1218 
1219 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1220 {
1221 	return browser->he_selection;
1222 }
1223 
1224 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1225 {
1226 	return browser->he_selection->thread;
1227 }
1228 
1229 /* Check whether the browser is for 'top' or 'report' */
1230 static inline bool is_report_browser(void *timer)
1231 {
1232 	return timer == NULL;
1233 }
1234 
1235 static int hists__browser_title(struct hists *hists,
1236 				struct hist_browser_timer *hbt,
1237 				char *bf, size_t size)
1238 {
1239 	char unit;
1240 	int printed;
1241 	const struct dso *dso = hists->dso_filter;
1242 	const struct thread *thread = hists->thread_filter;
1243 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1244 	u64 nr_events = hists->stats.total_period;
1245 	struct perf_evsel *evsel = hists_to_evsel(hists);
1246 	const char *ev_name = perf_evsel__name(evsel);
1247 	char buf[512];
1248 	size_t buflen = sizeof(buf);
1249 
1250 	if (symbol_conf.filter_relative) {
1251 		nr_samples = hists->stats.nr_non_filtered_samples;
1252 		nr_events = hists->stats.total_non_filtered_period;
1253 	}
1254 
1255 	if (perf_evsel__is_group_event(evsel)) {
1256 		struct perf_evsel *pos;
1257 
1258 		perf_evsel__group_desc(evsel, buf, buflen);
1259 		ev_name = buf;
1260 
1261 		for_each_group_member(pos, evsel) {
1262 			struct hists *pos_hists = evsel__hists(pos);
1263 
1264 			if (symbol_conf.filter_relative) {
1265 				nr_samples += pos_hists->stats.nr_non_filtered_samples;
1266 				nr_events += pos_hists->stats.total_non_filtered_period;
1267 			} else {
1268 				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1269 				nr_events += pos_hists->stats.total_period;
1270 			}
1271 		}
1272 	}
1273 
1274 	nr_samples = convert_unit(nr_samples, &unit);
1275 	printed = scnprintf(bf, size,
1276 			   "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64,
1277 			   nr_samples, unit, ev_name, nr_events);
1278 
1279 
1280 	if (hists->uid_filter_str)
1281 		printed += snprintf(bf + printed, size - printed,
1282 				    ", UID: %s", hists->uid_filter_str);
1283 	if (thread)
1284 		printed += scnprintf(bf + printed, size - printed,
1285 				    ", Thread: %s(%d)",
1286 				     (thread->comm_set ? thread__comm_str(thread) : ""),
1287 				    thread->tid);
1288 	if (dso)
1289 		printed += scnprintf(bf + printed, size - printed,
1290 				    ", DSO: %s", dso->short_name);
1291 	if (!is_report_browser(hbt)) {
1292 		struct perf_top *top = hbt->arg;
1293 
1294 		if (top->zero)
1295 			printed += scnprintf(bf + printed, size - printed, " [z]");
1296 	}
1297 
1298 	return printed;
1299 }
1300 
1301 static inline void free_popup_options(char **options, int n)
1302 {
1303 	int i;
1304 
1305 	for (i = 0; i < n; ++i)
1306 		zfree(&options[i]);
1307 }
1308 
1309 /*
1310  * Only runtime switching of perf data file will make "input_name" point
1311  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1312  * whether we need to call free() for current "input_name" during the switch.
1313  */
1314 static bool is_input_name_malloced = false;
1315 
1316 static int switch_data_file(void)
1317 {
1318 	char *pwd, *options[32], *abs_path[32], *tmp;
1319 	DIR *pwd_dir;
1320 	int nr_options = 0, choice = -1, ret = -1;
1321 	struct dirent *dent;
1322 
1323 	pwd = getenv("PWD");
1324 	if (!pwd)
1325 		return ret;
1326 
1327 	pwd_dir = opendir(pwd);
1328 	if (!pwd_dir)
1329 		return ret;
1330 
1331 	memset(options, 0, sizeof(options));
1332 	memset(options, 0, sizeof(abs_path));
1333 
1334 	while ((dent = readdir(pwd_dir))) {
1335 		char path[PATH_MAX];
1336 		u64 magic;
1337 		char *name = dent->d_name;
1338 		FILE *file;
1339 
1340 		if (!(dent->d_type == DT_REG))
1341 			continue;
1342 
1343 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1344 
1345 		file = fopen(path, "r");
1346 		if (!file)
1347 			continue;
1348 
1349 		if (fread(&magic, 1, 8, file) < 8)
1350 			goto close_file_and_continue;
1351 
1352 		if (is_perf_magic(magic)) {
1353 			options[nr_options] = strdup(name);
1354 			if (!options[nr_options])
1355 				goto close_file_and_continue;
1356 
1357 			abs_path[nr_options] = strdup(path);
1358 			if (!abs_path[nr_options]) {
1359 				zfree(&options[nr_options]);
1360 				ui__warning("Can't search all data files due to memory shortage.\n");
1361 				fclose(file);
1362 				break;
1363 			}
1364 
1365 			nr_options++;
1366 		}
1367 
1368 close_file_and_continue:
1369 		fclose(file);
1370 		if (nr_options >= 32) {
1371 			ui__warning("Too many perf data files in PWD!\n"
1372 				    "Only the first 32 files will be listed.\n");
1373 			break;
1374 		}
1375 	}
1376 	closedir(pwd_dir);
1377 
1378 	if (nr_options) {
1379 		choice = ui__popup_menu(nr_options, options);
1380 		if (choice < nr_options && choice >= 0) {
1381 			tmp = strdup(abs_path[choice]);
1382 			if (tmp) {
1383 				if (is_input_name_malloced)
1384 					free((void *)input_name);
1385 				input_name = tmp;
1386 				is_input_name_malloced = true;
1387 				ret = 0;
1388 			} else
1389 				ui__warning("Data switch failed due to memory shortage!\n");
1390 		}
1391 	}
1392 
1393 	free_popup_options(options, nr_options);
1394 	free_popup_options(abs_path, nr_options);
1395 	return ret;
1396 }
1397 
1398 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1399 {
1400 	u64 nr_entries = 0;
1401 	struct rb_node *nd = rb_first(&hb->hists->entries);
1402 
1403 	if (hb->min_pcnt == 0) {
1404 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1405 		return;
1406 	}
1407 
1408 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1409 		nr_entries++;
1410 		nd = rb_next(nd);
1411 	}
1412 
1413 	hb->nr_non_filtered_entries = nr_entries;
1414 }
1415 
1416 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1417 				    const char *helpline,
1418 				    bool left_exits,
1419 				    struct hist_browser_timer *hbt,
1420 				    float min_pcnt,
1421 				    struct perf_session_env *env)
1422 {
1423 	struct hists *hists = evsel__hists(evsel);
1424 	struct hist_browser *browser = hist_browser__new(hists);
1425 	struct branch_info *bi;
1426 	struct pstack *fstack;
1427 	char *options[16];
1428 	int nr_options = 0;
1429 	int key = -1;
1430 	char buf[64];
1431 	char script_opt[64];
1432 	int delay_secs = hbt ? hbt->refresh : 0;
1433 	struct perf_hpp_fmt *fmt;
1434 
1435 #define HIST_BROWSER_HELP_COMMON					\
1436 	"h/?/F1        Show this window\n"				\
1437 	"UP/DOWN/PGUP\n"						\
1438 	"PGDN/SPACE    Navigate\n"					\
1439 	"q/ESC/CTRL+C  Exit browser\n\n"				\
1440 	"For multiple event sessions:\n\n"				\
1441 	"TAB/UNTAB     Switch events\n\n"				\
1442 	"For symbolic views (--sort has sym):\n\n"			\
1443 	"->            Zoom into DSO/Threads & Annotate current symbol\n" \
1444 	"<-            Zoom out\n"					\
1445 	"a             Annotate current symbol\n"			\
1446 	"C             Collapse all callchains\n"			\
1447 	"d             Zoom into current DSO\n"				\
1448 	"E             Expand all callchains\n"				\
1449 	"F             Toggle percentage of filtered entries\n"		\
1450 	"H             Display column headers\n"			\
1451 
1452 	/* help messages are sorted by lexical order of the hotkey */
1453 	const char report_help[] = HIST_BROWSER_HELP_COMMON
1454 	"i             Show header information\n"
1455 	"P             Print histograms to perf.hist.N\n"
1456 	"r             Run available scripts\n"
1457 	"s             Switch to another data file in PWD\n"
1458 	"t             Zoom into current Thread\n"
1459 	"V             Verbose (DSO names in callchains, etc)\n"
1460 	"/             Filter symbol by name";
1461 	const char top_help[] = HIST_BROWSER_HELP_COMMON
1462 	"P             Print histograms to perf.hist.N\n"
1463 	"t             Zoom into current Thread\n"
1464 	"V             Verbose (DSO names in callchains, etc)\n"
1465 	"z             Toggle zeroing of samples\n"
1466 	"/             Filter symbol by name";
1467 
1468 	if (browser == NULL)
1469 		return -1;
1470 
1471 	if (min_pcnt) {
1472 		browser->min_pcnt = min_pcnt;
1473 		hist_browser__update_nr_entries(browser);
1474 	}
1475 
1476 	fstack = pstack__new(2);
1477 	if (fstack == NULL)
1478 		goto out;
1479 
1480 	ui_helpline__push(helpline);
1481 
1482 	memset(options, 0, sizeof(options));
1483 
1484 	perf_hpp__for_each_format(fmt)
1485 		perf_hpp__reset_width(fmt, hists);
1486 
1487 	if (symbol_conf.col_width_list_str)
1488 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1489 
1490 	while (1) {
1491 		struct thread *thread = NULL;
1492 		const struct dso *dso = NULL;
1493 		int choice = 0,
1494 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1495 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1496 		int scripts_comm = -2, scripts_symbol = -2,
1497 		    scripts_all = -2, switch_data = -2;
1498 
1499 		nr_options = 0;
1500 
1501 		key = hist_browser__run(browser, hbt);
1502 
1503 		if (browser->he_selection != NULL) {
1504 			thread = hist_browser__selected_thread(browser);
1505 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1506 		}
1507 		switch (key) {
1508 		case K_TAB:
1509 		case K_UNTAB:
1510 			if (nr_events == 1)
1511 				continue;
1512 			/*
1513 			 * Exit the browser, let hists__browser_tree
1514 			 * go to the next or previous
1515 			 */
1516 			goto out_free_stack;
1517 		case 'a':
1518 			if (!sort__has_sym) {
1519 				ui_browser__warning(&browser->b, delay_secs * 2,
1520 			"Annotation is only available for symbolic views, "
1521 			"include \"sym*\" in --sort to use it.");
1522 				continue;
1523 			}
1524 
1525 			if (browser->selection == NULL ||
1526 			    browser->selection->sym == NULL ||
1527 			    browser->selection->map->dso->annotate_warned)
1528 				continue;
1529 			goto do_annotate;
1530 		case 'P':
1531 			hist_browser__dump(browser);
1532 			continue;
1533 		case 'd':
1534 			goto zoom_dso;
1535 		case 'V':
1536 			browser->show_dso = !browser->show_dso;
1537 			continue;
1538 		case 't':
1539 			goto zoom_thread;
1540 		case '/':
1541 			if (ui_browser__input_window("Symbol to show",
1542 					"Please enter the name of symbol you want to see",
1543 					buf, "ENTER: OK, ESC: Cancel",
1544 					delay_secs * 2) == K_ENTER) {
1545 				hists->symbol_filter_str = *buf ? buf : NULL;
1546 				hists__filter_by_symbol(hists);
1547 				hist_browser__reset(browser);
1548 			}
1549 			continue;
1550 		case 'r':
1551 			if (is_report_browser(hbt))
1552 				goto do_scripts;
1553 			continue;
1554 		case 's':
1555 			if (is_report_browser(hbt))
1556 				goto do_data_switch;
1557 			continue;
1558 		case 'i':
1559 			/* env->arch is NULL for live-mode (i.e. perf top) */
1560 			if (env->arch)
1561 				tui__header_window(env);
1562 			continue;
1563 		case 'F':
1564 			symbol_conf.filter_relative ^= 1;
1565 			continue;
1566 		case 'z':
1567 			if (!is_report_browser(hbt)) {
1568 				struct perf_top *top = hbt->arg;
1569 
1570 				top->zero = !top->zero;
1571 			}
1572 			continue;
1573 		case K_F1:
1574 		case 'h':
1575 		case '?':
1576 			ui_browser__help_window(&browser->b,
1577 				is_report_browser(hbt) ? report_help : top_help);
1578 			continue;
1579 		case K_ENTER:
1580 		case K_RIGHT:
1581 			/* menu */
1582 			break;
1583 		case K_LEFT: {
1584 			const void *top;
1585 
1586 			if (pstack__empty(fstack)) {
1587 				/*
1588 				 * Go back to the perf_evsel_menu__run or other user
1589 				 */
1590 				if (left_exits)
1591 					goto out_free_stack;
1592 				continue;
1593 			}
1594 			top = pstack__pop(fstack);
1595 			if (top == &browser->hists->dso_filter)
1596 				goto zoom_out_dso;
1597 			if (top == &browser->hists->thread_filter)
1598 				goto zoom_out_thread;
1599 			continue;
1600 		}
1601 		case K_ESC:
1602 			if (!left_exits &&
1603 			    !ui_browser__dialog_yesno(&browser->b,
1604 					       "Do you really want to exit?"))
1605 				continue;
1606 			/* Fall thru */
1607 		case 'q':
1608 		case CTRL('c'):
1609 			goto out_free_stack;
1610 		default:
1611 			continue;
1612 		}
1613 
1614 		if (!sort__has_sym)
1615 			goto add_exit_option;
1616 
1617 		if (browser->selection == NULL)
1618 			goto skip_annotation;
1619 
1620 		if (sort__mode == SORT_MODE__BRANCH) {
1621 			bi = browser->he_selection->branch_info;
1622 
1623 			if (bi == NULL)
1624 				goto skip_annotation;
1625 
1626 			if (bi->from.sym != NULL &&
1627 			    !bi->from.map->dso->annotate_warned &&
1628 			    asprintf(&options[nr_options], "Annotate %s", bi->from.sym->name) > 0) {
1629 				annotate_f = nr_options++;
1630 			}
1631 
1632 			if (bi->to.sym != NULL &&
1633 			    !bi->to.map->dso->annotate_warned &&
1634 			    (bi->to.sym != bi->from.sym ||
1635 			     bi->to.map->dso != bi->from.map->dso) &&
1636 			    asprintf(&options[nr_options], "Annotate %s", bi->to.sym->name) > 0) {
1637 				annotate_t = nr_options++;
1638 			}
1639 		} else {
1640 			if (browser->selection->sym != NULL &&
1641 			    !browser->selection->map->dso->annotate_warned) {
1642 				struct annotation *notes;
1643 
1644 				notes = symbol__annotation(browser->selection->sym);
1645 
1646 				if (notes->src &&
1647 				    asprintf(&options[nr_options], "Annotate %s",
1648 						 browser->selection->sym->name) > 0) {
1649 					annotate = nr_options++;
1650 				}
1651 			}
1652 		}
1653 skip_annotation:
1654 		if (thread != NULL &&
1655 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1656 			     (browser->hists->thread_filter ? "out of" : "into"),
1657 			     (thread->comm_set ? thread__comm_str(thread) : ""),
1658 			     thread->tid) > 0)
1659 			zoom_thread = nr_options++;
1660 
1661 		if (dso != NULL &&
1662 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1663 			     (browser->hists->dso_filter ? "out of" : "into"),
1664 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1665 			zoom_dso = nr_options++;
1666 
1667 		if (browser->selection != NULL &&
1668 		    browser->selection->map != NULL &&
1669 		    asprintf(&options[nr_options], "Browse map details") > 0)
1670 			browse_map = nr_options++;
1671 
1672 		/* perf script support */
1673 		if (browser->he_selection) {
1674 			struct symbol *sym;
1675 
1676 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1677 				     thread__comm_str(browser->he_selection->thread)) > 0)
1678 				scripts_comm = nr_options++;
1679 
1680 			sym = browser->he_selection->ms.sym;
1681 			if (sym && sym->namelen &&
1682 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1683 						sym->name) > 0)
1684 				scripts_symbol = nr_options++;
1685 		}
1686 
1687 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1688 			scripts_all = nr_options++;
1689 
1690 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1691 				"Switch to another data file in PWD") > 0)
1692 			switch_data = nr_options++;
1693 add_exit_option:
1694 		options[nr_options++] = (char *)"Exit";
1695 retry_popup_menu:
1696 		choice = ui__popup_menu(nr_options, options);
1697 
1698 		if (choice == nr_options - 1)
1699 			break;
1700 
1701 		if (choice == -1) {
1702 			free_popup_options(options, nr_options - 1);
1703 			continue;
1704 		}
1705 
1706 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1707 			struct hist_entry *he;
1708 			struct annotation *notes;
1709 			struct map_symbol ms;
1710 			int err;
1711 do_annotate:
1712 			if (!objdump_path && perf_session_env__lookup_objdump(env))
1713 				continue;
1714 
1715 			he = hist_browser__selected_entry(browser);
1716 			if (he == NULL)
1717 				continue;
1718 
1719 			if (choice == annotate_f) {
1720 				ms.map = he->branch_info->from.map;
1721 				ms.sym = he->branch_info->from.sym;
1722 			} else if (choice == annotate_t) {
1723 				ms.map = he->branch_info->to.map;
1724 				ms.sym = he->branch_info->to.sym;
1725 			} else {
1726 				ms = *browser->selection;
1727 			}
1728 
1729 			notes = symbol__annotation(ms.sym);
1730 			if (!notes->src)
1731 				continue;
1732 
1733 			err = map_symbol__tui_annotate(&ms, evsel, hbt);
1734 			/*
1735 			 * offer option to annotate the other branch source or target
1736 			 * (if they exists) when returning from annotate
1737 			 */
1738 			if ((err == 'q' || err == CTRL('c'))
1739 			    && annotate_t != -2 && annotate_f != -2)
1740 				goto retry_popup_menu;
1741 
1742 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1743 			if (err)
1744 				ui_browser__handle_resize(&browser->b);
1745 
1746 		} else if (choice == browse_map)
1747 			map__browse(browser->selection->map);
1748 		else if (choice == zoom_dso) {
1749 zoom_dso:
1750 			if (browser->hists->dso_filter) {
1751 				pstack__remove(fstack, &browser->hists->dso_filter);
1752 zoom_out_dso:
1753 				ui_helpline__pop();
1754 				browser->hists->dso_filter = NULL;
1755 				perf_hpp__set_elide(HISTC_DSO, false);
1756 			} else {
1757 				if (dso == NULL)
1758 					continue;
1759 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1760 						   dso->kernel ? "the Kernel" : dso->short_name);
1761 				browser->hists->dso_filter = dso;
1762 				perf_hpp__set_elide(HISTC_DSO, true);
1763 				pstack__push(fstack, &browser->hists->dso_filter);
1764 			}
1765 			hists__filter_by_dso(hists);
1766 			hist_browser__reset(browser);
1767 		} else if (choice == zoom_thread) {
1768 zoom_thread:
1769 			if (browser->hists->thread_filter) {
1770 				pstack__remove(fstack, &browser->hists->thread_filter);
1771 zoom_out_thread:
1772 				ui_helpline__pop();
1773 				thread__zput(browser->hists->thread_filter);
1774 				perf_hpp__set_elide(HISTC_THREAD, false);
1775 			} else {
1776 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1777 						   thread->comm_set ? thread__comm_str(thread) : "",
1778 						   thread->tid);
1779 				browser->hists->thread_filter = thread__get(thread);
1780 				perf_hpp__set_elide(HISTC_THREAD, false);
1781 				pstack__push(fstack, &browser->hists->thread_filter);
1782 			}
1783 			hists__filter_by_thread(hists);
1784 			hist_browser__reset(browser);
1785 		}
1786 		/* perf scripts support */
1787 		else if (choice == scripts_all || choice == scripts_comm ||
1788 				choice == scripts_symbol) {
1789 do_scripts:
1790 			memset(script_opt, 0, 64);
1791 
1792 			if (choice == scripts_comm)
1793 				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1794 
1795 			if (choice == scripts_symbol)
1796 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1797 
1798 			script_browse(script_opt);
1799 		}
1800 		/* Switch to another data file */
1801 		else if (choice == switch_data) {
1802 do_data_switch:
1803 			if (!switch_data_file()) {
1804 				key = K_SWITCH_INPUT_DATA;
1805 				break;
1806 			} else
1807 				ui__warning("Won't switch the data files due to\n"
1808 					"no valid data file get selected!\n");
1809 		}
1810 	}
1811 out_free_stack:
1812 	pstack__delete(fstack);
1813 out:
1814 	hist_browser__delete(browser);
1815 	free_popup_options(options, nr_options - 1);
1816 	return key;
1817 }
1818 
1819 struct perf_evsel_menu {
1820 	struct ui_browser b;
1821 	struct perf_evsel *selection;
1822 	bool lost_events, lost_events_warned;
1823 	float min_pcnt;
1824 	struct perf_session_env *env;
1825 };
1826 
1827 static void perf_evsel_menu__write(struct ui_browser *browser,
1828 				   void *entry, int row)
1829 {
1830 	struct perf_evsel_menu *menu = container_of(browser,
1831 						    struct perf_evsel_menu, b);
1832 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1833 	struct hists *hists = evsel__hists(evsel);
1834 	bool current_entry = ui_browser__is_current_entry(browser, row);
1835 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1836 	const char *ev_name = perf_evsel__name(evsel);
1837 	char bf[256], unit;
1838 	const char *warn = " ";
1839 	size_t printed;
1840 
1841 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1842 						       HE_COLORSET_NORMAL);
1843 
1844 	if (perf_evsel__is_group_event(evsel)) {
1845 		struct perf_evsel *pos;
1846 
1847 		ev_name = perf_evsel__group_name(evsel);
1848 
1849 		for_each_group_member(pos, evsel) {
1850 			struct hists *pos_hists = evsel__hists(pos);
1851 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1852 		}
1853 	}
1854 
1855 	nr_events = convert_unit(nr_events, &unit);
1856 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1857 			   unit, unit == ' ' ? "" : " ", ev_name);
1858 	slsmg_printf("%s", bf);
1859 
1860 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
1861 	if (nr_events != 0) {
1862 		menu->lost_events = true;
1863 		if (!current_entry)
1864 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1865 		nr_events = convert_unit(nr_events, &unit);
1866 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1867 				     nr_events, unit, unit == ' ' ? "" : " ");
1868 		warn = bf;
1869 	}
1870 
1871 	slsmg_write_nstring(warn, browser->width - printed);
1872 
1873 	if (current_entry)
1874 		menu->selection = evsel;
1875 }
1876 
1877 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1878 				int nr_events, const char *help,
1879 				struct hist_browser_timer *hbt)
1880 {
1881 	struct perf_evlist *evlist = menu->b.priv;
1882 	struct perf_evsel *pos;
1883 	const char *title = "Available samples";
1884 	int delay_secs = hbt ? hbt->refresh : 0;
1885 	int key;
1886 
1887 	if (ui_browser__show(&menu->b, title,
1888 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1889 		return -1;
1890 
1891 	while (1) {
1892 		key = ui_browser__run(&menu->b, delay_secs);
1893 
1894 		switch (key) {
1895 		case K_TIMER:
1896 			hbt->timer(hbt->arg);
1897 
1898 			if (!menu->lost_events_warned && menu->lost_events) {
1899 				ui_browser__warn_lost_events(&menu->b);
1900 				menu->lost_events_warned = true;
1901 			}
1902 			continue;
1903 		case K_RIGHT:
1904 		case K_ENTER:
1905 			if (!menu->selection)
1906 				continue;
1907 			pos = menu->selection;
1908 browse_hists:
1909 			perf_evlist__set_selected(evlist, pos);
1910 			/*
1911 			 * Give the calling tool a chance to populate the non
1912 			 * default evsel resorted hists tree.
1913 			 */
1914 			if (hbt)
1915 				hbt->timer(hbt->arg);
1916 			key = perf_evsel__hists_browse(pos, nr_events, help,
1917 						       true, hbt,
1918 						       menu->min_pcnt,
1919 						       menu->env);
1920 			ui_browser__show_title(&menu->b, title);
1921 			switch (key) {
1922 			case K_TAB:
1923 				if (pos->node.next == &evlist->entries)
1924 					pos = perf_evlist__first(evlist);
1925 				else
1926 					pos = perf_evsel__next(pos);
1927 				goto browse_hists;
1928 			case K_UNTAB:
1929 				if (pos->node.prev == &evlist->entries)
1930 					pos = perf_evlist__last(evlist);
1931 				else
1932 					pos = perf_evsel__prev(pos);
1933 				goto browse_hists;
1934 			case K_ESC:
1935 				if (!ui_browser__dialog_yesno(&menu->b,
1936 						"Do you really want to exit?"))
1937 					continue;
1938 				/* Fall thru */
1939 			case K_SWITCH_INPUT_DATA:
1940 			case 'q':
1941 			case CTRL('c'):
1942 				goto out;
1943 			default:
1944 				continue;
1945 			}
1946 		case K_LEFT:
1947 			continue;
1948 		case K_ESC:
1949 			if (!ui_browser__dialog_yesno(&menu->b,
1950 					       "Do you really want to exit?"))
1951 				continue;
1952 			/* Fall thru */
1953 		case 'q':
1954 		case CTRL('c'):
1955 			goto out;
1956 		default:
1957 			continue;
1958 		}
1959 	}
1960 
1961 out:
1962 	ui_browser__hide(&menu->b);
1963 	return key;
1964 }
1965 
1966 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1967 				 void *entry)
1968 {
1969 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1970 
1971 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1972 		return true;
1973 
1974 	return false;
1975 }
1976 
1977 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1978 					   int nr_entries, const char *help,
1979 					   struct hist_browser_timer *hbt,
1980 					   float min_pcnt,
1981 					   struct perf_session_env *env)
1982 {
1983 	struct perf_evsel *pos;
1984 	struct perf_evsel_menu menu = {
1985 		.b = {
1986 			.entries    = &evlist->entries,
1987 			.refresh    = ui_browser__list_head_refresh,
1988 			.seek	    = ui_browser__list_head_seek,
1989 			.write	    = perf_evsel_menu__write,
1990 			.filter	    = filter_group_entries,
1991 			.nr_entries = nr_entries,
1992 			.priv	    = evlist,
1993 		},
1994 		.min_pcnt = min_pcnt,
1995 		.env = env,
1996 	};
1997 
1998 	ui_helpline__push("Press ESC to exit");
1999 
2000 	evlist__for_each(evlist, pos) {
2001 		const char *ev_name = perf_evsel__name(pos);
2002 		size_t line_len = strlen(ev_name) + 7;
2003 
2004 		if (menu.b.width < line_len)
2005 			menu.b.width = line_len;
2006 	}
2007 
2008 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2009 }
2010 
2011 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2012 				  struct hist_browser_timer *hbt,
2013 				  float min_pcnt,
2014 				  struct perf_session_env *env)
2015 {
2016 	int nr_entries = evlist->nr_entries;
2017 
2018 single_entry:
2019 	if (nr_entries == 1) {
2020 		struct perf_evsel *first = perf_evlist__first(evlist);
2021 
2022 		return perf_evsel__hists_browse(first, nr_entries, help,
2023 						false, hbt, min_pcnt,
2024 						env);
2025 	}
2026 
2027 	if (symbol_conf.event_group) {
2028 		struct perf_evsel *pos;
2029 
2030 		nr_entries = 0;
2031 		evlist__for_each(evlist, pos) {
2032 			if (perf_evsel__is_group_leader(pos))
2033 				nr_entries++;
2034 		}
2035 
2036 		if (nr_entries == 1)
2037 			goto single_entry;
2038 	}
2039 
2040 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2041 					       hbt, min_pcnt, env);
2042 }
2043