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        struct thread *thread = machine__findnew_thread(machine, sample->pid,
  32                                                        sample->tid);
  33
  34        if (thread == NULL) {
  35                pr_err("problem processing %d event, skipping it.\n",
  36                        event->header.type);
  37                return -1;
  38        }
  39
  40        thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, sample->ip, &al);
  41
  42        if (al.map != NULL)
  43                al.map->dso->hit = 1;
  44
  45        thread__put(thread);
  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                machine__remove_thread(machine, thread);
  64                thread__put(thread);
  65        }
  66
  67        return 0;
  68}
  69
  70struct perf_tool build_id__mark_dso_hit_ops = {
  71        .sample = build_id__mark_dso_hit,
  72        .mmap   = perf_event__process_mmap,
  73        .mmap2  = perf_event__process_mmap2,
  74        .fork   = perf_event__process_fork,
  75        .exit   = perf_event__exit_del_thread,
  76        .attr            = perf_event__process_attr,
  77        .build_id        = perf_event__process_build_id,
  78        .ordered_events  = true,
  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 (bid - bf) + 1;
  94}
  95
  96int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id)
  97{
  98        char notes[PATH_MAX];
  99        u8 build_id[BUILD_ID_SIZE];
 100        int ret;
 101
 102        if (!root_dir)
 103                root_dir = "";
 104
 105        scnprintf(notes, sizeof(notes), "%s/sys/kernel/notes", root_dir);
 106
 107        ret = sysfs__read_build_id(notes, build_id, sizeof(build_id));
 108        if (ret < 0)
 109                return ret;
 110
 111        return build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
 112}
 113
 114int filename__sprintf_build_id(const char *pathname, char *sbuild_id)
 115{
 116        u8 build_id[BUILD_ID_SIZE];
 117        int ret;
 118
 119        ret = filename__read_build_id(pathname, build_id, sizeof(build_id));
 120        if (ret < 0)
 121                return ret;
 122        else if (ret != sizeof(build_id))
 123                return -EINVAL;
 124
 125        return build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
 126}
 127
 128/* asnprintf consolidates asprintf and snprintf */
 129static int asnprintf(char **strp, size_t size, const char *fmt, ...)
 130{
 131        va_list ap;
 132        int ret;
 133
 134        if (!strp)
 135                return -EINVAL;
 136
 137        va_start(ap, fmt);
 138        if (*strp)
 139                ret = vsnprintf(*strp, size, fmt, ap);
 140        else
 141                ret = vasprintf(strp, fmt, ap);
 142        va_end(ap);
 143
 144        return ret;
 145}
 146
 147static char *build_id__filename(const char *sbuild_id, char *bf, size_t size)
 148{
 149        char *tmp = bf;
 150        int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
 151                            sbuild_id, sbuild_id + 2);
 152        if (ret < 0 || (tmp && size < (unsigned int)ret))
 153                return NULL;
 154        return bf;
 155}
 156
 157char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
 158{
 159        char build_id_hex[SBUILD_ID_SIZE];
 160
 161        if (!dso->has_build_id)
 162                return NULL;
 163
 164        build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex);
 165        return build_id__filename(build_id_hex, bf, size);
 166}
 167
 168bool dso__build_id_is_kmod(const struct dso *dso, char *bf, size_t size)
 169{
 170        char *id_name, *ch;
 171        struct stat sb;
 172
 173        id_name = dso__build_id_filename(dso, bf, size);
 174        if (!id_name)
 175                goto err;
 176        if (access(id_name, F_OK))
 177                goto err;
 178        if (lstat(id_name, &sb) == -1)
 179                goto err;
 180        if ((size_t)sb.st_size > size - 1)
 181                goto err;
 182        if (readlink(id_name, bf, size - 1) < 0)
 183                goto err;
 184
 185        bf[sb.st_size] = '\0';
 186
 187        /*
 188         * link should be:
 189         * ../../lib/modules/4.4.0-rc4/kernel/net/ipv4/netfilter/nf_nat_ipv4.ko/a09fe3eb3147dafa4e3b31dbd6257e4d696bdc92
 190         */
 191        ch = strrchr(bf, '/');
 192        if (!ch)
 193                goto err;
 194        if (ch - 3 < bf)
 195                goto err;
 196
 197        return strncmp(".ko", ch - 3, 3) == 0;
 198err:
 199        /*
 200         * If dso__build_id_filename work, get id_name again,
 201         * because id_name points to bf and is broken.
 202         */
 203        if (id_name)
 204                id_name = dso__build_id_filename(dso, bf, size);
 205        pr_err("Invalid build id: %s\n", id_name ? :
 206                                         dso->long_name ? :
 207                                         dso->short_name ? :
 208                                         "[unknown]");
 209        return false;
 210}
 211
 212#define dsos__for_each_with_build_id(pos, head) \
 213        list_for_each_entry(pos, head, node)    \
 214                if (!pos->has_build_id)         \
 215                        continue;               \
 216                else
 217
 218static int write_buildid(const char *name, size_t name_len, u8 *build_id,
 219                         pid_t pid, u16 misc, int fd)
 220{
 221        int err;
 222        struct build_id_event b;
 223        size_t len;
 224
 225        len = name_len + 1;
 226        len = PERF_ALIGN(len, NAME_ALIGN);
 227
 228        memset(&b, 0, sizeof(b));
 229        memcpy(&b.build_id, build_id, BUILD_ID_SIZE);
 230        b.pid = pid;
 231        b.header.misc = misc;
 232        b.header.size = sizeof(b) + len;
 233
 234        err = writen(fd, &b, sizeof(b));
 235        if (err < 0)
 236                return err;
 237
 238        return write_padded(fd, name, name_len + 1, len);
 239}
 240
 241static int machine__write_buildid_table(struct machine *machine, int fd)
 242{
 243        int err = 0;
 244        char nm[PATH_MAX];
 245        struct dso *pos;
 246        u16 kmisc = PERF_RECORD_MISC_KERNEL,
 247            umisc = PERF_RECORD_MISC_USER;
 248
 249        if (!machine__is_host(machine)) {
 250                kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
 251                umisc = PERF_RECORD_MISC_GUEST_USER;
 252        }
 253
 254        dsos__for_each_with_build_id(pos, &machine->dsos.head) {
 255                const char *name;
 256                size_t name_len;
 257                bool in_kernel = false;
 258
 259                if (!pos->hit)
 260                        continue;
 261
 262                if (dso__is_vdso(pos)) {
 263                        name = pos->short_name;
 264                        name_len = pos->short_name_len + 1;
 265                } else if (dso__is_kcore(pos)) {
 266                        machine__mmap_name(machine, nm, sizeof(nm));
 267                        name = nm;
 268                        name_len = strlen(nm) + 1;
 269                } else {
 270                        name = pos->long_name;
 271                        name_len = pos->long_name_len + 1;
 272                }
 273
 274                in_kernel = pos->kernel ||
 275                                is_kernel_module(name,
 276                                        PERF_RECORD_MISC_CPUMODE_UNKNOWN);
 277                err = write_buildid(name, name_len, pos->build_id, machine->pid,
 278                                    in_kernel ? kmisc : umisc, fd);
 279                if (err)
 280                        break;
 281        }
 282
 283        return err;
 284}
 285
 286int perf_session__write_buildid_table(struct perf_session *session, int fd)
 287{
 288        struct rb_node *nd;
 289        int err = machine__write_buildid_table(&session->machines.host, fd);
 290
 291        if (err)
 292                return err;
 293
 294        for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
 295                struct machine *pos = rb_entry(nd, struct machine, rb_node);
 296                err = machine__write_buildid_table(pos, fd);
 297                if (err)
 298                        break;
 299        }
 300        return err;
 301}
 302
 303static int __dsos__hit_all(struct list_head *head)
 304{
 305        struct dso *pos;
 306
 307        list_for_each_entry(pos, head, node)
 308                pos->hit = true;
 309
 310        return 0;
 311}
 312
 313static int machine__hit_all_dsos(struct machine *machine)
 314{
 315        return __dsos__hit_all(&machine->dsos.head);
 316}
 317
 318int dsos__hit_all(struct perf_session *session)
 319{
 320        struct rb_node *nd;
 321        int err;
 322
 323        err = machine__hit_all_dsos(&session->machines.host);
 324        if (err)
 325                return err;
 326
 327        for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
 328                struct machine *pos = rb_entry(nd, struct machine, rb_node);
 329
 330                err = machine__hit_all_dsos(pos);
 331                if (err)
 332                        return err;
 333        }
 334
 335        return 0;
 336}
 337
 338void disable_buildid_cache(void)
 339{
 340        no_buildid_cache = true;
 341}
 342
 343static char *build_id_cache__dirname_from_path(const char *name,
 344                                               bool is_kallsyms, bool is_vdso)
 345{
 346        char *realname = (char *)name, *filename;
 347        bool slash = is_kallsyms || is_vdso;
 348
 349        if (!slash) {
 350                realname = realpath(name, NULL);
 351                if (!realname)
 352                        return NULL;
 353        }
 354
 355        if (asprintf(&filename, "%s%s%s", buildid_dir, slash ? "/" : "",
 356                     is_vdso ? DSO__NAME_VDSO : realname) < 0)
 357                filename = NULL;
 358
 359        if (!slash)
 360                free(realname);
 361
 362        return filename;
 363}
 364
 365int build_id_cache__list_build_ids(const char *pathname,
 366                                   struct strlist **result)
 367{
 368        struct strlist *list;
 369        char *dir_name;
 370        DIR *dir;
 371        struct dirent *d;
 372        int ret = 0;
 373
 374        list = strlist__new(NULL, NULL);
 375        dir_name = build_id_cache__dirname_from_path(pathname, false, false);
 376        if (!list || !dir_name) {
 377                ret = -ENOMEM;
 378                goto out;
 379        }
 380
 381        /* List up all dirents */
 382        dir = opendir(dir_name);
 383        if (!dir) {
 384                ret = -errno;
 385                goto out;
 386        }
 387
 388        while ((d = readdir(dir)) != NULL) {
 389                if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
 390                        continue;
 391                strlist__add(list, d->d_name);
 392        }
 393        closedir(dir);
 394
 395out:
 396        free(dir_name);
 397        if (ret)
 398                strlist__delete(list);
 399        else
 400                *result = list;
 401
 402        return ret;
 403}
 404
 405int build_id_cache__add_s(const char *sbuild_id, const char *name,
 406                          bool is_kallsyms, bool is_vdso)
 407{
 408        const size_t size = PATH_MAX;
 409        char *realname = NULL, *filename = NULL, *dir_name = NULL,
 410             *linkname = zalloc(size), *targetname, *tmp;
 411        int err = -1;
 412
 413        if (!is_kallsyms) {
 414                realname = realpath(name, NULL);
 415                if (!realname)
 416                        goto out_free;
 417        }
 418
 419        dir_name = build_id_cache__dirname_from_path(name, is_kallsyms, is_vdso);
 420        if (!dir_name)
 421                goto out_free;
 422
 423        if (mkdir_p(dir_name, 0755))
 424                goto out_free;
 425
 426        if (asprintf(&filename, "%s/%s", dir_name, sbuild_id) < 0) {
 427                filename = NULL;
 428                goto out_free;
 429        }
 430
 431        if (access(filename, F_OK)) {
 432                if (is_kallsyms) {
 433                         if (copyfile("/proc/kallsyms", filename))
 434                                goto out_free;
 435                } else if (link(realname, filename) && errno != EEXIST &&
 436                                copyfile(name, filename))
 437                        goto out_free;
 438        }
 439
 440        if (!build_id__filename(sbuild_id, linkname, size))
 441                goto out_free;
 442        tmp = strrchr(linkname, '/');
 443        *tmp = '\0';
 444
 445        if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
 446                goto out_free;
 447
 448        *tmp = '/';
 449        targetname = filename + strlen(buildid_dir) - 5;
 450        memcpy(targetname, "../..", 5);
 451
 452        if (symlink(targetname, linkname) == 0)
 453                err = 0;
 454out_free:
 455        if (!is_kallsyms)
 456                free(realname);
 457        free(filename);
 458        free(dir_name);
 459        free(linkname);
 460        return err;
 461}
 462
 463static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
 464                                 const char *name, bool is_kallsyms,
 465                                 bool is_vdso)
 466{
 467        char sbuild_id[SBUILD_ID_SIZE];
 468
 469        build_id__sprintf(build_id, build_id_size, sbuild_id);
 470
 471        return build_id_cache__add_s(sbuild_id, name, is_kallsyms, is_vdso);
 472}
 473
 474bool build_id_cache__cached(const char *sbuild_id)
 475{
 476        bool ret = false;
 477        char *filename = build_id__filename(sbuild_id, NULL, 0);
 478
 479        if (filename && !access(filename, F_OK))
 480                ret = true;
 481        free(filename);
 482
 483        return ret;
 484}
 485
 486int build_id_cache__remove_s(const char *sbuild_id)
 487{
 488        const size_t size = PATH_MAX;
 489        char *filename = zalloc(size),
 490             *linkname = zalloc(size), *tmp;
 491        int err = -1;
 492
 493        if (filename == NULL || linkname == NULL)
 494                goto out_free;
 495
 496        if (!build_id__filename(sbuild_id, linkname, size))
 497                goto out_free;
 498
 499        if (access(linkname, F_OK))
 500                goto out_free;
 501
 502        if (readlink(linkname, filename, size - 1) < 0)
 503                goto out_free;
 504
 505        if (unlink(linkname))
 506                goto out_free;
 507
 508        /*
 509         * Since the link is relative, we must make it absolute:
 510         */
 511        tmp = strrchr(linkname, '/') + 1;
 512        snprintf(tmp, size - (tmp - linkname), "%s", filename);
 513
 514        if (unlink(linkname))
 515                goto out_free;
 516
 517        err = 0;
 518out_free:
 519        free(filename);
 520        free(linkname);
 521        return err;
 522}
 523
 524static int dso__cache_build_id(struct dso *dso, struct machine *machine)
 525{
 526        bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
 527        bool is_vdso = dso__is_vdso(dso);
 528        const char *name = dso->long_name;
 529        char nm[PATH_MAX];
 530
 531        if (dso__is_kcore(dso)) {
 532                is_kallsyms = true;
 533                machine__mmap_name(machine, nm, sizeof(nm));
 534                name = nm;
 535        }
 536        return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
 537                                     is_kallsyms, is_vdso);
 538}
 539
 540static int __dsos__cache_build_ids(struct list_head *head,
 541                                   struct machine *machine)
 542{
 543        struct dso *pos;
 544        int err = 0;
 545
 546        dsos__for_each_with_build_id(pos, head)
 547                if (dso__cache_build_id(pos, machine))
 548                        err = -1;
 549
 550        return err;
 551}
 552
 553static int machine__cache_build_ids(struct machine *machine)
 554{
 555        return __dsos__cache_build_ids(&machine->dsos.head, machine);
 556}
 557
 558int perf_session__cache_build_ids(struct perf_session *session)
 559{
 560        struct rb_node *nd;
 561        int ret;
 562
 563        if (no_buildid_cache)
 564                return 0;
 565
 566        if (mkdir(buildid_dir, 0755) != 0 && errno != EEXIST)
 567                return -1;
 568
 569        ret = machine__cache_build_ids(&session->machines.host);
 570
 571        for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
 572                struct machine *pos = rb_entry(nd, struct machine, rb_node);
 573                ret |= machine__cache_build_ids(pos);
 574        }
 575        return ret ? -1 : 0;
 576}
 577
 578static bool machine__read_build_ids(struct machine *machine, bool with_hits)
 579{
 580        return __dsos__read_build_ids(&machine->dsos.head, with_hits);
 581}
 582
 583bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
 584{
 585        struct rb_node *nd;
 586        bool ret = machine__read_build_ids(&session->machines.host, with_hits);
 587
 588        for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
 589                struct machine *pos = rb_entry(nd, struct machine, rb_node);
 590                ret |= machine__read_build_ids(pos, with_hits);
 591        }
 592
 593        return ret;
 594}
 595