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