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