linux/tools/perf/util/build-id.c
<<
>>
Prefs
   1/*
   2 * build-id.c
   3 *
   4 * build-id support
   5 *
   6 * Copyright (C) 2009, 2010 Red Hat Inc.
   7 * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
   8 */
   9#include "util.h"
  10#include <stdio.h>
  11#include "build-id.h"
  12#include "event.h"
  13#include "symbol.h"
  14#include <linux/kernel.h>
  15#include "debug.h"
  16#include "session.h"
  17#include "tool.h"
  18#include "header.h"
  19#include "vdso.h"
  20
  21
  22static bool no_buildid_cache;
  23
  24int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
  25                           union perf_event *event,
  26                           struct perf_sample *sample,
  27                           struct perf_evsel *evsel __maybe_unused,
  28                           struct machine *machine)
  29{
  30        struct addr_location al;
  31        u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
  32        struct thread *thread = machine__findnew_thread(machine, sample->pid,
  33                                                        sample->tid);
  34
  35        if (thread == NULL) {
  36                pr_err("problem processing %d event, skipping it.\n",
  37                        event->header.type);
  38                return -1;
  39        }
  40
  41        thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
  42
  43        if (al.map != NULL)
  44                al.map->dso->hit = 1;
  45
  46        return 0;
  47}
  48
  49static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused,
  50                                       union perf_event *event,
  51                                       struct perf_sample *sample
  52                                       __maybe_unused,
  53                                       struct machine *machine)
  54{
  55        struct thread *thread = machine__findnew_thread(machine,
  56                                                        event->fork.pid,
  57                                                        event->fork.tid);
  58
  59        dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid,
  60                    event->fork.ppid, event->fork.ptid);
  61
  62        if (thread) {
  63                rb_erase(&thread->rb_node, &machine->threads);
  64                machine->last_match = NULL;
  65                thread__delete(thread);
  66        }
  67
  68        return 0;
  69}
  70
  71struct perf_tool build_id__mark_dso_hit_ops = {
  72        .sample = build_id__mark_dso_hit,
  73        .mmap   = perf_event__process_mmap,
  74        .mmap2  = perf_event__process_mmap2,
  75        .fork   = perf_event__process_fork,
  76        .exit   = perf_event__exit_del_thread,
  77        .attr            = perf_event__process_attr,
  78        .build_id        = perf_event__process_build_id,
  79};
  80
  81int build_id__sprintf(const u8 *build_id, int len, char *bf)
  82{
  83        char *bid = bf;
  84        const u8 *raw = build_id;
  85        int i;
  86
  87        for (i = 0; i < len; ++i) {
  88                sprintf(bid, "%02x", *raw);
  89                ++raw;
  90                bid += 2;
  91        }
  92
  93        return raw - build_id;
  94}
  95
  96char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
  97{
  98        char build_id_hex[BUILD_ID_SIZE * 2 + 1];
  99
 100        if (!dso->has_build_id)
 101                return NULL;
 102
 103        build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex);
 104        if (bf == NULL) {
 105                if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,
 106                             build_id_hex, build_id_hex + 2) < 0)
 107                        return NULL;
 108        } else
 109                snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
 110                         build_id_hex, build_id_hex + 2);
 111        return bf;
 112}
 113
 114#define dsos__for_each_with_build_id(pos, head) \
 115        list_for_each_entry(pos, head, node)    \
 116                if (!pos->has_build_id)         \
 117                        continue;               \
 118                else
 119
 120static int write_buildid(const char *name, size_t name_len, u8 *build_id,
 121                         pid_t pid, u16 misc, int fd)
 122{
 123        int err;
 124        struct build_id_event b;
 125        size_t len;
 126
 127        len = name_len + 1;
 128        len = PERF_ALIGN(len, NAME_ALIGN);
 129
 130        memset(&b, 0, sizeof(b));
 131        memcpy(&b.build_id, build_id, BUILD_ID_SIZE);
 132        b.pid = pid;
 133        b.header.misc = misc;
 134        b.header.size = sizeof(b) + len;
 135
 136        err = writen(fd, &b, sizeof(b));
 137        if (err < 0)
 138                return err;
 139
 140        return write_padded(fd, name, name_len + 1, len);
 141}
 142
 143static int __dsos__write_buildid_table(struct list_head *head,
 144                                       struct machine *machine,
 145                                       pid_t pid, u16 misc, int fd)
 146{
 147        char nm[PATH_MAX];
 148        struct dso *pos;
 149
 150        dsos__for_each_with_build_id(pos, head) {
 151                int err;
 152                const char *name;
 153                size_t name_len;
 154
 155                if (!pos->hit)
 156                        continue;
 157
 158                if (dso__is_vdso(pos)) {
 159                        name = pos->short_name;
 160                        name_len = pos->short_name_len + 1;
 161                } else if (dso__is_kcore(pos)) {
 162                        machine__mmap_name(machine, nm, sizeof(nm));
 163                        name = nm;
 164                        name_len = strlen(nm) + 1;
 165                } else {
 166                        name = pos->long_name;
 167                        name_len = pos->long_name_len + 1;
 168                }
 169
 170                err = write_buildid(name, name_len, pos->build_id,
 171                                    pid, misc, fd);
 172                if (err)
 173                        return err;
 174        }
 175
 176        return 0;
 177}
 178
 179static int machine__write_buildid_table(struct machine *machine, int fd)
 180{
 181        int err;
 182        u16 kmisc = PERF_RECORD_MISC_KERNEL,
 183            umisc = PERF_RECORD_MISC_USER;
 184
 185        if (!machine__is_host(machine)) {
 186                kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
 187                umisc = PERF_RECORD_MISC_GUEST_USER;
 188        }
 189
 190        err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine,
 191                                          machine->pid, kmisc, fd);
 192        if (err == 0)
 193                err = __dsos__write_buildid_table(&machine->user_dsos.head,
 194                                                  machine, machine->pid, umisc,
 195                                                  fd);
 196        return err;
 197}
 198
 199int perf_session__write_buildid_table(struct perf_session *session, int fd)
 200{
 201        struct rb_node *nd;
 202        int err = machine__write_buildid_table(&session->machines.host, fd);
 203
 204        if (err)
 205                return err;
 206
 207        for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
 208                struct machine *pos = rb_entry(nd, struct machine, rb_node);
 209                err = machine__write_buildid_table(pos, fd);
 210                if (err)
 211                        break;
 212        }
 213        return err;
 214}
 215
 216static int __dsos__hit_all(struct list_head *head)
 217{
 218        struct dso *pos;
 219
 220        list_for_each_entry(pos, head, node)
 221                pos->hit = true;
 222
 223        return 0;
 224}
 225
 226static int machine__hit_all_dsos(struct machine *machine)
 227{
 228        int err;
 229
 230        err = __dsos__hit_all(&machine->kernel_dsos.head);
 231        if (err)
 232                return err;
 233
 234        return __dsos__hit_all(&machine->user_dsos.head);
 235}
 236
 237int dsos__hit_all(struct perf_session *session)
 238{
 239        struct rb_node *nd;
 240        int err;
 241
 242        err = machine__hit_all_dsos(&session->machines.host);
 243        if (err)
 244                return err;
 245
 246        for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
 247                struct machine *pos = rb_entry(nd, struct machine, rb_node);
 248
 249                err = machine__hit_all_dsos(pos);
 250                if (err)
 251                        return err;
 252        }
 253
 254        return 0;
 255}
 256
 257void disable_buildid_cache(void)
 258{
 259        no_buildid_cache = true;
 260}
 261
 262int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
 263                          const char *name, bool is_kallsyms, bool is_vdso)
 264{
 265        const size_t size = PATH_MAX;
 266        char *realname, *filename = zalloc(size),
 267             *linkname = zalloc(size), *targetname;
 268        int len, err = -1;
 269        bool slash = is_kallsyms || is_vdso;
 270
 271        if (is_kallsyms) {
 272                if (symbol_conf.kptr_restrict) {
 273                        pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
 274                        err = 0;
 275                        goto out_free;
 276                }
 277                realname = (char *) name;
 278        } else
 279                realname = realpath(name, NULL);
 280
 281        if (realname == NULL || filename == NULL || linkname == NULL)
 282                goto out_free;
 283
 284        len = scnprintf(filename, size, "%s%s%s",
 285                       debugdir, slash ? "/" : "",
 286                       is_vdso ? DSO__NAME_VDSO : realname);
 287        if (mkdir_p(filename, 0755))
 288                goto out_free;
 289
 290        snprintf(filename + len, size - len, "/%s", sbuild_id);
 291
 292        if (access(filename, F_OK)) {
 293                if (is_kallsyms) {
 294                         if (copyfile("/proc/kallsyms", filename))
 295                                goto out_free;
 296                } else if (link(realname, filename) && copyfile(name, filename))
 297                        goto out_free;
 298        }
 299
 300        len = scnprintf(linkname, size, "%s/.build-id/%.2s",
 301                       debugdir, sbuild_id);
 302
 303        if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
 304                goto out_free;
 305
 306        snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
 307        targetname = filename + strlen(debugdir) - 5;
 308        memcpy(targetname, "../..", 5);
 309
 310        if (symlink(targetname, linkname) == 0)
 311                err = 0;
 312out_free:
 313        if (!is_kallsyms)
 314                free(realname);
 315        free(filename);
 316        free(linkname);
 317        return err;
 318}
 319
 320static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
 321                                 const char *name, const char *debugdir,
 322                                 bool is_kallsyms, bool is_vdso)
 323{
 324        char sbuild_id[BUILD_ID_SIZE * 2 + 1];
 325
 326        build_id__sprintf(build_id, build_id_size, sbuild_id);
 327
 328        return build_id_cache__add_s(sbuild_id, debugdir, name,
 329                                     is_kallsyms, is_vdso);
 330}
 331
 332int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
 333{
 334        const size_t size = PATH_MAX;
 335        char *filename = zalloc(size),
 336             *linkname = zalloc(size);
 337        int err = -1;
 338
 339        if (filename == NULL || linkname == NULL)
 340                goto out_free;
 341
 342        snprintf(linkname, size, "%s/.build-id/%.2s/%s",
 343                 debugdir, sbuild_id, sbuild_id + 2);
 344
 345        if (access(linkname, F_OK))
 346                goto out_free;
 347
 348        if (readlink(linkname, filename, size - 1) < 0)
 349                goto out_free;
 350
 351        if (unlink(linkname))
 352                goto out_free;
 353
 354        /*
 355         * Since the link is relative, we must make it absolute:
 356         */
 357        snprintf(linkname, size, "%s/.build-id/%.2s/%s",
 358                 debugdir, sbuild_id, filename);
 359
 360        if (unlink(linkname))
 361                goto out_free;
 362
 363        err = 0;
 364out_free:
 365        free(filename);
 366        free(linkname);
 367        return err;
 368}
 369
 370static int dso__cache_build_id(struct dso *dso, struct machine *machine,
 371                               const char *debugdir)
 372{
 373        bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
 374        bool is_vdso = dso__is_vdso(dso);
 375        const char *name = dso->long_name;
 376        char nm[PATH_MAX];
 377
 378        if (dso__is_kcore(dso)) {
 379                is_kallsyms = true;
 380                machine__mmap_name(machine, nm, sizeof(nm));
 381                name = nm;
 382        }
 383        return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
 384                                     debugdir, is_kallsyms, is_vdso);
 385}
 386
 387static int __dsos__cache_build_ids(struct list_head *head,
 388                                   struct machine *machine, const char *debugdir)
 389{
 390        struct dso *pos;
 391        int err = 0;
 392
 393        dsos__for_each_with_build_id(pos, head)
 394                if (dso__cache_build_id(pos, machine, debugdir))
 395                        err = -1;
 396
 397        return err;
 398}
 399
 400static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
 401{
 402        int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine,
 403                                          debugdir);
 404        ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine,
 405                                       debugdir);
 406        return ret;
 407}
 408
 409int perf_session__cache_build_ids(struct perf_session *session)
 410{
 411        struct rb_node *nd;
 412        int ret;
 413
 414        if (no_buildid_cache)
 415                return 0;
 416
 417        if (mkdir(buildid_dir, 0755) != 0 && errno != EEXIST)
 418                return -1;
 419
 420        ret = machine__cache_build_ids(&session->machines.host, buildid_dir);
 421
 422        for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
 423                struct machine *pos = rb_entry(nd, struct machine, rb_node);
 424                ret |= machine__cache_build_ids(pos, buildid_dir);
 425        }
 426        return ret ? -1 : 0;
 427}
 428
 429static bool machine__read_build_ids(struct machine *machine, bool with_hits)
 430{
 431        bool ret;
 432
 433        ret  = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits);
 434        ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits);
 435        return ret;
 436}
 437
 438bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
 439{
 440        struct rb_node *nd;
 441        bool ret = machine__read_build_ids(&session->machines.host, with_hits);
 442
 443        for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
 444                struct machine *pos = rb_entry(nd, struct machine, rb_node);
 445                ret |= machine__read_build_ids(pos, with_hits);
 446        }
 447
 448        return ret;
 449}
 450