Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jiri Olsa and committed by
Arnaldo Carvalho de Melo
7dbf4dcf 1c4be9ff

+194 -26
+2
tools/perf/Makefile
··· 337 337 LIB_H += util/perf_regs.h 338 338 LIB_H += util/unwind.h 339 339 LIB_H += ui/helpline.h 340 + LIB_H += util/vdso.h 340 341 341 342 LIB_OBJS += $(OUTPUT)util/abspath.o 342 343 LIB_OBJS += $(OUTPUT)util/alias.o ··· 405 404 LIB_OBJS += $(OUTPUT)util/target.o 406 405 LIB_OBJS += $(OUTPUT)util/rblist.o 407 406 LIB_OBJS += $(OUTPUT)util/intlist.o 407 + LIB_OBJS += $(OUTPUT)util/vdso.o 408 408 409 409 LIB_OBJS += $(OUTPUT)ui/helpline.o 410 410 LIB_OBJS += $(OUTPUT)ui/hist.o
+2 -1
tools/perf/builtin-buildid-cache.c
··· 43 43 } 44 44 45 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 48 if (verbose) 48 49 pr_info("Adding %s %s: %s\n", sbuild_id, filename, 49 50 err ? "FAIL" : "Ok");
+49 -21
tools/perf/util/header.c
··· 21 21 #include "debug.h" 22 22 #include "cpumap.h" 23 23 #include "pmu.h" 24 + #include "vdso.h" 24 25 25 26 static bool no_buildid_cache = false; 26 27 ··· 208 207 continue; \ 209 208 else 210 209 210 + static int write_buildid(char *name, size_t name_len, u8 *build_id, 211 + pid_t pid, u16 misc, int fd) 212 + { 213 + int err; 214 + struct build_id_event b; 215 + size_t len; 216 + 217 + len = name_len + 1; 218 + len = PERF_ALIGN(len, NAME_ALIGN); 219 + 220 + memset(&b, 0, sizeof(b)); 221 + memcpy(&b.build_id, build_id, BUILD_ID_SIZE); 222 + b.pid = pid; 223 + b.header.misc = misc; 224 + b.header.size = sizeof(b) + len; 225 + 226 + err = do_write(fd, &b, sizeof(b)); 227 + if (err < 0) 228 + return err; 229 + 230 + return write_padded(fd, name, name_len + 1, len); 231 + } 232 + 211 233 static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, 212 234 u16 misc, int fd) 213 235 { ··· 238 214 239 215 dsos__for_each_with_build_id(pos, head) { 240 216 int err; 241 - struct build_id_event b; 242 - size_t len; 217 + char *name; 218 + size_t name_len; 243 219 244 220 if (!pos->hit) 245 221 continue; 246 - len = pos->long_name_len + 1; 247 - len = PERF_ALIGN(len, NAME_ALIGN); 248 - memset(&b, 0, sizeof(b)); 249 - memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); 250 - b.pid = pid; 251 - b.header.misc = misc; 252 - b.header.size = sizeof(b) + len; 253 - err = do_write(fd, &b, sizeof(b)); 254 - if (err < 0) 255 - return err; 256 - err = write_padded(fd, pos->long_name, 257 - pos->long_name_len + 1, len); 258 - if (err < 0) 222 + 223 + if (is_vdso_map(pos->short_name)) { 224 + name = (char *) VDSO__MAP_NAME; 225 + name_len = sizeof(VDSO__MAP_NAME) + 1; 226 + } else { 227 + name = pos->long_name; 228 + name_len = pos->long_name_len + 1; 229 + } 230 + 231 + err = write_buildid(name, name_len, pos->build_id, 232 + pid, misc, fd); 233 + if (err) 259 234 return err; 260 235 } 261 236 ··· 300 277 } 301 278 302 279 int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, 303 - const char *name, bool is_kallsyms) 280 + const char *name, bool is_kallsyms, bool is_vdso) 304 281 { 305 282 const size_t size = PATH_MAX; 306 283 char *realname, *filename = zalloc(size), 307 284 *linkname = zalloc(size), *targetname; 308 285 int len, err = -1; 286 + bool slash = is_kallsyms || is_vdso; 309 287 310 288 if (is_kallsyms) { 311 289 if (symbol_conf.kptr_restrict) { 312 290 pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); 313 291 return 0; 314 292 } 315 - realname = (char *)name; 293 + realname = (char *) name; 316 294 } else 317 295 realname = realpath(name, NULL); 318 296 ··· 321 297 goto out_free; 322 298 323 299 len = scnprintf(filename, size, "%s%s%s", 324 - debugdir, is_kallsyms ? "/" : "", realname); 300 + debugdir, slash ? "/" : "", 301 + is_vdso ? VDSO__MAP_NAME : realname); 325 302 if (mkdir_p(filename, 0755)) 326 303 goto out_free; 327 304 ··· 358 333 359 334 static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, 360 335 const char *name, const char *debugdir, 361 - bool is_kallsyms) 336 + bool is_kallsyms, bool is_vdso) 362 337 { 363 338 char sbuild_id[BUILD_ID_SIZE * 2 + 1]; 364 339 365 340 build_id__sprintf(build_id, build_id_size, sbuild_id); 366 341 367 - return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); 342 + return build_id_cache__add_s(sbuild_id, debugdir, name, 343 + is_kallsyms, is_vdso); 368 344 } 369 345 370 346 int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) ··· 409 383 static int dso__cache_build_id(struct dso *dso, const char *debugdir) 410 384 { 411 385 bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; 386 + bool is_vdso = is_vdso_map(dso->short_name); 412 387 413 388 return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), 414 - dso->long_name, debugdir, is_kallsyms); 389 + dso->long_name, debugdir, 390 + is_kallsyms, is_vdso); 415 391 } 416 392 417 393 static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
+1 -1
tools/perf/util/header.h
··· 96 96 int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); 97 97 98 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 100 int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); 101 101 102 102 int perf_event__synthesize_attr(struct perf_tool *tool,
+9 -3
tools/perf/util/map.c
··· 9 9 #include "map.h" 10 10 #include "thread.h" 11 11 #include "strlist.h" 12 + #include "vdso.h" 12 13 13 14 const char *map_type__name[MAP__NR_TYPES] = { 14 15 [MAP__FUNCTION] = "Functions", ··· 24 23 static inline int is_no_dso_memory(const char *filename) 25 24 { 26 25 return !strcmp(filename, "[stack]") || 27 - !strcmp(filename, "[vdso]") || 28 26 !strcmp(filename, "[heap]"); 29 27 } 30 28 ··· 52 52 if (self != NULL) { 53 53 char newfilename[PATH_MAX]; 54 54 struct dso *dso; 55 - int anon, no_dso; 55 + int anon, no_dso, vdso; 56 56 57 57 anon = is_anon_memory(filename); 58 + vdso = is_vdso_map(filename); 58 59 no_dso = is_no_dso_memory(filename); 59 60 60 61 if (anon) { ··· 63 62 filename = newfilename; 64 63 } 65 64 66 - dso = __dsos__findnew(dsos__list, filename); 65 + if (vdso) { 66 + pgoff = 0; 67 + dso = vdso__dso_findnew(dsos__list); 68 + } else 69 + dso = __dsos__findnew(dsos__list, filename); 70 + 67 71 if (dso == NULL) 68 72 goto out_delete; 69 73
+2
tools/perf/util/session.c
··· 17 17 #include "event-parse.h" 18 18 #include "perf_regs.h" 19 19 #include "unwind.h" 20 + #include "vdso.h" 20 21 21 22 static int perf_session__open(struct perf_session *self, bool force) 22 23 { ··· 212 211 machine__exit(&self->host_machine); 213 212 close(self->fd); 214 213 free(self); 214 + vdso__exit(); 215 215 } 216 216 217 217 void machine__remove_thread(struct machine *self, struct thread *th)
+111
tools/perf/util/vdso.c
··· 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 + }
+18
tools/perf/util/vdso.h
··· 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__ */