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