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