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