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(); |
