Navigation Menu

Skip to content

Commit

Permalink
perf tools: Back [vdso] DSO with real data
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jiri Olsa authored and acmel committed Sep 11, 2012
1 parent 1c4be9f commit 7dbf4dc
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 26 deletions.
2 changes: 2 additions & 0 deletions tools/perf/Makefile
Expand Up @@ -337,6 +337,7 @@ LIB_H += util/intlist.h
LIB_H += util/perf_regs.h
LIB_H += util/unwind.h
LIB_H += ui/helpline.h
LIB_H += util/vdso.h

LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
Expand Down Expand Up @@ -404,6 +405,7 @@ LIB_OBJS += $(OUTPUT)util/cgroup.o
LIB_OBJS += $(OUTPUT)util/target.o
LIB_OBJS += $(OUTPUT)util/rblist.o
LIB_OBJS += $(OUTPUT)util/intlist.o
LIB_OBJS += $(OUTPUT)util/vdso.o

LIB_OBJS += $(OUTPUT)ui/helpline.o
LIB_OBJS += $(OUTPUT)ui/hist.o
Expand Down
3 changes: 2 additions & 1 deletion tools/perf/builtin-buildid-cache.c
Expand Up @@ -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);
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)
pr_info("Adding %s %s: %s\n", sbuild_id, filename,
err ? "FAIL" : "Ok");
Expand Down
70 changes: 49 additions & 21 deletions tools/perf/util/header.c
Expand Up @@ -21,6 +21,7 @@
#include "debug.h"
#include "cpumap.h"
#include "pmu.h"
#include "vdso.h"

static bool no_buildid_cache = false;

Expand Down Expand Up @@ -207,31 +208,53 @@ perf_header__set_cmdline(int argc, const char **argv)
continue; \
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,
u16 misc, int fd)
{
struct dso *pos;

dsos__for_each_with_build_id(pos, head) {
int err;
struct build_id_event b;
size_t len;
char *name;
size_t name_len;

if (!pos->hit)
continue;
len = pos->long_name_len + 1;
len = PERF_ALIGN(len, NAME_ALIGN);
memset(&b, 0, sizeof(b));
memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
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;
err = write_padded(fd, pos->long_name,
pos->long_name_len + 1, len);
if (err < 0)

if (is_vdso_map(pos->short_name)) {
name = (char *) VDSO__MAP_NAME;
name_len = sizeof(VDSO__MAP_NAME) + 1;
} else {
name = pos->long_name;
name_len = pos->long_name_len + 1;
}

err = write_buildid(name, name_len, pos->build_id,
pid, misc, fd);
if (err)
return err;
}

Expand Down Expand Up @@ -277,27 +300,29 @@ 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,
const char *name, bool is_kallsyms)
const char *name, bool is_kallsyms, bool is_vdso)
{
const size_t size = PATH_MAX;
char *realname, *filename = zalloc(size),
*linkname = zalloc(size), *targetname;
int len, err = -1;
bool slash = is_kallsyms || is_vdso;

if (is_kallsyms) {
if (symbol_conf.kptr_restrict) {
pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
return 0;
}
realname = (char *)name;
realname = (char *) name;
} else
realname = realpath(name, NULL);

if (realname == NULL || filename == NULL || linkname == NULL)
goto out_free;

len = scnprintf(filename, size, "%s%s%s",
debugdir, is_kallsyms ? "/" : "", realname);
debugdir, slash ? "/" : "",
is_vdso ? VDSO__MAP_NAME : realname);
if (mkdir_p(filename, 0755))
goto out_free;

Expand Down Expand Up @@ -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,
const char *name, const char *debugdir,
bool is_kallsyms)
bool is_kallsyms, bool is_vdso)
{
char sbuild_id[BUILD_ID_SIZE * 2 + 1];

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)
Expand Down Expand Up @@ -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)
{
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),
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)
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/util/header.h
Expand Up @@ -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 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 perf_event__synthesize_attr(struct perf_tool *tool,
Expand Down
12 changes: 9 additions & 3 deletions tools/perf/util/map.c
Expand Up @@ -9,6 +9,7 @@
#include "map.h"
#include "thread.h"
#include "strlist.h"
#include "vdso.h"

const char *map_type__name[MAP__NR_TYPES] = {
[MAP__FUNCTION] = "Functions",
Expand All @@ -23,7 +24,6 @@ static inline int is_anon_memory(const char *filename)
static inline int is_no_dso_memory(const char *filename)
{
return !strcmp(filename, "[stack]") ||
!strcmp(filename, "[vdso]") ||
!strcmp(filename, "[heap]");
}

Expand Down Expand Up @@ -52,17 +52,23 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
if (self != NULL) {
char newfilename[PATH_MAX];
struct dso *dso;
int anon, no_dso;
int anon, no_dso, vdso;

anon = is_anon_memory(filename);
vdso = is_vdso_map(filename);
no_dso = is_no_dso_memory(filename);

if (anon) {
snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
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)
goto out_delete;

Expand Down
2 changes: 2 additions & 0 deletions tools/perf/util/session.c
Expand Up @@ -17,6 +17,7 @@
#include "event-parse.h"
#include "perf_regs.h"
#include "unwind.h"
#include "vdso.h"

static int perf_session__open(struct perf_session *self, bool force)
{
Expand Down Expand Up @@ -211,6 +212,7 @@ void perf_session__delete(struct perf_session *self)
machine__exit(&self->host_machine);
close(self->fd);
free(self);
vdso__exit();
}

void machine__remove_thread(struct machine *self, struct thread *th)
Expand Down
111 changes: 111 additions & 0 deletions tools/perf/util/vdso.c
@@ -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 changes: 18 additions & 0 deletions tools/perf/util/vdso.h
@@ -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__ */

0 comments on commit 7dbf4dc

Please sign in to comment.