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