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