xref: /openbmc/linux/tools/perf/ui/browsers/hists.c (revision 297871cb)
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)2251 perf_evsel_browser__new(struct evsel *evsel,
2252 			struct hist_browser_timer *hbt,
2253 			struct perf_env *env)
2254 {
2255 	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2256 
2257 	if (browser) {
2258 		browser->hbt   = hbt;
2259 		browser->env   = env;
2260 		browser->title = hists_browser__scnprintf_title;
2261 	}
2262 	return browser;
2263 }
2264 
hist_browser__delete(struct hist_browser * browser)2265 void hist_browser__delete(struct hist_browser *browser)
2266 {
2267 	free(browser);
2268 }
2269 
hist_browser__selected_entry(struct hist_browser * browser)2270 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2271 {
2272 	return browser->he_selection;
2273 }
2274 
hist_browser__selected_thread(struct hist_browser * browser)2275 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2276 {
2277 	return browser->he_selection->thread;
2278 }
2279 
hist_browser__selected_res_sample(struct hist_browser * browser)2280 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2281 {
2282 	return browser->he_selection ? browser->he_selection->res_samples : NULL;
2283 }
2284 
2285 /* Check whether the browser is for 'top' or 'report' */
is_report_browser(void * timer)2286 static inline bool is_report_browser(void *timer)
2287 {
2288 	return timer == NULL;
2289 }
2290 
hists_browser__scnprintf_title(struct hist_browser * browser,char * bf,size_t size)2291 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2292 {
2293 	struct hist_browser_timer *hbt = browser->hbt;
2294 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2295 
2296 	if (!is_report_browser(hbt)) {
2297 		struct perf_top *top = hbt->arg;
2298 
2299 		printed += scnprintf(bf + printed, size - printed,
2300 				     " lost: %" PRIu64 "/%" PRIu64,
2301 				     top->lost, top->lost_total);
2302 
2303 		printed += scnprintf(bf + printed, size - printed,
2304 				     " drop: %" PRIu64 "/%" PRIu64,
2305 				     top->drop, top->drop_total);
2306 
2307 		if (top->zero)
2308 			printed += scnprintf(bf + printed, size - printed, " [z]");
2309 
2310 		perf_top__reset_sample_counters(top);
2311 	}
2312 
2313 
2314 	return printed;
2315 }
2316 
free_popup_options(char ** options,int n)2317 static inline void free_popup_options(char **options, int n)
2318 {
2319 	int i;
2320 
2321 	for (i = 0; i < n; ++i)
2322 		zfree(&options[i]);
2323 }
2324 
2325 /*
2326  * Only runtime switching of perf data file will make "input_name" point
2327  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2328  * whether we need to call free() for current "input_name" during the switch.
2329  */
2330 static bool is_input_name_malloced = false;
2331 
switch_data_file(void)2332 static int switch_data_file(void)
2333 {
2334 	char *pwd, *options[32], *abs_path[32], *tmp;
2335 	DIR *pwd_dir;
2336 	int nr_options = 0, choice = -1, ret = -1;
2337 	struct dirent *dent;
2338 
2339 	pwd = getenv("PWD");
2340 	if (!pwd)
2341 		return ret;
2342 
2343 	pwd_dir = opendir(pwd);
2344 	if (!pwd_dir)
2345 		return ret;
2346 
2347 	memset(options, 0, sizeof(options));
2348 	memset(abs_path, 0, sizeof(abs_path));
2349 
2350 	while ((dent = readdir(pwd_dir))) {
2351 		char path[PATH_MAX];
2352 		u64 magic;
2353 		char *name = dent->d_name;
2354 		FILE *file;
2355 
2356 		if (!(dent->d_type == DT_REG))
2357 			continue;
2358 
2359 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2360 
2361 		file = fopen(path, "r");
2362 		if (!file)
2363 			continue;
2364 
2365 		if (fread(&magic, 1, 8, file) < 8)
2366 			goto close_file_and_continue;
2367 
2368 		if (is_perf_magic(magic)) {
2369 			options[nr_options] = strdup(name);
2370 			if (!options[nr_options])
2371 				goto close_file_and_continue;
2372 
2373 			abs_path[nr_options] = strdup(path);
2374 			if (!abs_path[nr_options]) {
2375 				zfree(&options[nr_options]);
2376 				ui__warning("Can't search all data files due to memory shortage.\n");
2377 				fclose(file);
2378 				break;
2379 			}
2380 
2381 			nr_options++;
2382 		}
2383 
2384 close_file_and_continue:
2385 		fclose(file);
2386 		if (nr_options >= 32) {
2387 			ui__warning("Too many perf data files in PWD!\n"
2388 				    "Only the first 32 files will be listed.\n");
2389 			break;
2390 		}
2391 	}
2392 	closedir(pwd_dir);
2393 
2394 	if (nr_options) {
2395 		choice = ui__popup_menu(nr_options, options, NULL);
2396 		if (choice < nr_options && choice >= 0) {
2397 			tmp = strdup(abs_path[choice]);
2398 			if (tmp) {
2399 				if (is_input_name_malloced)
2400 					free((void *)input_name);
2401 				input_name = tmp;
2402 				is_input_name_malloced = true;
2403 				ret = 0;
2404 			} else
2405 				ui__warning("Data switch failed due to memory shortage!\n");
2406 		}
2407 	}
2408 
2409 	free_popup_options(options, nr_options);
2410 	free_popup_options(abs_path, nr_options);
2411 	return ret;
2412 }
2413 
2414 struct popup_action {
2415 	unsigned long		time;
2416 	struct thread 		*thread;
2417 	struct map_symbol 	ms;
2418 	int			socket;
2419 	struct evsel	*evsel;
2420 	enum rstype		rstype;
2421 
2422 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2423 };
2424 
2425 static int
do_annotate(struct hist_browser * browser,struct popup_action * act)2426 do_annotate(struct hist_browser *browser, struct popup_action *act)
2427 {
2428 	struct evsel *evsel;
2429 	struct annotation *notes;
2430 	struct hist_entry *he;
2431 	int err;
2432 
2433 	if (!annotate_opts.objdump_path &&
2434 	    perf_env__lookup_objdump(browser->env, &annotate_opts.objdump_path))
2435 		return 0;
2436 
2437 	notes = symbol__annotation(act->ms.sym);
2438 	if (!notes->src)
2439 		return 0;
2440 
2441 	if (browser->block_evsel)
2442 		evsel = browser->block_evsel;
2443 	else
2444 		evsel = hists_to_evsel(browser->hists);
2445 
2446 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2447 	he = hist_browser__selected_entry(browser);
2448 	/*
2449 	 * offer option to annotate the other branch source or target
2450 	 * (if they exists) when returning from annotate
2451 	 */
2452 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2453 		return 1;
2454 
2455 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2456 	if (err)
2457 		ui_browser__handle_resize(&browser->b);
2458 	return 0;
2459 }
2460 
symbol__new_unresolved(u64 addr,struct map * map)2461 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2462 {
2463 	struct annotated_source *src;
2464 	struct symbol *sym;
2465 	char name[64];
2466 
2467 	snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2468 
2469 	sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2470 	if (sym) {
2471 		src = symbol__hists(sym, 1);
2472 		if (!src) {
2473 			symbol__delete(sym);
2474 			return NULL;
2475 		}
2476 
2477 		dso__insert_symbol(map__dso(map), sym);
2478 	}
2479 
2480 	return sym;
2481 }
2482 
2483 static int
add_annotate_opt(struct hist_browser * browser __maybe_unused,struct popup_action * act,char ** optstr,struct map_symbol * ms,u64 addr)2484 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2485 		 struct popup_action *act, char **optstr,
2486 		 struct map_symbol *ms,
2487 		 u64 addr)
2488 {
2489 	struct dso *dso;
2490 
2491 	if (!ms->map || (dso = map__dso(ms->map)) == NULL || dso->annotate_warned)
2492 		return 0;
2493 
2494 	if (!ms->sym)
2495 		ms->sym = symbol__new_unresolved(addr, ms->map);
2496 
2497 	if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2498 		return 0;
2499 
2500 	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2501 		return 0;
2502 
2503 	act->ms = *ms;
2504 	act->fn = do_annotate;
2505 	return 1;
2506 }
2507 
2508 static int
do_zoom_thread(struct hist_browser * browser,struct popup_action * act)2509 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2510 {
2511 	struct thread *thread = act->thread;
2512 
2513 	if ((!hists__has(browser->hists, thread) &&
2514 	     !hists__has(browser->hists, comm)) || thread == NULL)
2515 		return 0;
2516 
2517 	if (browser->hists->thread_filter) {
2518 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2519 		perf_hpp__set_elide(HISTC_THREAD, false);
2520 		thread__zput(browser->hists->thread_filter);
2521 		ui_helpline__pop();
2522 	} else {
2523 		const char *comm_set_str =
2524 			thread__comm_set(thread) ? thread__comm_str(thread) : "";
2525 
2526 		if (hists__has(browser->hists, thread)) {
2527 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2528 					   comm_set_str, thread__tid(thread));
2529 		} else {
2530 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2531 					   comm_set_str);
2532 		}
2533 
2534 		browser->hists->thread_filter = thread__get(thread);
2535 		perf_hpp__set_elide(HISTC_THREAD, false);
2536 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2537 	}
2538 
2539 	hists__filter_by_thread(browser->hists);
2540 	hist_browser__reset(browser);
2541 	return 0;
2542 }
2543 
2544 static int
add_thread_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,struct thread * thread)2545 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2546 	       char **optstr, struct thread *thread)
2547 {
2548 	int ret;
2549 	const char *comm_set_str, *in_out;
2550 
2551 	if ((!hists__has(browser->hists, thread) &&
2552 	     !hists__has(browser->hists, comm)) || thread == NULL)
2553 		return 0;
2554 
2555 	in_out = browser->hists->thread_filter ? "out of" : "into";
2556 	comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : "";
2557 	if (hists__has(browser->hists, thread)) {
2558 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2559 			       in_out, comm_set_str, thread__tid(thread));
2560 	} else {
2561 		ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str);
2562 	}
2563 	if (ret < 0)
2564 		return 0;
2565 
2566 	act->thread = thread;
2567 	act->fn = do_zoom_thread;
2568 	return 1;
2569 }
2570 
hists_browser__zoom_map(struct hist_browser * browser,struct map * map)2571 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2572 {
2573 	if (!hists__has(browser->hists, dso) || map == NULL)
2574 		return 0;
2575 
2576 	if (browser->hists->dso_filter) {
2577 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2578 		perf_hpp__set_elide(HISTC_DSO, false);
2579 		browser->hists->dso_filter = NULL;
2580 		ui_helpline__pop();
2581 	} else {
2582 		struct dso *dso = map__dso(map);
2583 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2584 				   __map__is_kernel(map) ? "the Kernel" : dso->short_name);
2585 		browser->hists->dso_filter = dso;
2586 		perf_hpp__set_elide(HISTC_DSO, true);
2587 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2588 	}
2589 
2590 	hists__filter_by_dso(browser->hists);
2591 	hist_browser__reset(browser);
2592 	return 0;
2593 }
2594 
2595 static int
do_zoom_dso(struct hist_browser * browser,struct popup_action * act)2596 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2597 {
2598 	return hists_browser__zoom_map(browser, act->ms.map);
2599 }
2600 
2601 static int
add_dso_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,struct map * map)2602 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2603 	    char **optstr, struct map *map)
2604 {
2605 	if (!hists__has(browser->hists, dso) || map == NULL)
2606 		return 0;
2607 
2608 	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2609 		     browser->hists->dso_filter ? "out of" : "into",
2610 		     __map__is_kernel(map) ? "the Kernel" : map__dso(map)->short_name) < 0)
2611 		return 0;
2612 
2613 	act->ms.map = map;
2614 	act->fn = do_zoom_dso;
2615 	return 1;
2616 }
2617 
do_toggle_callchain(struct hist_browser * browser,struct popup_action * act __maybe_unused)2618 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2619 {
2620 	hist_browser__toggle_fold(browser);
2621 	return 0;
2622 }
2623 
add_callchain_toggle_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr)2624 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2625 {
2626 	char sym_name[512];
2627 
2628         if (!hist_browser__selection_has_children(browser))
2629                 return 0;
2630 
2631 	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2632 		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2633 		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2634 		return 0;
2635 
2636 	act->fn = do_toggle_callchain;
2637 	return 1;
2638 }
2639 
2640 static int
do_browse_map(struct hist_browser * browser __maybe_unused,struct popup_action * act)2641 do_browse_map(struct hist_browser *browser __maybe_unused,
2642 	      struct popup_action *act)
2643 {
2644 	map__browse(act->ms.map);
2645 	return 0;
2646 }
2647 
2648 static int
add_map_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,struct map * map)2649 add_map_opt(struct hist_browser *browser,
2650 	    struct popup_action *act, char **optstr, struct map *map)
2651 {
2652 	if (!hists__has(browser->hists, dso) || map == NULL)
2653 		return 0;
2654 
2655 	if (asprintf(optstr, "Browse map details") < 0)
2656 		return 0;
2657 
2658 	act->ms.map = map;
2659 	act->fn = do_browse_map;
2660 	return 1;
2661 }
2662 
2663 static int
do_run_script(struct hist_browser * browser __maybe_unused,struct popup_action * act)2664 do_run_script(struct hist_browser *browser __maybe_unused,
2665 	      struct popup_action *act)
2666 {
2667 	char *script_opt;
2668 	int len;
2669 	int n = 0;
2670 
2671 	len = 100;
2672 	if (act->thread)
2673 		len += strlen(thread__comm_str(act->thread));
2674 	else if (act->ms.sym)
2675 		len += strlen(act->ms.sym->name);
2676 	script_opt = malloc(len);
2677 	if (!script_opt)
2678 		return -1;
2679 
2680 	script_opt[0] = 0;
2681 	if (act->thread) {
2682 		n = scnprintf(script_opt, len, " -c %s ",
2683 			  thread__comm_str(act->thread));
2684 	} else if (act->ms.sym) {
2685 		n = scnprintf(script_opt, len, " -S %s ",
2686 			  act->ms.sym->name);
2687 	}
2688 
2689 	if (act->time) {
2690 		char start[32], end[32];
2691 		unsigned long starttime = act->time;
2692 		unsigned long endtime = act->time + symbol_conf.time_quantum;
2693 
2694 		if (starttime == endtime) { /* Display 1ms as fallback */
2695 			starttime -= 1*NSEC_PER_MSEC;
2696 			endtime += 1*NSEC_PER_MSEC;
2697 		}
2698 		timestamp__scnprintf_usec(starttime, start, sizeof start);
2699 		timestamp__scnprintf_usec(endtime, end, sizeof end);
2700 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2701 	}
2702 
2703 	script_browse(script_opt, act->evsel);
2704 	free(script_opt);
2705 	return 0;
2706 }
2707 
2708 static int
do_res_sample_script(struct hist_browser * browser __maybe_unused,struct popup_action * act)2709 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2710 		     struct popup_action *act)
2711 {
2712 	struct hist_entry *he;
2713 
2714 	he = hist_browser__selected_entry(browser);
2715 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2716 	return 0;
2717 }
2718 
2719 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)2720 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2721 	       struct popup_action *act, char **optstr,
2722 	       struct thread *thread, struct symbol *sym,
2723 	       struct evsel *evsel, const char *tstr)
2724 {
2725 
2726 	if (thread) {
2727 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2728 			     thread__comm_str(thread), tstr) < 0)
2729 			return 0;
2730 	} else if (sym) {
2731 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2732 			     sym->name, tstr) < 0)
2733 			return 0;
2734 	} else {
2735 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2736 			return 0;
2737 	}
2738 
2739 	act->thread = thread;
2740 	act->ms.sym = sym;
2741 	act->evsel = evsel;
2742 	act->fn = do_run_script;
2743 	return 1;
2744 }
2745 
2746 static int
add_script_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,struct thread * thread,struct symbol * sym,struct evsel * evsel)2747 add_script_opt(struct hist_browser *browser,
2748 	       struct popup_action *act, char **optstr,
2749 	       struct thread *thread, struct symbol *sym,
2750 	       struct evsel *evsel)
2751 {
2752 	int n, j;
2753 	struct hist_entry *he;
2754 
2755 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2756 
2757 	he = hist_browser__selected_entry(browser);
2758 	if (sort_order && strstr(sort_order, "time")) {
2759 		char tstr[128];
2760 
2761 		optstr++;
2762 		act++;
2763 		j = sprintf(tstr, " in ");
2764 		j += timestamp__scnprintf_usec(he->time, tstr + j,
2765 					       sizeof tstr - j);
2766 		j += sprintf(tstr + j, "-");
2767 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2768 				          tstr + j, sizeof tstr - j);
2769 		n += add_script_opt_2(browser, act, optstr, thread, sym,
2770 					  evsel, tstr);
2771 		act->time = he->time;
2772 	}
2773 	return n;
2774 }
2775 
2776 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)2777 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2778 		   struct popup_action *act, char **optstr,
2779 		   struct res_sample *res_sample,
2780 		   struct evsel *evsel,
2781 		   enum rstype type)
2782 {
2783 	if (!res_sample)
2784 		return 0;
2785 
2786 	if (asprintf(optstr, "Show context for individual samples %s",
2787 		type == A_ASM ? "with assembler" :
2788 		type == A_SOURCE ? "with source" : "") < 0)
2789 		return 0;
2790 
2791 	act->fn = do_res_sample_script;
2792 	act->evsel = evsel;
2793 	act->rstype = type;
2794 	return 1;
2795 }
2796 
2797 static int
do_switch_data(struct hist_browser * browser __maybe_unused,struct popup_action * act __maybe_unused)2798 do_switch_data(struct hist_browser *browser __maybe_unused,
2799 	       struct popup_action *act __maybe_unused)
2800 {
2801 	if (switch_data_file()) {
2802 		ui__warning("Won't switch the data files due to\n"
2803 			    "no valid data file get selected!\n");
2804 		return 0;
2805 	}
2806 
2807 	return K_SWITCH_INPUT_DATA;
2808 }
2809 
2810 static int
add_switch_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr)2811 add_switch_opt(struct hist_browser *browser,
2812 	       struct popup_action *act, char **optstr)
2813 {
2814 	if (!is_report_browser(browser->hbt))
2815 		return 0;
2816 
2817 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2818 		return 0;
2819 
2820 	act->fn = do_switch_data;
2821 	return 1;
2822 }
2823 
2824 static int
do_exit_browser(struct hist_browser * browser __maybe_unused,struct popup_action * act __maybe_unused)2825 do_exit_browser(struct hist_browser *browser __maybe_unused,
2826 		struct popup_action *act __maybe_unused)
2827 {
2828 	return 0;
2829 }
2830 
2831 static int
add_exit_opt(struct hist_browser * browser __maybe_unused,struct popup_action * act,char ** optstr)2832 add_exit_opt(struct hist_browser *browser __maybe_unused,
2833 	     struct popup_action *act, char **optstr)
2834 {
2835 	if (asprintf(optstr, "Exit") < 0)
2836 		return 0;
2837 
2838 	act->fn = do_exit_browser;
2839 	return 1;
2840 }
2841 
2842 static int
do_zoom_socket(struct hist_browser * browser,struct popup_action * act)2843 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2844 {
2845 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2846 		return 0;
2847 
2848 	if (browser->hists->socket_filter > -1) {
2849 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2850 		browser->hists->socket_filter = -1;
2851 		perf_hpp__set_elide(HISTC_SOCKET, false);
2852 	} else {
2853 		browser->hists->socket_filter = act->socket;
2854 		perf_hpp__set_elide(HISTC_SOCKET, true);
2855 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2856 	}
2857 
2858 	hists__filter_by_socket(browser->hists);
2859 	hist_browser__reset(browser);
2860 	return 0;
2861 }
2862 
2863 static int
add_socket_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,int socket_id)2864 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2865 	       char **optstr, int socket_id)
2866 {
2867 	if (!hists__has(browser->hists, socket) || socket_id < 0)
2868 		return 0;
2869 
2870 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2871 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2872 		     socket_id) < 0)
2873 		return 0;
2874 
2875 	act->socket = socket_id;
2876 	act->fn = do_zoom_socket;
2877 	return 1;
2878 }
2879 
hist_browser__update_nr_entries(struct hist_browser * hb)2880 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2881 {
2882 	u64 nr_entries = 0;
2883 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2884 
2885 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2886 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2887 		return;
2888 	}
2889 
2890 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2891 		nr_entries++;
2892 		nd = rb_hierarchy_next(nd);
2893 	}
2894 
2895 	hb->nr_non_filtered_entries = nr_entries;
2896 	hb->nr_hierarchy_entries = nr_entries;
2897 }
2898 
hist_browser__update_percent_limit(struct hist_browser * hb,double percent)2899 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2900 					       double percent)
2901 {
2902 	struct hist_entry *he;
2903 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2904 	u64 total = hists__total_period(hb->hists);
2905 	u64 min_callchain_hits = total * (percent / 100);
2906 
2907 	hb->min_pcnt = callchain_param.min_percent = percent;
2908 
2909 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2910 		he = rb_entry(nd, struct hist_entry, rb_node);
2911 
2912 		if (he->has_no_entry) {
2913 			he->has_no_entry = false;
2914 			he->nr_rows = 0;
2915 		}
2916 
2917 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2918 			goto next;
2919 
2920 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2921 			total = he->stat.period;
2922 
2923 			if (symbol_conf.cumulate_callchain)
2924 				total = he->stat_acc->period;
2925 
2926 			min_callchain_hits = total * (percent / 100);
2927 		}
2928 
2929 		callchain_param.sort(&he->sorted_chain, he->callchain,
2930 				     min_callchain_hits, &callchain_param);
2931 
2932 next:
2933 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2934 
2935 		/* force to re-evaluate folding state of callchains */
2936 		he->init_have_children = false;
2937 		hist_entry__set_folding(he, hb, false);
2938 	}
2939 }
2940 
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)2941 static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
2942 			       bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
2943 			       struct perf_env *env, bool warn_lost_event)
2944 {
2945 	struct hists *hists = evsel__hists(evsel);
2946 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2947 	struct branch_info *bi = NULL;
2948 #define MAX_OPTIONS  16
2949 	char *options[MAX_OPTIONS];
2950 	struct popup_action actions[MAX_OPTIONS];
2951 	int nr_options = 0;
2952 	int key = -1;
2953 	char buf[128];
2954 	int delay_secs = hbt ? hbt->refresh : 0;
2955 
2956 #define HIST_BROWSER_HELP_COMMON					\
2957 	"h/?/F1        Show this window\n"				\
2958 	"UP/DOWN/PGUP\n"						\
2959 	"PGDN/SPACE    Navigate\n"					\
2960 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2961 	"For multiple event sessions:\n\n"				\
2962 	"TAB/UNTAB     Switch events\n\n"				\
2963 	"For symbolic views (--sort has sym):\n\n"			\
2964 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2965 	"ESC           Zoom out\n"					\
2966 	"+             Expand/Collapse one callchain level\n"		\
2967 	"a             Annotate current symbol\n"			\
2968 	"C             Collapse all callchains\n"			\
2969 	"d             Zoom into current DSO\n"				\
2970 	"e             Expand/Collapse main entry callchains\n"	\
2971 	"E             Expand all callchains\n"				\
2972 	"F             Toggle percentage of filtered entries\n"		\
2973 	"H             Display column headers\n"			\
2974 	"k             Zoom into the kernel map\n"			\
2975 	"L             Change percent limit\n"				\
2976 	"m             Display context menu\n"				\
2977 	"S             Zoom into current Processor Socket\n"		\
2978 
2979 	/* help messages are sorted by lexical order of the hotkey */
2980 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
2981 	"i             Show header information\n"
2982 	"P             Print histograms to perf.hist.N\n"
2983 	"r             Run available scripts\n"
2984 	"s             Switch to another data file in PWD\n"
2985 	"t             Zoom into current Thread\n"
2986 	"V             Verbose (DSO names in callchains, etc)\n"
2987 	"/             Filter symbol by name\n"
2988 	"0-9           Sort by event n in group";
2989 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
2990 	"P             Print histograms to perf.hist.N\n"
2991 	"t             Zoom into current Thread\n"
2992 	"V             Verbose (DSO names in callchains, etc)\n"
2993 	"z             Toggle zeroing of samples\n"
2994 	"f             Enable/Disable events\n"
2995 	"/             Filter symbol by name";
2996 
2997 	if (browser == NULL)
2998 		return -1;
2999 
3000 	/* reset abort key so that it can get Ctrl-C as a key */
3001 	SLang_reset_tty();
3002 	SLang_init_tty(0, 0, 0);
3003 
3004 	if (min_pcnt)
3005 		browser->min_pcnt = min_pcnt;
3006 	hist_browser__update_nr_entries(browser);
3007 
3008 	browser->pstack = pstack__new(3);
3009 	if (browser->pstack == NULL)
3010 		goto out;
3011 
3012 	ui_helpline__push(helpline);
3013 
3014 	memset(options, 0, sizeof(options));
3015 	memset(actions, 0, sizeof(actions));
3016 
3017 	if (symbol_conf.col_width_list_str)
3018 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3019 
3020 	if (!is_report_browser(hbt))
3021 		browser->b.no_samples_msg = "Collecting samples...";
3022 
3023 	while (1) {
3024 		struct thread *thread = NULL;
3025 		struct map *map = NULL;
3026 		int choice;
3027 		int socked_id = -1;
3028 
3029 		key = 0; // reset key
3030 do_hotkey:		 // key came straight from options ui__popup_menu()
3031 		choice = nr_options = 0;
3032 		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3033 
3034 		if (browser->he_selection != NULL) {
3035 			thread = hist_browser__selected_thread(browser);
3036 			map = browser->selection->map;
3037 			socked_id = browser->he_selection->socket;
3038 		}
3039 		switch (key) {
3040 		case K_TAB:
3041 		case K_UNTAB:
3042 			if (nr_events == 1)
3043 				continue;
3044 			/*
3045 			 * Exit the browser, let hists__browser_tree
3046 			 * go to the next or previous
3047 			 */
3048 			goto out_free_stack;
3049 		case '0' ... '9':
3050 			if (!symbol_conf.event_group ||
3051 			    evsel->core.nr_members < 2) {
3052 				snprintf(buf, sizeof(buf),
3053 					 "Sort by index only available with group events!");
3054 				helpline = buf;
3055 				continue;
3056 			}
3057 
3058 			if (key - '0' == symbol_conf.group_sort_idx)
3059 				continue;
3060 
3061 			symbol_conf.group_sort_idx = key - '0';
3062 
3063 			if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3064 				snprintf(buf, sizeof(buf),
3065 					 "Max event group index to sort is %d (index from 0 to %d)",
3066 					 evsel->core.nr_members - 1,
3067 					 evsel->core.nr_members - 1);
3068 				helpline = buf;
3069 				continue;
3070 			}
3071 
3072 			key = K_RELOAD;
3073 			goto out_free_stack;
3074 		case 'a':
3075 			if (!hists__has(hists, sym)) {
3076 				ui_browser__warning(&browser->b, delay_secs * 2,
3077 			"Annotation is only available for symbolic views, "
3078 			"include \"sym*\" in --sort to use it.");
3079 				continue;
3080 			}
3081 
3082 			if (!browser->selection ||
3083 			    !browser->selection->map ||
3084 			    !map__dso(browser->selection->map) ||
3085 			    map__dso(browser->selection->map)->annotate_warned) {
3086 				continue;
3087 			}
3088 
3089 			if (!browser->selection->sym) {
3090 				if (!browser->he_selection)
3091 					continue;
3092 
3093 				if (sort__mode == SORT_MODE__BRANCH) {
3094 					bi = browser->he_selection->branch_info;
3095 					if (!bi || !bi->to.ms.map)
3096 						continue;
3097 
3098 					actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3099 					actions->ms.map = bi->to.ms.map;
3100 				} else {
3101 					actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3102 										 browser->selection->map);
3103 					actions->ms.map = browser->selection->map;
3104 				}
3105 
3106 				if (!actions->ms.sym)
3107 					continue;
3108 			} else {
3109 				if (symbol__annotation(browser->selection->sym)->src == NULL) {
3110 					ui_browser__warning(&browser->b, delay_secs * 2,
3111 						"No samples for the \"%s\" symbol.\n\n"
3112 						"Probably appeared just in a callchain",
3113 						browser->selection->sym->name);
3114 					continue;
3115 				}
3116 
3117 				actions->ms.map = browser->selection->map;
3118 				actions->ms.sym = browser->selection->sym;
3119 			}
3120 
3121 			do_annotate(browser, actions);
3122 			continue;
3123 		case 'P':
3124 			hist_browser__dump(browser);
3125 			continue;
3126 		case 'd':
3127 			actions->ms.map = map;
3128 			do_zoom_dso(browser, actions);
3129 			continue;
3130 		case 'k':
3131 			if (browser->selection != NULL)
3132 				hists_browser__zoom_map(browser,
3133 					      maps__machine(browser->selection->maps)->vmlinux_map);
3134 			continue;
3135 		case 'V':
3136 			verbose = (verbose + 1) % 4;
3137 			browser->show_dso = verbose > 0;
3138 			ui_helpline__fpush("Verbosity level set to %d\n",
3139 					   verbose);
3140 			continue;
3141 		case 't':
3142 			actions->thread = thread;
3143 			do_zoom_thread(browser, actions);
3144 			continue;
3145 		case 'S':
3146 			actions->socket = socked_id;
3147 			do_zoom_socket(browser, actions);
3148 			continue;
3149 		case '/':
3150 			if (ui_browser__input_window("Symbol to show",
3151 					"Please enter the name of symbol you want to see.\n"
3152 					"To remove the filter later, press / + ENTER.",
3153 					buf, "ENTER: OK, ESC: Cancel",
3154 					delay_secs * 2) == K_ENTER) {
3155 				hists->symbol_filter_str = *buf ? buf : NULL;
3156 				hists__filter_by_symbol(hists);
3157 				hist_browser__reset(browser);
3158 			}
3159 			continue;
3160 		case 'r':
3161 			if (is_report_browser(hbt)) {
3162 				actions->thread = NULL;
3163 				actions->ms.sym = NULL;
3164 				do_run_script(browser, actions);
3165 			}
3166 			continue;
3167 		case 's':
3168 			if (is_report_browser(hbt)) {
3169 				key = do_switch_data(browser, actions);
3170 				if (key == K_SWITCH_INPUT_DATA)
3171 					goto out_free_stack;
3172 			}
3173 			continue;
3174 		case 'i':
3175 			/* env->arch is NULL for live-mode (i.e. perf top) */
3176 			if (env->arch)
3177 				tui__header_window(env);
3178 			continue;
3179 		case 'F':
3180 			symbol_conf.filter_relative ^= 1;
3181 			continue;
3182 		case 'z':
3183 			if (!is_report_browser(hbt)) {
3184 				struct perf_top *top = hbt->arg;
3185 
3186 				top->zero = !top->zero;
3187 			}
3188 			continue;
3189 		case 'L':
3190 			if (ui_browser__input_window("Percent Limit",
3191 					"Please enter the value you want to hide entries under that percent.",
3192 					buf, "ENTER: OK, ESC: Cancel",
3193 					delay_secs * 2) == K_ENTER) {
3194 				char *end;
3195 				double new_percent = strtod(buf, &end);
3196 
3197 				if (new_percent < 0 || new_percent > 100) {
3198 					ui_browser__warning(&browser->b, delay_secs * 2,
3199 						"Invalid percent: %.2f", new_percent);
3200 					continue;
3201 				}
3202 
3203 				hist_browser__update_percent_limit(browser, new_percent);
3204 				hist_browser__reset(browser);
3205 			}
3206 			continue;
3207 		case K_F1:
3208 		case 'h':
3209 		case '?':
3210 			ui_browser__help_window(&browser->b,
3211 				is_report_browser(hbt) ? report_help : top_help);
3212 			continue;
3213 		case K_ENTER:
3214 		case K_RIGHT:
3215 		case 'm':
3216 			/* menu */
3217 			break;
3218 		case K_ESC:
3219 		case K_LEFT: {
3220 			const void *top;
3221 
3222 			if (pstack__empty(browser->pstack)) {
3223 				/*
3224 				 * Go back to the perf_evsel_menu__run or other user
3225 				 */
3226 				if (left_exits)
3227 					goto out_free_stack;
3228 
3229 				if (key == K_ESC &&
3230 				    ui_browser__dialog_yesno(&browser->b,
3231 							     "Do you really want to exit?"))
3232 					goto out_free_stack;
3233 
3234 				continue;
3235 			}
3236 			actions->ms.map = map;
3237 			top = pstack__peek(browser->pstack);
3238 			if (top == &browser->hists->dso_filter) {
3239 				/*
3240 				 * No need to set actions->dso here since
3241 				 * it's just to remove the current filter.
3242 				 * Ditto for thread below.
3243 				 */
3244 				do_zoom_dso(browser, actions);
3245 			} else if (top == &browser->hists->thread_filter) {
3246 				do_zoom_thread(browser, actions);
3247 			} else if (top == &browser->hists->socket_filter) {
3248 				do_zoom_socket(browser, actions);
3249 			}
3250 			continue;
3251 		}
3252 		case 'q':
3253 		case CTRL('c'):
3254 			goto out_free_stack;
3255 		case 'f':
3256 			if (!is_report_browser(hbt)) {
3257 				struct perf_top *top = hbt->arg;
3258 
3259 				evlist__toggle_enable(top->evlist);
3260 				/*
3261 				 * No need to refresh, resort/decay histogram
3262 				 * entries if we are not collecting samples:
3263 				 */
3264 				if (top->evlist->enabled) {
3265 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3266 					hbt->refresh = delay_secs;
3267 				} else {
3268 					helpline = "Press 'f' again to re-enable the events";
3269 					hbt->refresh = 0;
3270 				}
3271 				continue;
3272 			}
3273 			/* Fall thru */
3274 		default:
3275 			helpline = "Press '?' for help on key bindings";
3276 			continue;
3277 		}
3278 
3279 		if (!hists__has(hists, sym) || browser->selection == NULL)
3280 			goto skip_annotation;
3281 
3282 		if (sort__mode == SORT_MODE__BRANCH) {
3283 
3284 			if (browser->he_selection)
3285 				bi = browser->he_selection->branch_info;
3286 
3287 			if (bi == NULL)
3288 				goto skip_annotation;
3289 
3290 			nr_options += add_annotate_opt(browser,
3291 						       &actions[nr_options],
3292 						       &options[nr_options],
3293 						       &bi->from.ms,
3294 						       bi->from.al_addr);
3295 			if (bi->to.ms.sym != bi->from.ms.sym)
3296 				nr_options += add_annotate_opt(browser,
3297 							&actions[nr_options],
3298 							&options[nr_options],
3299 							&bi->to.ms,
3300 							bi->to.al_addr);
3301 		} else {
3302 			nr_options += add_annotate_opt(browser,
3303 						       &actions[nr_options],
3304 						       &options[nr_options],
3305 						       browser->selection,
3306 						       browser->he_selection->ip);
3307 		}
3308 skip_annotation:
3309 		nr_options += add_thread_opt(browser, &actions[nr_options],
3310 					     &options[nr_options], thread);
3311 		nr_options += add_dso_opt(browser, &actions[nr_options],
3312 					  &options[nr_options], map);
3313 		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3314 		nr_options += add_map_opt(browser, &actions[nr_options],
3315 					  &options[nr_options],
3316 					  browser->selection ?
3317 						browser->selection->map : NULL);
3318 		nr_options += add_socket_opt(browser, &actions[nr_options],
3319 					     &options[nr_options],
3320 					     socked_id);
3321 		/* perf script support */
3322 		if (!is_report_browser(hbt))
3323 			goto skip_scripting;
3324 
3325 		if (browser->he_selection) {
3326 			if (hists__has(hists, thread) && thread) {
3327 				nr_options += add_script_opt(browser,
3328 							     &actions[nr_options],
3329 							     &options[nr_options],
3330 							     thread, NULL, evsel);
3331 			}
3332 			/*
3333 			 * Note that browser->selection != NULL
3334 			 * when browser->he_selection is not NULL,
3335 			 * so we don't need to check browser->selection
3336 			 * before fetching browser->selection->sym like what
3337 			 * we do before fetching browser->selection->map.
3338 			 *
3339 			 * See hist_browser__show_entry.
3340 			 */
3341 			if (hists__has(hists, sym) && browser->selection->sym) {
3342 				nr_options += add_script_opt(browser,
3343 							     &actions[nr_options],
3344 							     &options[nr_options],
3345 							     NULL, browser->selection->sym,
3346 							     evsel);
3347 			}
3348 		}
3349 		nr_options += add_script_opt(browser, &actions[nr_options],
3350 					     &options[nr_options], NULL, NULL, evsel);
3351 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3352 						 &options[nr_options],
3353 						 hist_browser__selected_res_sample(browser),
3354 						 evsel, A_NORMAL);
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_ASM);
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_SOURCE);
3363 		nr_options += add_switch_opt(browser, &actions[nr_options],
3364 					     &options[nr_options]);
3365 skip_scripting:
3366 		nr_options += add_exit_opt(browser, &actions[nr_options],
3367 					   &options[nr_options]);
3368 
3369 		do {
3370 			struct popup_action *act;
3371 
3372 			choice = ui__popup_menu(nr_options, options, &key);
3373 			if (choice == -1)
3374 				break;
3375 
3376 			if (choice == nr_options)
3377 				goto do_hotkey;
3378 
3379 			act = &actions[choice];
3380 			key = act->fn(browser, act);
3381 		} while (key == 1);
3382 
3383 		if (key == K_SWITCH_INPUT_DATA)
3384 			break;
3385 	}
3386 out_free_stack:
3387 	pstack__delete(browser->pstack);
3388 out:
3389 	hist_browser__delete(browser);
3390 	free_popup_options(options, MAX_OPTIONS);
3391 	return key;
3392 }
3393 
3394 struct evsel_menu {
3395 	struct ui_browser b;
3396 	struct evsel *selection;
3397 	bool lost_events, lost_events_warned;
3398 	float min_pcnt;
3399 	struct perf_env *env;
3400 };
3401 
perf_evsel_menu__write(struct ui_browser * browser,void * entry,int row)3402 static void perf_evsel_menu__write(struct ui_browser *browser,
3403 				   void *entry, int row)
3404 {
3405 	struct evsel_menu *menu = container_of(browser,
3406 						    struct evsel_menu, b);
3407 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3408 	struct hists *hists = evsel__hists(evsel);
3409 	bool current_entry = ui_browser__is_current_entry(browser, row);
3410 	unsigned long nr_events = hists->stats.nr_samples;
3411 	const char *ev_name = evsel__name(evsel);
3412 	char bf[256], unit;
3413 	const char *warn = " ";
3414 	size_t printed;
3415 
3416 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3417 						       HE_COLORSET_NORMAL);
3418 
3419 	if (evsel__is_group_event(evsel)) {
3420 		struct evsel *pos;
3421 
3422 		ev_name = evsel__group_name(evsel);
3423 
3424 		for_each_group_member(pos, evsel) {
3425 			struct hists *pos_hists = evsel__hists(pos);
3426 			nr_events += pos_hists->stats.nr_samples;
3427 		}
3428 	}
3429 
3430 	nr_events = convert_unit(nr_events, &unit);
3431 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3432 			   unit, unit == ' ' ? "" : " ", ev_name);
3433 	ui_browser__printf(browser, "%s", bf);
3434 
3435 	nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3436 	if (nr_events != 0) {
3437 		menu->lost_events = true;
3438 		if (!current_entry)
3439 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3440 		nr_events = convert_unit(nr_events, &unit);
3441 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3442 				     nr_events, unit, unit == ' ' ? "" : " ");
3443 		warn = bf;
3444 	}
3445 
3446 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3447 
3448 	if (current_entry)
3449 		menu->selection = evsel;
3450 }
3451 
perf_evsel_menu__run(struct evsel_menu * menu,int nr_events,const char * help,struct hist_browser_timer * hbt,bool warn_lost_event)3452 static int perf_evsel_menu__run(struct evsel_menu *menu,
3453 				int nr_events, const char *help,
3454 				struct hist_browser_timer *hbt,
3455 				bool warn_lost_event)
3456 {
3457 	struct evlist *evlist = menu->b.priv;
3458 	struct evsel *pos;
3459 	const char *title = "Available samples";
3460 	int delay_secs = hbt ? hbt->refresh : 0;
3461 	int key;
3462 
3463 	if (ui_browser__show(&menu->b, title,
3464 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3465 		return -1;
3466 
3467 	while (1) {
3468 		key = ui_browser__run(&menu->b, delay_secs);
3469 
3470 		switch (key) {
3471 		case K_TIMER:
3472 			if (hbt)
3473 				hbt->timer(hbt->arg);
3474 
3475 			if (!menu->lost_events_warned &&
3476 			    menu->lost_events &&
3477 			    warn_lost_event) {
3478 				ui_browser__warn_lost_events(&menu->b);
3479 				menu->lost_events_warned = true;
3480 			}
3481 			continue;
3482 		case K_RIGHT:
3483 		case K_ENTER:
3484 			if (!menu->selection)
3485 				continue;
3486 			pos = menu->selection;
3487 browse_hists:
3488 			evlist__set_selected(evlist, pos);
3489 			/*
3490 			 * Give the calling tool a chance to populate the non
3491 			 * default evsel resorted hists tree.
3492 			 */
3493 			if (hbt)
3494 				hbt->timer(hbt->arg);
3495 			key = evsel__hists_browse(pos, nr_events, help, true, hbt,
3496 						  menu->min_pcnt, menu->env,
3497 						  warn_lost_event);
3498 			ui_browser__show_title(&menu->b, title);
3499 			switch (key) {
3500 			case K_TAB:
3501 				if (pos->core.node.next == &evlist->core.entries)
3502 					pos = evlist__first(evlist);
3503 				else
3504 					pos = evsel__next(pos);
3505 				goto browse_hists;
3506 			case K_UNTAB:
3507 				if (pos->core.node.prev == &evlist->core.entries)
3508 					pos = evlist__last(evlist);
3509 				else
3510 					pos = evsel__prev(pos);
3511 				goto browse_hists;
3512 			case K_SWITCH_INPUT_DATA:
3513 			case K_RELOAD:
3514 			case 'q':
3515 			case CTRL('c'):
3516 				goto out;
3517 			case K_ESC:
3518 			default:
3519 				continue;
3520 			}
3521 		case K_LEFT:
3522 			continue;
3523 		case K_ESC:
3524 			if (!ui_browser__dialog_yesno(&menu->b,
3525 					       "Do you really want to exit?"))
3526 				continue;
3527 			/* Fall thru */
3528 		case 'q':
3529 		case CTRL('c'):
3530 			goto out;
3531 		default:
3532 			continue;
3533 		}
3534 	}
3535 
3536 out:
3537 	ui_browser__hide(&menu->b);
3538 	return key;
3539 }
3540 
filter_group_entries(struct ui_browser * browser __maybe_unused,void * entry)3541 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3542 				 void *entry)
3543 {
3544 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3545 
3546 	if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3547 		return true;
3548 
3549 	return false;
3550 }
3551 
__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)3552 static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3553 				      struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3554 				      bool warn_lost_event)
3555 {
3556 	struct evsel *pos;
3557 	struct evsel_menu menu = {
3558 		.b = {
3559 			.entries    = &evlist->core.entries,
3560 			.refresh    = ui_browser__list_head_refresh,
3561 			.seek	    = ui_browser__list_head_seek,
3562 			.write	    = perf_evsel_menu__write,
3563 			.filter	    = filter_group_entries,
3564 			.nr_entries = nr_entries,
3565 			.priv	    = evlist,
3566 		},
3567 		.min_pcnt = min_pcnt,
3568 		.env = env,
3569 	};
3570 
3571 	ui_helpline__push("Press ESC to exit");
3572 
3573 	evlist__for_each_entry(evlist, pos) {
3574 		const char *ev_name = evsel__name(pos);
3575 		size_t line_len = strlen(ev_name) + 7;
3576 
3577 		if (menu.b.width < line_len)
3578 			menu.b.width = line_len;
3579 	}
3580 
3581 	return perf_evsel_menu__run(&menu, nr_entries, help,
3582 				    hbt, warn_lost_event);
3583 }
3584 
evlist__single_entry(struct evlist * evlist)3585 static bool evlist__single_entry(struct evlist *evlist)
3586 {
3587 	int nr_entries = evlist->core.nr_entries;
3588 
3589 	if (nr_entries == 1)
3590 	       return true;
3591 
3592 	if (nr_entries == 2) {
3593 		struct evsel *last = evlist__last(evlist);
3594 
3595 		if (evsel__is_dummy_event(last))
3596 			return true;
3597 	}
3598 
3599 	return false;
3600 }
3601 
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)3602 int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3603 			     float min_pcnt, struct perf_env *env, bool warn_lost_event)
3604 {
3605 	int nr_entries = evlist->core.nr_entries;
3606 
3607 	if (evlist__single_entry(evlist)) {
3608 single_entry: {
3609 		struct evsel *first = evlist__first(evlist);
3610 
3611 		return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
3612 					   env, warn_lost_event);
3613 	}
3614 	}
3615 
3616 	if (symbol_conf.event_group) {
3617 		struct evsel *pos;
3618 
3619 		nr_entries = 0;
3620 		evlist__for_each_entry(evlist, pos) {
3621 			if (evsel__is_group_leader(pos))
3622 				nr_entries++;
3623 		}
3624 
3625 		if (nr_entries == 1)
3626 			goto single_entry;
3627 	}
3628 
3629 	return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3630 					  warn_lost_event);
3631 }
3632 
block_hists_browser__title(struct hist_browser * browser,char * bf,size_t size)3633 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3634 				      size_t size)
3635 {
3636 	struct hists *hists = evsel__hists(browser->block_evsel);
3637 	const char *evname = evsel__name(browser->block_evsel);
3638 	unsigned long nr_samples = hists->stats.nr_samples;
3639 	int ret;
3640 
3641 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3642 	if (evname)
3643 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3644 
3645 	return 0;
3646 }
3647 
block_hists_tui_browse(struct block_hist * bh,struct evsel * evsel,float min_percent,struct perf_env * env)3648 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3649 			   float min_percent, struct perf_env *env)
3650 {
3651 	struct hists *hists = &bh->block_hists;
3652 	struct hist_browser *browser;
3653 	int key = -1;
3654 	struct popup_action action;
3655 	static const char help[] =
3656 	" q             Quit \n";
3657 
3658 	browser = hist_browser__new(hists);
3659 	if (!browser)
3660 		return -1;
3661 
3662 	browser->block_evsel = evsel;
3663 	browser->title = block_hists_browser__title;
3664 	browser->min_pcnt = min_percent;
3665 	browser->env = env;
3666 
3667 	/* reset abort key so that it can get Ctrl-C as a key */
3668 	SLang_reset_tty();
3669 	SLang_init_tty(0, 0, 0);
3670 
3671 	memset(&action, 0, sizeof(action));
3672 
3673 	while (1) {
3674 		key = hist_browser__run(browser, "? - help", true, 0);
3675 
3676 		switch (key) {
3677 		case 'q':
3678 			goto out;
3679 		case '?':
3680 			ui_browser__help_window(&browser->b, help);
3681 			break;
3682 		case 'a':
3683 		case K_ENTER:
3684 			if (!browser->selection ||
3685 			    !browser->selection->sym) {
3686 				continue;
3687 			}
3688 
3689 			action.ms.map = browser->selection->map;
3690 			action.ms.sym = browser->selection->sym;
3691 			do_annotate(browser, &action);
3692 			continue;
3693 		default:
3694 			break;
3695 		}
3696 	}
3697 
3698 out:
3699 	hist_browser__delete(browser);
3700 	return 0;
3701 }
3702