perf tools: Add support for 32-bit compatibility VDSOs
authorAdrian Hunter <adrian.hunter@intel.com>
Thu, 23 Oct 2014 10:45:23 +0000 (13:45 +0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 29 Oct 2014 12:32:48 +0000 (10:32 -0200)
'perf record' post-processes the event stream  to create a list of
build-ids for object files for which sample events have been recorded.
That results in those object files being recorded in the build-id cache.

In the case of VDSO, perf tools reads it from memory and copies it into
a temporary file, which as decribed above, gets added to the build-id
cache.

Then when the perf.data file is processed by other tools, the build-id
of VDSO is listed in the perf.data file and the VDSO can be read from
the build-id cache.  In that case the name of the map, the short name of
the DSO, and the entry in the build-id cache are all "[vdso]".

However, in the 64-bit case, there also can be 32-bit compatibility
VDSOs.

A previous patch added programs "perf-read-vdso32" and "perf
read-vdsox32".

This patch uses those programs to read the correct VDSO for a thread and
create a temporary file just as for the 64-bit VDSO.

The map name and the entry in the build-id cache are still "[vdso]" but
the DSO short name becomes "[vdso32]" and "[vdsox32]" respectively.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1414061124-26830-16-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/vdso.c
tools/perf/util/vdso.h

index f51390a1ed5162af88460fa8e35465dba24deb63..69daef6a17d583ba1dc9f48190c26520b01396e2 100644 (file)
@@ -12,6 +12,7 @@
 #include "util.h"
 #include "symbol.h"
 #include "machine.h"
+#include "thread.h"
 #include "linux/string.h"
 #include "debug.h"
 
@@ -28,10 +29,15 @@ struct vdso_file {
        bool error;
        char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
        const char *dso_name;
+       const char *read_prog;
 };
 
 struct vdso_info {
        struct vdso_file vdso;
+#if BITS_PER_LONG == 64
+       struct vdso_file vdso32;
+       struct vdso_file vdsox32;
+#endif
 };
 
 static struct vdso_info *vdso_info__new(void)
@@ -41,6 +47,18 @@ static struct vdso_info *vdso_info__new(void)
                        .temp_file_name = VDSO__TEMP_FILE_NAME,
                        .dso_name = DSO__NAME_VDSO,
                },
+#if BITS_PER_LONG == 64
+               .vdso32  = {
+                       .temp_file_name = VDSO__TEMP_FILE_NAME,
+                       .dso_name = DSO__NAME_VDSO32,
+                       .read_prog = "perf-read-vdso32",
+               },
+               .vdsox32  = {
+                       .temp_file_name = VDSO__TEMP_FILE_NAME,
+                       .dso_name = DSO__NAME_VDSOX32,
+                       .read_prog = "perf-read-vdsox32",
+               },
+#endif
        };
 
        return memdup(&vdso_info_init, sizeof(vdso_info_init));
@@ -92,6 +110,12 @@ void vdso__exit(struct machine *machine)
 
        if (vdso_info->vdso.found)
                unlink(vdso_info->vdso.temp_file_name);
+#if BITS_PER_LONG == 64
+       if (vdso_info->vdso32.found)
+               unlink(vdso_info->vdso32.temp_file_name);
+       if (vdso_info->vdsox32.found)
+               unlink(vdso_info->vdsox32.temp_file_name);
+#endif
 
        zfree(&machine->vdso_info);
 }
@@ -110,6 +134,143 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name,
        return dso;
 }
 
