84c8251f39a1f3ada8600286738f168eb369b8f1
[deliverable/linux.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 hb->he_selection = NULL;
932 hb->selection = NULL;
933
934 for (nd = browser->top; nd; nd = rb_next(nd)) {
935 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
936 float percent;
937
938 if (h->filtered)
939 continue;
940
941 percent = hist_entry__get_percent_limit(h);
942 if (percent < hb->min_pcnt)
943 continue;
944
945 row += hist_browser__show_entry(hb, h, row);
946 if (row == browser->rows)
947 break;
948 }
949
950 return row + header_offset;
951 }
952
953 static struct rb_node *hists__filter_entries(struct rb_node *nd,
954 float min_pcnt)
955 {
956 while (nd != NULL) {
957 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
958 float percent = hist_entry__get_percent_limit(h);
959
960 if (!h->filtered && percent >= min_pcnt)
961 return nd;
962
963 nd = rb_next(nd);
964 }
965
966 return NULL;
967 }
968
969 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
970 float min_pcnt)
971 {
972 while (nd != NULL) {
973 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
974 float percent = hist_entry__get_percent_limit(h);
975
976 if (!h->filtered && percent >= min_pcnt)
977 return nd;
978
979 nd = rb_prev(nd);
980 }
981
982 return NULL;
983 }
984
985 static void ui_browser__hists_seek(struct ui_browser *browser,
986 off_t offset, int whence)
987 {
988 struct hist_entry *h;
989 struct rb_node *nd;
990 bool first = true;
991 struct hist_browser *hb;
992
993 hb = container_of(browser, struct hist_browser, b);
994
995 if (browser->nr_entries == 0)
996 return;
997
998 ui_browser__hists_init_top(browser);
999
1000 switch (whence) {
1001 case SEEK_SET:
1002 nd = hists__filter_entries(rb_first(browser->entries),
1003 hb->min_pcnt);
1004 break;
1005 case SEEK_CUR:
1006 nd = browser->top;
1007 goto do_offset;
1008 case SEEK_END:
1009 nd = hists__filter_prev_entries(rb_last(browser->entries),
1010 hb->min_pcnt);
1011 first = false;
1012 break;
1013 default:
1014 return;
1015 }
1016
1017 /*
1018 * Moves not relative to the first visible entry invalidates its
1019 * row_offset:
1020 */
1021 h = rb_entry(browser->top, struct hist_entry, rb_node);
1022 h->row_offset = 0;
1023
1024 /*
1025 * Here we have to check if nd is expanded (+), if it is we can't go
1026 * the next top level hist_entry, instead we must compute an offset of
1027 * what _not_ to show and not change the first visible entry.
1028 *
1029 * This offset increments when we are going from top to bottom and
1030 * decreases when we're going from bottom to top.
1031 *
1032 * As we don't have backpointers to the top level in the callchains
1033 * structure, we need to always print the whole hist_entry callchain,
1034 * skipping the first ones that are before the first visible entry
1035 * and stop when we printed enough lines to fill the screen.
1036 */
1037 do_offset:
1038 if (!nd)
1039 return;
1040
1041 if (offset > 0) {
1042 do {
1043 h = rb_entry(nd, struct hist_entry, rb_node);
1044 if (h->unfolded) {
1045 u16 remaining = h->nr_rows - h->row_offset;
1046 if (offset > remaining) {
1047 offset -= remaining;
1048 h->row_offset = 0;
1049 } else {
1050 h->row_offset += offset;
1051 offset = 0;
1052 browser->top = nd;
1053 break;
1054 }
1055 }
1056 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1057 if (nd == NULL)
1058 break;
1059 --offset;
1060 browser->top = nd;
1061 } while (offset != 0);
1062 } else if (offset < 0) {
1063 while (1) {
1064 h = rb_entry(nd, struct hist_entry, rb_node);
1065 if (h->unfolded) {
1066 if (first) {
1067 if (-offset > h->row_offset) {
1068 offset += h->row_offset;
1069 h->row_offset = 0;
1070 } else {
1071 h->row_offset += offset;
1072 offset = 0;
1073 browser->top = nd;
1074 break;
1075 }
1076 } else {
1077 if (-offset > h->nr_rows) {
1078 offset += h->nr_rows;
1079 h->row_offset = 0;
1080 } else {
1081 h->row_offset = h->nr_rows + offset;
1082 offset = 0;
1083 browser->top = nd;
1084 break;
1085 }
1086 }
1087 }
1088
1089 nd = hists__filter_prev_entries(rb_prev(nd),
1090 hb->min_pcnt);
1091 if (nd == NULL)
1092 break;
1093 ++offset;
1094 browser->top = nd;
1095 if (offset == 0) {
1096 /*
1097 * Last unfiltered hist_entry, check if it is
1098 * unfolded, if it is then we should have
1099 * row_offset at its last entry.
1100 */
1101 h = rb_entry(nd, struct hist_entry, rb_node);
1102 if (h->unfolded)
1103 h->row_offset = h->nr_rows;
1104 break;
1105 }
1106 first = false;
1107 }
1108 } else {
1109 browser->top = nd;
1110 h = rb_entry(nd, struct hist_entry, rb_node);
1111 h->row_offset = 0;
1112 }
1113 }
1114
1115 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1116 struct hist_entry *he, FILE *fp)
1117 {
1118 u64 total = hists__total_period(he->hists);
1119 struct callchain_print_arg arg = {
1120 .fp = fp,
1121 };
1122
1123 if (symbol_conf.cumulate_callchain)
1124 total = he->stat_acc->period;
1125
1126 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1127 hist_browser__fprintf_callchain_entry, &arg,
1128 hist_browser__check_dump_full);
1129 return arg.printed;
1130 }
1131
1132 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1133 struct hist_entry *he, FILE *fp)
1134 {
1135 char s[8192];
1136 int printed = 0;
1137 char folded_sign = ' ';
1138 struct perf_hpp hpp = {
1139 .buf = s,
1140 .size = sizeof(s),
1141 };
1142 struct perf_hpp_fmt *fmt;
1143 bool first = true;
1144 int ret;
1145
1146 if (symbol_conf.use_callchain)
1147 folded_sign = hist_entry__folded(he);
1148
1149 if (symbol_conf.use_callchain)
1150 printed += fprintf(fp, "%c ", folded_sign);
1151
1152 perf_hpp__for_each_format(fmt) {
1153 if (perf_hpp__should_skip(fmt))
1154 continue;
1155
1156 if (!first) {
1157 ret = scnprintf(hpp.buf, hpp.size, " ");
1158 advance_hpp(&hpp, ret);
1159 } else
1160 first = false;
1161
1162 ret = fmt->entry(fmt, &hpp, he);
1163 advance_hpp(&hpp, ret);
1164 }
1165 printed += fprintf(fp, "%s\n", rtrim(s));
1166
1167 if (folded_sign == '-')
1168 printed += hist_browser__fprintf_callchain(browser, he, fp);
1169
1170 return printed;
1171 }
1172
1173 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1174 {
1175 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1176 browser->min_pcnt);
1177 int printed = 0;
1178
1179 while (nd) {
1180 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1181
1182 printed += hist_browser__fprintf_entry(browser, h, fp);
1183 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1184 }
1185
1186 return printed;
1187 }
1188
1189 static int hist_browser__dump(struct hist_browser *browser)
1190 {
1191 char filename[64];
1192 FILE *fp;
1193
1194 while (1) {
1195 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1196 if (access(filename, F_OK))
1197 break;
1198 /*
1199 * XXX: Just an arbitrary lazy upper limit
1200 */
1201 if (++browser->print_seq == 8192) {
1202 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1203 return -1;
1204 }
1205 }
1206
1207 fp = fopen(filename, "w");
1208 if (fp == NULL) {
1209 char bf[64];
1210 const char *err = strerror_r(errno, bf, sizeof(bf));
1211 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1212 return -1;
1213 }
1214
1215 ++browser->print_seq;
1216 hist_browser__fprintf(browser, fp);
1217 fclose(fp);
1218 ui_helpline__fpush("%s written!", filename);
1219
1220 return 0;
1221 }
1222
1223 static struct hist_browser *hist_browser__new(struct hists *hists,
1224 struct hist_browser_timer *hbt,
1225 struct perf_env *env)
1226 {
1227 struct hist_browser *browser = zalloc(sizeof(*browser));
1228
1229 if (browser) {
1230 browser->hists = hists;
1231 browser->b.refresh = hist_browser__refresh;
1232 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1233 browser->b.seek = ui_browser__hists_seek;
1234 browser->b.use_navkeypressed = true;
1235 browser->show_headers = symbol_conf.show_hist_headers;
1236 browser->hbt = hbt;
1237 browser->env = env;
1238 }
1239
1240 return browser;
1241 }
1242
1243 static void hist_browser__delete(struct hist_browser *browser)
1244 {
1245 free(browser);
1246 }
1247
1248 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1249 {
1250 return browser->he_selection;
1251 }
1252
1253 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1254 {
1255 return browser->he_selection->thread;
1256 }
1257
1258 /* Check whether the browser is for 'top' or 'report' */
1259 static inline bool is_report_browser(void *timer)
1260 {
1261 return timer == NULL;
1262 }
1263
1264 static int hists__browser_title(struct hists *hists,
1265 struct hist_browser_timer *hbt,
1266 char *bf, size_t size)
1267 {
1268 char unit;
1269 int printed;
1270 const struct dso *dso = hists->dso_filter;
1271 const struct thread *thread = hists->thread_filter;
1272 int socket_id = hists->socket_filter;
1273 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1274 u64 nr_events = hists->stats.total_period;
1275 struct perf_evsel *evsel = hists_to_evsel(hists);
1276 const char *ev_name = perf_evsel__name(evsel);
1277 char buf[512];
1278 size_t buflen = sizeof(buf);
1279 char ref[30] = " show reference callgraph, ";
1280 bool enable_ref = false;
1281
1282 if (symbol_conf.filter_relative) {
1283 nr_samples = hists->stats.nr_non_filtered_samples;
1284 nr_events = hists->stats.total_non_filtered_period;
1285 }
1286
1287 if (perf_evsel__is_group_event(evsel)) {
1288 struct perf_evsel *pos;
1289
1290 perf_evsel__group_desc(evsel, buf, buflen);
1291 ev_name = buf;
1292
1293 for_each_group_member(pos, evsel) {
1294 struct hists *pos_hists = evsel__hists(pos);
1295
1296 if (symbol_conf.filter_relative) {
1297 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1298 nr_events += pos_hists->stats.total_non_filtered_period;
1299 } else {
1300 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1301 nr_events += pos_hists->stats.total_period;
1302 }
1303 }
1304 }
1305
1306 if (symbol_conf.show_ref_callgraph &&
1307 strstr(ev_name, "call-graph=no"))
1308 enable_ref = true;
1309 nr_samples = convert_unit(nr_samples, &unit);
1310 printed = scnprintf(bf, size,
1311 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1312 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1313
1314
1315 if (hists->uid_filter_str)
1316 printed += snprintf(bf + printed, size - printed,
1317 ", UID: %s", hists->uid_filter_str);
1318 if (thread)
1319 printed += scnprintf(bf + printed, size - printed,
1320 ", Thread: %s(%d)",
1321 (thread->comm_set ? thread__comm_str(thread) : ""),
1322 thread->tid);
1323 if (dso)
1324 printed += scnprintf(bf + printed, size - printed,
1325 ", DSO: %s", dso->short_name);
1326 if (socket_id > -1)
1327 printed += scnprintf(bf + printed, size - printed,
1328 ", Processor Socket: %d", socket_id);
1329 if (!is_report_browser(hbt)) {
1330 struct perf_top *top = hbt->arg;
1331
1332 if (top->zero)
1333 printed += scnprintf(bf + printed, size - printed, " [z]");
1334 }
1335
1336 return printed;
1337 }
1338
1339 static inline void free_popup_options(char **options, int n)
1340 {
1341 int i;
1342
1343 for (i = 0; i < n; ++i)
1344 zfree(&options[i]);
1345 }
1346
1347 /*
1348 * Only runtime switching of perf data file will make "input_name" point
1349 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1350 * whether we need to call free() for current "input_name" during the switch.
1351 */
1352 static bool is_input_name_malloced = false;
1353
1354 static int switch_data_file(void)
1355 {
1356 char *pwd, *options[32], *abs_path[32], *tmp;
1357 DIR *pwd_dir;
1358 int nr_options = 0, choice = -1, ret = -1;
1359 struct dirent *dent;
1360
1361 pwd = getenv("PWD");
1362 if (!pwd)
1363 return ret;
1364
1365 pwd_dir = opendir(pwd);
1366 if (!pwd_dir)
1367 return ret;
1368
1369 memset(options, 0, sizeof(options));
1370 memset(options, 0, sizeof(abs_path));
1371
1372 while ((dent = readdir(pwd_dir))) {
1373 char path[PATH_MAX];
1374 u64 magic;
1375 char *name = dent->d_name;
1376 FILE *file;
1377
1378 if (!(dent->d_type == DT_REG))
1379 continue;
1380
1381 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1382
1383 file = fopen(path, "r");
1384 if (!file)
1385 continue;
1386
1387 if (fread(&magic, 1, 8, file) < 8)
1388 goto close_file_and_continue;
1389
1390 if (is_perf_magic(magic)) {
1391 options[nr_options] = strdup(name);
1392 if (!options[nr_options])
1393 goto close_file_and_continue;
1394
1395 abs_path[nr_options] = strdup(path);
1396 if (!abs_path[nr_options]) {
1397 zfree(&options[nr_options]);
1398 ui__warning("Can't search all data files due to memory shortage.\n");
1399 fclose(file);
1400 break;
1401 }
1402
1403 nr_options++;
1404 }
1405
1406 close_file_and_continue:
1407 fclose(file);
1408 if (nr_options >= 32) {
1409 ui__warning("Too many perf data files in PWD!\n"
1410 "Only the first 32 files will be listed.\n");
1411 break;
1412 }
1413 }
1414 closedir(pwd_dir);
1415
1416 if (nr_options) {
1417 choice = ui__popup_menu(nr_options, options);
1418 if (choice < nr_options && choice >= 0) {
1419 tmp = strdup(abs_path[choice]);
1420 if (tmp) {
1421 if (is_input_name_malloced)
1422 free((void *)input_name);
1423 input_name = tmp;
1424 is_input_name_malloced = true;
1425 ret = 0;
1426 } else
1427 ui__warning("Data switch failed due to memory shortage!\n");
1428 }
1429 }
1430
1431 free_popup_options(options, nr_options);
1432 free_popup_options(abs_path, nr_options);
1433 return ret;
1434 }
1435
1436 struct popup_action {
1437 struct thread *thread;
1438 struct map_symbol ms;
1439 int socket;
1440
1441 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1442 };
1443
1444 static int
1445 do_annotate(struct hist_browser *browser, struct popup_action *act)
1446 {
1447 struct perf_evsel *evsel;
1448 struct annotation *notes;
1449 struct hist_entry *he;
1450 int err;
1451
1452 if (!objdump_path && perf_env__lookup_objdump(browser->env))
1453 return 0;
1454
1455 notes = symbol__annotation(act->ms.sym);
1456 if (!notes->src)
1457 return 0;
1458
1459 evsel = hists_to_evsel(browser->hists);
1460 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1461 he = hist_browser__selected_entry(browser);
1462 /*
1463 * offer option to annotate the other branch source or target
1464 * (if they exists) when returning from annotate
1465 */
1466 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1467 return 1;
1468
1469 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1470 if (err)
1471 ui_browser__handle_resize(&browser->b);
1472 return 0;
1473 }
1474
1475 static int
1476 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1477 struct popup_action *act, char **optstr,
1478 struct map *map, struct symbol *sym)
1479 {
1480 if (sym == NULL || map->dso->annotate_warned)
1481 return 0;
1482
1483 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1484 return 0;
1485
1486 act->ms.map = map;
1487 act->ms.sym = sym;
1488 act->fn = do_annotate;
1489 return 1;
1490 }
1491
1492 static int
1493 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1494 {
1495 struct thread *thread = act->thread;
1496
1497 if (browser->hists->thread_filter) {
1498 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1499 perf_hpp__set_elide(HISTC_THREAD, false);
1500 thread__zput(browser->hists->thread_filter);
1501 ui_helpline__pop();
1502 } else {
1503 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1504 thread->comm_set ? thread__comm_str(thread) : "",
1505 thread->tid);
1506 browser->hists->thread_filter = thread__get(thread);
1507 perf_hpp__set_elide(HISTC_THREAD, false);
1508 pstack__push(browser->pstack, &browser->hists->thread_filter);
1509 }
1510
1511 hists__filter_by_thread(browser->hists);
1512 hist_browser__reset(browser);
1513 return 0;
1514 }
1515
1516 static int
1517 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1518 char **optstr, struct thread *thread)
1519 {
1520 if (thread == NULL)
1521 return 0;
1522
1523 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1524 browser->hists->thread_filter ? "out of" : "into",
1525 thread->comm_set ? thread__comm_str(thread) : "",
1526 thread->tid) < 0)
1527 return 0;
1528
1529 act->thread = thread;
1530 act->fn = do_zoom_thread;
1531 return 1;
1532 }
1533
1534 static int
1535 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1536 {
1537 struct map *map = act->ms.map;
1538
1539 if (browser->hists->dso_filter) {
1540 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1541 perf_hpp__set_elide(HISTC_DSO, false);
1542 browser->hists->dso_filter = NULL;
1543 ui_helpline__pop();
1544 } else {
1545 if (map == NULL)
1546 return 0;
1547 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1548 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1549 browser->hists->dso_filter = map->dso;
1550 perf_hpp__set_elide(HISTC_DSO, true);
1551 pstack__push(browser->pstack, &browser->hists->dso_filter);
1552 }
1553
1554 hists__filter_by_dso(browser->hists);
1555 hist_browser__reset(browser);
1556 return 0;
1557 }
1558
1559 static int
1560 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1561 char **optstr, struct map *map)
1562 {
1563 if (map == NULL)
1564 return 0;
1565
1566 if (asprintf(optstr, "Zoom %s %s DSO",
1567 browser->hists->dso_filter ? "out of" : "into",
1568 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1569 return 0;
1570
1571 act->ms.map = map;
1572 act->fn = do_zoom_dso;
1573 return 1;
1574 }
1575
1576 static int
1577 do_browse_map(struct hist_browser *browser __maybe_unused,
1578 struct popup_action *act)
1579 {
1580 map__browse(act->ms.map);
1581 return 0;
1582 }
1583
1584 static int
1585 add_map_opt(struct hist_browser *browser __maybe_unused,
1586 struct popup_action *act, char **optstr, struct map *map)
1587 {
1588 if (map == NULL)
1589 return 0;
1590
1591 if (asprintf(optstr, "Browse map details") < 0)
1592 return 0;
1593
1594 act->ms.map = map;
1595 act->fn = do_browse_map;
1596 return 1;
1597 }
1598
1599 static int
1600 do_run_script(struct hist_browser *browser __maybe_unused,
1601 struct popup_action *act)
1602 {
1603 char script_opt[64];
1604 memset(script_opt, 0, sizeof(script_opt));
1605
1606 if (act->thread) {
1607 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1608 thread__comm_str(act->thread));
1609 } else if (act->ms.sym) {
1610 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1611 act->ms.sym->name);
1612 }
1613
1614 script_browse(script_opt);
1615 return 0;
1616 }
1617
1618 static int
1619 add_script_opt(struct hist_browser *browser __maybe_unused,
1620 struct popup_action *act, char **optstr,
1621 struct thread *thread, struct symbol *sym)
1622 {
1623 if (thread) {
1624 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1625 thread__comm_str(thread)) < 0)
1626 return 0;
1627 } else if (sym) {
1628 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1629 sym->name) < 0)
1630 return 0;
1631 } else {
1632 if (asprintf(optstr, "Run scripts for all samples") < 0)
1633 return 0;
1634 }
1635
1636 act->thread = thread;
1637 act->ms.sym = sym;
1638 act->fn = do_run_script;
1639 return 1;
1640 }
1641
1642 static int
1643 do_switch_data(struct hist_browser *browser __maybe_unused,
1644 struct popup_action *act __maybe_unused)
1645 {
1646 if (switch_data_file()) {
1647 ui__warning("Won't switch the data files due to\n"
1648 "no valid data file get selected!\n");
1649 return 0;
1650 }
1651
1652 return K_SWITCH_INPUT_DATA;
1653 }
1654
1655 static int
1656 add_switch_opt(struct hist_browser *browser,
1657 struct popup_action *act, char **optstr)
1658 {
1659 if (!is_report_browser(browser->hbt))
1660 return 0;
1661
1662 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1663 return 0;
1664
1665 act->fn = do_switch_data;
1666 return 1;
1667 }
1668
1669 static int
1670 do_exit_browser(struct hist_browser *browser __maybe_unused,
1671 struct popup_action *act __maybe_unused)
1672 {
1673 return 0;
1674 }
1675
1676 static int
1677 add_exit_opt(struct hist_browser *browser __maybe_unused,
1678 struct popup_action *act, char **optstr)
1679 {
1680 if (asprintf(optstr, "Exit") < 0)
1681 return 0;
1682
1683 act->fn = do_exit_browser;
1684 return 1;
1685 }
1686
1687 static int
1688 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1689 {
1690 if (browser->hists->socket_filter > -1) {
1691 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1692 browser->hists->socket_filter = -1;
1693 perf_hpp__set_elide(HISTC_SOCKET, false);
1694 } else {
1695 browser->hists->socket_filter = act->socket;
1696 perf_hpp__set_elide(HISTC_SOCKET, true);
1697 pstack__push(browser->pstack, &browser->hists->socket_filter);
1698 }
1699
1700 hists__filter_by_socket(browser->hists);
1701 hist_browser__reset(browser);
1702 return 0;
1703 }
1704
1705 static int
1706 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1707 char **optstr, int socket_id)
1708 {
1709 if (socket_id < 0)
1710 return 0;
1711
1712 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1713 (browser->hists->socket_filter > -1) ? "out of" : "into",
1714 socket_id) < 0)
1715 return 0;
1716
1717 act->socket = socket_id;
1718 act->fn = do_zoom_socket;
1719 return 1;
1720 }
1721
1722 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1723 {
1724 u64 nr_entries = 0;
1725 struct rb_node *nd = rb_first(&hb->hists->entries);
1726
1727 if (hb->min_pcnt == 0) {
1728 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1729 return;
1730 }
1731
1732 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1733 nr_entries++;
1734 nd = rb_next(nd);
1735 }
1736
1737 hb->nr_non_filtered_entries = nr_entries;
1738 }
1739
1740 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1741 const char *helpline,
1742 bool left_exits,
1743 struct hist_browser_timer *hbt,
1744 float min_pcnt,
1745 struct perf_env *env)
1746 {
1747 struct hists *hists = evsel__hists(evsel);
1748 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1749 struct branch_info *bi;
1750 #define MAX_OPTIONS 16
1751 char *options[MAX_OPTIONS];
1752 struct popup_action actions[MAX_OPTIONS];
1753 int nr_options = 0;
1754 int key = -1;
1755 char buf[64];
1756 int delay_secs = hbt ? hbt->refresh : 0;
1757 struct perf_hpp_fmt *fmt;
1758
1759 #define HIST_BROWSER_HELP_COMMON \
1760 "h/?/F1 Show this window\n" \
1761 "UP/DOWN/PGUP\n" \
1762 "PGDN/SPACE Navigate\n" \
1763 "q/ESC/CTRL+C Exit browser\n\n" \
1764 "For multiple event sessions:\n\n" \
1765 "TAB/UNTAB Switch events\n\n" \
1766 "For symbolic views (--sort has sym):\n\n" \
1767 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
1768 "ESC Zoom out\n" \
1769 "a Annotate current symbol\n" \
1770 "C Collapse all callchains\n" \
1771 "d Zoom into current DSO\n" \
1772 "E Expand all callchains\n" \
1773 "F Toggle percentage of filtered entries\n" \
1774 "H Display column headers\n" \
1775 "m Display context menu\n" \
1776 "S Zoom into current Processor Socket\n" \
1777
1778 /* help messages are sorted by lexical order of the hotkey */
1779 const char report_help[] = HIST_BROWSER_HELP_COMMON
1780 "i Show header information\n"
1781 "P Print histograms to perf.hist.N\n"
1782 "r Run available scripts\n"
1783 "s Switch to another data file in PWD\n"
1784 "t Zoom into current Thread\n"
1785 "V Verbose (DSO names in callchains, etc)\n"
1786 "/ Filter symbol by name";
1787 const char top_help[] = HIST_BROWSER_HELP_COMMON
1788 "P Print histograms to perf.hist.N\n"
1789 "t Zoom into current Thread\n"
1790 "V Verbose (DSO names in callchains, etc)\n"
1791 "z Toggle zeroing of samples\n"
1792 "f Enable/Disable events\n"
1793 "/ Filter symbol by name";
1794
1795 if (browser == NULL)
1796 return -1;
1797
1798 /* reset abort key so that it can get Ctrl-C as a key */
1799 SLang_reset_tty();
1800 SLang_init_tty(0, 0, 0);
1801
1802 if (min_pcnt) {
1803 browser->min_pcnt = min_pcnt;
1804 hist_browser__update_nr_entries(browser);
1805 }
1806
1807 browser->pstack = pstack__new(3);
1808 if (browser->pstack == NULL)
1809 goto out;
1810
1811 ui_helpline__push(helpline);
1812
1813 memset(options, 0, sizeof(options));
1814 memset(actions, 0, sizeof(actions));
1815
1816 perf_hpp__for_each_format(fmt) {
1817 perf_hpp__reset_width(fmt, hists);
1818 /*
1819 * This is done just once, and activates the horizontal scrolling
1820 * code in the ui_browser code, it would be better to have a the
1821 * counter in the perf_hpp code, but I couldn't find doing it here
1822 * works, FIXME by setting this in hist_browser__new, for now, be
1823 * clever 8-)
1824 */
1825 ++browser->b.columns;
1826 }
1827
1828 if (symbol_conf.col_width_list_str)
1829 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1830
1831 while (1) {
1832 struct thread *thread = NULL;
1833 struct map *map = NULL;
1834 int choice = 0;
1835 int socked_id = -1;
1836
1837 nr_options = 0;
1838
1839 key = hist_browser__run(browser, helpline);
1840
1841 if (browser->he_selection != NULL) {
1842 thread = hist_browser__selected_thread(browser);
1843 map = browser->selection->map;
1844 socked_id = browser->he_selection->socket;
1845 }
1846 switch (key) {
1847 case K_TAB:
1848 case K_UNTAB:
1849 if (nr_events == 1)
1850 continue;
1851 /*
1852 * Exit the browser, let hists__browser_tree
1853 * go to the next or previous
1854 */
1855 goto out_free_stack;
1856 case 'a':
1857 if (!sort__has_sym) {
1858 ui_browser__warning(&browser->b, delay_secs * 2,
1859 "Annotation is only available for symbolic views, "
1860 "include \"sym*\" in --sort to use it.");
1861 continue;
1862 }
1863
1864 if (browser->selection == NULL ||
1865 browser->selection->sym == NULL ||
1866 browser->selection->map->dso->annotate_warned)
1867 continue;
1868
1869 actions->ms.map = browser->selection->map;
1870 actions->ms.sym = browser->selection->sym;
1871 do_annotate(browser, actions);
1872 continue;
1873 case 'P':
1874 hist_browser__dump(browser);
1875 continue;
1876 case 'd':
1877 actions->ms.map = map;
1878 do_zoom_dso(browser, actions);
1879 continue;
1880 case 'V':
1881 browser->show_dso = !browser->show_dso;
1882 continue;
1883 case 't':
1884 actions->thread = thread;
1885 do_zoom_thread(browser, actions);
1886 continue;
1887 case 'S':
1888 actions->socket = socked_id;
1889 do_zoom_socket(browser, actions);
1890 continue;
1891 case '/':
1892 if (ui_browser__input_window("Symbol to show",
1893 "Please enter the name of symbol you want to see.\n"
1894 "To remove the filter later, press / + ENTER.",
1895 buf, "ENTER: OK, ESC: Cancel",
1896 delay_secs * 2) == K_ENTER) {
1897 hists->symbol_filter_str = *buf ? buf : NULL;
1898 hists__filter_by_symbol(hists);
1899 hist_browser__reset(browser);
1900 }
1901 continue;
1902 case 'r':
1903 if (is_report_browser(hbt)) {
1904 actions->thread = NULL;
1905 actions->ms.sym = NULL;
1906 do_run_script(browser, actions);
1907 }
1908 continue;
1909 case 's':
1910 if (is_report_browser(hbt)) {
1911 key = do_switch_data(browser, actions);
1912 if (key == K_SWITCH_INPUT_DATA)
1913 goto out_free_stack;
1914 }
1915 continue;
1916 case 'i':
1917 /* env->arch is NULL for live-mode (i.e. perf top) */
1918 if (env->arch)
1919 tui__header_window(env);
1920 continue;
1921 case 'F':
1922 symbol_conf.filter_relative ^= 1;
1923 continue;
1924 case 'z':
1925 if (!is_report_browser(hbt)) {
1926 struct perf_top *top = hbt->arg;
1927
1928 top->zero = !top->zero;
1929 }
1930 continue;
1931 case K_F1:
1932 case 'h':
1933 case '?':
1934 ui_browser__help_window(&browser->b,
1935 is_report_browser(hbt) ? report_help : top_help);
1936 continue;
1937 case K_ENTER:
1938 case K_RIGHT:
1939 case 'm':
1940 /* menu */
1941 break;
1942 case K_ESC:
1943 case K_LEFT: {
1944 const void *top;
1945
1946 if (pstack__empty(browser->pstack)) {
1947 /*
1948 * Go back to the perf_evsel_menu__run or other user
1949 */
1950 if (left_exits)
1951 goto out_free_stack;
1952
1953 if (key == K_ESC &&
1954 ui_browser__dialog_yesno(&browser->b,
1955 "Do you really want to exit?"))
1956 goto out_free_stack;
1957
1958 continue;
1959 }
1960 top = pstack__peek(browser->pstack);
1961 if (top == &browser->hists->dso_filter) {
1962 /*
1963 * No need to set actions->dso here since
1964 * it's just to remove the current filter.
1965 * Ditto for thread below.
1966 */
1967 do_zoom_dso(browser, actions);
1968 } else if (top == &browser->hists->thread_filter) {
1969 do_zoom_thread(browser, actions);
1970 } else if (top == &browser->hists->socket_filter) {
1971 do_zoom_socket(browser, actions);
1972 }
1973 continue;
1974 }
1975 case 'q':
1976 case CTRL('c'):
1977 goto out_free_stack;
1978 case 'f':
1979 if (!is_report_browser(hbt)) {
1980 struct perf_top *top = hbt->arg;
1981
1982 perf_evlist__toggle_enable(top->evlist);
1983 /*
1984 * No need to refresh, resort/decay histogram
1985 * entries if we are not collecting samples:
1986 */
1987 if (top->evlist->enabled) {
1988 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1989 hbt->refresh = delay_secs;
1990 } else {
1991 helpline = "Press 'f' again to re-enable the events";
1992 hbt->refresh = 0;
1993 }
1994 continue;
1995 }
1996 /* Fall thru */
1997 default:
1998 helpline = "Press '?' for help on key bindings";
1999 continue;
2000 }
2001
2002 if (!sort__has_sym)
2003 goto add_exit_option;
2004
2005 if (browser->selection == NULL)
2006 goto skip_annotation;
2007
2008 if (sort__mode == SORT_MODE__BRANCH) {
2009 bi = browser->he_selection->branch_info;
2010
2011 if (bi == NULL)
2012 goto skip_annotation;
2013
2014 nr_options += add_annotate_opt(browser,
2015 &actions[nr_options],
2016 &options[nr_options],
2017 bi->from.map,
2018 bi->from.sym);
2019 if (bi->to.sym != bi->from.sym)
2020 nr_options += add_annotate_opt(browser,
2021 &actions[nr_options],
2022 &options[nr_options],
2023 bi->to.map,
2024 bi->to.sym);
2025 } else {
2026 nr_options += add_annotate_opt(browser,
2027 &actions[nr_options],
2028 &options[nr_options],
2029 browser->selection->map,
2030 browser->selection->sym);
2031 }
2032 skip_annotation:
2033 nr_options += add_thread_opt(browser, &actions[nr_options],
2034 &options[nr_options], thread);
2035 nr_options += add_dso_opt(browser, &actions[nr_options],
2036 &options[nr_options], map);
2037 nr_options += add_map_opt(browser, &actions[nr_options],
2038 &options[nr_options],
2039 browser->selection ?
2040 browser->selection->map : NULL);
2041 nr_options += add_socket_opt(browser, &actions[nr_options],
2042 &options[nr_options],
2043 socked_id);
2044 /* perf script support */
2045 if (browser->he_selection) {
2046 nr_options += add_script_opt(browser,
2047 &actions[nr_options],
2048 &options[nr_options],
2049 thread, NULL);
2050 /*
2051 * Note that browser->selection != NULL
2052 * when browser->he_selection is not NULL,
2053 * so we don't need to check browser->selection
2054 * before fetching browser->selection->sym like what
2055 * we do before fetching browser->selection->map.
2056 *
2057 * See hist_browser__show_entry.
2058 */
2059 nr_options += add_script_opt(browser,
2060 &actions[nr_options],
2061 &options[nr_options],
2062 NULL, browser->selection->sym);
2063 }
2064 nr_options += add_script_opt(browser, &actions[nr_options],
2065 &options[nr_options], NULL, NULL);
2066 nr_options += add_switch_opt(browser, &actions[nr_options],
2067 &options[nr_options]);
2068 add_exit_option:
2069 nr_options += add_exit_opt(browser, &actions[nr_options],
2070 &options[nr_options]);
2071
2072 do {
2073 struct popup_action *act;
2074
2075 choice = ui__popup_menu(nr_options, options);
2076 if (choice == -1 || choice >= nr_options)
2077 break;
2078
2079 act = &actions[choice];
2080 key = act->fn(browser, act);
2081 } while (key == 1);
2082
2083 if (key == K_SWITCH_INPUT_DATA)
2084 break;
2085 }
2086 out_free_stack:
2087 pstack__delete(browser->pstack);
2088 out:
2089 hist_browser__delete(browser);
2090 free_popup_options(options, MAX_OPTIONS);
2091 return key;
2092 }
2093
2094 struct perf_evsel_menu {
2095 struct ui_browser b;
2096 struct perf_evsel *selection;
2097 bool lost_events, lost_events_warned;
2098 float min_pcnt;
2099 struct perf_env *env;
2100 };
2101
2102 static void perf_evsel_menu__write(struct ui_browser *browser,
2103 void *entry, int row)
2104 {
2105 struct perf_evsel_menu *menu = container_of(browser,
2106 struct perf_evsel_menu, b);
2107 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2108 struct hists *hists = evsel__hists(evsel);
2109 bool current_entry = ui_browser__is_current_entry(browser, row);
2110 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2111 const char *ev_name = perf_evsel__name(evsel);
2112 char bf[256], unit;
2113 const char *warn = " ";
2114 size_t printed;
2115
2116 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2117 HE_COLORSET_NORMAL);
2118
2119 if (perf_evsel__is_group_event(evsel)) {
2120 struct perf_evsel *pos;
2121
2122 ev_name = perf_evsel__group_name(evsel);
2123
2124 for_each_group_member(pos, evsel) {
2125 struct hists *pos_hists = evsel__hists(pos);
2126 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2127 }
2128 }
2129
2130 nr_events = convert_unit(nr_events, &unit);
2131 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2132 unit, unit == ' ' ? "" : " ", ev_name);
2133 ui_browser__printf(browser, "%s", bf);
2134
2135 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2136 if (nr_events != 0) {
2137 menu->lost_events = true;
2138 if (!current_entry)
2139 ui_browser__set_color(browser, HE_COLORSET_TOP);
2140 nr_events = convert_unit(nr_events, &unit);
2141 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2142 nr_events, unit, unit == ' ' ? "" : " ");
2143 warn = bf;
2144 }
2145
2146 ui_browser__write_nstring(browser, warn, browser->width - printed);
2147
2148 if (current_entry)
2149 menu->selection = evsel;
2150 }
2151
2152 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2153 int nr_events, const char *help,
2154 struct hist_browser_timer *hbt)
2155 {
2156 struct perf_evlist *evlist = menu->b.priv;
2157 struct perf_evsel *pos;
2158 const char *title = "Available samples";
2159 int delay_secs = hbt ? hbt->refresh : 0;
2160 int key;
2161
2162 if (ui_browser__show(&menu->b, title,
2163 "ESC: exit, ENTER|->: Browse histograms") < 0)
2164 return -1;
2165
2166 while (1) {
2167 key = ui_browser__run(&menu->b, delay_secs);
2168
2169 switch (key) {
2170 case K_TIMER:
2171 hbt->timer(hbt->arg);
2172
2173 if (!menu->lost_events_warned && menu->lost_events) {
2174 ui_browser__warn_lost_events(&menu->b);
2175 menu->lost_events_warned = true;
2176 }
2177 continue;
2178 case K_RIGHT:
2179 case K_ENTER:
2180 if (!menu->selection)
2181 continue;
2182 pos = menu->selection;
2183 browse_hists:
2184 perf_evlist__set_selected(evlist, pos);
2185 /*
2186 * Give the calling tool a chance to populate the non
2187 * default evsel resorted hists tree.
2188 */
2189 if (hbt)
2190 hbt->timer(hbt->arg);
2191 key = perf_evsel__hists_browse(pos, nr_events, help,
2192 true, hbt,
2193 menu->min_pcnt,
2194 menu->env);
2195 ui_browser__show_title(&menu->b, title);
2196 switch (key) {
2197 case K_TAB:
2198 if (pos->node.next == &evlist->entries)
2199 pos = perf_evlist__first(evlist);
2200 else
2201 pos = perf_evsel__next(pos);
2202 goto browse_hists;
2203 case K_UNTAB:
2204 if (pos->node.prev == &evlist->entries)
2205 pos = perf_evlist__last(evlist);
2206 else
2207 pos = perf_evsel__prev(pos);
2208 goto browse_hists;
2209 case K_SWITCH_INPUT_DATA:
2210 case 'q':
2211 case CTRL('c'):
2212 goto out;
2213 case K_ESC:
2214 default:
2215 continue;
2216 }
2217 case K_LEFT:
2218 continue;
2219 case K_ESC:
2220 if (!ui_browser__dialog_yesno(&menu->b,
2221 "Do you really want to exit?"))
2222 continue;
2223 /* Fall thru */
2224 case 'q':
2225 case CTRL('c'):
2226 goto out;
2227 default:
2228 continue;
2229 }
2230 }
2231
2232 out:
2233 ui_browser__hide(&menu->b);
2234 return key;
2235 }
2236
2237 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2238 void *entry)
2239 {
2240 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2241
2242 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2243 return true;
2244
2245 return false;
2246 }
2247
2248 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2249 int nr_entries, const char *help,
2250 struct hist_browser_timer *hbt,
2251 float min_pcnt,
2252 struct perf_env *env)
2253 {
2254 struct perf_evsel *pos;
2255 struct perf_evsel_menu menu = {
2256 .b = {
2257 .entries = &evlist->entries,
2258 .refresh = ui_browser__list_head_refresh,
2259 .seek = ui_browser__list_head_seek,
2260 .write = perf_evsel_menu__write,
2261 .filter = filter_group_entries,
2262 .nr_entries = nr_entries,
2263 .priv = evlist,
2264 },
2265 .min_pcnt = min_pcnt,
2266 .env = env,
2267 };
2268
2269 ui_helpline__push("Press ESC to exit");
2270
2271 evlist__for_each(evlist, pos) {
2272 const char *ev_name = perf_evsel__name(pos);
2273 size_t line_len = strlen(ev_name) + 7;
2274
2275 if (menu.b.width < line_len)
2276 menu.b.width = line_len;
2277 }
2278
2279 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2280 }
2281
2282 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2283 struct hist_browser_timer *hbt,
2284 float min_pcnt,
2285 struct perf_env *env)
2286 {
2287 int nr_entries = evlist->nr_entries;
2288
2289 single_entry:
2290 if (nr_entries == 1) {
2291 struct perf_evsel *first = perf_evlist__first(evlist);
2292
2293 return perf_evsel__hists_browse(first, nr_entries, help,
2294 false, hbt, min_pcnt,
2295 env);
2296 }
2297
2298 if (symbol_conf.event_group) {
2299 struct perf_evsel *pos;
2300
2301 nr_entries = 0;
2302 evlist__for_each(evlist, pos) {
2303 if (perf_evsel__is_group_leader(pos))
2304 nr_entries++;
2305 }
2306
2307 if (nr_entries == 1)
2308 goto single_entry;
2309 }
2310
2311 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2312 hbt, min_pcnt, env);
2313 }
This page took 0.089495 seconds and 4 git commands to generate.