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