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