diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2009-11-04 15:50:43 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-11-08 04:44:36 -0500 |
commit | 8d06367fa79c053a4a56a2ce0bb9e840f5da1236 (patch) | |
tree | 8ea057102e390c8411b77aeab7ea9c06ec0e0561 /tools/perf/util/symbol.c | |
parent | 2643ce11457a99a85c5bed8dd631e35968e6ca5a (diff) |
perf symbols: Use the buildids if present
With this change 'perf record' will intercept PERF_RECORD_MMAP
calls, creating a linked list of DSOs, then when the session
finishes, it will traverse this list and read the buildids,
stashing them at the end of the file and will set up a new
feature bit in the header bitmask.
'perf report' will then notice this feature and populate the
'dsos' list and set the build ids.
When reading the symtabs it will refuse to load from a file that
doesn't have the same build id. This improves the
reliability of the profiler output, as symbols and profiling
data is more guaranteed to match.
Example:
[root@doppio ~]# perf report | head
/home/acme/bin/perf with build id b1ea544ac3746e7538972548a09aadecc5753868 not found, continuing without symbols
# Samples: 2621434559
#
# Overhead Command Shared Object Symbol
# ........ ............... ............................. ......
#
7.91% init [kernel] [k] read_hpet
7.64% init [kernel] [k] mwait_idle_with_hints
7.60% swapper [kernel] [k] read_hpet
7.60% swapper [kernel] [k] mwait_idle_with_hints
3.65% init [kernel] [k] 0xffffffffa02339d9
[root@doppio ~]#
In this case the 'perf' binary was an older one, vanished,
so its symbols probably wouldn't match or would cause subtly
different (and misleading) output.
Next patches will support the kernel as well, reading the build
id notes for it and the modules from /sys.
Another patch should also introduce a new plumbing command:
'perf list-buildids'
that will then be used in porcelain that is distro specific to
fetch -debuginfo packages where such buildids are present. This
will in turn allow for one to run 'perf record' in one machine
and 'perf report' in another.
Future work on having the buildid sent directly from the kernel
in the PERF_RECORD_MMAP event is needed to close races, as the
DSO can be changed during a 'perf record' session, but this
patch at least helps with non-corner cases and current/older
kernels.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: K. Prasad <prasad@linux.vnet.ibm.com>
Cc: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Roland McGrath <roland@redhat.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <1257367843-26224-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 78 |
1 files changed, 56 insertions, 22 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e7c7cdb851c2..a2e95ce1f223 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -121,7 +121,8 @@ struct dso *dso__new(const char *name) | |||
121 | self->find_symbol = dso__find_symbol; | 121 | self->find_symbol = dso__find_symbol; |
122 | self->slen_calculated = 0; | 122 | self->slen_calculated = 0; |
123 | self->origin = DSO__ORIG_NOT_FOUND; | 123 | self->origin = DSO__ORIG_NOT_FOUND; |
124 | self->loaded = false; | 124 | self->loaded = 0; |
125 | self->has_build_id = 0; | ||
125 | } | 126 | } |
126 | 127 | ||
127 | return self; | 128 | return self; |
@@ -148,6 +149,12 @@ void dso__delete(struct dso *self) | |||
148 | free(self); | 149 | free(self); |
149 | } | 150 | } |
150 | 151 | ||
152 | void dso__set_build_id(struct dso *self, void *build_id) | ||
153 | { | ||
154 | memcpy(self->build_id, build_id, sizeof(self->build_id)); | ||
155 | self->has_build_id = 1; | ||
156 | } | ||
157 | |||
151 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | 158 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) |
152 | { | 159 | { |
153 | struct rb_node **p = &self->syms.rb_node; | 160 | struct rb_node **p = &self->syms.rb_node; |
@@ -190,11 +197,30 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip) | |||
190 | return NULL; | 197 | return NULL; |
191 | } | 198 | } |
192 | 199 | ||
193 | size_t dso__fprintf(struct dso *self, FILE *fp) | 200 | int build_id__sprintf(u8 *self, int len, char *bf) |
194 | { | 201 | { |
195 | size_t ret = fprintf(fp, "dso: %s\n", self->short_name); | 202 | char *bid = bf; |
203 | u8 *raw = self; | ||
204 | int i; | ||
196 | 205 | ||
206 | for (i = 0; i < len; ++i) { | ||
207 | sprintf(bid, "%02x", *raw); | ||
208 | ++raw; | ||
209 | bid += 2; | ||
210 | } | ||
211 | |||
212 | return raw - self; | ||
213 | } | ||
214 | |||
215 | size_t dso__fprintf(struct dso *self, FILE *fp) | ||
216 | { | ||
217 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
197 | struct rb_node *nd; | 218 | struct rb_node *nd; |
219 | size_t ret; | ||
220 | |||
221 | build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id); | ||
222 | ret = fprintf(fp, "dso: %s (%s)\n", self->short_name, sbuild_id); | ||
223 | |||
198 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | 224 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { |
199 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 225 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
200 | ret += symbol__fprintf(pos, fp); | 226 | ret += symbol__fprintf(pos, fp); |
@@ -825,8 +851,6 @@ out_close: | |||
825 | return err; | 851 | return err; |
826 | } | 852 | } |
827 | 853 | ||
828 | #define BUILD_ID_SIZE 20 | ||
829 | |||
830 | int filename__read_build_id(const char *filename, void *bf, size_t size) | 854 | int filename__read_build_id(const char *filename, void *bf, size_t size) |
831 | { | 855 | { |
832 | int fd, err = -1; | 856 | int fd, err = -1; |
@@ -845,7 +869,7 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
845 | 869 | ||
846 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 870 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
847 | if (elf == NULL) { | 871 | if (elf == NULL) { |
848 | pr_err("%s: cannot read %s ELF file.\n", __func__, filename); | 872 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); |
849 | goto out_close; | 873 | goto out_close; |
850 | } | 874 | } |
851 | 875 | ||
@@ -874,9 +898,9 @@ out: | |||
874 | 898 | ||
875 | static char *dso__read_build_id(struct dso *self) | 899 | static char *dso__read_build_id(struct dso *self) |
876 | { | 900 | { |
877 | int i, len; | 901 | int len; |
878 | char *build_id = NULL, *bid; | 902 | char *build_id = NULL; |
879 | unsigned char rawbf[BUILD_ID_SIZE], *raw; | 903 | unsigned char rawbf[BUILD_ID_SIZE]; |
880 | 904 | ||
881 | len = filename__read_build_id(self->long_name, rawbf, sizeof(rawbf)); | 905 | len = filename__read_build_id(self->long_name, rawbf, sizeof(rawbf)); |
882 | if (len < 0) | 906 | if (len < 0) |
@@ -885,15 +909,8 @@ static char *dso__read_build_id(struct dso *self) | |||
885 | build_id = malloc(len * 2 + 1); | 909 | build_id = malloc(len * 2 + 1); |
886 | if (build_id == NULL) | 910 | if (build_id == NULL) |
887 | goto out; | 911 | goto out; |
888 | bid = build_id; | ||
889 | 912 | ||
890 | raw = rawbf; | 913 | build_id__sprintf(rawbf, len, build_id); |
891 | for (i = 0; i < len; ++i) { | ||
892 | sprintf(bid, "%02x", *raw); | ||
893 | ++raw; | ||
894 | bid += 2; | ||
895 | } | ||
896 | pr_debug2("%s(%s): %s\n", __func__, self->long_name, build_id); | ||
897 | out: | 914 | out: |
898 | return build_id; | 915 | return build_id; |
899 | } | 916 | } |
@@ -922,7 +939,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
922 | int ret = -1; | 939 | int ret = -1; |
923 | int fd; | 940 | int fd; |
924 | 941 | ||
925 | self->loaded = true; | 942 | self->loaded = 1; |
926 | 943 | ||
927 | if (!name) | 944 | if (!name) |
928 | return -1; | 945 | return -1; |
@@ -940,6 +957,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
940 | 957 | ||
941 | more: | 958 | more: |
942 | do { | 959 | do { |
960 | int berr = 0; | ||
961 | |||
943 | self->origin++; | 962 | self->origin++; |
944 | switch (self->origin) { | 963 | switch (self->origin) { |
945 | case DSO__ORIG_FEDORA: | 964 | case DSO__ORIG_FEDORA: |
@@ -956,8 +975,7 @@ more: | |||
956 | snprintf(name, size, | 975 | snprintf(name, size, |
957 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 976 | "/usr/lib/debug/.build-id/%.2s/%s.debug", |
958 | build_id, build_id + 2); | 977 | build_id, build_id + 2); |
959 | free(build_id); | 978 | goto compare_build_id; |
960 | break; | ||
961 | } | 979 | } |
962 | self->origin++; | 980 | self->origin++; |
963 | /* Fall thru */ | 981 | /* Fall thru */ |
@@ -969,6 +987,22 @@ more: | |||
969 | goto out; | 987 | goto out; |
970 | } | 988 | } |
971 | 989 | ||
990 | if (self->has_build_id) { | ||
991 | bool match; | ||
992 | build_id = malloc(BUILD_ID_SIZE); | ||
993 | if (build_id == NULL) | ||
994 | goto more; | ||
995 | berr = filename__read_build_id(name, build_id, | ||
996 | BUILD_ID_SIZE); | ||
997 | compare_build_id: | ||
998 | match = berr > 0 && memcmp(build_id, self->build_id, | ||
999 | sizeof(self->build_id)) == 0; | ||
1000 | free(build_id); | ||
1001 | build_id = NULL; | ||
1002 | if (!match) | ||
1003 | goto more; | ||
1004 | } | ||
1005 | |||
972 | fd = open(name, O_RDONLY); | 1006 | fd = open(name, O_RDONLY); |
973 | } while (fd < 0); | 1007 | } while (fd < 0); |
974 | 1008 | ||
@@ -1034,7 +1068,7 @@ static int dso__load_module_sym(struct dso *self, struct map *map, | |||
1034 | { | 1068 | { |
1035 | int err = 0, fd = open(self->long_name, O_RDONLY); | 1069 | int err = 0, fd = open(self->long_name, O_RDONLY); |
1036 | 1070 | ||
1037 | self->loaded = true; | 1071 | self->loaded = 1; |
1038 | 1072 | ||
1039 | if (fd < 0) { | 1073 | if (fd < 0) { |
1040 | pr_err("%s: cannot open %s\n", __func__, self->long_name); | 1074 | pr_err("%s: cannot open %s\n", __func__, self->long_name); |
@@ -1225,7 +1259,7 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1225 | { | 1259 | { |
1226 | int err, fd = open(vmlinux, O_RDONLY); | 1260 | int err, fd = open(vmlinux, O_RDONLY); |
1227 | 1261 | ||
1228 | self->loaded = true; | 1262 | self->loaded = 1; |
1229 | 1263 | ||
1230 | if (fd < 0) | 1264 | if (fd < 0) |
1231 | return -1; | 1265 | return -1; |