]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - tools/perf/ui/browsers/hists.c
Merge remote-tracking branches 'asoc/topic/tlv320aic3x', 'asoc/topic/width', 'asoc...
[mirror_ubuntu-bionic-kernel.git] / tools / perf / ui / browsers / hists.c
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <linux/rbtree.h>
6
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../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 #include "annotate.h"
21
22 struct hist_browser {
23 struct ui_browser b;
24 struct hists *hists;
25 struct hist_entry *he_selection;
26 struct map_symbol *selection;
27 int print_seq;
28 bool show_dso;
29 float min_pcnt;
30 u64 nr_non_filtered_entries;
31 u64 nr_callchain_rows;
32 };
33
34 extern void hist_browser__init_hpp(void);
35
36 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
37 const char *ev_name);
38 static void hist_browser__update_nr_entries(struct hist_browser *hb);
39
40 static struct rb_node *hists__filter_entries(struct rb_node *nd,
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, 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 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
655 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
656 { \
657 return he->stat_acc->_field; \
658 } \
659 \
660 static int \
661 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
662 struct perf_hpp *hpp, \
663 struct hist_entry *he) \
664 { \
665 if (!symbol_conf.cumulate_callchain) { \
666 int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
667 slsmg_printf("%s", hpp->buf); \
668 \
669 return ret; \
670 } \
671 return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \
672 __hpp__slsmg_color_printf, true); \
673 }
674
675 __HPP_COLOR_PERCENT_FN(overhead, period)
676 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
677 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
678 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
679 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
680 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
681
682 #undef __HPP_COLOR_PERCENT_FN
683 #undef __HPP_COLOR_ACC_PERCENT_FN
684
685 void hist_browser__init_hpp(void)
686 {
687 perf_hpp__format[PERF_HPP__OVERHEAD].color =
688 hist_browser__hpp_color_overhead;
689 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
690 hist_browser__hpp_color_overhead_sys;
691 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
692 hist_browser__hpp_color_overhead_us;
693 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
694 hist_browser__hpp_color_overhead_guest_sys;
695 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
696 hist_browser__hpp_color_overhead_guest_us;
697 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
698 hist_browser__hpp_color_overhead_acc;
699 }
700
701 static int hist_browser__show_entry(struct hist_browser *browser,
702 struct hist_entry *entry,
703 unsigned short row)
704 {
705 char s[256];
706 int printed = 0;
707 int width = browser->b.width;
708 char folded_sign = ' ';
709 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
710 off_t row_offset = entry->row_offset;
711 bool first = true;
712 struct perf_hpp_fmt *fmt;
713
714 if (current_entry) {
715 browser->he_selection = entry;
716 browser->selection = &entry->ms;
717 }
718
719 if (symbol_conf.use_callchain) {
720 hist_entry__init_have_children(entry);
721 folded_sign = hist_entry__folded(entry);
722 }
723
724 if (row_offset == 0) {
725 struct hpp_arg arg = {
726 .b = &browser->b,
727 .folded_sign = folded_sign,
728 .current_entry = current_entry,
729 };
730 struct perf_hpp hpp = {
731 .buf = s,
732 .size = sizeof(s),
733 .ptr = &arg,
734 };
735
736 ui_browser__gotorc(&browser->b, row, 0);
737
738 perf_hpp__for_each_format(fmt) {
739 if (perf_hpp__should_skip(fmt))
740 continue;
741
742 if (current_entry && browser->b.navkeypressed) {
743 ui_browser__set_color(&browser->b,
744 HE_COLORSET_SELECTED);
745 } else {
746 ui_browser__set_color(&browser->b,
747 HE_COLORSET_NORMAL);
748 }
749
750 if (first) {
751 if (symbol_conf.use_callchain) {
752 slsmg_printf("%c ", folded_sign);
753 width -= 2;
754 }
755 first = false;
756 } else {
757 slsmg_printf(" ");
758 width -= 2;
759 }
760
761 if (fmt->color) {
762 width -= fmt->color(fmt, &hpp, entry);
763 } else {
764 width -= fmt->entry(fmt, &hpp, entry);
765 slsmg_printf("%s", s);
766 }
767 }
768
769 /* The scroll bar isn't being used */
770 if (!browser->b.navkeypressed)
771 width += 1;
772
773 slsmg_write_nstring("", width);
774
775 ++row;
776 ++printed;
777 } else
778 --row_offset;
779
780 if (folded_sign == '-' && row != browser->b.height) {
781 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
782 1, row, &row_offset,
783 &current_entry);
784 if (current_entry)
785 browser->he_selection = entry;
786 }
787
788 return printed;
789 }
790
791 static void ui_browser__hists_init_top(struct ui_browser *browser)
792 {
793 if (browser->top == NULL) {
794 struct hist_browser *hb;
795
796 hb = container_of(browser, struct hist_browser, b);
797 browser->top = rb_first(&hb->hists->entries);
798 }
799 }
800
801 static unsigned int hist_browser__refresh(struct ui_browser *browser)
802 {
803 unsigned row = 0;
804 struct rb_node *nd;
805 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
806
807 ui_browser__hists_init_top(browser);
808
809 for (nd = browser->top; nd; nd = rb_next(nd)) {
810 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
811 float percent;
812
813 if (h->filtered)
814 continue;
815
816 percent = hist_entry__get_percent_limit(h);
817 if (percent < hb->min_pcnt)
818 continue;
819
820 row += hist_browser__show_entry(hb, h, row);
821 if (row == browser->height)
822 break;
823 }
824
825 return row;
826 }
827
828 static struct rb_node *hists__filter_entries(struct rb_node *nd,
829 float min_pcnt)
830 {
831 while (nd != NULL) {
832 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
833 float percent = hist_entry__get_percent_limit(h);
834
835 if (!h->filtered && percent >= min_pcnt)
836 return nd;
837
838 nd = rb_next(nd);
839 }
840
841 return NULL;
842 }
843
844 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
845 float min_pcnt)
846 {
847 while (nd != NULL) {
848 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
849 float percent = hist_entry__get_percent_limit(h);
850
851 if (!h->filtered && percent >= min_pcnt)
852 return nd;
853
854 nd = rb_prev(nd);
855 }
856
857 return NULL;
858 }
859
860 static void ui_browser__hists_seek(struct ui_browser *browser,
861 off_t offset, int whence)
862 {
863 struct hist_entry *h;
864 struct rb_node *nd;
865 bool first = true;
866 struct hist_browser *hb;
867
868 hb = container_of(browser, struct hist_browser, b);
869
870 if (browser->nr_entries == 0)
871 return;
872
873 ui_browser__hists_init_top(browser);
874
875 switch (whence) {
876 case SEEK_SET:
877 nd = hists__filter_entries(rb_first(browser->entries),
878 hb->min_pcnt);
879 break;
880 case SEEK_CUR:
881 nd = browser->top;
882 goto do_offset;
883 case SEEK_END:
884 nd = hists__filter_prev_entries(rb_last(browser->entries),
885 hb->min_pcnt);
886 first = false;
887 break;
888 default:
889 return;
890 }
891
892 /*
893 * Moves not relative to the first visible entry invalidates its
894 * row_offset:
895 */
896 h = rb_entry(browser->top, struct hist_entry, rb_node);
897 h->row_offset = 0;
898
899 /*
900 * Here we have to check if nd is expanded (+), if it is we can't go
901 * the next top level hist_entry, instead we must compute an offset of
902 * what _not_ to show and not change the first visible entry.
903 *
904 * This offset increments when we are going from top to bottom and
905 * decreases when we're going from bottom to top.
906 *
907 * As we don't have backpointers to the top level in the callchains
908 * structure, we need to always print the whole hist_entry callchain,
909 * skipping the first ones that are before the first visible entry
910 * and stop when we printed enough lines to fill the screen.
911 */
912 do_offset:
913 if (offset > 0) {
914 do {
915 h = rb_entry(nd, struct hist_entry, rb_node);
916 if (h->ms.unfolded) {
917 u16 remaining = h->nr_rows - h->row_offset;
918 if (offset > remaining) {
919 offset -= remaining;
920 h->row_offset = 0;
921 } else {
922 h->row_offset += offset;
923 offset = 0;
924 browser->top = nd;
925 break;
926 }
927 }
928 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
929 if (nd == NULL)
930 break;
931 --offset;
932 browser->top = nd;
933 } while (offset != 0);
934 } else if (offset < 0) {
935 while (1) {
936 h = rb_entry(nd, struct hist_entry, rb_node);
937 if (h->ms.unfolded) {
938 if (first) {
939 if (-offset > h->row_offset) {
940 offset += h->row_offset;
941 h->row_offset = 0;
942 } else {
943 h->row_offset += offset;
944 offset = 0;
945 browser->top = nd;
946 break;
947 }
948 } else {
949 if (-offset > h->nr_rows) {
950 offset += h->nr_rows;
951 h->row_offset = 0;
952 } else {
953 h->row_offset = h->nr_rows + offset;
954 offset = 0;
955 browser->top = nd;
956 break;
957 }
958 }
959 }
960
961 nd = hists__filter_prev_entries(rb_prev(nd),
962 hb->min_pcnt);
963 if (nd == NULL)
964 break;
965 ++offset;
966 browser->top = nd;
967 if (offset == 0) {
968 /*
969 * Last unfiltered hist_entry, check if it is
970 * unfolded, if it is then we should have
971 * row_offset at its last entry.
972 */
973 h = rb_entry(nd, struct hist_entry, rb_node);
974 if (h->ms.unfolded)
975 h->row_offset = h->nr_rows;
976 break;
977 }
978 first = false;
979 }
980 } else {
981 browser->top = nd;
982 h = rb_entry(nd, struct hist_entry, rb_node);
983 h->row_offset = 0;
984 }
985 }
986
987 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
988 struct callchain_node *chain_node,
989 u64 total, int level,
990 FILE *fp)
991 {
992 struct rb_node *node;
993 int offset = level * LEVEL_OFFSET_STEP;
994 u64 new_total, remaining;
995 int printed = 0;
996
997 if (callchain_param.mode == CHAIN_GRAPH_REL)
998 new_total = chain_node->children_hit;
999 else
1000 new_total = total;
1001
1002 remaining = new_total;
1003 node = rb_first(&chain_node->rb_root);
1004 while (node) {
1005 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1006 struct rb_node *next = rb_next(node);
1007 u64 cumul = callchain_cumul_hits(child);
1008 struct callchain_list *chain;
1009 char folded_sign = ' ';
1010 int first = true;
1011 int extra_offset = 0;
1012
1013 remaining -= cumul;
1014
1015 list_for_each_entry(chain, &child->val, list) {
1016 char bf[1024], *alloc_str;
1017 const char *str;
1018 bool was_first = first;
1019
1020 if (first)
1021 first = false;
1022 else
1023 extra_offset = LEVEL_OFFSET_STEP;
1024
1025 folded_sign = callchain_list__folded(chain);
1026
1027 alloc_str = NULL;
1028 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1029 browser->show_dso);
1030 if (was_first) {
1031 double percent = cumul * 100.0 / new_total;
1032
1033 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1034 str = "Not enough memory!";
1035 else
1036 str = alloc_str;
1037 }
1038
1039 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1040 free(alloc_str);
1041 if (folded_sign == '+')
1042 break;
1043 }
1044
1045 if (folded_sign == '-') {
1046 const int new_level = level + (extra_offset ? 2 : 1);
1047 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1048 new_level, fp);
1049 }
1050
1051 node = next;
1052 }
1053
1054 return printed;
1055 }
1056
1057 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1058 struct callchain_node *node,
1059 int level, FILE *fp)
1060 {
1061 struct callchain_list *chain;
1062 int offset = level * LEVEL_OFFSET_STEP;
1063 char folded_sign = ' ';
1064 int printed = 0;
1065
1066 list_for_each_entry(chain, &node->val, list) {
1067 char bf[1024], *s;
1068
1069 folded_sign = callchain_list__folded(chain);
1070 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1071 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1072 }
1073
1074 if (folded_sign == '-')
1075 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1076 browser->hists->stats.total_period,
1077 level + 1, fp);
1078 return printed;
1079 }
1080
1081 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1082 struct rb_root *chain, int level, FILE *fp)
1083 {
1084 struct rb_node *nd;
1085 int printed = 0;
1086
1087 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1088 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1089
1090 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1091 }
1092
1093 return printed;
1094 }
1095
1096 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1097 struct hist_entry *he, FILE *fp)
1098 {
1099 char s[8192];
1100 int printed = 0;
1101 char folded_sign = ' ';
1102 struct perf_hpp hpp = {
1103 .buf = s,
1104 .size = sizeof(s),
1105 };
1106 struct perf_hpp_fmt *fmt;
1107 bool first = true;
1108 int ret;
1109
1110 if (symbol_conf.use_callchain)
1111 folded_sign = hist_entry__folded(he);
1112
1113 if (symbol_conf.use_callchain)
1114 printed += fprintf(fp, "%c ", folded_sign);
1115
1116 perf_hpp__for_each_format(fmt) {
1117 if (perf_hpp__should_skip(fmt))
1118 continue;
1119
1120 if (!first) {
1121 ret = scnprintf(hpp.buf, hpp.size, " ");
1122 advance_hpp(&hpp, ret);
1123 } else
1124 first = false;
1125
1126 ret = fmt->entry(fmt, &hpp, he);
1127 advance_hpp(&hpp, ret);
1128 }
1129 printed += fprintf(fp, "%s\n", rtrim(s));
1130
1131 if (folded_sign == '-')
1132 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1133
1134 return printed;
1135 }
1136
1137 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1138 {
1139 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1140 browser->min_pcnt);
1141 int printed = 0;
1142
1143 while (nd) {
1144 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1145
1146 printed += hist_browser__fprintf_entry(browser, h, fp);
1147 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1148 }
1149
1150 return printed;
1151 }
1152
1153 static int hist_browser__dump(struct hist_browser *browser)
1154 {
1155 char filename[64];
1156 FILE *fp;
1157
1158 while (1) {
1159 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1160 if (access(filename, F_OK))
1161 break;
1162 /*
1163 * XXX: Just an arbitrary lazy upper limit
1164 */
1165 if (++browser->print_seq == 8192) {
1166 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1167 return -1;
1168 }
1169 }
1170
1171 fp = fopen(filename, "w");
1172 if (fp == NULL) {
1173 char bf[64];
1174 const char *err = strerror_r(errno, bf, sizeof(bf));
1175 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1176 return -1;
1177 }
1178
1179 ++browser->print_seq;
1180 hist_browser__fprintf(browser, fp);
1181 fclose(fp);
1182 ui_helpline__fpush("%s written!", filename);
1183
1184 return 0;
1185 }
1186
1187 static struct hist_browser *hist_browser__new(struct hists *hists)
1188 {
1189 struct hist_browser *browser = zalloc(sizeof(*browser));
1190
1191 if (browser) {
1192 browser->hists = hists;
1193 browser->b.refresh = hist_browser__refresh;
1194 browser->b.seek = ui_browser__hists_seek;
1195 browser->b.use_navkeypressed = true;
1196 }
1197
1198 return browser;
1199 }
1200
1201 static void hist_browser__delete(struct hist_browser *browser)
1202 {
1203 free(browser);
1204 }
1205
1206 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1207 {
1208 return browser->he_selection;
1209 }
1210
1211 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1212 {
1213 return browser->he_selection->thread;
1214 }
1215
1216 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1217 const char *ev_name)
1218 {
1219 char unit;
1220 int printed;
1221 const struct dso *dso = hists->dso_filter;
1222 const struct thread *thread = hists->thread_filter;
1223 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1224 u64 nr_events = hists->stats.total_period;
1225 struct perf_evsel *evsel = hists_to_evsel(hists);
1226 char buf[512];
1227 size_t buflen = sizeof(buf);
1228
1229 if (symbol_conf.filter_relative) {
1230 nr_samples = hists->stats.nr_non_filtered_samples;
1231 nr_events = hists->stats.total_non_filtered_period;
1232 }
1233
1234 if (perf_evsel__is_group_event(evsel)) {
1235 struct perf_evsel *pos;
1236
1237 perf_evsel__group_desc(evsel, buf, buflen);
1238 ev_name = buf;
1239
1240 for_each_group_member(pos, evsel) {
1241 if (symbol_conf.filter_relative) {
1242 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1243 nr_events += pos->hists.stats.total_non_filtered_period;
1244 } else {
1245 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1246 nr_events += pos->hists.stats.total_period;
1247 }
1248 }
1249 }
1250
1251 nr_samples = convert_unit(nr_samples, &unit);
1252 printed = scnprintf(bf, size,
1253 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1254 nr_samples, unit, ev_name, nr_events);
1255
1256
1257 if (hists->uid_filter_str)
1258 printed += snprintf(bf + printed, size - printed,
1259 ", UID: %s", hists->uid_filter_str);
1260 if (thread)
1261 printed += scnprintf(bf + printed, size - printed,
1262 ", Thread: %s(%d)",
1263 (thread->comm_set ? thread__comm_str(thread) : ""),
1264 thread->tid);
1265 if (dso)
1266 printed += scnprintf(bf + printed, size - printed,
1267 ", DSO: %s", dso->short_name);
1268 return printed;
1269 }
1270
1271 static inline void free_popup_options(char **options, int n)
1272 {
1273 int i;
1274
1275 for (i = 0; i < n; ++i)
1276 zfree(&options[i]);
1277 }
1278
1279 /* Check whether the browser is for 'top' or 'report' */
1280 static inline bool is_report_browser(void *timer)
1281 {
1282 return timer == NULL;
1283 }
1284
1285 /*
1286 * Only runtime switching of perf data file will make "input_name" point
1287 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1288 * whether we need to call free() for current "input_name" during the switch.
1289 */
1290 static bool is_input_name_malloced = false;
1291
1292 static int switch_data_file(void)
1293 {
1294 char *pwd, *options[32], *abs_path[32], *tmp;
1295 DIR *pwd_dir;
1296 int nr_options = 0, choice = -1, ret = -1;
1297 struct dirent *dent;
1298
1299 pwd = getenv("PWD");
1300 if (!pwd)
1301 return ret;
1302
1303 pwd_dir = opendir(pwd);
1304 if (!pwd_dir)
1305 return ret;
1306
1307 memset(options, 0, sizeof(options));
1308 memset(options, 0, sizeof(abs_path));
1309
1310 while ((dent = readdir(pwd_dir))) {
1311 char path[PATH_MAX];
1312 u64 magic;
1313 char *name = dent->d_name;
1314 FILE *file;
1315
1316 if (!(dent->d_type == DT_REG))
1317 continue;
1318
1319 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1320
1321 file = fopen(path, "r");
1322 if (!file)
1323 continue;
1324
1325 if (fread(&magic, 1, 8, file) < 8)
1326 goto close_file_and_continue;
1327
1328 if (is_perf_magic(magic)) {
1329 options[nr_options] = strdup(name);
1330 if (!options[nr_options])
1331 goto close_file_and_continue;
1332
1333 abs_path[nr_options] = strdup(path);
1334 if (!abs_path[nr_options]) {
1335 zfree(&options[nr_options]);
1336 ui__warning("Can't search all data files due to memory shortage.\n");
1337 fclose(file);
1338 break;
1339 }
1340
1341 nr_options++;
1342 }
1343
1344 close_file_and_continue:
1345 fclose(file);
1346 if (nr_options >= 32) {
1347 ui__warning("Too many perf data files in PWD!\n"
1348 "Only the first 32 files will be listed.\n");
1349 break;
1350 }
1351 }
1352 closedir(pwd_dir);
1353
1354 if (nr_options) {
1355 choice = ui__popup_menu(nr_options, options);
1356 if (choice < nr_options && choice >= 0) {
1357 tmp = strdup(abs_path[choice]);
1358 if (tmp) {
1359 if (is_input_name_malloced)
1360 free((void *)input_name);
1361 input_name = tmp;
1362 is_input_name_malloced = true;
1363 ret = 0;
1364 } else
1365 ui__warning("Data switch failed due to memory shortage!\n");
1366 }
1367 }
1368
1369 free_popup_options(options, nr_options);
1370 free_popup_options(abs_path, nr_options);
1371 return ret;
1372 }
1373
1374 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1375 {
1376 u64 nr_entries = 0;
1377 struct rb_node *nd = rb_first(&hb->hists->entries);
1378
1379 if (hb->min_pcnt == 0) {
1380 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1381 return;
1382 }
1383
1384 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1385 nr_entries++;
1386 nd = rb_next(nd);
1387 }
1388
1389 hb->nr_non_filtered_entries = nr_entries;
1390 }
1391
1392 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1393 const char *helpline, const char *ev_name,
1394 bool left_exits,
1395 struct hist_browser_timer *hbt,
1396 float min_pcnt,
1397 struct perf_session_env *env)
1398 {
1399 struct hists *hists = &evsel->hists;
1400 struct hist_browser *browser = hist_browser__new(hists);
1401 struct branch_info *bi;
1402 struct pstack *fstack;
1403 char *options[16];
1404 int nr_options = 0;
1405 int key = -1;
1406 char buf[64];
1407 char script_opt[64];
1408 int delay_secs = hbt ? hbt->refresh : 0;
1409
1410 #define HIST_BROWSER_HELP_COMMON \
1411 "h/?/F1 Show this window\n" \
1412 "UP/DOWN/PGUP\n" \
1413 "PGDN/SPACE Navigate\n" \
1414 "q/ESC/CTRL+C Exit browser\n\n" \
1415 "For multiple event sessions:\n\n" \
1416 "TAB/UNTAB Switch events\n\n" \
1417 "For symbolic views (--sort has sym):\n\n" \
1418 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1419 "<- Zoom out\n" \
1420 "a Annotate current symbol\n" \
1421 "C Collapse all callchains\n" \
1422 "d Zoom into current DSO\n" \
1423 "E Expand all callchains\n" \
1424 "F Toggle percentage of filtered entries\n" \
1425
1426 /* help messages are sorted by lexical order of the hotkey */
1427 const char report_help[] = HIST_BROWSER_HELP_COMMON
1428 "i Show header information\n"
1429 "P Print histograms to perf.hist.N\n"
1430 "r Run available scripts\n"
1431 "s Switch to another data file in PWD\n"
1432 "t Zoom into current Thread\n"
1433 "V Verbose (DSO names in callchains, etc)\n"
1434 "/ Filter symbol by name";
1435 const char top_help[] = HIST_BROWSER_HELP_COMMON
1436 "P Print histograms to perf.hist.N\n"
1437 "t Zoom into current Thread\n"
1438 "V Verbose (DSO names in callchains, etc)\n"
1439 "/ Filter symbol by name";
1440
1441 if (browser == NULL)
1442 return -1;
1443
1444 if (min_pcnt) {
1445 browser->min_pcnt = min_pcnt;
1446 hist_browser__update_nr_entries(browser);
1447 }
1448
1449 fstack = pstack__new(2);
1450 if (fstack == NULL)
1451 goto out;
1452
1453 ui_helpline__push(helpline);
1454
1455 memset(options, 0, sizeof(options));
1456
1457 while (1) {
1458 const struct thread *thread = NULL;
1459 const struct dso *dso = NULL;
1460 int choice = 0,
1461 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1462 annotate_f = -2, annotate_t = -2, browse_map = -2;
1463 int scripts_comm = -2, scripts_symbol = -2,
1464 scripts_all = -2, switch_data = -2;
1465
1466 nr_options = 0;
1467
1468 key = hist_browser__run(browser, ev_name, hbt);
1469
1470 if (browser->he_selection != NULL) {
1471 thread = hist_browser__selected_thread(browser);
1472 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1473 }
1474 switch (key) {
1475 case K_TAB:
1476 case K_UNTAB:
1477 if (nr_events == 1)
1478 continue;
1479 /*
1480 * Exit the browser, let hists__browser_tree
1481 * go to the next or previous
1482 */
1483 goto out_free_stack;
1484 case 'a':
1485 if (!sort__has_sym) {
1486 ui_browser__warning(&browser->b, delay_secs * 2,
1487 "Annotation is only available for symbolic views, "
1488 "include \"sym*\" in --sort to use it.");
1489 continue;
1490 }
1491
1492 if (browser->selection == NULL ||
1493 browser->selection->sym == NULL ||
1494 browser->selection->map->dso->annotate_warned)
1495 continue;
1496 goto do_annotate;
1497 case 'P':
1498 hist_browser__dump(browser);
1499 continue;
1500 case 'd':
1501 goto zoom_dso;
1502 case 'V':
1503 browser->show_dso = !browser->show_dso;
1504 continue;
1505 case 't':
1506 goto zoom_thread;
1507 case '/':
1508 if (ui_browser__input_window("Symbol to show",
1509 "Please enter the name of symbol you want to see",
1510 buf, "ENTER: OK, ESC: Cancel",
1511 delay_secs * 2) == K_ENTER) {
1512 hists->symbol_filter_str = *buf ? buf : NULL;
1513 hists__filter_by_symbol(hists);
1514 hist_browser__reset(browser);
1515 }
1516 continue;
1517 case 'r':
1518 if (is_report_browser(hbt))
1519 goto do_scripts;
1520 continue;
1521 case 's':
1522 if (is_report_browser(hbt))
1523 goto do_data_switch;
1524 continue;
1525 case 'i':
1526 /* env->arch is NULL for live-mode (i.e. perf top) */
1527 if (env->arch)
1528 tui__header_window(env);
1529 continue;
1530 case 'F':
1531 symbol_conf.filter_relative ^= 1;
1532 continue;
1533 case K_F1:
1534 case 'h':
1535 case '?':
1536 ui_browser__help_window(&browser->b,
1537 is_report_browser(hbt) ? report_help : top_help);
1538 continue;
1539 case K_ENTER:
1540 case K_RIGHT:
1541 /* menu */
1542 break;
1543 case K_LEFT: {
1544 const void *top;
1545
1546 if (pstack__empty(fstack)) {
1547 /*
1548 * Go back to the perf_evsel_menu__run or other user
1549 */
1550 if (left_exits)
1551 goto out_free_stack;
1552 continue;
1553 }
1554 top = pstack__pop(fstack);
1555 if (top == &browser->hists->dso_filter)
1556 goto zoom_out_dso;
1557 if (top == &browser->hists->thread_filter)
1558 goto zoom_out_thread;
1559 continue;
1560 }
1561 case K_ESC:
1562 if (!left_exits &&
1563 !ui_browser__dialog_yesno(&browser->b,
1564 "Do you really want to exit?"))
1565 continue;
1566 /* Fall thru */
1567 case 'q':
1568 case CTRL('c'):
1569 goto out_free_stack;
1570 default:
1571 continue;
1572 }
1573
1574 if (!sort__has_sym)
1575 goto add_exit_option;
1576
1577 if (sort__mode == SORT_MODE__BRANCH) {
1578 bi = browser->he_selection->branch_info;
1579 if (browser->selection != NULL &&
1580 bi &&
1581 bi->from.sym != NULL &&
1582 !bi->from.map->dso->annotate_warned &&
1583 asprintf(&options[nr_options], "Annotate %s",
1584 bi->from.sym->name) > 0)
1585 annotate_f = nr_options++;
1586
1587 if (browser->selection != NULL &&
1588 bi &&
1589 bi->to.sym != NULL &&
1590 !bi->to.map->dso->annotate_warned &&
1591 (bi->to.sym != bi->from.sym ||
1592 bi->to.map->dso != bi->from.map->dso) &&
1593 asprintf(&options[nr_options], "Annotate %s",
1594 bi->to.sym->name) > 0)
1595 annotate_t = nr_options++;
1596 } else {
1597 if (browser->selection != NULL &&
1598 browser->selection->sym != NULL &&
1599 !browser->selection->map->dso->annotate_warned) {
1600 struct annotation *notes;
1601
1602 notes = symbol__annotation(browser->selection->sym);
1603
1604 if (notes->src &&
1605 asprintf(&options[nr_options], "Annotate %s",
1606 browser->selection->sym->name) > 0)
1607 annotate = nr_options++;
1608 }
1609 }
1610
1611 if (thread != NULL &&
1612 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1613 (browser->hists->thread_filter ? "out of" : "into"),
1614 (thread->comm_set ? thread__comm_str(thread) : ""),
1615 thread->tid) > 0)
1616 zoom_thread = nr_options++;
1617
1618 if (dso != NULL &&
1619 asprintf(&options[nr_options], "Zoom %s %s DSO",
1620 (browser->hists->dso_filter ? "out of" : "into"),
1621 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1622 zoom_dso = nr_options++;
1623
1624 if (browser->selection != NULL &&
1625 browser->selection->map != NULL &&
1626 asprintf(&options[nr_options], "Browse map details") > 0)
1627 browse_map = nr_options++;
1628
1629 /* perf script support */
1630 if (browser->he_selection) {
1631 struct symbol *sym;
1632
1633 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1634 thread__comm_str(browser->he_selection->thread)) > 0)
1635 scripts_comm = nr_options++;
1636
1637 sym = browser->he_selection->ms.sym;
1638 if (sym && sym->namelen &&
1639 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1640 sym->name) > 0)
1641 scripts_symbol = nr_options++;
1642 }
1643
1644 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1645 scripts_all = nr_options++;
1646
1647 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1648 "Switch to another data file in PWD") > 0)
1649 switch_data = nr_options++;
1650 add_exit_option:
1651 options[nr_options++] = (char *)"Exit";
1652 retry_popup_menu:
1653 choice = ui__popup_menu(nr_options, options);
1654
1655 if (choice == nr_options - 1)
1656 break;
1657
1658 if (choice == -1) {
1659 free_popup_options(options, nr_options - 1);
1660 continue;
1661 }
1662
1663 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1664 struct hist_entry *he;
1665 struct annotation *notes;
1666 int err;
1667 do_annotate:
1668 if (!objdump_path && perf_session_env__lookup_objdump(env))
1669 continue;
1670
1671 he = hist_browser__selected_entry(browser);
1672 if (he == NULL)
1673 continue;
1674
1675 /*
1676 * we stash the branch_info symbol + map into the
1677 * the ms so we don't have to rewrite all the annotation
1678 * code to use branch_info.
1679 * in branch mode, the ms struct is not used
1680 */
1681 if (choice == annotate_f) {
1682 he->ms.sym = he->branch_info->from.sym;
1683 he->ms.map = he->branch_info->from.map;
1684 } else if (choice == annotate_t) {
1685 he->ms.sym = he->branch_info->to.sym;
1686 he->ms.map = he->branch_info->to.map;
1687 }
1688
1689 notes = symbol__annotation(he->ms.sym);
1690 if (!notes->src)
1691 continue;
1692
1693 /*
1694 * Don't let this be freed, say, by hists__decay_entry.
1695 */
1696 he->used = true;
1697 err = hist_entry__tui_annotate(he, evsel, hbt);
1698 he->used = false;
1699 /*
1700 * offer option to annotate the other branch source or target
1701 * (if they exists) when returning from annotate
1702 */
1703 if ((err == 'q' || err == CTRL('c'))
1704 && annotate_t != -2 && annotate_f != -2)
1705 goto retry_popup_menu;
1706
1707 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1708 if (err)
1709 ui_browser__handle_resize(&browser->b);
1710
1711 } else if (choice == browse_map)
1712 map__browse(browser->selection->map);
1713 else if (choice == zoom_dso) {
1714 zoom_dso:
1715 if (browser->hists->dso_filter) {
1716 pstack__remove(fstack, &browser->hists->dso_filter);
1717 zoom_out_dso:
1718 ui_helpline__pop();
1719 browser->hists->dso_filter = NULL;
1720 perf_hpp__set_elide(HISTC_DSO, false);
1721 } else {
1722 if (dso == NULL)
1723 continue;
1724 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1725 dso->kernel ? "the Kernel" : dso->short_name);
1726 browser->hists->dso_filter = dso;
1727 perf_hpp__set_elide(HISTC_DSO, true);
1728 pstack__push(fstack, &browser->hists->dso_filter);
1729 }
1730 hists__filter_by_dso(hists);
1731 hist_browser__reset(browser);
1732 } else if (choice == zoom_thread) {
1733 zoom_thread:
1734 if (browser->hists->thread_filter) {
1735 pstack__remove(fstack, &browser->hists->thread_filter);
1736 zoom_out_thread:
1737 ui_helpline__pop();
1738 browser->hists->thread_filter = NULL;
1739 perf_hpp__set_elide(HISTC_THREAD, false);
1740 } else {
1741 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1742 thread->comm_set ? thread__comm_str(thread) : "",
1743 thread->tid);
1744 browser->hists->thread_filter = thread;
1745 perf_hpp__set_elide(HISTC_THREAD, false);
1746 pstack__push(fstack, &browser->hists->thread_filter);
1747 }
1748 hists__filter_by_thread(hists);
1749 hist_browser__reset(browser);
1750 }
1751 /* perf scripts support */
1752 else if (choice == scripts_all || choice == scripts_comm ||
1753 choice == scripts_symbol) {
1754 do_scripts:
1755 memset(script_opt, 0, 64);
1756
1757 if (choice == scripts_comm)
1758 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1759
1760 if (choice == scripts_symbol)
1761 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1762
1763 script_browse(script_opt);
1764 }
1765 /* Switch to another data file */
1766 else if (choice == switch_data) {
1767 do_data_switch:
1768 if (!switch_data_file()) {
1769 key = K_SWITCH_INPUT_DATA;
1770 break;
1771 } else
1772 ui__warning("Won't switch the data files due to\n"
1773 "no valid data file get selected!\n");
1774 }
1775 }
1776 out_free_stack:
1777 pstack__delete(fstack);
1778 out:
1779 hist_browser__delete(browser);
1780 free_popup_options(options, nr_options - 1);
1781 return key;
1782 }
1783
1784 struct perf_evsel_menu {
1785 struct ui_browser b;
1786 struct perf_evsel *selection;
1787 bool lost_events, lost_events_warned;
1788 float min_pcnt;
1789 struct perf_session_env *env;
1790 };
1791
1792 static void perf_evsel_menu__write(struct ui_browser *browser,
1793 void *entry, int row)
1794 {
1795 struct perf_evsel_menu *menu = container_of(browser,
1796 struct perf_evsel_menu, b);
1797 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1798 bool current_entry = ui_browser__is_current_entry(browser, row);
1799 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1800 const char *ev_name = perf_evsel__name(evsel);
1801 char bf[256], unit;
1802 const char *warn = " ";
1803 size_t printed;
1804
1805 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1806 HE_COLORSET_NORMAL);
1807
1808 if (perf_evsel__is_group_event(evsel)) {
1809 struct perf_evsel *pos;
1810
1811 ev_name = perf_evsel__group_name(evsel);
1812
1813 for_each_group_member(pos, evsel) {
1814 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1815 }
1816 }
1817
1818 nr_events = convert_unit(nr_events, &unit);
1819 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1820 unit, unit == ' ' ? "" : " ", ev_name);
1821 slsmg_printf("%s", bf);
1822
1823 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1824 if (nr_events != 0) {
1825 menu->lost_events = true;
1826 if (!current_entry)
1827 ui_browser__set_color(browser, HE_COLORSET_TOP);
1828 nr_events = convert_unit(nr_events, &unit);
1829 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1830 nr_events, unit, unit == ' ' ? "" : " ");
1831 warn = bf;
1832 }
1833
1834 slsmg_write_nstring(warn, browser->width - printed);
1835
1836 if (current_entry)
1837 menu->selection = evsel;
1838 }
1839
1840 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1841 int nr_events, const char *help,
1842 struct hist_browser_timer *hbt)
1843 {
1844 struct perf_evlist *evlist = menu->b.priv;
1845 struct perf_evsel *pos;
1846 const char *ev_name, *title = "Available samples";
1847 int delay_secs = hbt ? hbt->refresh : 0;
1848 int key;
1849
1850 if (ui_browser__show(&menu->b, title,
1851 "ESC: exit, ENTER|->: Browse histograms") < 0)
1852 return -1;
1853
1854 while (1) {
1855 key = ui_browser__run(&menu->b, delay_secs);
1856
1857 switch (key) {
1858 case K_TIMER:
1859 hbt->timer(hbt->arg);
1860
1861 if (!menu->lost_events_warned && menu->lost_events) {
1862 ui_browser__warn_lost_events(&menu->b);
1863 menu->lost_events_warned = true;
1864 }
1865 continue;
1866 case K_RIGHT:
1867 case K_ENTER:
1868 if (!menu->selection)
1869 continue;
1870 pos = menu->selection;
1871 browse_hists:
1872 perf_evlist__set_selected(evlist, pos);
1873 /*
1874 * Give the calling tool a chance to populate the non
1875 * default evsel resorted hists tree.
1876 */
1877 if (hbt)
1878 hbt->timer(hbt->arg);
1879 ev_name = perf_evsel__name(pos);
1880 key = perf_evsel__hists_browse(pos, nr_events, help,
1881 ev_name, true, hbt,
1882 menu->min_pcnt,
1883 menu->env);
1884 ui_browser__show_title(&menu->b, title);
1885 switch (key) {
1886 case K_TAB:
1887 if (pos->node.next == &evlist->entries)
1888 pos = perf_evlist__first(evlist);
1889 else
1890 pos = perf_evsel__next(pos);
1891 goto browse_hists;
1892 case K_UNTAB:
1893 if (pos->node.prev == &evlist->entries)
1894 pos = perf_evlist__last(evlist);
1895 else
1896 pos = perf_evsel__prev(pos);
1897 goto browse_hists;
1898 case K_ESC:
1899 if (!ui_browser__dialog_yesno(&menu->b,
1900 "Do you really want to exit?"))
1901 continue;
1902 /* Fall thru */
1903 case K_SWITCH_INPUT_DATA:
1904 case 'q':
1905 case CTRL('c'):
1906 goto out;
1907 default:
1908 continue;
1909 }
1910 case K_LEFT:
1911 continue;
1912 case K_ESC:
1913 if (!ui_browser__dialog_yesno(&menu->b,
1914 "Do you really want to exit?"))
1915 continue;
1916 /* Fall thru */
1917 case 'q':
1918 case CTRL('c'):
1919 goto out;
1920 default:
1921 continue;
1922 }
1923 }
1924
1925 out:
1926 ui_browser__hide(&menu->b);
1927 return key;
1928 }
1929
1930 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1931 void *entry)
1932 {
1933 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1934
1935 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1936 return true;
1937
1938 return false;
1939 }
1940
1941 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1942 int nr_entries, const char *help,
1943 struct hist_browser_timer *hbt,
1944 float min_pcnt,
1945 struct perf_session_env *env)
1946 {
1947 struct perf_evsel *pos;
1948 struct perf_evsel_menu menu = {
1949 .b = {
1950 .entries = &evlist->entries,
1951 .refresh = ui_browser__list_head_refresh,
1952 .seek = ui_browser__list_head_seek,
1953 .write = perf_evsel_menu__write,
1954 .filter = filter_group_entries,
1955 .nr_entries = nr_entries,
1956 .priv = evlist,
1957 },
1958 .min_pcnt = min_pcnt,
1959 .env = env,
1960 };
1961
1962 ui_helpline__push("Press ESC to exit");
1963
1964 evlist__for_each(evlist, pos) {
1965 const char *ev_name = perf_evsel__name(pos);
1966 size_t line_len = strlen(ev_name) + 7;
1967
1968 if (menu.b.width < line_len)
1969 menu.b.width = line_len;
1970 }
1971
1972 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1973 }
1974
1975 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1976 struct hist_browser_timer *hbt,
1977 float min_pcnt,
1978 struct perf_session_env *env)
1979 {
1980 int nr_entries = evlist->nr_entries;
1981
1982 single_entry:
1983 if (nr_entries == 1) {
1984 struct perf_evsel *first = perf_evlist__first(evlist);
1985 const char *ev_name = perf_evsel__name(first);
1986
1987 return perf_evsel__hists_browse(first, nr_entries, help,
1988 ev_name, false, hbt, min_pcnt,
1989 env);
1990 }
1991
1992 if (symbol_conf.event_group) {
1993 struct perf_evsel *pos;
1994
1995 nr_entries = 0;
1996 evlist__for_each(evlist, pos) {
1997 if (perf_evsel__is_group_leader(pos))
1998 nr_entries++;
1999 }
2000
2001 if (nr_entries == 1)
2002 goto single_entry;
2003 }
2004
2005 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2006 hbt, min_pcnt, env);
2007 }