Merge remote-tracking branch 'moduleh/module.h-split'
[linux-2.6/next.git] / tools / perf / util / ui / browsers / hists.c
blob5d767c622dfc1f862fb747ababa610e9031944c8
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #undef _GNU_SOURCE
4 #include "../libslang.h"
5 #include <stdlib.h>
6 #include <string.h>
7 #include <newt.h>
8 #include <linux/rbtree.h>
10 #include "../../evsel.h"
11 #include "../../evlist.h"
12 #include "../../hist.h"
13 #include "../../pstack.h"
14 #include "../../sort.h"
15 #include "../../util.h"
17 #include "../browser.h"
18 #include "../helpline.h"
19 #include "../util.h"
20 #include "map.h"
22 struct hist_browser {
23 struct ui_browser b;
24 struct hists *hists;
25 struct hist_entry *he_selection;
26 struct map_symbol *selection;
29 static void hist_browser__refresh_dimensions(struct hist_browser *self)
31 /* 3 == +/- toggle symbol before actual hist_entry rendering */
32 self->b.width = 3 + (hists__sort_list_width(self->hists) +
33 sizeof("[k]"));
36 static void hist_browser__reset(struct hist_browser *self)
38 self->b.nr_entries = self->hists->nr_entries;
39 hist_browser__refresh_dimensions(self);
40 ui_browser__reset_index(&self->b);
43 static char tree__folded_sign(bool unfolded)
45 return unfolded ? '-' : '+';
48 static char map_symbol__folded(const struct map_symbol *self)
50 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
53 static char hist_entry__folded(const struct hist_entry *self)
55 return map_symbol__folded(&self->ms);
58 static char callchain_list__folded(const struct callchain_list *self)
60 return map_symbol__folded(&self->ms);
63 static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
65 self->unfolded = unfold ? self->has_children : false;
68 static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
70 int n = 0;
71 struct rb_node *nd;
73 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
74 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
75 struct callchain_list *chain;
76 char folded_sign = ' '; /* No children */
78 list_for_each_entry(chain, &child->val, list) {
79 ++n;
80 /* We need this because we may not have children */
81 folded_sign = callchain_list__folded(chain);
82 if (folded_sign == '+')
83 break;
86 if (folded_sign == '-') /* Have children and they're unfolded */
87 n += callchain_node__count_rows_rb_tree(child);
90 return n;
93 static int callchain_node__count_rows(struct callchain_node *node)
95 struct callchain_list *chain;
96 bool unfolded = false;
97 int n = 0;
99 list_for_each_entry(chain, &node->val, list) {
100 ++n;
101 unfolded = chain->ms.unfolded;
104 if (unfolded)
105 n += callchain_node__count_rows_rb_tree(node);
107 return n;
110 static int callchain__count_rows(struct rb_root *chain)
112 struct rb_node *nd;
113 int n = 0;
115 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
116 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
117 n += callchain_node__count_rows(node);
120 return n;
123 static bool map_symbol__toggle_fold(struct map_symbol *self)
125 if (!self->has_children)
126 return false;
128 self->unfolded = !self->unfolded;
129 return true;
132 static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
134 struct rb_node *nd = rb_first(&self->rb_root);
136 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
137 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
138 struct callchain_list *chain;
139 bool first = true;
141 list_for_each_entry(chain, &child->val, list) {
142 if (first) {
143 first = false;
144 chain->ms.has_children = chain->list.next != &child->val ||
145 !RB_EMPTY_ROOT(&child->rb_root);
146 } else
147 chain->ms.has_children = chain->list.next == &child->val &&
148 !RB_EMPTY_ROOT(&child->rb_root);
151 callchain_node__init_have_children_rb_tree(child);
155 static void callchain_node__init_have_children(struct callchain_node *self)
157 struct callchain_list *chain;
159 list_for_each_entry(chain, &self->val, list)
160 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
162 callchain_node__init_have_children_rb_tree(self);
165 static void callchain__init_have_children(struct rb_root *self)
167 struct rb_node *nd;
169 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
170 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
171 callchain_node__init_have_children(node);
175 static void hist_entry__init_have_children(struct hist_entry *self)
177 if (!self->init_have_children) {
178 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
179 callchain__init_have_children(&self->sorted_chain);
180 self->init_have_children = true;
184 static bool hist_browser__toggle_fold(struct hist_browser *self)
186 if (map_symbol__toggle_fold(self->selection)) {
187 struct hist_entry *he = self->he_selection;
189 hist_entry__init_have_children(he);
190 self->hists->nr_entries -= he->nr_rows;
192 if (he->ms.unfolded)
193 he->nr_rows = callchain__count_rows(&he->sorted_chain);
194 else
195 he->nr_rows = 0;
196 self->hists->nr_entries += he->nr_rows;
197 self->b.nr_entries = self->hists->nr_entries;
199 return true;
202 /* If it doesn't have children, no toggling performed */
203 return false;
206 static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
208 int n = 0;
209 struct rb_node *nd;
211 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
212 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
213 struct callchain_list *chain;
214 bool has_children = false;
216 list_for_each_entry(chain, &child->val, list) {
217 ++n;
218 map_symbol__set_folding(&chain->ms, unfold);
219 has_children = chain->ms.has_children;
222 if (has_children)
223 n += callchain_node__set_folding_rb_tree(child, unfold);
226 return n;
229 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
231 struct callchain_list *chain;
232 bool has_children = false;
233 int n = 0;
235 list_for_each_entry(chain, &node->val, list) {
236 ++n;
237 map_symbol__set_folding(&chain->ms, unfold);
238 has_children = chain->ms.has_children;
241 if (has_children)
242 n += callchain_node__set_folding_rb_tree(node, unfold);
244 return n;
247 static int callchain__set_folding(struct rb_root *chain, bool unfold)
249 struct rb_node *nd;
250 int n = 0;
252 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
253 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
254 n += callchain_node__set_folding(node, unfold);
257 return n;
260 static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
262 hist_entry__init_have_children(self);
263 map_symbol__set_folding(&self->ms, unfold);
265 if (self->ms.has_children) {
266 int n = callchain__set_folding(&self->sorted_chain, unfold);
267 self->nr_rows = unfold ? n : 0;
268 } else
269 self->nr_rows = 0;
272 static void hists__set_folding(struct hists *self, bool unfold)
274 struct rb_node *nd;
276 self->nr_entries = 0;
278 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
279 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
280 hist_entry__set_folding(he, unfold);
281 self->nr_entries += 1 + he->nr_rows;
285 static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
287 hists__set_folding(self->hists, unfold);
288 self->b.nr_entries = self->hists->nr_entries;
289 /* Go to the start, we may be way after valid entries after a collapse */
290 ui_browser__reset_index(&self->b);
293 static int hist_browser__run(struct hist_browser *self, const char *title)
295 int key;
296 int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
297 NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT,
298 NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, };
300 self->b.entries = &self->hists->entries;
301 self->b.nr_entries = self->hists->nr_entries;
303 hist_browser__refresh_dimensions(self);
305 if (ui_browser__show(&self->b, title,
306 "Press '?' for help on key bindings") < 0)
307 return -1;
309 ui_browser__add_exit_keys(&self->b, exit_keys);
311 while (1) {
312 key = ui_browser__run(&self->b);
314 switch (key) {
315 case 'D': { /* Debug */
316 static int seq;
317 struct hist_entry *h = rb_entry(self->b.top,
318 struct hist_entry, rb_node);
319 ui_helpline__pop();
320 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
321 seq++, self->b.nr_entries,
322 self->hists->nr_entries,
323 self->b.height,
324 self->b.index,
325 self->b.top_idx,
326 h->row_offset, h->nr_rows);
328 break;
329 case 'C':
330 /* Collapse the whole world. */
331 hist_browser__set_folding(self, false);
332 break;
333 case 'E':
334 /* Expand the whole world. */
335 hist_browser__set_folding(self, true);
336 break;
337 case NEWT_KEY_ENTER:
338 if (hist_browser__toggle_fold(self))
339 break;
340 /* fall thru */
341 default:
342 goto out;
345 out:
346 ui_browser__hide(&self->b);
347 return key;
350 static char *callchain_list__sym_name(struct callchain_list *self,
351 char *bf, size_t bfsize)
353 if (self->ms.sym)
354 return self->ms.sym->name;
356 snprintf(bf, bfsize, "%#" PRIx64, self->ip);
357 return bf;
360 #define LEVEL_OFFSET_STEP 3
362 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
363 struct callchain_node *chain_node,
364 u64 total, int level,
365 unsigned short row,
366 off_t *row_offset,
367 bool *is_current_entry)
369 struct rb_node *node;
370 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
371 u64 new_total, remaining;
373 if (callchain_param.mode == CHAIN_GRAPH_REL)
374 new_total = chain_node->children_hit;
375 else
376 new_total = total;
378 remaining = new_total;
379 node = rb_first(&chain_node->rb_root);
380 while (node) {
381 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
382 struct rb_node *next = rb_next(node);
383 u64 cumul = callchain_cumul_hits(child);
384 struct callchain_list *chain;
385 char folded_sign = ' ';
386 int first = true;
387 int extra_offset = 0;
389 remaining -= cumul;
391 list_for_each_entry(chain, &child->val, list) {
392 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
393 const char *str;
394 int color;
395 bool was_first = first;
397 if (first)
398 first = false;
399 else
400 extra_offset = LEVEL_OFFSET_STEP;
402 folded_sign = callchain_list__folded(chain);
403 if (*row_offset != 0) {
404 --*row_offset;
405 goto do_next;
408 alloc_str = NULL;
409 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
410 if (was_first) {
411 double percent = cumul * 100.0 / new_total;
413 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
414 str = "Not enough memory!";
415 else
416 str = alloc_str;
419 color = HE_COLORSET_NORMAL;
420 width = self->b.width - (offset + extra_offset + 2);
421 if (ui_browser__is_current_entry(&self->b, row)) {
422 self->selection = &chain->ms;
423 color = HE_COLORSET_SELECTED;
424 *is_current_entry = true;
427 ui_browser__set_color(&self->b, color);
428 ui_browser__gotorc(&self->b, row, 0);
429 slsmg_write_nstring(" ", offset + extra_offset);
430 slsmg_printf("%c ", folded_sign);
431 slsmg_write_nstring(str, width);
432 free(alloc_str);
434 if (++row == self->b.height)
435 goto out;
436 do_next:
437 if (folded_sign == '+')
438 break;
441 if (folded_sign == '-') {
442 const int new_level = level + (extra_offset ? 2 : 1);
443 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
444 new_level, row, row_offset,
445 is_current_entry);
447 if (row == self->b.height)
448 goto out;
449 node = next;
451 out:
452 return row - first_row;
455 static int hist_browser__show_callchain_node(struct hist_browser *self,
456 struct callchain_node *node,
457 int level, unsigned short row,
458 off_t *row_offset,
459 bool *is_current_entry)
461 struct callchain_list *chain;
462 int first_row = row,
463 offset = level * LEVEL_OFFSET_STEP,
464 width = self->b.width - offset;
465 char folded_sign = ' ';
467 list_for_each_entry(chain, &node->val, list) {
468 char ipstr[BITS_PER_LONG / 4 + 1], *s;
469 int color;
471 folded_sign = callchain_list__folded(chain);
473 if (*row_offset != 0) {
474 --*row_offset;
475 continue;
478 color = HE_COLORSET_NORMAL;
479 if (ui_browser__is_current_entry(&self->b, row)) {
480 self->selection = &chain->ms;
481 color = HE_COLORSET_SELECTED;
482 *is_current_entry = true;
485 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
486 ui_browser__gotorc(&self->b, row, 0);
487 ui_browser__set_color(&self->b, color);
488 slsmg_write_nstring(" ", offset);
489 slsmg_printf("%c ", folded_sign);
490 slsmg_write_nstring(s, width - 2);
492 if (++row == self->b.height)
493 goto out;
496 if (folded_sign == '-')
497 row += hist_browser__show_callchain_node_rb_tree(self, node,
498 self->hists->stats.total_period,
499 level + 1, row,
500 row_offset,
501 is_current_entry);
502 out:
503 return row - first_row;
506 static int hist_browser__show_callchain(struct hist_browser *self,
507 struct rb_root *chain,
508 int level, unsigned short row,
509 off_t *row_offset,
510 bool *is_current_entry)
512 struct rb_node *nd;
513 int first_row = row;
515 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
516 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
518 row += hist_browser__show_callchain_node(self, node, level,
519 row, row_offset,
520 is_current_entry);
521 if (row == self->b.height)
522 break;
525 return row - first_row;
528 static int hist_browser__show_entry(struct hist_browser *self,
529 struct hist_entry *entry,
530 unsigned short row)
532 char s[256];
533 double percent;
534 int printed = 0;
535 int color, width = self->b.width;
536 char folded_sign = ' ';
537 bool current_entry = ui_browser__is_current_entry(&self->b, row);
538 off_t row_offset = entry->row_offset;
540 if (current_entry) {
541 self->he_selection = entry;
542 self->selection = &entry->ms;
545 if (symbol_conf.use_callchain) {
546 hist_entry__init_have_children(entry);
547 folded_sign = hist_entry__folded(entry);
550 if (row_offset == 0) {
551 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
552 0, false, self->hists->stats.total_period);
553 percent = (entry->period * 100.0) / self->hists->stats.total_period;
555 color = HE_COLORSET_SELECTED;
556 if (!current_entry) {
557 if (percent >= MIN_RED)
558 color = HE_COLORSET_TOP;
559 else if (percent >= MIN_GREEN)
560 color = HE_COLORSET_MEDIUM;
561 else
562 color = HE_COLORSET_NORMAL;
565 ui_browser__set_color(&self->b, color);
566 ui_browser__gotorc(&self->b, row, 0);
567 if (symbol_conf.use_callchain) {
568 slsmg_printf("%c ", folded_sign);
569 width -= 2;
571 slsmg_write_nstring(s, width);
572 ++row;
573 ++printed;
574 } else
575 --row_offset;
577 if (folded_sign == '-' && row != self->b.height) {
578 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
579 1, row, &row_offset,
580 &current_entry);
581 if (current_entry)
582 self->he_selection = entry;
585 return printed;
588 static unsigned int hist_browser__refresh(struct ui_browser *self)
590 unsigned row = 0;
591 struct rb_node *nd;
592 struct hist_browser *hb = container_of(self, struct hist_browser, b);
594 if (self->top == NULL)
595 self->top = rb_first(&hb->hists->entries);
597 for (nd = self->top; nd; nd = rb_next(nd)) {
598 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
600 if (h->filtered)
601 continue;
603 row += hist_browser__show_entry(hb, h, row);
604 if (row == self->height)
605 break;
608 return row;
611 static struct rb_node *hists__filter_entries(struct rb_node *nd)
613 while (nd != NULL) {
614 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
615 if (!h->filtered)
616 return nd;
618 nd = rb_next(nd);
621 return NULL;
624 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
626 while (nd != NULL) {
627 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
628 if (!h->filtered)
629 return nd;
631 nd = rb_prev(nd);
634 return NULL;
637 static void ui_browser__hists_seek(struct ui_browser *self,
638 off_t offset, int whence)
640 struct hist_entry *h;
641 struct rb_node *nd;
642 bool first = true;
644 if (self->nr_entries == 0)
645 return;
647 switch (whence) {
648 case SEEK_SET:
649 nd = hists__filter_entries(rb_first(self->entries));
650 break;
651 case SEEK_CUR:
652 nd = self->top;
653 goto do_offset;
654 case SEEK_END:
655 nd = hists__filter_prev_entries(rb_last(self->entries));
656 first = false;
657 break;
658 default:
659 return;
663 * Moves not relative to the first visible entry invalidates its
664 * row_offset:
666 h = rb_entry(self->top, struct hist_entry, rb_node);
667 h->row_offset = 0;
670 * Here we have to check if nd is expanded (+), if it is we can't go
671 * the next top level hist_entry, instead we must compute an offset of
672 * what _not_ to show and not change the first visible entry.
674 * This offset increments when we are going from top to bottom and
675 * decreases when we're going from bottom to top.
677 * As we don't have backpointers to the top level in the callchains
678 * structure, we need to always print the whole hist_entry callchain,
679 * skipping the first ones that are before the first visible entry
680 * and stop when we printed enough lines to fill the screen.
682 do_offset:
683 if (offset > 0) {
684 do {
685 h = rb_entry(nd, struct hist_entry, rb_node);
686 if (h->ms.unfolded) {
687 u16 remaining = h->nr_rows - h->row_offset;
688 if (offset > remaining) {
689 offset -= remaining;
690 h->row_offset = 0;
691 } else {
692 h->row_offset += offset;
693 offset = 0;
694 self->top = nd;
695 break;
698 nd = hists__filter_entries(rb_next(nd));
699 if (nd == NULL)
700 break;
701 --offset;
702 self->top = nd;
703 } while (offset != 0);
704 } else if (offset < 0) {
705 while (1) {
706 h = rb_entry(nd, struct hist_entry, rb_node);
707 if (h->ms.unfolded) {
708 if (first) {
709 if (-offset > h->row_offset) {
710 offset += h->row_offset;
711 h->row_offset = 0;
712 } else {
713 h->row_offset += offset;
714 offset = 0;
715 self->top = nd;
716 break;
718 } else {
719 if (-offset > h->nr_rows) {
720 offset += h->nr_rows;
721 h->row_offset = 0;
722 } else {
723 h->row_offset = h->nr_rows + offset;
724 offset = 0;
725 self->top = nd;
726 break;
731 nd = hists__filter_prev_entries(rb_prev(nd));
732 if (nd == NULL)
733 break;
734 ++offset;
735 self->top = nd;
736 if (offset == 0) {
738 * Last unfiltered hist_entry, check if it is
739 * unfolded, if it is then we should have
740 * row_offset at its last entry.
742 h = rb_entry(nd, struct hist_entry, rb_node);
743 if (h->ms.unfolded)
744 h->row_offset = h->nr_rows;
745 break;
747 first = false;
749 } else {
750 self->top = nd;
751 h = rb_entry(nd, struct hist_entry, rb_node);
752 h->row_offset = 0;
756 static struct hist_browser *hist_browser__new(struct hists *hists)
758 struct hist_browser *self = zalloc(sizeof(*self));
760 if (self) {
761 self->hists = hists;
762 self->b.refresh = hist_browser__refresh;
763 self->b.seek = ui_browser__hists_seek;
766 return self;
769 static void hist_browser__delete(struct hist_browser *self)
771 free(self);
774 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
776 return self->he_selection;
779 static struct thread *hist_browser__selected_thread(struct hist_browser *self)
781 return self->he_selection->thread;
784 static int hists__browser_title(struct hists *self, char *bf, size_t size,
785 const char *ev_name, const struct dso *dso,
786 const struct thread *thread)
788 char unit;
789 int printed;
790 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
792 nr_events = convert_unit(nr_events, &unit);
793 printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
795 if (thread)
796 printed += snprintf(bf + printed, size - printed,
797 ", Thread: %s(%d)",
798 (thread->comm_set ? thread->comm : ""),
799 thread->pid);
800 if (dso)
801 printed += snprintf(bf + printed, size - printed,
802 ", DSO: %s", dso->short_name);
803 return printed;
806 static int perf_evsel__hists_browse(struct perf_evsel *evsel,
807 const char *helpline, const char *ev_name,
808 bool left_exits)
810 struct hists *self = &evsel->hists;
811 struct hist_browser *browser = hist_browser__new(self);
812 struct pstack *fstack;
813 const struct thread *thread_filter = NULL;
814 const struct dso *dso_filter = NULL;
815 char msg[160];
816 int key = -1;
818 if (browser == NULL)
819 return -1;
821 fstack = pstack__new(2);
822 if (fstack == NULL)
823 goto out;
825 ui_helpline__push(helpline);
827 hists__browser_title(self, msg, sizeof(msg), ev_name,
828 dso_filter, thread_filter);
829 while (1) {
830 const struct thread *thread = NULL;
831 const struct dso *dso = NULL;
832 char *options[16];
833 int nr_options = 0, choice = 0, i,
834 annotate = -2, zoom_dso = -2, zoom_thread = -2,
835 browse_map = -2;
837 key = hist_browser__run(browser, msg);
839 if (browser->he_selection != NULL) {
840 thread = hist_browser__selected_thread(browser);
841 dso = browser->selection->map ? browser->selection->map->dso : NULL;
844 switch (key) {
845 case NEWT_KEY_TAB:
846 case NEWT_KEY_UNTAB:
848 * Exit the browser, let hists__browser_tree
849 * go to the next or previous
851 goto out_free_stack;
852 case 'a':
853 if (browser->selection == NULL ||
854 browser->selection->sym == NULL ||
855 browser->selection->map->dso->annotate_warned)
856 continue;
857 goto do_annotate;
858 case 'd':
859 goto zoom_dso;
860 case 't':
861 goto zoom_thread;
862 case NEWT_KEY_F1:
863 case 'h':
864 case '?':
865 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
866 "<- Zoom out\n"
867 "a Annotate current symbol\n"
868 "h/?/F1 Show this window\n"
869 "C Collapse all callchains\n"
870 "E Expand all callchains\n"
871 "d Zoom into current DSO\n"
872 "t Zoom into current Thread\n"
873 "TAB/UNTAB Switch events\n"
874 "q/CTRL+C Exit browser");
875 continue;
876 case NEWT_KEY_ENTER:
877 case NEWT_KEY_RIGHT:
878 /* menu */
879 break;
880 case NEWT_KEY_LEFT: {
881 const void *top;
883 if (pstack__empty(fstack)) {
885 * Go back to the perf_evsel_menu__run or other user
887 if (left_exits)
888 goto out_free_stack;
889 continue;
891 top = pstack__pop(fstack);
892 if (top == &dso_filter)
893 goto zoom_out_dso;
894 if (top == &thread_filter)
895 goto zoom_out_thread;
896 continue;
898 case NEWT_KEY_ESCAPE:
899 if (!left_exits &&
900 !ui__dialog_yesno("Do you really want to exit?"))
901 continue;
902 /* Fall thru */
903 default:
904 goto out_free_stack;
907 if (browser->selection != NULL &&
908 browser->selection->sym != NULL &&
909 !browser->selection->map->dso->annotate_warned &&
910 asprintf(&options[nr_options], "Annotate %s",
911 browser->selection->sym->name) > 0)
912 annotate = nr_options++;
914 if (thread != NULL &&
915 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
916 (thread_filter ? "out of" : "into"),
917 (thread->comm_set ? thread->comm : ""),
918 thread->pid) > 0)
919 zoom_thread = nr_options++;
921 if (dso != NULL &&
922 asprintf(&options[nr_options], "Zoom %s %s DSO",
923 (dso_filter ? "out of" : "into"),
924 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
925 zoom_dso = nr_options++;
927 if (browser->selection != NULL &&
928 browser->selection->map != NULL &&
929 asprintf(&options[nr_options], "Browse map details") > 0)
930 browse_map = nr_options++;
932 options[nr_options++] = (char *)"Exit";
934 choice = ui__popup_menu(nr_options, options);
936 for (i = 0; i < nr_options - 1; ++i)
937 free(options[i]);
939 if (choice == nr_options - 1)
940 break;
942 if (choice == -1)
943 continue;
945 if (choice == annotate) {
946 struct hist_entry *he;
947 do_annotate:
948 he = hist_browser__selected_entry(browser);
949 if (he == NULL)
950 continue;
952 hist_entry__tui_annotate(he, evsel->idx);
953 } else if (choice == browse_map)
954 map__browse(browser->selection->map);
955 else if (choice == zoom_dso) {
956 zoom_dso:
957 if (dso_filter) {
958 pstack__remove(fstack, &dso_filter);
959 zoom_out_dso:
960 ui_helpline__pop();
961 dso_filter = NULL;
962 } else {
963 if (dso == NULL)
964 continue;
965 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
966 dso->kernel ? "the Kernel" : dso->short_name);
967 dso_filter = dso;
968 pstack__push(fstack, &dso_filter);
970 hists__filter_by_dso(self, dso_filter);
971 hists__browser_title(self, msg, sizeof(msg), ev_name,
972 dso_filter, thread_filter);
973 hist_browser__reset(browser);
974 } else if (choice == zoom_thread) {
975 zoom_thread:
976 if (thread_filter) {
977 pstack__remove(fstack, &thread_filter);
978 zoom_out_thread:
979 ui_helpline__pop();
980 thread_filter = NULL;
981 } else {
982 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
983 thread->comm_set ? thread->comm : "",
984 thread->pid);
985 thread_filter = thread;
986 pstack__push(fstack, &thread_filter);
988 hists__filter_by_thread(self, thread_filter);
989 hists__browser_title(self, msg, sizeof(msg), ev_name,
990 dso_filter, thread_filter);
991 hist_browser__reset(browser);
994 out_free_stack:
995 pstack__delete(fstack);
996 out:
997 hist_browser__delete(browser);
998 return key;
1001 struct perf_evsel_menu {
1002 struct ui_browser b;
1003 struct perf_evsel *selection;
1006 static void perf_evsel_menu__write(struct ui_browser *browser,
1007 void *entry, int row)
1009 struct perf_evsel_menu *menu = container_of(browser,
1010 struct perf_evsel_menu, b);
1011 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1012 bool current_entry = ui_browser__is_current_entry(browser, row);
1013 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1014 const char *ev_name = event_name(evsel);
1015 char bf[256], unit;
1017 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1018 HE_COLORSET_NORMAL);
1020 nr_events = convert_unit(nr_events, &unit);
1021 snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1022 unit, unit == ' ' ? "" : " ", ev_name);
1023 slsmg_write_nstring(bf, browser->width);
1025 if (current_entry)
1026 menu->selection = evsel;
1029 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
1031 int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, };
1032 struct perf_evlist *evlist = menu->b.priv;
1033 struct perf_evsel *pos;
1034 const char *ev_name, *title = "Available samples";
1035 int key;
1037 if (ui_browser__show(&menu->b, title,
1038 "ESC: exit, ENTER|->: Browse histograms") < 0)
1039 return -1;
1041 ui_browser__add_exit_keys(&menu->b, exit_keys);
1043 while (1) {
1044 key = ui_browser__run(&menu->b);
1046 switch (key) {
1047 case NEWT_KEY_RIGHT:
1048 case NEWT_KEY_ENTER:
1049 if (!menu->selection)
1050 continue;
1051 pos = menu->selection;
1052 browse_hists:
1053 ev_name = event_name(pos);
1054 key = perf_evsel__hists_browse(pos, help, ev_name, true);
1055 ui_browser__show_title(&menu->b, title);
1056 break;
1057 case NEWT_KEY_LEFT:
1058 continue;
1059 case NEWT_KEY_ESCAPE:
1060 if (!ui__dialog_yesno("Do you really want to exit?"))
1061 continue;
1062 /* Fall thru */
1063 default:
1064 goto out;
1067 switch (key) {
1068 case NEWT_KEY_TAB:
1069 if (pos->node.next == &evlist->entries)
1070 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1071 else
1072 pos = list_entry(pos->node.next, struct perf_evsel, node);
1073 goto browse_hists;
1074 case NEWT_KEY_UNTAB:
1075 if (pos->node.prev == &evlist->entries)
1076 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1077 else
1078 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1079 goto browse_hists;
1080 case 'q':
1081 case CTRL('c'):
1082 goto out;
1083 default:
1084 break;
1088 out:
1089 ui_browser__hide(&menu->b);
1090 return key;
1093 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1094 const char *help)
1096 struct perf_evsel *pos;
1097 struct perf_evsel_menu menu = {
1098 .b = {
1099 .entries = &evlist->entries,
1100 .refresh = ui_browser__list_head_refresh,
1101 .seek = ui_browser__list_head_seek,
1102 .write = perf_evsel_menu__write,
1103 .nr_entries = evlist->nr_entries,
1104 .priv = evlist,
1108 ui_helpline__push("Press ESC to exit");
1110 list_for_each_entry(pos, &evlist->entries, node) {
1111 const char *ev_name = event_name(pos);
1112 size_t line_len = strlen(ev_name) + 7;
1114 if (menu.b.width < line_len)
1115 menu.b.width = line_len;
1117 * Cache the evsel name, tracepoints have a _high_ cost per
1118 * event_name() call.
1120 if (pos->name == NULL)
1121 pos->name = strdup(ev_name);
1124 return perf_evsel_menu__run(&menu, help);
1127 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help)
1130 if (evlist->nr_entries == 1) {
1131 struct perf_evsel *first = list_entry(evlist->entries.next,
1132 struct perf_evsel, node);
1133 const char *ev_name = event_name(first);
1134 return perf_evsel__hists_browse(first, help, ev_name, false);
1137 return __perf_evlist__tui_browse_hists(evlist, help);