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