diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 109 |
1 files changed, 72 insertions, 37 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 4b91d8cf00ec..31f2e597800c 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -94,6 +94,7 @@ struct source_line { | |||
94 | 94 | ||
95 | static char *sym_filter = NULL; | 95 | static char *sym_filter = NULL; |
96 | struct sym_entry *sym_filter_entry = NULL; | 96 | struct sym_entry *sym_filter_entry = NULL; |
97 | struct sym_entry *sym_filter_entry_sched = NULL; | ||
97 | static int sym_pcnt_filter = 5; | 98 | static int sym_pcnt_filter = 5; |
98 | static int sym_counter = 0; | 99 | static int sym_counter = 0; |
99 | static int display_weighted = -1; | 100 | static int display_weighted = -1; |
@@ -201,10 +202,9 @@ static void parse_source(struct sym_entry *syme) | |||
201 | len = sym->end - sym->start; | 202 | len = sym->end - sym->start; |
202 | 203 | ||
203 | sprintf(command, | 204 | sprintf(command, |
204 | "objdump --start-address=0x%016Lx " | 205 | "objdump --start-address=%#0*Lx --stop-address=%#0*Lx -dS %s", |
205 | "--stop-address=0x%016Lx -dS %s", | 206 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), |
206 | map->unmap_ip(map, sym->start), | 207 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); |
207 | map->unmap_ip(map, sym->end), path); | ||
208 | 208 | ||
209 | file = popen(command, "r"); | 209 | file = popen(command, "r"); |
210 | if (!file) | 210 | if (!file) |
@@ -215,7 +215,7 @@ static void parse_source(struct sym_entry *syme) | |||
215 | while (!feof(file)) { | 215 | while (!feof(file)) { |
216 | struct source_line *src; | 216 | struct source_line *src; |
217 | size_t dummy = 0; | 217 | size_t dummy = 0; |
218 | char *c; | 218 | char *c, *sep; |
219 | 219 | ||
220 | src = malloc(sizeof(struct source_line)); | 220 | src = malloc(sizeof(struct source_line)); |
221 | assert(src != NULL); | 221 | assert(src != NULL); |
@@ -234,14 +234,11 @@ static void parse_source(struct sym_entry *syme) | |||
234 | *source->lines_tail = src; | 234 | *source->lines_tail = src; |
235 | source->lines_tail = &src->next; | 235 | source->lines_tail = &src->next; |
236 | 236 | ||
237 | if (strlen(src->line)>8 && src->line[8] == ':') { | 237 | src->eip = strtoull(src->line, &sep, 16); |
238 | src->eip = strtoull(src->line, NULL, 16); | 238 | if (*sep == ':') |
239 | src->eip = map->unmap_ip(map, src->eip); | 239 | src->eip = map__objdump_2ip(map, src->eip); |
240 | } | 240 | else /* this line has no ip info (e.g. source line) */ |
241 | if (strlen(src->line)>8 && src->line[16] == ':') { | 241 | src->eip = 0; |
242 | src->eip = strtoull(src->line, NULL, 16); | ||
243 | src->eip = map->unmap_ip(map, src->eip); | ||
244 | } | ||
245 | } | 242 | } |
246 | pclose(file); | 243 | pclose(file); |
247 | out_assign: | 244 | out_assign: |
@@ -276,6 +273,9 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
276 | goto out_unlock; | 273 | goto out_unlock; |
277 | 274 | ||
278 | for (line = syme->src->lines; line; line = line->next) { | 275 | for (line = syme->src->lines; line; line = line->next) { |
276 | /* skip lines without IP info */ | ||
277 | if (line->eip == 0) | ||
278 | continue; | ||
279 | if (line->eip == ip) { | 279 | if (line->eip == ip) { |
280 | line->count[counter]++; | 280 | line->count[counter]++; |
281 | break; | 281 | break; |
@@ -287,17 +287,20 @@ out_unlock: | |||
287 | pthread_mutex_unlock(&syme->src->lock); | 287 | pthread_mutex_unlock(&syme->src->lock); |
288 | } | 288 | } |
289 | 289 | ||
290 | #define PATTERN_LEN (BITS_PER_LONG / 4 + 2) | ||
291 | |||
290 | static void lookup_sym_source(struct sym_entry *syme) | 292 | static void lookup_sym_source(struct sym_entry *syme) |
291 | { | 293 | { |
292 | struct symbol *symbol = sym_entry__symbol(syme); | 294 | struct symbol *symbol = sym_entry__symbol(syme); |
293 | struct source_line *line; | 295 | struct source_line *line; |
294 | char pattern[PATH_MAX]; | 296 | char pattern[PATTERN_LEN + 1]; |
295 | 297 | ||
296 | sprintf(pattern, "<%s>:", symbol->name); | 298 | sprintf(pattern, "%0*Lx <", BITS_PER_LONG / 4, |
299 | map__rip_2objdump(syme->map, symbol->start)); | ||
297 | 300 | ||
298 | pthread_mutex_lock(&syme->src->lock); | 301 | pthread_mutex_lock(&syme->src->lock); |
299 | for (line = syme->src->lines; line; line = line->next) { | 302 | for (line = syme->src->lines; line; line = line->next) { |
300 | if (strstr(line->line, pattern)) { | 303 | if (memcmp(line->line, pattern, PATTERN_LEN) == 0) { |
301 | syme->src->source = line; | 304 | syme->src->source = line; |
302 | break; | 305 | break; |
303 | } | 306 | } |
@@ -667,7 +670,7 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
667 | } | 670 | } |
668 | 671 | ||
669 | if (!found) { | 672 | if (!found) { |
670 | fprintf(stderr, "Sorry, %s is not active.\n", sym_filter); | 673 | fprintf(stderr, "Sorry, %s is not active.\n", buf); |
671 | sleep(1); | 674 | sleep(1); |
672 | return; | 675 | return; |
673 | } else | 676 | } else |
@@ -695,11 +698,9 @@ static void print_mapped_keys(void) | |||
695 | 698 | ||
696 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 699 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); |
697 | 700 | ||
698 | if (symbol_conf.vmlinux_name) { | 701 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); |
699 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); | 702 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
700 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 703 | fprintf(stdout, "\t[S] stop annotation.\n"); |
701 | fprintf(stdout, "\t[S] stop annotation.\n"); | ||
702 | } | ||
703 | 704 | ||
704 | if (nr_counters > 1) | 705 | if (nr_counters > 1) |
705 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 706 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
@@ -725,14 +726,13 @@ static int key_mapped(int c) | |||
725 | case 'Q': | 726 | case 'Q': |
726 | case 'K': | 727 | case 'K': |
727 | case 'U': | 728 | case 'U': |
729 | case 'F': | ||
730 | case 's': | ||
731 | case 'S': | ||
728 | return 1; | 732 | return 1; |
729 | case 'E': | 733 | case 'E': |
730 | case 'w': | 734 | case 'w': |
731 | return nr_counters > 1 ? 1 : 0; | 735 | return nr_counters > 1 ? 1 : 0; |
732 | case 'F': | ||
733 | case 's': | ||
734 | case 'S': | ||
735 | return symbol_conf.vmlinux_name ? 1 : 0; | ||
736 | default: | 736 | default: |
737 | break; | 737 | break; |
738 | } | 738 | } |
@@ -910,8 +910,12 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
910 | syme = symbol__priv(sym); | 910 | syme = symbol__priv(sym); |
911 | syme->map = map; | 911 | syme->map = map; |
912 | syme->src = NULL; | 912 | syme->src = NULL; |
913 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) | 913 | |
914 | sym_filter_entry = syme; | 914 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { |
915 | /* schedule initial sym_filter_entry setup */ | ||
916 | sym_filter_entry_sched = syme; | ||
917 | sym_filter = NULL; | ||
918 | } | ||
915 | 919 | ||
916 | for (i = 0; skip_symbols[i]; i++) { | 920 | for (i = 0; skip_symbols[i]; i++) { |
917 | if (!strcmp(skip_symbols[i], name)) { | 921 | if (!strcmp(skip_symbols[i], name)) { |
@@ -934,8 +938,11 @@ static void event__process_sample(const event_t *self, | |||
934 | struct addr_location al; | 938 | struct addr_location al; |
935 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 939 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
936 | 940 | ||
941 | ++samples; | ||
942 | |||
937 | switch (origin) { | 943 | switch (origin) { |
938 | case PERF_RECORD_MISC_USER: | 944 | case PERF_RECORD_MISC_USER: |
945 | ++userspace_samples; | ||
939 | if (hide_user_symbols) | 946 | if (hide_user_symbols) |
940 | return; | 947 | return; |
941 | break; | 948 | break; |
@@ -948,9 +955,38 @@ static void event__process_sample(const event_t *self, | |||
948 | } | 955 | } |
949 | 956 | ||
950 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || | 957 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || |
951 | al.sym == NULL || al.filtered) | 958 | al.filtered) |
952 | return; | 959 | return; |
953 | 960 | ||
961 | if (al.sym == NULL) { | ||
962 | /* | ||
963 | * As we do lazy loading of symtabs we only will know if the | ||
964 | * specified vmlinux file is invalid when we actually have a | ||
965 | * hit in kernel space and then try to load it. So if we get | ||
966 | * here and there are _no_ symbols in the DSO backing the | ||
967 | * kernel map, bail out. | ||
968 | * | ||
969 | * We may never get here, for instance, if we use -K/ | ||
970 | * --hide-kernel-symbols, even if the user specifies an | ||
971 | * invalid --vmlinux ;-) | ||
972 | */ | ||
973 | if (al.map == session->vmlinux_maps[MAP__FUNCTION] && | ||
974 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | ||
975 | pr_err("The %s file can't be used\n", | ||
976 | symbol_conf.vmlinux_name); | ||
977 | exit(1); | ||
978 | } | ||
979 | |||
980 | return; | ||
981 | } | ||
982 | |||
983 | /* let's see, whether we need to install initial sym_filter_entry */ | ||
984 | if (sym_filter_entry_sched) { | ||
985 | sym_filter_entry = sym_filter_entry_sched; | ||
986 | sym_filter_entry_sched = NULL; | ||
987 | parse_source(sym_filter_entry); | ||
988 | } | ||
989 | |||
954 | syme = symbol__priv(al.sym); | 990 | syme = symbol__priv(al.sym); |
955 | if (!syme->skip) { | 991 | if (!syme->skip) { |
956 | syme->count[counter]++; | 992 | syme->count[counter]++; |
@@ -960,9 +996,6 @@ static void event__process_sample(const event_t *self, | |||
960 | if (list_empty(&syme->node) || !syme->node.next) | 996 | if (list_empty(&syme->node) || !syme->node.next) |
961 | __list_insert_active_sym(syme); | 997 | __list_insert_active_sym(syme); |
962 | pthread_mutex_unlock(&active_symbols_lock); | 998 | pthread_mutex_unlock(&active_symbols_lock); |
963 | if (origin == PERF_RECORD_MISC_USER) | ||
964 | ++userspace_samples; | ||
965 | ++samples; | ||
966 | } | 999 | } |
967 | } | 1000 | } |
968 | 1001 | ||
@@ -975,6 +1008,10 @@ static int event__process(event_t *event, struct perf_session *session) | |||
975 | case PERF_RECORD_MMAP: | 1008 | case PERF_RECORD_MMAP: |
976 | event__process_mmap(event, session); | 1009 | event__process_mmap(event, session); |
977 | break; | 1010 | break; |
1011 | case PERF_RECORD_FORK: | ||
1012 | case PERF_RECORD_EXIT: | ||
1013 | event__process_task(event, session); | ||
1014 | break; | ||
978 | default: | 1015 | default: |
979 | break; | 1016 | break; |
980 | } | 1017 | } |
@@ -1244,7 +1281,7 @@ static const struct option options[] = { | |||
1244 | OPT_BOOLEAN('i', "inherit", &inherit, | 1281 | OPT_BOOLEAN('i', "inherit", &inherit, |
1245 | "child tasks inherit counters"), | 1282 | "child tasks inherit counters"), |
1246 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | 1283 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", |
1247 | "symbol to annotate - requires -k option"), | 1284 | "symbol to annotate"), |
1248 | OPT_BOOLEAN('z', "zero", &zero, | 1285 | OPT_BOOLEAN('z', "zero", &zero, |
1249 | "zero history across updates"), | 1286 | "zero history across updates"), |
1250 | OPT_INTEGER('F', "freq", &freq, | 1287 | OPT_INTEGER('F', "freq", &freq, |
@@ -1280,16 +1317,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1280 | 1317 | ||
1281 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | 1318 | symbol_conf.priv_size = (sizeof(struct sym_entry) + |
1282 | (nr_counters + 1) * sizeof(unsigned long)); | 1319 | (nr_counters + 1) * sizeof(unsigned long)); |
1283 | if (symbol_conf.vmlinux_name == NULL) | 1320 | |
1284 | symbol_conf.try_vmlinux_path = true; | 1321 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1285 | if (symbol__init() < 0) | 1322 | if (symbol__init() < 0) |
1286 | return -1; | 1323 | return -1; |
1287 | 1324 | ||
1288 | if (delay_secs < 1) | 1325 | if (delay_secs < 1) |
1289 | delay_secs = 1; | 1326 | delay_secs = 1; |
1290 | 1327 | ||
1291 | parse_source(sym_filter_entry); | ||
1292 | |||
1293 | /* | 1328 | /* |
1294 | * User specified count overrides default frequency. | 1329 | * User specified count overrides default frequency. |
1295 | */ | 1330 | */ |