• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <linux/rbtree.h>
6 
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../arch/common.h"
14 
15 #include "../browser.h"
16 #include "../helpline.h"
17 #include "../util.h"
18 #include "../ui.h"
19 #include "map.h"
20 
21 struct hist_browser {
22 	struct ui_browser   b;
23 	struct hists	    *hists;
24 	struct hist_entry   *he_selection;
25 	struct map_symbol   *selection;
26 	int		     print_seq;
27 	bool		     show_dso;
28 	bool		     has_symbols;
29 };
30 
31 extern void hist_browser__init_hpp(void);
32 
33 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
34 				const char *ev_name);
35 
hist_browser__refresh_dimensions(struct hist_browser * browser)36 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
37 {
38 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
39 	browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
40 			     sizeof("[k]"));
41 }
42 
hist_browser__reset(struct hist_browser * browser)43 static void hist_browser__reset(struct hist_browser *browser)
44 {
45 	browser->b.nr_entries = browser->hists->nr_entries;
46 	hist_browser__refresh_dimensions(browser);
47 	ui_browser__reset_index(&browser->b);
48 }
49 
tree__folded_sign(bool unfolded)50 static char tree__folded_sign(bool unfolded)
51 {
52 	return unfolded ? '-' : '+';
53 }
54 
map_symbol__folded(const struct map_symbol * ms)55 static char map_symbol__folded(const struct map_symbol *ms)
56 {
57 	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
58 }
59 
hist_entry__folded(const struct hist_entry * he)60 static char hist_entry__folded(const struct hist_entry *he)
61 {
62 	return map_symbol__folded(&he->ms);
63 }
64 
callchain_list__folded(const struct callchain_list * cl)65 static char callchain_list__folded(const struct callchain_list *cl)
66 {
67 	return map_symbol__folded(&cl->ms);
68 }
69 
map_symbol__set_folding(struct map_symbol * ms,bool unfold)70 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
71 {
72 	ms->unfolded = unfold ? ms->has_children : false;
73 }
74 
callchain_node__count_rows_rb_tree(struct callchain_node * node)75 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
76 {
77 	int n = 0;
78 	struct rb_node *nd;
79 
80 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
81 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
82 		struct callchain_list *chain;
83 		char folded_sign = ' '; /* No children */
84 
85 		list_for_each_entry(chain, &child->val, list) {
86 			++n;
87 			/* We need this because we may not have children */
88 			folded_sign = callchain_list__folded(chain);
89 			if (folded_sign == '+')
90 				break;
91 		}
92 
93 		if (folded_sign == '-') /* Have children and they're unfolded */
94 			n += callchain_node__count_rows_rb_tree(child);
95 	}
96 
97 	return n;
98 }
99 
callchain_node__count_rows(struct callchain_node * node)100 static int callchain_node__count_rows(struct callchain_node *node)
101 {
102 	struct callchain_list *chain;
103 	bool unfolded = false;
104 	int n = 0;
105 
106 	list_for_each_entry(chain, &node->val, list) {
107 		++n;
108 		unfolded = chain->ms.unfolded;
109 	}
110 
111 	if (unfolded)
112 		n += callchain_node__count_rows_rb_tree(node);
113 
114 	return n;
115 }
116 
callchain__count_rows(struct rb_root * chain)117 static int callchain__count_rows(struct rb_root *chain)
118 {
119 	struct rb_node *nd;
120 	int n = 0;
121 
122 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
123 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
124 		n += callchain_node__count_rows(node);
125 	}
126 
127 	return n;
128 }
129 
map_symbol__toggle_fold(struct map_symbol * ms)130 static bool map_symbol__toggle_fold(struct map_symbol *ms)
131 {
132 	if (!ms)
133 		return false;
134 
135 	if (!ms->has_children)
136 		return false;
137 
138 	ms->unfolded = !ms->unfolded;
139 	return true;
140 }
141 
callchain_node__init_have_children_rb_tree(struct callchain_node * node)142 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
143 {
144 	struct rb_node *nd = rb_first(&node->rb_root);
145 
146 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
147 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
148 		struct callchain_list *chain;
149 		bool first = true;
150 
151 		list_for_each_entry(chain, &child->val, list) {
152 			if (first) {
153 				first = false;
154 				chain->ms.has_children = chain->list.next != &child->val ||
155 							 !RB_EMPTY_ROOT(&child->rb_root);
156 			} else
157 				chain->ms.has_children = chain->list.next == &child->val &&
158 							 !RB_EMPTY_ROOT(&child->rb_root);
159 		}
160 
161 		callchain_node__init_have_children_rb_tree(child);
162 	}
163 }
164 
callchain_node__init_have_children(struct callchain_node * node)165 static void callchain_node__init_have_children(struct callchain_node *node)
166 {
167 	struct callchain_list *chain;
168 
169 	list_for_each_entry(chain, &node->val, list)
170 		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
171 
172 	callchain_node__init_have_children_rb_tree(node);
173 }
174 
callchain__init_have_children(struct rb_root * root)175 static void callchain__init_have_children(struct rb_root *root)
176 {
177 	struct rb_node *nd;
178 
179 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
180 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
181 		callchain_node__init_have_children(node);
182 	}
183 }
184 
hist_entry__init_have_children(struct hist_entry * he)185 static void hist_entry__init_have_children(struct hist_entry *he)
186 {
187 	if (!he->init_have_children) {
188 		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
189 		callchain__init_have_children(&he->sorted_chain);
190 		he->init_have_children = true;
191 	}
192 }
193 
hist_browser__toggle_fold(struct hist_browser * browser)194 static bool hist_browser__toggle_fold(struct hist_browser *browser)
195 {
196 	if (map_symbol__toggle_fold(browser->selection)) {
197 		struct hist_entry *he = browser->he_selection;
198 
199 		hist_entry__init_have_children(he);
200 		browser->hists->nr_entries -= he->nr_rows;
201 
202 		if (he->ms.unfolded)
203 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
204 		else
205 			he->nr_rows = 0;
206 		browser->hists->nr_entries += he->nr_rows;
207 		browser->b.nr_entries = browser->hists->nr_entries;
208 
209 		return true;
210 	}
211 
212 	/* If it doesn't have children, no toggling performed */
213 	return false;
214 }
215 
callchain_node__set_folding_rb_tree(struct callchain_node * node,bool unfold)216 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
217 {
218 	int n = 0;
219 	struct rb_node *nd;
220 
221 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
222 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
223 		struct callchain_list *chain;
224 		bool has_children = false;
225 
226 		list_for_each_entry(chain, &child->val, list) {
227 			++n;
228 			map_symbol__set_folding(&chain->ms, unfold);
229 			has_children = chain->ms.has_children;
230 		}
231 
232 		if (has_children)
233 			n += callchain_node__set_folding_rb_tree(child, unfold);
234 	}
235 
236 	return n;
237 }
238 
callchain_node__set_folding(struct callchain_node * node,bool unfold)239 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
240 {
241 	struct callchain_list *chain;
242 	bool has_children = false;
243 	int n = 0;
244 
245 	list_for_each_entry(chain, &node->val, list) {
246 		++n;
247 		map_symbol__set_folding(&chain->ms, unfold);
248 		has_children = chain->ms.has_children;
249 	}
250 
251 	if (has_children)
252 		n += callchain_node__set_folding_rb_tree(node, unfold);
253 
254 	return n;
255 }
256 
callchain__set_folding(struct rb_root * chain,bool unfold)257 static int callchain__set_folding(struct rb_root *chain, bool unfold)
258 {
259 	struct rb_node *nd;
260 	int n = 0;
261 
262 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
263 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
264 		n += callchain_node__set_folding(node, unfold);
265 	}
266 
267 	return n;
268 }
269 
hist_entry__set_folding(struct hist_entry * he,bool unfold)270 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
271 {
272 	hist_entry__init_have_children(he);
273 	map_symbol__set_folding(&he->ms, unfold);
274 
275 	if (he->ms.has_children) {
276 		int n = callchain__set_folding(&he->sorted_chain, unfold);
277 		he->nr_rows = unfold ? n : 0;
278 	} else
279 		he->nr_rows = 0;
280 }
281 
hists__set_folding(struct hists * hists,bool unfold)282 static void hists__set_folding(struct hists *hists, bool unfold)
283 {
284 	struct rb_node *nd;
285 
286 	hists->nr_entries = 0;
287 
288 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
289 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
290 		hist_entry__set_folding(he, unfold);
291 		hists->nr_entries += 1 + he->nr_rows;
292 	}
293 }
294 
hist_browser__set_folding(struct hist_browser * browser,bool unfold)295 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
296 {
297 	hists__set_folding(browser->hists, unfold);
298 	browser->b.nr_entries = browser->hists->nr_entries;
299 	/* Go to the start, we may be way after valid entries after a collapse */
300 	ui_browser__reset_index(&browser->b);
301 }
302 
ui_browser__warn_lost_events(struct ui_browser * browser)303 static void ui_browser__warn_lost_events(struct ui_browser *browser)
304 {
305 	ui_browser__warning(browser, 4,
306 		"Events are being lost, check IO/CPU overload!\n\n"
307 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
308 		" perf top -r 80\n\n"
309 		"Or reduce the sampling frequency.");
310 }
311 
hist_browser__run(struct hist_browser * browser,const char * ev_name,struct hist_browser_timer * hbt)312 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
313 			     struct hist_browser_timer *hbt)
314 {
315 	int key;
316 	char title[160];
317 	int delay_secs = hbt ? hbt->refresh : 0;
318 
319 	browser->b.entries = &browser->hists->entries;
320 	browser->b.nr_entries = browser->hists->nr_entries;
321 
322 	hist_browser__refresh_dimensions(browser);
323 	hists__browser_title(browser->hists, title, sizeof(title), ev_name);
324 
325 	if (ui_browser__show(&browser->b, title,
326 			     "Press '?' for help on key bindings") < 0)
327 		return -1;
328 
329 	while (1) {
330 		key = ui_browser__run(&browser->b, delay_secs);
331 
332 		switch (key) {
333 		case K_TIMER:
334 			hbt->timer(hbt->arg);
335 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
336 
337 			if (browser->hists->stats.nr_lost_warned !=
338 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
339 				browser->hists->stats.nr_lost_warned =
340 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
341 				ui_browser__warn_lost_events(&browser->b);
342 			}
343 
344 			hists__browser_title(browser->hists, title, sizeof(title), ev_name);
345 			ui_browser__show_title(&browser->b, title);
346 			continue;
347 		case 'D': { /* Debug */
348 			static int seq;
349 			struct hist_entry *h = rb_entry(browser->b.top,
350 							struct hist_entry, rb_node);
351 			ui_helpline__pop();
352 			ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
353 					   seq++, browser->b.nr_entries,
354 					   browser->hists->nr_entries,
355 					   browser->b.height,
356 					   browser->b.index,
357 					   browser->b.top_idx,
358 					   h->row_offset, h->nr_rows);
359 		}
360 			break;
361 		case 'C':
362 			/* Collapse the whole world. */
363 			hist_browser__set_folding(browser, false);
364 			break;
365 		case 'E':
366 			/* Expand the whole world. */
367 			hist_browser__set_folding(browser, true);
368 			break;
369 		case K_ENTER:
370 			if (hist_browser__toggle_fold(browser))
371 				break;
372 			/* fall thru */
373 		default:
374 			goto out;
375 		}
376 	}
377 out:
378 	ui_browser__hide(&browser->b);
379 	return key;
380 }
381 
callchain_list__sym_name(struct callchain_list * cl,char * bf,size_t bfsize,bool show_dso)382 static char *callchain_list__sym_name(struct callchain_list *cl,
383 				      char *bf, size_t bfsize, bool show_dso)
384 {
385 	int printed;
386 
387 	if (cl->ms.sym)
388 		printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
389 	else
390 		printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
391 
392 	if (show_dso)
393 		scnprintf(bf + printed, bfsize - printed, " %s",
394 			  cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
395 
396 	return bf;
397 }
398 
399 #define LEVEL_OFFSET_STEP 3
400 
hist_browser__show_callchain_node_rb_tree(struct hist_browser * browser,struct callchain_node * chain_node,u64 total,int level,unsigned short row,off_t * row_offset,bool * is_current_entry)401 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
402 						     struct callchain_node *chain_node,
403 						     u64 total, int level,
404 						     unsigned short row,
405 						     off_t *row_offset,
406 						     bool *is_current_entry)
407 {
408 	struct rb_node *node;
409 	int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
410 	u64 new_total, remaining;
411 
412 	if (callchain_param.mode == CHAIN_GRAPH_REL)
413 		new_total = chain_node->children_hit;
414 	else
415 		new_total = total;
416 
417 	remaining = new_total;
418 	node = rb_first(&chain_node->rb_root);
419 	while (node) {
420 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
421 		struct rb_node *next = rb_next(node);
422 		u64 cumul = callchain_cumul_hits(child);
423 		struct callchain_list *chain;
424 		char folded_sign = ' ';
425 		int first = true;
426 		int extra_offset = 0;
427 
428 		remaining -= cumul;
429 
430 		list_for_each_entry(chain, &child->val, list) {
431 			char bf[1024], *alloc_str;
432 			const char *str;
433 			int color;
434 			bool was_first = first;
435 
436 			if (first)
437 				first = false;
438 			else
439 				extra_offset = LEVEL_OFFSET_STEP;
440 
441 			folded_sign = callchain_list__folded(chain);
442 			if (*row_offset != 0) {
443 				--*row_offset;
444 				goto do_next;
445 			}
446 
447 			alloc_str = NULL;
448 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
449 						       browser->show_dso);
450 			if (was_first) {
451 				double percent = cumul * 100.0 / new_total;
452 
453 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
454 					str = "Not enough memory!";
455 				else
456 					str = alloc_str;
457 			}
458 
459 			color = HE_COLORSET_NORMAL;
460 			width = browser->b.width - (offset + extra_offset + 2);
461 			if (ui_browser__is_current_entry(&browser->b, row)) {
462 				browser->selection = &chain->ms;
463 				color = HE_COLORSET_SELECTED;
464 				*is_current_entry = true;
465 			}
466 
467 			ui_browser__set_color(&browser->b, color);
468 			ui_browser__gotorc(&browser->b, row, 0);
469 			slsmg_write_nstring(" ", offset + extra_offset);
470 			slsmg_printf("%c ", folded_sign);
471 			slsmg_write_nstring(str, width);
472 			free(alloc_str);
473 
474 			if (++row == browser->b.height)
475 				goto out;
476 do_next:
477 			if (folded_sign == '+')
478 				break;
479 		}
480 
481 		if (folded_sign == '-') {
482 			const int new_level = level + (extra_offset ? 2 : 1);
483 			row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
484 									 new_level, row, row_offset,
485 									 is_current_entry);
486 		}
487 		if (row == browser->b.height)
488 			goto out;
489 		node = next;
490 	}
491 out:
492 	return row - first_row;
493 }
494 
hist_browser__show_callchain_node(struct hist_browser * browser,struct callchain_node * node,int level,unsigned short row,off_t * row_offset,bool * is_current_entry)495 static int hist_browser__show_callchain_node(struct hist_browser *browser,
496 					     struct callchain_node *node,
497 					     int level, unsigned short row,
498 					     off_t *row_offset,
499 					     bool *is_current_entry)
500 {
501 	struct callchain_list *chain;
502 	int first_row = row,
503 	     offset = level * LEVEL_OFFSET_STEP,
504 	     width = browser->b.width - offset;
505 	char folded_sign = ' ';
506 
507 	list_for_each_entry(chain, &node->val, list) {
508 		char bf[1024], *s;
509 		int color;
510 
511 		folded_sign = callchain_list__folded(chain);
512 
513 		if (*row_offset != 0) {
514 			--*row_offset;
515 			continue;
516 		}
517 
518 		color = HE_COLORSET_NORMAL;
519 		if (ui_browser__is_current_entry(&browser->b, row)) {
520 			browser->selection = &chain->ms;
521 			color = HE_COLORSET_SELECTED;
522 			*is_current_entry = true;
523 		}
524 
525 		s = callchain_list__sym_name(chain, bf, sizeof(bf),
526 					     browser->show_dso);
527 		ui_browser__gotorc(&browser->b, row, 0);
528 		ui_browser__set_color(&browser->b, color);
529 		slsmg_write_nstring(" ", offset);
530 		slsmg_printf("%c ", folded_sign);
531 		slsmg_write_nstring(s, width - 2);
532 
533 		if (++row == browser->b.height)
534 			goto out;
535 	}
536 
537 	if (folded_sign == '-')
538 		row += hist_browser__show_callchain_node_rb_tree(browser, node,
539 								 browser->hists->stats.total_period,
540 								 level + 1, row,
541 								 row_offset,
542 								 is_current_entry);
543 out:
544 	return row - first_row;
545 }
546 
hist_browser__show_callchain(struct hist_browser * browser,struct rb_root * chain,int level,unsigned short row,off_t * row_offset,bool * is_current_entry)547 static int hist_browser__show_callchain(struct hist_browser *browser,
548 					struct rb_root *chain,
549 					int level, unsigned short row,
550 					off_t *row_offset,
551 					bool *is_current_entry)
552 {
553 	struct rb_node *nd;
554 	int first_row = row;
555 
556 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
557 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
558 
559 		row += hist_browser__show_callchain_node(browser, node, level,
560 							 row, row_offset,
561 							 is_current_entry);
562 		if (row == browser->b.height)
563 			break;
564 	}
565 
566 	return row - first_row;
567 }
568 
569 struct hpp_arg {
570 	struct ui_browser *b;
571 	char folded_sign;
572 	bool current_entry;
573 };
574 
__hpp__color_callchain(struct hpp_arg * arg)575 static int __hpp__color_callchain(struct hpp_arg *arg)
576 {
577 	if (!symbol_conf.use_callchain)
578 		return 0;
579 
580 	slsmg_printf("%c ", arg->folded_sign);
581 	return 2;
582 }
583 
__hpp__color_fmt(struct perf_hpp * hpp,struct hist_entry * he,u64 (* get_field)(struct hist_entry *),int (* callchain_cb)(struct hpp_arg *))584 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
585 			    u64 (*get_field)(struct hist_entry *),
586 			    int (*callchain_cb)(struct hpp_arg *))
587 {
588 	int ret = 0;
589 	double percent = 0.0;
590 	struct hists *hists = he->hists;
591 	struct hpp_arg *arg = hpp->ptr;
592 
593 	if (hists->stats.total_period)
594 		percent = 100.0 * get_field(he) / hists->stats.total_period;
595 
596 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
597 
598 	if (callchain_cb)
599 		ret += callchain_cb(arg);
600 
601 	ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
602 	slsmg_printf("%s", hpp->buf);
603 
604 	if (symbol_conf.event_group) {
605 		int prev_idx, idx_delta;
606 		struct perf_evsel *evsel = hists_to_evsel(hists);
607 		struct hist_entry *pair;
608 		int nr_members = evsel->nr_members;
609 
610 		if (nr_members <= 1)
611 			goto out;
612 
613 		prev_idx = perf_evsel__group_idx(evsel);
614 
615 		list_for_each_entry(pair, &he->pairs.head, pairs.node) {
616 			u64 period = get_field(pair);
617 			u64 total = pair->hists->stats.total_period;
618 
619 			if (!total)
620 				continue;
621 
622 			evsel = hists_to_evsel(pair->hists);
623 			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
624 
625 			while (idx_delta--) {
626 				/*
627 				 * zero-fill group members in the middle which
628 				 * have no sample
629 				 */
630 				ui_browser__set_percent_color(arg->b, 0.0,
631 							arg->current_entry);
632 				ret += scnprintf(hpp->buf, hpp->size,
633 						 " %6.2f%%", 0.0);
634 				slsmg_printf("%s", hpp->buf);
635 			}
636 
637 			percent = 100.0 * period / total;
638 			ui_browser__set_percent_color(arg->b, percent,
639 						      arg->current_entry);
640 			ret += scnprintf(hpp->buf, hpp->size,
641 					 " %6.2f%%", percent);
642 			slsmg_printf("%s", hpp->buf);
643 
644 			prev_idx = perf_evsel__group_idx(evsel);
645 		}
646 
647 		idx_delta = nr_members - prev_idx - 1;
648 
649 		while (idx_delta--) {
650 			/*
651 			 * zero-fill group members at last which have no sample
652 			 */
653 			ui_browser__set_percent_color(arg->b, 0.0,
654 						      arg->current_entry);
655 			ret += scnprintf(hpp->buf, hpp->size,
656 					 " %6.2f%%", 0.0);
657 			slsmg_printf("%s", hpp->buf);
658 		}
659 	}
660 out:
661 	if (!arg->current_entry || !arg->b->navkeypressed)
662 		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
663 
664 	return ret;
665 }
666 
667 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)			\
668 static u64 __hpp_get_##_field(struct hist_entry *he)			\
669 {									\
670 	return he->stat._field;						\
671 }									\
672 									\
673 static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp,	\
674 					   struct hist_entry *he)	\
675 {									\
676 	return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);	\
677 }
678 
__HPP_COLOR_PERCENT_FN(overhead,period,__hpp__color_callchain)679 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
680 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
681 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
682 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
683 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
684 
685 #undef __HPP_COLOR_PERCENT_FN
686 
687 void hist_browser__init_hpp(void)
688 {
689 	perf_hpp__column_enable(PERF_HPP__OVERHEAD);
690 
691 	perf_hpp__init();
692 
693 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
694 				hist_browser__hpp_color_overhead;
695 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
696 				hist_browser__hpp_color_overhead_sys;
697 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
698 				hist_browser__hpp_color_overhead_us;
699 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
700 				hist_browser__hpp_color_overhead_guest_sys;
701 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
702 				hist_browser__hpp_color_overhead_guest_us;
703 }
704 
hist_browser__show_entry(struct hist_browser * browser,struct hist_entry * entry,unsigned short row)705 static int hist_browser__show_entry(struct hist_browser *browser,
706 				    struct hist_entry *entry,
707 				    unsigned short row)
708 {
709 	char s[256];
710 	int printed = 0;
711 	int width = browser->b.width;
712 	char folded_sign = ' ';
713 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
714 	off_t row_offset = entry->row_offset;
715 	bool first = true;
716 	struct perf_hpp_fmt *fmt;
717 
718 	if (current_entry) {
719 		browser->he_selection = entry;
720 		browser->selection = &entry->ms;
721 	}
722 
723 	if (symbol_conf.use_callchain) {
724 		hist_entry__init_have_children(entry);
725 		folded_sign = hist_entry__folded(entry);
726 	}
727 
728 	if (row_offset == 0) {
729 		struct hpp_arg arg = {
730 			.b 		= &browser->b,
731 			.folded_sign	= folded_sign,
732 			.current_entry	= current_entry,
733 		};
734 		struct perf_hpp hpp = {
735 			.buf		= s,
736 			.size		= sizeof(s),
737 			.ptr		= &arg,
738 		};
739 
740 		ui_browser__gotorc(&browser->b, row, 0);
741 
742 		perf_hpp__for_each_format(fmt) {
743 			if (!first) {
744 				slsmg_printf("  ");
745 				width -= 2;
746 			}
747 			first = false;
748 
749 			if (fmt->color) {
750 				width -= fmt->color(&hpp, entry);
751 			} else {
752 				width -= fmt->entry(&hpp, entry);
753 				slsmg_printf("%s", s);
754 			}
755 		}
756 
757 		/* The scroll bar isn't being used */
758 		if (!browser->b.navkeypressed)
759 			width += 1;
760 
761 		hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
762 		slsmg_write_nstring(s, width);
763 		++row;
764 		++printed;
765 	} else
766 		--row_offset;
767 
768 	if (folded_sign == '-' && row != browser->b.height) {
769 		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
770 							1, row, &row_offset,
771 							&current_entry);
772 		if (current_entry)
773 			browser->he_selection = entry;
774 	}
775 
776 	return printed;
777 }
778 
ui_browser__hists_init_top(struct ui_browser * browser)779 static void ui_browser__hists_init_top(struct ui_browser *browser)
780 {
781 	if (browser->top == NULL) {
782 		struct hist_browser *hb;
783 
784 		hb = container_of(browser, struct hist_browser, b);
785 		browser->top = rb_first(&hb->hists->entries);
786 	}
787 }
788 
hist_browser__refresh(struct ui_browser * browser)789 static unsigned int hist_browser__refresh(struct ui_browser *browser)
790 {
791 	unsigned row = 0;
792 	struct rb_node *nd;
793 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
794 
795 	ui_browser__hists_init_top(browser);
796 
797 	for (nd = browser->top; nd; nd = rb_next(nd)) {
798 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
799 
800 		if (h->filtered)
801 			continue;
802 
803 		row += hist_browser__show_entry(hb, h, row);
804 		if (row == browser->height)
805 			break;
806 	}
807 
808 	return row;
809 }
810 
hists__filter_entries(struct rb_node * nd)811 static struct rb_node *hists__filter_entries(struct rb_node *nd)
812 {
813 	while (nd != NULL) {
814 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
815 		if (!h->filtered)
816 			return nd;
817 
818 		nd = rb_next(nd);
819 	}
820 
821 	return NULL;
822 }
823 
hists__filter_prev_entries(struct rb_node * nd)824 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
825 {
826 	while (nd != NULL) {
827 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
828 		if (!h->filtered)
829 			return nd;
830 
831 		nd = rb_prev(nd);
832 	}
833 
834 	return NULL;
835 }
836 
ui_browser__hists_seek(struct ui_browser * browser,off_t offset,int whence)837 static void ui_browser__hists_seek(struct ui_browser *browser,
838 				   off_t offset, int whence)
839 {
840 	struct hist_entry *h;
841 	struct rb_node *nd;
842 	bool first = true;
843 
844 	if (browser->nr_entries == 0)
845 		return;
846 
847 	ui_browser__hists_init_top(browser);
848 
849 	switch (whence) {
850 	case SEEK_SET:
851 		nd = hists__filter_entries(rb_first(browser->entries));
852 		break;
853 	case SEEK_CUR:
854 		nd = browser->top;
855 		goto do_offset;
856 	case SEEK_END:
857 		nd = hists__filter_prev_entries(rb_last(browser->entries));
858 		first = false;
859 		break;
860 	default:
861 		return;
862 	}
863 
864 	/*
865 	 * Moves not relative to the first visible entry invalidates its
866 	 * row_offset:
867 	 */
868 	h = rb_entry(browser->top, struct hist_entry, rb_node);
869 	h->row_offset = 0;
870 
871 	/*
872 	 * Here we have to check if nd is expanded (+), if it is we can't go
873 	 * the next top level hist_entry, instead we must compute an offset of
874 	 * what _not_ to show and not change the first visible entry.
875 	 *
876 	 * This offset increments when we are going from top to bottom and
877 	 * decreases when we're going from bottom to top.
878 	 *
879 	 * As we don't have backpointers to the top level in the callchains
880 	 * structure, we need to always print the whole hist_entry callchain,
881 	 * skipping the first ones that are before the first visible entry
882 	 * and stop when we printed enough lines to fill the screen.
883 	 */
884 do_offset:
885 	if (offset > 0) {
886 		do {
887 			h = rb_entry(nd, struct hist_entry, rb_node);
888 			if (h->ms.unfolded) {
889 				u16 remaining = h->nr_rows - h->row_offset;
890 				if (offset > remaining) {
891 					offset -= remaining;
892 					h->row_offset = 0;
893 				} else {
894 					h->row_offset += offset;
895 					offset = 0;
896 					browser->top = nd;
897 					break;
898 				}
899 			}
900 			nd = hists__filter_entries(rb_next(nd));
901 			if (nd == NULL)
902 				break;
903 			--offset;
904 			browser->top = nd;
905 		} while (offset != 0);
906 	} else if (offset < 0) {
907 		while (1) {
908 			h = rb_entry(nd, struct hist_entry, rb_node);
909 			if (h->ms.unfolded) {
910 				if (first) {
911 					if (-offset > h->row_offset) {
912 						offset += h->row_offset;
913 						h->row_offset = 0;
914 					} else {
915 						h->row_offset += offset;
916 						offset = 0;
917 						browser->top = nd;
918 						break;
919 					}
920 				} else {
921 					if (-offset > h->nr_rows) {
922 						offset += h->nr_rows;
923 						h->row_offset = 0;
924 					} else {
925 						h->row_offset = h->nr_rows + offset;
926 						offset = 0;
927 						browser->top = nd;
928 						break;
929 					}
930 				}
931 			}
932 
933 			nd = hists__filter_prev_entries(rb_prev(nd));
934 			if (nd == NULL)
935 				break;
936 			++offset;
937 			browser->top = nd;
938 			if (offset == 0) {
939 				/*
940 				 * Last unfiltered hist_entry, check if it is
941 				 * unfolded, if it is then we should have
942 				 * row_offset at its last entry.
943 				 */
944 				h = rb_entry(nd, struct hist_entry, rb_node);
945 				if (h->ms.unfolded)
946 					h->row_offset = h->nr_rows;
947 				break;
948 			}
949 			first = false;
950 		}
951 	} else {
952 		browser->top = nd;
953 		h = rb_entry(nd, struct hist_entry, rb_node);
954 		h->row_offset = 0;
955 	}
956 }
957 
hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser * browser,struct callchain_node * chain_node,u64 total,int level,FILE * fp)958 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
959 							struct callchain_node *chain_node,
960 							u64 total, int level,
961 							FILE *fp)
962 {
963 	struct rb_node *node;
964 	int offset = level * LEVEL_OFFSET_STEP;
965 	u64 new_total, remaining;
966 	int printed = 0;
967 
968 	if (callchain_param.mode == CHAIN_GRAPH_REL)
969 		new_total = chain_node->children_hit;
970 	else
971 		new_total = total;
972 
973 	remaining = new_total;
974 	node = rb_first(&chain_node->rb_root);
975 	while (node) {
976 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
977 		struct rb_node *next = rb_next(node);
978 		u64 cumul = callchain_cumul_hits(child);
979 		struct callchain_list *chain;
980 		char folded_sign = ' ';
981 		int first = true;
982 		int extra_offset = 0;
983 
984 		remaining -= cumul;
985 
986 		list_for_each_entry(chain, &child->val, list) {
987 			char bf[1024], *alloc_str;
988 			const char *str;
989 			bool was_first = first;
990 
991 			if (first)
992 				first = false;
993 			else
994 				extra_offset = LEVEL_OFFSET_STEP;
995 
996 			folded_sign = callchain_list__folded(chain);
997 
998 			alloc_str = NULL;
999 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
1000 						       browser->show_dso);
1001 			if (was_first) {
1002 				double percent = cumul * 100.0 / new_total;
1003 
1004 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1005 					str = "Not enough memory!";
1006 				else
1007 					str = alloc_str;
1008 			}
1009 
1010 			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1011 			free(alloc_str);
1012 			if (folded_sign == '+')
1013 				break;
1014 		}
1015 
1016 		if (folded_sign == '-') {
1017 			const int new_level = level + (extra_offset ? 2 : 1);
1018 			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1019 										new_level, fp);
1020 		}
1021 
1022 		node = next;
1023 	}
1024 
1025 	return printed;
1026 }
1027 
hist_browser__fprintf_callchain_node(struct hist_browser * browser,struct callchain_node * node,int level,FILE * fp)1028 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1029 						struct callchain_node *node,
1030 						int level, FILE *fp)
1031 {
1032 	struct callchain_list *chain;
1033 	int offset = level * LEVEL_OFFSET_STEP;
1034 	char folded_sign = ' ';
1035 	int printed = 0;
1036 
1037 	list_for_each_entry(chain, &node->val, list) {
1038 		char bf[1024], *s;
1039 
1040 		folded_sign = callchain_list__folded(chain);
1041 		s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1042 		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1043 	}
1044 
1045 	if (folded_sign == '-')
1046 		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1047 									browser->hists->stats.total_period,
1048 									level + 1,  fp);
1049 	return printed;
1050 }
1051 
hist_browser__fprintf_callchain(struct hist_browser * browser,struct rb_root * chain,int level,FILE * fp)1052 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1053 					   struct rb_root *chain, int level, FILE *fp)
1054 {
1055 	struct rb_node *nd;
1056 	int printed = 0;
1057 
1058 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1059 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1060 
1061 		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1062 	}
1063 
1064 	return printed;
1065 }
1066 
hist_browser__fprintf_entry(struct hist_browser * browser,struct hist_entry * he,FILE * fp)1067 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1068 				       struct hist_entry *he, FILE *fp)
1069 {
1070 	char s[8192];
1071 	double percent;
1072 	int printed = 0;
1073 	char folded_sign = ' ';
1074 
1075 	if (symbol_conf.use_callchain)
1076 		folded_sign = hist_entry__folded(he);
1077 
1078 	hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1079 	percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1080 
1081 	if (symbol_conf.use_callchain)
1082 		printed += fprintf(fp, "%c ", folded_sign);
1083 
1084 	printed += fprintf(fp, " %5.2f%%", percent);
1085 
1086 	if (symbol_conf.show_nr_samples)
1087 		printed += fprintf(fp, " %11u", he->stat.nr_events);
1088 
1089 	if (symbol_conf.show_total_period)
1090 		printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1091 
1092 	printed += fprintf(fp, "%s\n", rtrim(s));
1093 
1094 	if (folded_sign == '-')
1095 		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1096 
1097 	return printed;
1098 }
1099 
hist_browser__fprintf(struct hist_browser * browser,FILE * fp)1100 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1101 {
1102 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
1103 	int printed = 0;
1104 
1105 	while (nd) {
1106 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1107 
1108 		printed += hist_browser__fprintf_entry(browser, h, fp);
1109 		nd = hists__filter_entries(rb_next(nd));
1110 	}
1111 
1112 	return printed;
1113 }
1114 
hist_browser__dump(struct hist_browser * browser)1115 static int hist_browser__dump(struct hist_browser *browser)
1116 {
1117 	char filename[64];
1118 	FILE *fp;
1119 
1120 	while (1) {
1121 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1122 		if (access(filename, F_OK))
1123 			break;
1124 		/*
1125  		 * XXX: Just an arbitrary lazy upper limit
1126  		 */
1127 		if (++browser->print_seq == 8192) {
1128 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1129 			return -1;
1130 		}
1131 	}
1132 
1133 	fp = fopen(filename, "w");
1134 	if (fp == NULL) {
1135 		char bf[64];
1136 		const char *err = strerror_r(errno, bf, sizeof(bf));
1137 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1138 		return -1;
1139 	}
1140 
1141 	++browser->print_seq;
1142 	hist_browser__fprintf(browser, fp);
1143 	fclose(fp);
1144 	ui_helpline__fpush("%s written!", filename);
1145 
1146 	return 0;
1147 }
1148 
hist_browser__new(struct hists * hists)1149 static struct hist_browser *hist_browser__new(struct hists *hists)
1150 {
1151 	struct hist_browser *browser = zalloc(sizeof(*browser));
1152 
1153 	if (browser) {
1154 		browser->hists = hists;
1155 		browser->b.refresh = hist_browser__refresh;
1156 		browser->b.seek = ui_browser__hists_seek;
1157 		browser->b.use_navkeypressed = true;
1158 		if (sort__branch_mode == 1)
1159 			browser->has_symbols = sort_sym_from.list.next != NULL;
1160 		else
1161 			browser->has_symbols = sort_sym.list.next != NULL;
1162 	}
1163 
1164 	return browser;
1165 }
1166 
hist_browser__delete(struct hist_browser * browser)1167 static void hist_browser__delete(struct hist_browser *browser)
1168 {
1169 	free(browser);
1170 }
1171 
hist_browser__selected_entry(struct hist_browser * browser)1172 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1173 {
1174 	return browser->he_selection;
1175 }
1176 
hist_browser__selected_thread(struct hist_browser * browser)1177 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1178 {
1179 	return browser->he_selection->thread;
1180 }
1181 
hists__browser_title(struct hists * hists,char * bf,size_t size,const char * ev_name)1182 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1183 				const char *ev_name)
1184 {
1185 	char unit;
1186 	int printed;
1187 	const struct dso *dso = hists->dso_filter;
1188 	const struct thread *thread = hists->thread_filter;
1189 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1190 	u64 nr_events = hists->stats.total_period;
1191 	struct perf_evsel *evsel = hists_to_evsel(hists);
1192 	char buf[512];
1193 	size_t buflen = sizeof(buf);
1194 
1195 	if (perf_evsel__is_group_event(evsel)) {
1196 		struct perf_evsel *pos;
1197 
1198 		perf_evsel__group_desc(evsel, buf, buflen);
1199 		ev_name = buf;
1200 
1201 		for_each_group_member(pos, evsel) {
1202 			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1203 			nr_events += pos->hists.stats.total_period;
1204 		}
1205 	}
1206 
1207 	nr_samples = convert_unit(nr_samples, &unit);
1208 	printed = scnprintf(bf, size,
1209 			   "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1210 			   nr_samples, unit, ev_name, nr_events);
1211 
1212 
1213 	if (hists->uid_filter_str)
1214 		printed += snprintf(bf + printed, size - printed,
1215 				    ", UID: %s", hists->uid_filter_str);
1216 	if (thread)
1217 		printed += scnprintf(bf + printed, size - printed,
1218 				    ", Thread: %s(%d)",
1219 				    (thread->comm_set ? thread->comm : ""),
1220 				    thread->pid);
1221 	if (dso)
1222 		printed += scnprintf(bf + printed, size - printed,
1223 				    ", DSO: %s", dso->short_name);
1224 	return printed;
1225 }
1226 
free_popup_options(char ** options,int n)1227 static inline void free_popup_options(char **options, int n)
1228 {
1229 	int i;
1230 
1231 	for (i = 0; i < n; ++i) {
1232 		free(options[i]);
1233 		options[i] = NULL;
1234 	}
1235 }
1236 
1237 /* Check whether the browser is for 'top' or 'report' */
is_report_browser(void * timer)1238 static inline bool is_report_browser(void *timer)
1239 {
1240 	return timer == NULL;
1241 }
1242 
1243 /*
1244  * Only runtime switching of perf data file will make "input_name" point
1245  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1246  * whether we need to call free() for current "input_name" during the switch.
1247  */
1248 static bool is_input_name_malloced = false;
1249 
switch_data_file(void)1250 static int switch_data_file(void)
1251 {
1252 	char *pwd, *options[32], *abs_path[32], *tmp;
1253 	DIR *pwd_dir;
1254 	int nr_options = 0, choice = -1, ret = -1;
1255 	struct dirent *dent;
1256 
1257 	pwd = getenv("PWD");
1258 	if (!pwd)
1259 		return ret;
1260 
1261 	pwd_dir = opendir(pwd);
1262 	if (!pwd_dir)
1263 		return ret;
1264 
1265 	memset(options, 0, sizeof(options));
1266 	memset(options, 0, sizeof(abs_path));
1267 
1268 	while ((dent = readdir(pwd_dir))) {
1269 		char path[PATH_MAX];
1270 		u64 magic;
1271 		char *name = dent->d_name;
1272 		FILE *file;
1273 
1274 		if (!(dent->d_type == DT_REG))
1275 			continue;
1276 
1277 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
1278 
1279 		file = fopen(path, "r");
1280 		if (!file)
1281 			continue;
1282 
1283 		if (fread(&magic, 1, 8, file) < 8)
1284 			goto close_file_and_continue;
1285 
1286 		if (is_perf_magic(magic)) {
1287 			options[nr_options] = strdup(name);
1288 			if (!options[nr_options])
1289 				goto close_file_and_continue;
1290 
1291 			abs_path[nr_options] = strdup(path);
1292 			if (!abs_path[nr_options]) {
1293 				free(options[nr_options]);
1294 				ui__warning("Can't search all data files due to memory shortage.\n");
1295 				fclose(file);
1296 				break;
1297 			}
1298 
1299 			nr_options++;
1300 		}
1301 
1302 close_file_and_continue:
1303 		fclose(file);
1304 		if (nr_options >= 32) {
1305 			ui__warning("Too many perf data files in PWD!\n"
1306 				    "Only the first 32 files will be listed.\n");
1307 			break;
1308 		}
1309 	}
1310 	closedir(pwd_dir);
1311 
1312 	if (nr_options) {
1313 		choice = ui__popup_menu(nr_options, options);
1314 		if (choice < nr_options && choice >= 0) {
1315 			tmp = strdup(abs_path[choice]);
1316 			if (tmp) {
1317 				if (is_input_name_malloced)
1318 					free((void *)input_name);
1319 				input_name = tmp;
1320 				is_input_name_malloced = true;
1321 				ret = 0;
1322 			} else
1323 				ui__warning("Data switch failed due to memory shortage!\n");
1324 		}
1325 	}
1326 
1327 	free_popup_options(options, nr_options);
1328 	free_popup_options(abs_path, nr_options);
1329 	return ret;
1330 }
1331 
1332 
perf_evsel__hists_browse(struct perf_evsel * evsel,int nr_events,const char * helpline,const char * ev_name,bool left_exits,struct hist_browser_timer * hbt,struct perf_session_env * env)1333 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1334 				    const char *helpline, const char *ev_name,
1335 				    bool left_exits,
1336 				    struct hist_browser_timer *hbt,
1337 				    struct perf_session_env *env)
1338 {
1339 	struct hists *hists = &evsel->hists;
1340 	struct hist_browser *browser = hist_browser__new(hists);
1341 	struct branch_info *bi;
1342 	struct pstack *fstack;
1343 	char *options[16];
1344 	int nr_options = 0;
1345 	int key = -1;
1346 	char buf[64];
1347 	char script_opt[64];
1348 	int delay_secs = hbt ? hbt->refresh : 0;
1349 
1350 	if (browser == NULL)
1351 		return -1;
1352 
1353 	fstack = pstack__new(2);
1354 	if (fstack == NULL)
1355 		goto out;
1356 
1357 	ui_helpline__push(helpline);
1358 
1359 	memset(options, 0, sizeof(options));
1360 
1361 	while (1) {
1362 		const struct thread *thread = NULL;
1363 		const struct dso *dso = NULL;
1364 		int choice = 0,
1365 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1366 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
1367 		int scripts_comm = -2, scripts_symbol = -2,
1368 		    scripts_all = -2, switch_data = -2;
1369 
1370 		nr_options = 0;
1371 
1372 		key = hist_browser__run(browser, ev_name, hbt);
1373 
1374 		if (browser->he_selection != NULL) {
1375 			thread = hist_browser__selected_thread(browser);
1376 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
1377 		}
1378 		switch (key) {
1379 		case K_TAB:
1380 		case K_UNTAB:
1381 			if (nr_events == 1)
1382 				continue;
1383 			/*
1384 			 * Exit the browser, let hists__browser_tree
1385 			 * go to the next or previous
1386 			 */
1387 			goto out_free_stack;
1388 		case 'a':
1389 			if (!browser->has_symbols) {
1390 				ui_browser__warning(&browser->b, delay_secs * 2,
1391 			"Annotation is only available for symbolic views, "
1392 			"include \"sym*\" in --sort to use it.");
1393 				continue;
1394 			}
1395 
1396 			if (browser->selection == NULL ||
1397 			    browser->selection->sym == NULL ||
1398 			    browser->selection->map->dso->annotate_warned)
1399 				continue;
1400 			goto do_annotate;
1401 		case 'P':
1402 			hist_browser__dump(browser);
1403 			continue;
1404 		case 'd':
1405 			goto zoom_dso;
1406 		case 'V':
1407 			browser->show_dso = !browser->show_dso;
1408 			continue;
1409 		case 't':
1410 			goto zoom_thread;
1411 		case '/':
1412 			if (ui_browser__input_window("Symbol to show",
1413 					"Please enter the name of symbol you want to see",
1414 					buf, "ENTER: OK, ESC: Cancel",
1415 					delay_secs * 2) == K_ENTER) {
1416 				hists->symbol_filter_str = *buf ? buf : NULL;
1417 				hists__filter_by_symbol(hists);
1418 				hist_browser__reset(browser);
1419 			}
1420 			continue;
1421 		case 'r':
1422 			if (is_report_browser(hbt))
1423 				goto do_scripts;
1424 			continue;
1425 		case 's':
1426 			if (is_report_browser(hbt))
1427 				goto do_data_switch;
1428 			continue;
1429 		case K_F1:
1430 		case 'h':
1431 		case '?':
1432 			ui_browser__help_window(&browser->b,
1433 					"h/?/F1        Show this window\n"
1434 					"UP/DOWN/PGUP\n"
1435 					"PGDN/SPACE    Navigate\n"
1436 					"q/ESC/CTRL+C  Exit browser\n\n"
1437 					"For multiple event sessions:\n\n"
1438 					"TAB/UNTAB Switch events\n\n"
1439 					"For symbolic views (--sort has sym):\n\n"
1440 					"->            Zoom into DSO/Threads & Annotate current symbol\n"
1441 					"<-            Zoom out\n"
1442 					"a             Annotate current symbol\n"
1443 					"C             Collapse all callchains\n"
1444 					"E             Expand all callchains\n"
1445 					"d             Zoom into current DSO\n"
1446 					"t             Zoom into current Thread\n"
1447 					"r             Run available scripts('perf report' only)\n"
1448 					"s             Switch to another data file in PWD ('perf report' only)\n"
1449 					"P             Print histograms to perf.hist.N\n"
1450 					"V             Verbose (DSO names in callchains, etc)\n"
1451 					"/             Filter symbol by name");
1452 			continue;
1453 		case K_ENTER:
1454 		case K_RIGHT:
1455 			/* menu */
1456 			break;
1457 		case K_LEFT: {
1458 			const void *top;
1459 
1460 			if (pstack__empty(fstack)) {
1461 				/*
1462 				 * Go back to the perf_evsel_menu__run or other user
1463 				 */
1464 				if (left_exits)
1465 					goto out_free_stack;
1466 				continue;
1467 			}
1468 			top = pstack__pop(fstack);
1469 			if (top == &browser->hists->dso_filter)
1470 				goto zoom_out_dso;
1471 			if (top == &browser->hists->thread_filter)
1472 				goto zoom_out_thread;
1473 			continue;
1474 		}
1475 		case K_ESC:
1476 			if (!left_exits &&
1477 			    !ui_browser__dialog_yesno(&browser->b,
1478 					       "Do you really want to exit?"))
1479 				continue;
1480 			/* Fall thru */
1481 		case 'q':
1482 		case CTRL('c'):
1483 			goto out_free_stack;
1484 		default:
1485 			continue;
1486 		}
1487 
1488 		if (!browser->has_symbols)
1489 			goto add_exit_option;
1490 
1491 		if (sort__branch_mode == 1) {
1492 			bi = browser->he_selection->branch_info;
1493 			if (browser->selection != NULL &&
1494 			    bi &&
1495 			    bi->from.sym != NULL &&
1496 			    !bi->from.map->dso->annotate_warned &&
1497 				asprintf(&options[nr_options], "Annotate %s",
1498 					 bi->from.sym->name) > 0)
1499 				annotate_f = nr_options++;
1500 
1501 			if (browser->selection != NULL &&
1502 			    bi &&
1503 			    bi->to.sym != NULL &&
1504 			    !bi->to.map->dso->annotate_warned &&
1505 			    (bi->to.sym != bi->from.sym ||
1506 			     bi->to.map->dso != bi->from.map->dso) &&
1507 				asprintf(&options[nr_options], "Annotate %s",
1508 					 bi->to.sym->name) > 0)
1509 				annotate_t = nr_options++;
1510 		} else {
1511 
1512 			if (browser->selection != NULL &&
1513 			    browser->selection->sym != NULL &&
1514 			    !browser->selection->map->dso->annotate_warned &&
1515 				asprintf(&options[nr_options], "Annotate %s",
1516 					 browser->selection->sym->name) > 0)
1517 				annotate = nr_options++;
1518 		}
1519 
1520 		if (thread != NULL &&
1521 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1522 			     (browser->hists->thread_filter ? "out of" : "into"),
1523 			     (thread->comm_set ? thread->comm : ""),
1524 			     thread->pid) > 0)
1525 			zoom_thread = nr_options++;
1526 
1527 		if (dso != NULL &&
1528 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
1529 			     (browser->hists->dso_filter ? "out of" : "into"),
1530 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1531 			zoom_dso = nr_options++;
1532 
1533 		if (browser->selection != NULL &&
1534 		    browser->selection->map != NULL &&
1535 		    asprintf(&options[nr_options], "Browse map details") > 0)
1536 			browse_map = nr_options++;
1537 
1538 		/* perf script support */
1539 		if (browser->he_selection) {
1540 			struct symbol *sym;
1541 
1542 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1543 				browser->he_selection->thread->comm) > 0)
1544 				scripts_comm = nr_options++;
1545 
1546 			sym = browser->he_selection->ms.sym;
1547 			if (sym && sym->namelen &&
1548 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1549 						sym->name) > 0)
1550 				scripts_symbol = nr_options++;
1551 		}
1552 
1553 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1554 			scripts_all = nr_options++;
1555 
1556 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
1557 				"Switch to another data file in PWD") > 0)
1558 			switch_data = nr_options++;
1559 add_exit_option:
1560 		options[nr_options++] = (char *)"Exit";
1561 retry_popup_menu:
1562 		choice = ui__popup_menu(nr_options, options);
1563 
1564 		if (choice == nr_options - 1)
1565 			break;
1566 
1567 		if (choice == -1) {
1568 			free_popup_options(options, nr_options - 1);
1569 			continue;
1570 		}
1571 
1572 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1573 			struct hist_entry *he;
1574 			int err;
1575 do_annotate:
1576 			if (!objdump_path && perf_session_env__lookup_objdump(env))
1577 				continue;
1578 
1579 			he = hist_browser__selected_entry(browser);
1580 			if (he == NULL)
1581 				continue;
1582 
1583 			/*
1584 			 * we stash the branch_info symbol + map into the
1585 			 * the ms so we don't have to rewrite all the annotation
1586 			 * code to use branch_info.
1587 			 * in branch mode, the ms struct is not used
1588 			 */
1589 			if (choice == annotate_f) {
1590 				he->ms.sym = he->branch_info->from.sym;
1591 				he->ms.map = he->branch_info->from.map;
1592 			}  else if (choice == annotate_t) {
1593 				he->ms.sym = he->branch_info->to.sym;
1594 				he->ms.map = he->branch_info->to.map;
1595 			}
1596 
1597 			/*
1598 			 * Don't let this be freed, say, by hists__decay_entry.
1599 			 */
1600 			he->used = true;
1601 			err = hist_entry__tui_annotate(he, evsel, hbt);
1602 			he->used = false;
1603 			/*
1604 			 * offer option to annotate the other branch source or target
1605 			 * (if they exists) when returning from annotate
1606 			 */
1607 			if ((err == 'q' || err == CTRL('c'))
1608 			    && annotate_t != -2 && annotate_f != -2)
1609 				goto retry_popup_menu;
1610 
1611 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1612 			if (err)
1613 				ui_browser__handle_resize(&browser->b);
1614 
1615 		} else if (choice == browse_map)
1616 			map__browse(browser->selection->map);
1617 		else if (choice == zoom_dso) {
1618 zoom_dso:
1619 			if (browser->hists->dso_filter) {
1620 				pstack__remove(fstack, &browser->hists->dso_filter);
1621 zoom_out_dso:
1622 				ui_helpline__pop();
1623 				browser->hists->dso_filter = NULL;
1624 				sort_dso.elide = false;
1625 			} else {
1626 				if (dso == NULL)
1627 					continue;
1628 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1629 						   dso->kernel ? "the Kernel" : dso->short_name);
1630 				browser->hists->dso_filter = dso;
1631 				sort_dso.elide = true;
1632 				pstack__push(fstack, &browser->hists->dso_filter);
1633 			}
1634 			hists__filter_by_dso(hists);
1635 			hist_browser__reset(browser);
1636 		} else if (choice == zoom_thread) {
1637 zoom_thread:
1638 			if (browser->hists->thread_filter) {
1639 				pstack__remove(fstack, &browser->hists->thread_filter);
1640 zoom_out_thread:
1641 				ui_helpline__pop();
1642 				browser->hists->thread_filter = NULL;
1643 				sort_thread.elide = false;
1644 			} else {
1645 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1646 						   thread->comm_set ? thread->comm : "",
1647 						   thread->pid);
1648 				browser->hists->thread_filter = thread;
1649 				sort_thread.elide = true;
1650 				pstack__push(fstack, &browser->hists->thread_filter);
1651 			}
1652 			hists__filter_by_thread(hists);
1653 			hist_browser__reset(browser);
1654 		}
1655 		/* perf scripts support */
1656 		else if (choice == scripts_all || choice == scripts_comm ||
1657 				choice == scripts_symbol) {
1658 do_scripts:
1659 			memset(script_opt, 0, 64);
1660 
1661 			if (choice == scripts_comm)
1662 				sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1663 
1664 			if (choice == scripts_symbol)
1665 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1666 
1667 			script_browse(script_opt);
1668 		}
1669 		/* Switch to another data file */
1670 		else if (choice == switch_data) {
1671 do_data_switch:
1672 			if (!switch_data_file()) {
1673 				key = K_SWITCH_INPUT_DATA;
1674 				break;
1675 			} else
1676 				ui__warning("Won't switch the data files due to\n"
1677 					"no valid data file get selected!\n");
1678 		}
1679 	}
1680 out_free_stack:
1681 	pstack__delete(fstack);
1682 out:
1683 	hist_browser__delete(browser);
1684 	free_popup_options(options, nr_options - 1);
1685 	return key;
1686 }
1687 
1688 struct perf_evsel_menu {
1689 	struct ui_browser b;
1690 	struct perf_evsel *selection;
1691 	bool lost_events, lost_events_warned;
1692 	struct perf_session_env *env;
1693 };
1694 
perf_evsel_menu__write(struct ui_browser * browser,void * entry,int row)1695 static void perf_evsel_menu__write(struct ui_browser *browser,
1696 				   void *entry, int row)
1697 {
1698 	struct perf_evsel_menu *menu = container_of(browser,
1699 						    struct perf_evsel_menu, b);
1700 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1701 	bool current_entry = ui_browser__is_current_entry(browser, row);
1702 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1703 	const char *ev_name = perf_evsel__name(evsel);
1704 	char bf[256], unit;
1705 	const char *warn = " ";
1706 	size_t printed;
1707 
1708 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1709 						       HE_COLORSET_NORMAL);
1710 
1711 	if (perf_evsel__is_group_event(evsel)) {
1712 		struct perf_evsel *pos;
1713 
1714 		ev_name = perf_evsel__group_name(evsel);
1715 
1716 		for_each_group_member(pos, evsel) {
1717 			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1718 		}
1719 	}
1720 
1721 	nr_events = convert_unit(nr_events, &unit);
1722 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1723 			   unit, unit == ' ' ? "" : " ", ev_name);
1724 	slsmg_printf("%s", bf);
1725 
1726 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1727 	if (nr_events != 0) {
1728 		menu->lost_events = true;
1729 		if (!current_entry)
1730 			ui_browser__set_color(browser, HE_COLORSET_TOP);
1731 		nr_events = convert_unit(nr_events, &unit);
1732 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1733 				     nr_events, unit, unit == ' ' ? "" : " ");
1734 		warn = bf;
1735 	}
1736 
1737 	slsmg_write_nstring(warn, browser->width - printed);
1738 
1739 	if (current_entry)
1740 		menu->selection = evsel;
1741 }
1742 
perf_evsel_menu__run(struct perf_evsel_menu * menu,int nr_events,const char * help,struct hist_browser_timer * hbt)1743 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1744 				int nr_events, const char *help,
1745 				struct hist_browser_timer *hbt)
1746 {
1747 	struct perf_evlist *evlist = menu->b.priv;
1748 	struct perf_evsel *pos;
1749 	const char *ev_name, *title = "Available samples";
1750 	int delay_secs = hbt ? hbt->refresh : 0;
1751 	int key;
1752 
1753 	if (ui_browser__show(&menu->b, title,
1754 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
1755 		return -1;
1756 
1757 	while (1) {
1758 		key = ui_browser__run(&menu->b, delay_secs);
1759 
1760 		switch (key) {
1761 		case K_TIMER:
1762 			hbt->timer(hbt->arg);
1763 
1764 			if (!menu->lost_events_warned && menu->lost_events) {
1765 				ui_browser__warn_lost_events(&menu->b);
1766 				menu->lost_events_warned = true;
1767 			}
1768 			continue;
1769 		case K_RIGHT:
1770 		case K_ENTER:
1771 			if (!menu->selection)
1772 				continue;
1773 			pos = menu->selection;
1774 browse_hists:
1775 			perf_evlist__set_selected(evlist, pos);
1776 			/*
1777 			 * Give the calling tool a chance to populate the non
1778 			 * default evsel resorted hists tree.
1779 			 */
1780 			if (hbt)
1781 				hbt->timer(hbt->arg);
1782 			ev_name = perf_evsel__name(pos);
1783 			key = perf_evsel__hists_browse(pos, nr_events, help,
1784 						       ev_name, true, hbt,
1785 						       menu->env);
1786 			ui_browser__show_title(&menu->b, title);
1787 			switch (key) {
1788 			case K_TAB:
1789 				if (pos->node.next == &evlist->entries)
1790 					pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1791 				else
1792 					pos = list_entry(pos->node.next, struct perf_evsel, node);
1793 				goto browse_hists;
1794 			case K_UNTAB:
1795 				if (pos->node.prev == &evlist->entries)
1796 					pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1797 				else
1798 					pos = list_entry(pos->node.prev, struct perf_evsel, node);
1799 				goto browse_hists;
1800 			case K_ESC:
1801 				if (!ui_browser__dialog_yesno(&menu->b,
1802 						"Do you really want to exit?"))
1803 					continue;
1804 				/* Fall thru */
1805 			case K_SWITCH_INPUT_DATA:
1806 			case 'q':
1807 			case CTRL('c'):
1808 				goto out;
1809 			default:
1810 				continue;
1811 			}
1812 		case K_LEFT:
1813 			continue;
1814 		case K_ESC:
1815 			if (!ui_browser__dialog_yesno(&menu->b,
1816 					       "Do you really want to exit?"))
1817 				continue;
1818 			/* Fall thru */
1819 		case 'q':
1820 		case CTRL('c'):
1821 			goto out;
1822 		default:
1823 			continue;
1824 		}
1825 	}
1826 
1827 out:
1828 	ui_browser__hide(&menu->b);
1829 	return key;
1830 }
1831 
filter_group_entries(struct ui_browser * self __maybe_unused,void * entry)1832 static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1833 				 void *entry)
1834 {
1835 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1836 
1837 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1838 		return true;
1839 
1840 	return false;
1841 }
1842 
__perf_evlist__tui_browse_hists(struct perf_evlist * evlist,int nr_entries,const char * help,struct hist_browser_timer * hbt,struct perf_session_env * env)1843 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1844 					   int nr_entries, const char *help,
1845 					   struct hist_browser_timer *hbt,
1846 					   struct perf_session_env *env)
1847 {
1848 	struct perf_evsel *pos;
1849 	struct perf_evsel_menu menu = {
1850 		.b = {
1851 			.entries    = &evlist->entries,
1852 			.refresh    = ui_browser__list_head_refresh,
1853 			.seek	    = ui_browser__list_head_seek,
1854 			.write	    = perf_evsel_menu__write,
1855 			.filter	    = filter_group_entries,
1856 			.nr_entries = nr_entries,
1857 			.priv	    = evlist,
1858 		},
1859 		.env = env,
1860 	};
1861 
1862 	ui_helpline__push("Press ESC to exit");
1863 
1864 	list_for_each_entry(pos, &evlist->entries, node) {
1865 		const char *ev_name = perf_evsel__name(pos);
1866 		size_t line_len = strlen(ev_name) + 7;
1867 
1868 		if (menu.b.width < line_len)
1869 			menu.b.width = line_len;
1870 	}
1871 
1872 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1873 }
1874 
perf_evlist__tui_browse_hists(struct perf_evlist * evlist,const char * help,struct hist_browser_timer * hbt,struct perf_session_env * env)1875 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1876 				  struct hist_browser_timer *hbt,
1877 				  struct perf_session_env *env)
1878 {
1879 	int nr_entries = evlist->nr_entries;
1880 
1881 single_entry:
1882 	if (nr_entries == 1) {
1883 		struct perf_evsel *first = list_entry(evlist->entries.next,
1884 						      struct perf_evsel, node);
1885 		const char *ev_name = perf_evsel__name(first);
1886 
1887 		return perf_evsel__hists_browse(first, nr_entries, help,
1888 						ev_name, false, hbt, env);
1889 	}
1890 
1891 	if (symbol_conf.event_group) {
1892 		struct perf_evsel *pos;
1893 
1894 		nr_entries = 0;
1895 		list_for_each_entry(pos, &evlist->entries, node)
1896 			if (perf_evsel__is_group_leader(pos))
1897 				nr_entries++;
1898 
1899 		if (nr_entries == 1)
1900 			goto single_entry;
1901 	}
1902 
1903 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1904 					       hbt, env);
1905 }
1906