diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-09-23 16:59:37 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-09-23 16:59:37 -0400 |
| commit | eab8bcb67a6f4e8d428b76c894a447229812c8ac (patch) | |
| tree | 22f2119b6cec46358f7a2d0bede5af6eb04ad316 | |
| parent | f35f3dc4850d482143233fb51c714a81ce24083a (diff) | |
| parent | 9e59e0995a57048c53773f1de99c6637ad12dd25 (diff) | |
Merge branch 'perf-tools-for-linus' of git://github.com/acmel/linux
* 'perf-tools-for-linus' of git://github.com/acmel/linux:
perf tools: Add support for disabling -Werror via WERROR=0
perf top: Fix userspace sample addr map offset
perf symbols: Fix issue with binaries using 16-bytes buildids (v2)
perf tool: Fix endianness handling of u32 data in samples
perf sort: Fix symbol sort output by separating unresolved samples by type
perf symbols: Synthesize anonymous mmap events
perf record: Create events initially disabled and enable after init
perf symbols: Add some heuristics for choosing the best duplicate symbol
perf symbols: Preserve symbol scope when parsing /proc/kallsyms
perf symbols: /proc/kallsyms does not sort module symbols
perf symbols: Fix ppc64 SEGV in dso__load_sym with debuginfo files
perf probe: Fix regression of variable finder
| -rw-r--r-- | tools/perf/Makefile | 9 | ||||
| -rw-r--r-- | tools/perf/builtin-record.c | 3 | ||||
| -rw-r--r-- | tools/perf/builtin-test.c | 2 | ||||
| -rw-r--r-- | tools/perf/builtin-top.c | 9 | ||||
| -rw-r--r-- | tools/perf/util/event.c | 5 | ||||
| -rw-r--r-- | tools/perf/util/event.h | 2 | ||||
| -rw-r--r-- | tools/perf/util/evlist.c | 13 | ||||
| -rw-r--r-- | tools/perf/util/evlist.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/evsel.c | 54 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.c | 2 | ||||
| -rw-r--r-- | tools/perf/util/session.h | 3 | ||||
| -rw-r--r-- | tools/perf/util/sort.c | 10 | ||||
| -rw-r--r-- | tools/perf/util/symbol.c | 153 |
13 files changed, 209 insertions, 57 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3b8f7b80376b..e9d5c271db69 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -30,6 +30,8 @@ endif | |||
| 30 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. | 30 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. |
| 31 | # | 31 | # |
| 32 | # Define NO_DWARF if you do not want debug-info analysis feature at all. | 32 | # Define NO_DWARF if you do not want debug-info analysis feature at all. |
| 33 | # | ||
| 34 | # Define WERROR=0 to disable treating any warnings as errors. | ||
| 33 | 35 | ||
| 34 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 36 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
| 35 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) | 37 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) |
| @@ -63,6 +65,11 @@ ifeq ($(ARCH),x86_64) | |||
| 63 | endif | 65 | endif |
| 64 | endif | 66 | endif |
| 65 | 67 | ||
| 68 | # Treat warnings as errors unless directed not to | ||
| 69 | ifneq ($(WERROR),0) | ||
| 70 | CFLAGS_WERROR := -Werror | ||
| 71 | endif | ||
| 72 | |||
| 66 | # | 73 | # |
| 67 | # Include saner warnings here, which can catch bugs: | 74 | # Include saner warnings here, which can catch bugs: |
| 68 | # | 75 | # |
| @@ -95,7 +102,7 @@ ifndef PERF_DEBUG | |||
| 95 | CFLAGS_OPTIMIZE = -O6 | 102 | CFLAGS_OPTIMIZE = -O6 |
| 96 | endif | 103 | endif |
| 97 | 104 | ||
| 98 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) | 105 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) |
| 99 | EXTLIBS = -lpthread -lrt -lelf -lm | 106 | EXTLIBS = -lpthread -lrt -lelf -lm |
| 100 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 | 107 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 |
| 101 | ALL_LDFLAGS = $(LDFLAGS) | 108 | ALL_LDFLAGS = $(LDFLAGS) |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 6b0519f885e4..f4c3fbee4bad 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -161,6 +161,7 @@ static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) | |||
| 161 | struct perf_event_attr *attr = &evsel->attr; | 161 | struct perf_event_attr *attr = &evsel->attr; |
| 162 | int track = !evsel->idx; /* only the first counter needs these */ | 162 | int track = !evsel->idx; /* only the first counter needs these */ |
| 163 | 163 | ||
| 164 | attr->disabled = 1; | ||
| 164 | attr->inherit = !no_inherit; | 165 | attr->inherit = !no_inherit; |
| 165 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 166 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
| 166 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 167 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
| @@ -671,6 +672,8 @@ static int __cmd_record(int argc, const char **argv) | |||
| 671 | } | 672 | } |
| 672 | } | 673 | } |
| 673 | 674 | ||
| 675 | perf_evlist__enable(evsel_list); | ||
| 676 | |||
| 674 | /* | 677 | /* |
| 675 | * Let the child rip | 678 | * Let the child rip |
| 676 | */ | 679 | */ |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 55f4c76f2821..efe696f936e2 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
| @@ -561,7 +561,7 @@ static int test__basic_mmap(void) | |||
| 561 | } | 561 | } |
| 562 | 562 | ||
| 563 | err = perf_event__parse_sample(event, attr.sample_type, sample_size, | 563 | err = perf_event__parse_sample(event, attr.sample_type, sample_size, |
| 564 | false, &sample); | 564 | false, &sample, false); |
| 565 | if (err) { | 565 | if (err) { |
| 566 | pr_err("Can't parse sample, err = %d\n", err); | 566 | pr_err("Can't parse sample, err = %d\n", err); |
| 567 | goto out_munmap; | 567 | goto out_munmap; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index a43433f08300..d28013b7d61c 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -191,7 +191,8 @@ static void __zero_source_counters(struct sym_entry *syme) | |||
| 191 | symbol__annotate_zero_histograms(sym); | 191 | symbol__annotate_zero_histograms(sym); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | 194 | static void record_precise_ip(struct sym_entry *syme, struct map *map, |
| 195 | int counter, u64 ip) | ||
| 195 | { | 196 | { |
| 196 | struct annotation *notes; | 197 | struct annotation *notes; |
| 197 | struct symbol *sym; | 198 | struct symbol *sym; |
| @@ -205,8 +206,8 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
| 205 | if (pthread_mutex_trylock(¬es->lock)) | 206 | if (pthread_mutex_trylock(¬es->lock)) |
| 206 | return; | 207 | return; |
| 207 | 208 | ||
| 208 | ip = syme->map->map_ip(syme->map, ip); | 209 | ip = map->map_ip(map, ip); |
| 209 | symbol__inc_addr_samples(sym, syme->map, counter, ip); | 210 | symbol__inc_addr_samples(sym, map, counter, ip); |
| 210 | 211 | ||
| 211 | pthread_mutex_unlock(¬es->lock); | 212 | pthread_mutex_unlock(¬es->lock); |
| 212 | } | 213 | } |
| @@ -810,7 +811,7 @@ static void perf_event__process_sample(const union perf_event *event, | |||
| 810 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); | 811 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); |
| 811 | assert(evsel != NULL); | 812 | assert(evsel != NULL); |
| 812 | syme->count[evsel->idx]++; | 813 | syme->count[evsel->idx]++; |
| 813 | record_precise_ip(syme, evsel->idx, ip); | 814 | record_precise_ip(syme, al.map, evsel->idx, ip); |
| 814 | pthread_mutex_lock(&top.active_symbols_lock); | 815 | pthread_mutex_lock(&top.active_symbols_lock); |
| 815 | if (list_empty(&syme->node) || !syme->node.next) { | 816 | if (list_empty(&syme->node) || !syme->node.next) { |
| 816 | static bool first = true; | 817 | static bool first = true; |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3c1b8a632101..437f8ca679a0 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -169,12 +169,17 @@ static int perf_event__synthesize_mmap_events(union perf_event *event, | |||
| 169 | continue; | 169 | continue; |
| 170 | pbf += n + 3; | 170 | pbf += n + 3; |
| 171 | if (*pbf == 'x') { /* vm_exec */ | 171 | if (*pbf == 'x') { /* vm_exec */ |
| 172 | char anonstr[] = "//anon\n"; | ||
| 172 | char *execname = strchr(bf, '/'); | 173 | char *execname = strchr(bf, '/'); |
| 173 | 174 | ||
| 174 | /* Catch VDSO */ | 175 | /* Catch VDSO */ |
| 175 | if (execname == NULL) | 176 | if (execname == NULL) |
| 176 | execname = strstr(bf, "[vdso]"); | 177 | execname = strstr(bf, "[vdso]"); |
| 177 | 178 | ||
| 179 | /* Catch anonymous mmaps */ | ||
| 180 | if ((execname == NULL) && !strstr(bf, "[")) | ||
| 181 | execname = anonstr; | ||
| 182 | |||
| 178 | if (execname == NULL) | 183 | if (execname == NULL) |
| 179 | continue; | 184 | continue; |
| 180 | 185 | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 1d7f66488a88..357a85b85248 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -186,6 +186,6 @@ const char *perf_event__name(unsigned int id); | |||
| 186 | 186 | ||
| 187 | int perf_event__parse_sample(const union perf_event *event, u64 type, | 187 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
| 188 | int sample_size, bool sample_id_all, | 188 | int sample_size, bool sample_id_all, |
| 189 | struct perf_sample *sample); | 189 | struct perf_sample *sample, bool swapped); |
| 190 | 190 | ||
| 191 | #endif /* __PERF_RECORD_H */ | 191 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c12bd476c6f7..72e9f4886b6d 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
| @@ -113,6 +113,19 @@ void perf_evlist__disable(struct perf_evlist *evlist) | |||
| 113 | } | 113 | } |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | void perf_evlist__enable(struct perf_evlist *evlist) | ||
| 117 | { | ||
| 118 | int cpu, thread; | ||
| 119 | struct perf_evsel *pos; | ||
| 120 | |||
| 121 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
| 122 | list_for_each_entry(pos, &evlist->entries, node) { | ||
| 123 | for (thread = 0; thread < evlist->threads->nr; thread++) | ||
| 124 | ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_ENABLE); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 116 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 129 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
| 117 | { | 130 | { |
| 118 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; | 131 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index ce85ae9ae57a..f34915002745 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
| @@ -54,6 +54,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); | |||
| 54 | void perf_evlist__munmap(struct perf_evlist *evlist); | 54 | void perf_evlist__munmap(struct perf_evlist *evlist); |
| 55 | 55 | ||
| 56 | void perf_evlist__disable(struct perf_evlist *evlist); | 56 | void perf_evlist__disable(struct perf_evlist *evlist); |
| 57 | void perf_evlist__enable(struct perf_evlist *evlist); | ||
| 57 | 58 | ||
| 58 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | 59 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, |
| 59 | struct cpu_map *cpus, | 60 | struct cpu_map *cpus, |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index a03a36b7908a..c5748c52318f 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -7,6 +7,8 @@ | |||
| 7 | * Released under the GPL v2. (and only v2, not any later version) | 7 | * Released under the GPL v2. (and only v2, not any later version) |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <byteswap.h> | ||
| 11 | #include "asm/bug.h" | ||
| 10 | #include "evsel.h" | 12 | #include "evsel.h" |
| 11 | #include "evlist.h" | 13 | #include "evlist.h" |
| 12 | #include "util.h" | 14 | #include "util.h" |
| @@ -342,10 +344,20 @@ static bool sample_overlap(const union perf_event *event, | |||
| 342 | 344 | ||
| 343 | int perf_event__parse_sample(const union perf_event *event, u64 type, | 345 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
| 344 | int sample_size, bool sample_id_all, | 346 | int sample_size, bool sample_id_all, |
| 345 | struct perf_sample *data) | 347 | struct perf_sample *data, bool swapped) |
| 346 | { | 348 | { |
| 347 | const u64 *array; | 349 | const u64 *array; |
| 348 | 350 | ||
| 351 | /* | ||
| 352 | * used for cross-endian analysis. See git commit 65014ab3 | ||
| 353 | * for why this goofiness is needed. | ||
| 354 | */ | ||
| 355 | union { | ||
| 356 | u64 val64; | ||
| 357 | u32 val32[2]; | ||
| 358 | } u; | ||
| 359 | |||
| 360 | |||
| 349 | data->cpu = data->pid = data->tid = -1; | 361 | data->cpu = data->pid = data->tid = -1; |
| 350 | data->stream_id = data->id = data->time = -1ULL; | 362 | data->stream_id = data->id = data->time = -1ULL; |
| 351 | 363 | ||
| @@ -366,9 +378,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
| 366 | } | 378 | } |
| 367 | 379 | ||
| 368 | if (type & PERF_SAMPLE_TID) { | 380 | if (type & PERF_SAMPLE_TID) { |
| 369 | u32 *p = (u32 *)array; | 381 | u.val64 = *array; |
| 370 | data->pid = p[0]; | 382 | if (swapped) { |
| 371 | data->tid = p[1]; | 383 | /* undo swap of u64, then swap on individual u32s */ |
| 384 | u.val64 = bswap_64(u.val64); | ||
| 385 | u.val32[0] = bswap_32(u.val32[0]); | ||
| 386 | u.val32[1] = bswap_32(u.val32[1]); | ||
| 387 | } | ||
| 388 | |||
| 389 | data->pid = u.val32[0]; | ||
| 390 | data->tid = u.val32[1]; | ||
| 372 | array++; | 391 | array++; |
| 373 | } | 392 | } |
| 374 | 393 | ||
| @@ -395,8 +414,15 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
| 395 | } | 414 | } |
| 396 | 415 | ||
| 397 | if (type & PERF_SAMPLE_CPU) { | 416 | if (type & PERF_SAMPLE_CPU) { |
| 398 | u32 *p = (u32 *)array; | 417 | |
| 399 | data->cpu = *p; | 418 | u.val64 = *array; |
| 419 | if (swapped) { | ||
| 420 | /* undo swap of u64, then swap on individual u32s */ | ||
| 421 | u.val64 = bswap_64(u.val64); | ||
| 422 | u.val32[0] = bswap_32(u.val32[0]); | ||
| 423 | } | ||
| 424 | |||
| 425 | data->cpu = u.val32[0]; | ||
| 400 | array++; | 426 | array++; |
| 401 | } | 427 | } |
| 402 | 428 | ||
| @@ -423,18 +449,24 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
| 423 | } | 449 | } |
| 424 | 450 | ||
| 425 | if (type & PERF_SAMPLE_RAW) { | 451 | if (type & PERF_SAMPLE_RAW) { |
| 426 | u32 *p = (u32 *)array; | 452 | u.val64 = *array; |
| 453 | if (WARN_ONCE(swapped, | ||
| 454 | "Endianness of raw data not corrected!\n")) { | ||
| 455 | /* undo swap of u64, then swap on individual u32s */ | ||
| 456 | u.val64 = bswap_64(u.val64); | ||
| 457 | u.val32[0] = bswap_32(u.val32[0]); | ||
| 458 | u.val32[1] = bswap_32(u.val32[1]); | ||
| 459 | } | ||
| 427 | 460 | ||
| 428 | if (sample_overlap(event, array, sizeof(u32))) | 461 | if (sample_overlap(event, array, sizeof(u32))) |
| 429 | return -EFAULT; | 462 | return -EFAULT; |
| 430 | 463 | ||
| 431 | data->raw_size = *p; | 464 | data->raw_size = u.val32[0]; |
| 432 | p++; | ||
| 433 | 465 | ||
| 434 | if (sample_overlap(event, p, data->raw_size)) | 466 | if (sample_overlap(event, &u.val32[1], data->raw_size)) |
| 435 | return -EFAULT; | 467 | return -EFAULT; |
| 436 | 468 | ||
| 437 | data->raw_data = p; | 469 | data->raw_data = &u.val32[1]; |
| 438 | } | 470 | } |
| 439 | 471 | ||
| 440 | return 0; | 472 | return 0; |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 555fc3864b90..5d732621a462 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -659,7 +659,7 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
| 659 | if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) | 659 | if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) |
| 660 | ret = -ENOENT; | 660 | ret = -ENOENT; |
| 661 | } | 661 | } |
| 662 | if (ret == 0) | 662 | if (ret >= 0) |
| 663 | ret = convert_variable(&vr_die, pf); | 663 | ret = convert_variable(&vr_die, pf); |
| 664 | 664 | ||
| 665 | if (ret < 0) | 665 | if (ret < 0) |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 170601e67d6b..974d0cbee5e9 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -162,7 +162,8 @@ static inline int perf_session__parse_sample(struct perf_session *session, | |||
| 162 | { | 162 | { |
| 163 | return perf_event__parse_sample(event, session->sample_type, | 163 | return perf_event__parse_sample(event, session->sample_type, |
| 164 | session->sample_size, | 164 | session->sample_size, |
| 165 | session->sample_id_all, sample); | 165 | session->sample_id_all, sample, |
| 166 | session->header.needs_swap); | ||
| 166 | } | 167 | } |
| 167 | 168 | ||
| 168 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | 169 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 401e220566fd..1ee8f1e40f18 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -151,11 +151,17 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 151 | { | 151 | { |
| 152 | u64 ip_l, ip_r; | 152 | u64 ip_l, ip_r; |
| 153 | 153 | ||
| 154 | if (!left->ms.sym && !right->ms.sym) | ||
| 155 | return right->level - left->level; | ||
| 156 | |||
| 157 | if (!left->ms.sym || !right->ms.sym) | ||
| 158 | return cmp_null(left->ms.sym, right->ms.sym); | ||
| 159 | |||
| 154 | if (left->ms.sym == right->ms.sym) | 160 | if (left->ms.sym == right->ms.sym) |
| 155 | return 0; | 161 | return 0; |
| 156 | 162 | ||
| 157 | ip_l = left->ms.sym ? left->ms.sym->start : left->ip; | 163 | ip_l = left->ms.sym->start; |
| 158 | ip_r = right->ms.sym ? right->ms.sym->start : right->ip; | 164 | ip_r = right->ms.sym->start; |
| 159 | 165 | ||
| 160 | return (int64_t)(ip_r - ip_l); | 166 | return (int64_t)(ip_r - ip_l); |
| 161 | } | 167 | } |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 469c0264ed29..40eeaf07725b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -74,16 +74,104 @@ static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | |||
| 74 | 74 | ||
| 75 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 75 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
| 76 | { | 76 | { |
| 77 | symbol_type = toupper(symbol_type); | ||
| 78 | |||
| 77 | switch (map_type) { | 79 | switch (map_type) { |
| 78 | case MAP__FUNCTION: | 80 | case MAP__FUNCTION: |
| 79 | return symbol_type == 'T' || symbol_type == 'W'; | 81 | return symbol_type == 'T' || symbol_type == 'W'; |
| 80 | case MAP__VARIABLE: | 82 | case MAP__VARIABLE: |
| 81 | return symbol_type == 'D' || symbol_type == 'd'; | 83 | return symbol_type == 'D'; |
| 82 | default: | 84 | default: |
| 83 | return false; | 85 | return false; |
| 84 | } | 86 | } |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 89 | static int prefix_underscores_count(const char *str) | ||
| 90 | { | ||
| 91 | const char *tail = str; | ||
| 92 | |||
| 93 | while (*tail == '_') | ||
| 94 | tail++; | ||
| 95 | |||
| 96 | return tail - str; | ||
| 97 | } | ||
| 98 | |||
| 99 | #define SYMBOL_A 0 | ||
| 100 | #define SYMBOL_B 1 | ||
| 101 | |||
| 102 | static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | ||
| 103 | { | ||
| 104 | s64 a; | ||
| 105 | s64 b; | ||
| 106 | |||
| 107 | /* Prefer a symbol with non zero length */ | ||
| 108 | a = syma->end - syma->start; | ||
| 109 | b = symb->end - symb->start; | ||
| 110 | if ((b == 0) && (a > 0)) | ||
| 111 | return SYMBOL_A; | ||
| 112 | else if ((a == 0) && (b > 0)) | ||
| 113 | return SYMBOL_B; | ||
| 114 | |||
| 115 | /* Prefer a non weak symbol over a weak one */ | ||
| 116 | a = syma->binding == STB_WEAK; | ||
| 117 | b = symb->binding == STB_WEAK; | ||
| 118 | if (b && !a) | ||
| 119 | return SYMBOL_A; | ||
| 120 | if (a && !b) | ||
| 121 | return SYMBOL_B; | ||
| 122 | |||
| 123 | /* Prefer a global symbol over a non global one */ | ||
| 124 | a = syma->binding == STB_GLOBAL; | ||
| 125 | b = symb->binding == STB_GLOBAL; | ||
| 126 | if (a && !b) | ||
| 127 | return SYMBOL_A; | ||
| 128 | if (b && !a) | ||
| 129 | return SYMBOL_B; | ||
| 130 | |||
| 131 | /* Prefer a symbol with less underscores */ | ||
| 132 | a = prefix_underscores_count(syma->name); | ||
| 133 | b = prefix_underscores_count(symb->name); | ||
| 134 | if (b > a) | ||
| 135 | return SYMBOL_A; | ||
| 136 | else if (a > b) | ||
| 137 | return SYMBOL_B; | ||
| 138 | |||
| 139 | /* If all else fails, choose the symbol with the longest name */ | ||
| 140 | if (strlen(syma->name) >= strlen(symb->name)) | ||
| 141 | return SYMBOL_A; | ||
| 142 | else | ||
| 143 | return SYMBOL_B; | ||
| 144 | } | ||
| 145 | |||
| 146 | static void symbols__fixup_duplicate(struct rb_root *symbols) | ||
| 147 | { | ||
| 148 | struct rb_node *nd; | ||
| 149 | struct symbol *curr, *next; | ||
| 150 | |||
| 151 | nd = rb_first(symbols); | ||
| 152 | |||
| 153 | while (nd) { | ||
| 154 | curr = rb_entry(nd, struct symbol, rb_node); | ||
| 155 | again: | ||
| 156 | nd = rb_next(&curr->rb_node); | ||
| 157 | next = rb_entry(nd, struct symbol, rb_node); | ||
| 158 | |||
| 159 | if (!nd) | ||
| 160 | break; | ||
| 161 | |||
| 162 | if (curr->start != next->start) | ||
| 163 | continue; | ||
| 164 | |||
| 165 | if (choose_best_symbol(curr, next) == SYMBOL_A) { | ||
| 166 | rb_erase(&next->rb_node, symbols); | ||
| 167 | goto again; | ||
| 168 | } else { | ||
| 169 | nd = rb_next(&curr->rb_node); | ||
| 170 | rb_erase(&curr->rb_node, symbols); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 87 | static void symbols__fixup_end(struct rb_root *symbols) | 175 | static void symbols__fixup_end(struct rb_root *symbols) |
| 88 | { | 176 | { |
| 89 | struct rb_node *nd, *prevnd = rb_first(symbols); | 177 | struct rb_node *nd, *prevnd = rb_first(symbols); |
| @@ -438,18 +526,11 @@ int kallsyms__parse(const char *filename, void *arg, | |||
| 438 | char *line = NULL; | 526 | char *line = NULL; |
| 439 | size_t n; | 527 | size_t n; |
| 440 | int err = -1; | 528 | int err = -1; |
| 441 | u64 prev_start = 0; | ||
| 442 | char prev_symbol_type = 0; | ||
| 443 | char *prev_symbol_name; | ||
| 444 | FILE *file = fopen(filename, "r"); | 529 | FILE *file = fopen(filename, "r"); |
| 445 | 530 | ||
| 446 | if (file == NULL) | 531 | if (file == NULL) |
| 447 | goto out_failure; | 532 | goto out_failure; |
| 448 | 533 | ||
| 449 | prev_symbol_name = malloc(KSYM_NAME_LEN); | ||
| 450 | if (prev_symbol_name == NULL) | ||
| 451 | goto out_close; | ||
| 452 | |||
| 453 | err = 0; | 534 | err = 0; |
| 454 | 535 | ||
| 455 | while (!feof(file)) { | 536 | while (!feof(file)) { |
| @@ -470,7 +551,7 @@ int kallsyms__parse(const char *filename, void *arg, | |||
| 470 | if (len + 2 >= line_len) | 551 | if (len + 2 >= line_len) |
| 471 | continue; | 552 | continue; |
| 472 | 553 | ||
| 473 | symbol_type = toupper(line[len]); | 554 | symbol_type = line[len]; |
| 474 | len += 2; | 555 | len += 2; |
| 475 | symbol_name = line + len; | 556 | symbol_name = line + len; |
| 476 | len = line_len - len; | 557 | len = line_len - len; |
| @@ -480,24 +561,18 @@ int kallsyms__parse(const char *filename, void *arg, | |||
| 480 | break; | 561 | break; |
| 481 | } | 562 | } |
| 482 | 563 | ||
| 483 | if (prev_symbol_type) { | 564 | /* |
| 484 | u64 end = start; | 565 | * module symbols are not sorted so we add all |
| 485 | if (end != prev_start) | 566 | * symbols with zero length and rely on |
| 486 | --end; | 567 | * symbols__fixup_end() to fix it up. |
| 487 | err = process_symbol(arg, prev_symbol_name, | 568 | */ |
| 488 | prev_symbol_type, prev_start, end); | 569 | err = process_symbol(arg, symbol_name, |
| 489 | if (err) | 570 | symbol_type, start, start); |
| 490 | break; | 571 | if (err) |
| 491 | } | 572 | break; |
| 492 | |||
| 493 | memcpy(prev_symbol_name, symbol_name, len + 1); | ||
| 494 | prev_symbol_type = symbol_type; | ||
| 495 | prev_start = start; | ||
| 496 | } | 573 | } |
| 497 | 574 | ||
| 498 | free(prev_symbol_name); | ||
| 499 | free(line); | 575 | free(line); |
| 500 | out_close: | ||
| 501 | fclose(file); | 576 | fclose(file); |
| 502 | return err; | 577 | return err; |
| 503 | 578 | ||
| @@ -703,6 +778,9 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, | |||
| 703 | if (dso__load_all_kallsyms(dso, filename, map) < 0) | 778 | if (dso__load_all_kallsyms(dso, filename, map) < 0) |
| 704 | return -1; | 779 | return -1; |
| 705 | 780 | ||
| 781 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
| 782 | symbols__fixup_end(&dso->symbols[map->type]); | ||
| 783 | |||
| 706 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 784 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
| 707 | dso->symtab_type = SYMTAB__GUEST_KALLSYMS; | 785 | dso->symtab_type = SYMTAB__GUEST_KALLSYMS; |
| 708 | else | 786 | else |
| @@ -1092,8 +1170,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | |||
| 1092 | if (dso->has_build_id) { | 1170 | if (dso->has_build_id) { |
| 1093 | u8 build_id[BUILD_ID_SIZE]; | 1171 | u8 build_id[BUILD_ID_SIZE]; |
| 1094 | 1172 | ||
| 1095 | if (elf_read_build_id(elf, build_id, | 1173 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) |
| 1096 | BUILD_ID_SIZE) != BUILD_ID_SIZE) | ||
| 1097 | goto out_elf_end; | 1174 | goto out_elf_end; |
| 1098 | 1175 | ||
| 1099 | if (!dso__build_id_equal(dso, build_id)) | 1176 | if (!dso__build_id_equal(dso, build_id)) |
| @@ -1111,6 +1188,8 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | |||
| 1111 | } | 1188 | } |
| 1112 | 1189 | ||
| 1113 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | 1190 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); |
| 1191 | if (opdshdr.sh_type != SHT_PROGBITS) | ||
| 1192 | opdsec = NULL; | ||
| 1114 | if (opdsec) | 1193 | if (opdsec) |
| 1115 | opddata = elf_rawdata(opdsec, NULL); | 1194 | opddata = elf_rawdata(opdsec, NULL); |
| 1116 | 1195 | ||
| @@ -1276,6 +1355,7 @@ new_symbol: | |||
| 1276 | * For misannotated, zeroed, ASM function sizes. | 1355 | * For misannotated, zeroed, ASM function sizes. |
| 1277 | */ | 1356 | */ |
| 1278 | if (nr > 0) { | 1357 | if (nr > 0) { |
| 1358 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
| 1279 | symbols__fixup_end(&dso->symbols[map->type]); | 1359 | symbols__fixup_end(&dso->symbols[map->type]); |
| 1280 | if (kmap) { | 1360 | if (kmap) { |
| 1281 | /* | 1361 | /* |
| @@ -1362,8 +1442,8 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) | |||
| 1362 | ptr = data->d_buf; | 1442 | ptr = data->d_buf; |
| 1363 | while (ptr < (data->d_buf + data->d_size)) { | 1443 | while (ptr < (data->d_buf + data->d_size)) { |
| 1364 | GElf_Nhdr *nhdr = ptr; | 1444 | GElf_Nhdr *nhdr = ptr; |
| 1365 | int namesz = NOTE_ALIGN(nhdr->n_namesz), | 1445 | size_t namesz = NOTE_ALIGN(nhdr->n_namesz), |
| 1366 | descsz = NOTE_ALIGN(nhdr->n_descsz); | 1446 | descsz = NOTE_ALIGN(nhdr->n_descsz); |
| 1367 | const char *name; | 1447 | const char *name; |
| 1368 | 1448 | ||
| 1369 | ptr += sizeof(*nhdr); | 1449 | ptr += sizeof(*nhdr); |
| @@ -1372,8 +1452,10 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) | |||
| 1372 | if (nhdr->n_type == NT_GNU_BUILD_ID && | 1452 | if (nhdr->n_type == NT_GNU_BUILD_ID && |
| 1373 | nhdr->n_namesz == sizeof("GNU")) { | 1453 | nhdr->n_namesz == sizeof("GNU")) { |
| 1374 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | 1454 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { |
| 1375 | memcpy(bf, ptr, BUILD_ID_SIZE); | 1455 | size_t sz = min(size, descsz); |
| 1376 | err = BUILD_ID_SIZE; | 1456 | memcpy(bf, ptr, sz); |
| 1457 | memset(bf + sz, 0, size - sz); | ||
| 1458 | err = descsz; | ||
| 1377 | break; | 1459 | break; |
| 1378 | } | 1460 | } |
| 1379 | } | 1461 | } |
| @@ -1425,7 +1507,7 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | |||
| 1425 | while (1) { | 1507 | while (1) { |
| 1426 | char bf[BUFSIZ]; | 1508 | char bf[BUFSIZ]; |
| 1427 | GElf_Nhdr nhdr; | 1509 | GElf_Nhdr nhdr; |
| 1428 | int namesz, descsz; | 1510 | size_t namesz, descsz; |
| 1429 | 1511 | ||
| 1430 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | 1512 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) |
| 1431 | break; | 1513 | break; |
| @@ -1434,15 +1516,16 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | |||
| 1434 | descsz = NOTE_ALIGN(nhdr.n_descsz); | 1516 | descsz = NOTE_ALIGN(nhdr.n_descsz); |
| 1435 | if (nhdr.n_type == NT_GNU_BUILD_ID && | 1517 | if (nhdr.n_type == NT_GNU_BUILD_ID && |
| 1436 | nhdr.n_namesz == sizeof("GNU")) { | 1518 | nhdr.n_namesz == sizeof("GNU")) { |
| 1437 | if (read(fd, bf, namesz) != namesz) | 1519 | if (read(fd, bf, namesz) != (ssize_t)namesz) |
| 1438 | break; | 1520 | break; |
| 1439 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | 1521 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { |
| 1440 | if (read(fd, build_id, | 1522 | size_t sz = min(descsz, size); |
| 1441 | BUILD_ID_SIZE) == BUILD_ID_SIZE) { | 1523 | if (read(fd, build_id, sz) == (ssize_t)sz) { |
| 1524 | memset(build_id + sz, 0, size - sz); | ||
| 1442 | err = 0; | 1525 | err = 0; |
| 1443 | break; | 1526 | break; |
| 1444 | } | 1527 | } |
| 1445 | } else if (read(fd, bf, descsz) != descsz) | 1528 | } else if (read(fd, bf, descsz) != (ssize_t)descsz) |
| 1446 | break; | 1529 | break; |
| 1447 | } else { | 1530 | } else { |
| 1448 | int n = namesz + descsz; | 1531 | int n = namesz + descsz; |
