Loading tools/perf/Makefile +10 −2 Original line number Diff line number Diff line Loading @@ -506,8 +506,8 @@ PERFLIBS = $(LIB_FILE) -include config.mak ifndef NO_DWARF ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/libdw-dev); ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo '\#include <version.h>'; echo '\#ifndef _ELFUTILS_PREREQ'; echo '\#error'; echo '\#endif'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); NO_DWARF := 1 endif # Dwarf support endif # NO_DWARF Loading Loading @@ -560,6 +560,8 @@ ifneq ($(shell sh -c "(echo '\#include <newt.h>'; echo 'int main(void) { newtIni msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); BASIC_CFLAGS += -DNO_NEWT_SUPPORT else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt LIB_OBJS += $(OUTPUT)util/newt.o endif Loading Loading @@ -592,6 +594,9 @@ endif ifdef NO_DEMANGLE BASIC_CFLAGS += -DNO_DEMANGLE else ifdef HAVE_CPLUS_DEMANGLE EXTLIBS += -liberty BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE else has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd "$(QUIET_STDERR)" && echo y") Loading Loading @@ -945,6 +950,9 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< $(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< Loading tools/perf/builtin-annotate.c +2 −196 Original line number Diff line number Diff line Loading @@ -34,68 +34,8 @@ static bool full_paths; static bool print_line; struct sym_hist { u64 sum; u64 ip[0]; }; struct sym_ext { struct rb_node node; double percent; char *path; }; struct sym_priv { struct sym_hist *hist; struct sym_ext *ext; }; static const char *sym_hist_filter; static int sym__alloc_hist(struct symbol *self) { struct sym_priv *priv = symbol__priv(self); const int size = (sizeof(*priv->hist) + (self->end - self->start) * sizeof(u64)); priv->hist = zalloc(size); return priv->hist == NULL ? -1 : 0; } /* * collect histogram counts */ static int annotate__hist_hit(struct hist_entry *he, u64 ip) { unsigned int sym_size, offset; struct symbol *sym = he->ms.sym; struct sym_priv *priv; struct sym_hist *h; if (!sym || !he->ms.map) return 0; priv = symbol__priv(sym); if (priv->hist == NULL && sym__alloc_hist(sym) < 0) return -ENOMEM; sym_size = sym->end - sym->start; offset = ip - sym->start; pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); if (offset >= sym_size) return 0; h = priv->hist; h->sum++; h->ip[offset]++; pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); return 0; } static int hists__add_entry(struct hists *self, struct addr_location *al) { struct hist_entry *he; Loading @@ -115,7 +55,7 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) if (he == NULL) return -ENOMEM; return annotate__hist_hit(he, al->addr); return hist_entry__inc_addr_samples(he, al->addr); } static int process_sample_event(event_t *event, struct perf_session *session) Loading @@ -140,101 +80,6 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } struct objdump_line { struct list_head node; s64 offset; char *line; }; static struct objdump_line *objdump_line__new(s64 offset, char *line) { struct objdump_line *self = malloc(sizeof(*self)); if (self != NULL) { self->offset = offset; self->line = line; } return self; } static void objdump_line__free(struct objdump_line *self) { free(self->line); free(self); } static void objdump__add_line(struct list_head *head, struct objdump_line *line) { list_add_tail(&line->node, head); } static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, struct objdump_line *pos) { list_for_each_entry_continue(pos, head, node) if (pos->offset >= 0) return pos; return NULL; } static int parse_line(FILE *file, struct hist_entry *he, struct list_head *head) { struct symbol *sym = he->ms.sym; struct objdump_line *objdump_line; char *line = NULL, *tmp, *tmp2; size_t line_len; s64 line_ip, offset = -1; char *c; if (getline(&line, &line_len, file) < 0) return -1; if (!line) return -1; c = strchr(line, '\n'); if (c) *c = 0; line_ip = -1; /* * Strip leading spaces: */ tmp = line; while (*tmp) { if (*tmp != ' ') break; tmp++; } if (*tmp) { /* * Parse hexa addresses followed by ':' */ line_ip = strtoull(tmp, &tmp2, 16); if (*tmp2 != ':') line_ip = -1; } if (line_ip != -1) { u64 start = map__rip_2objdump(he->ms.map, sym->start); offset = line_ip - start; } objdump_line = objdump_line__new(offset, line); if (objdump_line == NULL) { free(line); return -1; } objdump__add_line(head, objdump_line); return 0; } static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) Loading Loading @@ -439,28 +284,12 @@ static void annotate_sym(struct hist_entry *he) struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; char command[PATH_MAX*2]; FILE *file; LIST_HEAD(head); struct objdump_line *pos, *n; if (!filename) if (hist_entry__annotate(he, &head) < 0) return; if (dso->origin == DSO__ORIG_KERNEL) { if (dso->annotate_warned) return; dso->annotate_warned = 1; pr_err("Can't annotate %s: No vmlinux file was found in the " "path:\n", sym->name); vmlinux_path__fprintf(stderr); return; } pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); if (full_paths) d_filename = filename; else Loading @@ -477,29 +306,6 @@ static void annotate_sym(struct hist_entry *he) printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); if (verbose >= 2) printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), filename, filename); if (verbose >= 3) printf("doing: %s\n", command); file = popen(command, "r"); if (!file) return; while (!feof(file)) { if (parse_line(file, he, &head) < 0) break; } pclose(file); if (verbose) hist_entry__print_hits(he); Loading tools/perf/builtin-report.c +19 −5 Original line number Diff line number Diff line Loading @@ -106,8 +106,18 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (he == NULL) goto out_free_syms; err = 0; if (symbol_conf.use_callchain) if (symbol_conf.use_callchain) { err = append_chain(he->callchain, data->callchain, syms); if (err) goto out_free_syms; } /* * Only in the newt browser we are doing integrated annotation, * so we don't allocated the extra space needed because the stdio * code will not use it. */ if (use_browser) err = hist_entry__inc_addr_samples(he, al->addr); out_free_syms: free(syms); return err; Loading Loading @@ -301,10 +311,7 @@ static int __cmd_report(void) hists__collapse_resort(hists); hists__output_resort(hists); if (use_browser) perf_session__browse_hists(&hists->entries, hists->nr_entries, hists->stats.total, help, input_name); hists__browse(hists, help, input_name); else { if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) Loading Loading @@ -461,6 +468,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) if (strcmp(input_name, "-") != 0) setup_browser(); /* * Only in the newt browser we are doing integrated annotation, * so don't allocate extra space that won't be used in the stdio * implementation. */ if (use_browser) symbol_conf.priv_size = sizeof(struct sym_priv); if (symbol__init() < 0) return -1; Loading tools/perf/util/hist.c +243 −0 Original line number Diff line number Diff line Loading @@ -784,3 +784,246 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, return ret; } enum hist_filter { HIST_FILTER__DSO, HIST_FILTER__THREAD, }; void hists__filter_by_dso(struct hists *self, const struct dso *dso) { struct rb_node *nd; self->nr_entries = self->stats.total = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (symbol_conf.exclude_other && !h->parent) continue; if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { h->filtered |= (1 << HIST_FILTER__DSO); continue; } h->filtered &= ~(1 << HIST_FILTER__DSO); if (!h->filtered) { ++self->nr_entries; self->stats.total += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; } } } void hists__filter_by_thread(struct hists *self, const struct thread *thread) { struct rb_node *nd; self->nr_entries = self->stats.total = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (thread != NULL && h->thread != thread) { h->filtered |= (1 << HIST_FILTER__THREAD); continue; } h->filtered &= ~(1 << HIST_FILTER__THREAD); if (!h->filtered) { ++self->nr_entries; self->stats.total += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; } } } static int symbol__alloc_hist(struct symbol *self) { struct sym_priv *priv = symbol__priv(self); const int size = (sizeof(*priv->hist) + (self->end - self->start) * sizeof(u64)); priv->hist = zalloc(size); return priv->hist == NULL ? -1 : 0; } int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) { unsigned int sym_size, offset; struct symbol *sym = self->ms.sym; struct sym_priv *priv; struct sym_hist *h; if (!sym || !self->ms.map) return 0; priv = symbol__priv(sym); if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) return -ENOMEM; sym_size = sym->end - sym->start; offset = ip - sym->start; pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); if (offset >= sym_size) return 0; h = priv->hist; h->sum++; h->ip[offset]++; pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); return 0; } static struct objdump_line *objdump_line__new(s64 offset, char *line) { struct objdump_line *self = malloc(sizeof(*self)); if (self != NULL) { self->offset = offset; self->line = line; } return self; } void objdump_line__free(struct objdump_line *self) { free(self->line); free(self); } static void objdump__add_line(struct list_head *head, struct objdump_line *line) { list_add_tail(&line->node, head); } struct objdump_line *objdump__get_next_ip_line(struct list_head *head, struct objdump_line *pos) { list_for_each_entry_continue(pos, head, node) if (pos->offset >= 0) return pos; return NULL; } static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, struct list_head *head) { struct symbol *sym = self->ms.sym; struct objdump_line *objdump_line; char *line = NULL, *tmp, *tmp2, *c; size_t line_len; s64 line_ip, offset = -1; if (getline(&line, &line_len, file) < 0) return -1; if (!line) return -1; while (line_len != 0 && isspace(line[line_len - 1])) line[--line_len] = '\0'; c = strchr(line, '\n'); if (c) *c = 0; line_ip = -1; /* * Strip leading spaces: */ tmp = line; while (*tmp) { if (*tmp != ' ') break; tmp++; } if (*tmp) { /* * Parse hexa addresses followed by ':' */ line_ip = strtoull(tmp, &tmp2, 16); if (*tmp2 != ':') line_ip = -1; } if (line_ip != -1) { u64 start = map__rip_2objdump(self->ms.map, sym->start); offset = line_ip - start; } objdump_line = objdump_line__new(offset, line); if (objdump_line == NULL) { free(line); return -1; } objdump__add_line(head, objdump_line); return 0; } int hist_entry__annotate(struct hist_entry *self, struct list_head *head) { struct symbol *sym = self->ms.sym; struct map *map = self->ms.map; struct dso *dso = map->dso; const char *filename = dso->long_name; char command[PATH_MAX * 2]; FILE *file; u64 len; if (!filename) return -1; if (dso->origin == DSO__ORIG_KERNEL) { if (dso->annotate_warned) return 0; dso->annotate_warned = 1; pr_err("Can't annotate %s: No vmlinux file was found in the " "path:\n", sym->name); vmlinux_path__fprintf(stderr); return -1; } pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); len = sym->end - sym->start; pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); snprintf(command, sizeof(command), "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), filename, filename); pr_debug("Executing: %s\n", command); file = popen(command, "r"); if (!file) return -1; while (!feof(file)) if (hist_entry__parse_objdump_line(self, file, head) < 0) break; pclose(file); return 0; } tools/perf/util/hist.h +44 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,32 @@ struct addr_location; struct symbol; struct rb_root; struct objdump_line { struct list_head node; s64 offset; char *line; }; void objdump_line__free(struct objdump_line *self); struct objdump_line *objdump__get_next_ip_line(struct list_head *head, struct objdump_line *pos); struct sym_hist { u64 sum; u64 ip[0]; }; struct sym_ext { struct rb_node node; double percent; char *path; }; struct sym_priv { struct sym_hist *hist; struct sym_ext *ext; }; struct events_stats { u64 total; u64 lost; Loading Loading @@ -44,4 +70,22 @@ void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); int hist_entry__annotate(struct hist_entry *self, struct list_head *head); void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); #ifdef NO_NEWT_SUPPORT static inline int hists__browse(struct hists self __used, const char *helpline __used, const char *input_name __used) { return 0; } #else int hists__browse(struct hists *self, const char *helpline, const char *input_name); #endif #endif /* __PERF_HIST_H */ Loading
tools/perf/Makefile +10 −2 Original line number Diff line number Diff line Loading @@ -506,8 +506,8 @@ PERFLIBS = $(LIB_FILE) -include config.mak ifndef NO_DWARF ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/libdw-dev); ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo '\#include <version.h>'; echo '\#ifndef _ELFUTILS_PREREQ'; echo '\#error'; echo '\#endif'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); NO_DWARF := 1 endif # Dwarf support endif # NO_DWARF Loading Loading @@ -560,6 +560,8 @@ ifneq ($(shell sh -c "(echo '\#include <newt.h>'; echo 'int main(void) { newtIni msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); BASIC_CFLAGS += -DNO_NEWT_SUPPORT else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt LIB_OBJS += $(OUTPUT)util/newt.o endif Loading Loading @@ -592,6 +594,9 @@ endif ifdef NO_DEMANGLE BASIC_CFLAGS += -DNO_DEMANGLE else ifdef HAVE_CPLUS_DEMANGLE EXTLIBS += -liberty BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE else has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd "$(QUIET_STDERR)" && echo y") Loading Loading @@ -945,6 +950,9 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< $(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< Loading
tools/perf/builtin-annotate.c +2 −196 Original line number Diff line number Diff line Loading @@ -34,68 +34,8 @@ static bool full_paths; static bool print_line; struct sym_hist { u64 sum; u64 ip[0]; }; struct sym_ext { struct rb_node node; double percent; char *path; }; struct sym_priv { struct sym_hist *hist; struct sym_ext *ext; }; static const char *sym_hist_filter; static int sym__alloc_hist(struct symbol *self) { struct sym_priv *priv = symbol__priv(self); const int size = (sizeof(*priv->hist) + (self->end - self->start) * sizeof(u64)); priv->hist = zalloc(size); return priv->hist == NULL ? -1 : 0; } /* * collect histogram counts */ static int annotate__hist_hit(struct hist_entry *he, u64 ip) { unsigned int sym_size, offset; struct symbol *sym = he->ms.sym; struct sym_priv *priv; struct sym_hist *h; if (!sym || !he->ms.map) return 0; priv = symbol__priv(sym); if (priv->hist == NULL && sym__alloc_hist(sym) < 0) return -ENOMEM; sym_size = sym->end - sym->start; offset = ip - sym->start; pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); if (offset >= sym_size) return 0; h = priv->hist; h->sum++; h->ip[offset]++; pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); return 0; } static int hists__add_entry(struct hists *self, struct addr_location *al) { struct hist_entry *he; Loading @@ -115,7 +55,7 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) if (he == NULL) return -ENOMEM; return annotate__hist_hit(he, al->addr); return hist_entry__inc_addr_samples(he, al->addr); } static int process_sample_event(event_t *event, struct perf_session *session) Loading @@ -140,101 +80,6 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } struct objdump_line { struct list_head node; s64 offset; char *line; }; static struct objdump_line *objdump_line__new(s64 offset, char *line) { struct objdump_line *self = malloc(sizeof(*self)); if (self != NULL) { self->offset = offset; self->line = line; } return self; } static void objdump_line__free(struct objdump_line *self) { free(self->line); free(self); } static void objdump__add_line(struct list_head *head, struct objdump_line *line) { list_add_tail(&line->node, head); } static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, struct objdump_line *pos) { list_for_each_entry_continue(pos, head, node) if (pos->offset >= 0) return pos; return NULL; } static int parse_line(FILE *file, struct hist_entry *he, struct list_head *head) { struct symbol *sym = he->ms.sym; struct objdump_line *objdump_line; char *line = NULL, *tmp, *tmp2; size_t line_len; s64 line_ip, offset = -1; char *c; if (getline(&line, &line_len, file) < 0) return -1; if (!line) return -1; c = strchr(line, '\n'); if (c) *c = 0; line_ip = -1; /* * Strip leading spaces: */ tmp = line; while (*tmp) { if (*tmp != ' ') break; tmp++; } if (*tmp) { /* * Parse hexa addresses followed by ':' */ line_ip = strtoull(tmp, &tmp2, 16); if (*tmp2 != ':') line_ip = -1; } if (line_ip != -1) { u64 start = map__rip_2objdump(he->ms.map, sym->start); offset = line_ip - start; } objdump_line = objdump_line__new(offset, line); if (objdump_line == NULL) { free(line); return -1; } objdump__add_line(head, objdump_line); return 0; } static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) Loading Loading @@ -439,28 +284,12 @@ static void annotate_sym(struct hist_entry *he) struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; char command[PATH_MAX*2]; FILE *file; LIST_HEAD(head); struct objdump_line *pos, *n; if (!filename) if (hist_entry__annotate(he, &head) < 0) return; if (dso->origin == DSO__ORIG_KERNEL) { if (dso->annotate_warned) return; dso->annotate_warned = 1; pr_err("Can't annotate %s: No vmlinux file was found in the " "path:\n", sym->name); vmlinux_path__fprintf(stderr); return; } pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); if (full_paths) d_filename = filename; else Loading @@ -477,29 +306,6 @@ static void annotate_sym(struct hist_entry *he) printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); if (verbose >= 2) printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), filename, filename); if (verbose >= 3) printf("doing: %s\n", command); file = popen(command, "r"); if (!file) return; while (!feof(file)) { if (parse_line(file, he, &head) < 0) break; } pclose(file); if (verbose) hist_entry__print_hits(he); Loading
tools/perf/builtin-report.c +19 −5 Original line number Diff line number Diff line Loading @@ -106,8 +106,18 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (he == NULL) goto out_free_syms; err = 0; if (symbol_conf.use_callchain) if (symbol_conf.use_callchain) { err = append_chain(he->callchain, data->callchain, syms); if (err) goto out_free_syms; } /* * Only in the newt browser we are doing integrated annotation, * so we don't allocated the extra space needed because the stdio * code will not use it. */ if (use_browser) err = hist_entry__inc_addr_samples(he, al->addr); out_free_syms: free(syms); return err; Loading Loading @@ -301,10 +311,7 @@ static int __cmd_report(void) hists__collapse_resort(hists); hists__output_resort(hists); if (use_browser) perf_session__browse_hists(&hists->entries, hists->nr_entries, hists->stats.total, help, input_name); hists__browse(hists, help, input_name); else { if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) Loading Loading @@ -461,6 +468,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) if (strcmp(input_name, "-") != 0) setup_browser(); /* * Only in the newt browser we are doing integrated annotation, * so don't allocate extra space that won't be used in the stdio * implementation. */ if (use_browser) symbol_conf.priv_size = sizeof(struct sym_priv); if (symbol__init() < 0) return -1; Loading
tools/perf/util/hist.c +243 −0 Original line number Diff line number Diff line Loading @@ -784,3 +784,246 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, return ret; } enum hist_filter { HIST_FILTER__DSO, HIST_FILTER__THREAD, }; void hists__filter_by_dso(struct hists *self, const struct dso *dso) { struct rb_node *nd; self->nr_entries = self->stats.total = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (symbol_conf.exclude_other && !h->parent) continue; if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { h->filtered |= (1 << HIST_FILTER__DSO); continue; } h->filtered &= ~(1 << HIST_FILTER__DSO); if (!h->filtered) { ++self->nr_entries; self->stats.total += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; } } } void hists__filter_by_thread(struct hists *self, const struct thread *thread) { struct rb_node *nd; self->nr_entries = self->stats.total = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (thread != NULL && h->thread != thread) { h->filtered |= (1 << HIST_FILTER__THREAD); continue; } h->filtered &= ~(1 << HIST_FILTER__THREAD); if (!h->filtered) { ++self->nr_entries; self->stats.total += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; } } } static int symbol__alloc_hist(struct symbol *self) { struct sym_priv *priv = symbol__priv(self); const int size = (sizeof(*priv->hist) + (self->end - self->start) * sizeof(u64)); priv->hist = zalloc(size); return priv->hist == NULL ? -1 : 0; } int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) { unsigned int sym_size, offset; struct symbol *sym = self->ms.sym; struct sym_priv *priv; struct sym_hist *h; if (!sym || !self->ms.map) return 0; priv = symbol__priv(sym); if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) return -ENOMEM; sym_size = sym->end - sym->start; offset = ip - sym->start; pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); if (offset >= sym_size) return 0; h = priv->hist; h->sum++; h->ip[offset]++; pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); return 0; } static struct objdump_line *objdump_line__new(s64 offset, char *line) { struct objdump_line *self = malloc(sizeof(*self)); if (self != NULL) { self->offset = offset; self->line = line; } return self; } void objdump_line__free(struct objdump_line *self) { free(self->line); free(self); } static void objdump__add_line(struct list_head *head, struct objdump_line *line) { list_add_tail(&line->node, head); } struct objdump_line *objdump__get_next_ip_line(struct list_head *head, struct objdump_line *pos) { list_for_each_entry_continue(pos, head, node) if (pos->offset >= 0) return pos; return NULL; } static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, struct list_head *head) { struct symbol *sym = self->ms.sym; struct objdump_line *objdump_line; char *line = NULL, *tmp, *tmp2, *c; size_t line_len; s64 line_ip, offset = -1; if (getline(&line, &line_len, file) < 0) return -1; if (!line) return -1; while (line_len != 0 && isspace(line[line_len - 1])) line[--line_len] = '\0'; c = strchr(line, '\n'); if (c) *c = 0; line_ip = -1; /* * Strip leading spaces: */ tmp = line; while (*tmp) { if (*tmp != ' ') break; tmp++; } if (*tmp) { /* * Parse hexa addresses followed by ':' */ line_ip = strtoull(tmp, &tmp2, 16); if (*tmp2 != ':') line_ip = -1; } if (line_ip != -1) { u64 start = map__rip_2objdump(self->ms.map, sym->start); offset = line_ip - start; } objdump_line = objdump_line__new(offset, line); if (objdump_line == NULL) { free(line); return -1; } objdump__add_line(head, objdump_line); return 0; } int hist_entry__annotate(struct hist_entry *self, struct list_head *head) { struct symbol *sym = self->ms.sym; struct map *map = self->ms.map; struct dso *dso = map->dso; const char *filename = dso->long_name; char command[PATH_MAX * 2]; FILE *file; u64 len; if (!filename) return -1; if (dso->origin == DSO__ORIG_KERNEL) { if (dso->annotate_warned) return 0; dso->annotate_warned = 1; pr_err("Can't annotate %s: No vmlinux file was found in the " "path:\n", sym->name); vmlinux_path__fprintf(stderr); return -1; } pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); len = sym->end - sym->start; pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); snprintf(command, sizeof(command), "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), filename, filename); pr_debug("Executing: %s\n", command); file = popen(command, "r"); if (!file) return -1; while (!feof(file)) if (hist_entry__parse_objdump_line(self, file, head) < 0) break; pclose(file); return 0; }
tools/perf/util/hist.h +44 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,32 @@ struct addr_location; struct symbol; struct rb_root; struct objdump_line { struct list_head node; s64 offset; char *line; }; void objdump_line__free(struct objdump_line *self); struct objdump_line *objdump__get_next_ip_line(struct list_head *head, struct objdump_line *pos); struct sym_hist { u64 sum; u64 ip[0]; }; struct sym_ext { struct rb_node node; double percent; char *path; }; struct sym_priv { struct sym_hist *hist; struct sym_ext *ext; }; struct events_stats { u64 total; u64 lost; Loading Loading @@ -44,4 +70,22 @@ void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); int hist_entry__annotate(struct hist_entry *self, struct list_head *head); void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); #ifdef NO_NEWT_SUPPORT static inline int hists__browse(struct hists self __used, const char *helpline __used, const char *input_name __used) { return 0; } #else int hists__browse(struct hists *self, const char *helpline, const char *input_name); #endif #endif /* __PERF_HIST_H */