linux/tools/perf/util/vdso.c
<<
>>
Prefs
   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 "machine.h"
  15#include "thread.h"
  16#include "linux/string.h"
  17#include "debug.h"
  18
  19/*
  20 * Include definition of find_vdso_map() also used in perf-read-vdso.c for
  21 * building perf-read-vdso32 and perf-read-vdsox32.
  22 */
  23#include "find-vdso-map.c"
  24
  25#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"
  26
  27struct vdso_file {
  28        bool found;
  29        bool error;
  30        char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
  31        const char *dso_name;
  32        const char *read_prog;
  33};
  34
  35struct vdso_info {
  36        struct vdso_file vdso;
  37#if BITS_PER_LONG == 64
  38        struct vdso_file vdso32;
  39        struct vdso_file vdsox32;
  40#endif
  41};
  42
  43static struct vdso_info *vdso_info__new(void)
  44{
  45        static const struct vdso_info vdso_info_init = {
  46                .vdso    = {
  47                        .temp_file_name = VDSO__TEMP_FILE_NAME,
  48                        .dso_name = DSO__NAME_VDSO,
  49                },
  50#if BITS_PER_LONG == 64
  51                .vdso32  = {
  52                        .temp_file_name = VDSO__TEMP_FILE_NAME,
  53                        .dso_name = DSO__NAME_VDSO32,
  54                        .read_prog = "perf-read-vdso32",
  55                },
  56                .vdsox32  = {
  57                        .temp_file_name = VDSO__TEMP_FILE_NAME,
  58                        .dso_name = DSO__NAME_VDSOX32,
  59                        .read_prog = "perf-read-vdsox32",
  60                },
  61#endif
  62        };
  63
  64        return memdup(&vdso_info_init, sizeof(vdso_info_init));
  65}
  66
  67static char *get_file(struct vdso_file *vdso_file)
  68{
  69        char *vdso = NULL;
  70        char *buf = NULL;
  71        void *start, *end;
  72        size_t size;
  73        int fd;
  74
  75        if (vdso_file->found)
  76                return vdso_file->temp_file_name;
  77
  78        if (vdso_file->error || find_vdso_map(&start, &end))
  79                return NULL;
  80
  81        size = end - start;
  82
  83        buf = memdup(start, size);
  84        if (!buf)
  85                return NULL;
  86
  87        fd = mkstemp(vdso_file->temp_file_name);
  88        if (fd < 0)
  89                goto out;
  90
  91        if (size == (size_t) write(fd, buf, size))
  92                vdso = vdso_file->temp_file_name;
  93
  94        close(fd);
  95
  96 out:
  97        free(buf);
  98
  99        vdso_file->found = (vdso != NULL);
 100        vdso_file->error = !vdso_file->found;
 101        return vdso;
 102}
 103
 104void machine__exit_vdso(struct machine *machine)
 105{
 106        struct vdso_info *vdso_info = machine->vdso_info;
 107
 108        if (!vdso_info)
 109                return;
 110
 111        if (vdso_info->vdso.found)
 112                unlink(vdso_info->vdso.temp_file_name);
 113#if BITS_PER_LONG == 64
 114        if (vdso_info->vdso32.found)
 115                unlink(vdso_info->vdso32.temp_file_name);
 116        if (vdso_info->vdsox32.found)
 117                unlink(vdso_info->vdsox32.temp_file_name);
 118#endif
 119
 120        zfree(&machine->vdso_info);
 121}
 122
 123static struct dso *__machine__addnew_vdso(struct machine *machine, const char *short_name,
 124                                          const char *long_name)
 125{
 126        struct dso *dso;
 127
 128        dso = dso__new(short_name);
 129        if (dso != NULL) {
 130                __dsos__add(&machine->dsos, dso);
 131                dso__set_long_name(dso, long_name, false);
 132        }
 133
 134        return dso;
 135}
 136
 137static enum dso_type machine__thread_dso_type(struct machine *machine,
 138                                              struct thread *thread)
 139{
 140        enum dso_type dso_type = DSO__TYPE_UNKNOWN;
 141        struct map *map;
 142        struct dso *dso;
 143
 144        map = map_groups__first(thread->mg, MAP__FUNCTION);
 145        for (; map ; map = map_groups__next(map)) {
 146                dso = map->dso;
 147                if (!dso || dso->long_name[0] != '/')
 148                        continue;
 149                dso_type = dso__type(dso, machine);
 150                if (dso_type != DSO__TYPE_UNKNOWN)
 151                        break;
 152        }
 153
 154        return dso_type;
 155}
 156
 157#if BITS_PER_LONG == 64
 158
 159static int vdso__do_copy_compat(FILE *f, int fd)
 160{
 161        char buf[4096];
 162        size_t count;
 163
 164        while (1) {
 165                count = fread(buf, 1, sizeof(buf), f);
 166                if (ferror(f))
 167                        return -errno;
 168                if (feof(f))
 169                        break;
 170                if (count && writen(fd, buf, count) != (ssize_t)count)
 171                        return -errno;
 172        }
 173
 174        return 0;
 175}
 176
 177static int vdso__copy_compat(const char *prog, int fd)
 178{
 179        FILE *f;
 180        int err;
 181
 182        f = popen(prog, "r");
 183        if (!f)
 184                return -errno;
 185
 186        err = vdso__do_copy_compat(f, fd);
 187
 188        if (pclose(f) == -1)
 189                return -errno;
 190
 191        return err;
 192}
 193
 194static int vdso__create_compat_file(const char *prog, char *temp_name)
 195{
 196        int fd, err;
 197
 198        fd = mkstemp(temp_name);
 199        if (fd < 0)
 200                return -errno;
 201
 202        err = vdso__copy_compat(prog, fd);
 203
 204        if (close(fd) == -1)
 205                return -errno;
 206
 207        return err;
 208}
 209
 210static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
 211{
 212        int err;
 213
 214        if (vdso_file->found)
 215                return vdso_file->temp_file_name;
 216
 217        if (vdso_file->error)
 218                return NULL;
 219
 220        err = vdso__create_compat_file(vdso_file->read_prog,
 221                                       vdso_file->temp_file_name);
 222        if (err) {
 223                pr_err("%s failed, error %d\n", vdso_file->read_prog, err);
 224                vdso_file->error = true;
 225                return NULL;
 226        }
 227
 228        vdso_file->found = true;
 229
 230        return vdso_file->temp_file_name;
 231}
 232
 233static struct dso *__machine__findnew_compat(struct machine *machine,
 234                                             struct vdso_file *vdso_file)
 235{
 236        const char *file_name;
 237        struct dso *dso;
 238
 239        dso = __dsos__find(&machine->dsos, vdso_file->dso_name, true);
 240        if (dso)
 241                goto out;
 242
 243        file_name = vdso__get_compat_file(vdso_file);
 244        if (!file_name)
 245                goto out;
 246
 247        dso = __machine__addnew_vdso(machine, vdso_file->dso_name, file_name);
 248out:
 249        return dso;
 250}
 251
 252static int __machine__findnew_vdso_compat(struct machine *machine,
 253                                          struct thread *thread,
 254                                          struct vdso_info *vdso_info,
 255                                          struct dso **dso)
 256{
 257        enum dso_type dso_type;
 258
 259        dso_type = machine__thread_dso_type(machine, thread);
 260
 261#ifndef HAVE_PERF_READ_VDSO32
 262        if (dso_type == DSO__TYPE_32BIT)
 263                return 0;
 264#endif
 265#ifndef HAVE_PERF_READ_VDSOX32
 266        if (dso_type == DSO__TYPE_X32BIT)
 267                return 0;
 268#endif
 269
 270        switch (dso_type) {
 271        case DSO__TYPE_32BIT:
 272                *dso = __machine__findnew_compat(machine, &vdso_info->vdso32);
 273                return 1;
 274        case DSO__TYPE_X32BIT:
 275                *dso = __machine__findnew_compat(machine, &vdso_info->vdsox32);
 276                return 1;
 277        case DSO__TYPE_UNKNOWN:
 278        case DSO__TYPE_64BIT:
 279        default:
 280                return 0;
 281        }
 282}
 283
 284#endif
 285
 286static struct dso *machine__find_vdso(struct machine *machine,
 287                                      struct thread *thread)
 288{
 289        struct dso *dso = NULL;
 290        enum dso_type dso_type;
 291
 292        dso_type = machine__thread_dso_type(machine, thread);
 293        switch (dso_type) {
 294        case DSO__TYPE_32BIT:
 295                dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO32, true);
 296                if (!dso) {
 297                        dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO,
 298                                           true);
 299                        if (dso && dso_type != dso__type(dso, machine))
 300                                dso = NULL;
 301                }
 302                break;
 303        case DSO__TYPE_X32BIT:
 304                dso = __dsos__find(&machine->dsos, DSO__NAME_VDSOX32, true);
 305                break;
 306        case DSO__TYPE_64BIT:
 307        case DSO__TYPE_UNKNOWN:
 308        default:
 309                dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
 310                break;
 311        }
 312
 313        return dso;
 314}
 315
 316struct dso *machine__findnew_vdso(struct machine *machine,
 317                                  struct thread *thread)
 318{
 319        struct vdso_info *vdso_info;
 320        struct dso *dso = NULL;
 321
 322        pthread_rwlock_wrlock(&machine->dsos.lock);
 323        if (!machine->vdso_info)
 324                machine->vdso_info = vdso_info__new();
 325
 326        vdso_info = machine->vdso_info;
 327        if (!vdso_info)
 328                goto out_unlock;
 329
 330        dso = machine__find_vdso(machine, thread);
 331        if (dso)
 332                goto out_unlock;
 333
 334#if BITS_PER_LONG == 64
 335        if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso))
 336                goto out_unlock;
 337#endif
 338
 339        dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
 340        if (!dso) {
 341                char *file;
 342
 343                file = get_file(&vdso_info->vdso);
 344                if (file)
 345                        dso = __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
 346        }
 347
 348out_unlock:
 349        dso__get(dso);
 350        pthread_rwlock_unlock(&machine->dsos.lock);
 351        return dso;
 352}
 353
 354bool dso__is_vdso(struct dso *dso)
 355{
 356        return !strcmp(dso->short_name, DSO__NAME_VDSO) ||
 357               !strcmp(dso->short_name, DSO__NAME_VDSO32) ||
 358               !strcmp(dso->short_name, DSO__NAME_VDSOX32);
 359}
 360