diff options
Diffstat (limited to 'tools/perf/util/build-id.c')
| -rw-r--r-- | tools/perf/util/build-id.c | 380 |
1 files changed, 203 insertions, 177 deletions
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index e32e8f2ff3bd..fdb35133fde4 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -42,10 +42,20 @@ static bool no_buildid_cache; -int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, +static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data __maybe_unused) +{ + struct map *map = node->ms.map; + + if (map) + dso__set_hit(map__dso(map)); + + return 0; +} + +int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel __maybe_unused, + struct evsel *evsel, struct machine *machine) { struct addr_location al; @@ -58,66 +68,41 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, return -1; } + addr_location__init(&al); if (thread__find_map(thread, sample->cpumode, sample->ip, &al)) - al.map->dso->hit = 1; - - thread__put(thread); - return 0; -} + dso__set_hit(map__dso(al.map)); -static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, - union perf_event *event, - struct perf_sample *sample - __maybe_unused, - struct machine *machine) -{ - struct thread *thread = machine__findnew_thread(machine, - event->fork.pid, - event->fork.tid); + addr_location__exit(&al); - dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, - event->fork.ppid, event->fork.ptid); + sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, + /*symbols=*/false, mark_dso_hit_callback, /*data=*/NULL); - if (thread) { - machine__remove_thread(machine, thread); - thread__put(thread); - } + thread__put(thread); return 0; } -struct perf_tool build_id__mark_dso_hit_ops = { - .sample = build_id__mark_dso_hit, - .mmap = perf_event__process_mmap, - .mmap2 = perf_event__process_mmap2, - .fork = perf_event__process_fork, - .exit = perf_event__exit_del_thread, - .attr = perf_event__process_attr, - .build_id = perf_event__process_build_id, - .ordered_events = true, -}; - -int build_id__sprintf(const struct build_id *build_id, char *bf) +int build_id__snprintf(const struct build_id *build_id, char *bf, size_t bf_size) { - char *bid = bf; - const u8 *raw = build_id->data; - size_t i; + size_t offs = 0; - bf[0] = 0x0; - - for (i = 0; i < build_id->size; ++i) { - sprintf(bid, "%02x", *raw); - ++raw; - bid += 2; + if (build_id->size == 0) { + /* Ensure bf is always \0 terminated. */ + if (bf_size > 0) + bf[0] = '\0'; + return 0; } - return (bid - bf) + 1; + for (size_t i = 0; i < build_id->size && offs < bf_size; ++i) + offs += snprintf(bf + offs, bf_size - offs, "%02x", build_id->data[i]); + + return offs; } -int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id) +int sysfs__snprintf_build_id(const char *root_dir, char *sbuild_id, size_t sbuild_id_size) { char notes[PATH_MAX]; - struct build_id bid; + struct build_id bid = { .size = 0, }; int ret; if (!root_dir) @@ -129,19 +114,19 @@ int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id) if (ret < 0) return ret; - return build_id__sprintf(&bid, sbuild_id); + return build_id__snprintf(&bid, sbuild_id, sbuild_id_size); } -int filename__sprintf_build_id(const char *pathname, char *sbuild_id) +int filename__snprintf_build_id(const char *pathname, char *sbuild_id, size_t sbuild_id_size) { - struct build_id bid; + struct build_id bid = { .size = 0, }; int ret; ret = filename__read_build_id(pathname, &bid); if (ret < 0) return ret; - return build_id__sprintf(&bid, sbuild_id); + return build_id__snprintf(&bid, sbuild_id, sbuild_id_size); } /* asnprintf consolidates asprintf and snprintf */ @@ -242,9 +227,9 @@ static bool build_id_cache__valid_id(char *sbuild_id) return false; if (!strcmp(pathname, DSO__NAME_KALLSYMS)) - ret = sysfs__sprintf_build_id("/", real_sbuild_id); + ret = sysfs__snprintf_build_id("/", real_sbuild_id, sizeof(real_sbuild_id)); else if (pathname[0] == '/') - ret = filename__sprintf_build_id(pathname, real_sbuild_id); + ret = filename__snprintf_build_id(pathname, real_sbuild_id, sizeof(real_sbuild_id)); else ret = -EINVAL; /* Should we support other special DSO cache? */ if (ret >= 0) @@ -270,10 +255,10 @@ char *__dso__build_id_filename(const struct dso *dso, char *bf, size_t size, bool alloc = (bf == NULL); int ret; - if (!dso->has_build_id) + if (!dso__has_build_id(dso)) return NULL; - build_id__sprintf(&dso->bid, sbuild_id); + build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); linkname = build_id_cache__linkname(sbuild_id, NULL, 0); if (!linkname) return NULL; @@ -300,12 +285,6 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size, return __dso__build_id_filename(dso, bf, size, is_debug, is_kallsyms); } -#define dsos__for_each_with_build_id(pos, head) \ - list_for_each_entry(pos, head, node) \ - if (!pos->has_build_id) \ - continue; \ - else - static int write_buildid(const char *name, size_t name_len, struct build_id *bid, pid_t pid, u16 misc, struct feat_fd *fd) { @@ -314,7 +293,7 @@ static int write_buildid(const char *name, size_t name_len, struct build_id *bid size_t len; len = name_len + 1; - len = PERF_ALIGN(len, NAME_ALIGN); + len = PERF_ALIGN(len, sizeof(u64)); memset(&b, 0, sizeof(b)); memcpy(&b.data, bid->data, bid->size); @@ -331,48 +310,56 @@ static int write_buildid(const char *name, size_t name_len, struct build_id *bid return write_padded(fd, name, name_len + 1, len); } -static int machine__write_buildid_table(struct machine *machine, - struct feat_fd *fd) +struct machine__write_buildid_table_cb_args { + struct machine *machine; + struct feat_fd *fd; + u16 kmisc, umisc; +}; + +static int machine__write_buildid_table_cb(struct dso *dso, void *data) { - int err = 0; - struct dso *pos; - u16 kmisc = PERF_RECORD_MISC_KERNEL, - umisc = PERF_RECORD_MISC_USER; + struct machine__write_buildid_table_cb_args *args = data; + const char *name; + size_t name_len; + bool in_kernel = false; - if (!machine__is_host(machine)) { - kmisc = PERF_RECORD_MISC_GUEST_KERNEL; - umisc = PERF_RECORD_MISC_GUEST_USER; - } + if (!dso__has_build_id(dso)) + return 0; + + if (!dso__hit(dso) && !dso__is_vdso(dso)) + return 0; - dsos__for_each_with_build_id(pos, &machine->dsos.head) { - const char *name; - size_t name_len; - bool in_kernel = false; + if (dso__is_vdso(dso)) { + name = dso__short_name(dso); + name_len = dso__short_name_len(dso); + } else if (dso__is_kcore(dso)) { + name = args->machine->mmap_name; + name_len = strlen(name); + } else { + name = dso__long_name(dso); + name_len = dso__long_name_len(dso); + } - if (!pos->hit && !dso__is_vdso(pos)) - continue; + in_kernel = dso__kernel(dso) || is_kernel_module(name, PERF_RECORD_MISC_CPUMODE_UNKNOWN); + return write_buildid(name, name_len, &dso__id(dso)->build_id, args->machine->pid, + in_kernel ? args->kmisc : args->umisc, args->fd); +} - if (dso__is_vdso(pos)) { - name = pos->short_name; - name_len = pos->short_name_len; - } else if (dso__is_kcore(pos)) { - name = machine->mmap_name; - name_len = strlen(name); - } else { - name = pos->long_name; - name_len = pos->long_name_len; - } +static int machine__write_buildid_table(struct machine *machine, struct feat_fd *fd) +{ + struct machine__write_buildid_table_cb_args args = { + .machine = machine, + .fd = fd, + .kmisc = PERF_RECORD_MISC_KERNEL, + .umisc = PERF_RECORD_MISC_USER, + }; - in_kernel = pos->kernel || - is_kernel_module(name, - PERF_RECORD_MISC_CPUMODE_UNKNOWN); - err = write_buildid(name, name_len, &pos->bid, machine->pid, - in_kernel ? kmisc : umisc, fd); - if (err) - break; + if (!machine__is_host(machine)) { + args.kmisc = PERF_RECORD_MISC_GUEST_KERNEL; + args.umisc = PERF_RECORD_MISC_GUEST_USER; } - return err; + return dsos__for_each_dso(&machine->dsos, machine__write_buildid_table_cb, &args); } int perf_session__write_buildid_table(struct perf_session *session, @@ -394,42 +381,6 @@ int perf_session__write_buildid_table(struct perf_session *session, return err; } -static int __dsos__hit_all(struct list_head *head) -{ - struct dso *pos; - - list_for_each_entry(pos, head, node) - pos->hit = true; - - return 0; -} - -static int machine__hit_all_dsos(struct machine *machine) -{ - return __dsos__hit_all(&machine->dsos.head); -} - -int dsos__hit_all(struct perf_session *session) -{ - struct rb_node *nd; - int err; - - err = machine__hit_all_dsos(&session->machines.host); - if (err) - return err; - - for (nd = rb_first_cached(&session->machines.guests); nd; - nd = rb_next(nd)) { - struct machine *pos = rb_entry(nd, struct machine, rb_node); - - err = machine__hit_all_dsos(pos); - if (err) - return err; - } - - return 0; -} - void disable_buildid_cache(void) { no_buildid_cache = true; @@ -564,23 +515,18 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name, struct nsinfo *nsi, bool is_kallsyms, bool is_vdso) { - char *realname = (char *)name, *filename; + char *realname = NULL, *filename; bool slash = is_kallsyms || is_vdso; - if (!slash) { + if (!slash) realname = nsinfo__realpath(name, nsi); - if (!realname) - return NULL; - } if (asprintf(&filename, "%s%s%s%s%s", buildid_dir, slash ? "/" : "", - is_vdso ? DSO__NAME_VDSO : realname, + is_vdso ? DSO__NAME_VDSO : (realname ? realname : name), sbuild_id ? "/" : "", sbuild_id ?: "") < 0) filename = NULL; - if (!slash) - free(realname); - + free(realname); return filename; } @@ -631,9 +577,12 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id, #endif static char *build_id_cache__find_debug(const char *sbuild_id, - struct nsinfo *nsi) + struct nsinfo *nsi, + const char *root_dir) { + const char *dirname = "/usr/lib/debug/.build-id/"; char *realname = NULL; + char dirbuf[PATH_MAX]; char *debugfile; struct nscookie nsc; size_t len = 0; @@ -642,8 +591,12 @@ static char *build_id_cache__find_debug(const char *sbuild_id, if (!debugfile) goto out; - len = __symbol__join_symfs(debugfile, PATH_MAX, - "/usr/lib/debug/.build-id/"); + if (root_dir) { + path__join(dirbuf, PATH_MAX, root_dir, dirname); + dirname = dirbuf; + } + + len = __symbol__join_symfs(debugfile, PATH_MAX, dirname); snprintf(debugfile + len, PATH_MAX - len, "%.2s/%s.debug", sbuild_id, sbuild_id + 2); @@ -654,17 +607,21 @@ static char *build_id_cache__find_debug(const char *sbuild_id, nsinfo__mountns_exit(&nsc); #ifdef HAVE_DEBUGINFOD_SUPPORT - if (realname == NULL) { - debuginfod_client* c = debuginfod_begin(); - if (c != NULL) { - int fd = debuginfod_find_debuginfo(c, - (const unsigned char*)sbuild_id, 0, - &realname); - if (fd >= 0) - close(fd); /* retaining reference by realname */ - debuginfod_end(c); - } - } + if (realname == NULL) { + debuginfod_client* c; + + pr_debug("Downloading debug info with build id %s\n", sbuild_id); + + c = debuginfod_begin(); + if (c != NULL) { + int fd = debuginfod_find_debuginfo(c, + (const unsigned char*)sbuild_id, 0, + &realname); + if (fd >= 0) + close(fd); /* retaining reference by realname */ + debuginfod_end(c); + } + } #endif out: @@ -674,14 +631,18 @@ out: int build_id_cache__add(const char *sbuild_id, const char *name, const char *realname, - struct nsinfo *nsi, bool is_kallsyms, bool is_vdso) + struct nsinfo *nsi, bool is_kallsyms, bool is_vdso, + const char *proper_name, const char *root_dir) { const size_t size = PATH_MAX; char *filename = NULL, *dir_name = NULL, *linkname = zalloc(size), *tmp; char *debugfile = NULL; int err = -1; - dir_name = build_id_cache__cachedir(sbuild_id, name, nsi, is_kallsyms, + if (!proper_name) + proper_name = name; + + dir_name = build_id_cache__cachedir(sbuild_id, proper_name, nsi, is_kallsyms, is_vdso); if (!dir_name) goto out_free; @@ -706,12 +667,16 @@ build_id_cache__add(const char *sbuild_id, const char *name, const char *realnam if (is_kallsyms) { if (copyfile("/proc/kallsyms", filename)) goto out_free; - } else if (nsi && nsi->need_setns) { + } else if (nsi && nsinfo__need_setns(nsi)) { if (copyfile_ns(name, filename, nsi)) goto out_free; - } else if (link(realname, filename) && errno != EEXIST && - copyfile(name, filename)) - goto out_free; + } else if (link(realname, filename) && errno != EEXIST) { + struct stat f_stat; + + if (!(stat(name, &f_stat) < 0) && + copyfile_mode(name, filename, f_stat.st_mode)) + goto out_free; + } } /* Some binaries are stripped, but have .debug files with their symbol @@ -721,7 +686,7 @@ build_id_cache__add(const char *sbuild_id, const char *name, const char *realnam */ if (!is_kallsyms && !is_vdso && strncmp(".ko", name + strlen(name) - 3, 3)) { - debugfile = build_id_cache__find_debug(sbuild_id, nsi); + debugfile = build_id_cache__find_debug(sbuild_id, nsi, root_dir); if (debugfile) { zfree(&filename); if (asprintf(&filename, "%s/%s", dir_name, @@ -730,7 +695,7 @@ build_id_cache__add(const char *sbuild_id, const char *name, const char *realnam goto out_free; } if (access(filename, F_OK)) { - if (nsi && nsi->need_setns) { + if (nsi && nsinfo__need_setns(nsi)) { if (copyfile_ns(debugfile, filename, nsi)) goto out_free; @@ -762,7 +727,7 @@ build_id_cache__add(const char *sbuild_id, const char *name, const char *realnam len = readlink(linkname, path, sizeof(path) - 1); if (len <= 0) { - pr_err("Cant read link: %s\n", linkname); + pr_err("Can't read link: %s\n", linkname); goto out_free; } path[len] = '\0'; @@ -787,8 +752,9 @@ out_free: return err; } -int build_id_cache__add_s(const char *sbuild_id, const char *name, - struct nsinfo *nsi, bool is_kallsyms, bool is_vdso) +int __build_id_cache__add_s(const char *sbuild_id, const char *name, + struct nsinfo *nsi, bool is_kallsyms, bool is_vdso, + const char *proper_name, const char *root_dir) { char *realname = NULL; int err = -1; @@ -802,8 +768,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, goto out_free; } - err = build_id_cache__add(sbuild_id, name, realname, nsi, is_kallsyms, is_vdso); - + err = build_id_cache__add(sbuild_id, name, realname, nsi, + is_kallsyms, is_vdso, proper_name, root_dir); out_free: if (!is_kallsyms) free(realname); @@ -812,14 +778,16 @@ out_free: static int build_id_cache__add_b(const struct build_id *bid, const char *name, struct nsinfo *nsi, - bool is_kallsyms, bool is_vdso) + bool is_kallsyms, bool is_vdso, + const char *proper_name, + const char *root_dir) { char sbuild_id[SBUILD_ID_SIZE]; - build_id__sprintf(bid, sbuild_id); + build_id__snprintf(bid, sbuild_id, sizeof(sbuild_id)); - return build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms, - is_vdso); + return __build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms, + is_vdso, proper_name, root_dir); } bool build_id_cache__cached(const char *sbuild_id) @@ -872,22 +840,77 @@ out_free: return err; } +static int filename__read_build_id_ns(const char *filename, + struct build_id *bid, + struct nsinfo *nsi) +{ + struct nscookie nsc; + int ret; + + nsinfo__mountns_enter(nsi, &nsc); + ret = filename__read_build_id(filename, bid); + nsinfo__mountns_exit(&nsc); + + return ret; +} + +static bool dso__build_id_mismatch(struct dso *dso, const char *name) +{ + struct build_id bid = { .size = 0, }; + bool ret = false; + + mutex_lock(dso__lock(dso)); + if (filename__read_build_id_ns(name, &bid, dso__nsinfo(dso)) >= 0) + ret = !dso__build_id_equal(dso, &bid); + + mutex_unlock(dso__lock(dso)); + + return ret; +} + static int dso__cache_build_id(struct dso *dso, struct machine *machine, void *priv __maybe_unused) { bool is_kallsyms = dso__is_kallsyms(dso); bool is_vdso = dso__is_vdso(dso); - const char *name = dso->long_name; + const char *name = dso__long_name(dso); + const char *proper_name = NULL; + const char *root_dir = NULL; + char *allocated_name = NULL; + int ret = 0; - if (!dso->has_build_id) + if (!dso__has_build_id(dso) || !dso__hit(dso)) return 0; if (dso__is_kcore(dso)) { is_kallsyms = true; name = machine->mmap_name; } - return build_id_cache__add_b(&dso->bid, name, dso->nsinfo, - is_kallsyms, is_vdso); + + if (!machine__is_host(machine)) { + if (*machine->root_dir) { + root_dir = machine->root_dir; + ret = asprintf(&allocated_name, "%s/%s", root_dir, name); + if (ret < 0) + return ret; + proper_name = name; + name = allocated_name; + } else if (is_kallsyms) { + /* Cannot get guest kallsyms */ + return 0; + } + } + + if (!is_kallsyms && dso__build_id_mismatch(dso, name)) + goto out_free; + + mutex_lock(dso__lock(dso)); + ret = build_id_cache__add_b(dso__bid(dso), name, dso__nsinfo(dso), + is_kallsyms, is_vdso, proper_name, root_dir); + mutex_unlock(dso__lock(dso)); +out_free: + free(allocated_name); + return ret; } static int @@ -924,7 +947,7 @@ int perf_session__cache_build_ids(struct perf_session *session) static bool machine__read_build_ids(struct machine *machine, bool with_hits) { - return __dsos__read_build_ids(&machine->dsos.head, with_hits); + return dsos__read_build_ids(&machine->dsos, with_hits); } bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) @@ -943,7 +966,10 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) void build_id__init(struct build_id *bid, const u8 *data, size_t size) { - WARN_ON(size > BUILD_ID_SIZE); + if (size > BUILD_ID_SIZE) { + pr_debug("Truncating build_id size from %zd\n", size); + size = BUILD_ID_SIZE; + } memcpy(bid->data, data, size); bid->size = size; } |
