• 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(&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 
2227 /* Check whether the browser is for 'top' or 'report' */
is_report_browser(void * timer)2228 static inline bool is_report_browser(void *timer)
2229 {
2230 	return timer == NULL;
2231 }
2232 
hists_browser__scnprintf_title(struct hist_browser * browser,char * bf,size_t size)2233 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2234 {
2235 	struct hist_browser_timer *hbt = browser->hbt;
2236 	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2237 
2238 	if (!is_report_browser(hbt)) {
2239 		struct perf_top *top = hbt->arg;
2240 
2241 		printed += scnprintf(bf + printed, size - printed,
2242 				     " lost: %" PRIu64 "/%" PRIu64,
2243 				     top->lost, top->lost_total);
2244 
2245 		printed += scnprintf(bf + printed, size - printed,
2246 				     " drop: %" PRIu64 "/%" PRIu64,
2247 				     top->drop, top->drop_total);
2248 
2249 		if (top->zero)
2250 			printed += scnprintf(bf + printed, size - printed, " [z]");
2251 
2252 		perf_top__reset_sample_counters(top);
2253 	}
2254 
2255 
2256 	return printed;
2257 }
2258 
free_popup_options(char ** options,int n)2259 static inline void free_popup_options(char **options, int n)
2260 {
2261 	int i;
2262 
2263 	for (i = 0; i < n; ++i)
2264 		zfree(&options[i]);
2265 }
2266 
2267 /*
2268  * Only runtime switching of perf data file will make "input_name" point
2269  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2270  * whether we need to call free() for current "input_name" during the switch.
2271  */
2272 static bool is_input_name_malloced = false;
2273 
switch_data_file(void)2274 static int switch_data_file(void)
2275 {
2276 	char *pwd, *options[32], *abs_path[32], *tmp;
2277 	DIR *pwd_dir;
2278 	int nr_options = 0, choice = -1, ret = -1;
2279 	struct dirent *dent;
2280 
2281 	pwd = getenv("PWD");
2282 	if (!pwd)
2283 		return ret;
2284 
2285 	pwd_dir = opendir(pwd);
2286 	if (!pwd_dir)
2287 		return ret;
2288 
2289 	memset(options, 0, sizeof(options));
2290 	memset(abs_path, 0, sizeof(abs_path));
2291 
2292 	while ((dent = readdir(pwd_dir))) {
2293 		char path[PATH_MAX];
2294 		u64 magic;
2295 		char *name = dent->d_name;
2296 		FILE *file;
2297 
2298 		if (!(dent->d_type == DT_REG))
2299 			continue;
2300 
2301 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2302 
2303 		file = fopen(path, "r");
2304 		if (!file)
2305 			continue;
2306 
2307 		if (fread(&magic, 1, 8, file) < 8)
2308 			goto close_file_and_continue;
2309 
2310 		if (is_perf_magic(magic)) {
2311 			options[nr_options] = strdup(name);
2312 			if (!options[nr_options])
2313 				goto close_file_and_continue;
2314 
2315 			abs_path[nr_options] = strdup(path);
2316 			if (!abs_path[nr_options]) {
2317 				zfree(&options[nr_options]);
2318 				ui__warning("Can't search all data files due to memory shortage.\n");
2319 				fclose(file);
2320 				break;
2321 			}
2322 
2323 			nr_options++;
2324 		}
2325 
2326 close_file_and_continue:
2327 		fclose(file);
2328 		if (nr_options >= 32) {
2329 			ui__warning("Too many perf data files in PWD!\n"
2330 				    "Only the first 32 files will be listed.\n");
2331 			break;
2332 		}
2333 	}
2334 	closedir(pwd_dir);
2335 
2336 	if (nr_options) {
2337 		choice = ui__popup_menu(nr_options, options);
2338 		if (choice < nr_options && choice >= 0) {
2339 			tmp = strdup(abs_path[choice]);
2340 			if (tmp) {
2341 				if (is_input_name_malloced)
2342 					free((void *)input_name);
2343 				input_name = tmp;
2344 				is_input_name_malloced = true;
2345 				ret = 0;
2346 			} else
2347 				ui__warning("Data switch failed due to memory shortage!\n");
2348 		}
2349 	}
2350 
2351 	free_popup_options(options, nr_options);
2352 	free_popup_options(abs_path, nr_options);
2353 	return ret;
2354 }
2355 
2356 struct popup_action {
2357 	unsigned long		time;
2358 	struct thread 		*thread;
2359 	struct map_symbol 	ms;
2360 	int			socket;
2361 	struct evsel	*evsel;
2362 	enum rstype		rstype;
2363 
2364 	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2365 };
2366 
2367 static int
do_annotate(struct hist_browser * browser,struct popup_action * act)2368 do_annotate(struct hist_browser *browser, struct popup_action *act)
2369 {
2370 	struct evsel *evsel;
2371 	struct annotation *notes;
2372 	struct hist_entry *he;
2373 	int err;
2374 
2375 	if (!browser->annotation_opts->objdump_path &&
2376 	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2377 		return 0;
2378 
2379 	notes = symbol__annotation(act->ms.sym);
2380 	if (!notes->src)
2381 		return 0;
2382 
2383 	evsel = hists_to_evsel(browser->hists);
2384 	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2385 				       browser->annotation_opts);
2386 	he = hist_browser__selected_entry(browser);
2387 	/*
2388 	 * offer option to annotate the other branch source or target
2389 	 * (if they exists) when returning from annotate
2390 	 */
2391 	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2392 		return 1;
2393 
2394 	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2395 	if (err)
2396 		ui_browser__handle_resize(&browser->b);
2397 	return 0;
2398 }
2399 
2400 static int
add_annotate_opt(struct hist_browser * browser __maybe_unused,struct popup_action * act,char ** optstr,struct map * map,struct symbol * sym)2401 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2402 		 struct popup_action *act, char **optstr,
2403 		 struct map *map, struct symbol *sym)
2404 {
2405 	if (sym == NULL || map->dso->annotate_warned)
2406 		return 0;
2407 
2408 	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2409 		return 0;
2410 
2411 	act->ms.map = map;
2412 	act->ms.sym = sym;
2413 	act->fn = do_annotate;
2414 	return 1;
2415 }
2416 
2417 static int
do_zoom_thread(struct hist_browser * browser,struct popup_action * act)2418 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2419 {
2420 	struct thread *thread = act->thread;
2421 
2422 	if ((!hists__has(browser->hists, thread) &&
2423 	     !hists__has(browser->hists, comm)) || thread == NULL)
2424 		return 0;
2425 
2426 	if (browser->hists->thread_filter) {
2427 		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2428 		perf_hpp__set_elide(HISTC_THREAD, false);
2429 		thread__zput(browser->hists->thread_filter);
2430 		ui_helpline__pop();
2431 	} else {
2432 		if (hists__has(browser->hists, thread)) {
2433 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2434 					   thread->comm_set ? thread__comm_str(thread) : "",
2435 					   thread->tid);
2436 		} else {
2437 			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2438 					   thread->comm_set ? thread__comm_str(thread) : "");
2439 		}
2440 
2441 		browser->hists->thread_filter = thread__get(thread);
2442 		perf_hpp__set_elide(HISTC_THREAD, false);
2443 		pstack__push(browser->pstack, &browser->hists->thread_filter);
2444 	}
2445 
2446 	hists__filter_by_thread(browser->hists);
2447 	hist_browser__reset(browser);
2448 	return 0;
2449 }
2450 
2451 static int
add_thread_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,struct thread * thread)2452 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2453 	       char **optstr, struct thread *thread)
2454 {
2455 	int ret;
2456 
2457 	if ((!hists__has(browser->hists, thread) &&
2458 	     !hists__has(browser->hists, comm)) || thread == NULL)
2459 		return 0;
2460 
2461 	if (hists__has(browser->hists, thread)) {
2462 		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2463 			       browser->hists->thread_filter ? "out of" : "into",
2464 			       thread->comm_set ? thread__comm_str(thread) : "",
2465 			       thread->tid);
2466 	} else {
2467 		ret = asprintf(optstr, "Zoom %s %s thread",
2468 			       browser->hists->thread_filter ? "out of" : "into",
2469 			       thread->comm_set ? thread__comm_str(thread) : "");
2470 	}
2471 	if (ret < 0)
2472 		return 0;
2473 
2474 	act->thread = thread;
2475 	act->fn = do_zoom_thread;
2476 	return 1;
2477 }
2478 
2479 static int
do_zoom_dso(struct hist_browser * browser,struct popup_action * act)2480 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2481 {
2482 	struct map *map = act->ms.map;
2483 
2484 	if (!hists__has(browser->hists, dso) || map == NULL)
2485 		return 0;
2486 
2487 	if (browser->hists->dso_filter) {
2488 		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2489 		perf_hpp__set_elide(HISTC_DSO, false);
2490 		browser->hists->dso_filter = NULL;
2491 		ui_helpline__pop();
2492 	} else {
2493 		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2494 				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2495 		browser->hists->dso_filter = map->dso;
2496 		perf_hpp__set_elide(HISTC_DSO, true);
2497 		pstack__push(browser->pstack, &browser->hists->dso_filter);
2498 	}
2499 
2500 	hists__filter_by_dso(browser->hists);
2501 	hist_browser__reset(browser);
2502 	return 0;
2503 }
2504 
2505 static int
add_dso_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,struct map * map)2506 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2507 	    char **optstr, struct map *map)
2508 {
2509 	if (!hists__has(browser->hists, dso) || map == NULL)
2510 		return 0;
2511 
2512 	if (asprintf(optstr, "Zoom %s %s DSO",
2513 		     browser->hists->dso_filter ? "out of" : "into",
2514 		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2515 		return 0;
2516 
2517 	act->ms.map = map;
2518 	act->fn = do_zoom_dso;
2519 	return 1;
2520 }
2521 
2522 static int
do_browse_map(struct hist_browser * browser __maybe_unused,struct popup_action * act)2523 do_browse_map(struct hist_browser *browser __maybe_unused,
2524 	      struct popup_action *act)
2525 {
2526 	map__browse(act->ms.map);
2527 	return 0;
2528 }
2529 
2530 static int
add_map_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,struct map * map)2531 add_map_opt(struct hist_browser *browser,
2532 	    struct popup_action *act, char **optstr, struct map *map)
2533 {
2534 	if (!hists__has(browser->hists, dso) || map == NULL)
2535 		return 0;
2536 
2537 	if (asprintf(optstr, "Browse map details") < 0)
2538 		return 0;
2539 
2540 	act->ms.map = map;
2541 	act->fn = do_browse_map;
2542 	return 1;
2543 }
2544 
2545 static int
do_run_script(struct hist_browser * browser __maybe_unused,struct popup_action * act)2546 do_run_script(struct hist_browser *browser __maybe_unused,
2547 	      struct popup_action *act)
2548 {
2549 	char *script_opt;
2550 	int len;
2551 	int n = 0;
2552 
2553 	len = 100;
2554 	if (act->thread)
2555 		len += strlen(thread__comm_str(act->thread));
2556 	else if (act->ms.sym)
2557 		len += strlen(act->ms.sym->name);
2558 	script_opt = malloc(len);
2559 	if (!script_opt)
2560 		return -1;
2561 
2562 	script_opt[0] = 0;
2563 	if (act->thread) {
2564 		n = scnprintf(script_opt, len, " -c %s ",
2565 			  thread__comm_str(act->thread));
2566 	} else if (act->ms.sym) {
2567 		n = scnprintf(script_opt, len, " -S %s ",
2568 			  act->ms.sym->name);
2569 	}
2570 
2571 	if (act->time) {
2572 		char start[32], end[32];
2573 		unsigned long starttime = act->time;
2574 		unsigned long endtime = act->time + symbol_conf.time_quantum;
2575 
2576 		if (starttime == endtime) { /* Display 1ms as fallback */
2577 			starttime -= 1*NSEC_PER_MSEC;
2578 			endtime += 1*NSEC_PER_MSEC;
2579 		}
2580 		timestamp__scnprintf_usec(starttime, start, sizeof start);
2581 		timestamp__scnprintf_usec(endtime, end, sizeof end);
2582 		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2583 	}
2584 
2585 	script_browse(script_opt, act->evsel);
2586 	free(script_opt);
2587 	return 0;
2588 }
2589 
2590 static int
do_res_sample_script(struct hist_browser * browser __maybe_unused,struct popup_action * act)2591 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2592 		     struct popup_action *act)
2593 {
2594 	struct hist_entry *he;
2595 
2596 	he = hist_browser__selected_entry(browser);
2597 	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2598 	return 0;
2599 }
2600 
2601 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)2602 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2603 	       struct popup_action *act, char **optstr,
2604 	       struct thread *thread, struct symbol *sym,
2605 	       struct evsel *evsel, const char *tstr)
2606 {
2607 
2608 	if (thread) {
2609 		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2610 			     thread__comm_str(thread), tstr) < 0)
2611 			return 0;
2612 	} else if (sym) {
2613 		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2614 			     sym->name, tstr) < 0)
2615 			return 0;
2616 	} else {
2617 		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2618 			return 0;
2619 	}
2620 
2621 	act->thread = thread;
2622 	act->ms.sym = sym;
2623 	act->evsel = evsel;
2624 	act->fn = do_run_script;
2625 	return 1;
2626 }
2627 
2628 static int
add_script_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,struct thread * thread,struct symbol * sym,struct evsel * evsel)2629 add_script_opt(struct hist_browser *browser,
2630 	       struct popup_action *act, char **optstr,
2631 	       struct thread *thread, struct symbol *sym,
2632 	       struct evsel *evsel)
2633 {
2634 	int n, j;
2635 	struct hist_entry *he;
2636 
2637 	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2638 
2639 	he = hist_browser__selected_entry(browser);
2640 	if (sort_order && strstr(sort_order, "time")) {
2641 		char tstr[128];
2642 
2643 		optstr++;
2644 		act++;
2645 		j = sprintf(tstr, " in ");
2646 		j += timestamp__scnprintf_usec(he->time, tstr + j,
2647 					       sizeof tstr - j);
2648 		j += sprintf(tstr + j, "-");
2649 		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2650 				          tstr + j, sizeof tstr - j);
2651 		n += add_script_opt_2(browser, act, optstr, thread, sym,
2652 					  evsel, tstr);
2653 		act->time = he->time;
2654 	}
2655 	return n;
2656 }
2657 
2658 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)2659 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2660 		   struct popup_action *act, char **optstr,
2661 		   struct res_sample *res_sample,
2662 		   struct evsel *evsel,
2663 		   enum rstype type)
2664 {
2665 	if (!res_sample)
2666 		return 0;
2667 
2668 	if (asprintf(optstr, "Show context for individual samples %s",
2669 		type == A_ASM ? "with assembler" :
2670 		type == A_SOURCE ? "with source" : "") < 0)
2671 		return 0;
2672 
2673 	act->fn = do_res_sample_script;
2674 	act->evsel = evsel;
2675 	act->rstype = type;
2676 	return 1;
2677 }
2678 
2679 static int
do_switch_data(struct hist_browser * browser __maybe_unused,struct popup_action * act __maybe_unused)2680 do_switch_data(struct hist_browser *browser __maybe_unused,
2681 	       struct popup_action *act __maybe_unused)
2682 {
2683 	if (switch_data_file()) {
2684 		ui__warning("Won't switch the data files due to\n"
2685 			    "no valid data file get selected!\n");
2686 		return 0;
2687 	}
2688 
2689 	return K_SWITCH_INPUT_DATA;
2690 }
2691 
2692 static int
add_switch_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr)2693 add_switch_opt(struct hist_browser *browser,
2694 	       struct popup_action *act, char **optstr)
2695 {
2696 	if (!is_report_browser(browser->hbt))
2697 		return 0;
2698 
2699 	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2700 		return 0;
2701 
2702 	act->fn = do_switch_data;
2703 	return 1;
2704 }
2705 
2706 static int
do_exit_browser(struct hist_browser * browser __maybe_unused,struct popup_action * act __maybe_unused)2707 do_exit_browser(struct hist_browser *browser __maybe_unused,
2708 		struct popup_action *act __maybe_unused)
2709 {
2710 	return 0;
2711 }
2712 
2713 static int
add_exit_opt(struct hist_browser * browser __maybe_unused,struct popup_action * act,char ** optstr)2714 add_exit_opt(struct hist_browser *browser __maybe_unused,
2715 	     struct popup_action *act, char **optstr)
2716 {
2717 	if (asprintf(optstr, "Exit") < 0)
2718 		return 0;
2719 
2720 	act->fn = do_exit_browser;
2721 	return 1;
2722 }
2723 
2724 static int
do_zoom_socket(struct hist_browser * browser,struct popup_action * act)2725 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2726 {
2727 	if (!hists__has(browser->hists, socket) || act->socket < 0)
2728 		return 0;
2729 
2730 	if (browser->hists->socket_filter > -1) {
2731 		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2732 		browser->hists->socket_filter = -1;
2733 		perf_hpp__set_elide(HISTC_SOCKET, false);
2734 	} else {
2735 		browser->hists->socket_filter = act->socket;
2736 		perf_hpp__set_elide(HISTC_SOCKET, true);
2737 		pstack__push(browser->pstack, &browser->hists->socket_filter);
2738 	}
2739 
2740 	hists__filter_by_socket(browser->hists);
2741 	hist_browser__reset(browser);
2742 	return 0;
2743 }
2744 
2745 static int
add_socket_opt(struct hist_browser * browser,struct popup_action * act,char ** optstr,int socket_id)2746 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2747 	       char **optstr, int socket_id)
2748 {
2749 	if (!hists__has(browser->hists, socket) || socket_id < 0)
2750 		return 0;
2751 
2752 	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2753 		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2754 		     socket_id) < 0)
2755 		return 0;
2756 
2757 	act->socket = socket_id;
2758 	act->fn = do_zoom_socket;
2759 	return 1;
2760 }
2761 
hist_browser__update_nr_entries(struct hist_browser * hb)2762 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2763 {
2764 	u64 nr_entries = 0;
2765 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2766 
2767 	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2768 		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2769 		return;
2770 	}
2771 
2772 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2773 		nr_entries++;
2774 		nd = rb_hierarchy_next(nd);
2775 	}
2776 
2777 	hb->nr_non_filtered_entries = nr_entries;
2778 	hb->nr_hierarchy_entries = nr_entries;
2779 }
2780 
hist_browser__update_percent_limit(struct hist_browser * hb,double percent)2781 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2782 					       double percent)
2783 {
2784 	struct hist_entry *he;
2785 	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2786 	u64 total = hists__total_period(hb->hists);
2787 	u64 min_callchain_hits = total * (percent / 100);
2788 
2789 	hb->min_pcnt = callchain_param.min_percent = percent;
2790 
2791 	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2792 		he = rb_entry(nd, struct hist_entry, rb_node);
2793 
2794 		if (he->has_no_entry) {
2795 			he->has_no_entry = false;
2796 			he->nr_rows = 0;
2797 		}
2798 
2799 		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2800 			goto next;
2801 
2802 		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2803 			total = he->stat.period;
2804 
2805 			if (symbol_conf.cumulate_callchain)
2806 				total = he->stat_acc->period;
2807 
2808 			min_callchain_hits = total * (percent / 100);
2809 		}
2810 
2811 		callchain_param.sort(&he->sorted_chain, he->callchain,
2812 				     min_callchain_hits, &callchain_param);
2813 
2814 next:
2815 		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2816 
2817 		/* force to re-evaluate folding state of callchains */
2818 		he->init_have_children = false;
2819 		hist_entry__set_folding(he, hb, false);
2820 	}
2821 }
2822 
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)2823 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2824 				    const char *helpline,
2825 				    bool left_exits,
2826 				    struct hist_browser_timer *hbt,
2827 				    float min_pcnt,
2828 				    struct perf_env *env,
2829 				    bool warn_lost_event,
2830 				    struct annotation_options *annotation_opts)
2831 {
2832 	struct hists *hists = evsel__hists(evsel);
2833 	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2834 	struct branch_info *bi = NULL;
2835 #define MAX_OPTIONS  16
2836 	char *options[MAX_OPTIONS];
2837 	struct popup_action actions[MAX_OPTIONS];
2838 	int nr_options = 0;
2839 	int key = -1;
2840 	char buf[64];
2841 	int delay_secs = hbt ? hbt->refresh : 0;
2842 
2843 #define HIST_BROWSER_HELP_COMMON					\
2844 	"h/?/F1        Show this window\n"				\
2845 	"UP/DOWN/PGUP\n"						\
2846 	"PGDN/SPACE    Navigate\n"					\
2847 	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2848 	"For multiple event sessions:\n\n"				\
2849 	"TAB/UNTAB     Switch events\n\n"				\
2850 	"For symbolic views (--sort has sym):\n\n"			\
2851 	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2852 	"ESC           Zoom out\n"					\
2853 	"a             Annotate current symbol\n"			\
2854 	"C             Collapse all callchains\n"			\
2855 	"d             Zoom into current DSO\n"				\
2856 	"E             Expand all callchains\n"				\
2857 	"F             Toggle percentage of filtered entries\n"		\
2858 	"H             Display column headers\n"			\
2859 	"L             Change percent limit\n"				\
2860 	"m             Display context menu\n"				\
2861 	"S             Zoom into current Processor Socket\n"		\
2862 
2863 	/* help messages are sorted by lexical order of the hotkey */
2864 	static const char report_help[] = HIST_BROWSER_HELP_COMMON
2865 	"i             Show header information\n"
2866 	"P             Print histograms to perf.hist.N\n"
2867 	"r             Run available scripts\n"
2868 	"s             Switch to another data file in PWD\n"
2869 	"t             Zoom into current Thread\n"
2870 	"V             Verbose (DSO names in callchains, etc)\n"
2871 	"/             Filter symbol by name";
2872 	static const char top_help[] = HIST_BROWSER_HELP_COMMON
2873 	"P             Print histograms to perf.hist.N\n"
2874 	"t             Zoom into current Thread\n"
2875 	"V             Verbose (DSO names in callchains, etc)\n"
2876 	"z             Toggle zeroing of samples\n"
2877 	"f             Enable/Disable events\n"
2878 	"/             Filter symbol by name";
2879 
2880 	if (browser == NULL)
2881 		return -1;
2882 
2883 	/* reset abort key so that it can get Ctrl-C as a key */
2884 	SLang_reset_tty();
2885 	SLang_init_tty(0, 0, 0);
2886 
2887 	if (min_pcnt)
2888 		browser->min_pcnt = min_pcnt;
2889 	hist_browser__update_nr_entries(browser);
2890 
2891 	browser->pstack = pstack__new(3);
2892 	if (browser->pstack == NULL)
2893 		goto out;
2894 
2895 	ui_helpline__push(helpline);
2896 
2897 	memset(options, 0, sizeof(options));
2898 	memset(actions, 0, sizeof(actions));
2899 
2900 	if (symbol_conf.col_width_list_str)
2901 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2902 
2903 	if (!is_report_browser(hbt))
2904 		browser->b.no_samples_msg = "Collecting samples...";
2905 
2906 	while (1) {
2907 		struct thread *thread = NULL;
2908 		struct map *map = NULL;
2909 		int choice = 0;
2910 		int socked_id = -1;
2911 
2912 		nr_options = 0;
2913 
2914 		key = hist_browser__run(browser, helpline,
2915 					warn_lost_event);
2916 
2917 		if (browser->he_selection != NULL) {
2918 			thread = hist_browser__selected_thread(browser);
2919 			map = browser->selection->map;
2920 			socked_id = browser->he_selection->socket;
2921 		}
2922 		switch (key) {
2923 		case K_TAB:
2924 		case K_UNTAB:
2925 			if (nr_events == 1)
2926 				continue;
2927 			/*
2928 			 * Exit the browser, let hists__browser_tree
2929 			 * go to the next or previous
2930 			 */
2931 			goto out_free_stack;
2932 		case 'a':
2933 			if (!hists__has(hists, sym)) {
2934 				ui_browser__warning(&browser->b, delay_secs * 2,
2935 			"Annotation is only available for symbolic views, "
2936 			"include \"sym*\" in --sort to use it.");
2937 				continue;
2938 			}
2939 
2940 			if (browser->selection == NULL ||
2941 			    browser->selection->sym == NULL ||
2942 			    browser->selection->map->dso->annotate_warned)
2943 				continue;
2944 
2945 			actions->ms.map = browser->selection->map;
2946 			actions->ms.sym = browser->selection->sym;
2947 			do_annotate(browser, actions);
2948 			continue;
2949 		case 'P':
2950 			hist_browser__dump(browser);
2951 			continue;
2952 		case 'd':
2953 			actions->ms.map = map;
2954 			do_zoom_dso(browser, actions);
2955 			continue;
2956 		case 'V':
2957 			verbose = (verbose + 1) % 4;
2958 			browser->show_dso = verbose > 0;
2959 			ui_helpline__fpush("Verbosity level set to %d\n",
2960 					   verbose);
2961 			continue;
2962 		case 't':
2963 			actions->thread = thread;
2964 			do_zoom_thread(browser, actions);
2965 			continue;
2966 		case 'S':
2967 			actions->socket = socked_id;
2968 			do_zoom_socket(browser, actions);
2969 			continue;
2970 		case '/':
2971 			if (ui_browser__input_window("Symbol to show",
2972 					"Please enter the name of symbol you want to see.\n"
2973 					"To remove the filter later, press / + ENTER.",
2974 					buf, "ENTER: OK, ESC: Cancel",
2975 					delay_secs * 2) == K_ENTER) {
2976 				hists->symbol_filter_str = *buf ? buf : NULL;
2977 				hists__filter_by_symbol(hists);
2978 				hist_browser__reset(browser);
2979 			}
2980 			continue;
2981 		case 'r':
2982 			if (is_report_browser(hbt)) {
2983 				actions->thread = NULL;
2984 				actions->ms.sym = NULL;
2985 				do_run_script(browser, actions);
2986 			}
2987 			continue;
2988 		case 's':
2989 			if (is_report_browser(hbt)) {
2990 				key = do_switch_data(browser, actions);
2991 				if (key == K_SWITCH_INPUT_DATA)
2992 					goto out_free_stack;
2993 			}
2994 			continue;
2995 		case 'i':
2996 			/* env->arch is NULL for live-mode (i.e. perf top) */
2997 			if (env->arch)
2998 				tui__header_window(env);
2999 			continue;
3000 		case 'F':
3001 			symbol_conf.filter_relative ^= 1;
3002 			continue;
3003 		case 'z':
3004 			if (!is_report_browser(hbt)) {
3005 				struct perf_top *top = hbt->arg;
3006 
3007 				top->zero = !top->zero;
3008 			}
3009 			continue;
3010 		case 'L':
3011 			if (ui_browser__input_window("Percent Limit",
3012 					"Please enter the value you want to hide entries under that percent.",
3013 					buf, "ENTER: OK, ESC: Cancel",
3014 					delay_secs * 2) == K_ENTER) {
3015 				char *end;
3016 				double new_percent = strtod(buf, &end);
3017 
3018 				if (new_percent < 0 || new_percent > 100) {
3019 					ui_browser__warning(&browser->b, delay_secs * 2,
3020 						"Invalid percent: %.2f", new_percent);
3021 					continue;
3022 				}
3023 
3024 				hist_browser__update_percent_limit(browser, new_percent);
3025 				hist_browser__reset(browser);
3026 			}
3027 			continue;
3028 		case K_F1:
3029 		case 'h':
3030 		case '?':
3031 			ui_browser__help_window(&browser->b,
3032 				is_report_browser(hbt) ? report_help : top_help);
3033 			continue;
3034 		case K_ENTER:
3035 		case K_RIGHT:
3036 		case 'm':
3037 			/* menu */
3038 			break;
3039 		case K_ESC:
3040 		case K_LEFT: {
3041 			const void *top;
3042 
3043 			if (pstack__empty(browser->pstack)) {
3044 				/*
3045 				 * Go back to the perf_evsel_menu__run or other user
3046 				 */
3047 				if (left_exits)
3048 					goto out_free_stack;
3049 
3050 				if (key == K_ESC &&
3051 				    ui_browser__dialog_yesno(&browser->b,
3052 							     "Do you really want to exit?"))
3053 					goto out_free_stack;
3054 
3055 				continue;
3056 			}
3057 			top = pstack__peek(browser->pstack);
3058 			if (top == &browser->hists->dso_filter) {
3059 				/*
3060 				 * No need to set actions->dso here since
3061 				 * it's just to remove the current filter.
3062 				 * Ditto for thread below.
3063 				 */
3064 				do_zoom_dso(browser, actions);
3065 			} else if (top == &browser->hists->thread_filter) {
3066 				do_zoom_thread(browser, actions);
3067 			} else if (top == &browser->hists->socket_filter) {
3068 				do_zoom_socket(browser, actions);
3069 			}
3070 			continue;
3071 		}
3072 		case 'q':
3073 		case CTRL('c'):
3074 			goto out_free_stack;
3075 		case 'f':
3076 			if (!is_report_browser(hbt)) {
3077 				struct perf_top *top = hbt->arg;
3078 
3079 				perf_evlist__toggle_enable(top->evlist);
3080 				/*
3081 				 * No need to refresh, resort/decay histogram
3082 				 * entries if we are not collecting samples:
3083 				 */
3084 				if (top->evlist->enabled) {
3085 					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3086 					hbt->refresh = delay_secs;
3087 				} else {
3088 					helpline = "Press 'f' again to re-enable the events";
3089 					hbt->refresh = 0;
3090 				}
3091 				continue;
3092 			}
3093 			/* Fall thru */
3094 		default:
3095 			helpline = "Press '?' for help on key bindings";
3096 			continue;
3097 		}
3098 
3099 		if (!hists__has(hists, sym) || browser->selection == NULL)
3100 			goto skip_annotation;
3101 
3102 		if (sort__mode == SORT_MODE__BRANCH) {
3103 
3104 			if (browser->he_selection)
3105 				bi = browser->he_selection->branch_info;
3106 
3107 			if (bi == NULL)
3108 				goto skip_annotation;
3109 
3110 			nr_options += add_annotate_opt(browser,
3111 						       &actions[nr_options],
3112 						       &options[nr_options],
3113 						       bi->from.map,
3114 						       bi->from.sym);
3115 			if (bi->to.sym != bi->from.sym)
3116 				nr_options += add_annotate_opt(browser,
3117 							&actions[nr_options],
3118 							&options[nr_options],
3119 							bi->to.map,
3120 							bi->to.sym);
3121 		} else {
3122 			nr_options += add_annotate_opt(browser,
3123 						       &actions[nr_options],
3124 						       &options[nr_options],
3125 						       browser->selection->map,
3126 						       browser->selection->sym);
3127 		}
3128 skip_annotation:
3129 		nr_options += add_thread_opt(browser, &actions[nr_options],
3130 					     &options[nr_options], thread);
3131 		nr_options += add_dso_opt(browser, &actions[nr_options],
3132 					  &options[nr_options], map);
3133 		nr_options += add_map_opt(browser, &actions[nr_options],
3134 					  &options[nr_options],
3135 					  browser->selection ?
3136 						browser->selection->map : NULL);
3137 		nr_options += add_socket_opt(browser, &actions[nr_options],
3138 					     &options[nr_options],
3139 					     socked_id);
3140 		/* perf script support */
3141 		if (!is_report_browser(hbt))
3142 			goto skip_scripting;
3143 
3144 		if (browser->he_selection) {
3145 			if (hists__has(hists, thread) && thread) {
3146 				nr_options += add_script_opt(browser,
3147 							     &actions[nr_options],
3148 							     &options[nr_options],
3149 							     thread, NULL, evsel);
3150 			}
3151 			/*
3152 			 * Note that browser->selection != NULL
3153 			 * when browser->he_selection is not NULL,
3154 			 * so we don't need to check browser->selection
3155 			 * before fetching browser->selection->sym like what
3156 			 * we do before fetching browser->selection->map.
3157 			 *
3158 			 * See hist_browser__show_entry.
3159 			 */
3160 			if (hists__has(hists, sym) && browser->selection->sym) {
3161 				nr_options += add_script_opt(browser,
3162 							     &actions[nr_options],
3163 							     &options[nr_options],
3164 							     NULL, browser->selection->sym,
3165 							     evsel);
3166 			}
3167 		}
3168 		nr_options += add_script_opt(browser, &actions[nr_options],
3169 					     &options[nr_options], NULL, NULL, evsel);
3170 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3171 						 &options[nr_options],
3172 				 hist_browser__selected_entry(browser)->res_samples,
3173 				 evsel, A_NORMAL);
3174 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3175 						 &options[nr_options],
3176 				 hist_browser__selected_entry(browser)->res_samples,
3177 				 evsel, A_ASM);
3178 		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3179 						 &options[nr_options],
3180 				 hist_browser__selected_entry(browser)->res_samples,
3181 				 evsel, A_SOURCE);
3182 		nr_options += add_switch_opt(browser, &actions[nr_options],
3183 					     &options[nr_options]);
3184 skip_scripting:
3185 		nr_options += add_exit_opt(browser, &actions[nr_options],
3186 					   &options[nr_options]);
3187 
3188 		do {
3189 			struct popup_action *act;
3190 
3191 			choice = ui__popup_menu(nr_options, options);
3192 			if (choice == -1 || choice >= nr_options)
3193 				break;
3194 
3195 			act = &actions[choice];
3196 			key = act->fn(browser, act);
3197 		} while (key == 1);
3198 
3199 		if (key == K_SWITCH_INPUT_DATA)
3200 			break;
3201 	}
3202 out_free_stack:
3203 	pstack__delete(browser->pstack);
3204 out:
3205 	hist_browser__delete(browser);
3206 	free_popup_options(options, MAX_OPTIONS);
3207 	return key;
3208 }
3209 
3210 struct evsel_menu {
3211 	struct ui_browser b;
3212 	struct evsel *selection;
3213 	struct annotation_options *annotation_opts;
3214 	bool lost_events, lost_events_warned;
3215 	float min_pcnt;
3216 	struct perf_env *env;
3217 };
3218 
perf_evsel_menu__write(struct ui_browser * browser,void * entry,int row)3219 static void perf_evsel_menu__write(struct ui_browser *browser,
3220 				   void *entry, int row)
3221 {
3222 	struct evsel_menu *menu = container_of(browser,
3223 						    struct evsel_menu, b);
3224 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3225 	struct hists *hists = evsel__hists(evsel);
3226 	bool current_entry = ui_browser__is_current_entry(browser, row);
3227 	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3228 	const char *ev_name = perf_evsel__name(evsel);
3229 	char bf[256], unit;
3230 	const char *warn = " ";
3231 	size_t printed;
3232 
3233 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3234 						       HE_COLORSET_NORMAL);
3235 
3236 	if (perf_evsel__is_group_event(evsel)) {
3237 		struct evsel *pos;
3238 
3239 		ev_name = perf_evsel__group_name(evsel);
3240 
3241 		for_each_group_member(pos, evsel) {
3242 			struct hists *pos_hists = evsel__hists(pos);
3243 			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3244 		}
3245 	}
3246 
3247 	nr_events = convert_unit(nr_events, &unit);
3248 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3249 			   unit, unit == ' ' ? "" : " ", ev_name);
3250 	ui_browser__printf(browser, "%s", bf);
3251 
3252 	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3253 	if (nr_events != 0) {
3254 		menu->lost_events = true;
3255 		if (!current_entry)
3256 			ui_browser__set_color(browser, HE_COLORSET_TOP);
3257 		nr_events = convert_unit(nr_events, &unit);
3258 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3259 				     nr_events, unit, unit == ' ' ? "" : " ");
3260 		warn = bf;
3261 	}
3262 
3263 	ui_browser__write_nstring(browser, warn, browser->width - printed);
3264 
3265 	if (current_entry)
3266 		menu->selection = evsel;
3267 }
3268 
perf_evsel_menu__run(struct evsel_menu * menu,int nr_events,const char * help,struct hist_browser_timer * hbt,bool warn_lost_event)3269 static int perf_evsel_menu__run(struct evsel_menu *menu,
3270 				int nr_events, const char *help,
3271 				struct hist_browser_timer *hbt,
3272 				bool warn_lost_event)
3273 {
3274 	struct evlist *evlist = menu->b.priv;
3275 	struct evsel *pos;
3276 	const char *title = "Available samples";
3277 	int delay_secs = hbt ? hbt->refresh : 0;
3278 	int key;
3279 
3280 	if (ui_browser__show(&menu->b, title,
3281 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3282 		return -1;
3283 
3284 	while (1) {
3285 		key = ui_browser__run(&menu->b, delay_secs);
3286 
3287 		switch (key) {
3288 		case K_TIMER:
3289 			if (hbt)
3290 				hbt->timer(hbt->arg);
3291 
3292 			if (!menu->lost_events_warned &&
3293 			    menu->lost_events &&
3294 			    warn_lost_event) {
3295 				ui_browser__warn_lost_events(&menu->b);
3296 				menu->lost_events_warned = true;
3297 			}
3298 			continue;
3299 		case K_RIGHT:
3300 		case K_ENTER:
3301 			if (!menu->selection)
3302 				continue;
3303 			pos = menu->selection;
3304 browse_hists:
3305 			perf_evlist__set_selected(evlist, pos);
3306 			/*
3307 			 * Give the calling tool a chance to populate the non
3308 			 * default evsel resorted hists tree.
3309 			 */
3310 			if (hbt)
3311 				hbt->timer(hbt->arg);
3312 			key = perf_evsel__hists_browse(pos, nr_events, help,
3313 						       true, hbt,
3314 						       menu->min_pcnt,
3315 						       menu->env,
3316 						       warn_lost_event,
3317 						       menu->annotation_opts);
3318 			ui_browser__show_title(&menu->b, title);
3319 			switch (key) {
3320 			case K_TAB:
3321 				if (pos->core.node.next == &evlist->core.entries)
3322 					pos = evlist__first(evlist);
3323 				else
3324 					pos = perf_evsel__next(pos);
3325 				goto browse_hists;
3326 			case K_UNTAB:
3327 				if (pos->core.node.prev == &evlist->core.entries)
3328 					pos = evlist__last(evlist);
3329 				else
3330 					pos = perf_evsel__prev(pos);
3331 				goto browse_hists;
3332 			case K_SWITCH_INPUT_DATA:
3333 			case 'q':
3334 			case CTRL('c'):
3335 				goto out;
3336 			case K_ESC:
3337 			default:
3338 				continue;
3339 			}
3340 		case K_LEFT:
3341 			continue;
3342 		case K_ESC:
3343 			if (!ui_browser__dialog_yesno(&menu->b,
3344 					       "Do you really want to exit?"))
3345 				continue;
3346 			/* Fall thru */
3347 		case 'q':
3348 		case CTRL('c'):
3349 			goto out;
3350 		default:
3351 			continue;
3352 		}
3353 	}
3354 
3355 out:
3356 	ui_browser__hide(&menu->b);
3357 	return key;
3358 }
3359 
filter_group_entries(struct ui_browser * browser __maybe_unused,void * entry)3360 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3361 				 void *entry)
3362 {
3363 	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3364 
3365 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3366 		return true;
3367 
3368 	return false;
3369 }
3370 
__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)3371 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3372 					   int nr_entries, const char *help,
3373 					   struct hist_browser_timer *hbt,
3374 					   float min_pcnt,
3375 					   struct perf_env *env,
3376 					   bool warn_lost_event,
3377 					   struct annotation_options *annotation_opts)
3378 {
3379 	struct evsel *pos;
3380 	struct evsel_menu menu = {
3381 		.b = {
3382 			.entries    = &evlist->core.entries,
3383 			.refresh    = ui_browser__list_head_refresh,
3384 			.seek	    = ui_browser__list_head_seek,
3385 			.write	    = perf_evsel_menu__write,
3386 			.filter	    = filter_group_entries,
3387 			.nr_entries = nr_entries,
3388 			.priv	    = evlist,
3389 		},
3390 		.min_pcnt = min_pcnt,
3391 		.env = env,
3392 		.annotation_opts = annotation_opts,
3393 	};
3394 
3395 	ui_helpline__push("Press ESC to exit");
3396 
3397 	evlist__for_each_entry(evlist, pos) {
3398 		const char *ev_name = perf_evsel__name(pos);
3399 		size_t line_len = strlen(ev_name) + 7;
3400 
3401 		if (menu.b.width < line_len)
3402 			menu.b.width = line_len;
3403 	}
3404 
3405 	return perf_evsel_menu__run(&menu, nr_entries, help,
3406 				    hbt, warn_lost_event);
3407 }
3408 
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)3409 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3410 				  struct hist_browser_timer *hbt,
3411 				  float min_pcnt,
3412 				  struct perf_env *env,
3413 				  bool warn_lost_event,
3414 				  struct annotation_options *annotation_opts)
3415 {
3416 	int nr_entries = evlist->core.nr_entries;
3417 
3418 single_entry:
3419 	if (nr_entries == 1) {
3420 		struct evsel *first = evlist__first(evlist);
3421 
3422 		return perf_evsel__hists_browse(first, nr_entries, help,
3423 						false, hbt, min_pcnt,
3424 						env, warn_lost_event,
3425 						annotation_opts);
3426 	}
3427 
3428 	if (symbol_conf.event_group) {
3429 		struct evsel *pos;
3430 
3431 		nr_entries = 0;
3432 		evlist__for_each_entry(evlist, pos) {
3433 			if (perf_evsel__is_group_leader(pos))
3434 				nr_entries++;
3435 		}
3436 
3437 		if (nr_entries == 1)
3438 			goto single_entry;
3439 	}
3440 
3441 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3442 					       hbt, min_pcnt, env,
3443 					       warn_lost_event,
3444 					       annotation_opts);
3445 }
3446