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