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