diff options
author | Jiri Olsa <jolsa@redhat.com> | 2012-09-10 12:50:19 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-09-11 11:08:30 -0400 |
commit | 7dbf4dcfe2987c35c2c4675cd7ae1b6006979176 (patch) | |
tree | ce04e8dd07222ed105d32a5ecb7b45f5469c68c4 | |
parent | 1c4be9ff5933e5c0f033ea98169cd89e22c90900 (diff) |
perf tools: Back [vdso] DSO with real data
Storing data for VDSO shared object, because we need it for the post
unwind processing.
The VDSO shared object is same for all process on a running system, so
it makes no difference when we store it inside the tracer - perf.
When [vdso] map memory is hit, we retrieve [vdso] DSO image and store it
into temporary file.
During the build-id processing phase, the [vdso] DSO image is stored in
build-id db, and build-id reference is made inside perf.data. The
build-id vdso file object is called '[vdso]'. We don't use temporary
file name which gets removed when record is finished.
During report phase the vdso build-id object is treated as any other
build-id DSO object.
Adding following API for vdso object:
bool is_vdso_map(const char *filename)
- returns true if the filename matches vdso map name
struct dso *vdso__dso_findnew(struct list_head *head)
- find/create proper vdso DSO object
vdso__exit(void)
- removes temporary VDSO image if there's any
This change makes backtrace dwarf post unwind possible from [vdso] maps.
Following output is current report of [vdso] sample dwarf backtrace:
# Overhead Command Shared Object Symbol
# ........ ....... ................. .............................
#
99.52% ex [vdso] [.] 0x00007fff3ace89af
|
--- 0x7fff3ace89af
Following output is new report of [vdso] sample dwarf backtrace:
# Overhead Command Shared Object Symbol
# ........ ....... ................. .............................
#
99.52% ex [vdso] [.] 0x00000000000009af
|
--- 0x7fff3ace89af
main
__libc_start_main
_start
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1347295819-23177-5-git-send-email-jolsa@redhat.com
[ committer note: s/ALIGN/PERF_ALIGN/g to cope with the android build changes ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/Makefile | 2 | ||||
-rw-r--r-- | tools/perf/builtin-buildid-cache.c | 3 | ||||
-rw-r--r-- | tools/perf/util/header.c | 70 | ||||
-rw-r--r-- | tools/perf/util/header.h | 2 | ||||
-rw-r--r-- | tools/perf/util/map.c | 12 | ||||
-rw-r--r-- | tools/perf/util/session.c | 2 | ||||
-rw-r--r-- | tools/perf/util/vdso.c | 111 | ||||
-rw-r--r-- | tools/perf/util/vdso.h | 18 |
8 files changed, 194 insertions, 26 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 1d2723c01b82..209774bcee2e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -337,6 +337,7 @@ LIB_H += util/intlist.h | |||
337 | LIB_H += util/perf_regs.h | 337 | LIB_H += util/perf_regs.h |
338 | LIB_H += util/unwind.h | 338 | LIB_H += util/unwind.h |
339 | LIB_H += ui/helpline.h | 339 | LIB_H += ui/helpline.h |
340 | LIB_H += util/vdso.h | ||
340 | 341 | ||
341 | LIB_OBJS += $(OUTPUT)util/abspath.o | 342 | LIB_OBJS += $(OUTPUT)util/abspath.o |
342 | LIB_OBJS += $(OUTPUT)util/alias.o | 343 | LIB_OBJS += $(OUTPUT)util/alias.o |
@@ -404,6 +405,7 @@ LIB_OBJS += $(OUTPUT)util/cgroup.o | |||
404 | LIB_OBJS += $(OUTPUT)util/target.o | 405 | LIB_OBJS += $(OUTPUT)util/target.o |
405 | LIB_OBJS += $(OUTPUT)util/rblist.o | 406 | LIB_OBJS += $(OUTPUT)util/rblist.o |
406 | LIB_OBJS += $(OUTPUT)util/intlist.o | 407 | LIB_OBJS += $(OUTPUT)util/intlist.o |
408 | LIB_OBJS += $(OUTPUT)util/vdso.o | ||
407 | 409 | ||
408 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 410 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
409 | LIB_OBJS += $(OUTPUT)ui/hist.o | 411 | LIB_OBJS += $(OUTPUT)ui/hist.o |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 29ad20e67919..995368e84e42 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -43,7 +43,8 @@ static int build_id_cache__add_file(const char *filename, const char *debugdir) | |||
43 | } | 43 | } |
44 | 44 | ||
45 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | 45 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); |
46 | err = build_id_cache__add_s(sbuild_id, debugdir, filename, false); | 46 | err = build_id_cache__add_s(sbuild_id, debugdir, filename, |
47 | false, false); | ||
47 | if (verbose) | 48 | if (verbose) |
48 | pr_info("Adding %s %s: %s\n", sbuild_id, filename, | 49 | pr_info("Adding %s %s: %s\n", sbuild_id, filename, |
49 | err ? "FAIL" : "Ok"); | 50 | err ? "FAIL" : "Ok"); |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 974e7589a6b5..87996cab21d0 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "debug.h" | 21 | #include "debug.h" |
22 | #include "cpumap.h" | 22 | #include "cpumap.h" |
23 | #include "pmu.h" | 23 | #include "pmu.h" |
24 | #include "vdso.h" | ||
24 | 25 | ||
25 | static bool no_buildid_cache = false; | 26 | static bool no_buildid_cache = false; |
26 | 27 | ||
@@ -207,6 +208,29 @@ perf_header__set_cmdline(int argc, const char **argv) | |||
207 | continue; \ | 208 | continue; \ |
208 | else | 209 | else |
209 | 210 | ||
211 | static int write_buildid(char *name, size_t name_len, u8 *build_id, | ||
212 | pid_t pid, u16 misc, int fd) | ||
213 | { | ||
214 | int err; | ||
215 | struct build_id_event b; | ||
216 | size_t len; | ||
217 | |||
218 | len = name_len + 1; | ||
219 | len = PERF_ALIGN(len, NAME_ALIGN); | ||
220 | |||
221 | memset(&b, 0, sizeof(b)); | ||
222 | memcpy(&b.build_id, build_id, BUILD_ID_SIZE); | ||
223 | b.pid = pid; | ||
224 | b.header.misc = misc; | ||
225 | b.header.size = sizeof(b) + len; | ||
226 | |||
227 | err = do_write(fd, &b, sizeof(b)); | ||
228 | if (err < 0) | ||
229 | return err; | ||
230 | |||
231 | return write_padded(fd, name, name_len + 1, len); | ||
232 | } | ||
233 | |||
210 | static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | 234 | static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, |
211 | u16 misc, int fd) | 235 | u16 misc, int fd) |
212 | { | 236 | { |
@@ -214,24 +238,23 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | |||
214 | 238 | ||
215 | dsos__for_each_with_build_id(pos, head) { | 239 | dsos__for_each_with_build_id(pos, head) { |
216 | int err; | 240 | int err; |
217 | struct build_id_event b; | 241 | char *name; |
218 | size_t len; | 242 | size_t name_len; |
219 | 243 | ||
220 | if (!pos->hit) | 244 | if (!pos->hit) |
221 | continue; | 245 | continue; |
222 | len = pos->long_name_len + 1; | 246 | |
223 | len = PERF_ALIGN(len, NAME_ALIGN); | 247 | if (is_vdso_map(pos->short_name)) { |
224 | memset(&b, 0, sizeof(b)); | 248 | name = (char *) VDSO__MAP_NAME; |
225 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | 249 | name_len = sizeof(VDSO__MAP_NAME) + 1; |
226 | b.pid = pid; | 250 | } else { |
227 | b.header.misc = misc; | 251 | name = pos->long_name; |
228 | b.header.size = sizeof(b) + len; | 252 | name_len = pos->long_name_len + 1; |
229 | err = do_write(fd, &b, sizeof(b)); | 253 | } |
230 | if (err < 0) | 254 | |
231 | return err; | 255 | err = write_buildid(name, name_len, pos->build_id, |
232 | err = write_padded(fd, pos->long_name, | 256 | pid, misc, fd); |
233 | pos->long_name_len + 1, len); | 257 | if (err) |
234 | if (err < 0) | ||
235 | return err; | 258 | return err; |
236 | } | 259 | } |
237 | 260 | ||
@@ -277,19 +300,20 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) | |||
277 | } | 300 | } |
278 | 301 | ||
279 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | 302 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
280 | const char *name, bool is_kallsyms) | 303 | const char *name, bool is_kallsyms, bool is_vdso) |
281 | { | 304 | { |
282 | const size_t size = PATH_MAX; | 305 | const size_t size = PATH_MAX; |
283 | char *realname, *filename = zalloc(size), | 306 | char *realname, *filename = zalloc(size), |
284 | *linkname = zalloc(size), *targetname; | 307 | *linkname = zalloc(size), *targetname; |
285 | int len, err = -1; | 308 | int len, err = -1; |
309 | bool slash = is_kallsyms || is_vdso; | ||
286 | 310 | ||
287 | if (is_kallsyms) { | 311 | if (is_kallsyms) { |
288 | if (symbol_conf.kptr_restrict) { | 312 | if (symbol_conf.kptr_restrict) { |
289 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | 313 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); |
290 | return 0; | 314 | return 0; |
291 | } | 315 | } |
292 | realname = (char *)name; | 316 | realname = (char *) name; |
293 | } else | 317 | } else |
294 | realname = realpath(name, NULL); | 318 | realname = realpath(name, NULL); |
295 | 319 | ||
@@ -297,7 +321,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
297 | goto out_free; | 321 | goto out_free; |
298 | 322 | ||
299 | len = scnprintf(filename, size, "%s%s%s", | 323 | len = scnprintf(filename, size, "%s%s%s", |
300 | debugdir, is_kallsyms ? "/" : "", realname); | 324 | debugdir, slash ? "/" : "", |
325 | is_vdso ? VDSO__MAP_NAME : realname); | ||
301 | if (mkdir_p(filename, 0755)) | 326 | if (mkdir_p(filename, 0755)) |
302 | goto out_free; | 327 | goto out_free; |
303 | 328 | ||
@@ -333,13 +358,14 @@ out_free: | |||
333 | 358 | ||
334 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | 359 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, |
335 | const char *name, const char *debugdir, | 360 | const char *name, const char *debugdir, |
336 | bool is_kallsyms) | 361 | bool is_kallsyms, bool is_vdso) |
337 | { | 362 | { |
338 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 363 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
339 | 364 | ||
340 | build_id__sprintf(build_id, build_id_size, sbuild_id); | 365 | build_id__sprintf(build_id, build_id_size, sbuild_id); |
341 | 366 | ||
342 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); | 367 | return build_id_cache__add_s(sbuild_id, debugdir, name, |
368 | is_kallsyms, is_vdso); | ||
343 | } | 369 | } |
344 | 370 | ||
345 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | 371 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) |
@@ -383,9 +409,11 @@ out_free: | |||
383 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) | 409 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) |
384 | { | 410 | { |
385 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | 411 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; |
412 | bool is_vdso = is_vdso_map(dso->short_name); | ||
386 | 413 | ||
387 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), | 414 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), |
388 | dso->long_name, debugdir, is_kallsyms); | 415 | dso->long_name, debugdir, |
416 | is_kallsyms, is_vdso); | ||
389 | } | 417 | } |
390 | 418 | ||
391 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | 419 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 9d5eedceda72..209dad4fee2b 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -96,7 +96,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
96 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); | 96 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); |
97 | 97 | ||
98 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | 98 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
99 | const char *name, bool is_kallsyms); | 99 | const char *name, bool is_kallsyms, bool is_vdso); |
100 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | 100 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); |
101 | 101 | ||
102 | int perf_event__synthesize_attr(struct perf_tool *tool, | 102 | int perf_event__synthesize_attr(struct perf_tool *tool, |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 7d37159c1e99..b442ee49452b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "map.h" | 9 | #include "map.h" |
10 | #include "thread.h" | 10 | #include "thread.h" |
11 | #include "strlist.h" | 11 | #include "strlist.h" |
12 | #include "vdso.h" | ||
12 | 13 | ||
13 | const char *map_type__name[MAP__NR_TYPES] = { | 14 | const char *map_type__name[MAP__NR_TYPES] = { |
14 | [MAP__FUNCTION] = "Functions", | 15 | [MAP__FUNCTION] = "Functions", |
@@ -23,7 +24,6 @@ static inline int is_anon_memory(const char *filename) | |||
23 | static inline int is_no_dso_memory(const char *filename) | 24 | static inline int is_no_dso_memory(const char *filename) |
24 | { | 25 | { |
25 | return !strcmp(filename, "[stack]") || | 26 | return !strcmp(filename, "[stack]") || |
26 | !strcmp(filename, "[vdso]") || | ||
27 | !strcmp(filename, "[heap]"); | 27 | !strcmp(filename, "[heap]"); |
28 | } | 28 | } |
29 | 29 | ||
@@ -52,9 +52,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
52 | if (self != NULL) { | 52 | if (self != NULL) { |
53 | char newfilename[PATH_MAX]; | 53 | char newfilename[PATH_MAX]; |
54 | struct dso *dso; | 54 | struct dso *dso; |
55 | int anon, no_dso; | 55 | int anon, no_dso, vdso; |
56 | 56 | ||
57 | anon = is_anon_memory(filename); | 57 | anon = is_anon_memory(filename); |
58 | vdso = is_vdso_map(filename); | ||
58 | no_dso = is_no_dso_memory(filename); | 59 | no_dso = is_no_dso_memory(filename); |
59 | 60 | ||
60 | if (anon) { | 61 | if (anon) { |
@@ -62,7 +63,12 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
62 | filename = newfilename; | 63 | filename = newfilename; |
63 | } | 64 | } |
64 | 65 | ||
65 | dso = __dsos__findnew(dsos__list, filename); | 66 | if (vdso) { |
67 | pgoff = 0; | ||
68 | dso = vdso__dso_findnew(dsos__list); | ||
69 | } else | ||
70 | dso = __dsos__findnew(dsos__list, filename); | ||
71 | |||
66 | if (dso == NULL) | 72 | if (dso == NULL) |
67 | goto out_delete; | 73 | goto out_delete; |
68 | 74 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0ecd62be2091..e0fd6c71cc5f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "event-parse.h" | 17 | #include "event-parse.h" |
18 | #include "perf_regs.h" | 18 | #include "perf_regs.h" |
19 | #include "unwind.h" | 19 | #include "unwind.h" |
20 | #include "vdso.h" | ||
20 | 21 | ||
21 | static int perf_session__open(struct perf_session *self, bool force) | 22 | static int perf_session__open(struct perf_session *self, bool force) |
22 | { | 23 | { |
@@ -211,6 +212,7 @@ void perf_session__delete(struct perf_session *self) | |||
211 | machine__exit(&self->host_machine); | 212 | machine__exit(&self->host_machine); |
212 | close(self->fd); | 213 | close(self->fd); |
213 | free(self); | 214 | free(self); |
215 | vdso__exit(); | ||
214 | } | 216 | } |
215 | 217 | ||
216 | void machine__remove_thread(struct machine *self, struct thread *th) | 218 | void machine__remove_thread(struct machine *self, struct thread *th) |
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c new file mode 100644 index 000000000000..e60951fcdb12 --- /dev/null +++ b/tools/perf/util/vdso.c | |||
@@ -0,0 +1,111 @@ | |||
1 | |||
2 | #include <unistd.h> | ||
3 | #include <stdio.h> | ||
4 | #include <string.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <sys/stat.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <linux/kernel.h> | ||
10 | |||
11 | #include "vdso.h" | ||
12 | #include "util.h" | ||
13 | #include "symbol.h" | ||
14 | #include "linux/string.h" | ||
15 | |||
16 | static bool vdso_found; | ||
17 | static char vdso_file[] = "/tmp/perf-vdso.so-XXXXXX"; | ||
18 | |||
19 | static int find_vdso_map(void **start, void **end) | ||
20 | { | ||
21 | FILE *maps; | ||
22 | char line[128]; | ||
23 | int found = 0; | ||
24 | |||
25 | maps = fopen("/proc/self/maps", "r"); | ||
26 | if (!maps) { | ||
27 | pr_err("vdso: cannot open maps\n"); | ||
28 | return -1; | ||
29 | } | ||
30 | |||
31 | while (!found && fgets(line, sizeof(line), maps)) { | ||
32 | int m = -1; | ||
33 | |||
34 | /* We care only about private r-x mappings. */ | ||
35 | if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n", | ||
36 | start, end, &m)) | ||
37 | continue; | ||
38 | if (m < 0) | ||
39 | continue; | ||
40 | |||
41 | if (!strncmp(&line[m], VDSO__MAP_NAME, | ||
42 | sizeof(VDSO__MAP_NAME) - 1)) | ||
43 | found = 1; | ||
44 | } | ||
45 | |||
46 | fclose(maps); | ||
47 | return !found; | ||
48 | } | ||
49 | |||
50 | static char *get_file(void) | ||
51 | { | ||
52 | char *vdso = NULL; | ||
53 | char *buf = NULL; | ||
54 | void *start, *end; | ||
55 | size_t size; | ||
56 | int fd; | ||
57 | |||
58 | if (vdso_found) | ||
59 | return vdso_file; | ||
60 | |||
61 | if (find_vdso_map(&start, &end)) | ||
62 | return NULL; | ||
63 | |||
64 | size = end - start; | ||
65 | |||
66 | buf = memdup(start, size); | ||
67 | if (!buf) | ||
68 | return NULL; | ||
69 | |||
70 | fd = mkstemp(vdso_file); | ||
71 | if (fd < 0) | ||
72 | goto out; | ||
73 | |||
74 | if (size == (size_t) write(fd, buf, size)) | ||
75 | vdso = vdso_file; | ||
76 | |||
77 | close(fd); | ||
78 | |||
79 | out: | ||
80 | free(buf); | ||
81 | |||
82 | vdso_found = (vdso != NULL); | ||
83 | return vdso; | ||
84 | } | ||
85 | |||
86 | void vdso__exit(void) | ||
87 | { | ||
88 | if (vdso_found) | ||
89 | unlink(vdso_file); | ||
90 | } | ||
91 | |||
92 | struct dso *vdso__dso_findnew(struct list_head *head) | ||
93 | { | ||
94 | struct dso *dso = dsos__find(head, VDSO__MAP_NAME); | ||
95 | |||
96 | if (!dso) { | ||
97 | char *file; | ||
98 | |||
99 | file = get_file(); | ||
100 | if (!file) | ||
101 | return NULL; | ||
102 | |||
103 | dso = dso__new(VDSO__MAP_NAME); | ||
104 | if (dso != NULL) { | ||
105 | dsos__add(head, dso); | ||
106 | dso__set_long_name(dso, file); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | return dso; | ||
111 | } | ||
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h new file mode 100644 index 000000000000..0f76e7caf6f8 --- /dev/null +++ b/tools/perf/util/vdso.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef __PERF_VDSO__ | ||
2 | #define __PERF_VDSO__ | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include <string.h> | ||
6 | #include <stdbool.h> | ||
7 | |||
8 | #define VDSO__MAP_NAME "[vdso]" | ||
9 | |||
10 | static inline bool is_vdso_map(const char *filename) | ||
11 | { | ||
12 | return !strcmp(filename, VDSO__MAP_NAME); | ||
13 | } | ||
14 | |||
15 | struct dso *vdso__dso_findnew(struct list_head *head); | ||
16 | void vdso__exit(void); | ||
17 | |||
18 | #endif /* __PERF_VDSO__ */ | ||