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