diff options
Diffstat (limited to 'tools/perf/util/ui/browsers/hists.c')
-rw-r--r-- | tools/perf/util/ui/browsers/hists.c | 351 |
1 files changed, 237 insertions, 114 deletions
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 5d767c622dfc..1212a386a033 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "../browser.h" | 17 | #include "../browser.h" |
18 | #include "../helpline.h" | 18 | #include "../helpline.h" |
19 | #include "../util.h" | 19 | #include "../util.h" |
20 | #include "../ui.h" | ||
20 | #include "map.h" | 21 | #include "map.h" |
21 | 22 | ||
22 | struct hist_browser { | 23 | struct hist_browser { |
@@ -24,8 +25,12 @@ struct hist_browser { | |||
24 | struct hists *hists; | 25 | struct hists *hists; |
25 | struct hist_entry *he_selection; | 26 | struct hist_entry *he_selection; |
26 | struct map_symbol *selection; | 27 | struct map_symbol *selection; |
28 | bool has_symbols; | ||
27 | }; | 29 | }; |
28 | 30 | ||
31 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | ||
32 | const char *ev_name); | ||
33 | |||
29 | static void hist_browser__refresh_dimensions(struct hist_browser *self) | 34 | static void hist_browser__refresh_dimensions(struct hist_browser *self) |
30 | { | 35 | { |
31 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ | 36 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ |
@@ -290,28 +295,49 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold) | |||
290 | ui_browser__reset_index(&self->b); | 295 | ui_browser__reset_index(&self->b); |
291 | } | 296 | } |
292 | 297 | ||
293 | static int hist_browser__run(struct hist_browser *self, const char *title) | 298 | static void ui_browser__warn_lost_events(struct ui_browser *browser) |
299 | { | ||
300 | ui_browser__warning(browser, 4, | ||
301 | "Events are being lost, check IO/CPU overload!\n\n" | ||
302 | "You may want to run 'perf' using a RT scheduler policy:\n\n" | ||
303 | " perf top -r 80\n\n" | ||
304 | "Or reduce the sampling frequency."); | ||
305 | } | ||
306 | |||
307 | static int hist_browser__run(struct hist_browser *self, const char *ev_name, | ||
308 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
294 | { | 309 | { |
295 | int key; | 310 | int key; |
296 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', | 311 | char title[160]; |
297 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, | ||
298 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; | ||
299 | 312 | ||
300 | self->b.entries = &self->hists->entries; | 313 | self->b.entries = &self->hists->entries; |
301 | self->b.nr_entries = self->hists->nr_entries; | 314 | self->b.nr_entries = self->hists->nr_entries; |
302 | 315 | ||
303 | hist_browser__refresh_dimensions(self); | 316 | hist_browser__refresh_dimensions(self); |
317 | hists__browser_title(self->hists, title, sizeof(title), ev_name); | ||
304 | 318 | ||
305 | if (ui_browser__show(&self->b, title, | 319 | if (ui_browser__show(&self->b, title, |
306 | "Press '?' for help on key bindings") < 0) | 320 | "Press '?' for help on key bindings") < 0) |
307 | return -1; | 321 | return -1; |
308 | 322 | ||
309 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
310 | |||
311 | while (1) { | 323 | while (1) { |
312 | key = ui_browser__run(&self->b); | 324 | key = ui_browser__run(&self->b, delay_secs); |
313 | 325 | ||
314 | switch (key) { | 326 | switch (key) { |
327 | case K_TIMER: | ||
328 | timer(arg); | ||
329 | ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); | ||
330 | |||
331 | if (self->hists->stats.nr_lost_warned != | ||
332 | self->hists->stats.nr_events[PERF_RECORD_LOST]) { | ||
333 | self->hists->stats.nr_lost_warned = | ||
334 | self->hists->stats.nr_events[PERF_RECORD_LOST]; | ||
335 | ui_browser__warn_lost_events(&self->b); | ||
336 | } | ||
337 | |||
338 | hists__browser_title(self->hists, title, sizeof(title), ev_name); | ||
339 | ui_browser__show_title(&self->b, title); | ||
340 | continue; | ||
315 | case 'D': { /* Debug */ | 341 | case 'D': { /* Debug */ |
316 | static int seq; | 342 | static int seq; |
317 | struct hist_entry *h = rb_entry(self->b.top, | 343 | struct hist_entry *h = rb_entry(self->b.top, |
@@ -334,7 +360,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title) | |||
334 | /* Expand the whole world. */ | 360 | /* Expand the whole world. */ |
335 | hist_browser__set_folding(self, true); | 361 | hist_browser__set_folding(self, true); |
336 | break; | 362 | break; |
337 | case NEWT_KEY_ENTER: | 363 | case K_ENTER: |
338 | if (hist_browser__toggle_fold(self)) | 364 | if (hist_browser__toggle_fold(self)) |
339 | break; | 365 | break; |
340 | /* fall thru */ | 366 | /* fall thru */ |
@@ -532,7 +558,7 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
532 | char s[256]; | 558 | char s[256]; |
533 | double percent; | 559 | double percent; |
534 | int printed = 0; | 560 | int printed = 0; |
535 | int color, width = self->b.width; | 561 | int width = self->b.width - 6; /* The percentage */ |
536 | char folded_sign = ' '; | 562 | char folded_sign = ' '; |
537 | bool current_entry = ui_browser__is_current_entry(&self->b, row); | 563 | bool current_entry = ui_browser__is_current_entry(&self->b, row); |
538 | off_t row_offset = entry->row_offset; | 564 | off_t row_offset = entry->row_offset; |
@@ -548,26 +574,35 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
548 | } | 574 | } |
549 | 575 | ||
550 | if (row_offset == 0) { | 576 | if (row_offset == 0) { |
551 | hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, | 577 | hist_entry__snprintf(entry, s, sizeof(s), self->hists); |
552 | 0, false, self->hists->stats.total_period); | ||
553 | percent = (entry->period * 100.0) / self->hists->stats.total_period; | 578 | percent = (entry->period * 100.0) / self->hists->stats.total_period; |
554 | 579 | ||
555 | color = HE_COLORSET_SELECTED; | 580 | ui_browser__set_percent_color(&self->b, percent, current_entry); |
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; | ||
563 | } | ||
564 | |||
565 | ui_browser__set_color(&self->b, color); | ||
566 | ui_browser__gotorc(&self->b, row, 0); | 581 | ui_browser__gotorc(&self->b, row, 0); |
567 | if (symbol_conf.use_callchain) { | 582 | if (symbol_conf.use_callchain) { |
568 | slsmg_printf("%c ", folded_sign); | 583 | slsmg_printf("%c ", folded_sign); |
569 | width -= 2; | 584 | width -= 2; |
570 | } | 585 | } |
586 | |||
587 | slsmg_printf(" %5.2f%%", percent); | ||
588 | |||
589 | /* The scroll bar isn't being used */ | ||
590 | if (!self->b.navkeypressed) | ||
591 | width += 1; | ||
592 | |||
593 | if (!current_entry || !self->b.navkeypressed) | ||
594 | ui_browser__set_color(&self->b, HE_COLORSET_NORMAL); | ||
595 | |||
596 | if (symbol_conf.show_nr_samples) { | ||
597 | slsmg_printf(" %11u", entry->nr_events); | ||
598 | width -= 12; | ||
599 | } | ||
600 | |||
601 | if (symbol_conf.show_total_period) { | ||
602 | slsmg_printf(" %12" PRIu64, entry->period); | ||
603 | width -= 13; | ||
604 | } | ||
605 | |||
571 | slsmg_write_nstring(s, width); | 606 | slsmg_write_nstring(s, width); |
572 | ++row; | 607 | ++row; |
573 | ++printed; | 608 | ++printed; |
@@ -585,14 +620,23 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
585 | return printed; | 620 | return printed; |
586 | } | 621 | } |
587 | 622 | ||
623 | static void ui_browser__hists_init_top(struct ui_browser *browser) | ||
624 | { | ||
625 | if (browser->top == NULL) { | ||
626 | struct hist_browser *hb; | ||
627 | |||
628 | hb = container_of(browser, struct hist_browser, b); | ||
629 | browser->top = rb_first(&hb->hists->entries); | ||
630 | } | ||
631 | } | ||
632 | |||
588 | static unsigned int hist_browser__refresh(struct ui_browser *self) | 633 | static unsigned int hist_browser__refresh(struct ui_browser *self) |
589 | { | 634 | { |
590 | unsigned row = 0; | 635 | unsigned row = 0; |
591 | struct rb_node *nd; | 636 | struct rb_node *nd; |
592 | struct hist_browser *hb = container_of(self, struct hist_browser, b); | 637 | struct hist_browser *hb = container_of(self, struct hist_browser, b); |
593 | 638 | ||
594 | if (self->top == NULL) | 639 | ui_browser__hists_init_top(self); |
595 | self->top = rb_first(&hb->hists->entries); | ||
596 | 640 | ||
597 | for (nd = self->top; nd; nd = rb_next(nd)) { | 641 | for (nd = self->top; nd; nd = rb_next(nd)) { |
598 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 642 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -644,6 +688,8 @@ static void ui_browser__hists_seek(struct ui_browser *self, | |||
644 | if (self->nr_entries == 0) | 688 | if (self->nr_entries == 0) |
645 | return; | 689 | return; |
646 | 690 | ||
691 | ui_browser__hists_init_top(self); | ||
692 | |||
647 | switch (whence) { | 693 | switch (whence) { |
648 | case SEEK_SET: | 694 | case SEEK_SET: |
649 | nd = hists__filter_entries(rb_first(self->entries)); | 695 | nd = hists__filter_entries(rb_first(self->entries)); |
@@ -761,6 +807,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
761 | self->hists = hists; | 807 | self->hists = hists; |
762 | self->b.refresh = hist_browser__refresh; | 808 | self->b.refresh = hist_browser__refresh; |
763 | self->b.seek = ui_browser__hists_seek; | 809 | self->b.seek = ui_browser__hists_seek; |
810 | self->b.use_navkeypressed = true, | ||
811 | self->has_symbols = sort_sym.list.next != NULL; | ||
764 | } | 812 | } |
765 | 813 | ||
766 | return self; | 814 | return self; |
@@ -782,11 +830,12 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) | |||
782 | } | 830 | } |
783 | 831 | ||
784 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | 832 | static int hists__browser_title(struct hists *self, char *bf, size_t size, |
785 | const char *ev_name, const struct dso *dso, | 833 | const char *ev_name) |
786 | const struct thread *thread) | ||
787 | { | 834 | { |
788 | char unit; | 835 | char unit; |
789 | int printed; | 836 | int printed; |
837 | const struct dso *dso = self->dso_filter; | ||
838 | const struct thread *thread = self->thread_filter; | ||
790 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 839 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; |
791 | 840 | ||
792 | nr_events = convert_unit(nr_events, &unit); | 841 | nr_events = convert_unit(nr_events, &unit); |
@@ -803,16 +852,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
803 | return printed; | 852 | return printed; |
804 | } | 853 | } |
805 | 854 | ||
806 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, | 855 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
807 | const char *helpline, const char *ev_name, | 856 | const char *helpline, const char *ev_name, |
808 | bool left_exits) | 857 | bool left_exits, |
858 | void(*timer)(void *arg), void *arg, | ||
859 | int delay_secs) | ||
809 | { | 860 | { |
810 | struct hists *self = &evsel->hists; | 861 | struct hists *self = &evsel->hists; |
811 | struct hist_browser *browser = hist_browser__new(self); | 862 | struct hist_browser *browser = hist_browser__new(self); |
812 | struct pstack *fstack; | 863 | 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; | 864 | int key = -1; |
817 | 865 | ||
818 | if (browser == NULL) | 866 | if (browser == NULL) |
@@ -824,8 +872,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
824 | 872 | ||
825 | ui_helpline__push(helpline); | 873 | ui_helpline__push(helpline); |
826 | 874 | ||
827 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
828 | dso_filter, thread_filter); | ||
829 | while (1) { | 875 | while (1) { |
830 | const struct thread *thread = NULL; | 876 | const struct thread *thread = NULL; |
831 | const struct dso *dso = NULL; | 877 | const struct dso *dso = NULL; |
@@ -834,7 +880,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
834 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 880 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
835 | browse_map = -2; | 881 | browse_map = -2; |
836 | 882 | ||
837 | key = hist_browser__run(browser, msg); | 883 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); |
838 | 884 | ||
839 | if (browser->he_selection != NULL) { | 885 | if (browser->he_selection != NULL) { |
840 | thread = hist_browser__selected_thread(browser); | 886 | thread = hist_browser__selected_thread(browser); |
@@ -842,14 +888,23 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
842 | } | 888 | } |
843 | 889 | ||
844 | switch (key) { | 890 | switch (key) { |
845 | case NEWT_KEY_TAB: | 891 | case K_TAB: |
846 | case NEWT_KEY_UNTAB: | 892 | case K_UNTAB: |
893 | if (nr_events == 1) | ||
894 | continue; | ||
847 | /* | 895 | /* |
848 | * Exit the browser, let hists__browser_tree | 896 | * Exit the browser, let hists__browser_tree |
849 | * go to the next or previous | 897 | * go to the next or previous |
850 | */ | 898 | */ |
851 | goto out_free_stack; | 899 | goto out_free_stack; |
852 | case 'a': | 900 | case 'a': |
901 | if (!browser->has_symbols) { | ||
902 | ui_browser__warning(&browser->b, delay_secs * 2, | ||
903 | "Annotation is only available for symbolic views, " | ||
904 | "include \"sym\" in --sort to use it."); | ||
905 | continue; | ||
906 | } | ||
907 | |||
853 | if (browser->selection == NULL || | 908 | if (browser->selection == NULL || |
854 | browser->selection->sym == NULL || | 909 | browser->selection->sym == NULL || |
855 | browser->selection->map->dso->annotate_warned) | 910 | browser->selection->map->dso->annotate_warned) |
@@ -859,25 +914,30 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
859 | goto zoom_dso; | 914 | goto zoom_dso; |
860 | case 't': | 915 | case 't': |
861 | goto zoom_thread; | 916 | goto zoom_thread; |
862 | case NEWT_KEY_F1: | 917 | case K_F1: |
863 | case 'h': | 918 | case 'h': |
864 | case '?': | 919 | case '?': |
865 | ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" | 920 | ui_browser__help_window(&browser->b, |
866 | "<- Zoom out\n" | 921 | "h/?/F1 Show this window\n" |
867 | "a Annotate current symbol\n" | 922 | "UP/DOWN/PGUP\n" |
868 | "h/?/F1 Show this window\n" | 923 | "PGDN/SPACE Navigate\n" |
869 | "C Collapse all callchains\n" | 924 | "q/ESC/CTRL+C Exit browser\n\n" |
870 | "E Expand all callchains\n" | 925 | "For multiple event sessions:\n\n" |
871 | "d Zoom into current DSO\n" | 926 | "TAB/UNTAB Switch events\n\n" |
872 | "t Zoom into current Thread\n" | 927 | "For symbolic views (--sort has sym):\n\n" |
873 | "TAB/UNTAB Switch events\n" | 928 | "-> Zoom into DSO/Threads & Annotate current symbol\n" |
874 | "q/CTRL+C Exit browser"); | 929 | "<- Zoom out\n" |
930 | "a Annotate current symbol\n" | ||
931 | "C Collapse all callchains\n" | ||
932 | "E Expand all callchains\n" | ||
933 | "d Zoom into current DSO\n" | ||
934 | "t Zoom into current Thread"); | ||
875 | continue; | 935 | continue; |
876 | case NEWT_KEY_ENTER: | 936 | case K_ENTER: |
877 | case NEWT_KEY_RIGHT: | 937 | case K_RIGHT: |
878 | /* menu */ | 938 | /* menu */ |
879 | break; | 939 | break; |
880 | case NEWT_KEY_LEFT: { | 940 | case K_LEFT: { |
881 | const void *top; | 941 | const void *top; |
882 | 942 | ||
883 | if (pstack__empty(fstack)) { | 943 | if (pstack__empty(fstack)) { |
@@ -889,21 +949,28 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
889 | continue; | 949 | continue; |
890 | } | 950 | } |
891 | top = pstack__pop(fstack); | 951 | top = pstack__pop(fstack); |
892 | if (top == &dso_filter) | 952 | if (top == &browser->hists->dso_filter) |
893 | goto zoom_out_dso; | 953 | goto zoom_out_dso; |
894 | if (top == &thread_filter) | 954 | if (top == &browser->hists->thread_filter) |
895 | goto zoom_out_thread; | 955 | goto zoom_out_thread; |
896 | continue; | 956 | continue; |
897 | } | 957 | } |
898 | case NEWT_KEY_ESCAPE: | 958 | case K_ESC: |
899 | if (!left_exits && | 959 | if (!left_exits && |
900 | !ui__dialog_yesno("Do you really want to exit?")) | 960 | !ui_browser__dialog_yesno(&browser->b, |
961 | "Do you really want to exit?")) | ||
901 | continue; | 962 | continue; |
902 | /* Fall thru */ | 963 | /* Fall thru */ |
903 | default: | 964 | case 'q': |
965 | case CTRL('c'): | ||
904 | goto out_free_stack; | 966 | goto out_free_stack; |
967 | default: | ||
968 | continue; | ||
905 | } | 969 | } |
906 | 970 | ||
971 | if (!browser->has_symbols) | ||
972 | goto add_exit_option; | ||
973 | |||
907 | if (browser->selection != NULL && | 974 | if (browser->selection != NULL && |
908 | browser->selection->sym != NULL && | 975 | browser->selection->sym != NULL && |
909 | !browser->selection->map->dso->annotate_warned && | 976 | !browser->selection->map->dso->annotate_warned && |
@@ -913,14 +980,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
913 | 980 | ||
914 | if (thread != NULL && | 981 | if (thread != NULL && |
915 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 982 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", |
916 | (thread_filter ? "out of" : "into"), | 983 | (browser->hists->thread_filter ? "out of" : "into"), |
917 | (thread->comm_set ? thread->comm : ""), | 984 | (thread->comm_set ? thread->comm : ""), |
918 | thread->pid) > 0) | 985 | thread->pid) > 0) |
919 | zoom_thread = nr_options++; | 986 | zoom_thread = nr_options++; |
920 | 987 | ||
921 | if (dso != NULL && | 988 | if (dso != NULL && |
922 | asprintf(&options[nr_options], "Zoom %s %s DSO", | 989 | asprintf(&options[nr_options], "Zoom %s %s DSO", |
923 | (dso_filter ? "out of" : "into"), | 990 | (browser->hists->dso_filter ? "out of" : "into"), |
924 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | 991 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) |
925 | zoom_dso = nr_options++; | 992 | zoom_dso = nr_options++; |
926 | 993 | ||
@@ -928,7 +995,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
928 | browser->selection->map != NULL && | 995 | browser->selection->map != NULL && |
929 | asprintf(&options[nr_options], "Browse map details") > 0) | 996 | asprintf(&options[nr_options], "Browse map details") > 0) |
930 | browse_map = nr_options++; | 997 | browse_map = nr_options++; |
931 | 998 | add_exit_option: | |
932 | options[nr_options++] = (char *)"Exit"; | 999 | options[nr_options++] = (char *)"Exit"; |
933 | 1000 | ||
934 | choice = ui__popup_menu(nr_options, options); | 1001 | choice = ui__popup_menu(nr_options, options); |
@@ -944,50 +1011,59 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
944 | 1011 | ||
945 | if (choice == annotate) { | 1012 | if (choice == annotate) { |
946 | struct hist_entry *he; | 1013 | struct hist_entry *he; |
1014 | int err; | ||
947 | do_annotate: | 1015 | do_annotate: |
948 | he = hist_browser__selected_entry(browser); | 1016 | he = hist_browser__selected_entry(browser); |
949 | if (he == NULL) | 1017 | if (he == NULL) |
950 | continue; | 1018 | continue; |
951 | 1019 | /* | |
952 | hist_entry__tui_annotate(he, evsel->idx); | 1020 | * Don't let this be freed, say, by hists__decay_entry. |
1021 | */ | ||
1022 | he->used = true; | ||
1023 | err = hist_entry__tui_annotate(he, evsel->idx, | ||
1024 | timer, arg, delay_secs); | ||
1025 | he->used = false; | ||
1026 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | ||
1027 | if (err) | ||
1028 | ui_browser__handle_resize(&browser->b); | ||
953 | } else if (choice == browse_map) | 1029 | } else if (choice == browse_map) |
954 | map__browse(browser->selection->map); | 1030 | map__browse(browser->selection->map); |
955 | else if (choice == zoom_dso) { | 1031 | else if (choice == zoom_dso) { |
956 | zoom_dso: | 1032 | zoom_dso: |
957 | if (dso_filter) { | 1033 | if (browser->hists->dso_filter) { |
958 | pstack__remove(fstack, &dso_filter); | 1034 | pstack__remove(fstack, &browser->hists->dso_filter); |
959 | zoom_out_dso: | 1035 | zoom_out_dso: |
960 | ui_helpline__pop(); | 1036 | ui_helpline__pop(); |
961 | dso_filter = NULL; | 1037 | browser->hists->dso_filter = NULL; |
1038 | sort_dso.elide = false; | ||
962 | } else { | 1039 | } else { |
963 | if (dso == NULL) | 1040 | if (dso == NULL) |
964 | continue; | 1041 | continue; |
965 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", | 1042 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", |
966 | dso->kernel ? "the Kernel" : dso->short_name); | 1043 | dso->kernel ? "the Kernel" : dso->short_name); |
967 | dso_filter = dso; | 1044 | browser->hists->dso_filter = dso; |
968 | pstack__push(fstack, &dso_filter); | 1045 | sort_dso.elide = true; |
1046 | pstack__push(fstack, &browser->hists->dso_filter); | ||
969 | } | 1047 | } |
970 | hists__filter_by_dso(self, dso_filter); | 1048 | hists__filter_by_dso(self); |
971 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
972 | dso_filter, thread_filter); | ||
973 | hist_browser__reset(browser); | 1049 | hist_browser__reset(browser); |
974 | } else if (choice == zoom_thread) { | 1050 | } else if (choice == zoom_thread) { |
975 | zoom_thread: | 1051 | zoom_thread: |
976 | if (thread_filter) { | 1052 | if (browser->hists->thread_filter) { |
977 | pstack__remove(fstack, &thread_filter); | 1053 | pstack__remove(fstack, &browser->hists->thread_filter); |
978 | zoom_out_thread: | 1054 | zoom_out_thread: |
979 | ui_helpline__pop(); | 1055 | ui_helpline__pop(); |
980 | thread_filter = NULL; | 1056 | browser->hists->thread_filter = NULL; |
1057 | sort_thread.elide = false; | ||
981 | } else { | 1058 | } else { |
982 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | 1059 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", |
983 | thread->comm_set ? thread->comm : "", | 1060 | thread->comm_set ? thread->comm : "", |
984 | thread->pid); | 1061 | thread->pid); |
985 | thread_filter = thread; | 1062 | browser->hists->thread_filter = thread; |
986 | pstack__push(fstack, &thread_filter); | 1063 | sort_thread.elide = true; |
1064 | pstack__push(fstack, &browser->hists->thread_filter); | ||
987 | } | 1065 | } |
988 | hists__filter_by_thread(self, thread_filter); | 1066 | hists__filter_by_thread(self); |
989 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
990 | dso_filter, thread_filter); | ||
991 | hist_browser__reset(browser); | 1067 | hist_browser__reset(browser); |
992 | } | 1068 | } |
993 | } | 1069 | } |
@@ -1001,6 +1077,7 @@ out: | |||
1001 | struct perf_evsel_menu { | 1077 | struct perf_evsel_menu { |
1002 | struct ui_browser b; | 1078 | struct ui_browser b; |
1003 | struct perf_evsel *selection; | 1079 | struct perf_evsel *selection; |
1080 | bool lost_events, lost_events_warned; | ||
1004 | }; | 1081 | }; |
1005 | 1082 | ||
1006 | static void perf_evsel_menu__write(struct ui_browser *browser, | 1083 | static void perf_evsel_menu__write(struct ui_browser *browser, |
@@ -1013,22 +1090,38 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1013 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1090 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; |
1014 | const char *ev_name = event_name(evsel); | 1091 | const char *ev_name = event_name(evsel); |
1015 | char bf[256], unit; | 1092 | char bf[256], unit; |
1093 | const char *warn = " "; | ||
1094 | size_t printed; | ||
1016 | 1095 | ||
1017 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | 1096 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
1018 | HE_COLORSET_NORMAL); | 1097 | HE_COLORSET_NORMAL); |
1019 | 1098 | ||
1020 | nr_events = convert_unit(nr_events, &unit); | 1099 | nr_events = convert_unit(nr_events, &unit); |
1021 | snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | 1100 | printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, |
1022 | unit, unit == ' ' ? "" : " ", ev_name); | 1101 | unit, unit == ' ' ? "" : " ", ev_name); |
1023 | slsmg_write_nstring(bf, browser->width); | 1102 | slsmg_printf("%s", bf); |
1103 | |||
1104 | nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | ||
1105 | if (nr_events != 0) { | ||
1106 | menu->lost_events = true; | ||
1107 | if (!current_entry) | ||
1108 | ui_browser__set_color(browser, HE_COLORSET_TOP); | ||
1109 | nr_events = convert_unit(nr_events, &unit); | ||
1110 | snprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", nr_events, | ||
1111 | unit, unit == ' ' ? "" : " "); | ||
1112 | warn = bf; | ||
1113 | } | ||
1114 | |||
1115 | slsmg_write_nstring(warn, browser->width - printed); | ||
1024 | 1116 | ||
1025 | if (current_entry) | 1117 | if (current_entry) |
1026 | menu->selection = evsel; | 1118 | menu->selection = evsel; |
1027 | } | 1119 | } |
1028 | 1120 | ||
1029 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | 1121 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
1122 | int nr_events, const char *help, | ||
1123 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
1030 | { | 1124 | { |
1031 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
1032 | struct perf_evlist *evlist = menu->b.priv; | 1125 | struct perf_evlist *evlist = menu->b.priv; |
1033 | struct perf_evsel *pos; | 1126 | struct perf_evsel *pos; |
1034 | const char *ev_name, *title = "Available samples"; | 1127 | const char *ev_name, *title = "Available samples"; |
@@ -1038,50 +1131,72 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | |||
1038 | "ESC: exit, ENTER|->: Browse histograms") < 0) | 1131 | "ESC: exit, ENTER|->: Browse histograms") < 0) |
1039 | return -1; | 1132 | return -1; |
1040 | 1133 | ||
1041 | ui_browser__add_exit_keys(&menu->b, exit_keys); | ||
1042 | |||
1043 | while (1) { | 1134 | while (1) { |
1044 | key = ui_browser__run(&menu->b); | 1135 | key = ui_browser__run(&menu->b, delay_secs); |
1045 | 1136 | ||
1046 | switch (key) { | 1137 | switch (key) { |
1047 | case NEWT_KEY_RIGHT: | 1138 | case K_TIMER: |
1048 | case NEWT_KEY_ENTER: | 1139 | timer(arg); |
1140 | |||
1141 | if (!menu->lost_events_warned && menu->lost_events) { | ||
1142 | ui_browser__warn_lost_events(&menu->b); | ||
1143 | menu->lost_events_warned = true; | ||
1144 | } | ||
1145 | continue; | ||
1146 | case K_RIGHT: | ||
1147 | case K_ENTER: | ||
1049 | if (!menu->selection) | 1148 | if (!menu->selection) |
1050 | continue; | 1149 | continue; |
1051 | pos = menu->selection; | 1150 | pos = menu->selection; |
1052 | browse_hists: | 1151 | browse_hists: |
1152 | perf_evlist__set_selected(evlist, pos); | ||
1153 | /* | ||
1154 | * Give the calling tool a chance to populate the non | ||
1155 | * default evsel resorted hists tree. | ||
1156 | */ | ||
1157 | if (timer) | ||
1158 | timer(arg); | ||
1053 | ev_name = event_name(pos); | 1159 | ev_name = event_name(pos); |
1054 | key = perf_evsel__hists_browse(pos, help, ev_name, true); | 1160 | key = perf_evsel__hists_browse(pos, nr_events, help, |
1161 | ev_name, true, timer, | ||
1162 | arg, delay_secs); | ||
1055 | ui_browser__show_title(&menu->b, title); | 1163 | ui_browser__show_title(&menu->b, title); |
1056 | break; | 1164 | switch (key) { |
1057 | case NEWT_KEY_LEFT: | 1165 | case K_TAB: |
1166 | if (pos->node.next == &evlist->entries) | ||
1167 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
1168 | else | ||
1169 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
1170 | goto browse_hists; | ||
1171 | case K_UNTAB: | ||
1172 | if (pos->node.prev == &evlist->entries) | ||
1173 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
1174 | else | ||
1175 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
1176 | goto browse_hists; | ||
1177 | case K_ESC: | ||
1178 | if (!ui_browser__dialog_yesno(&menu->b, | ||
1179 | "Do you really want to exit?")) | ||
1180 | continue; | ||
1181 | /* Fall thru */ | ||
1182 | case 'q': | ||
1183 | case CTRL('c'): | ||
1184 | goto out; | ||
1185 | default: | ||
1186 | continue; | ||
1187 | } | ||
1188 | case K_LEFT: | ||
1058 | continue; | 1189 | continue; |
1059 | case NEWT_KEY_ESCAPE: | 1190 | case K_ESC: |
1060 | if (!ui__dialog_yesno("Do you really want to exit?")) | 1191 | if (!ui_browser__dialog_yesno(&menu->b, |
1192 | "Do you really want to exit?")) | ||
1061 | continue; | 1193 | continue; |
1062 | /* Fall thru */ | 1194 | /* Fall thru */ |
1063 | default: | ||
1064 | goto out; | ||
1065 | } | ||
1066 | |||
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': | 1195 | case 'q': |
1081 | case CTRL('c'): | 1196 | case CTRL('c'): |
1082 | goto out; | 1197 | goto out; |
1083 | default: | 1198 | default: |
1084 | break; | 1199 | continue; |
1085 | } | 1200 | } |
1086 | } | 1201 | } |
1087 | 1202 | ||
@@ -1091,7 +1206,9 @@ out: | |||
1091 | } | 1206 | } |
1092 | 1207 | ||
1093 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1208 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
1094 | const char *help) | 1209 | const char *help, |
1210 | void(*timer)(void *arg), void *arg, | ||
1211 | int delay_secs) | ||
1095 | { | 1212 | { |
1096 | struct perf_evsel *pos; | 1213 | struct perf_evsel *pos; |
1097 | struct perf_evsel_menu menu = { | 1214 | struct perf_evsel_menu menu = { |
@@ -1121,18 +1238,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1121 | pos->name = strdup(ev_name); | 1238 | pos->name = strdup(ev_name); |
1122 | } | 1239 | } |
1123 | 1240 | ||
1124 | return perf_evsel_menu__run(&menu, help); | 1241 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, |
1242 | arg, delay_secs); | ||
1125 | } | 1243 | } |
1126 | 1244 | ||
1127 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) | 1245 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
1246 | void(*timer)(void *arg), void *arg, | ||
1247 | int delay_secs) | ||
1128 | { | 1248 | { |
1129 | 1249 | ||
1130 | if (evlist->nr_entries == 1) { | 1250 | if (evlist->nr_entries == 1) { |
1131 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1251 | struct perf_evsel *first = list_entry(evlist->entries.next, |
1132 | struct perf_evsel, node); | 1252 | struct perf_evsel, node); |
1133 | const char *ev_name = event_name(first); | 1253 | const char *ev_name = event_name(first); |
1134 | return perf_evsel__hists_browse(first, help, ev_name, false); | 1254 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, |
1255 | ev_name, false, timer, arg, | ||
1256 | delay_secs); | ||
1135 | } | 1257 | } |
1136 | 1258 | ||
1137 | return __perf_evlist__tui_browse_hists(evlist, help); | 1259 | return __perf_evlist__tui_browse_hists(evlist, help, |
1260 | timer, arg, delay_secs); | ||
1138 | } | 1261 | } |