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