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

perf tools: Add support for 32-bit compatibility VDSOs

'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>

authored by

Adrian Hunter and committed by
Arnaldo Carvalho de Melo
f6832e17 e477f3f0

+172 -2
+169 -1
tools/perf/util/vdso.c
··· 12 12 #include "util.h" 13 13 #include "symbol.h" 14 14 #include "machine.h" 15 + #include "thread.h" 15 16 #include "linux/string.h" 16 17 #include "debug.h" 17 18 ··· 29 28 bool error; 30 29 char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)]; 31 30 const char *dso_name; 31 + const char *read_prog; 32 32 }; 33 33 34 34 struct vdso_info { 35 35 struct vdso_file vdso; 36 + #if BITS_PER_LONG == 64 37 + struct vdso_file vdso32; 38 + struct vdso_file vdsox32; 39 + #endif 36 40 }; 37 41 38 42 static struct vdso_info *vdso_info__new(void) ··· 47 41 .temp_file_name = VDSO__TEMP_FILE_NAME, 48 42 .dso_name = DSO__NAME_VDSO, 49 43 }, 44 + #if BITS_PER_LONG == 64 45 + .vdso32 = { 46 + .temp_file_name = VDSO__TEMP_FILE_NAME, 47 + .dso_name = DSO__NAME_VDSO32, 48 + .read_prog = "perf-read-vdso32", 49 + }, 50 + .vdsox32 = { 51 + .temp_file_name = VDSO__TEMP_FILE_NAME, 52 + .dso_name = DSO__NAME_VDSOX32, 53 + .read_prog = "perf-read-vdsox32", 54 + }, 55 + #endif 50 56 }; 51 57 52 58 return memdup(&vdso_info_init, sizeof(vdso_info_init)); ··· 110 92 111 93 if (vdso_info->vdso.found) 112 94 unlink(vdso_info->vdso.temp_file_name); 95 + #if BITS_PER_LONG == 64 96 + if (vdso_info->vdso32.found) 97 + unlink(vdso_info->vdso32.temp_file_name); 98 + if (vdso_info->vdsox32.found) 99 + unlink(vdso_info->vdsox32.temp_file_name); 100 + #endif 113 101 114 102 zfree(&machine->vdso_info); 115 103 } ··· 134 110 return dso; 135 111 } 136 112 113 + #if BITS_PER_LONG == 64 114 + 115 + static enum dso_type machine__thread_dso_type(struct machine *machine, 116 + struct thread *thread) 117 + { 118 + enum dso_type dso_type = DSO__TYPE_UNKNOWN; 119 + struct map *map; 120 + struct dso *dso; 121 + 122 + map = map_groups__first(thread->mg, MAP__FUNCTION); 123 + for (; map ; map = map_groups__next(map)) { 124 + dso = map->dso; 125 + if (!dso || dso->long_name[0] != '/') 126 + continue; 127 + dso_type = dso__type(dso, machine); 128 + if (dso_type != DSO__TYPE_UNKNOWN) 129 + break; 130 + } 131 + 132 + return dso_type; 133 + } 134 + 135 + static int vdso__do_copy_compat(FILE *f, int fd) 136 + { 137 + char buf[4096]; 138 + size_t count; 139 + 140 + while (1) { 141 + count = fread(buf, 1, sizeof(buf), f); 142 + if (ferror(f)) 143 + return -errno; 144 + if (feof(f)) 145 + break; 146 + if (count && writen(fd, buf, count) != (ssize_t)count) 147 + return -errno; 148 + } 149 + 150 + return 0; 151 + } 152 + 153 + static int vdso__copy_compat(const char *prog, int fd) 154 + { 155 + FILE *f; 156 + int err; 157 + 158 + f = popen(prog, "r"); 159 + if (!f) 160 + return -errno; 161 + 162 + err = vdso__do_copy_compat(f, fd); 163 + 164 + if (pclose(f) == -1) 165 + return -errno; 166 + 167 + return err; 168 + } 169 + 170 + static int vdso__create_compat_file(const char *prog, char *temp_name) 171 + { 172 + int fd, err; 173 + 174 + fd = mkstemp(temp_name); 175 + if (fd < 0) 176 + return -errno; 177 + 178 + err = vdso__copy_compat(prog, fd); 179 + 180 + if (close(fd) == -1) 181 + return -errno; 182 + 183 + return err; 184 + } 185 + 186 + static const char *vdso__get_compat_file(struct vdso_file *vdso_file) 187 + { 188 + int err; 189 + 190 + if (vdso_file->found) 191 + return vdso_file->temp_file_name; 192 + 193 + if (vdso_file->error) 194 + return NULL; 195 + 196 + err = vdso__create_compat_file(vdso_file->read_prog, 197 + vdso_file->temp_file_name); 198 + if (err) { 199 + pr_err("%s failed, error %d\n", vdso_file->read_prog, err); 200 + vdso_file->error = true; 201 + return NULL; 202 + } 203 + 204 + vdso_file->found = true; 205 + 206 + return vdso_file->temp_file_name; 207 + } 208 + 209 + static struct dso *vdso__findnew_compat(struct machine *machine, 210 + struct vdso_file *vdso_file) 211 + { 212 + const char *file_name; 213 + struct dso *dso; 214 + 215 + dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true); 216 + if (dso) 217 + return dso; 218 + 219 + file_name = vdso__get_compat_file(vdso_file); 220 + if (!file_name) 221 + return NULL; 222 + 223 + return vdso__new(machine, vdso_file->dso_name, file_name); 224 + } 225 + 226 + static int vdso__dso_findnew_compat(struct machine *machine, 227 + struct thread *thread, 228 + struct vdso_info *vdso_info, 229 + struct dso **dso) 230 + { 231 + enum dso_type dso_type; 232 + 233 + dso_type = machine__thread_dso_type(machine, thread); 234 + switch (dso_type) { 235 + case DSO__TYPE_32BIT: 236 + *dso = vdso__findnew_compat(machine, &vdso_info->vdso32); 237 + return 1; 238 + case DSO__TYPE_X32BIT: 239 + *dso = vdso__findnew_compat(machine, &vdso_info->vdsox32); 240 + return 1; 241 + case DSO__TYPE_UNKNOWN: 242 + case DSO__TYPE_64BIT: 243 + default: 244 + return 0; 245 + } 246 + } 247 + 248 + #endif 249 + 137 250 struct dso *vdso__dso_findnew(struct machine *machine, 138 251 struct thread *thread __maybe_unused) 139 252 { ··· 283 122 vdso_info = machine->vdso_info; 284 123 if (!vdso_info) 285 124 return NULL; 125 + 126 + #if BITS_PER_LONG == 64 127 + if (vdso__dso_findnew_compat(machine, thread, vdso_info, &dso)) 128 + return dso; 129 + #endif 286 130 287 131 dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true); 288 132 if (!dso) { ··· 305 139 306 140 bool dso__is_vdso(struct dso *dso) 307 141 { 308 - return !strcmp(dso->short_name, DSO__NAME_VDSO); 142 + return !strcmp(dso->short_name, DSO__NAME_VDSO) || 143 + !strcmp(dso->short_name, DSO__NAME_VDSO32) || 144 + !strcmp(dso->short_name, DSO__NAME_VDSOX32); 309 145 }
+3 -1
tools/perf/util/vdso.h
··· 7 7 8 8 #define VDSO__MAP_NAME "[vdso]" 9 9 10 - #define DSO__NAME_VDSO "[vdso]" 10 + #define DSO__NAME_VDSO "[vdso]" 11 + #define DSO__NAME_VDSO32 "[vdso32]" 12 + #define DSO__NAME_VDSOX32 "[vdsox32]" 11 13 12 14 static inline bool is_vdso_map(const char *filename) 13 15 {