diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 298 |
1 files changed, 207 insertions, 91 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 1f529321607e..397290a0a76e 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -55,9 +55,9 @@ | |||
55 | #include <linux/unistd.h> | 55 | #include <linux/unistd.h> |
56 | #include <linux/types.h> | 56 | #include <linux/types.h> |
57 | 57 | ||
58 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | 58 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; |
59 | 59 | ||
60 | static int system_wide = 0; | 60 | static bool system_wide = false; |
61 | 61 | ||
62 | static int default_interval = 0; | 62 | static int default_interval = 0; |
63 | 63 | ||
@@ -65,18 +65,21 @@ static int count_filter = 5; | |||
65 | static int print_entries; | 65 | static int print_entries; |
66 | 66 | ||
67 | static int target_pid = -1; | 67 | static int target_pid = -1; |
68 | static int inherit = 0; | 68 | static int target_tid = -1; |
69 | static pid_t *all_tids = NULL; | ||
70 | static int thread_num = 0; | ||
71 | static bool inherit = false; | ||
69 | static int profile_cpu = -1; | 72 | static int profile_cpu = -1; |
70 | static int nr_cpus = 0; | 73 | static int nr_cpus = 0; |
71 | static unsigned int realtime_prio = 0; | 74 | static int realtime_prio = 0; |
72 | static int group = 0; | 75 | static bool group = false; |
73 | static unsigned int page_size; | 76 | static unsigned int page_size; |
74 | static unsigned int mmap_pages = 16; | 77 | static unsigned int mmap_pages = 16; |
75 | static int freq = 1000; /* 1 KHz */ | 78 | static int freq = 1000; /* 1 KHz */ |
76 | 79 | ||
77 | static int delay_secs = 2; | 80 | static int delay_secs = 2; |
78 | static int zero = 0; | 81 | static bool zero = false; |
79 | static int dump_symtab = 0; | 82 | static bool dump_symtab = false; |
80 | 83 | ||
81 | static bool hide_kernel_symbols = false; | 84 | static bool hide_kernel_symbols = false; |
82 | static bool hide_user_symbols = false; | 85 | static bool hide_user_symbols = false; |
@@ -93,7 +96,7 @@ struct source_line { | |||
93 | struct source_line *next; | 96 | struct source_line *next; |
94 | }; | 97 | }; |
95 | 98 | ||
96 | static char *sym_filter = NULL; | 99 | static const char *sym_filter = NULL; |
97 | struct sym_entry *sym_filter_entry = NULL; | 100 | struct sym_entry *sym_filter_entry = NULL; |
98 | struct sym_entry *sym_filter_entry_sched = NULL; | 101 | struct sym_entry *sym_filter_entry_sched = NULL; |
99 | static int sym_pcnt_filter = 5; | 102 | static int sym_pcnt_filter = 5; |
@@ -133,7 +136,7 @@ static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | |||
133 | return ((void *)self) + symbol_conf.priv_size; | 136 | return ((void *)self) + symbol_conf.priv_size; |
134 | } | 137 | } |
135 | 138 | ||
136 | static void get_term_dimensions(struct winsize *ws) | 139 | void get_term_dimensions(struct winsize *ws) |
137 | { | 140 | { |
138 | char *s = getenv("LINES"); | 141 | char *s = getenv("LINES"); |
139 | 142 | ||
@@ -169,7 +172,7 @@ static void sig_winch_handler(int sig __used) | |||
169 | update_print_entries(&winsize); | 172 | update_print_entries(&winsize); |
170 | } | 173 | } |
171 | 174 | ||
172 | static void parse_source(struct sym_entry *syme) | 175 | static int parse_source(struct sym_entry *syme) |
173 | { | 176 | { |
174 | struct symbol *sym; | 177 | struct symbol *sym; |
175 | struct sym_entry_source *source; | 178 | struct sym_entry_source *source; |
@@ -180,12 +183,21 @@ static void parse_source(struct sym_entry *syme) | |||
180 | u64 len; | 183 | u64 len; |
181 | 184 | ||
182 | if (!syme) | 185 | if (!syme) |
183 | return; | 186 | return -1; |
187 | |||
188 | sym = sym_entry__symbol(syme); | ||
189 | map = syme->map; | ||
190 | |||
191 | /* | ||
192 | * We can't annotate with just /proc/kallsyms | ||
193 | */ | ||
194 | if (map->dso->origin == DSO__ORIG_KERNEL) | ||
195 | return -1; | ||
184 | 196 | ||
185 | if (syme->src == NULL) { | 197 | if (syme->src == NULL) { |
186 | syme->src = zalloc(sizeof(*source)); | 198 | syme->src = zalloc(sizeof(*source)); |
187 | if (syme->src == NULL) | 199 | if (syme->src == NULL) |
188 | return; | 200 | return -1; |
189 | pthread_mutex_init(&syme->src->lock, NULL); | 201 | pthread_mutex_init(&syme->src->lock, NULL); |
190 | } | 202 | } |
191 | 203 | ||
@@ -195,9 +207,6 @@ static void parse_source(struct sym_entry *syme) | |||
195 | pthread_mutex_lock(&source->lock); | 207 | pthread_mutex_lock(&source->lock); |
196 | goto out_assign; | 208 | goto out_assign; |
197 | } | 209 | } |
198 | |||
199 | sym = sym_entry__symbol(syme); | ||
200 | map = syme->map; | ||
201 | path = map->dso->long_name; | 210 | path = map->dso->long_name; |
202 | 211 | ||
203 | len = sym->end - sym->start; | 212 | len = sym->end - sym->start; |
@@ -209,7 +218,7 @@ static void parse_source(struct sym_entry *syme) | |||
209 | 218 | ||
210 | file = popen(command, "r"); | 219 | file = popen(command, "r"); |
211 | if (!file) | 220 | if (!file) |
212 | return; | 221 | return -1; |
213 | 222 | ||
214 | pthread_mutex_lock(&source->lock); | 223 | pthread_mutex_lock(&source->lock); |
215 | source->lines_tail = &source->lines; | 224 | source->lines_tail = &source->lines; |
@@ -245,6 +254,7 @@ static void parse_source(struct sym_entry *syme) | |||
245 | out_assign: | 254 | out_assign: |
246 | sym_filter_entry = syme; | 255 | sym_filter_entry = syme; |
247 | pthread_mutex_unlock(&source->lock); | 256 | pthread_mutex_unlock(&source->lock); |
257 | return 0; | ||
248 | } | 258 | } |
249 | 259 | ||
250 | static void __zero_source_counters(struct sym_entry *syme) | 260 | static void __zero_source_counters(struct sym_entry *syme) |
@@ -410,7 +420,9 @@ static double sym_weight(const struct sym_entry *sym) | |||
410 | } | 420 | } |
411 | 421 | ||
412 | static long samples; | 422 | static long samples; |
413 | static long userspace_samples; | 423 | static long kernel_samples, us_samples; |
424 | static long exact_samples; | ||
425 | static long guest_us_samples, guest_kernel_samples; | ||
414 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 426 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
415 | 427 | ||
416 | static void __list_insert_active_sym(struct sym_entry *syme) | 428 | static void __list_insert_active_sym(struct sym_entry *syme) |
@@ -450,7 +462,11 @@ static void print_sym_table(void) | |||
450 | int printed = 0, j; | 462 | int printed = 0, j; |
451 | int counter, snap = !display_weighted ? sym_counter : 0; | 463 | int counter, snap = !display_weighted ? sym_counter : 0; |
452 | float samples_per_sec = samples/delay_secs; | 464 | float samples_per_sec = samples/delay_secs; |
453 | float ksamples_per_sec = (samples-userspace_samples)/delay_secs; | 465 | float ksamples_per_sec = kernel_samples/delay_secs; |
466 | float us_samples_per_sec = (us_samples)/delay_secs; | ||
467 | float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs; | ||
468 | float guest_us_samples_per_sec = (guest_us_samples)/delay_secs; | ||
469 | float esamples_percent = (100.0*exact_samples)/samples; | ||
454 | float sum_ksamples = 0.0; | 470 | float sum_ksamples = 0.0; |
455 | struct sym_entry *syme, *n; | 471 | struct sym_entry *syme, *n; |
456 | struct rb_root tmp = RB_ROOT; | 472 | struct rb_root tmp = RB_ROOT; |
@@ -458,7 +474,8 @@ static void print_sym_table(void) | |||
458 | int sym_width = 0, dso_width = 0, dso_short_width = 0; | 474 | int sym_width = 0, dso_width = 0, dso_short_width = 0; |
459 | const int win_width = winsize.ws_col - 1; | 475 | const int win_width = winsize.ws_col - 1; |
460 | 476 | ||
461 | samples = userspace_samples = 0; | 477 | samples = us_samples = kernel_samples = exact_samples = 0; |
478 | guest_kernel_samples = guest_us_samples = 0; | ||
462 | 479 | ||
463 | /* Sort the active symbols */ | 480 | /* Sort the active symbols */ |
464 | pthread_mutex_lock(&active_symbols_lock); | 481 | pthread_mutex_lock(&active_symbols_lock); |
@@ -489,9 +506,30 @@ static void print_sym_table(void) | |||
489 | puts(CONSOLE_CLEAR); | 506 | puts(CONSOLE_CLEAR); |
490 | 507 | ||
491 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 508 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
492 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", | 509 | if (!perf_guest) { |
493 | samples_per_sec, | 510 | printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%" |
494 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); | 511 | " exact: %4.1f%% [", |
512 | samples_per_sec, | ||
513 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
514 | samples_per_sec)), | ||
515 | esamples_percent); | ||
516 | } else { | ||
517 | printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" | ||
518 | " guest kernel:%4.1f%% guest us:%4.1f%%" | ||
519 | " exact: %4.1f%% [", | ||
520 | samples_per_sec, | ||
521 | 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) / | ||
522 | samples_per_sec)), | ||
523 | 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) / | ||
524 | samples_per_sec)), | ||
525 | 100.0 - (100.0 * ((samples_per_sec - | ||
526 | guest_kernel_samples_per_sec) / | ||
527 | samples_per_sec)), | ||
528 | 100.0 - (100.0 * ((samples_per_sec - | ||
529 | guest_us_samples_per_sec) / | ||
530 | samples_per_sec)), | ||
531 | esamples_percent); | ||
532 | } | ||
495 | 533 | ||
496 | if (nr_counters == 1 || !display_weighted) { | 534 | if (nr_counters == 1 || !display_weighted) { |
497 | printf("%Ld", (u64)attrs[0].sample_period); | 535 | printf("%Ld", (u64)attrs[0].sample_period); |
@@ -514,13 +552,15 @@ static void print_sym_table(void) | |||
514 | 552 | ||
515 | if (target_pid != -1) | 553 | if (target_pid != -1) |
516 | printf(" (target_pid: %d", target_pid); | 554 | printf(" (target_pid: %d", target_pid); |
555 | else if (target_tid != -1) | ||
556 | printf(" (target_tid: %d", target_tid); | ||
517 | else | 557 | else |
518 | printf(" (all"); | 558 | printf(" (all"); |
519 | 559 | ||
520 | if (profile_cpu != -1) | 560 | if (profile_cpu != -1) |
521 | printf(", cpu: %d)\n", profile_cpu); | 561 | printf(", cpu: %d)\n", profile_cpu); |
522 | else { | 562 | else { |
523 | if (target_pid != -1) | 563 | if (target_tid != -1) |
524 | printf(")\n"); | 564 | printf(")\n"); |
525 | else | 565 | else |
526 | printf(", %d CPUs)\n", nr_cpus); | 566 | printf(", %d CPUs)\n", nr_cpus); |
@@ -582,7 +622,6 @@ static void print_sym_table(void) | |||
582 | 622 | ||
583 | syme = rb_entry(nd, struct sym_entry, rb_node); | 623 | syme = rb_entry(nd, struct sym_entry, rb_node); |
584 | sym = sym_entry__symbol(syme); | 624 | sym = sym_entry__symbol(syme); |
585 | |||
586 | if (++printed > print_entries || (int)syme->snap_count < count_filter) | 625 | if (++printed > print_entries || (int)syme->snap_count < count_filter) |
587 | continue; | 626 | continue; |
588 | 627 | ||
@@ -746,7 +785,7 @@ static int key_mapped(int c) | |||
746 | return 0; | 785 | return 0; |
747 | } | 786 | } |
748 | 787 | ||
749 | static void handle_keypress(int c) | 788 | static void handle_keypress(struct perf_session *session, int c) |
750 | { | 789 | { |
751 | if (!key_mapped(c)) { | 790 | if (!key_mapped(c)) { |
752 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 791 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
@@ -815,7 +854,7 @@ static void handle_keypress(int c) | |||
815 | case 'Q': | 854 | case 'Q': |
816 | printf("exiting.\n"); | 855 | printf("exiting.\n"); |
817 | if (dump_symtab) | 856 | if (dump_symtab) |
818 | dsos__fprintf(stderr); | 857 | perf_session__fprintf_dsos(session, stderr); |
819 | exit(0); | 858 | exit(0); |
820 | case 's': | 859 | case 's': |
821 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); | 860 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); |
@@ -839,7 +878,7 @@ static void handle_keypress(int c) | |||
839 | display_weighted = ~display_weighted; | 878 | display_weighted = ~display_weighted; |
840 | break; | 879 | break; |
841 | case 'z': | 880 | case 'z': |
842 | zero = ~zero; | 881 | zero = !zero; |
843 | break; | 882 | break; |
844 | default: | 883 | default: |
845 | break; | 884 | break; |
@@ -851,6 +890,7 @@ static void *display_thread(void *arg __used) | |||
851 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 890 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
852 | struct termios tc, save; | 891 | struct termios tc, save; |
853 | int delay_msecs, c; | 892 | int delay_msecs, c; |
893 | struct perf_session *session = (struct perf_session *) arg; | ||
854 | 894 | ||
855 | tcgetattr(0, &save); | 895 | tcgetattr(0, &save); |
856 | tc = save; | 896 | tc = save; |
@@ -871,7 +911,7 @@ repeat: | |||
871 | c = getc(stdin); | 911 | c = getc(stdin); |
872 | tcsetattr(0, TCSAFLUSH, &save); | 912 | tcsetattr(0, TCSAFLUSH, &save); |
873 | 913 | ||
874 | handle_keypress(c); | 914 | handle_keypress(session, c); |
875 | goto repeat; | 915 | goto repeat; |
876 | 916 | ||
877 | return NULL; | 917 | return NULL; |
@@ -942,24 +982,48 @@ static void event__process_sample(const event_t *self, | |||
942 | u64 ip = self->ip.ip; | 982 | u64 ip = self->ip.ip; |
943 | struct sym_entry *syme; | 983 | struct sym_entry *syme; |
944 | struct addr_location al; | 984 | struct addr_location al; |
985 | struct machine *machine; | ||
945 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 986 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
946 | 987 | ||
947 | ++samples; | 988 | ++samples; |
948 | 989 | ||
949 | switch (origin) { | 990 | switch (origin) { |
950 | case PERF_RECORD_MISC_USER: | 991 | case PERF_RECORD_MISC_USER: |
951 | ++userspace_samples; | 992 | ++us_samples; |
952 | if (hide_user_symbols) | 993 | if (hide_user_symbols) |
953 | return; | 994 | return; |
995 | machine = perf_session__find_host_machine(session); | ||
954 | break; | 996 | break; |
955 | case PERF_RECORD_MISC_KERNEL: | 997 | case PERF_RECORD_MISC_KERNEL: |
998 | ++kernel_samples; | ||
956 | if (hide_kernel_symbols) | 999 | if (hide_kernel_symbols) |
957 | return; | 1000 | return; |
1001 | machine = perf_session__find_host_machine(session); | ||
1002 | break; | ||
1003 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
1004 | ++guest_kernel_samples; | ||
1005 | machine = perf_session__find_machine(session, self->ip.pid); | ||
958 | break; | 1006 | break; |
1007 | case PERF_RECORD_MISC_GUEST_USER: | ||
1008 | ++guest_us_samples; | ||
1009 | /* | ||
1010 | * TODO: we don't process guest user from host side | ||
1011 | * except simple counting. | ||
1012 | */ | ||
1013 | return; | ||
959 | default: | 1014 | default: |
960 | return; | 1015 | return; |
961 | } | 1016 | } |
962 | 1017 | ||
1018 | if (!machine && perf_guest) { | ||
1019 | pr_err("Can't find guest [%d]'s kernel information\n", | ||
1020 | self->ip.pid); | ||
1021 | return; | ||
1022 | } | ||
1023 | |||
1024 | if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) | ||
1025 | exact_samples++; | ||
1026 | |||
963 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || | 1027 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || |
964 | al.filtered) | 1028 | al.filtered) |
965 | return; | 1029 | return; |
@@ -976,7 +1040,7 @@ static void event__process_sample(const event_t *self, | |||
976 | * --hide-kernel-symbols, even if the user specifies an | 1040 | * --hide-kernel-symbols, even if the user specifies an |
977 | * invalid --vmlinux ;-) | 1041 | * invalid --vmlinux ;-) |
978 | */ | 1042 | */ |
979 | if (al.map == session->vmlinux_maps[MAP__FUNCTION] && | 1043 | if (al.map == machine->vmlinux_maps[MAP__FUNCTION] && |
980 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | 1044 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { |
981 | pr_err("The %s file can't be used\n", | 1045 | pr_err("The %s file can't be used\n", |
982 | symbol_conf.vmlinux_name); | 1046 | symbol_conf.vmlinux_name); |
@@ -990,7 +1054,17 @@ static void event__process_sample(const event_t *self, | |||
990 | if (sym_filter_entry_sched) { | 1054 | if (sym_filter_entry_sched) { |
991 | sym_filter_entry = sym_filter_entry_sched; | 1055 | sym_filter_entry = sym_filter_entry_sched; |
992 | sym_filter_entry_sched = NULL; | 1056 | sym_filter_entry_sched = NULL; |
993 | parse_source(sym_filter_entry); | 1057 | if (parse_source(sym_filter_entry) < 0) { |
1058 | struct symbol *sym = sym_entry__symbol(sym_filter_entry); | ||
1059 | |||
1060 | pr_err("Can't annotate %s", sym->name); | ||
1061 | if (sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) { | ||
1062 | pr_err(": No vmlinux file was found in the path:\n"); | ||
1063 | vmlinux_path__fprintf(stderr); | ||
1064 | } else | ||
1065 | pr_err(".\n"); | ||
1066 | exit(1); | ||
1067 | } | ||
994 | } | 1068 | } |
995 | 1069 | ||
996 | syme = symbol__priv(al.sym); | 1070 | syme = symbol__priv(al.sym); |
@@ -1106,16 +1180,21 @@ static void perf_session__mmap_read_counter(struct perf_session *self, | |||
1106 | md->prev = old; | 1180 | md->prev = old; |
1107 | } | 1181 | } |
1108 | 1182 | ||
1109 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; | 1183 | static struct pollfd *event_array; |
1110 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | 1184 | static struct mmap_data *mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; |
1111 | 1185 | ||
1112 | static void perf_session__mmap_read(struct perf_session *self) | 1186 | static void perf_session__mmap_read(struct perf_session *self) |
1113 | { | 1187 | { |
1114 | int i, counter; | 1188 | int i, counter, thread_index; |
1115 | 1189 | ||
1116 | for (i = 0; i < nr_cpus; i++) { | 1190 | for (i = 0; i < nr_cpus; i++) { |
1117 | for (counter = 0; counter < nr_counters; counter++) | 1191 | for (counter = 0; counter < nr_counters; counter++) |
1118 | perf_session__mmap_read_counter(self, &mmap_array[i][counter]); | 1192 | for (thread_index = 0; |
1193 | thread_index < thread_num; | ||
1194 | thread_index++) { | ||
1195 | perf_session__mmap_read_counter(self, | ||
1196 | &mmap_array[i][counter][thread_index]); | ||
1197 | } | ||
1119 | } | 1198 | } |
1120 | } | 1199 | } |
1121 | 1200 | ||
@@ -1126,9 +1205,10 @@ static void start_counter(int i, int counter) | |||
1126 | { | 1205 | { |
1127 | struct perf_event_attr *attr; | 1206 | struct perf_event_attr *attr; |
1128 | int cpu; | 1207 | int cpu; |
1208 | int thread_index; | ||
1129 | 1209 | ||
1130 | cpu = profile_cpu; | 1210 | cpu = profile_cpu; |
1131 | if (target_pid == -1 && profile_cpu == -1) | 1211 | if (target_tid == -1 && profile_cpu == -1) |
1132 | cpu = cpumap[i]; | 1212 | cpu = cpumap[i]; |
1133 | 1213 | ||
1134 | attr = attrs + counter; | 1214 | attr = attrs + counter; |
@@ -1144,55 +1224,58 @@ static void start_counter(int i, int counter) | |||
1144 | attr->inherit = (cpu < 0) && inherit; | 1224 | attr->inherit = (cpu < 0) && inherit; |
1145 | attr->mmap = 1; | 1225 | attr->mmap = 1; |
1146 | 1226 | ||
1227 | for (thread_index = 0; thread_index < thread_num; thread_index++) { | ||
1147 | try_again: | 1228 | try_again: |
1148 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); | 1229 | fd[i][counter][thread_index] = sys_perf_event_open(attr, |
1149 | 1230 | all_tids[thread_index], cpu, group_fd, 0); | |
1150 | if (fd[i][counter] < 0) { | 1231 | |
1151 | int err = errno; | 1232 | if (fd[i][counter][thread_index] < 0) { |
1233 | int err = errno; | ||
1234 | |||
1235 | if (err == EPERM || err == EACCES) | ||
1236 | die("No permission - are you root?\n"); | ||
1237 | /* | ||
1238 | * If it's cycles then fall back to hrtimer | ||
1239 | * based cpu-clock-tick sw counter, which | ||
1240 | * is always available even if no PMU support: | ||
1241 | */ | ||
1242 | if (attr->type == PERF_TYPE_HARDWARE | ||
1243 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | ||
1244 | |||
1245 | if (verbose) | ||
1246 | warning(" ... trying to fall back to cpu-clock-ticks\n"); | ||
1247 | |||
1248 | attr->type = PERF_TYPE_SOFTWARE; | ||
1249 | attr->config = PERF_COUNT_SW_CPU_CLOCK; | ||
1250 | goto try_again; | ||
1251 | } | ||
1252 | printf("\n"); | ||
1253 | error("perfcounter syscall returned with %d (%s)\n", | ||
1254 | fd[i][counter][thread_index], strerror(err)); | ||
1255 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | ||
1256 | exit(-1); | ||
1257 | } | ||
1258 | assert(fd[i][counter][thread_index] >= 0); | ||
1259 | fcntl(fd[i][counter][thread_index], F_SETFL, O_NONBLOCK); | ||
1152 | 1260 | ||
1153 | if (err == EPERM || err == EACCES) | ||
1154 | die("No permission - are you root?\n"); | ||
1155 | /* | 1261 | /* |
1156 | * If it's cycles then fall back to hrtimer | 1262 | * First counter acts as the group leader: |
1157 | * based cpu-clock-tick sw counter, which | ||
1158 | * is always available even if no PMU support: | ||
1159 | */ | 1263 | */ |
1160 | if (attr->type == PERF_TYPE_HARDWARE | 1264 | if (group && group_fd == -1) |
1161 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | 1265 | group_fd = fd[i][counter][thread_index]; |
1162 | 1266 | ||
1163 | if (verbose) | 1267 | event_array[nr_poll].fd = fd[i][counter][thread_index]; |
1164 | warning(" ... trying to fall back to cpu-clock-ticks\n"); | 1268 | event_array[nr_poll].events = POLLIN; |
1165 | 1269 | nr_poll++; | |
1166 | attr->type = PERF_TYPE_SOFTWARE; | 1270 | |
1167 | attr->config = PERF_COUNT_SW_CPU_CLOCK; | 1271 | mmap_array[i][counter][thread_index].counter = counter; |
1168 | goto try_again; | 1272 | mmap_array[i][counter][thread_index].prev = 0; |
1169 | } | 1273 | mmap_array[i][counter][thread_index].mask = mmap_pages*page_size - 1; |
1170 | printf("\n"); | 1274 | mmap_array[i][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size, |
1171 | error("perfcounter syscall returned with %d (%s)\n", | 1275 | PROT_READ, MAP_SHARED, fd[i][counter][thread_index], 0); |
1172 | fd[i][counter], strerror(err)); | 1276 | if (mmap_array[i][counter][thread_index].base == MAP_FAILED) |
1173 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 1277 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
1174 | exit(-1); | ||
1175 | } | 1278 | } |
1176 | assert(fd[i][counter] >= 0); | ||
1177 | fcntl(fd[i][counter], F_SETFL, O_NONBLOCK); | ||
1178 | |||
1179 | /* | ||
1180 | * First counter acts as the group leader: | ||
1181 | */ | ||
1182 | if (group && group_fd == -1) | ||
1183 | group_fd = fd[i][counter]; | ||
1184 | |||
1185 | event_array[nr_poll].fd = fd[i][counter]; | ||
1186 | event_array[nr_poll].events = POLLIN; | ||
1187 | nr_poll++; | ||
1188 | |||
1189 | mmap_array[i][counter].counter = counter; | ||
1190 | mmap_array[i][counter].prev = 0; | ||
1191 | mmap_array[i][counter].mask = mmap_pages*page_size - 1; | ||
1192 | mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
1193 | PROT_READ, MAP_SHARED, fd[i][counter], 0); | ||
1194 | if (mmap_array[i][counter].base == MAP_FAILED) | ||
1195 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
1196 | } | 1279 | } |
1197 | 1280 | ||
1198 | static int __cmd_top(void) | 1281 | static int __cmd_top(void) |
@@ -1204,12 +1287,12 @@ static int __cmd_top(void) | |||
1204 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 1287 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
1205 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 1288 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
1206 | */ | 1289 | */ |
1207 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false); | 1290 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false); |
1208 | if (session == NULL) | 1291 | if (session == NULL) |
1209 | return -ENOMEM; | 1292 | return -ENOMEM; |
1210 | 1293 | ||
1211 | if (target_pid != -1) | 1294 | if (target_tid != -1) |
1212 | event__synthesize_thread(target_pid, event__process, session); | 1295 | event__synthesize_thread(target_tid, event__process, session); |
1213 | else | 1296 | else |
1214 | event__synthesize_threads(event__process, session); | 1297 | event__synthesize_threads(event__process, session); |
1215 | 1298 | ||
@@ -1220,11 +1303,11 @@ static int __cmd_top(void) | |||
1220 | } | 1303 | } |
1221 | 1304 | ||
1222 | /* Wait for a minimal set of events before starting the snapshot */ | 1305 | /* Wait for a minimal set of events before starting the snapshot */ |
1223 | poll(event_array, nr_poll, 100); | 1306 | poll(&event_array[0], nr_poll, 100); |
1224 | 1307 | ||
1225 | perf_session__mmap_read(session); | 1308 | perf_session__mmap_read(session); |
1226 | 1309 | ||
1227 | if (pthread_create(&thread, NULL, display_thread, NULL)) { | 1310 | if (pthread_create(&thread, NULL, display_thread, session)) { |
1228 | printf("Could not create display thread.\n"); | 1311 | printf("Could not create display thread.\n"); |
1229 | exit(-1); | 1312 | exit(-1); |
1230 | } | 1313 | } |
@@ -1263,7 +1346,9 @@ static const struct option options[] = { | |||
1263 | OPT_INTEGER('c', "count", &default_interval, | 1346 | OPT_INTEGER('c', "count", &default_interval, |
1264 | "event period to sample"), | 1347 | "event period to sample"), |
1265 | OPT_INTEGER('p', "pid", &target_pid, | 1348 | OPT_INTEGER('p', "pid", &target_pid, |
1266 | "profile events on existing pid"), | 1349 | "profile events on existing process id"), |
1350 | OPT_INTEGER('t', "tid", &target_tid, | ||
1351 | "profile events on existing thread id"), | ||
1267 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1352 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1268 | "system-wide collection from all CPUs"), | 1353 | "system-wide collection from all CPUs"), |
1269 | OPT_INTEGER('C', "CPU", &profile_cpu, | 1354 | OPT_INTEGER('C', "CPU", &profile_cpu, |
@@ -1272,8 +1357,7 @@ static const struct option options[] = { | |||
1272 | "file", "vmlinux pathname"), | 1357 | "file", "vmlinux pathname"), |
1273 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | 1358 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, |
1274 | "hide kernel symbols"), | 1359 | "hide kernel symbols"), |
1275 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | 1360 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), |
1276 | "number of mmap data pages"), | ||
1277 | OPT_INTEGER('r', "realtime", &realtime_prio, | 1361 | OPT_INTEGER('r', "realtime", &realtime_prio, |
1278 | "collect data with this RT SCHED_FIFO priority"), | 1362 | "collect data with this RT SCHED_FIFO priority"), |
1279 | OPT_INTEGER('d', "delay", &delay_secs, | 1363 | OPT_INTEGER('d', "delay", &delay_secs, |
@@ -1296,7 +1380,7 @@ static const struct option options[] = { | |||
1296 | "display this many functions"), | 1380 | "display this many functions"), |
1297 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, | 1381 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, |
1298 | "hide user symbols"), | 1382 | "hide user symbols"), |
1299 | OPT_BOOLEAN('v', "verbose", &verbose, | 1383 | OPT_INCR('v', "verbose", &verbose, |
1300 | "be more verbose (show counter open errors, etc)"), | 1384 | "be more verbose (show counter open errors, etc)"), |
1301 | OPT_END() | 1385 | OPT_END() |
1302 | }; | 1386 | }; |
@@ -1304,6 +1388,7 @@ static const struct option options[] = { | |||
1304 | int cmd_top(int argc, const char **argv, const char *prefix __used) | 1388 | int cmd_top(int argc, const char **argv, const char *prefix __used) |
1305 | { | 1389 | { |
1306 | int counter; | 1390 | int counter; |
1391 | int i,j; | ||
1307 | 1392 | ||
1308 | page_size = sysconf(_SC_PAGE_SIZE); | 1393 | page_size = sysconf(_SC_PAGE_SIZE); |
1309 | 1394 | ||
@@ -1311,8 +1396,39 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1311 | if (argc) | 1396 | if (argc) |
1312 | usage_with_options(top_usage, options); | 1397 | usage_with_options(top_usage, options); |
1313 | 1398 | ||
1399 | if (target_pid != -1) { | ||
1400 | target_tid = target_pid; | ||
1401 | thread_num = find_all_tid(target_pid, &all_tids); | ||
1402 | if (thread_num <= 0) { | ||
1403 | fprintf(stderr, "Can't find all threads of pid %d\n", | ||
1404 | target_pid); | ||
1405 | usage_with_options(top_usage, options); | ||
1406 | } | ||
1407 | } else { | ||
1408 | all_tids=malloc(sizeof(pid_t)); | ||
1409 | if (!all_tids) | ||
1410 | return -ENOMEM; | ||
1411 | |||
1412 | all_tids[0] = target_tid; | ||
1413 | thread_num = 1; | ||
1414 | } | ||
1415 | |||
1416 | for (i = 0; i < MAX_NR_CPUS; i++) { | ||
1417 | for (j = 0; j < MAX_COUNTERS; j++) { | ||
1418 | fd[i][j] = malloc(sizeof(int)*thread_num); | ||
1419 | mmap_array[i][j] = zalloc( | ||
1420 | sizeof(struct mmap_data)*thread_num); | ||
1421 | if (!fd[i][j] || !mmap_array[i][j]) | ||
1422 | return -ENOMEM; | ||
1423 | } | ||
1424 | } | ||
1425 | event_array = malloc( | ||
1426 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); | ||
1427 | if (!event_array) | ||
1428 | return -ENOMEM; | ||
1429 | |||
1314 | /* CPU and PID are mutually exclusive */ | 1430 | /* CPU and PID are mutually exclusive */ |
1315 | if (target_pid != -1 && profile_cpu != -1) { | 1431 | if (target_tid > 0 && profile_cpu != -1) { |
1316 | printf("WARNING: PID switch overriding CPU\n"); | 1432 | printf("WARNING: PID switch overriding CPU\n"); |
1317 | sleep(1); | 1433 | sleep(1); |
1318 | profile_cpu = -1; | 1434 | profile_cpu = -1; |
@@ -1353,7 +1469,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1353 | attrs[counter].sample_period = default_interval; | 1469 | attrs[counter].sample_period = default_interval; |
1354 | } | 1470 | } |
1355 | 1471 | ||
1356 | if (target_pid != -1 || profile_cpu != -1) | 1472 | if (target_tid != -1 || profile_cpu != -1) |
1357 | nr_cpus = 1; | 1473 | nr_cpus = 1; |
1358 | else | 1474 | else |
1359 | nr_cpus = read_cpu_map(); | 1475 | nr_cpus = read_cpu_map(); |