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