+#if BITS_PER_LONG == 64
+
+static enum dso_type machine__thread_dso_type(struct machine *machine,
+                                             struct thread *thread)
+{
+       enum dso_type dso_type = DSO__TYPE_UNKNOWN;
+       struct map *map;
+       struct dso *dso;
+
+       map = map_groups__first(thread->mg, MAP__FUNCTION);
+       for (; map ; map = map_groups__next(map)) {
+               dso = map->dso;
+               if (!dso || dso->long_name[0] != '/')
+                       continue;
+               dso_type = dso__type(dso, machine);
+               if (dso_type != DSO__TYPE_UNKNOWN)
+                       break;
+       }
+
+       return dso_type;
+}
+
+static int vdso__do_copy_compat(FILE *f, int fd)
+{
+       char buf[4096];
+       size_t count;
+
+       while (1) {
+               count = fread(buf, 1, sizeof(buf), f);
+               if (ferror(f))
+                       return -errno;
+               if (feof(f))
+                       break;
+               if (count && writen(fd, buf, count) != (ssize_t)count)
+                       return -errno;
+       }
+
+       return 0;
+}
+
+static int vdso__copy_compat(const char *prog, int fd)
+{
+       FILE *f;
+       int err;
+
+       f = popen(prog, "r");
+       if (!f)
+               return -errno;
+
+       err = vdso__do_copy_compat(f, fd);
+
+       if (pclose(f) == -1)
+               return -errno;
+
+       return err;
+}
+
+static int vdso__create_compat_file(const char *prog, char *temp_name)
+{
+       int fd, err;
+
+       fd = mkstemp(temp_name);
+       if (fd < 0)
+               return -errno;
+
+       err = vdso__copy_compat(prog, fd);
+
+       if (close(fd) == -1)
+               return -errno;
+
+       return err;
+}
+
+static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
+{
+       int err;
+
+       if (vdso_file->found)
+               return vdso_file->temp_file_name;
+
+       if (vdso_file->error)
+               return NULL;
+
+       err = vdso__create_compat_file(vdso_file->read_prog,
+                                      vdso_file->temp_file_name);
+       if (err) {
+               pr_err("%s failed, error %d\n", vdso_file->read_prog, err);
+               vdso_file->error = true;
+               return NULL;
+       }
+
+       vdso_file->found = true;
+
+       return vdso_file->temp_file_name;
+}
+
+static struct dso *vdso__findnew_compat(struct machine *machine,
+                                       struct vdso_file *vdso_file)
+{
+       const char *file_name;
+       struct dso *dso;
+
+       dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true);
+       if (dso)
+               return dso;
+
+       file_name = vdso__get_compat_file(vdso_file);
+       if (!file_name)
+               return NULL;
+
+       return vdso__new(machine, vdso_file->dso_name, file_name);
+}
+
+static int vdso__dso_findnew_compat(struct machine *machine,
+                                   struct thread *thread,
+                                   struct vdso_info *vdso_info,
+                                   struct dso **dso)
+{
+       enum dso_type dso_type;
+
+       dso_type = machine__thread_dso_type(machine, thread);
+       switch (dso_type) {
+       case DSO__TYPE_32BIT:
+               *dso = vdso__findnew_compat(machine, &vdso_info->vdso32);
+               return 1;
+       case DSO__TYPE_X32BIT:
+               *dso = vdso__findnew_compat(machine, &vdso_info->vdsox32);
+               return 1;
+       case DSO__TYPE_UNKNOWN:
+       case DSO__TYPE_64BIT:
+       default:
+               return 0;
+       }
+}
+
+#endif
+
 struct dso *vdso__dso_findnew(struct machine *machine,
                              struct thread *thread __maybe_unused)
 {
@@ -123,6 +284,11 @@ struct dso *vdso__dso_findnew(struct machine *machine,
        if (!vdso_info)
                return NULL;
 
+#if BITS_PER_LONG == 64
+       if (vdso__dso_findnew_compat(machine, thread, vdso_info, &dso))
+               return dso;
+#endif
+
        dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true);
        if (!dso) {
                char *file;
@@ -139,5 +305,7 @@ struct dso *vdso__dso_findnew(struct machine *machine,
 
 bool dso__is_vdso(struct dso *dso)
 {
-       return !strcmp(dso->short_name, DSO__NAME_VDSO);
+       return !strcmp(dso->short_name, DSO__NAME_VDSO) ||
+              !strcmp(dso->short_name, DSO__NAME_VDSO32) ||
+              !strcmp(dso->short_name, DSO__NAME_VDSOX32);
 }
index af9d6929a215571712afb7598b1b90f6216c9ccc..d97da1616f0c5b658d8c1846b0285835b3a3478c 100644 (file)
@@ -7,7 +7,9 @@
 
 #define VDSO__MAP_NAME "[vdso]"
 
-#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO    "[vdso]"
+#define DSO__NAME_VDSO32  "[vdso32]"
+#define DSO__NAME_VDSOX32 "[vdsox32]"
 
 static inline bool is_vdso_map(const char *filename)
 {