perf tools: Consolidate output field handling to hpp format routines
[linux-2.6/btrfs-unstable.git] / tools / perf / ui / browsers / hists.c
blob847de116b9ec0d4bce8e3622d394d8ca2c0de80f
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <linux/rbtree.h>
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../arch/common.h"
15 #include "../browser.h"
16 #include "../helpline.h"
17 #include "../util.h"
18 #include "../ui.h"
19 #include "map.h"
21 struct hist_browser {
22 struct ui_browser b;
23 struct hists *hists;
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
26 int print_seq;
27 bool show_dso;
28 float min_pcnt;
29 u64 nr_non_filtered_entries;
30 u64 nr_callchain_rows;
33 extern void hist_browser__init_hpp(void);
35 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
36 const char *ev_name);
37 static void hist_browser__update_nr_entries(struct hist_browser *hb);
39 static struct rb_node *hists__filter_entries(struct rb_node *nd,
40 struct hists *hists,
41 float min_pcnt);
43 static bool hist_browser__has_filter(struct hist_browser *hb)
45 return hists__has_filter(hb->hists) || hb->min_pcnt;
48 static u32 hist_browser__nr_entries(struct hist_browser *hb)
50 u32 nr_entries;
52 if (hist_browser__has_filter(hb))
53 nr_entries = hb->nr_non_filtered_entries;
54 else
55 nr_entries = hb->hists->nr_entries;
57 return nr_entries + hb->nr_callchain_rows;
60 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
62 /* 3 == +/- toggle symbol before actual hist_entry rendering */
63 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
64 sizeof("[k]"));
67 static void hist_browser__reset(struct hist_browser *browser)
70 * The hists__remove_entry_filter() already folds non-filtered
71 * entries so we can assume it has 0 callchain rows.
73 browser->nr_callchain_rows = 0;
75 hist_browser__update_nr_entries(browser);
76 browser->b.nr_entries = hist_browser__nr_entries(browser);
77 hist_browser__refresh_dimensions(browser);
78 ui_browser__reset_index(&browser->b);
81 static char tree__folded_sign(bool unfolded)
83 return unfolded ? '-' : '+';
86 static char map_symbol__folded(const struct map_symbol *ms)
88 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
91 static char hist_entry__folded(const struct hist_entry *he)
93 return map_symbol__folded(&he->ms);
96 static char callchain_list__folded(const struct callchain_list *cl)
98 return map_symbol__folded(&cl->ms);
101 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
103 ms->unfolded = unfold ? ms->has_children : false;
106 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
108 int n = 0;
109 struct rb_node *nd;
111 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
112 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
113 struct callchain_list *chain;
114 char folded_sign = ' '; /* No children */
116 list_for_each_entry(chain, &child->val, list) {
117 ++n;
118 /* We need this because we may not have children */
119 folded_sign = callchain_list__folded(chain);
120 if (folded_sign == '+')
121 break;
124 if (folded_sign == '-') /* Have children and they're unfolded */
125 n += callchain_node__count_rows_rb_tree(child);
128 return n;
131 static int callchain_node__count_rows(struct callchain_node *node)
133 struct callchain_list *chain;
134 bool unfolded = false;
135 int n = 0;
137 list_for_each_entry(chain, &node->val, list) {
138 ++n;
139 unfolded = chain->ms.unfolded;
142 if (unfolded)
143 n += callchain_node__count_rows_rb_tree(node);
145 return n;
148 static int callchain__count_rows(struct rb_root *chain)
150 struct rb_node *nd;
151 int n = 0;
153 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
154 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
155 n += callchain_node__count_rows(node);
158 return n;
161 static bool map_symbol__toggle_fold(struct map_symbol *ms)
163 if (!ms)
164 return false;
166 if (!ms->has_children)
167 return false;
169 ms->unfolded = !ms->unfolded;
170 return true;
173 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
175 struct rb_node *nd = rb_first(&node->rb_root);
177 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
178 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
179 struct callchain_list *chain;
180 bool first = true;
182 list_for_each_entry(chain, &child->val, list) {
183 if (first) {
184 first = false;
185 chain->ms.has_children = chain->list.next != &child->val ||
186 !RB_EMPTY_ROOT(&child->rb_root);
187 } else
188 chain->ms.has_children = chain->list.next == &child->val &&
189 !RB_EMPTY_ROOT(&child->rb_root);
192 callchain_node__init_have_children_rb_tree(child);
196 static void callchain_node__init_have_children(struct callchain_node *node)
198 struct callchain_list *chain;
200 list_for_each_entry(chain, &node->val, list)
201 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
203 callchain_node__init_have_children_rb_tree(node);
206 static void callchain__init_have_children(struct rb_root *root)
208 struct rb_node *nd;
210 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
211 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
212 callchain_node__init_have_children(node);
216 static void hist_entry__init_have_children(struct hist_entry *he)
218 if (!he->init_have_children) {
219 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
220 callchain__init_have_children(&he->sorted_chain);
221 he->init_have_children = true;
225 static bool hist_browser__toggle_fold(struct hist_browser *browser)
227 if (map_symbol__toggle_fold(browser->selection)) {
228 struct hist_entry *he = browser->he_selection;
230 hist_entry__init_have_children(he);
231 browser->b.nr_entries -= he->nr_rows;
232 browser->nr_callchain_rows -= he->nr_rows;
234 if (he->ms.unfolded)
235 he->nr_rows = callchain__count_rows(&he->sorted_chain);
236 else
237 he->nr_rows = 0;
239 browser->b.nr_entries += he->nr_rows;
240 browser->nr_callchain_rows += he->nr_rows;
242 return true;
245 /* If it doesn't have children, no toggling performed */
246 return false;
249 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
251 int n = 0;
252 struct rb_node *nd;
254 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
255 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
256 struct callchain_list *chain;
257 bool has_children = false;
259 list_for_each_entry(chain, &child->val, list) {
260 ++n;
261 map_symbol__set_folding(&chain->ms, unfold);
262 has_children = chain->ms.has_children;
265 if (has_children)
266 n += callchain_node__set_folding_rb_tree(child, unfold);
269 return n;
272 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
274 struct callchain_list *chain;
275 bool has_children = false;
276 int n = 0;
278 list_for_each_entry(chain, &node->val, list) {
279 ++n;
280 map_symbol__set_folding(&chain->ms, unfold);
281 has_children = chain->ms.has_children;
284 if (has_children)
285 n += callchain_node__set_folding_rb_tree(node, unfold);
287 return n;
290 static int callchain__set_folding(struct rb_root *chain, bool unfold)
292 struct rb_node *nd;
293 int n = 0;
295 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
296 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
297 n += callchain_node__set_folding(node, unfold);
300 return n;
303 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
305 hist_entry__init_have_children(he);
306 map_symbol__set_folding(&he->ms, unfold);
308 if (he->ms.has_children) {
309 int n = callchain__set_folding(&he->sorted_chain, unfold);
310 he->nr_rows = unfold ? n : 0;
311 } else
312 he->nr_rows = 0;
315 static void
316 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
318 struct rb_node *nd;
319 struct hists *hists = browser->hists;
321 for (nd = rb_first(&hists->entries);
322 (nd = hists__filter_entries(nd, hists, browser->min_pcnt)) != NULL;
323 nd = rb_next(nd)) {
324 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
325 hist_entry__set_folding(he, unfold);
326 browser->nr_callchain_rows += he->nr_rows;
330 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
332 browser->nr_callchain_rows = 0;
333 __hist_browser__set_folding(browser, unfold);
335 browser->b.nr_entries = hist_browser__nr_entries(browser);
336 /* Go to the start, we may be way after valid entries after a collapse */
337 ui_browser__reset_index(&browser->b);
340 static void ui_browser__warn_lost_events(struct ui_browser *browser)
342 ui_browser__warning(browser, 4,
343 "Events are being lost, check IO/CPU overload!\n\n"
344 "You may want to run 'perf' using a RT scheduler policy:\n\n"
345 " perf top -r 80\n\n"
346 "Or reduce the sampling frequency.");
349 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
350 struct hist_browser_timer *hbt)
352 int key;
353 char title[160];
354 int delay_secs = hbt ? hbt->refresh : 0;
356 browser->b.entries = &browser->hists->entries;
357 browser->b.nr_entries = hist_browser__nr_entries(browser);
359 hist_browser__refresh_dimensions(browser);
360 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
362 if (ui_browser__show(&browser->b, title,
363 "Press '?' for help on key bindings") < 0)
364 return -1;
366 while (1) {
367 key = ui_browser__run(&browser->b, delay_secs);
369 switch (key) {
370 case K_TIMER: {
371 u64 nr_entries;
372 hbt->timer(hbt->arg);
374 if (hist_browser__has_filter(browser))
375 hist_browser__update_nr_entries(browser);
377 nr_entries = hist_browser__nr_entries(browser);
378 ui_browser__update_nr_entries(&browser->b, nr_entries);
380 if (browser->hists->stats.nr_lost_warned !=
381 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
382 browser->hists->stats.nr_lost_warned =
383 browser->hists->stats.nr_events[PERF_RECORD_LOST];
384 ui_browser__warn_lost_events(&browser->b);
387 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
388 ui_browser__show_title(&browser->b, title);
389 continue;
391 case 'D': { /* Debug */
392 static int seq;
393 struct hist_entry *h = rb_entry(browser->b.top,
394 struct hist_entry, rb_node);
395 ui_helpline__pop();
396 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
397 seq++, browser->b.nr_entries,
398 browser->hists->nr_entries,
399 browser->b.height,
400 browser->b.index,
401 browser->b.top_idx,
402 h->row_offset, h->nr_rows);
404 break;
405 case 'C':
406 /* Collapse the whole world. */
407 hist_browser__set_folding(browser, false);
408 break;
409 case 'E':
410 /* Expand the whole world. */
411 hist_browser__set_folding(browser, true);
412 break;
413 case K_ENTER:
414 if (hist_browser__toggle_fold(browser))
415 break;
416 /* fall thru */
417 default:
418 goto out;
421 out:
422 ui_browser__hide(&browser->b);
423 return key;
426 static char *callchain_list__sym_name(struct callchain_list *cl,
427 char *bf, size_t bfsize, bool show_dso)
429 int printed;
431 if (cl->ms.sym)
432 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
433 else
434 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
436 if (show_dso)
437 scnprintf(bf + printed, bfsize - printed, " %s",
438 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
440 return bf;
443 #define LEVEL_OFFSET_STEP 3
445 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
446 struct callchain_node *chain_node,
447 u64 total, int level,
448 unsigned short row,
449 off_t *row_offset,
450 bool *is_current_entry)
452 struct rb_node *node;
453 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
454 u64 new_total, remaining;
456 if (callchain_param.mode == CHAIN_GRAPH_REL)
457 new_total = chain_node->children_hit;
458 else
459 new_total = total;
461 remaining = new_total;
462 node = rb_first(&chain_node->rb_root);
463 while (node) {
464 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
465 struct rb_node *next = rb_next(node);
466 u64 cumul = callchain_cumul_hits(child);
467 struct callchain_list *chain;
468 char folded_sign = ' ';
469 int first = true;
470 int extra_offset = 0;
472 remaining -= cumul;
474 list_for_each_entry(chain, &child->val, list) {
475 char bf[1024], *alloc_str;
476 const char *str;
477 int color;
478 bool was_first = first;
480 if (first)
481 first = false;
482 else
483 extra_offset = LEVEL_OFFSET_STEP;
485 folded_sign = callchain_list__folded(chain);
486 if (*row_offset != 0) {
487 --*row_offset;
488 goto do_next;
491 alloc_str = NULL;
492 str = callchain_list__sym_name(chain, bf, sizeof(bf),
493 browser->show_dso);
494 if (was_first) {
495 double percent = cumul * 100.0 / new_total;
497 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
498 str = "Not enough memory!";
499 else
500 str = alloc_str;
503 color = HE_COLORSET_NORMAL;
504 width = browser->b.width - (offset + extra_offset + 2);
505 if (ui_browser__is_current_entry(&browser->b, row)) {
506 browser->selection = &chain->ms;
507 color = HE_COLORSET_SELECTED;
508 *is_current_entry = true;
511 ui_browser__set_color(&browser->b, color);
512 ui_browser__gotorc(&browser->b, row, 0);
513 slsmg_write_nstring(" ", offset + extra_offset);
514 slsmg_printf("%c ", folded_sign);
515 slsmg_write_nstring(str, width);
516 free(alloc_str);
518 if (++row == browser->b.height)
519 goto out;
520 do_next:
521 if (folded_sign == '+')
522 break;
525 if (folded_sign == '-') {
526 const int new_level = level + (extra_offset ? 2 : 1);
527 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
528 new_level, row, row_offset,
529 is_current_entry);
531 if (row == browser->b.height)
532 goto out;
533 node = next;
535 out:
536 return row - first_row;
539 static int hist_browser__show_callchain_node(struct hist_browser *browser,
540 struct callchain_node *node,
541 int level, unsigned short row,
542 off_t *row_offset,
543 bool *is_current_entry)
545 struct callchain_list *chain;
546 int first_row = row,
547 offset = level * LEVEL_OFFSET_STEP,
548 width = browser->b.width - offset;
549 char folded_sign = ' ';
551 list_for_each_entry(chain, &node->val, list) {
552 char bf[1024], *s;
553 int color;
555 folded_sign = callchain_list__folded(chain);
557 if (*row_offset != 0) {
558 --*row_offset;
559 continue;
562 color = HE_COLORSET_NORMAL;
563 if (ui_browser__is_current_entry(&browser->b, row)) {
564 browser->selection = &chain->ms;
565 color = HE_COLORSET_SELECTED;
566 *is_current_entry = true;
569 s = callchain_list__sym_name(chain, bf, sizeof(bf),
570 browser->show_dso);
571 ui_browser__gotorc(&browser->b, row, 0);
572 ui_browser__set_color(&browser->b, color);
573 slsmg_write_nstring(" ", offset);
574 slsmg_printf("%c ", folded_sign);
575 slsmg_write_nstring(s, width - 2);
577 if (++row == browser->b.height)
578 goto out;
581 if (folded_sign == '-')
582 row += hist_browser__show_callchain_node_rb_tree(browser, node,
583 browser->hists->stats.total_period,
584 level + 1, row,
585 row_offset,
586 is_current_entry);
587 out:
588 return row - first_row;
591 static int hist_browser__show_callchain(struct hist_browser *browser,
592 struct rb_root *chain,
593 int level, unsigned short row,
594 off_t *row_offset,
595 bool *is_current_entry)
597 struct rb_node *nd;
598 int first_row = row;
600 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
601 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
603 row += hist_browser__show_callchain_node(browser, node, level,
604 row, row_offset,
605 is_current_entry);
606 if (row == browser->b.height)
607 break;
610 return row - first_row;
613 struct hpp_arg {
614 struct ui_browser *b;
615 char folded_sign;
616 bool current_entry;
619 static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
621 struct hpp_arg *arg = hpp->ptr;
623 if (arg->current_entry && arg->b->navkeypressed)
624 ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
625 else
626 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
628 if (front) {
629 if (!symbol_conf.use_callchain)
630 return 0;
632 slsmg_printf("%c ", arg->folded_sign);
633 return 2;
636 return 0;
639 static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
641 struct hpp_arg *arg = hpp->ptr;
643 if (!arg->current_entry || !arg->b->navkeypressed)
644 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
645 return 0;
648 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
650 struct hpp_arg *arg = hpp->ptr;
651 int ret;
652 va_list args;
653 double percent;
655 va_start(args, fmt);
656 percent = va_arg(args, double);
657 va_end(args);
659 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
661 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
662 slsmg_printf("%s", hpp->buf);
664 advance_hpp(hpp, ret);
665 return ret;
668 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
669 static u64 __hpp_get_##_field(struct hist_entry *he) \
671 return he->stat._field; \
674 static int \
675 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
676 struct perf_hpp *hpp, \
677 struct hist_entry *he) \
679 return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \
680 __hpp__slsmg_color_printf, true); \
683 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback)
684 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback)
685 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback)
686 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback)
687 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback)
689 #undef __HPP_COLOR_PERCENT_FN
691 void hist_browser__init_hpp(void)
693 perf_hpp__init();
695 perf_hpp__format[PERF_HPP__OVERHEAD].color =
696 hist_browser__hpp_color_overhead;
697 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
698 hist_browser__hpp_color_overhead_sys;
699 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
700 hist_browser__hpp_color_overhead_us;
701 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
702 hist_browser__hpp_color_overhead_guest_sys;
703 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
704 hist_browser__hpp_color_overhead_guest_us;
707 static int hist_browser__show_entry(struct hist_browser *browser,
708 struct hist_entry *entry,
709 unsigned short row)
711 char s[256];
712 int printed = 0;
713 int width = browser->b.width;
714 char folded_sign = ' ';
715 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
716 off_t row_offset = entry->row_offset;
717 bool first = true;
718 struct perf_hpp_fmt *fmt;
720 if (current_entry) {
721 browser->he_selection = entry;
722 browser->selection = &entry->ms;
725 if (symbol_conf.use_callchain) {
726 hist_entry__init_have_children(entry);
727 folded_sign = hist_entry__folded(entry);
730 if (row_offset == 0) {
731 struct hpp_arg arg = {
732 .b = &browser->b,
733 .folded_sign = folded_sign,
734 .current_entry = current_entry,
736 struct perf_hpp hpp = {
737 .buf = s,
738 .size = sizeof(s),
739 .ptr = &arg,
742 ui_browser__gotorc(&browser->b, row, 0);
744 perf_hpp__for_each_format(fmt) {
745 if (!first) {
746 slsmg_printf(" ");
747 width -= 2;
749 first = false;
751 if (fmt->color) {
752 width -= fmt->color(fmt, &hpp, entry);
753 } else {
754 width -= fmt->entry(fmt, &hpp, entry);
755 slsmg_printf("%s", s);
759 /* The scroll bar isn't being used */
760 if (!browser->b.navkeypressed)
761 width += 1;
763 slsmg_write_nstring("", width);
765 ++row;
766 ++printed;
767 } else
768 --row_offset;
770 if (folded_sign == '-' && row != browser->b.height) {
771 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
772 1, row, &row_offset,
773 &current_entry);
774 if (current_entry)
775 browser->he_selection = entry;
778 return printed;
781 static void ui_browser__hists_init_top(struct ui_browser *browser)
783 if (browser->top == NULL) {
784 struct hist_browser *hb;
786 hb = container_of(browser, struct hist_browser, b);
787 browser->top = rb_first(&hb->hists->entries);
791 static unsigned int hist_browser__refresh(struct ui_browser *browser)
793 unsigned row = 0;
794 struct rb_node *nd;
795 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
797 ui_browser__hists_init_top(browser);
799 for (nd = browser->top; nd; nd = rb_next(nd)) {
800 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
801 u64 total = hists__total_period(h->hists);
802 float percent = 0.0;
804 if (h->filtered)
805 continue;
807 if (total)
808 percent = h->stat.period * 100.0 / total;
810 if (percent < hb->min_pcnt)
811 continue;
813 row += hist_browser__show_entry(hb, h, row);
814 if (row == browser->height)
815 break;
818 return row;
821 static struct rb_node *hists__filter_entries(struct rb_node *nd,
822 struct hists *hists,
823 float min_pcnt)
825 while (nd != NULL) {
826 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
827 u64 total = hists__total_period(hists);
828 float percent = 0.0;
830 if (total)
831 percent = h->stat.period * 100.0 / total;
833 if (percent < min_pcnt)
834 return NULL;
836 if (!h->filtered)
837 return nd;
839 nd = rb_next(nd);
842 return NULL;
845 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
846 struct hists *hists,
847 float min_pcnt)
849 while (nd != NULL) {
850 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
851 u64 total = hists__total_period(hists);
852 float percent = 0.0;
854 if (total)
855 percent = h->stat.period * 100.0 / total;
857 if (!h->filtered && percent >= min_pcnt)
858 return nd;
860 nd = rb_prev(nd);
863 return NULL;
866 static void ui_browser__hists_seek(struct ui_browser *browser,
867 off_t offset, int whence)
869 struct hist_entry *h;
870 struct rb_node *nd;
871 bool first = true;
872 struct hist_browser *hb;
874 hb = container_of(browser, struct hist_browser, b);
876 if (browser->nr_entries == 0)
877 return;
879 ui_browser__hists_init_top(browser);
881 switch (whence) {
882 case SEEK_SET:
883 nd = hists__filter_entries(rb_first(browser->entries),
884 hb->hists, hb->min_pcnt);
885 break;
886 case SEEK_CUR:
887 nd = browser->top;
888 goto do_offset;
889 case SEEK_END:
890 nd = hists__filter_prev_entries(rb_last(browser->entries),
891 hb->hists, hb->min_pcnt);
892 first = false;
893 break;
894 default:
895 return;
899 * Moves not relative to the first visible entry invalidates its
900 * row_offset:
902 h = rb_entry(browser->top, struct hist_entry, rb_node);
903 h->row_offset = 0;
906 * Here we have to check if nd is expanded (+), if it is we can't go
907 * the next top level hist_entry, instead we must compute an offset of
908 * what _not_ to show and not change the first visible entry.
910 * This offset increments when we are going from top to bottom and
911 * decreases when we're going from bottom to top.
913 * As we don't have backpointers to the top level in the callchains
914 * structure, we need to always print the whole hist_entry callchain,
915 * skipping the first ones that are before the first visible entry
916 * and stop when we printed enough lines to fill the screen.
918 do_offset:
919 if (offset > 0) {
920 do {
921 h = rb_entry(nd, struct hist_entry, rb_node);
922 if (h->ms.unfolded) {
923 u16 remaining = h->nr_rows - h->row_offset;
924 if (offset > remaining) {
925 offset -= remaining;
926 h->row_offset = 0;
927 } else {
928 h->row_offset += offset;
929 offset = 0;
930 browser->top = nd;
931 break;
934 nd = hists__filter_entries(rb_next(nd), hb->hists,
935 hb->min_pcnt);
936 if (nd == NULL)
937 break;
938 --offset;
939 browser->top = nd;
940 } while (offset != 0);
941 } else if (offset < 0) {
942 while (1) {
943 h = rb_entry(nd, struct hist_entry, rb_node);
944 if (h->ms.unfolded) {
945 if (first) {
946 if (-offset > h->row_offset) {
947 offset += h->row_offset;
948 h->row_offset = 0;
949 } else {
950 h->row_offset += offset;
951 offset = 0;
952 browser->top = nd;
953 break;
955 } else {
956 if (-offset > h->nr_rows) {
957 offset += h->nr_rows;
958 h->row_offset = 0;
959 } else {
960 h->row_offset = h->nr_rows + offset;
961 offset = 0;
962 browser->top = nd;
963 break;
968 nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
969 hb->min_pcnt);
970 if (nd == NULL)
971 break;
972 ++offset;
973 browser->top = nd;
974 if (offset == 0) {
976 * Last unfiltered hist_entry, check if it is
977 * unfolded, if it is then we should have
978 * row_offset at its last entry.
980 h = rb_entry(nd, struct hist_entry, rb_node);
981 if (h->ms.unfolded)
982 h->row_offset = h->nr_rows;
983 break;
985 first = false;
987 } else {
988 browser->top = nd;
989 h = rb_entry(nd, struct hist_entry, rb_node);
990 h->row_offset = 0;
994 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
995 struct callchain_node *chain_node,
996 u64 total, int level,
997 FILE *fp)
999 struct rb_node *node;
1000 int offset = level * LEVEL_OFFSET_STEP;
1001 u64 new_total, remaining;
1002 int printed = 0;
1004 if (callchain_param.mode == CHAIN_GRAPH_REL)
1005 new_total = chain_node->children_hit;
1006 else
1007 new_total = total;
1009 remaining = new_total;
1010 node = rb_first(&chain_node->rb_root);
1011 while (node) {
1012 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1013 struct rb_node *next = rb_next(node);
1014 u64 cumul = callchain_cumul_hits(child);
1015 struct callchain_list *chain;
1016 char folded_sign = ' ';
1017 int first = true;
1018 int extra_offset = 0;
1020 remaining -= cumul;
1022 list_for_each_entry(chain, &child->val, list) {
1023 char bf[1024], *alloc_str;
1024 const char *str;
1025 bool was_first = first;
1027 if (first)
1028 first = false;
1029 else
1030 extra_offset = LEVEL_OFFSET_STEP;
1032 folded_sign = callchain_list__folded(chain);
1034 alloc_str = NULL;
1035 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1036 browser->show_dso);
1037 if (was_first) {
1038 double percent = cumul * 100.0 / new_total;
1040 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1041 str = "Not enough memory!";
1042 else
1043 str = alloc_str;
1046 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1047 free(alloc_str);
1048 if (folded_sign == '+')
1049 break;
1052 if (folded_sign == '-') {
1053 const int new_level = level + (extra_offset ? 2 : 1);
1054 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1055 new_level, fp);
1058 node = next;
1061 return printed;
1064 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1065 struct callchain_node *node,
1066 int level, FILE *fp)
1068 struct callchain_list *chain;
1069 int offset = level * LEVEL_OFFSET_STEP;
1070 char folded_sign = ' ';
1071 int printed = 0;
1073 list_for_each_entry(chain, &node->val, list) {
1074 char bf[1024], *s;
1076 folded_sign = callchain_list__folded(chain);
1077 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1078 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1081 if (folded_sign == '-')
1082 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1083 browser->hists->stats.total_period,
1084 level + 1, fp);
1085 return printed;
1088 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1089 struct rb_root *chain, int level, FILE *fp)
1091 struct rb_node *nd;
1092 int printed = 0;
1094 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1095 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1097 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1100 return printed;
1103 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1104 struct hist_entry *he, FILE *fp)
1106 char s[8192];
1107 int printed = 0;
1108 char folded_sign = ' ';
1109 struct perf_hpp hpp = {
1110 .buf = s,
1111 .size = sizeof(s),
1113 struct perf_hpp_fmt *fmt;
1114 bool first = true;
1115 int ret;
1117 if (symbol_conf.use_callchain)
1118 folded_sign = hist_entry__folded(he);
1120 if (symbol_conf.use_callchain)
1121 printed += fprintf(fp, "%c ", folded_sign);
1123 perf_hpp__for_each_format(fmt) {
1124 if (!first) {
1125 ret = scnprintf(hpp.buf, hpp.size, " ");
1126 advance_hpp(&hpp, ret);
1127 } else
1128 first = false;
1130 ret = fmt->entry(fmt, &hpp, he);
1131 advance_hpp(&hpp, ret);
1133 printed += fprintf(fp, "%s\n", rtrim(s));
1135 if (folded_sign == '-')
1136 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1138 return printed;
1141 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1143 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1144 browser->hists,
1145 browser->min_pcnt);
1146 int printed = 0;
1148 while (nd) {
1149 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1151 printed += hist_browser__fprintf_entry(browser, h, fp);
1152 nd = hists__filter_entries(rb_next(nd), browser->hists,
1153 browser->min_pcnt);
1156 return printed;
1159 static int hist_browser__dump(struct hist_browser *browser)
1161 char filename[64];
1162 FILE *fp;
1164 while (1) {
1165 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1166 if (access(filename, F_OK))
1167 break;
1169 * XXX: Just an arbitrary lazy upper limit
1171 if (++browser->print_seq == 8192) {
1172 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1173 return -1;
1177 fp = fopen(filename, "w");
1178 if (fp == NULL) {
1179 char bf[64];
1180 const char *err = strerror_r(errno, bf, sizeof(bf));
1181 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1182 return -1;
1185 ++browser->print_seq;
1186 hist_browser__fprintf(browser, fp);
1187 fclose(fp);
1188 ui_helpline__fpush("%s written!", filename);
1190 return 0;
1193 static struct hist_browser *hist_browser__new(struct hists *hists)
1195 struct hist_browser *browser = zalloc(sizeof(*browser));
1197 if (browser) {
1198 browser->hists = hists;
1199 browser->b.refresh = hist_browser__refresh;
1200 browser->b.seek = ui_browser__hists_seek;
1201 browser->b.use_navkeypressed = true;
1204 return browser;
1207 static void hist_browser__delete(struct hist_browser *browser)
1209 free(browser);
1212 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1214 return browser->he_selection;
1217 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1219 return browser->he_selection->thread;
1222 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1223 const char *ev_name)
1225 char unit;
1226 int printed;
1227 const struct dso *dso = hists->dso_filter;
1228 const struct thread *thread = hists->thread_filter;
1229 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1230 u64 nr_events = hists->stats.total_period;
1231 struct perf_evsel *evsel = hists_to_evsel(hists);
1232 char buf[512];
1233 size_t buflen = sizeof(buf);
1235 if (symbol_conf.filter_relative) {
1236 nr_samples = hists->stats.nr_non_filtered_samples;
1237 nr_events = hists->stats.total_non_filtered_period;
1240 if (perf_evsel__is_group_event(evsel)) {
1241 struct perf_evsel *pos;
1243 perf_evsel__group_desc(evsel, buf, buflen);
1244 ev_name = buf;
1246 for_each_group_member(pos, evsel) {
1247 if (symbol_conf.filter_relative) {
1248 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1249 nr_events += pos->hists.stats.total_non_filtered_period;
1250 } else {
1251 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1252 nr_events += pos->hists.stats.total_period;
1257 nr_samples = convert_unit(nr_samples, &unit);
1258 printed = scnprintf(bf, size,
1259 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1260 nr_samples, unit, ev_name, nr_events);
1263 if (hists->uid_filter_str)
1264 printed += snprintf(bf + printed, size - printed,
1265 ", UID: %s", hists->uid_filter_str);
1266 if (thread)
1267 printed += scnprintf(bf + printed, size - printed,
1268 ", Thread: %s(%d)",
1269 (thread->comm_set ? thread__comm_str(thread) : ""),
1270 thread->tid);
1271 if (dso)
1272 printed += scnprintf(bf + printed, size - printed,
1273 ", DSO: %s", dso->short_name);
1274 return printed;
1277 static inline void free_popup_options(char **options, int n)
1279 int i;
1281 for (i = 0; i < n; ++i)
1282 zfree(&options[i]);
1285 /* Check whether the browser is for 'top' or 'report' */
1286 static inline bool is_report_browser(void *timer)
1288 return timer == NULL;
1292 * Only runtime switching of perf data file will make "input_name" point
1293 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1294 * whether we need to call free() for current "input_name" during the switch.
1296 static bool is_input_name_malloced = false;
1298 static int switch_data_file(void)
1300 char *pwd, *options[32], *abs_path[32], *tmp;
1301 DIR *pwd_dir;
1302 int nr_options = 0, choice = -1, ret = -1;
1303 struct dirent *dent;
1305 pwd = getenv("PWD");
1306 if (!pwd)
1307 return ret;
1309 pwd_dir = opendir(pwd);
1310 if (!pwd_dir)
1311 return ret;
1313 memset(options, 0, sizeof(options));
1314 memset(options, 0, sizeof(abs_path));
1316 while ((dent = readdir(pwd_dir))) {
1317 char path[PATH_MAX];
1318 u64 magic;
1319 char *name = dent->d_name;
1320 FILE *file;
1322 if (!(dent->d_type == DT_REG))
1323 continue;
1325 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1327 file = fopen(path, "r");
1328 if (!file)
1329 continue;
1331 if (fread(&magic, 1, 8, file) < 8)
1332 goto close_file_and_continue;
1334 if (is_perf_magic(magic)) {
1335 options[nr_options] = strdup(name);
1336 if (!options[nr_options])
1337 goto close_file_and_continue;
1339 abs_path[nr_options] = strdup(path);
1340 if (!abs_path[nr_options]) {
1341 zfree(&options[nr_options]);
1342 ui__warning("Can't search all data files due to memory shortage.\n");
1343 fclose(file);
1344 break;
1347 nr_options++;
1350 close_file_and_continue:
1351 fclose(file);
1352 if (nr_options >= 32) {
1353 ui__warning("Too many perf data files in PWD!\n"
1354 "Only the first 32 files will be listed.\n");
1355 break;
1358 closedir(pwd_dir);
1360 if (nr_options) {
1361 choice = ui__popup_menu(nr_options, options);
1362 if (choice < nr_options && choice >= 0) {
1363 tmp = strdup(abs_path[choice]);
1364 if (tmp) {
1365 if (is_input_name_malloced)
1366 free((void *)input_name);
1367 input_name = tmp;
1368 is_input_name_malloced = true;
1369 ret = 0;
1370 } else
1371 ui__warning("Data switch failed due to memory shortage!\n");
1375 free_popup_options(options, nr_options);
1376 free_popup_options(abs_path, nr_options);
1377 return ret;
1380 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1382 u64 nr_entries = 0;
1383 struct rb_node *nd = rb_first(&hb->hists->entries);
1385 if (hb->min_pcnt == 0) {
1386 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1387 return;
1390 while ((nd = hists__filter_entries(nd, hb->hists,
1391 hb->min_pcnt)) != NULL) {
1392 nr_entries++;
1393 nd = rb_next(nd);
1396 hb->nr_non_filtered_entries = nr_entries;
1399 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1400 const char *helpline, const char *ev_name,
1401 bool left_exits,
1402 struct hist_browser_timer *hbt,
1403 float min_pcnt,
1404 struct perf_session_env *env)
1406 struct hists *hists = &evsel->hists;
1407 struct hist_browser *browser = hist_browser__new(hists);
1408 struct branch_info *bi;
1409 struct pstack *fstack;
1410 char *options[16];
1411 int nr_options = 0;
1412 int key = -1;
1413 char buf[64];
1414 char script_opt[64];
1415 int delay_secs = hbt ? hbt->refresh : 0;
1417 #define HIST_BROWSER_HELP_COMMON \
1418 "h/?/F1 Show this window\n" \
1419 "UP/DOWN/PGUP\n" \
1420 "PGDN/SPACE Navigate\n" \
1421 "q/ESC/CTRL+C Exit browser\n\n" \
1422 "For multiple event sessions:\n\n" \
1423 "TAB/UNTAB Switch events\n\n" \
1424 "For symbolic views (--sort has sym):\n\n" \
1425 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1426 "<- Zoom out\n" \
1427 "a Annotate current symbol\n" \
1428 "C Collapse all callchains\n" \
1429 "d Zoom into current DSO\n" \
1430 "E Expand all callchains\n" \
1431 "F Toggle percentage of filtered entries\n" \
1433 /* help messages are sorted by lexical order of the hotkey */
1434 const char report_help[] = HIST_BROWSER_HELP_COMMON
1435 "i Show header information\n"
1436 "P Print histograms to perf.hist.N\n"
1437 "r Run available scripts\n"
1438 "s Switch to another data file in PWD\n"
1439 "t Zoom into current Thread\n"
1440 "V Verbose (DSO names in callchains, etc)\n"
1441 "/ Filter symbol by name";
1442 const char top_help[] = HIST_BROWSER_HELP_COMMON
1443 "P Print histograms to perf.hist.N\n"
1444 "t Zoom into current Thread\n"
1445 "V Verbose (DSO names in callchains, etc)\n"
1446 "/ Filter symbol by name";
1448 if (browser == NULL)
1449 return -1;
1451 if (min_pcnt) {
1452 browser->min_pcnt = min_pcnt;
1453 hist_browser__update_nr_entries(browser);
1456 fstack = pstack__new(2);
1457 if (fstack == NULL)
1458 goto out;
1460 ui_helpline__push(helpline);
1462 memset(options, 0, sizeof(options));
1464 while (1) {
1465 const struct thread *thread = NULL;
1466 const struct dso *dso = NULL;
1467 int choice = 0,
1468 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1469 annotate_f = -2, annotate_t = -2, browse_map = -2;
1470 int scripts_comm = -2, scripts_symbol = -2,
1471 scripts_all = -2, switch_data = -2;
1473 nr_options = 0;
1475 key = hist_browser__run(browser, ev_name, hbt);
1477 if (browser->he_selection != NULL) {
1478 thread = hist_browser__selected_thread(browser);
1479 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1481 switch (key) {
1482 case K_TAB:
1483 case K_UNTAB:
1484 if (nr_events == 1)
1485 continue;
1487 * Exit the browser, let hists__browser_tree
1488 * go to the next or previous
1490 goto out_free_stack;
1491 case 'a':
1492 if (!sort__has_sym) {
1493 ui_browser__warning(&browser->b, delay_secs * 2,
1494 "Annotation is only available for symbolic views, "
1495 "include \"sym*\" in --sort to use it.");
1496 continue;
1499 if (browser->selection == NULL ||
1500 browser->selection->sym == NULL ||
1501 browser->selection->map->dso->annotate_warned)
1502 continue;
1503 goto do_annotate;
1504 case 'P':
1505 hist_browser__dump(browser);
1506 continue;
1507 case 'd':
1508 goto zoom_dso;
1509 case 'V':
1510 browser->show_dso = !browser->show_dso;
1511 continue;
1512 case 't':
1513 goto zoom_thread;
1514 case '/':
1515 if (ui_browser__input_window("Symbol to show",
1516 "Please enter the name of symbol you want to see",
1517 buf, "ENTER: OK, ESC: Cancel",
1518 delay_secs * 2) == K_ENTER) {
1519 hists->symbol_filter_str = *buf ? buf : NULL;
1520 hists__filter_by_symbol(hists);
1521 hist_browser__reset(browser);
1523 continue;
1524 case 'r':
1525 if (is_report_browser(hbt))
1526 goto do_scripts;
1527 continue;
1528 case 's':
1529 if (is_report_browser(hbt))
1530 goto do_data_switch;
1531 continue;
1532 case 'i':
1533 /* env->arch is NULL for live-mode (i.e. perf top) */
1534 if (env->arch)
1535 tui__header_window(env);
1536 continue;
1537 case 'F':
1538 symbol_conf.filter_relative ^= 1;
1539 continue;
1540 case K_F1:
1541 case 'h':
1542 case '?':
1543 ui_browser__help_window(&browser->b,
1544 is_report_browser(hbt) ? report_help : top_help);
1545 continue;
1546 case K_ENTER:
1547 case K_RIGHT:
1548 /* menu */
1549 break;
1550 case K_LEFT: {
1551 const void *top;
1553 if (pstack__empty(fstack)) {
1555 * Go back to the perf_evsel_menu__run or other user
1557 if (left_exits)
1558 goto out_free_stack;
1559 continue;
1561 top = pstack__pop(fstack);
1562 if (top == &browser->hists->dso_filter)
1563 goto zoom_out_dso;
1564 if (top == &browser->hists->thread_filter)
1565 goto zoom_out_thread;
1566 continue;
1568 case K_ESC:
1569 if (!left_exits &&
1570 !ui_browser__dialog_yesno(&browser->b,
1571 "Do you really want to exit?"))
1572 continue;
1573 /* Fall thru */
1574 case 'q':
1575 case CTRL('c'):
1576 goto out_free_stack;
1577 default:
1578 continue;
1581 if (!sort__has_sym)
1582 goto add_exit_option;
1584 if (sort__mode == SORT_MODE__BRANCH) {
1585 bi = browser->he_selection->branch_info;
1586 if (browser->selection != NULL &&
1587 bi &&
1588 bi->from.sym != NULL &&
1589 !bi->from.map->dso->annotate_warned &&
1590 asprintf(&options[nr_options], "Annotate %s",
1591 bi->from.sym->name) > 0)
1592 annotate_f = nr_options++;
1594 if (browser->selection != NULL &&
1595 bi &&
1596 bi->to.sym != NULL &&
1597 !bi->to.map->dso->annotate_warned &&
1598 (bi->to.sym != bi->from.sym ||
1599 bi->to.map->dso != bi->from.map->dso) &&
1600 asprintf(&options[nr_options], "Annotate %s",
1601 bi->to.sym->name) > 0)
1602 annotate_t = nr_options++;
1603 } else {
1605 if (browser->selection != NULL &&
1606 browser->selection->sym != NULL &&
1607 !browser->selection->map->dso->annotate_warned &&
1608 asprintf(&options[nr_options], "Annotate %s",
1609 browser->selection->sym->name) > 0)
1610 annotate = nr_options++;
1613 if (thread != NULL &&
1614 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1615 (browser->hists->thread_filter ? "out of" : "into"),
1616 (thread->comm_set ? thread__comm_str(thread) : ""),
1617 thread->tid) > 0)
1618 zoom_thread = nr_options++;
1620 if (dso != NULL &&
1621 asprintf(&options[nr_options], "Zoom %s %s DSO",
1622 (browser->hists->dso_filter ? "out of" : "into"),
1623 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1624 zoom_dso = nr_options++;
1626 if (browser->selection != NULL &&
1627 browser->selection->map != NULL &&
1628 asprintf(&options[nr_options], "Browse map details") > 0)
1629 browse_map = nr_options++;
1631 /* perf script support */
1632 if (browser->he_selection) {
1633 struct symbol *sym;
1635 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1636 thread__comm_str(browser->he_selection->thread)) > 0)
1637 scripts_comm = nr_options++;
1639 sym = browser->he_selection->ms.sym;
1640 if (sym && sym->namelen &&
1641 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1642 sym->name) > 0)
1643 scripts_symbol = nr_options++;
1646 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1647 scripts_all = nr_options++;
1649 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1650 "Switch to another data file in PWD") > 0)
1651 switch_data = nr_options++;
1652 add_exit_option:
1653 options[nr_options++] = (char *)"Exit";
1654 retry_popup_menu:
1655 choice = ui__popup_menu(nr_options, options);
1657 if (choice == nr_options - 1)
1658 break;
1660 if (choice == -1) {
1661 free_popup_options(options, nr_options - 1);
1662 continue;
1665 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1666 struct hist_entry *he;
1667 int err;
1668 do_annotate:
1669 if (!objdump_path && perf_session_env__lookup_objdump(env))
1670 continue;
1672 he = hist_browser__selected_entry(browser);
1673 if (he == NULL)
1674 continue;
1677 * we stash the branch_info symbol + map into the
1678 * the ms so we don't have to rewrite all the annotation
1679 * code to use branch_info.
1680 * in branch mode, the ms struct is not used
1682 if (choice == annotate_f) {
1683 he->ms.sym = he->branch_info->from.sym;
1684 he->ms.map = he->branch_info->from.map;
1685 } else if (choice == annotate_t) {
1686 he->ms.sym = he->branch_info->to.sym;
1687 he->ms.map = he->branch_info->to.map;
1691 * Don't let this be freed, say, by hists__decay_entry.
1693 he->used = true;
1694 err = hist_entry__tui_annotate(he, evsel, hbt);
1695 he->used = false;
1697 * offer option to annotate the other branch source or target
1698 * (if they exists) when returning from annotate
1700 if ((err == 'q' || err == CTRL('c'))
1701 && annotate_t != -2 && annotate_f != -2)
1702 goto retry_popup_menu;
1704 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1705 if (err)
1706 ui_browser__handle_resize(&browser->b);
1708 } else if (choice == browse_map)
1709 map__browse(browser->selection->map);
1710 else if (choice == zoom_dso) {
1711 zoom_dso:
1712 if (browser->hists->dso_filter) {
1713 pstack__remove(fstack, &browser->hists->dso_filter);
1714 zoom_out_dso:
1715 ui_helpline__pop();
1716 browser->hists->dso_filter = NULL;
1717 sort_dso.elide = false;
1718 } else {
1719 if (dso == NULL)
1720 continue;
1721 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1722 dso->kernel ? "the Kernel" : dso->short_name);
1723 browser->hists->dso_filter = dso;
1724 sort_dso.elide = true;
1725 pstack__push(fstack, &browser->hists->dso_filter);
1727 hists__filter_by_dso(hists);
1728 hist_browser__reset(browser);
1729 } else if (choice == zoom_thread) {
1730 zoom_thread:
1731 if (browser->hists->thread_filter) {
1732 pstack__remove(fstack, &browser->hists->thread_filter);
1733 zoom_out_thread:
1734 ui_helpline__pop();
1735 browser->hists->thread_filter = NULL;
1736 sort_thread.elide = false;
1737 } else {
1738 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1739 thread->comm_set ? thread__comm_str(thread) : "",
1740 thread->tid);
1741 browser->hists->thread_filter = thread;
1742 sort_thread.elide = true;
1743 pstack__push(fstack, &browser->hists->thread_filter);
1745 hists__filter_by_thread(hists);
1746 hist_browser__reset(browser);
1748 /* perf scripts support */
1749 else if (choice == scripts_all || choice == scripts_comm ||
1750 choice == scripts_symbol) {
1751 do_scripts:
1752 memset(script_opt, 0, 64);
1754 if (choice == scripts_comm)
1755 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1757 if (choice == scripts_symbol)
1758 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1760 script_browse(script_opt);
1762 /* Switch to another data file */
1763 else if (choice == switch_data) {
1764 do_data_switch:
1765 if (!switch_data_file()) {
1766 key = K_SWITCH_INPUT_DATA;
1767 break;
1768 } else
1769 ui__warning("Won't switch the data files due to\n"
1770 "no valid data file get selected!\n");
1773 out_free_stack:
1774 pstack__delete(fstack);
1775 out:
1776 hist_browser__delete(browser);
1777 free_popup_options(options, nr_options - 1);
1778 return key;
1781 struct perf_evsel_menu {
1782 struct ui_browser b;
1783 struct perf_evsel *selection;
1784 bool lost_events, lost_events_warned;
1785 float min_pcnt;
1786 struct perf_session_env *env;
1789 static void perf_evsel_menu__write(struct ui_browser *browser,
1790 void *entry, int row)
1792 struct perf_evsel_menu *menu = container_of(browser,
1793 struct perf_evsel_menu, b);
1794 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1795 bool current_entry = ui_browser__is_current_entry(browser, row);
1796 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1797 const char *ev_name = perf_evsel__name(evsel);
1798 char bf[256], unit;
1799 const char *warn = " ";
1800 size_t printed;
1802 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1803 HE_COLORSET_NORMAL);
1805 if (perf_evsel__is_group_event(evsel)) {
1806 struct perf_evsel *pos;
1808 ev_name = perf_evsel__group_name(evsel);
1810 for_each_group_member(pos, evsel) {
1811 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1815 nr_events = convert_unit(nr_events, &unit);
1816 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1817 unit, unit == ' ' ? "" : " ", ev_name);
1818 slsmg_printf("%s", bf);
1820 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1821 if (nr_events != 0) {
1822 menu->lost_events = true;
1823 if (!current_entry)
1824 ui_browser__set_color(browser, HE_COLORSET_TOP);
1825 nr_events = convert_unit(nr_events, &unit);
1826 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1827 nr_events, unit, unit == ' ' ? "" : " ");
1828 warn = bf;
1831 slsmg_write_nstring(warn, browser->width - printed);
1833 if (current_entry)
1834 menu->selection = evsel;
1837 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1838 int nr_events, const char *help,
1839 struct hist_browser_timer *hbt)
1841 struct perf_evlist *evlist = menu->b.priv;
1842 struct perf_evsel *pos;
1843 const char *ev_name, *title = "Available samples";
1844 int delay_secs = hbt ? hbt->refresh : 0;
1845 int key;
1847 if (ui_browser__show(&menu->b, title,
1848 "ESC: exit, ENTER|->: Browse histograms") < 0)
1849 return -1;
1851 while (1) {
1852 key = ui_browser__run(&menu->b, delay_secs);
1854 switch (key) {
1855 case K_TIMER:
1856 hbt->timer(hbt->arg);
1858 if (!menu->lost_events_warned && menu->lost_events) {
1859 ui_browser__warn_lost_events(&menu->b);
1860 menu->lost_events_warned = true;
1862 continue;
1863 case K_RIGHT:
1864 case K_ENTER:
1865 if (!menu->selection)
1866 continue;
1867 pos = menu->selection;
1868 browse_hists:
1869 perf_evlist__set_selected(evlist, pos);
1871 * Give the calling tool a chance to populate the non
1872 * default evsel resorted hists tree.
1874 if (hbt)
1875 hbt->timer(hbt->arg);
1876 ev_name = perf_evsel__name(pos);
1877 key = perf_evsel__hists_browse(pos, nr_events, help,
1878 ev_name, true, hbt,
1879 menu->min_pcnt,
1880 menu->env);
1881 ui_browser__show_title(&menu->b, title);
1882 switch (key) {
1883 case K_TAB:
1884 if (pos->node.next == &evlist->entries)
1885 pos = perf_evlist__first(evlist);
1886 else
1887 pos = perf_evsel__next(pos);
1888 goto browse_hists;
1889 case K_UNTAB:
1890 if (pos->node.prev == &evlist->entries)
1891 pos = perf_evlist__last(evlist);
1892 else
1893 pos = perf_evsel__prev(pos);
1894 goto browse_hists;
1895 case K_ESC:
1896 if (!ui_browser__dialog_yesno(&menu->b,
1897 "Do you really want to exit?"))
1898 continue;
1899 /* Fall thru */
1900 case K_SWITCH_INPUT_DATA:
1901 case 'q':
1902 case CTRL('c'):
1903 goto out;
1904 default:
1905 continue;
1907 case K_LEFT:
1908 continue;
1909 case K_ESC:
1910 if (!ui_browser__dialog_yesno(&menu->b,
1911 "Do you really want to exit?"))
1912 continue;
1913 /* Fall thru */
1914 case 'q':
1915 case CTRL('c'):
1916 goto out;
1917 default:
1918 continue;
1922 out:
1923 ui_browser__hide(&menu->b);
1924 return key;
1927 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1928 void *entry)
1930 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1932 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1933 return true;
1935 return false;
1938 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1939 int nr_entries, const char *help,
1940 struct hist_browser_timer *hbt,
1941 float min_pcnt,
1942 struct perf_session_env *env)
1944 struct perf_evsel *pos;
1945 struct perf_evsel_menu menu = {
1946 .b = {
1947 .entries = &evlist->entries,
1948 .refresh = ui_browser__list_head_refresh,
1949 .seek = ui_browser__list_head_seek,
1950 .write = perf_evsel_menu__write,
1951 .filter = filter_group_entries,
1952 .nr_entries = nr_entries,
1953 .priv = evlist,
1955 .min_pcnt = min_pcnt,
1956 .env = env,
1959 ui_helpline__push("Press ESC to exit");
1961 evlist__for_each(evlist, pos) {
1962 const char *ev_name = perf_evsel__name(pos);
1963 size_t line_len = strlen(ev_name) + 7;
1965 if (menu.b.width < line_len)
1966 menu.b.width = line_len;
1969 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1972 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1973 struct hist_browser_timer *hbt,
1974 float min_pcnt,
1975 struct perf_session_env *env)
1977 int nr_entries = evlist->nr_entries;
1979 single_entry:
1980 if (nr_entries == 1) {
1981 struct perf_evsel *first = perf_evlist__first(evlist);
1982 const char *ev_name = perf_evsel__name(first);
1984 return perf_evsel__hists_browse(first, nr_entries, help,
1985 ev_name, false, hbt, min_pcnt,
1986 env);
1989 if (symbol_conf.event_group) {
1990 struct perf_evsel *pos;
1992 nr_entries = 0;
1993 evlist__for_each(evlist, pos) {
1994 if (perf_evsel__is_group_leader(pos))
1995 nr_entries++;
1998 if (nr_entries == 1)
1999 goto single_entry;
2002 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2003 hbt, min_pcnt, env);