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