diff options
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/annotate.c | 6 | ||||
-rw-r--r-- | tools/perf/util/annotate.h | 2 | ||||
-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 | 57 | ||||
-rw-r--r-- | tools/perf/util/map.c | 98 | ||||
-rw-r--r-- | tools/perf/util/map.h | 42 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 2 | ||||
-rw-r--r-- | tools/perf/util/python.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 | ||||
-rw-r--r-- | tools/perf/util/top.h | 3 | ||||
-rw-r--r-- | tools/perf/util/ui/browsers/top.c | 28 |
16 files changed, 308 insertions, 119 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 01d36ba5405..bc8f4773d4d 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -16,6 +16,8 @@ #include "annotate.h" #include <pthread.h> +const char *disassembler_style; + int symbol__annotate_init(struct map *map __used, struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); @@ -323,9 +325,11 @@ fallback: dso, dso->long_name, sym, sym->name); snprintf(command, sizeof(command), - "objdump --start-address=0x%016" PRIx64 + "objdump %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -d %s %s -C %s|grep -v %s|expand", + disassembler_style ? "-M " : "", + disassembler_style ? disassembler_style : "", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index c2c28689680..6ede1286ee7 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -100,4 +100,6 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, int refresh); #endif +extern const char *disassembler_style; + #endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3c1b8a63210..437f8ca679a 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, continue; pbf += n + 3; if (*pbf == 'x') { /* vm_exec */ + char anonstr[] = "//anon\n"; char *execname = strchr(bf, '/'); /* Catch VDSO */ if (execname == NULL) execname = strstr(bf, "[vdso]"); + /* Catch anonymous mmaps */ + if ((execname == NULL) && !strstr(bf, "[")) + execname = anonstr; + if (execname == NULL) continue; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 1d7f66488a8..357a85b8524 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); int perf_event__parse_sample(const union perf_event *event, u64 type, int sample_size, bool sample_id_all, - struct perf_sample *sample); + struct perf_sample *sample, bool swapped); #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c12bd476c6f..72e9f4886b6 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) } } +void perf_evlist__enable(struct perf_evlist *evlist) +{ + int cpu, thread; + struct perf_evsel *pos; + + for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { + list_for_each_entry(pos, &evlist->entries, node) { + for (thread = 0; thread < evlist->threads->nr; thread++) + ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_ENABLE); + } + } +} + int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) { 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 ce85ae9ae57..f3491500274 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); void perf_evlist__munmap(struct perf_evlist *evlist); void perf_evlist__disable(struct perf_evlist *evlist); +void perf_evlist__enable(struct perf_evlist *evlist); static inline void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus, diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index a03a36b7908..e389815078d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -7,6 +7,8 @@ * Released under the GPL v2. (and only v2, not any later version) */ +#include <byteswap.h> +#include "asm/bug.h" #include "evsel.h" #include "evlist.h" #include "util.h" @@ -342,10 +344,20 @@ static bool sample_overlap(const union perf_event *event, int perf_event__parse_sample(const union perf_event *event, u64 type, int sample_size, bool sample_id_all, - struct perf_sample *data) + struct perf_sample *data, bool swapped) { const u64 *array; + /* + * used for cross-endian analysis. See git commit 65014ab3 + * for why this goofiness is needed. + */ + union { + u64 val64; + u32 val32[2]; + } u; + + data->cpu = data->pid = data->tid = -1; data->stream_id = data->id = data->time = -1ULL; @@ -366,9 +378,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, } if (type & PERF_SAMPLE_TID) { - u32 *p = (u32 *)array; - data->pid = p[0]; - data->tid = p[1]; + u.val64 = *array; + if (swapped) { + /* undo swap of u64, then swap on individual u32s */ + u.val64 = bswap_64(u.val64); + u.val32[0] = bswap_32(u.val32[0]); + u.val32[1] = bswap_32(u.val32[1]); + } + + data->pid = u.val32[0]; + data->tid = u.val32[1]; array++; } @@ -395,8 +414,15 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, } if (type & PERF_SAMPLE_CPU) { - u32 *p = (u32 *)array; - data->cpu = *p; + + u.val64 = *array; + if (swapped) { + /* undo swap of u64, then swap on individual u32s */ + u.val64 = bswap_64(u.val64); + u.val32[0] = bswap_32(u.val32[0]); + } + + data->cpu = u.val32[0]; array++; } @@ -423,18 +449,27 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, } if (type & PERF_SAMPLE_RAW) { - u32 *p = (u32 *)array; + const u64 *pdata; + + u.val64 = *array; + if (WARN_ONCE(swapped, + "Endianness of raw data not corrected!\n")) { + /* undo swap of u64, then swap on individual u32s */ + u.val64 = bswap_64(u.val64); + u.val32[0] = bswap_32(u.val32[0]); + u.val32[1] = bswap_32(u.val32[1]); + } if (sample_overlap(event, array, sizeof(u32))) return -EFAULT; - data->raw_size = *p; - p++; + data->raw_size = u.val32[0]; + pdata = (void *) array + sizeof(u32); - if (sample_overlap(event, p, data->raw_size)) + if (sample_overlap(event, pdata, data->raw_size)) return -EFAULT; - data->raw_data = p; + data->raw_data = (void *) pdata; } return 0; diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index a16ecab5229..9cf0d4393c8 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -18,6 +18,13 @@ static inline int is_anon_memory(const char *filename) return strcmp(filename, "//anon") == 0; } +static inline int is_no_dso_memory(const char *filename) +{ + return !strcmp(filename, "[stack]") || + !strcmp(filename, "[vdso]") || + !strcmp(filename, "[heap]"); +} + void map__init(struct map *self, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso) { @@ -42,9 +49,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, if (self != NULL) { char newfilename[PATH_MAX]; struct dso *dso; - int anon; + int anon, no_dso; anon = is_anon_memory(filename); + no_dso = is_no_dso_memory(filename); if (anon) { snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); @@ -57,12 +65,16 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, map__init(self, type, start, start + len, pgoff, dso); - if (anon) { -set_identity: + if (anon || no_dso) { self->map_ip = self->unmap_ip = identity__map_ip; - } else if (strcmp(filename, "[vdso]") == 0) { - dso__set_loaded(dso, self->type); - goto set_identity; + + /* + * Set memory without DSO as loaded. All map__find_* + * functions still return NULL, and we avoid the + * unnecessary map__load warning. + */ + if (no_dso) + dso__set_loaded(dso, self->type); } } return self; @@ -220,55 +232,55 @@ u64 map__objdump_2ip(struct map *map, u64 addr) return ip; } -void map_groups__init(struct map_groups *self) +void map_groups__init(struct map_groups *mg) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) { - self->maps[i] = RB_ROOT; - INIT_LIST_HEAD(&self->removed_maps[i]); + mg->maps[i] = RB_ROOT; + INIT_LIST_HEAD(&mg->removed_maps[i]); } - self->machine = NULL; + mg->machine = NULL; } -static void maps__delete(struct rb_root *self) +static void maps__delete(struct rb_root *maps) { - struct rb_node *next = rb_first(self); + struct rb_node *next = rb_first(maps); while (next) { struct map *pos = rb_entry(next, struct map, rb_node); next = rb_next(&pos->rb_node); - rb_erase(&pos->rb_node, self); + rb_erase(&pos->rb_node, maps); map__delete(pos); } } -static void maps__delete_removed(struct list_head *self) +static void maps__delete_removed(struct list_head *maps) { struct map *pos, *n; - list_for_each_entry_safe(pos, n, self, node) { + list_for_each_entry_safe(pos, n, maps, node) { list_del(&pos->node); map__delete(pos); } } -void map_groups__exit(struct map_groups *self) +void map_groups__exit(struct map_groups *mg) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) { - maps__delete(&self->maps[i]); - maps__delete_removed(&self->removed_maps[i]); + maps__delete(&mg->maps[i]); + maps__delete_removed(&mg->removed_maps[i]); } } -void map_groups__flush(struct map_groups *self) +void map_groups__flush(struct map_groups *mg) { int type; for (type = 0; type < MAP__NR_TYPES; type++) { - struct rb_root *root = &self->maps[type]; + struct rb_root *root = &mg->maps[type]; struct rb_node *next = rb_first(root); while (next) { @@ -280,17 +292,17 @@ void map_groups__flush(struct map_groups *self) * instance in some hist_entry instances, so * just move them to a separate list. */ - list_add_tail(&pos->node, &self->removed_maps[pos->type]); + list_add_tail(&pos->node, &mg->removed_maps[pos->type]); } } } -struct symbol *map_groups__find_symbol(struct map_groups *self, +struct symbol *map_groups__find_symbol(struct map_groups *mg, enum map_type type, u64 addr, struct map **mapp, symbol_filter_t filter) { - struct map *map = map_groups__find(self, type, addr); + struct map *map = map_groups__find(mg, type, addr); if (map != NULL) { if (mapp != NULL) @@ -301,7 +313,7 @@ struct symbol *map_groups__find_symbol(struct map_groups *self, return NULL; } -struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, +struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, enum map_type type, const char *name, struct map **mapp, @@ -309,7 +321,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, { struct rb_node *nd; - for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { struct map *pos = rb_entry(nd, struct map, rb_node); struct symbol *sym = map__find_symbol_by_name(pos, name, filter); @@ -323,13 +335,13 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, return NULL; } -size_t __map_groups__fprintf_maps(struct map_groups *self, +size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, int verbose, FILE *fp) { size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); struct rb_node *nd; - for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { struct map *pos = rb_entry(nd, struct map, rb_node); printed += fprintf(fp, "Map:"); printed += map__fprintf(pos, fp); @@ -342,22 +354,22 @@ size_t __map_groups__fprintf_maps(struct map_groups *self, return printed; } -size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) +size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp) { size_t printed = 0, i; for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_maps(self, i, verbose, fp); + printed += __map_groups__fprintf_maps(mg, i, verbose, fp); return printed; } -static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, +static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg, enum map_type type, int verbose, FILE *fp) { struct map *pos; size_t printed = 0; - list_for_each_entry(pos, &self->removed_maps[type], node) { + list_for_each_entry(pos, &mg->removed_maps[type], node) { printed += fprintf(fp, "Map:"); printed += map__fprintf(pos, fp); if (verbose > 1) { @@ -368,26 +380,26 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, return printed; } -static size_t map_groups__fprintf_removed_maps(struct map_groups *self, +static size_t map_groups__fprintf_removed_maps(struct map_groups *mg, int verbose, FILE *fp) { size_t printed = 0, i; for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); + printed += __map_groups__fprintf_removed_maps(mg, i, verbose, fp); return printed; } -size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) +size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp) { - size_t printed = map_groups__fprintf_maps(self, verbose, fp); + size_t printed = map_groups__fprintf_maps(mg, verbose, fp); printed += fprintf(fp, "Removed maps:\n"); - return printed + map_groups__fprintf_removed_maps(self, verbose, fp); + return printed + map_groups__fprintf_removed_maps(mg, verbose, fp); } -int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, +int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, int verbose, FILE *fp) { - struct rb_root *root = &self->maps[map->type]; + struct rb_root *root = &mg->maps[map->type]; struct rb_node *next = rb_first(root); int err = 0; @@ -418,7 +430,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, } before->end = map->start - 1; - map_groups__insert(self, before); + map_groups__insert(mg, before); if (verbose >= 2) map__fprintf(before, fp); } @@ -432,7 +444,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, } after->start = map->end + 1; - map_groups__insert(self, after); + map_groups__insert(mg, after); if (verbose >= 2) map__fprintf(after, fp); } @@ -441,7 +453,7 @@ move_map: * If we have references, just move them to a separate list. */ if (pos->referenced) - list_add_tail(&pos->node, &self->removed_maps[map->type]); + list_add_tail(&pos->node, &mg->removed_maps[map->type]); else map__delete(pos); @@ -455,7 +467,7 @@ move_map: /* * XXX This should not really _copy_ te maps, but refcount them. */ -int map_groups__clone(struct map_groups *self, +int map_groups__clone(struct map_groups *mg, struct map_groups *parent, enum map_type type) { struct rb_node *nd; @@ -464,7 +476,7 @@ int map_groups__clone(struct map_groups *self, struct map *new = map__clone(map); if (new == NULL) return -ENOMEM; - map_groups__insert(self, new); + map_groups__insert(mg, new); } return 0; } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index b397c038372..890d85545d0 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -123,17 +123,17 @@ void map__fixup_end(struct map *self); void map__reloc_vmlinux(struct map *self); -size_t __map_groups__fprintf_maps(struct map_groups *self, +size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, int verbose, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); -void maps__remove(struct rb_root *self, struct map *map); +void maps__remove(struct rb_root *maps, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); -void map_groups__init(struct map_groups *self); -void map_groups__exit(struct map_groups *self); -int map_groups__clone(struct map_groups *self, +void map_groups__init(struct map_groups *mg); +void map_groups__exit(struct map_groups *mg); +int map_groups__clone(struct map_groups *mg, struct map_groups *parent, enum map_type type); -size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); -size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); +size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); +size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); typedef void (*machine__process_t)(struct machine *self, void *data); @@ -162,29 +162,29 @@ static inline bool machine__is_host(struct machine *self) return self ? self->pid == HOST_KERNEL_ID : false; } -static inline void map_groups__insert(struct map_groups *self, struct map *map) +static inline void map_groups__insert(struct map_groups *mg, struct map *map) { - maps__insert(&self->maps[map->type], map); - map->groups = self; + maps__insert(&mg->maps[map->type], map); + map->groups = mg; } -static inline void map_groups__remove(struct map_groups *self, struct map *map) +static inline void map_groups__remove(struct map_groups *mg, struct map *map) { - maps__remove(&self->maps[map->type], map); + maps__remove(&mg->maps[map->type], map); } -static inline struct map *map_groups__find(struct map_groups *self, +static inline struct map *map_groups__find(struct map_groups *mg, enum map_type type, u64 addr) { - return maps__find(&self->maps[type], addr); + return maps__find(&mg->maps[type], addr); } -struct symbol *map_groups__find_symbol(struct map_groups *self, +struct symbol *map_groups__find_symbol(struct map_groups *mg, enum map_type type, u64 addr, struct map **mapp, symbol_filter_t filter); -struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, +struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, enum map_type type, const char *name, struct map **mapp, @@ -208,11 +208,11 @@ struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, } static inline -struct symbol *map_groups__find_function_by_name(struct map_groups *self, +struct symbol *map_groups__find_function_by_name(struct map_groups *mg, const char *name, struct map **mapp, symbol_filter_t filter) { - return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); + return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); } static inline @@ -225,13 +225,13 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *self, filter); } -int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, +int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, int verbose, FILE *fp); -struct map *map_groups__find_by_name(struct map_groups *self, +struct map *map_groups__find_by_name(struct map_groups *mg, enum map_type type, const char *name); struct map *machine__new_module(struct machine *self, u64 start, const char *filename); -void map_groups__flush(struct map_groups *self); +void map_groups__flush(struct map_groups *mg); #endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 555fc3864b9..5d732621a46 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) if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) ret = -ENOENT; } - if (ret == 0) + if (ret >= 0) ret = convert_variable(&vr_die, pf); if (ret < 0) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index cbc8f215d4b..7624324efad 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -803,7 +803,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, first = list_entry(evlist->entries.next, struct perf_evsel, node); err = perf_event__parse_sample(event, first->attr.sample_type, perf_evsel__sample_size(first), - sample_id_all, &pevent->sample); + sample_id_all, &pevent->sample, false); if (err) return PyErr_Format(PyExc_OSError, "perf: can't parse sample, err=%d", err); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 170601e67d6..974d0cbee5e 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, { return perf_event__parse_sample(event, session->sample_type, session->sample_size, - session->sample_id_all, sample); + session->sample_id_all, sample, + session->header.needs_swap); } 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 401e220566f..1ee8f1e40f1 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) { u64 ip_l, ip_r; + if (!left->ms.sym && !right->ms.sym) + return right->level - left->level; + + if (!left->ms.sym || !right->ms.sym) + return cmp_null(left->ms.sym, right->ms.sym); + if (left->ms.sym == right->ms.sym) return 0; - ip_l = left->ms.sym ? left->ms.sym->start : left->ip; - ip_r = right->ms.sym ? right->ms.sym->start : right->ip; + ip_l = left->ms.sym->start; + ip_r = right->ms.sym->start; return (int64_t)(ip_r - ip_l); } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 245e60d6b4e..077df15ee70 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -76,16 +76,104 @@ static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) bool symbol_type__is_a(char symbol_type, enum map_type map_type) { + symbol_type = toupper(symbol_type); + switch (map_type) { case MAP__FUNCTION: return symbol_type == 'T' || symbol_type == 'W'; case MAP__VARIABLE: - return symbol_type == 'D' || symbol_type == 'd'; + return symbol_type == 'D'; default: return false; } } +static int prefix_underscores_count(const char *str) +{ + const char *tail = str; + + while (*tail == '_') + tail++; + + return tail - str; +} + +#define SYMBOL_A 0 +#define SYMBOL_B 1 + +static int choose_best_symbol(struct symbol *syma, struct symbol *symb) +{ + s64 a; + s64 b; + + /* Prefer a symbol with non zero length */ + a = syma->end - syma->start; + b = symb->end - symb->start; + if ((b == 0) && (a > 0)) + return SYMBOL_A; + else if ((a == 0) && (b > 0)) + return SYMBOL_B; + + /* Prefer a non weak symbol over a weak one */ + a = syma->binding == STB_WEAK; + b = symb->binding == STB_WEAK; + if (b && !a) + return SYMBOL_A; + if (a && !b) + return SYMBOL_B; + + /* Prefer a global symbol over a non global one */ + a = syma->binding == STB_GLOBAL; + b = symb->binding == STB_GLOBAL; + if (a && !b) + return SYMBOL_A; + if (b && !a) + return SYMBOL_B; + + /* Prefer a symbol with less underscores */ + a = prefix_underscores_count(syma->name); + b = prefix_underscores_count(symb->name); + if (b > a) + return SYMBOL_A; + else if (a > b) + return SYMBOL_B; + + /* If all else fails, choose the symbol with the longest name */ + if (strlen(syma->name) >= strlen(symb->name)) + return SYMBOL_A; + else + return SYMBOL_B; +} + +static void symbols__fixup_duplicate(struct rb_root *symbols) +{ + struct rb_node *nd; + struct symbol *curr, *next; + + nd = rb_first(symbols); + + while (nd) { + curr = rb_entry(nd, struct symbol, rb_node); +again: + nd = rb_next(&curr->rb_node); + next = rb_entry(nd, struct symbol, rb_node); + + if (!nd) + break; + + if (curr->start != next->start) + continue; + + if (choose_best_symbol(curr, next) == SYMBOL_A) { + rb_erase(&next->rb_node, symbols); + goto again; + } else { + nd = rb_next(&curr->rb_node); + rb_erase(&curr->rb_node, symbols); + } + } +} + static void symbols__fixup_end(struct rb_root *symbols) { struct rb_node *nd, *prevnd = rb_first(symbols); @@ -440,18 +528,11 @@ int kallsyms__parse(const char *filename, void *arg, char *line = NULL; size_t n; int err = -1; - u64 prev_start = 0; - char prev_symbol_type = 0; - char *prev_symbol_name; FILE *file = fopen(filename, "r"); if (file == NULL) goto out_failure; - prev_symbol_name = malloc(KSYM_NAME_LEN); - if (prev_symbol_name == NULL) - goto out_close; - err = 0; while (!feof(file)) { @@ -472,7 +553,7 @@ int kallsyms__parse(const char *filename, void *arg, if (len + 2 >= line_len) continue; - symbol_type = toupper(line[len]); + symbol_type = line[len]; len += 2; symbol_name = line + len; len = line_len - len; @@ -482,24 +563,18 @@ int kallsyms__parse(const char *filename, void *arg, break; } - if (prev_symbol_type) { - u64 end = start; - if (end != prev_start) - --end; - err = process_symbol(arg, prev_symbol_name, - prev_symbol_type, prev_start, end); - if (err) - break; - } - - memcpy(prev_symbol_name, symbol_name, len + 1); - prev_symbol_type = symbol_type; - prev_start = start; + /* + * module symbols are not sorted so we add all + * symbols with zero length and rely on + * symbols__fixup_end() to fix it up. + */ + err = process_symbol(arg, symbol_name, + symbol_type, start, start); + if (err) + break; } - free(prev_symbol_name); free(line); -out_close: fclose(file); return err; @@ -705,6 +780,9 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, if (dso__load_all_kallsyms(dso, filename, map) < 0) return -1; + symbols__fixup_duplicate(&dso->symbols[map->type]); + symbols__fixup_end(&dso->symbols[map->type]); + if (dso->kernel == DSO_TYPE_GUEST_KERNEL) dso->symtab_type = SYMTAB__GUEST_KALLSYMS; else @@ -1094,8 +1172,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, if (dso->has_build_id) { u8 build_id[BUILD_ID_SIZE]; - if (elf_read_build_id(elf, build_id, - BUILD_ID_SIZE) != BUILD_ID_SIZE) + if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) goto out_elf_end; if (!dso__build_id_equal(dso, build_id)) @@ -1113,6 +1190,8 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, } opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); + if (opdshdr.sh_type != SHT_PROGBITS) + opdsec = NULL; if (opdsec) opddata = elf_rawdata(opdsec, NULL); @@ -1278,6 +1357,7 @@ new_symbol: * For misannotated, zeroed, ASM function sizes. */ if (nr > 0) { + symbols__fixup_duplicate(&dso->symbols[map->type]); symbols__fixup_end(&dso->symbols[map->type]); if (kmap) { /* @@ -1364,8 +1444,8 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) ptr = data->d_buf; while (ptr < (data->d_buf + data->d_size)) { GElf_Nhdr *nhdr = ptr; - int namesz = NOTE_ALIGN(nhdr->n_namesz), - descsz = NOTE_ALIGN(nhdr->n_descsz); + size_t namesz = NOTE_ALIGN(nhdr->n_namesz), + descsz = NOTE_ALIGN(nhdr->n_descsz); const char *name; ptr += sizeof(*nhdr); @@ -1374,8 +1454,10 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) if (nhdr->n_type == NT_GNU_BUILD_ID && nhdr->n_namesz == sizeof("GNU")) { if (memcmp(name, "GNU", sizeof("GNU")) == 0) { - memcpy(bf, ptr, BUILD_ID_SIZE); - err = BUILD_ID_SIZE; + size_t sz = min(size, descsz); + memcpy(bf, ptr, sz); + memset(bf + sz, 0, size - sz); + err = descsz; break; } } @@ -1427,7 +1509,7 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) while (1) { char bf[BUFSIZ]; GElf_Nhdr nhdr; - int namesz, descsz; + size_t namesz, descsz; if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) break; @@ -1436,15 +1518,16 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) descsz = NOTE_ALIGN(nhdr.n_descsz); if (nhdr.n_type == NT_GNU_BUILD_ID && nhdr.n_namesz == sizeof("GNU")) { - if (read(fd, bf, namesz) != namesz) + if (read(fd, bf, namesz) != (ssize_t)namesz) break; if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { - if (read(fd, build_id, - BUILD_ID_SIZE) == BUILD_ID_SIZE) { + size_t sz = min(descsz, size); + if (read(fd, build_id, sz) == (ssize_t)sz) { + memset(build_id + sz, 0, size - sz); err = 0; break; } - } else if (read(fd, bf, descsz) != descsz) + } else if (read(fd, bf, descsz) != (ssize_t)descsz) break; } else { int n = namesz + descsz; diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index bfbf95bcc60..b07b0410463 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -10,6 +10,7 @@ struct perf_evlist; struct perf_evsel; +struct perf_session; struct sym_entry { struct rb_node rb_node; @@ -38,6 +39,7 @@ struct perf_top { u64 kernel_samples, us_samples; u64 exact_samples; u64 guest_us_samples, guest_kernel_samples; + u64 total_lost_warned; int print_entries, count_filter, delay_secs; int display_weighted, freq, rb_entries; pid_t target_pid, target_tid; @@ -45,6 +47,7 @@ struct perf_top { const char *cpu_list; struct sym_entry *sym_filter_entry; struct perf_evsel *sym_evsel; + struct perf_session *session; }; size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c index 88403cf8396..9b6b43b32ac 100644 --- a/tools/perf/util/ui/browsers/top.c +++ b/tools/perf/util/ui/browsers/top.c @@ -11,10 +11,12 @@ #include "../helpline.h" #include "../libslang.h" #include "../util.h" +#include "../ui.h" #include "../../evlist.h" #include "../../hist.h" #include "../../sort.h" #include "../../symbol.h" +#include "../../session.h" #include "../../top.h" struct perf_top_browser { @@ -43,10 +45,10 @@ static void perf_top_browser__write(struct ui_browser *browser, void *entry, int if (top->evlist->nr_entries == 1 || !top->display_weighted) { slsmg_printf("%20.2f ", syme->weight); - width -= 24; + width -= 21; } else { slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); - width -= 23; + width -= 20; } slsmg_printf("%4.1f%%", pcnt); @@ -143,6 +145,25 @@ do_annotation: symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); } +static void perf_top_browser__warn_lost(struct perf_top_browser *browser) +{ + struct perf_top *top = browser->b.priv; + char msg[128]; + int len; + + top->total_lost_warned = top->session->hists.stats.total_lost; + pthread_mutex_lock(&ui__lock); + ui_browser__set_color(&browser->b, HE_COLORSET_TOP); + len = snprintf(msg, sizeof(msg), + " WARNING: LOST %" PRIu64 " events, Check IO/CPU overload", + top->total_lost_warned); + if (len > browser->b.width) + len = browser->b.width; + SLsmg_gotorc(0, browser->b.width - len); + slsmg_write_nstring(msg, len); + pthread_mutex_unlock(&ui__lock); +} + static int perf_top_browser__run(struct perf_top_browser *browser) { int key; @@ -174,6 +195,9 @@ static int perf_top_browser__run(struct perf_top_browser *browser) ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); SLsmg_gotorc(0, 0); slsmg_write_nstring(title, browser->b.width); + + if (top->total_lost_warned != top->session->hists.stats.total_lost) + perf_top_browser__warn_lost(browser); break; case 'a': case NEWT_KEY_RIGHT: |