fix a kmap leak in virtio_console
[linux/fpc-iii.git] / tools / perf / ui / browsers / hists.c
blobb720b92eba6eb95cc7dd9bd4a57693f0a79d91b9
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_pcnt_entries;
32 extern void hist_browser__init_hpp(void);
34 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
35 const char *ev_name);
37 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
39 /* 3 == +/- toggle symbol before actual hist_entry rendering */
40 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
41 sizeof("[k]"));
44 static void hist_browser__reset(struct hist_browser *browser)
46 browser->b.nr_entries = browser->hists->nr_entries;
47 hist_browser__refresh_dimensions(browser);
48 ui_browser__reset_index(&browser->b);
51 static char tree__folded_sign(bool unfolded)
53 return unfolded ? '-' : '+';
56 static char map_symbol__folded(const struct map_symbol *ms)
58 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
61 static char hist_entry__folded(const struct hist_entry *he)
63 return map_symbol__folded(&he->ms);
66 static char callchain_list__folded(const struct callchain_list *cl)
68 return map_symbol__folded(&cl->ms);
71 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
73 ms->unfolded = unfold ? ms->has_children : false;
76 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
78 int n = 0;
79 struct rb_node *nd;
81 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
82 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
83 struct callchain_list *chain;
84 char folded_sign = ' '; /* No children */
86 list_for_each_entry(chain, &child->val, list) {
87 ++n;
88 /* We need this because we may not have children */
89 folded_sign = callchain_list__folded(chain);
90 if (folded_sign == '+')
91 break;
94 if (folded_sign == '-') /* Have children and they're unfolded */
95 n += callchain_node__count_rows_rb_tree(child);
98 return n;
101 static int callchain_node__count_rows(struct callchain_node *node)
103 struct callchain_list *chain;
104 bool unfolded = false;
105 int n = 0;
107 list_for_each_entry(chain, &node->val, list) {
108 ++n;
109 unfolded = chain->ms.unfolded;
112 if (unfolded)
113 n += callchain_node__count_rows_rb_tree(node);
115 return n;
118 static int callchain__count_rows(struct rb_root *chain)
120 struct rb_node *nd;
121 int n = 0;
123 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
124 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
125 n += callchain_node__count_rows(node);
128 return n;
131 static bool map_symbol__toggle_fold(struct map_symbol *ms)
133 if (!ms)
134 return false;
136 if (!ms->has_children)
137 return false;
139 ms->unfolded = !ms->unfolded;
140 return true;
143 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
145 struct rb_node *nd = rb_first(&node->rb_root);
147 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
148 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149 struct callchain_list *chain;
150 bool first = true;
152 list_for_each_entry(chain, &child->val, list) {
153 if (first) {
154 first = false;
155 chain->ms.has_children = chain->list.next != &child->val ||
156 !RB_EMPTY_ROOT(&child->rb_root);
157 } else
158 chain->ms.has_children = chain->list.next == &child->val &&
159 !RB_EMPTY_ROOT(&child->rb_root);
162 callchain_node__init_have_children_rb_tree(child);
166 static void callchain_node__init_have_children(struct callchain_node *node)
168 struct callchain_list *chain;
170 list_for_each_entry(chain, &node->val, list)
171 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
173 callchain_node__init_have_children_rb_tree(node);
176 static void callchain__init_have_children(struct rb_root *root)
178 struct rb_node *nd;
180 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
181 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
182 callchain_node__init_have_children(node);
186 static void hist_entry__init_have_children(struct hist_entry *he)
188 if (!he->init_have_children) {
189 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
190 callchain__init_have_children(&he->sorted_chain);
191 he->init_have_children = true;
195 static bool hist_browser__toggle_fold(struct hist_browser *browser)
197 if (map_symbol__toggle_fold(browser->selection)) {
198 struct hist_entry *he = browser->he_selection;
200 hist_entry__init_have_children(he);
201 browser->hists->nr_entries -= he->nr_rows;
203 if (he->ms.unfolded)
204 he->nr_rows = callchain__count_rows(&he->sorted_chain);
205 else
206 he->nr_rows = 0;
207 browser->hists->nr_entries += he->nr_rows;
208 browser->b.nr_entries = browser->hists->nr_entries;
210 return true;
213 /* If it doesn't have children, no toggling performed */
214 return false;
217 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
219 int n = 0;
220 struct rb_node *nd;
222 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
223 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
224 struct callchain_list *chain;
225 bool has_children = false;
227 list_for_each_entry(chain, &child->val, list) {
228 ++n;
229 map_symbol__set_folding(&chain->ms, unfold);
230 has_children = chain->ms.has_children;
233 if (has_children)
234 n += callchain_node__set_folding_rb_tree(child, unfold);
237 return n;
240 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
242 struct callchain_list *chain;
243 bool has_children = false;
244 int n = 0;
246 list_for_each_entry(chain, &node->val, list) {
247 ++n;
248 map_symbol__set_folding(&chain->ms, unfold);
249 has_children = chain->ms.has_children;
252 if (has_children)
253 n += callchain_node__set_folding_rb_tree(node, unfold);
255 return n;
258 static int callchain__set_folding(struct rb_root *chain, bool unfold)
260 struct rb_node *nd;
261 int n = 0;
263 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
264 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
265 n += callchain_node__set_folding(node, unfold);
268 return n;
271 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
273 hist_entry__init_have_children(he);
274 map_symbol__set_folding(&he->ms, unfold);
276 if (he->ms.has_children) {
277 int n = callchain__set_folding(&he->sorted_chain, unfold);
278 he->nr_rows = unfold ? n : 0;
279 } else
280 he->nr_rows = 0;
283 static void hists__set_folding(struct hists *hists, bool unfold)
285 struct rb_node *nd;
287 hists->nr_entries = 0;
289 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
290 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291 hist_entry__set_folding(he, unfold);
292 hists->nr_entries += 1 + he->nr_rows;
296 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
298 hists__set_folding(browser->hists, unfold);
299 browser->b.nr_entries = browser->hists->nr_entries;
300 /* Go to the start, we may be way after valid entries after a collapse */
301 ui_browser__reset_index(&browser->b);
304 static void ui_browser__warn_lost_events(struct ui_browser *browser)
306 ui_browser__warning(browser, 4,
307 "Events are being lost, check IO/CPU overload!\n\n"
308 "You may want to run 'perf' using a RT scheduler policy:\n\n"
309 " perf top -r 80\n\n"
310 "Or reduce the sampling frequency.");
313 static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
315 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
316 struct hist_browser_timer *hbt)
318 int key;
319 char title[160];
320 int delay_secs = hbt ? hbt->refresh : 0;
322 browser->b.entries = &browser->hists->entries;
323 browser->b.nr_entries = browser->hists->nr_entries;
324 if (browser->min_pcnt)
325 browser->b.nr_entries = browser->nr_pcnt_entries;
327 hist_browser__refresh_dimensions(browser);
328 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
330 if (ui_browser__show(&browser->b, title,
331 "Press '?' for help on key bindings") < 0)
332 return -1;
334 while (1) {
335 key = ui_browser__run(&browser->b, delay_secs);
337 switch (key) {
338 case K_TIMER: {
339 u64 nr_entries;
340 hbt->timer(hbt->arg);
342 if (browser->min_pcnt) {
343 hist_browser__update_pcnt_entries(browser);
344 nr_entries = browser->nr_pcnt_entries;
345 } else {
346 nr_entries = browser->hists->nr_entries;
349 ui_browser__update_nr_entries(&browser->b, nr_entries);
351 if (browser->hists->stats.nr_lost_warned !=
352 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
353 browser->hists->stats.nr_lost_warned =
354 browser->hists->stats.nr_events[PERF_RECORD_LOST];
355 ui_browser__warn_lost_events(&browser->b);
358 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
359 ui_browser__show_title(&browser->b, title);
360 continue;
362 case 'D': { /* Debug */
363 static int seq;
364 struct hist_entry *h = rb_entry(browser->b.top,
365 struct hist_entry, rb_node);
366 ui_helpline__pop();
367 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
368 seq++, browser->b.nr_entries,
369 browser->hists->nr_entries,
370 browser->b.height,
371 browser->b.index,
372 browser->b.top_idx,
373 h->row_offset, h->nr_rows);
375 break;
376 case 'C':
377 /* Collapse the whole world. */
378 hist_browser__set_folding(browser, false);
379 break;
380 case 'E':
381 /* Expand the whole world. */
382 hist_browser__set_folding(browser, true);
383 break;
384 case K_ENTER:
385 if (hist_browser__toggle_fold(browser))
386 break;
387 /* fall thru */
388 default:
389 goto out;
392 out:
393 ui_browser__hide(&browser->b);
394 return key;
397 static char *callchain_list__sym_name(struct callchain_list *cl,
398 char *bf, size_t bfsize, bool show_dso)
400 int printed;
402 if (cl->ms.sym)
403 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
404 else
405 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
407 if (show_dso)
408 scnprintf(bf + printed, bfsize - printed, " %s",
409 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
411 return bf;
414 #define LEVEL_OFFSET_STEP 3
416 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
417 struct callchain_node *chain_node,
418 u64 total, int level,
419 unsigned short row,
420 off_t *row_offset,
421 bool *is_current_entry)
423 struct rb_node *node;
424 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
425 u64 new_total, remaining;
427 if (callchain_param.mode == CHAIN_GRAPH_REL)
428 new_total = chain_node->children_hit;
429 else
430 new_total = total;
432 remaining = new_total;
433 node = rb_first(&chain_node->rb_root);
434 while (node) {
435 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
436 struct rb_node *next = rb_next(node);
437 u64 cumul = callchain_cumul_hits(child);
438 struct callchain_list *chain;
439 char folded_sign = ' ';
440 int first = true;
441 int extra_offset = 0;
443 remaining -= cumul;
445 list_for_each_entry(chain, &child->val, list) {
446 char bf[1024], *alloc_str;
447 const char *str;
448 int color;
449 bool was_first = first;
451 if (first)
452 first = false;
453 else
454 extra_offset = LEVEL_OFFSET_STEP;
456 folded_sign = callchain_list__folded(chain);
457 if (*row_offset != 0) {
458 --*row_offset;
459 goto do_next;
462 alloc_str = NULL;
463 str = callchain_list__sym_name(chain, bf, sizeof(bf),
464 browser->show_dso);
465 if (was_first) {
466 double percent = cumul * 100.0 / new_total;
468 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
469 str = "Not enough memory!";
470 else
471 str = alloc_str;
474 color = HE_COLORSET_NORMAL;
475 width = browser->b.width - (offset + extra_offset + 2);
476 if (ui_browser__is_current_entry(&browser->b, row)) {
477 browser->selection = &chain->ms;
478 color = HE_COLORSET_SELECTED;
479 *is_current_entry = true;
482 ui_browser__set_color(&browser->b, color);
483 ui_browser__gotorc(&browser->b, row, 0);
484 slsmg_write_nstring(" ", offset + extra_offset);
485 slsmg_printf("%c ", folded_sign);
486 slsmg_write_nstring(str, width);
487 free(alloc_str);
489 if (++row == browser->b.height)
490 goto out;
491 do_next:
492 if (folded_sign == '+')
493 break;
496 if (folded_sign == '-') {
497 const int new_level = level + (extra_offset ? 2 : 1);
498 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
499 new_level, row, row_offset,
500 is_current_entry);
502 if (row == browser->b.height)
503 goto out;
504 node = next;
506 out:
507 return row - first_row;
510 static int hist_browser__show_callchain_node(struct hist_browser *browser,
511 struct callchain_node *node,
512 int level, unsigned short row,
513 off_t *row_offset,
514 bool *is_current_entry)
516 struct callchain_list *chain;
517 int first_row = row,
518 offset = level * LEVEL_OFFSET_STEP,
519 width = browser->b.width - offset;
520 char folded_sign = ' ';
522 list_for_each_entry(chain, &node->val, list) {
523 char bf[1024], *s;
524 int color;
526 folded_sign = callchain_list__folded(chain);
528 if (*row_offset != 0) {
529 --*row_offset;
530 continue;
533 color = HE_COLORSET_NORMAL;
534 if (ui_browser__is_current_entry(&browser->b, row)) {
535 browser->selection = &chain->ms;
536 color = HE_COLORSET_SELECTED;
537 *is_current_entry = true;
540 s = callchain_list__sym_name(chain, bf, sizeof(bf),
541 browser->show_dso);
542 ui_browser__gotorc(&browser->b, row, 0);
543 ui_browser__set_color(&browser->b, color);
544 slsmg_write_nstring(" ", offset);
545 slsmg_printf("%c ", folded_sign);
546 slsmg_write_nstring(s, width - 2);
548 if (++row == browser->b.height)
549 goto out;
552 if (folded_sign == '-')
553 row += hist_browser__show_callchain_node_rb_tree(browser, node,
554 browser->hists->stats.total_period,
555 level + 1, row,
556 row_offset,
557 is_current_entry);
558 out:
559 return row - first_row;
562 static int hist_browser__show_callchain(struct hist_browser *browser,
563 struct rb_root *chain,
564 int level, unsigned short row,
565 off_t *row_offset,
566 bool *is_current_entry)
568 struct rb_node *nd;
569 int first_row = row;
571 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
572 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
574 row += hist_browser__show_callchain_node(browser, node, level,
575 row, row_offset,
576 is_current_entry);
577 if (row == browser->b.height)
578 break;
581 return row - first_row;
584 struct hpp_arg {
585 struct ui_browser *b;
586 char folded_sign;
587 bool current_entry;
590 static int __hpp__color_callchain(struct hpp_arg *arg)
592 if (!symbol_conf.use_callchain)
593 return 0;
595 slsmg_printf("%c ", arg->folded_sign);
596 return 2;
599 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
600 u64 (*get_field)(struct hist_entry *),
601 int (*callchain_cb)(struct hpp_arg *))
603 int ret = 0;
604 double percent = 0.0;
605 struct hists *hists = he->hists;
606 struct hpp_arg *arg = hpp->ptr;
608 if (hists->stats.total_period)
609 percent = 100.0 * get_field(he) / hists->stats.total_period;
611 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
613 if (callchain_cb)
614 ret += callchain_cb(arg);
616 ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
617 slsmg_printf("%s", hpp->buf);
619 if (symbol_conf.event_group) {
620 int prev_idx, idx_delta;
621 struct perf_evsel *evsel = hists_to_evsel(hists);
622 struct hist_entry *pair;
623 int nr_members = evsel->nr_members;
625 if (nr_members <= 1)
626 goto out;
628 prev_idx = perf_evsel__group_idx(evsel);
630 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
631 u64 period = get_field(pair);
632 u64 total = pair->hists->stats.total_period;
634 if (!total)
635 continue;
637 evsel = hists_to_evsel(pair->hists);
638 idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
640 while (idx_delta--) {
642 * zero-fill group members in the middle which
643 * have no sample
645 ui_browser__set_percent_color(arg->b, 0.0,
646 arg->current_entry);
647 ret += scnprintf(hpp->buf, hpp->size,
648 " %6.2f%%", 0.0);
649 slsmg_printf("%s", hpp->buf);
652 percent = 100.0 * period / total;
653 ui_browser__set_percent_color(arg->b, percent,
654 arg->current_entry);
655 ret += scnprintf(hpp->buf, hpp->size,
656 " %6.2f%%", percent);
657 slsmg_printf("%s", hpp->buf);
659 prev_idx = perf_evsel__group_idx(evsel);
662 idx_delta = nr_members - prev_idx - 1;
664 while (idx_delta--) {
666 * zero-fill group members at last which have no sample
668 ui_browser__set_percent_color(arg->b, 0.0,
669 arg->current_entry);
670 ret += scnprintf(hpp->buf, hpp->size,
671 " %6.2f%%", 0.0);
672 slsmg_printf("%s", hpp->buf);
675 out:
676 if (!arg->current_entry || !arg->b->navkeypressed)
677 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
679 return ret;
682 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
683 static u64 __hpp_get_##_field(struct hist_entry *he) \
685 return he->stat._field; \
688 static int \
689 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
690 struct perf_hpp *hpp, \
691 struct hist_entry *he) \
693 return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
696 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
697 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
698 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
699 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
700 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
702 #undef __HPP_COLOR_PERCENT_FN
704 void hist_browser__init_hpp(void)
706 perf_hpp__init();
708 perf_hpp__format[PERF_HPP__OVERHEAD].color =
709 hist_browser__hpp_color_overhead;
710 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
711 hist_browser__hpp_color_overhead_sys;
712 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
713 hist_browser__hpp_color_overhead_us;
714 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
715 hist_browser__hpp_color_overhead_guest_sys;
716 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
717 hist_browser__hpp_color_overhead_guest_us;
720 static int hist_browser__show_entry(struct hist_browser *browser,
721 struct hist_entry *entry,
722 unsigned short row)
724 char s[256];
725 int printed = 0;
726 int width = browser->b.width;
727 char folded_sign = ' ';
728 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
729 off_t row_offset = entry->row_offset;
730 bool first = true;
731 struct perf_hpp_fmt *fmt;
733 if (current_entry) {
734 browser->he_selection = entry;
735 browser->selection = &entry->ms;
738 if (symbol_conf.use_callchain) {
739 hist_entry__init_have_children(entry);
740 folded_sign = hist_entry__folded(entry);
743 if (row_offset == 0) {
744 struct hpp_arg arg = {
745 .b = &browser->b,
746 .folded_sign = folded_sign,
747 .current_entry = current_entry,
749 struct perf_hpp hpp = {
750 .buf = s,
751 .size = sizeof(s),
752 .ptr = &arg,
755 ui_browser__gotorc(&browser->b, row, 0);
757 perf_hpp__for_each_format(fmt) {
758 if (!first) {
759 slsmg_printf(" ");
760 width -= 2;
762 first = false;
764 if (fmt->color) {
765 width -= fmt->color(fmt, &hpp, entry);
766 } else {
767 width -= fmt->entry(fmt, &hpp, entry);
768 slsmg_printf("%s", s);
772 /* The scroll bar isn't being used */
773 if (!browser->b.navkeypressed)
774 width += 1;
776 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
777 slsmg_write_nstring(s, width);
778 ++row;
779 ++printed;
780 } else
781 --row_offset;
783 if (folded_sign == '-' && row != browser->b.height) {
784 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
785 1, row, &row_offset,
786 &current_entry);
787 if (current_entry)
788 browser->he_selection = entry;
791 return printed;
794 static void ui_browser__hists_init_top(struct ui_browser *browser)
796 if (browser->top == NULL) {
797 struct hist_browser *hb;
799 hb = container_of(browser, struct hist_browser, b);
800 browser->top = rb_first(&hb->hists->entries);
804 static unsigned int hist_browser__refresh(struct ui_browser *browser)
806 unsigned row = 0;
807 struct rb_node *nd;
808 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
810 ui_browser__hists_init_top(browser);
812 for (nd = browser->top; nd; nd = rb_next(nd)) {
813 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
814 float percent = h->stat.period * 100.0 /
815 hb->hists->stats.total_period;
817 if (h->filtered)
818 continue;
820 if (percent < hb->min_pcnt)
821 continue;
823 row += hist_browser__show_entry(hb, h, row);
824 if (row == browser->height)
825 break;
828 return row;
831 static struct rb_node *hists__filter_entries(struct rb_node *nd,
832 struct hists *hists,
833 float min_pcnt)
835 while (nd != NULL) {
836 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
837 float percent = h->stat.period * 100.0 /
838 hists->stats.total_period;
840 if (percent < min_pcnt)
841 return NULL;
843 if (!h->filtered)
844 return nd;
846 nd = rb_next(nd);
849 return NULL;
852 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
853 struct hists *hists,
854 float min_pcnt)
856 while (nd != NULL) {
857 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
858 float percent = h->stat.period * 100.0 /
859 hists->stats.total_period;
861 if (!h->filtered && percent >= min_pcnt)
862 return nd;
864 nd = rb_prev(nd);
867 return NULL;
870 static void ui_browser__hists_seek(struct ui_browser *browser,
871 off_t offset, int whence)
873 struct hist_entry *h;
874 struct rb_node *nd;
875 bool first = true;
876 struct hist_browser *hb;
878 hb = container_of(browser, struct hist_browser, b);
880 if (browser->nr_entries == 0)
881 return;
883 ui_browser__hists_init_top(browser);
885 switch (whence) {
886 case SEEK_SET:
887 nd = hists__filter_entries(rb_first(browser->entries),
888 hb->hists, hb->min_pcnt);
889 break;
890 case SEEK_CUR:
891 nd = browser->top;
892 goto do_offset;
893 case SEEK_END:
894 nd = hists__filter_prev_entries(rb_last(browser->entries),
895 hb->hists, hb->min_pcnt);
896 first = false;
897 break;
898 default:
899 return;
903 * Moves not relative to the first visible entry invalidates its
904 * row_offset:
906 h = rb_entry(browser->top, struct hist_entry, rb_node);
907 h->row_offset = 0;
910 * Here we have to check if nd is expanded (+), if it is we can't go
911 * the next top level hist_entry, instead we must compute an offset of
912 * what _not_ to show and not change the first visible entry.
914 * This offset increments when we are going from top to bottom and
915 * decreases when we're going from bottom to top.
917 * As we don't have backpointers to the top level in the callchains
918 * structure, we need to always print the whole hist_entry callchain,
919 * skipping the first ones that are before the first visible entry
920 * and stop when we printed enough lines to fill the screen.
922 do_offset:
923 if (offset > 0) {
924 do {
925 h = rb_entry(nd, struct hist_entry, rb_node);
926 if (h->ms.unfolded) {
927 u16 remaining = h->nr_rows - h->row_offset;
928 if (offset > remaining) {
929 offset -= remaining;
930 h->row_offset = 0;
931 } else {
932 h->row_offset += offset;
933 offset = 0;
934 browser->top = nd;
935 break;
938 nd = hists__filter_entries(rb_next(nd), hb->hists,
939 hb->min_pcnt);
940 if (nd == NULL)
941 break;
942 --offset;
943 browser->top = nd;
944 } while (offset != 0);
945 } else if (offset < 0) {
946 while (1) {
947 h = rb_entry(nd, struct hist_entry, rb_node);
948 if (h->ms.unfolded) {
949 if (first) {
950 if (-offset > h->row_offset) {
951 offset += h->row_offset;
952 h->row_offset = 0;
953 } else {
954 h->row_offset += offset;
955 offset = 0;
956 browser->top = nd;
957 break;
959 } else {
960 if (-offset > h->nr_rows) {
961 offset += h->nr_rows;
962 h->row_offset = 0;
963 } else {
964 h->row_offset = h->nr_rows + offset;
965 offset = 0;
966 browser->top = nd;
967 break;
972 nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
973 hb->min_pcnt);
974 if (nd == NULL)
975 break;
976 ++offset;
977 browser->top = nd;
978 if (offset == 0) {
980 * Last unfiltered hist_entry, check if it is
981 * unfolded, if it is then we should have
982 * row_offset at its last entry.
984 h = rb_entry(nd, struct hist_entry, rb_node);
985 if (h->ms.unfolded)
986 h->row_offset = h->nr_rows;
987 break;
989 first = false;
991 } else {
992 browser->top = nd;
993 h = rb_entry(nd, struct hist_entry, rb_node);
994 h->row_offset = 0;
998 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
999 struct callchain_node *chain_node,
1000 u64 total, int level,
1001 FILE *fp)
1003 struct rb_node *node;
1004 int offset = level * LEVEL_OFFSET_STEP;
1005 u64 new_total, remaining;
1006 int printed = 0;
1008 if (callchain_param.mode == CHAIN_GRAPH_REL)
1009 new_total = chain_node->children_hit;
1010 else
1011 new_total = total;
1013 remaining = new_total;
1014 node = rb_first(&chain_node->rb_root);
1015 while (node) {
1016 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1017 struct rb_node *next = rb_next(node);
1018 u64 cumul = callchain_cumul_hits(child);
1019 struct callchain_list *chain;
1020 char folded_sign = ' ';
1021 int first = true;
1022 int extra_offset = 0;
1024 remaining -= cumul;
1026 list_for_each_entry(chain, &child->val, list) {
1027 char bf[1024], *alloc_str;
1028 const char *str;
1029 bool was_first = first;
1031 if (first)
1032 first = false;
1033 else
1034 extra_offset = LEVEL_OFFSET_STEP;
1036 folded_sign = callchain_list__folded(chain);
1038 alloc_str = NULL;
1039 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1040 browser->show_dso);
1041 if (was_first) {
1042 double percent = cumul * 100.0 / new_total;
1044 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1045 str = "Not enough memory!";
1046 else
1047 str = alloc_str;
1050 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1051 free(alloc_str);
1052 if (folded_sign == '+')
1053 break;
1056 if (folded_sign == '-') {
1057 const int new_level = level + (extra_offset ? 2 : 1);
1058 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1059 new_level, fp);
1062 node = next;
1065 return printed;
1068 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1069 struct callchain_node *node,
1070 int level, FILE *fp)
1072 struct callchain_list *chain;
1073 int offset = level * LEVEL_OFFSET_STEP;
1074 char folded_sign = ' ';
1075 int printed = 0;
1077 list_for_each_entry(chain, &node->val, list) {
1078 char bf[1024], *s;
1080 folded_sign = callchain_list__folded(chain);
1081 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1082 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1085 if (folded_sign == '-')
1086 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1087 browser->hists->stats.total_period,
1088 level + 1, fp);
1089 return printed;
1092 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1093 struct rb_root *chain, int level, FILE *fp)
1095 struct rb_node *nd;
1096 int printed = 0;
1098 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1099 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1101 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1104 return printed;
1107 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1108 struct hist_entry *he, FILE *fp)
1110 char s[8192];
1111 double percent;
1112 int printed = 0;
1113 char folded_sign = ' ';
1115 if (symbol_conf.use_callchain)
1116 folded_sign = hist_entry__folded(he);
1118 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1119 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1121 if (symbol_conf.use_callchain)
1122 printed += fprintf(fp, "%c ", folded_sign);
1124 printed += fprintf(fp, " %5.2f%%", percent);
1126 if (symbol_conf.show_nr_samples)
1127 printed += fprintf(fp, " %11u", he->stat.nr_events);
1129 if (symbol_conf.show_total_period)
1130 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1132 printed += fprintf(fp, "%s\n", rtrim(s));
1134 if (folded_sign == '-')
1135 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1137 return printed;
1140 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1142 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1143 browser->hists,
1144 browser->min_pcnt);
1145 int printed = 0;
1147 while (nd) {
1148 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1150 printed += hist_browser__fprintf_entry(browser, h, fp);
1151 nd = hists__filter_entries(rb_next(nd), browser->hists,
1152 browser->min_pcnt);
1155 return printed;
1158 static int hist_browser__dump(struct hist_browser *browser)
1160 char filename[64];
1161 FILE *fp;
1163 while (1) {
1164 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1165 if (access(filename, F_OK))
1166 break;
1168 * XXX: Just an arbitrary lazy upper limit
1170 if (++browser->print_seq == 8192) {
1171 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1172 return -1;
1176 fp = fopen(filename, "w");
1177 if (fp == NULL) {
1178 char bf[64];
1179 const char *err = strerror_r(errno, bf, sizeof(bf));
1180 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1181 return -1;
1184 ++browser->print_seq;
1185 hist_browser__fprintf(browser, fp);
1186 fclose(fp);
1187 ui_helpline__fpush("%s written!", filename);
1189 return 0;
1192 static struct hist_browser *hist_browser__new(struct hists *hists)
1194 struct hist_browser *browser = zalloc(sizeof(*browser));
1196 if (browser) {
1197 browser->hists = hists;
1198 browser->b.refresh = hist_browser__refresh;
1199 browser->b.seek = ui_browser__hists_seek;
1200 browser->b.use_navkeypressed = true;
1203 return browser;
1206 static void hist_browser__delete(struct hist_browser *browser)
1208 free(browser);
1211 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1213 return browser->he_selection;
1216 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1218 return browser->he_selection->thread;
1221 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1222 const char *ev_name)
1224 char unit;
1225 int printed;
1226 const struct dso *dso = hists->dso_filter;
1227 const struct thread *thread = hists->thread_filter;
1228 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1229 u64 nr_events = hists->stats.total_period;
1230 struct perf_evsel *evsel = hists_to_evsel(hists);
1231 char buf[512];
1232 size_t buflen = sizeof(buf);
1234 if (perf_evsel__is_group_event(evsel)) {
1235 struct perf_evsel *pos;
1237 perf_evsel__group_desc(evsel, buf, buflen);
1238 ev_name = buf;
1240 for_each_group_member(pos, evsel) {
1241 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1242 nr_events += pos->hists.stats.total_period;
1246 nr_samples = convert_unit(nr_samples, &unit);
1247 printed = scnprintf(bf, size,
1248 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1249 nr_samples, unit, ev_name, nr_events);
1252 if (hists->uid_filter_str)
1253 printed += snprintf(bf + printed, size - printed,
1254 ", UID: %s", hists->uid_filter_str);
1255 if (thread)
1256 printed += scnprintf(bf + printed, size - printed,
1257 ", Thread: %s(%d)",
1258 (thread->comm_set ? thread__comm_str(thread) : ""),
1259 thread->tid);
1260 if (dso)
1261 printed += scnprintf(bf + printed, size - printed,
1262 ", DSO: %s", dso->short_name);
1263 return printed;
1266 static inline void free_popup_options(char **options, int n)
1268 int i;
1270 for (i = 0; i < n; ++i)
1271 zfree(&options[i]);
1274 /* Check whether the browser is for 'top' or 'report' */
1275 static inline bool is_report_browser(void *timer)
1277 return timer == NULL;
1281 * Only runtime switching of perf data file will make "input_name" point
1282 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1283 * whether we need to call free() for current "input_name" during the switch.
1285 static bool is_input_name_malloced = false;
1287 static int switch_data_file(void)
1289 char *pwd, *options[32], *abs_path[32], *tmp;
1290 DIR *pwd_dir;
1291 int nr_options = 0, choice = -1, ret = -1;
1292 struct dirent *dent;
1294 pwd = getenv("PWD");
1295 if (!pwd)
1296 return ret;
1298 pwd_dir = opendir(pwd);
1299 if (!pwd_dir)
1300 return ret;
1302 memset(options, 0, sizeof(options));
1303 memset(options, 0, sizeof(abs_path));
1305 while ((dent = readdir(pwd_dir))) {
1306 char path[PATH_MAX];
1307 u64 magic;
1308 char *name = dent->d_name;
1309 FILE *file;
1311 if (!(dent->d_type == DT_REG))
1312 continue;
1314 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1316 file = fopen(path, "r");
1317 if (!file)
1318 continue;
1320 if (fread(&magic, 1, 8, file) < 8)
1321 goto close_file_and_continue;
1323 if (is_perf_magic(magic)) {
1324 options[nr_options] = strdup(name);
1325 if (!options[nr_options])
1326 goto close_file_and_continue;
1328 abs_path[nr_options] = strdup(path);
1329 if (!abs_path[nr_options]) {
1330 zfree(&options[nr_options]);
1331 ui__warning("Can't search all data files due to memory shortage.\n");
1332 fclose(file);
1333 break;
1336 nr_options++;
1339 close_file_and_continue:
1340 fclose(file);
1341 if (nr_options >= 32) {
1342 ui__warning("Too many perf data files in PWD!\n"
1343 "Only the first 32 files will be listed.\n");
1344 break;
1347 closedir(pwd_dir);
1349 if (nr_options) {
1350 choice = ui__popup_menu(nr_options, options);
1351 if (choice < nr_options && choice >= 0) {
1352 tmp = strdup(abs_path[choice]);
1353 if (tmp) {
1354 if (is_input_name_malloced)
1355 free((void *)input_name);
1356 input_name = tmp;
1357 is_input_name_malloced = true;
1358 ret = 0;
1359 } else
1360 ui__warning("Data switch failed due to memory shortage!\n");
1364 free_popup_options(options, nr_options);
1365 free_popup_options(abs_path, nr_options);
1366 return ret;
1369 static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1371 u64 nr_entries = 0;
1372 struct rb_node *nd = rb_first(&hb->hists->entries);
1374 while (nd) {
1375 nr_entries++;
1376 nd = hists__filter_entries(rb_next(nd), hb->hists,
1377 hb->min_pcnt);
1380 hb->nr_pcnt_entries = nr_entries;
1383 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1384 const char *helpline, const char *ev_name,
1385 bool left_exits,
1386 struct hist_browser_timer *hbt,
1387 float min_pcnt,
1388 struct perf_session_env *env)
1390 struct hists *hists = &evsel->hists;
1391 struct hist_browser *browser = hist_browser__new(hists);
1392 struct branch_info *bi;
1393 struct pstack *fstack;
1394 char *options[16];
1395 int nr_options = 0;
1396 int key = -1;
1397 char buf[64];
1398 char script_opt[64];
1399 int delay_secs = hbt ? hbt->refresh : 0;
1401 #define HIST_BROWSER_HELP_COMMON \
1402 "h/?/F1 Show this window\n" \
1403 "UP/DOWN/PGUP\n" \
1404 "PGDN/SPACE Navigate\n" \
1405 "q/ESC/CTRL+C Exit browser\n\n" \
1406 "For multiple event sessions:\n\n" \
1407 "TAB/UNTAB Switch events\n\n" \
1408 "For symbolic views (--sort has sym):\n\n" \
1409 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1410 "<- Zoom out\n" \
1411 "a Annotate current symbol\n" \
1412 "C Collapse all callchains\n" \
1413 "d Zoom into current DSO\n" \
1414 "E Expand all callchains\n" \
1416 /* help messages are sorted by lexical order of the hotkey */
1417 const char report_help[] = HIST_BROWSER_HELP_COMMON
1418 "i Show header information\n"
1419 "P Print histograms to perf.hist.N\n"
1420 "r Run available scripts\n"
1421 "s Switch to another data file in PWD\n"
1422 "t Zoom into current Thread\n"
1423 "V Verbose (DSO names in callchains, etc)\n"
1424 "/ Filter symbol by name";
1425 const char top_help[] = HIST_BROWSER_HELP_COMMON
1426 "P Print histograms to perf.hist.N\n"
1427 "t Zoom into current Thread\n"
1428 "V Verbose (DSO names in callchains, etc)\n"
1429 "/ Filter symbol by name";
1431 if (browser == NULL)
1432 return -1;
1434 if (min_pcnt) {
1435 browser->min_pcnt = min_pcnt;
1436 hist_browser__update_pcnt_entries(browser);
1439 fstack = pstack__new(2);
1440 if (fstack == NULL)
1441 goto out;
1443 ui_helpline__push(helpline);
1445 memset(options, 0, sizeof(options));
1447 while (1) {
1448 const struct thread *thread = NULL;
1449 const struct dso *dso = NULL;
1450 int choice = 0,
1451 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1452 annotate_f = -2, annotate_t = -2, browse_map = -2;
1453 int scripts_comm = -2, scripts_symbol = -2,
1454 scripts_all = -2, switch_data = -2;
1456 nr_options = 0;
1458 key = hist_browser__run(browser, ev_name, hbt);
1460 if (browser->he_selection != NULL) {
1461 thread = hist_browser__selected_thread(browser);
1462 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1464 switch (key) {
1465 case K_TAB:
1466 case K_UNTAB:
1467 if (nr_events == 1)
1468 continue;
1470 * Exit the browser, let hists__browser_tree
1471 * go to the next or previous
1473 goto out_free_stack;
1474 case 'a':
1475 if (!sort__has_sym) {
1476 ui_browser__warning(&browser->b, delay_secs * 2,
1477 "Annotation is only available for symbolic views, "
1478 "include \"sym*\" in --sort to use it.");
1479 continue;
1482 if (browser->selection == NULL ||
1483 browser->selection->sym == NULL ||
1484 browser->selection->map->dso->annotate_warned)
1485 continue;
1486 goto do_annotate;
1487 case 'P':
1488 hist_browser__dump(browser);
1489 continue;
1490 case 'd':
1491 goto zoom_dso;
1492 case 'V':
1493 browser->show_dso = !browser->show_dso;
1494 continue;
1495 case 't':
1496 goto zoom_thread;
1497 case '/':
1498 if (ui_browser__input_window("Symbol to show",
1499 "Please enter the name of symbol you want to see",
1500 buf, "ENTER: OK, ESC: Cancel",
1501 delay_secs * 2) == K_ENTER) {
1502 hists->symbol_filter_str = *buf ? buf : NULL;
1503 hists__filter_by_symbol(hists);
1504 hist_browser__reset(browser);
1506 continue;
1507 case 'r':
1508 if (is_report_browser(hbt))
1509 goto do_scripts;
1510 continue;
1511 case 's':
1512 if (is_report_browser(hbt))
1513 goto do_data_switch;
1514 continue;
1515 case 'i':
1516 /* env->arch is NULL for live-mode (i.e. perf top) */
1517 if (env->arch)
1518 tui__header_window(env);
1519 continue;
1520 case K_F1:
1521 case 'h':
1522 case '?':
1523 ui_browser__help_window(&browser->b,
1524 is_report_browser(hbt) ? report_help : top_help);
1525 continue;
1526 case K_ENTER:
1527 case K_RIGHT:
1528 /* menu */
1529 break;
1530 case K_LEFT: {
1531 const void *top;
1533 if (pstack__empty(fstack)) {
1535 * Go back to the perf_evsel_menu__run or other user
1537 if (left_exits)
1538 goto out_free_stack;
1539 continue;
1541 top = pstack__pop(fstack);
1542 if (top == &browser->hists->dso_filter)
1543 goto zoom_out_dso;
1544 if (top == &browser->hists->thread_filter)
1545 goto zoom_out_thread;
1546 continue;
1548 case K_ESC:
1549 if (!left_exits &&
1550 !ui_browser__dialog_yesno(&browser->b,
1551 "Do you really want to exit?"))
1552 continue;
1553 /* Fall thru */
1554 case 'q':
1555 case CTRL('c'):
1556 goto out_free_stack;
1557 default:
1558 continue;
1561 if (!sort__has_sym)
1562 goto add_exit_option;
1564 if (sort__mode == SORT_MODE__BRANCH) {
1565 bi = browser->he_selection->branch_info;
1566 if (browser->selection != NULL &&
1567 bi &&
1568 bi->from.sym != NULL &&
1569 !bi->from.map->dso->annotate_warned &&
1570 asprintf(&options[nr_options], "Annotate %s",
1571 bi->from.sym->name) > 0)
1572 annotate_f = nr_options++;
1574 if (browser->selection != NULL &&
1575 bi &&
1576 bi->to.sym != NULL &&
1577 !bi->to.map->dso->annotate_warned &&
1578 (bi->to.sym != bi->from.sym ||
1579 bi->to.map->dso != bi->from.map->dso) &&
1580 asprintf(&options[nr_options], "Annotate %s",
1581 bi->to.sym->name) > 0)
1582 annotate_t = nr_options++;
1583 } else {
1585 if (browser->selection != NULL &&
1586 browser->selection->sym != NULL &&
1587 !browser->selection->map->dso->annotate_warned &&
1588 asprintf(&options[nr_options], "Annotate %s",
1589 browser->selection->sym->name) > 0)
1590 annotate = nr_options++;
1593 if (thread != NULL &&
1594 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1595 (browser->hists->thread_filter ? "out of" : "into"),
1596 (thread->comm_set ? thread__comm_str(thread) : ""),
1597 thread->tid) > 0)
1598 zoom_thread = nr_options++;
1600 if (dso != NULL &&
1601 asprintf(&options[nr_options], "Zoom %s %s DSO",
1602 (browser->hists->dso_filter ? "out of" : "into"),
1603 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1604 zoom_dso = nr_options++;
1606 if (browser->selection != NULL &&
1607 browser->selection->map != NULL &&
1608 asprintf(&options[nr_options], "Browse map details") > 0)
1609 browse_map = nr_options++;
1611 /* perf script support */
1612 if (browser->he_selection) {
1613 struct symbol *sym;
1615 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1616 thread__comm_str(browser->he_selection->thread)) > 0)
1617 scripts_comm = nr_options++;
1619 sym = browser->he_selection->ms.sym;
1620 if (sym && sym->namelen &&
1621 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1622 sym->name) > 0)
1623 scripts_symbol = nr_options++;
1626 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1627 scripts_all = nr_options++;
1629 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1630 "Switch to another data file in PWD") > 0)
1631 switch_data = nr_options++;
1632 add_exit_option:
1633 options[nr_options++] = (char *)"Exit";
1634 retry_popup_menu:
1635 choice = ui__popup_menu(nr_options, options);
1637 if (choice == nr_options - 1)
1638 break;
1640 if (choice == -1) {
1641 free_popup_options(options, nr_options - 1);
1642 continue;
1645 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1646 struct hist_entry *he;
1647 int err;
1648 do_annotate:
1649 if (!objdump_path && perf_session_env__lookup_objdump(env))
1650 continue;
1652 he = hist_browser__selected_entry(browser);
1653 if (he == NULL)
1654 continue;
1657 * we stash the branch_info symbol + map into the
1658 * the ms so we don't have to rewrite all the annotation
1659 * code to use branch_info.
1660 * in branch mode, the ms struct is not used
1662 if (choice == annotate_f) {
1663 he->ms.sym = he->branch_info->from.sym;
1664 he->ms.map = he->branch_info->from.map;
1665 } else if (choice == annotate_t) {
1666 he->ms.sym = he->branch_info->to.sym;
1667 he->ms.map = he->branch_info->to.map;
1671 * Don't let this be freed, say, by hists__decay_entry.
1673 he->used = true;
1674 err = hist_entry__tui_annotate(he, evsel, hbt);
1675 he->used = false;
1677 * offer option to annotate the other branch source or target
1678 * (if they exists) when returning from annotate
1680 if ((err == 'q' || err == CTRL('c'))
1681 && annotate_t != -2 && annotate_f != -2)
1682 goto retry_popup_menu;
1684 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1685 if (err)
1686 ui_browser__handle_resize(&browser->b);
1688 } else if (choice == browse_map)
1689 map__browse(browser->selection->map);
1690 else if (choice == zoom_dso) {
1691 zoom_dso:
1692 if (browser->hists->dso_filter) {
1693 pstack__remove(fstack, &browser->hists->dso_filter);
1694 zoom_out_dso:
1695 ui_helpline__pop();
1696 browser->hists->dso_filter = NULL;
1697 sort_dso.elide = false;
1698 } else {
1699 if (dso == NULL)
1700 continue;
1701 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1702 dso->kernel ? "the Kernel" : dso->short_name);
1703 browser->hists->dso_filter = dso;
1704 sort_dso.elide = true;
1705 pstack__push(fstack, &browser->hists->dso_filter);
1707 hists__filter_by_dso(hists);
1708 hist_browser__reset(browser);
1709 } else if (choice == zoom_thread) {
1710 zoom_thread:
1711 if (browser->hists->thread_filter) {
1712 pstack__remove(fstack, &browser->hists->thread_filter);
1713 zoom_out_thread:
1714 ui_helpline__pop();
1715 browser->hists->thread_filter = NULL;
1716 sort_thread.elide = false;
1717 } else {
1718 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1719 thread->comm_set ? thread__comm_str(thread) : "",
1720 thread->tid);
1721 browser->hists->thread_filter = thread;
1722 sort_thread.elide = true;
1723 pstack__push(fstack, &browser->hists->thread_filter);
1725 hists__filter_by_thread(hists);
1726 hist_browser__reset(browser);
1728 /* perf scripts support */
1729 else if (choice == scripts_all || choice == scripts_comm ||
1730 choice == scripts_symbol) {
1731 do_scripts:
1732 memset(script_opt, 0, 64);
1734 if (choice == scripts_comm)
1735 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1737 if (choice == scripts_symbol)
1738 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1740 script_browse(script_opt);
1742 /* Switch to another data file */
1743 else if (choice == switch_data) {
1744 do_data_switch:
1745 if (!switch_data_file()) {
1746 key = K_SWITCH_INPUT_DATA;
1747 break;
1748 } else
1749 ui__warning("Won't switch the data files due to\n"
1750 "no valid data file get selected!\n");
1753 out_free_stack:
1754 pstack__delete(fstack);
1755 out:
1756 hist_browser__delete(browser);
1757 free_popup_options(options, nr_options - 1);
1758 return key;
1761 struct perf_evsel_menu {
1762 struct ui_browser b;
1763 struct perf_evsel *selection;
1764 bool lost_events, lost_events_warned;
1765 float min_pcnt;
1766 struct perf_session_env *env;
1769 static void perf_evsel_menu__write(struct ui_browser *browser,
1770 void *entry, int row)
1772 struct perf_evsel_menu *menu = container_of(browser,
1773 struct perf_evsel_menu, b);
1774 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1775 bool current_entry = ui_browser__is_current_entry(browser, row);
1776 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1777 const char *ev_name = perf_evsel__name(evsel);
1778 char bf[256], unit;
1779 const char *warn = " ";
1780 size_t printed;
1782 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1783 HE_COLORSET_NORMAL);
1785 if (perf_evsel__is_group_event(evsel)) {
1786 struct perf_evsel *pos;
1788 ev_name = perf_evsel__group_name(evsel);
1790 for_each_group_member(pos, evsel) {
1791 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1795 nr_events = convert_unit(nr_events, &unit);
1796 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1797 unit, unit == ' ' ? "" : " ", ev_name);
1798 slsmg_printf("%s", bf);
1800 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1801 if (nr_events != 0) {
1802 menu->lost_events = true;
1803 if (!current_entry)
1804 ui_browser__set_color(browser, HE_COLORSET_TOP);
1805 nr_events = convert_unit(nr_events, &unit);
1806 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1807 nr_events, unit, unit == ' ' ? "" : " ");
1808 warn = bf;
1811 slsmg_write_nstring(warn, browser->width - printed);
1813 if (current_entry)
1814 menu->selection = evsel;
1817 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1818 int nr_events, const char *help,
1819 struct hist_browser_timer *hbt)
1821 struct perf_evlist *evlist = menu->b.priv;
1822 struct perf_evsel *pos;
1823 const char *ev_name, *title = "Available samples";
1824 int delay_secs = hbt ? hbt->refresh : 0;
1825 int key;
1827 if (ui_browser__show(&menu->b, title,
1828 "ESC: exit, ENTER|->: Browse histograms") < 0)
1829 return -1;
1831 while (1) {
1832 key = ui_browser__run(&menu->b, delay_secs);
1834 switch (key) {
1835 case K_TIMER:
1836 hbt->timer(hbt->arg);
1838 if (!menu->lost_events_warned && menu->lost_events) {
1839 ui_browser__warn_lost_events(&menu->b);
1840 menu->lost_events_warned = true;
1842 continue;
1843 case K_RIGHT:
1844 case K_ENTER:
1845 if (!menu->selection)
1846 continue;
1847 pos = menu->selection;
1848 browse_hists:
1849 perf_evlist__set_selected(evlist, pos);
1851 * Give the calling tool a chance to populate the non
1852 * default evsel resorted hists tree.
1854 if (hbt)
1855 hbt->timer(hbt->arg);
1856 ev_name = perf_evsel__name(pos);
1857 key = perf_evsel__hists_browse(pos, nr_events, help,
1858 ev_name, true, hbt,
1859 menu->min_pcnt,
1860 menu->env);
1861 ui_browser__show_title(&menu->b, title);
1862 switch (key) {
1863 case K_TAB:
1864 if (pos->node.next == &evlist->entries)
1865 pos = perf_evlist__first(evlist);
1866 else
1867 pos = perf_evsel__next(pos);
1868 goto browse_hists;
1869 case K_UNTAB:
1870 if (pos->node.prev == &evlist->entries)
1871 pos = perf_evlist__last(evlist);
1872 else
1873 pos = perf_evsel__prev(pos);
1874 goto browse_hists;
1875 case K_ESC:
1876 if (!ui_browser__dialog_yesno(&menu->b,
1877 "Do you really want to exit?"))
1878 continue;
1879 /* Fall thru */
1880 case K_SWITCH_INPUT_DATA:
1881 case 'q':
1882 case CTRL('c'):
1883 goto out;
1884 default:
1885 continue;
1887 case K_LEFT:
1888 continue;
1889 case K_ESC:
1890 if (!ui_browser__dialog_yesno(&menu->b,
1891 "Do you really want to exit?"))
1892 continue;
1893 /* Fall thru */
1894 case 'q':
1895 case CTRL('c'):
1896 goto out;
1897 default:
1898 continue;
1902 out:
1903 ui_browser__hide(&menu->b);
1904 return key;
1907 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1908 void *entry)
1910 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1912 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1913 return true;
1915 return false;
1918 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1919 int nr_entries, const char *help,
1920 struct hist_browser_timer *hbt,
1921 float min_pcnt,
1922 struct perf_session_env *env)
1924 struct perf_evsel *pos;
1925 struct perf_evsel_menu menu = {
1926 .b = {
1927 .entries = &evlist->entries,
1928 .refresh = ui_browser__list_head_refresh,
1929 .seek = ui_browser__list_head_seek,
1930 .write = perf_evsel_menu__write,
1931 .filter = filter_group_entries,
1932 .nr_entries = nr_entries,
1933 .priv = evlist,
1935 .min_pcnt = min_pcnt,
1936 .env = env,
1939 ui_helpline__push("Press ESC to exit");
1941 evlist__for_each(evlist, pos) {
1942 const char *ev_name = perf_evsel__name(pos);
1943 size_t line_len = strlen(ev_name) + 7;
1945 if (menu.b.width < line_len)
1946 menu.b.width = line_len;
1949 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1952 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1953 struct hist_browser_timer *hbt,
1954 float min_pcnt,
1955 struct perf_session_env *env)
1957 int nr_entries = evlist->nr_entries;
1959 single_entry:
1960 if (nr_entries == 1) {
1961 struct perf_evsel *first = perf_evlist__first(evlist);
1962 const char *ev_name = perf_evsel__name(first);
1964 return perf_evsel__hists_browse(first, nr_entries, help,
1965 ev_name, false, hbt, min_pcnt,
1966 env);
1969 if (symbol_conf.event_group) {
1970 struct perf_evsel *pos;
1972 nr_entries = 0;
1973 evlist__for_each(evlist, pos) {
1974 if (perf_evsel__is_group_leader(pos))
1975 nr_entries++;
1978 if (nr_entries == 1)
1979 goto single_entry;
1982 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1983 hbt, min_pcnt, env);