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>
This commit is contained in:
parent
1c4be9ff59
commit
7dbf4dcfe2
8 changed files with 194 additions and 26 deletions
|
@ -337,6 +337,7 @@ LIB_H += util/intlist.h
|
||||||
LIB_H += util/perf_regs.h
|
LIB_H += util/perf_regs.h
|
||||||
LIB_H += util/unwind.h
|
LIB_H += util/unwind.h
|
||||||
LIB_H += ui/helpline.h
|
LIB_H += ui/helpline.h
|
||||||
|
LIB_H += util/vdso.h
|
||||||
|
|
||||||
LIB_OBJS += $(OUTPUT)util/abspath.o
|
LIB_OBJS += $(OUTPUT)util/abspath.o
|
||||||
LIB_OBJS += $(OUTPUT)util/alias.o
|
LIB_OBJS += $(OUTPUT)util/alias.o
|
||||||
|
@ -404,6 +405,7 @@ LIB_OBJS += $(OUTPUT)util/cgroup.o
|
||||||
LIB_OBJS += $(OUTPUT)util/target.o
|
LIB_OBJS += $(OUTPUT)util/target.o
|
||||||
LIB_OBJS += $(OUTPUT)util/rblist.o
|
LIB_OBJS += $(OUTPUT)util/rblist.o
|
||||||
LIB_OBJS += $(OUTPUT)util/intlist.o
|
LIB_OBJS += $(OUTPUT)util/intlist.o
|
||||||
|
LIB_OBJS += $(OUTPUT)util/vdso.o
|
||||||
|
|
||||||
LIB_OBJS += $(OUTPUT)ui/helpline.o
|
LIB_OBJS += $(OUTPUT)ui/helpline.o
|
||||||
LIB_OBJS += $(OUTPUT)ui/hist.o
|
LIB_OBJS += $(OUTPUT)ui/hist.o
|
||||||
|
|
|
@ -43,7 +43,8 @@ static int build_id_cache__add_file(const char *filename, const char *debugdir)
|
||||||
}
|
}
|
||||||
|
|
||||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||||
err = build_id_cache__add_s(sbuild_id, debugdir, filename, false);
|
err = build_id_cache__add_s(sbuild_id, debugdir, filename,
|
||||||
|
false, false);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
pr_info("Adding %s %s: %s\n", sbuild_id, filename,
|
pr_info("Adding %s %s: %s\n", sbuild_id, filename,
|
||||||
err ? "FAIL" : "Ok");
|
err ? "FAIL" : "Ok");
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "cpumap.h"
|
#include "cpumap.h"
|
||||||
#include "pmu.h"
|
#include "pmu.h"
|
||||||
|
#include "vdso.h"
|
||||||
|
|
||||||
static bool no_buildid_cache = false;
|
static bool no_buildid_cache = false;
|
||||||
|
|
||||||
|
@ -207,6 +208,29 @@ perf_header__set_cmdline(int argc, const char **argv)
|
||||||
continue; \
|
continue; \
|
||||||
else
|
else
|
||||||
|
|
||||||
|
static int write_buildid(char *name, size_t name_len, u8 *build_id,
|
||||||
|
pid_t pid, u16 misc, int fd)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct build_id_event b;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = name_len + 1;
|
||||||
|
len = PERF_ALIGN(len, NAME_ALIGN);
|
||||||
|
|
||||||
|
memset(&b, 0, sizeof(b));
|
||||||
|
memcpy(&b.build_id, build_id, BUILD_ID_SIZE);
|
||||||
|
b.pid = pid;
|
||||||
|
b.header.misc = misc;
|
||||||
|
b.header.size = sizeof(b) + len;
|
||||||
|
|
||||||
|
err = do_write(fd, &b, sizeof(b));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return write_padded(fd, name, name_len + 1, len);
|
||||||
|
}
|
||||||
|
|
||||||
static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
|
static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
|
||||||
u16 misc, int fd)
|
u16 misc, int fd)
|
||||||
{
|
{
|
||||||
|
@ -214,24 +238,23 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
|
||||||
|
|
||||||
dsos__for_each_with_build_id(pos, head) {
|
dsos__for_each_with_build_id(pos, head) {
|
||||||
int err;
|
int err;
|
||||||
struct build_id_event b;
|
char *name;
|
||||||
size_t len;
|
size_t name_len;
|
||||||
|
|
||||||
if (!pos->hit)
|
if (!pos->hit)
|
||||||
continue;
|
continue;
|
||||||
len = pos->long_name_len + 1;
|
|
||||||
len = PERF_ALIGN(len, NAME_ALIGN);
|
if (is_vdso_map(pos->short_name)) {
|
||||||
memset(&b, 0, sizeof(b));
|
name = (char *) VDSO__MAP_NAME;
|
||||||
memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
|
name_len = sizeof(VDSO__MAP_NAME) + 1;
|
||||||
b.pid = pid;
|
} else {
|
||||||
b.header.misc = misc;
|
name = pos->long_name;
|
||||||
b.header.size = sizeof(b) + len;
|
name_len = pos->long_name_len + 1;
|
||||||
err = do_write(fd, &b, sizeof(b));
|
}
|
||||||
if (err < 0)
|
|
||||||
return err;
|
err = write_buildid(name, name_len, pos->build_id,
|
||||||
err = write_padded(fd, pos->long_name,
|
pid, misc, fd);
|
||||||
pos->long_name_len + 1, len);
|
if (err)
|
||||||
if (err < 0)
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,19 +300,20 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
||||||
const char *name, bool is_kallsyms)
|
const char *name, bool is_kallsyms, bool is_vdso)
|
||||||
{
|
{
|
||||||
const size_t size = PATH_MAX;
|
const size_t size = PATH_MAX;
|
||||||
char *realname, *filename = zalloc(size),
|
char *realname, *filename = zalloc(size),
|
||||||
*linkname = zalloc(size), *targetname;
|
*linkname = zalloc(size), *targetname;
|
||||||
int len, err = -1;
|
int len, err = -1;
|
||||||
|
bool slash = is_kallsyms || is_vdso;
|
||||||
|
|
||||||
if (is_kallsyms) {
|
if (is_kallsyms) {
|
||||||
if (symbol_conf.kptr_restrict) {
|
if (symbol_conf.kptr_restrict) {
|
||||||
pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
|
pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
realname = (char *)name;
|
realname = (char *) name;
|
||||||
} else
|
} else
|
||||||
realname = realpath(name, NULL);
|
realname = realpath(name, NULL);
|
||||||
|
|
||||||
|
@ -297,7 +321,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
len = scnprintf(filename, size, "%s%s%s",
|
len = scnprintf(filename, size, "%s%s%s",
|
||||||
debugdir, is_kallsyms ? "/" : "", realname);
|
debugdir, slash ? "/" : "",
|
||||||
|
is_vdso ? VDSO__MAP_NAME : realname);
|
||||||
if (mkdir_p(filename, 0755))
|
if (mkdir_p(filename, 0755))
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
|
@ -333,13 +358,14 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
||||||
|
|
||||||
static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
|
static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
|
||||||
const char *name, const char *debugdir,
|
const char *name, const char *debugdir,
|
||||||
bool is_kallsyms)
|
bool is_kallsyms, bool is_vdso)
|
||||||
{
|
{
|
||||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||||
|
|
||||||
build_id__sprintf(build_id, build_id_size, sbuild_id);
|
build_id__sprintf(build_id, build_id_size, sbuild_id);
|
||||||
|
|
||||||
return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms);
|
return build_id_cache__add_s(sbuild_id, debugdir, name,
|
||||||
|
is_kallsyms, is_vdso);
|
||||||
}
|
}
|
||||||
|
|
||||||
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
|
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
|
||||||
|
@ -383,9 +409,11 @@ int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
|
||||||
static int dso__cache_build_id(struct dso *dso, const char *debugdir)
|
static int dso__cache_build_id(struct dso *dso, const char *debugdir)
|
||||||
{
|
{
|
||||||
bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
|
bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
|
||||||
|
bool is_vdso = is_vdso_map(dso->short_name);
|
||||||
|
|
||||||
return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id),
|
return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id),
|
||||||
dso->long_name, debugdir, is_kallsyms);
|
dso->long_name, debugdir,
|
||||||
|
is_kallsyms, is_vdso);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
|
static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
|
||||||
|
|
|
@ -96,7 +96,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
|
||||||
int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
|
int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
|
||||||
|
|
||||||
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
||||||
const char *name, bool is_kallsyms);
|
const char *name, bool is_kallsyms, bool is_vdso);
|
||||||
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
|
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
|
||||||
|
|
||||||
int perf_event__synthesize_attr(struct perf_tool *tool,
|
int perf_event__synthesize_attr(struct perf_tool *tool,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "strlist.h"
|
#include "strlist.h"
|
||||||
|
#include "vdso.h"
|
||||||
|
|
||||||
const char *map_type__name[MAP__NR_TYPES] = {
|
const char *map_type__name[MAP__NR_TYPES] = {
|
||||||
[MAP__FUNCTION] = "Functions",
|
[MAP__FUNCTION] = "Functions",
|
||||||
|
@ -23,7 +24,6 @@ static inline int is_anon_memory(const char *filename)
|
||||||
static inline int is_no_dso_memory(const char *filename)
|
static inline int is_no_dso_memory(const char *filename)
|
||||||
{
|
{
|
||||||
return !strcmp(filename, "[stack]") ||
|
return !strcmp(filename, "[stack]") ||
|
||||||
!strcmp(filename, "[vdso]") ||
|
|
||||||
!strcmp(filename, "[heap]");
|
!strcmp(filename, "[heap]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,9 +52,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
|
||||||
if (self != NULL) {
|
if (self != NULL) {
|
||||||
char newfilename[PATH_MAX];
|
char newfilename[PATH_MAX];
|
||||||
struct dso *dso;
|
struct dso *dso;
|
||||||
int anon, no_dso;
|
int anon, no_dso, vdso;
|
||||||
|
|
||||||
anon = is_anon_memory(filename);
|
anon = is_anon_memory(filename);
|
||||||
|
vdso = is_vdso_map(filename);
|
||||||
no_dso = is_no_dso_memory(filename);
|
no_dso = is_no_dso_memory(filename);
|
||||||
|
|
||||||
if (anon) {
|
if (anon) {
|
||||||
|
@ -62,7 +63,12 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
|
||||||
filename = newfilename;
|
filename = newfilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
dso = __dsos__findnew(dsos__list, filename);
|
if (vdso) {
|
||||||
|
pgoff = 0;
|
||||||
|
dso = vdso__dso_findnew(dsos__list);
|
||||||
|
} else
|
||||||
|
dso = __dsos__findnew(dsos__list, filename);
|
||||||
|
|
||||||
if (dso == NULL)
|
if (dso == NULL)
|
||||||
goto out_delete;
|
goto out_delete;
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "event-parse.h"
|
#include "event-parse.h"
|
||||||
#include "perf_regs.h"
|
#include "perf_regs.h"
|
||||||
#include "unwind.h"
|
#include "unwind.h"
|
||||||
|
#include "vdso.h"
|
||||||
|
|
||||||
static int perf_session__open(struct perf_session *self, bool force)
|
static int perf_session__open(struct perf_session *self, bool force)
|
||||||
{
|
{
|
||||||
|
@ -211,6 +212,7 @@ void perf_session__delete(struct perf_session *self)
|
||||||
machine__exit(&self->host_machine);
|
machine__exit(&self->host_machine);
|
||||||
close(self->fd);
|
close(self->fd);
|
||||||
free(self);
|
free(self);
|
||||||
|
vdso__exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void machine__remove_thread(struct machine *self, struct thread *th)
|
void machine__remove_thread(struct machine *self, struct thread *th)
|
||||||
|
|
111
tools/perf/util/vdso.c
Normal file
111
tools/perf/util/vdso.c
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
|
#include "vdso.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "symbol.h"
|
||||||
|
#include "linux/string.h"
|
||||||
|
|
||||||
|
static bool vdso_found;
|
||||||
|
static char vdso_file[] = "/tmp/perf-vdso.so-XXXXXX";
|
||||||
|
|
||||||
|
static int find_vdso_map(void **start, void **end)
|
||||||
|
{
|
||||||
|
FILE *maps;
|
||||||
|
char line[128];
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
maps = fopen("/proc/self/maps", "r");
|
||||||
|
if (!maps) {
|
||||||
|
pr_err("vdso: cannot open maps\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!found && fgets(line, sizeof(line), maps)) {
|
||||||
|
int m = -1;
|
||||||
|
|
||||||
|
/* We care only about private r-x mappings. */
|
||||||
|
if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
|
||||||
|
start, end, &m))
|
||||||
|
continue;
|
||||||
|
if (m < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!strncmp(&line[m], VDSO__MAP_NAME,
|
||||||
|
sizeof(VDSO__MAP_NAME) - 1))
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(maps);
|
||||||
|
return !found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *get_file(void)
|
||||||
|
{
|
||||||
|
char *vdso = NULL;
|
||||||
|
char *buf = NULL;
|
||||||
|
void *start, *end;
|
||||||
|
size_t size;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (vdso_found)
|
||||||
|
return vdso_file;
|
||||||
|
|
||||||
|
if (find_vdso_map(&start, &end))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size = end - start;
|
||||||
|
|
||||||
|
buf = memdup(start, size);
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fd = mkstemp(vdso_file);
|
||||||
|
if (fd < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (size == (size_t) write(fd, buf, size))
|
||||||
|
vdso = vdso_file;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
vdso_found = (vdso != NULL);
|
||||||
|
return vdso;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdso__exit(void)
|
||||||
|
{
|
||||||
|
if (vdso_found)
|
||||||
|
unlink(vdso_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dso *vdso__dso_findnew(struct list_head *head)
|
||||||
|
{
|
||||||
|
struct dso *dso = dsos__find(head, VDSO__MAP_NAME);
|
||||||
|
|
||||||
|
if (!dso) {
|
||||||
|
char *file;
|
||||||
|
|
||||||
|
file = get_file();
|
||||||
|
if (!file)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dso = dso__new(VDSO__MAP_NAME);
|
||||||
|
if (dso != NULL) {
|
||||||
|
dsos__add(head, dso);
|
||||||
|
dso__set_long_name(dso, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dso;
|
||||||
|
}
|
18
tools/perf/util/vdso.h
Normal file
18
tools/perf/util/vdso.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef __PERF_VDSO__
|
||||||
|
#define __PERF_VDSO__
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define VDSO__MAP_NAME "[vdso]"
|
||||||
|
|
||||||
|
static inline bool is_vdso_map(const char *filename)
|
||||||
|
{
|
||||||
|
return !strcmp(filename, VDSO__MAP_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dso *vdso__dso_findnew(struct list_head *head);
|
||||||
|
void vdso__exit(void);
|
||||||
|
|
||||||
|
#endif /* __PERF_VDSO__ */
|
Loading…
Reference in a new issue