diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 230 |
1 files changed, 182 insertions, 48 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 4a9fe228be2a..89b7f68a1799 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -60,7 +60,7 @@ static int system_wide = 0; | |||
60 | static int default_interval = 0; | 60 | static int default_interval = 0; |
61 | 61 | ||
62 | static int count_filter = 5; | 62 | static int count_filter = 5; |
63 | static int print_entries = 15; | 63 | static int print_entries; |
64 | 64 | ||
65 | static int target_pid = -1; | 65 | static int target_pid = -1; |
66 | static int inherit = 0; | 66 | static int inherit = 0; |
@@ -76,6 +76,9 @@ static int delay_secs = 2; | |||
76 | static int zero = 0; | 76 | static int zero = 0; |
77 | static int dump_symtab = 0; | 77 | static int dump_symtab = 0; |
78 | 78 | ||
79 | static bool hide_kernel_symbols = false; | ||
80 | static bool hide_user_symbols = false; | ||
81 | |||
79 | /* | 82 | /* |
80 | * Source | 83 | * Source |
81 | */ | 84 | */ |
@@ -104,6 +107,7 @@ struct sym_entry { | |||
104 | unsigned long snap_count; | 107 | unsigned long snap_count; |
105 | double weight; | 108 | double weight; |
106 | int skip; | 109 | int skip; |
110 | u8 origin; | ||
107 | struct map *map; | 111 | struct map *map; |
108 | struct source_line *source; | 112 | struct source_line *source; |
109 | struct source_line *lines; | 113 | struct source_line *lines; |
@@ -115,6 +119,36 @@ struct sym_entry { | |||
115 | * Source functions | 119 | * Source functions |
116 | */ | 120 | */ |
117 | 121 | ||
122 | /* most GUI terminals set LINES (although some don't export it) */ | ||
123 | static int term_rows(void) | ||
124 | { | ||
125 | char *lines_string = getenv("LINES"); | ||
126 | int n_lines; | ||
127 | |||
128 | if (lines_string && (n_lines = atoi(lines_string)) > 0) | ||
129 | return n_lines; | ||
130 | #ifdef TIOCGWINSZ | ||
131 | else { | ||
132 | struct winsize ws; | ||
133 | if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_row) | ||
134 | return ws.ws_row; | ||
135 | } | ||
136 | #endif | ||
137 | return 25; | ||
138 | } | ||
139 | |||
140 | static void update_print_entries(void) | ||
141 | { | ||
142 | print_entries = term_rows(); | ||
143 | if (print_entries > 9) | ||
144 | print_entries -= 9; | ||
145 | } | ||
146 | |||
147 | static void sig_winch_handler(int sig __used) | ||
148 | { | ||
149 | update_print_entries(); | ||
150 | } | ||
151 | |||
118 | static void parse_source(struct sym_entry *syme) | 152 | static void parse_source(struct sym_entry *syme) |
119 | { | 153 | { |
120 | struct symbol *sym; | 154 | struct symbol *sym; |
@@ -318,7 +352,7 @@ static void show_details(struct sym_entry *syme) | |||
318 | } | 352 | } |
319 | 353 | ||
320 | /* | 354 | /* |
321 | * Symbols will be added here in record_ip and will get out | 355 | * Symbols will be added here in event__process_sample and will get out |
322 | * after decayed. | 356 | * after decayed. |
323 | */ | 357 | */ |
324 | static LIST_HEAD(active_symbols); | 358 | static LIST_HEAD(active_symbols); |
@@ -400,6 +434,13 @@ static void print_sym_table(void) | |||
400 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 434 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
401 | syme->snap_count = syme->count[snap]; | 435 | syme->snap_count = syme->count[snap]; |
402 | if (syme->snap_count != 0) { | 436 | if (syme->snap_count != 0) { |
437 | if ((hide_user_symbols && | ||
438 | syme->origin == PERF_RECORD_MISC_USER) || | ||
439 | (hide_kernel_symbols && | ||
440 | syme->origin == PERF_RECORD_MISC_KERNEL)) { | ||
441 | list_remove_active_sym(syme); | ||
442 | continue; | ||
443 | } | ||
403 | syme->weight = sym_weight(syme); | 444 | syme->weight = sym_weight(syme); |
404 | rb_insert_active_sym(&tmp, syme); | 445 | rb_insert_active_sym(&tmp, syme); |
405 | sum_ksamples += syme->snap_count; | 446 | sum_ksamples += syme->snap_count; |
@@ -459,18 +500,18 @@ static void print_sym_table(void) | |||
459 | } | 500 | } |
460 | 501 | ||
461 | if (nr_counters == 1) | 502 | if (nr_counters == 1) |
462 | printf(" samples pcnt"); | 503 | printf(" samples pcnt"); |
463 | else | 504 | else |
464 | printf(" weight samples pcnt"); | 505 | printf(" weight samples pcnt"); |
465 | 506 | ||
466 | if (verbose) | 507 | if (verbose) |
467 | printf(" RIP "); | 508 | printf(" RIP "); |
468 | printf(" kernel function\n"); | 509 | printf(" function DSO\n"); |
469 | printf(" %s _______ _____", | 510 | printf(" %s _______ _____", |
470 | nr_counters == 1 ? " " : "______"); | 511 | nr_counters == 1 ? " " : "______"); |
471 | if (verbose) | 512 | if (verbose) |
472 | printf(" ________________"); | 513 | printf(" ________________"); |
473 | printf(" _______________\n\n"); | 514 | printf(" ________________________________ ________________\n\n"); |
474 | 515 | ||
475 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | 516 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
476 | struct symbol *sym; | 517 | struct symbol *sym; |
@@ -486,16 +527,15 @@ static void print_sym_table(void) | |||
486 | sum_ksamples)); | 527 | sum_ksamples)); |
487 | 528 | ||
488 | if (nr_counters == 1 || !display_weighted) | 529 | if (nr_counters == 1 || !display_weighted) |
489 | printf("%20.2f - ", syme->weight); | 530 | printf("%20.2f ", syme->weight); |
490 | else | 531 | else |
491 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); | 532 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
492 | 533 | ||
493 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); | 534 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); |
494 | if (verbose) | 535 | if (verbose) |
495 | printf(" - %016llx", sym->start); | 536 | printf(" %016llx", sym->start); |
496 | printf(" : %s", sym->name); | 537 | printf(" %-32s", sym->name); |
497 | if (syme->map->dso->name[0] == '[') | 538 | printf(" %s", syme->map->dso->short_name); |
498 | printf(" \t%s", syme->map->dso->name); | ||
499 | printf("\n"); | 539 | printf("\n"); |
500 | } | 540 | } |
501 | } | 541 | } |
@@ -608,6 +648,12 @@ static void print_mapped_keys(void) | |||
608 | if (nr_counters > 1) | 648 | if (nr_counters > 1) |
609 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 649 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
610 | 650 | ||
651 | fprintf(stdout, | ||
652 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | ||
653 | hide_kernel_symbols ? "yes" : "no"); | ||
654 | fprintf(stdout, | ||
655 | "\t[U] hide user symbols. \t(%s)\n", | ||
656 | hide_user_symbols ? "yes" : "no"); | ||
611 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); | 657 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); |
612 | fprintf(stdout, "\t[qQ] quit.\n"); | 658 | fprintf(stdout, "\t[qQ] quit.\n"); |
613 | } | 659 | } |
@@ -621,6 +667,8 @@ static int key_mapped(int c) | |||
621 | case 'z': | 667 | case 'z': |
622 | case 'q': | 668 | case 'q': |
623 | case 'Q': | 669 | case 'Q': |
670 | case 'K': | ||
671 | case 'U': | ||
624 | return 1; | 672 | return 1; |
625 | case 'E': | 673 | case 'E': |
626 | case 'w': | 674 | case 'w': |
@@ -669,6 +717,11 @@ static void handle_keypress(int c) | |||
669 | break; | 717 | break; |
670 | case 'e': | 718 | case 'e': |
671 | prompt_integer(&print_entries, "Enter display entries (lines)"); | 719 | prompt_integer(&print_entries, "Enter display entries (lines)"); |
720 | if (print_entries == 0) { | ||
721 | update_print_entries(); | ||
722 | signal(SIGWINCH, sig_winch_handler); | ||
723 | } else | ||
724 | signal(SIGWINCH, SIG_DFL); | ||
672 | break; | 725 | break; |
673 | case 'E': | 726 | case 'E': |
674 | if (nr_counters > 1) { | 727 | if (nr_counters > 1) { |
@@ -693,6 +746,9 @@ static void handle_keypress(int c) | |||
693 | case 'F': | 746 | case 'F': |
694 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | 747 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); |
695 | break; | 748 | break; |
749 | case 'K': | ||
750 | hide_kernel_symbols = !hide_kernel_symbols; | ||
751 | break; | ||
696 | case 'q': | 752 | case 'q': |
697 | case 'Q': | 753 | case 'Q': |
698 | printf("exiting.\n"); | 754 | printf("exiting.\n"); |
@@ -712,6 +768,9 @@ static void handle_keypress(int c) | |||
712 | pthread_mutex_unlock(&syme->source_lock); | 768 | pthread_mutex_unlock(&syme->source_lock); |
713 | } | 769 | } |
714 | break; | 770 | break; |
771 | case 'U': | ||
772 | hide_user_symbols = !hide_user_symbols; | ||
773 | break; | ||
715 | case 'w': | 774 | case 'w': |
716 | display_weighted = ~display_weighted; | 775 | display_weighted = ~display_weighted; |
717 | break; | 776 | break; |
@@ -790,7 +849,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
790 | strstr(name, "_text_end")) | 849 | strstr(name, "_text_end")) |
791 | return 1; | 850 | return 1; |
792 | 851 | ||
793 | syme = dso__sym_priv(map->dso, sym); | 852 | syme = symbol__priv(sym); |
794 | syme->map = map; | 853 | syme->map = map; |
795 | pthread_mutex_init(&syme->source_lock, NULL); | 854 | pthread_mutex_init(&syme->source_lock, NULL); |
796 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) | 855 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) |
@@ -808,8 +867,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
808 | 867 | ||
809 | static int parse_symbols(void) | 868 | static int parse_symbols(void) |
810 | { | 869 | { |
811 | if (dsos__load_kernel(vmlinux_name, sizeof(struct sym_entry), | 870 | if (dsos__load_kernel(vmlinux_name, symbol_filter, 1) <= 0) |
812 | symbol_filter, 1) <= 0) | ||
813 | return -1; | 871 | return -1; |
814 | 872 | ||
815 | if (dump_symtab) | 873 | if (dump_symtab) |
@@ -818,41 +876,104 @@ static int parse_symbols(void) | |||
818 | return 0; | 876 | return 0; |
819 | } | 877 | } |
820 | 878 | ||
821 | /* | 879 | static void event__process_sample(const event_t *self, int counter) |
822 | * Binary search in the histogram table and record the hit: | ||
823 | */ | ||
824 | static void record_ip(u64 ip, int counter) | ||
825 | { | 880 | { |
881 | u64 ip = self->ip.ip; | ||
826 | struct map *map; | 882 | struct map *map; |
827 | struct symbol *sym = kernel_maps__find_symbol(ip, &map); | 883 | struct sym_entry *syme; |
828 | 884 | struct symbol *sym; | |
829 | if (sym != NULL) { | 885 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
830 | struct sym_entry *syme = dso__sym_priv(map->dso, sym); | 886 | |
831 | 887 | switch (origin) { | |
832 | if (!syme->skip) { | 888 | case PERF_RECORD_MISC_USER: { |
833 | syme->count[counter]++; | 889 | struct thread *thread; |
834 | record_precise_ip(syme, counter, ip); | 890 | |
835 | pthread_mutex_lock(&active_symbols_lock); | 891 | if (hide_user_symbols) |
836 | if (list_empty(&syme->node) || !syme->node.next) | 892 | return; |
837 | __list_insert_active_sym(syme); | 893 | |
838 | pthread_mutex_unlock(&active_symbols_lock); | 894 | thread = threads__findnew(self->ip.pid); |
895 | if (thread == NULL) | ||
839 | return; | 896 | return; |
897 | |||
898 | map = thread__find_map(thread, ip); | ||
899 | if (map != NULL) { | ||
900 | ip = map->map_ip(map, ip); | ||
901 | sym = map__find_symbol(map, ip, symbol_filter); | ||
902 | if (sym == NULL) | ||
903 | return; | ||
904 | userspace_samples++; | ||
905 | break; | ||
840 | } | 906 | } |
841 | } | 907 | } |
908 | /* | ||
909 | * If this is outside of all known maps, | ||
910 | * and is a negative address, try to look it | ||
911 | * up in the kernel dso, as it might be a | ||
912 | * vsyscall or vdso (which executes in user-mode). | ||
913 | */ | ||
914 | if ((long long)ip >= 0) | ||
915 | return; | ||
916 | /* Fall thru */ | ||
917 | case PERF_RECORD_MISC_KERNEL: | ||
918 | if (hide_kernel_symbols) | ||
919 | return; | ||
920 | |||
921 | sym = kernel_maps__find_symbol(ip, &map); | ||
922 | if (sym == NULL) | ||
923 | return; | ||
924 | break; | ||
925 | default: | ||
926 | return; | ||
927 | } | ||
842 | 928 | ||
843 | samples--; | 929 | syme = symbol__priv(sym); |
930 | |||
931 | if (!syme->skip) { | ||
932 | syme->count[counter]++; | ||
933 | syme->origin = origin; | ||
934 | record_precise_ip(syme, counter, ip); | ||
935 | pthread_mutex_lock(&active_symbols_lock); | ||
936 | if (list_empty(&syme->node) || !syme->node.next) | ||
937 | __list_insert_active_sym(syme); | ||
938 | pthread_mutex_unlock(&active_symbols_lock); | ||
939 | ++samples; | ||
940 | return; | ||
941 | } | ||
844 | } | 942 | } |
845 | 943 | ||
846 | static void process_event(u64 ip, int counter, int user) | 944 | static void event__process_mmap(event_t *self) |
847 | { | 945 | { |
848 | samples++; | 946 | struct thread *thread = threads__findnew(self->mmap.pid); |
849 | 947 | ||
850 | if (user) { | 948 | if (thread != NULL) { |
851 | userspace_samples++; | 949 | struct map *map = map__new(&self->mmap, NULL, 0); |
852 | return; | 950 | if (map != NULL) |
951 | thread__insert_map(thread, map); | ||
853 | } | 952 | } |
953 | } | ||
854 | 954 | ||
855 | record_ip(ip, counter); | 955 | static void event__process_comm(event_t *self) |
956 | { | ||
957 | struct thread *thread = threads__findnew(self->comm.pid); | ||
958 | |||
959 | if (thread != NULL) | ||
960 | thread__set_comm(thread, self->comm.comm); | ||
961 | } | ||
962 | |||
963 | static int event__process(event_t *event) | ||
964 | { | ||
965 | switch (event->header.type) { | ||
966 | case PERF_RECORD_COMM: | ||
967 | event__process_comm(event); | ||
968 | break; | ||
969 | case PERF_RECORD_MMAP: | ||
970 | event__process_mmap(event); | ||
971 | break; | ||
972 | default: | ||
973 | break; | ||
974 | } | ||
975 | |||
976 | return 0; | ||
856 | } | 977 | } |
857 | 978 | ||
858 | struct mmap_data { | 979 | struct mmap_data { |
@@ -925,13 +1046,11 @@ static void mmap_read_counter(struct mmap_data *md) | |||
925 | event = &event_copy; | 1046 | event = &event_copy; |
926 | } | 1047 | } |
927 | 1048 | ||
1049 | if (event->header.type == PERF_RECORD_SAMPLE) | ||
1050 | event__process_sample(event, md->counter); | ||
1051 | else | ||
1052 | event__process(event); | ||
928 | old += size; | 1053 | old += size; |
929 | |||
930 | if (event->header.type == PERF_RECORD_SAMPLE) { | ||
931 | int user = | ||
932 | (event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER; | ||
933 | process_event(event->ip.ip, md->counter, user); | ||
934 | } | ||
935 | } | 1054 | } |
936 | 1055 | ||
937 | md->prev = old; | 1056 | md->prev = old; |
@@ -973,6 +1092,7 @@ static void start_counter(int i, int counter) | |||
973 | } | 1092 | } |
974 | 1093 | ||
975 | attr->inherit = (cpu < 0) && inherit; | 1094 | attr->inherit = (cpu < 0) && inherit; |
1095 | attr->mmap = 1; | ||
976 | 1096 | ||
977 | try_again: | 1097 | try_again: |
978 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); | 1098 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); |
@@ -980,7 +1100,7 @@ try_again: | |||
980 | if (fd[i][counter] < 0) { | 1100 | if (fd[i][counter] < 0) { |
981 | int err = errno; | 1101 | int err = errno; |
982 | 1102 | ||
983 | if (err == EPERM) | 1103 | if (err == EPERM || err == EACCES) |
984 | die("No permission - are you root?\n"); | 1104 | die("No permission - are you root?\n"); |
985 | /* | 1105 | /* |
986 | * If it's cycles then fall back to hrtimer | 1106 | * If it's cycles then fall back to hrtimer |
@@ -1031,6 +1151,11 @@ static int __cmd_top(void) | |||
1031 | int i, counter; | 1151 | int i, counter; |
1032 | int ret; | 1152 | int ret; |
1033 | 1153 | ||
1154 | if (target_pid != -1) | ||
1155 | event__synthesize_thread(target_pid, event__process); | ||
1156 | else | ||
1157 | event__synthesize_threads(event__process); | ||
1158 | |||
1034 | for (i = 0; i < nr_cpus; i++) { | 1159 | for (i = 0; i < nr_cpus; i++) { |
1035 | group_fd = -1; | 1160 | group_fd = -1; |
1036 | for (counter = 0; counter < nr_counters; counter++) | 1161 | for (counter = 0; counter < nr_counters; counter++) |
@@ -1087,6 +1212,8 @@ static const struct option options[] = { | |||
1087 | OPT_INTEGER('C', "CPU", &profile_cpu, | 1212 | OPT_INTEGER('C', "CPU", &profile_cpu, |
1088 | "CPU to profile on"), | 1213 | "CPU to profile on"), |
1089 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), | 1214 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), |
1215 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | ||
1216 | "hide kernel symbols"), | ||
1090 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | 1217 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, |
1091 | "number of mmap data pages"), | 1218 | "number of mmap data pages"), |
1092 | OPT_INTEGER('r', "realtime", &realtime_prio, | 1219 | OPT_INTEGER('r', "realtime", &realtime_prio, |
@@ -1109,6 +1236,8 @@ static const struct option options[] = { | |||
1109 | "profile at this frequency"), | 1236 | "profile at this frequency"), |
1110 | OPT_INTEGER('E', "entries", &print_entries, | 1237 | OPT_INTEGER('E', "entries", &print_entries, |
1111 | "display this many functions"), | 1238 | "display this many functions"), |
1239 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, | ||
1240 | "hide user symbols"), | ||
1112 | OPT_BOOLEAN('v', "verbose", &verbose, | 1241 | OPT_BOOLEAN('v', "verbose", &verbose, |
1113 | "be more verbose (show counter open errors, etc)"), | 1242 | "be more verbose (show counter open errors, etc)"), |
1114 | OPT_END() | 1243 | OPT_END() |
@@ -1118,7 +1247,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1118 | { | 1247 | { |
1119 | int counter; | 1248 | int counter; |
1120 | 1249 | ||
1121 | symbol__init(); | 1250 | symbol__init(sizeof(struct sym_entry)); |
1122 | 1251 | ||
1123 | page_size = sysconf(_SC_PAGE_SIZE); | 1252 | page_size = sysconf(_SC_PAGE_SIZE); |
1124 | 1253 | ||
@@ -1172,5 +1301,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1172 | if (target_pid != -1 || profile_cpu != -1) | 1301 | if (target_pid != -1 || profile_cpu != -1) |
1173 | nr_cpus = 1; | 1302 | nr_cpus = 1; |
1174 | 1303 | ||
1304 | if (print_entries == 0) { | ||
1305 | update_print_entries(); | ||
1306 | signal(SIGWINCH, sig_winch_handler); | ||
1307 | } | ||
1308 | |||
1175 | return __cmd_top(); | 1309 | return __cmd_top(); |
1176 | } | 1310 | } |