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