162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * build-id.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * build-id support 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2009, 2010 Red Hat Inc. 862306a36Sopenharmony_ci * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include "util.h" // lsdir(), mkdir_p(), rm_rf() 1162306a36Sopenharmony_ci#include <dirent.h> 1262306a36Sopenharmony_ci#include <errno.h> 1362306a36Sopenharmony_ci#include <stdio.h> 1462306a36Sopenharmony_ci#include <sys/stat.h> 1562306a36Sopenharmony_ci#include <sys/types.h> 1662306a36Sopenharmony_ci#include "util/copyfile.h" 1762306a36Sopenharmony_ci#include "dso.h" 1862306a36Sopenharmony_ci#include "build-id.h" 1962306a36Sopenharmony_ci#include "event.h" 2062306a36Sopenharmony_ci#include "namespaces.h" 2162306a36Sopenharmony_ci#include "map.h" 2262306a36Sopenharmony_ci#include "symbol.h" 2362306a36Sopenharmony_ci#include "thread.h" 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include "debug.h" 2662306a36Sopenharmony_ci#include "session.h" 2762306a36Sopenharmony_ci#include "tool.h" 2862306a36Sopenharmony_ci#include "header.h" 2962306a36Sopenharmony_ci#include "vdso.h" 3062306a36Sopenharmony_ci#include "path.h" 3162306a36Sopenharmony_ci#include "probe-file.h" 3262306a36Sopenharmony_ci#include "strlist.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#ifdef HAVE_DEBUGINFOD_SUPPORT 3562306a36Sopenharmony_ci#include <elfutils/debuginfod.h> 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <linux/ctype.h> 3962306a36Sopenharmony_ci#include <linux/zalloc.h> 4062306a36Sopenharmony_ci#include <linux/string.h> 4162306a36Sopenharmony_ci#include <asm/bug.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic bool no_buildid_cache; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciint build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, 4662306a36Sopenharmony_ci union perf_event *event, 4762306a36Sopenharmony_ci struct perf_sample *sample, 4862306a36Sopenharmony_ci struct evsel *evsel __maybe_unused, 4962306a36Sopenharmony_ci struct machine *machine) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct addr_location al; 5262306a36Sopenharmony_ci struct thread *thread = machine__findnew_thread(machine, sample->pid, 5362306a36Sopenharmony_ci sample->tid); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (thread == NULL) { 5662306a36Sopenharmony_ci pr_err("problem processing %d event, skipping it.\n", 5762306a36Sopenharmony_ci event->header.type); 5862306a36Sopenharmony_ci return -1; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci addr_location__init(&al); 6262306a36Sopenharmony_ci if (thread__find_map(thread, sample->cpumode, sample->ip, &al)) 6362306a36Sopenharmony_ci map__dso(al.map)->hit = 1; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci addr_location__exit(&al); 6662306a36Sopenharmony_ci thread__put(thread); 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, 7162306a36Sopenharmony_ci union perf_event *event, 7262306a36Sopenharmony_ci struct perf_sample *sample 7362306a36Sopenharmony_ci __maybe_unused, 7462306a36Sopenharmony_ci struct machine *machine) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct thread *thread = machine__findnew_thread(machine, 7762306a36Sopenharmony_ci event->fork.pid, 7862306a36Sopenharmony_ci event->fork.tid); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, 8162306a36Sopenharmony_ci event->fork.ppid, event->fork.ptid); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (thread) { 8462306a36Sopenharmony_ci machine__remove_thread(machine, thread); 8562306a36Sopenharmony_ci thread__put(thread); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct perf_tool build_id__mark_dso_hit_ops = { 9262306a36Sopenharmony_ci .sample = build_id__mark_dso_hit, 9362306a36Sopenharmony_ci .mmap = perf_event__process_mmap, 9462306a36Sopenharmony_ci .mmap2 = perf_event__process_mmap2, 9562306a36Sopenharmony_ci .fork = perf_event__process_fork, 9662306a36Sopenharmony_ci .exit = perf_event__exit_del_thread, 9762306a36Sopenharmony_ci .attr = perf_event__process_attr, 9862306a36Sopenharmony_ci .build_id = perf_event__process_build_id, 9962306a36Sopenharmony_ci .ordered_events = true, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciint build_id__sprintf(const struct build_id *build_id, char *bf) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci char *bid = bf; 10562306a36Sopenharmony_ci const u8 *raw = build_id->data; 10662306a36Sopenharmony_ci size_t i; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci bf[0] = 0x0; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (i = 0; i < build_id->size; ++i) { 11162306a36Sopenharmony_ci sprintf(bid, "%02x", *raw); 11262306a36Sopenharmony_ci ++raw; 11362306a36Sopenharmony_ci bid += 2; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return (bid - bf) + 1; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ciint sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci char notes[PATH_MAX]; 12262306a36Sopenharmony_ci struct build_id bid; 12362306a36Sopenharmony_ci int ret; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (!root_dir) 12662306a36Sopenharmony_ci root_dir = ""; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci scnprintf(notes, sizeof(notes), "%s/sys/kernel/notes", root_dir); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ret = sysfs__read_build_id(notes, &bid); 13162306a36Sopenharmony_ci if (ret < 0) 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return build_id__sprintf(&bid, sbuild_id); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciint filename__sprintf_build_id(const char *pathname, char *sbuild_id) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct build_id bid; 14062306a36Sopenharmony_ci int ret; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ret = filename__read_build_id(pathname, &bid); 14362306a36Sopenharmony_ci if (ret < 0) 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return build_id__sprintf(&bid, sbuild_id); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* asnprintf consolidates asprintf and snprintf */ 15062306a36Sopenharmony_cistatic int asnprintf(char **strp, size_t size, const char *fmt, ...) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci va_list ap; 15362306a36Sopenharmony_ci int ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (!strp) 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci va_start(ap, fmt); 15962306a36Sopenharmony_ci if (*strp) 16062306a36Sopenharmony_ci ret = vsnprintf(*strp, size, fmt, ap); 16162306a36Sopenharmony_ci else 16262306a36Sopenharmony_ci ret = vasprintf(strp, fmt, ap); 16362306a36Sopenharmony_ci va_end(ap); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cichar *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf, 16962306a36Sopenharmony_ci size_t size) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci bool retry_old = true; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci snprintf(bf, size, "%s/%s/%s/kallsyms", 17462306a36Sopenharmony_ci buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); 17562306a36Sopenharmony_ciretry: 17662306a36Sopenharmony_ci if (!access(bf, F_OK)) 17762306a36Sopenharmony_ci return bf; 17862306a36Sopenharmony_ci if (retry_old) { 17962306a36Sopenharmony_ci /* Try old style kallsyms cache */ 18062306a36Sopenharmony_ci snprintf(bf, size, "%s/%s/%s", 18162306a36Sopenharmony_ci buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); 18262306a36Sopenharmony_ci retry_old = false; 18362306a36Sopenharmony_ci goto retry; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return NULL; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cichar *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci char *tmp = bf; 19262306a36Sopenharmony_ci int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir, 19362306a36Sopenharmony_ci sbuild_id, sbuild_id + 2); 19462306a36Sopenharmony_ci if (ret < 0 || (tmp && size < (unsigned int)ret)) 19562306a36Sopenharmony_ci return NULL; 19662306a36Sopenharmony_ci return bf; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* The caller is responsible to free the returned buffer. */ 20062306a36Sopenharmony_cichar *build_id_cache__origname(const char *sbuild_id) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci char *linkname; 20362306a36Sopenharmony_ci char buf[PATH_MAX]; 20462306a36Sopenharmony_ci char *ret = NULL, *p; 20562306a36Sopenharmony_ci size_t offs = 5; /* == strlen("../..") */ 20662306a36Sopenharmony_ci ssize_t len; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci linkname = build_id_cache__linkname(sbuild_id, NULL, 0); 20962306a36Sopenharmony_ci if (!linkname) 21062306a36Sopenharmony_ci return NULL; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci len = readlink(linkname, buf, sizeof(buf) - 1); 21362306a36Sopenharmony_ci if (len <= 0) 21462306a36Sopenharmony_ci goto out; 21562306a36Sopenharmony_ci buf[len] = '\0'; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* The link should be "../..<origpath>/<sbuild_id>" */ 21862306a36Sopenharmony_ci p = strrchr(buf, '/'); /* Cut off the "/<sbuild_id>" */ 21962306a36Sopenharmony_ci if (p && (p > buf + offs)) { 22062306a36Sopenharmony_ci *p = '\0'; 22162306a36Sopenharmony_ci if (buf[offs + 1] == '[') 22262306a36Sopenharmony_ci offs++; /* 22362306a36Sopenharmony_ci * This is a DSO name, like [kernel.kallsyms]. 22462306a36Sopenharmony_ci * Skip the first '/', since this is not the 22562306a36Sopenharmony_ci * cache of a regular file. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci ret = strdup(buf + offs); /* Skip "../..[/]" */ 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ciout: 23062306a36Sopenharmony_ci free(linkname); 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* Check if the given build_id cache is valid on current running system */ 23562306a36Sopenharmony_cistatic bool build_id_cache__valid_id(char *sbuild_id) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci char real_sbuild_id[SBUILD_ID_SIZE] = ""; 23862306a36Sopenharmony_ci char *pathname; 23962306a36Sopenharmony_ci int ret = 0; 24062306a36Sopenharmony_ci bool result = false; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci pathname = build_id_cache__origname(sbuild_id); 24362306a36Sopenharmony_ci if (!pathname) 24462306a36Sopenharmony_ci return false; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!strcmp(pathname, DSO__NAME_KALLSYMS)) 24762306a36Sopenharmony_ci ret = sysfs__sprintf_build_id("/", real_sbuild_id); 24862306a36Sopenharmony_ci else if (pathname[0] == '/') 24962306a36Sopenharmony_ci ret = filename__sprintf_build_id(pathname, real_sbuild_id); 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci ret = -EINVAL; /* Should we support other special DSO cache? */ 25262306a36Sopenharmony_ci if (ret >= 0) 25362306a36Sopenharmony_ci result = (strcmp(sbuild_id, real_sbuild_id) == 0); 25462306a36Sopenharmony_ci free(pathname); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return result; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso, 26062306a36Sopenharmony_ci bool is_debug) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : (is_debug ? 26362306a36Sopenharmony_ci "debug" : "elf")); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cichar *__dso__build_id_filename(const struct dso *dso, char *bf, size_t size, 26762306a36Sopenharmony_ci bool is_debug, bool is_kallsyms) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci bool is_vdso = dso__is_vdso((struct dso *)dso); 27062306a36Sopenharmony_ci char sbuild_id[SBUILD_ID_SIZE]; 27162306a36Sopenharmony_ci char *linkname; 27262306a36Sopenharmony_ci bool alloc = (bf == NULL); 27362306a36Sopenharmony_ci int ret; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (!dso->has_build_id) 27662306a36Sopenharmony_ci return NULL; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci build_id__sprintf(&dso->bid, sbuild_id); 27962306a36Sopenharmony_ci linkname = build_id_cache__linkname(sbuild_id, NULL, 0); 28062306a36Sopenharmony_ci if (!linkname) 28162306a36Sopenharmony_ci return NULL; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Check if old style build_id cache */ 28462306a36Sopenharmony_ci if (is_regular_file(linkname)) 28562306a36Sopenharmony_ci ret = asnprintf(&bf, size, "%s", linkname); 28662306a36Sopenharmony_ci else 28762306a36Sopenharmony_ci ret = asnprintf(&bf, size, "%s/%s", linkname, 28862306a36Sopenharmony_ci build_id_cache__basename(is_kallsyms, is_vdso, 28962306a36Sopenharmony_ci is_debug)); 29062306a36Sopenharmony_ci if (ret < 0 || (!alloc && size < (unsigned int)ret)) 29162306a36Sopenharmony_ci bf = NULL; 29262306a36Sopenharmony_ci free(linkname); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return bf; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cichar *dso__build_id_filename(const struct dso *dso, char *bf, size_t size, 29862306a36Sopenharmony_ci bool is_debug) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci bool is_kallsyms = dso__is_kallsyms((struct dso *)dso); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return __dso__build_id_filename(dso, bf, size, is_debug, is_kallsyms); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int write_buildid(const char *name, size_t name_len, struct build_id *bid, 30662306a36Sopenharmony_ci pid_t pid, u16 misc, struct feat_fd *fd) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci int err; 30962306a36Sopenharmony_ci struct perf_record_header_build_id b; 31062306a36Sopenharmony_ci size_t len; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci len = name_len + 1; 31362306a36Sopenharmony_ci len = PERF_ALIGN(len, NAME_ALIGN); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci memset(&b, 0, sizeof(b)); 31662306a36Sopenharmony_ci memcpy(&b.data, bid->data, bid->size); 31762306a36Sopenharmony_ci b.size = (u8) bid->size; 31862306a36Sopenharmony_ci misc |= PERF_RECORD_MISC_BUILD_ID_SIZE; 31962306a36Sopenharmony_ci b.pid = pid; 32062306a36Sopenharmony_ci b.header.misc = misc; 32162306a36Sopenharmony_ci b.header.size = sizeof(b) + len; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci err = do_write(fd, &b, sizeof(b)); 32462306a36Sopenharmony_ci if (err < 0) 32562306a36Sopenharmony_ci return err; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return write_padded(fd, name, name_len + 1, len); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int machine__write_buildid_table(struct machine *machine, 33162306a36Sopenharmony_ci struct feat_fd *fd) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci int err = 0; 33462306a36Sopenharmony_ci struct dso *pos; 33562306a36Sopenharmony_ci u16 kmisc = PERF_RECORD_MISC_KERNEL, 33662306a36Sopenharmony_ci umisc = PERF_RECORD_MISC_USER; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!machine__is_host(machine)) { 33962306a36Sopenharmony_ci kmisc = PERF_RECORD_MISC_GUEST_KERNEL; 34062306a36Sopenharmony_ci umisc = PERF_RECORD_MISC_GUEST_USER; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci dsos__for_each_with_build_id(pos, &machine->dsos.head) { 34462306a36Sopenharmony_ci const char *name; 34562306a36Sopenharmony_ci size_t name_len; 34662306a36Sopenharmony_ci bool in_kernel = false; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (!pos->hit && !dso__is_vdso(pos)) 34962306a36Sopenharmony_ci continue; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (dso__is_vdso(pos)) { 35262306a36Sopenharmony_ci name = pos->short_name; 35362306a36Sopenharmony_ci name_len = pos->short_name_len; 35462306a36Sopenharmony_ci } else if (dso__is_kcore(pos)) { 35562306a36Sopenharmony_ci name = machine->mmap_name; 35662306a36Sopenharmony_ci name_len = strlen(name); 35762306a36Sopenharmony_ci } else { 35862306a36Sopenharmony_ci name = pos->long_name; 35962306a36Sopenharmony_ci name_len = pos->long_name_len; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci in_kernel = pos->kernel || 36362306a36Sopenharmony_ci is_kernel_module(name, 36462306a36Sopenharmony_ci PERF_RECORD_MISC_CPUMODE_UNKNOWN); 36562306a36Sopenharmony_ci err = write_buildid(name, name_len, &pos->bid, machine->pid, 36662306a36Sopenharmony_ci in_kernel ? kmisc : umisc, fd); 36762306a36Sopenharmony_ci if (err) 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return err; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ciint perf_session__write_buildid_table(struct perf_session *session, 37562306a36Sopenharmony_ci struct feat_fd *fd) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct rb_node *nd; 37862306a36Sopenharmony_ci int err = machine__write_buildid_table(&session->machines.host, fd); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (err) 38162306a36Sopenharmony_ci return err; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci for (nd = rb_first_cached(&session->machines.guests); nd; 38462306a36Sopenharmony_ci nd = rb_next(nd)) { 38562306a36Sopenharmony_ci struct machine *pos = rb_entry(nd, struct machine, rb_node); 38662306a36Sopenharmony_ci err = machine__write_buildid_table(pos, fd); 38762306a36Sopenharmony_ci if (err) 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci return err; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int __dsos__hit_all(struct list_head *head) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct dso *pos; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci list_for_each_entry(pos, head, node) 39862306a36Sopenharmony_ci pos->hit = true; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int machine__hit_all_dsos(struct machine *machine) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci return __dsos__hit_all(&machine->dsos.head); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciint dsos__hit_all(struct perf_session *session) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct rb_node *nd; 41162306a36Sopenharmony_ci int err; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci err = machine__hit_all_dsos(&session->machines.host); 41462306a36Sopenharmony_ci if (err) 41562306a36Sopenharmony_ci return err; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci for (nd = rb_first_cached(&session->machines.guests); nd; 41862306a36Sopenharmony_ci nd = rb_next(nd)) { 41962306a36Sopenharmony_ci struct machine *pos = rb_entry(nd, struct machine, rb_node); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci err = machine__hit_all_dsos(pos); 42262306a36Sopenharmony_ci if (err) 42362306a36Sopenharmony_ci return err; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_civoid disable_buildid_cache(void) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci no_buildid_cache = true; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic bool lsdir_bid_head_filter(const char *name __maybe_unused, 43562306a36Sopenharmony_ci struct dirent *d) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci return (strlen(d->d_name) == 2) && 43862306a36Sopenharmony_ci isxdigit(d->d_name[0]) && isxdigit(d->d_name[1]); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic bool lsdir_bid_tail_filter(const char *name __maybe_unused, 44262306a36Sopenharmony_ci struct dirent *d) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci int i = 0; 44562306a36Sopenharmony_ci while (isxdigit(d->d_name[i]) && i < SBUILD_ID_SIZE - 3) 44662306a36Sopenharmony_ci i++; 44762306a36Sopenharmony_ci return (i >= SBUILD_ID_MIN_SIZE - 3) && (i <= SBUILD_ID_SIZE - 3) && 44862306a36Sopenharmony_ci (d->d_name[i] == '\0'); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistruct strlist *build_id_cache__list_all(bool validonly) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct strlist *toplist, *linklist = NULL, *bidlist; 45462306a36Sopenharmony_ci struct str_node *nd, *nd2; 45562306a36Sopenharmony_ci char *topdir, *linkdir = NULL; 45662306a36Sopenharmony_ci char sbuild_id[SBUILD_ID_SIZE]; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* for filename__ functions */ 45962306a36Sopenharmony_ci if (validonly) 46062306a36Sopenharmony_ci symbol__init(NULL); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Open the top-level directory */ 46362306a36Sopenharmony_ci if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0) 46462306a36Sopenharmony_ci return NULL; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci bidlist = strlist__new(NULL, NULL); 46762306a36Sopenharmony_ci if (!bidlist) 46862306a36Sopenharmony_ci goto out; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci toplist = lsdir(topdir, lsdir_bid_head_filter); 47162306a36Sopenharmony_ci if (!toplist) { 47262306a36Sopenharmony_ci pr_debug("Error in lsdir(%s): %d\n", topdir, errno); 47362306a36Sopenharmony_ci /* If there is no buildid cache, return an empty list */ 47462306a36Sopenharmony_ci if (errno == ENOENT) 47562306a36Sopenharmony_ci goto out; 47662306a36Sopenharmony_ci goto err_out; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci strlist__for_each_entry(nd, toplist) { 48062306a36Sopenharmony_ci if (asprintf(&linkdir, "%s/%s", topdir, nd->s) < 0) 48162306a36Sopenharmony_ci goto err_out; 48262306a36Sopenharmony_ci /* Open the lower-level directory */ 48362306a36Sopenharmony_ci linklist = lsdir(linkdir, lsdir_bid_tail_filter); 48462306a36Sopenharmony_ci if (!linklist) { 48562306a36Sopenharmony_ci pr_debug("Error in lsdir(%s): %d\n", linkdir, errno); 48662306a36Sopenharmony_ci goto err_out; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci strlist__for_each_entry(nd2, linklist) { 48962306a36Sopenharmony_ci if (snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s", 49062306a36Sopenharmony_ci nd->s, nd2->s) > SBUILD_ID_SIZE - 1) 49162306a36Sopenharmony_ci goto err_out; 49262306a36Sopenharmony_ci if (validonly && !build_id_cache__valid_id(sbuild_id)) 49362306a36Sopenharmony_ci continue; 49462306a36Sopenharmony_ci if (strlist__add(bidlist, sbuild_id) < 0) 49562306a36Sopenharmony_ci goto err_out; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci strlist__delete(linklist); 49862306a36Sopenharmony_ci zfree(&linkdir); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ciout_free: 50262306a36Sopenharmony_ci strlist__delete(toplist); 50362306a36Sopenharmony_ciout: 50462306a36Sopenharmony_ci free(topdir); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return bidlist; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cierr_out: 50962306a36Sopenharmony_ci strlist__delete(linklist); 51062306a36Sopenharmony_ci zfree(&linkdir); 51162306a36Sopenharmony_ci strlist__delete(bidlist); 51262306a36Sopenharmony_ci bidlist = NULL; 51362306a36Sopenharmony_ci goto out_free; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic bool str_is_build_id(const char *maybe_sbuild_id, size_t len) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci size_t i; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 52162306a36Sopenharmony_ci if (!isxdigit(maybe_sbuild_id[i])) 52262306a36Sopenharmony_ci return false; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci return true; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/* Return the valid complete build-id */ 52862306a36Sopenharmony_cichar *build_id_cache__complement(const char *incomplete_sbuild_id) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct strlist *bidlist; 53162306a36Sopenharmony_ci struct str_node *nd, *cand = NULL; 53262306a36Sopenharmony_ci char *sbuild_id = NULL; 53362306a36Sopenharmony_ci size_t len = strlen(incomplete_sbuild_id); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (len >= SBUILD_ID_SIZE || 53662306a36Sopenharmony_ci !str_is_build_id(incomplete_sbuild_id, len)) 53762306a36Sopenharmony_ci return NULL; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci bidlist = build_id_cache__list_all(true); 54062306a36Sopenharmony_ci if (!bidlist) 54162306a36Sopenharmony_ci return NULL; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci strlist__for_each_entry(nd, bidlist) { 54462306a36Sopenharmony_ci if (strncmp(nd->s, incomplete_sbuild_id, len) != 0) 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci if (cand) { /* Error: There are more than 2 candidates. */ 54762306a36Sopenharmony_ci cand = NULL; 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci cand = nd; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci if (cand) 55362306a36Sopenharmony_ci sbuild_id = strdup(cand->s); 55462306a36Sopenharmony_ci strlist__delete(bidlist); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return sbuild_id; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cichar *build_id_cache__cachedir(const char *sbuild_id, const char *name, 56062306a36Sopenharmony_ci struct nsinfo *nsi, bool is_kallsyms, 56162306a36Sopenharmony_ci bool is_vdso) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci char *realname = NULL, *filename; 56462306a36Sopenharmony_ci bool slash = is_kallsyms || is_vdso; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!slash) 56762306a36Sopenharmony_ci realname = nsinfo__realpath(name, nsi); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (asprintf(&filename, "%s%s%s%s%s", buildid_dir, slash ? "/" : "", 57062306a36Sopenharmony_ci is_vdso ? DSO__NAME_VDSO : (realname ? realname : name), 57162306a36Sopenharmony_ci sbuild_id ? "/" : "", sbuild_id ?: "") < 0) 57262306a36Sopenharmony_ci filename = NULL; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci free(realname); 57562306a36Sopenharmony_ci return filename; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ciint build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi, 57962306a36Sopenharmony_ci struct strlist **result) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci char *dir_name; 58262306a36Sopenharmony_ci int ret = 0; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci dir_name = build_id_cache__cachedir(NULL, pathname, nsi, false, false); 58562306a36Sopenharmony_ci if (!dir_name) 58662306a36Sopenharmony_ci return -ENOMEM; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci *result = lsdir(dir_name, lsdir_no_dot_filter); 58962306a36Sopenharmony_ci if (!*result) 59062306a36Sopenharmony_ci ret = -errno; 59162306a36Sopenharmony_ci free(dir_name); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return ret; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci#if defined(HAVE_LIBELF_SUPPORT) && defined(HAVE_GELF_GETNOTE_SUPPORT) 59762306a36Sopenharmony_cistatic int build_id_cache__add_sdt_cache(const char *sbuild_id, 59862306a36Sopenharmony_ci const char *realname, 59962306a36Sopenharmony_ci struct nsinfo *nsi) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct probe_cache *cache; 60262306a36Sopenharmony_ci int ret; 60362306a36Sopenharmony_ci struct nscookie nsc; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci cache = probe_cache__new(sbuild_id, nsi); 60662306a36Sopenharmony_ci if (!cache) 60762306a36Sopenharmony_ci return -1; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci nsinfo__mountns_enter(nsi, &nsc); 61062306a36Sopenharmony_ci ret = probe_cache__scan_sdt(cache, realname); 61162306a36Sopenharmony_ci nsinfo__mountns_exit(&nsc); 61262306a36Sopenharmony_ci if (ret >= 0) { 61362306a36Sopenharmony_ci pr_debug4("Found %d SDTs in %s\n", ret, realname); 61462306a36Sopenharmony_ci if (probe_cache__commit(cache) < 0) 61562306a36Sopenharmony_ci ret = -1; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci probe_cache__delete(cache); 61862306a36Sopenharmony_ci return ret; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci#else 62162306a36Sopenharmony_ci#define build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) (0) 62262306a36Sopenharmony_ci#endif 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic char *build_id_cache__find_debug(const char *sbuild_id, 62562306a36Sopenharmony_ci struct nsinfo *nsi, 62662306a36Sopenharmony_ci const char *root_dir) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci const char *dirname = "/usr/lib/debug/.build-id/"; 62962306a36Sopenharmony_ci char *realname = NULL; 63062306a36Sopenharmony_ci char dirbuf[PATH_MAX]; 63162306a36Sopenharmony_ci char *debugfile; 63262306a36Sopenharmony_ci struct nscookie nsc; 63362306a36Sopenharmony_ci size_t len = 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci debugfile = calloc(1, PATH_MAX); 63662306a36Sopenharmony_ci if (!debugfile) 63762306a36Sopenharmony_ci goto out; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (root_dir) { 64062306a36Sopenharmony_ci path__join(dirbuf, PATH_MAX, root_dir, dirname); 64162306a36Sopenharmony_ci dirname = dirbuf; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci len = __symbol__join_symfs(debugfile, PATH_MAX, dirname); 64562306a36Sopenharmony_ci snprintf(debugfile + len, PATH_MAX - len, "%.2s/%s.debug", sbuild_id, 64662306a36Sopenharmony_ci sbuild_id + 2); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci nsinfo__mountns_enter(nsi, &nsc); 64962306a36Sopenharmony_ci realname = realpath(debugfile, NULL); 65062306a36Sopenharmony_ci if (realname && access(realname, R_OK)) 65162306a36Sopenharmony_ci zfree(&realname); 65262306a36Sopenharmony_ci nsinfo__mountns_exit(&nsc); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci#ifdef HAVE_DEBUGINFOD_SUPPORT 65562306a36Sopenharmony_ci if (realname == NULL) { 65662306a36Sopenharmony_ci debuginfod_client* c; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci pr_debug("Downloading debug info with build id %s\n", sbuild_id); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci c = debuginfod_begin(); 66162306a36Sopenharmony_ci if (c != NULL) { 66262306a36Sopenharmony_ci int fd = debuginfod_find_debuginfo(c, 66362306a36Sopenharmony_ci (const unsigned char*)sbuild_id, 0, 66462306a36Sopenharmony_ci &realname); 66562306a36Sopenharmony_ci if (fd >= 0) 66662306a36Sopenharmony_ci close(fd); /* retaining reference by realname */ 66762306a36Sopenharmony_ci debuginfod_end(c); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci#endif 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ciout: 67362306a36Sopenharmony_ci free(debugfile); 67462306a36Sopenharmony_ci return realname; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ciint 67862306a36Sopenharmony_cibuild_id_cache__add(const char *sbuild_id, const char *name, const char *realname, 67962306a36Sopenharmony_ci struct nsinfo *nsi, bool is_kallsyms, bool is_vdso, 68062306a36Sopenharmony_ci const char *proper_name, const char *root_dir) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci const size_t size = PATH_MAX; 68362306a36Sopenharmony_ci char *filename = NULL, *dir_name = NULL, *linkname = zalloc(size), *tmp; 68462306a36Sopenharmony_ci char *debugfile = NULL; 68562306a36Sopenharmony_ci int err = -1; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (!proper_name) 68862306a36Sopenharmony_ci proper_name = name; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci dir_name = build_id_cache__cachedir(sbuild_id, proper_name, nsi, is_kallsyms, 69162306a36Sopenharmony_ci is_vdso); 69262306a36Sopenharmony_ci if (!dir_name) 69362306a36Sopenharmony_ci goto out_free; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* Remove old style build-id cache */ 69662306a36Sopenharmony_ci if (is_regular_file(dir_name)) 69762306a36Sopenharmony_ci if (unlink(dir_name)) 69862306a36Sopenharmony_ci goto out_free; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (mkdir_p(dir_name, 0755)) 70162306a36Sopenharmony_ci goto out_free; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* Save the allocated buildid dirname */ 70462306a36Sopenharmony_ci if (asprintf(&filename, "%s/%s", dir_name, 70562306a36Sopenharmony_ci build_id_cache__basename(is_kallsyms, is_vdso, 70662306a36Sopenharmony_ci false)) < 0) { 70762306a36Sopenharmony_ci filename = NULL; 70862306a36Sopenharmony_ci goto out_free; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (access(filename, F_OK)) { 71262306a36Sopenharmony_ci if (is_kallsyms) { 71362306a36Sopenharmony_ci if (copyfile("/proc/kallsyms", filename)) 71462306a36Sopenharmony_ci goto out_free; 71562306a36Sopenharmony_ci } else if (nsi && nsinfo__need_setns(nsi)) { 71662306a36Sopenharmony_ci if (copyfile_ns(name, filename, nsi)) 71762306a36Sopenharmony_ci goto out_free; 71862306a36Sopenharmony_ci } else if (link(realname, filename) && errno != EEXIST) { 71962306a36Sopenharmony_ci struct stat f_stat; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (!(stat(name, &f_stat) < 0) && 72262306a36Sopenharmony_ci copyfile_mode(name, filename, f_stat.st_mode)) 72362306a36Sopenharmony_ci goto out_free; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* Some binaries are stripped, but have .debug files with their symbol 72862306a36Sopenharmony_ci * table. Check to see if we can locate one of those, since the elf 72962306a36Sopenharmony_ci * file itself may not be very useful to users of our tools without a 73062306a36Sopenharmony_ci * symtab. 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci if (!is_kallsyms && !is_vdso && 73362306a36Sopenharmony_ci strncmp(".ko", name + strlen(name) - 3, 3)) { 73462306a36Sopenharmony_ci debugfile = build_id_cache__find_debug(sbuild_id, nsi, root_dir); 73562306a36Sopenharmony_ci if (debugfile) { 73662306a36Sopenharmony_ci zfree(&filename); 73762306a36Sopenharmony_ci if (asprintf(&filename, "%s/%s", dir_name, 73862306a36Sopenharmony_ci build_id_cache__basename(false, false, true)) < 0) { 73962306a36Sopenharmony_ci filename = NULL; 74062306a36Sopenharmony_ci goto out_free; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci if (access(filename, F_OK)) { 74362306a36Sopenharmony_ci if (nsi && nsinfo__need_setns(nsi)) { 74462306a36Sopenharmony_ci if (copyfile_ns(debugfile, filename, 74562306a36Sopenharmony_ci nsi)) 74662306a36Sopenharmony_ci goto out_free; 74762306a36Sopenharmony_ci } else if (link(debugfile, filename) && 74862306a36Sopenharmony_ci errno != EEXIST && 74962306a36Sopenharmony_ci copyfile(debugfile, filename)) 75062306a36Sopenharmony_ci goto out_free; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (!build_id_cache__linkname(sbuild_id, linkname, size)) 75662306a36Sopenharmony_ci goto out_free; 75762306a36Sopenharmony_ci tmp = strrchr(linkname, '/'); 75862306a36Sopenharmony_ci *tmp = '\0'; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) 76162306a36Sopenharmony_ci goto out_free; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci *tmp = '/'; 76462306a36Sopenharmony_ci tmp = dir_name + strlen(buildid_dir) - 5; 76562306a36Sopenharmony_ci memcpy(tmp, "../..", 5); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (symlink(tmp, linkname) == 0) { 76862306a36Sopenharmony_ci err = 0; 76962306a36Sopenharmony_ci } else if (errno == EEXIST) { 77062306a36Sopenharmony_ci char path[PATH_MAX]; 77162306a36Sopenharmony_ci ssize_t len; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci len = readlink(linkname, path, sizeof(path) - 1); 77462306a36Sopenharmony_ci if (len <= 0) { 77562306a36Sopenharmony_ci pr_err("Can't read link: %s\n", linkname); 77662306a36Sopenharmony_ci goto out_free; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci path[len] = '\0'; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (strcmp(tmp, path)) { 78162306a36Sopenharmony_ci pr_debug("build <%s> already linked to %s\n", 78262306a36Sopenharmony_ci sbuild_id, linkname); 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci err = 0; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* Update SDT cache : error is just warned */ 78862306a36Sopenharmony_ci if (realname && 78962306a36Sopenharmony_ci build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) < 0) 79062306a36Sopenharmony_ci pr_debug4("Failed to update/scan SDT cache for %s\n", realname); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ciout_free: 79362306a36Sopenharmony_ci free(filename); 79462306a36Sopenharmony_ci free(debugfile); 79562306a36Sopenharmony_ci free(dir_name); 79662306a36Sopenharmony_ci free(linkname); 79762306a36Sopenharmony_ci return err; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ciint __build_id_cache__add_s(const char *sbuild_id, const char *name, 80162306a36Sopenharmony_ci struct nsinfo *nsi, bool is_kallsyms, bool is_vdso, 80262306a36Sopenharmony_ci const char *proper_name, const char *root_dir) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci char *realname = NULL; 80562306a36Sopenharmony_ci int err = -1; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (!is_kallsyms) { 80862306a36Sopenharmony_ci if (!is_vdso) 80962306a36Sopenharmony_ci realname = nsinfo__realpath(name, nsi); 81062306a36Sopenharmony_ci else 81162306a36Sopenharmony_ci realname = realpath(name, NULL); 81262306a36Sopenharmony_ci if (!realname) 81362306a36Sopenharmony_ci goto out_free; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci err = build_id_cache__add(sbuild_id, name, realname, nsi, 81762306a36Sopenharmony_ci is_kallsyms, is_vdso, proper_name, root_dir); 81862306a36Sopenharmony_ciout_free: 81962306a36Sopenharmony_ci if (!is_kallsyms) 82062306a36Sopenharmony_ci free(realname); 82162306a36Sopenharmony_ci return err; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int build_id_cache__add_b(const struct build_id *bid, 82562306a36Sopenharmony_ci const char *name, struct nsinfo *nsi, 82662306a36Sopenharmony_ci bool is_kallsyms, bool is_vdso, 82762306a36Sopenharmony_ci const char *proper_name, 82862306a36Sopenharmony_ci const char *root_dir) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci char sbuild_id[SBUILD_ID_SIZE]; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci build_id__sprintf(bid, sbuild_id); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci return __build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms, 83562306a36Sopenharmony_ci is_vdso, proper_name, root_dir); 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cibool build_id_cache__cached(const char *sbuild_id) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci bool ret = false; 84162306a36Sopenharmony_ci char *filename = build_id_cache__linkname(sbuild_id, NULL, 0); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (filename && !access(filename, F_OK)) 84462306a36Sopenharmony_ci ret = true; 84562306a36Sopenharmony_ci free(filename); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return ret; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ciint build_id_cache__remove_s(const char *sbuild_id) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci const size_t size = PATH_MAX; 85362306a36Sopenharmony_ci char *filename = zalloc(size), 85462306a36Sopenharmony_ci *linkname = zalloc(size), *tmp; 85562306a36Sopenharmony_ci int err = -1; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (filename == NULL || linkname == NULL) 85862306a36Sopenharmony_ci goto out_free; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (!build_id_cache__linkname(sbuild_id, linkname, size)) 86162306a36Sopenharmony_ci goto out_free; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (access(linkname, F_OK)) 86462306a36Sopenharmony_ci goto out_free; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (readlink(linkname, filename, size - 1) < 0) 86762306a36Sopenharmony_ci goto out_free; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (unlink(linkname)) 87062306a36Sopenharmony_ci goto out_free; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* 87362306a36Sopenharmony_ci * Since the link is relative, we must make it absolute: 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_ci tmp = strrchr(linkname, '/') + 1; 87662306a36Sopenharmony_ci snprintf(tmp, size - (tmp - linkname), "%s", filename); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (rm_rf(linkname)) 87962306a36Sopenharmony_ci goto out_free; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci err = 0; 88262306a36Sopenharmony_ciout_free: 88362306a36Sopenharmony_ci free(filename); 88462306a36Sopenharmony_ci free(linkname); 88562306a36Sopenharmony_ci return err; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic int filename__read_build_id_ns(const char *filename, 88962306a36Sopenharmony_ci struct build_id *bid, 89062306a36Sopenharmony_ci struct nsinfo *nsi) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct nscookie nsc; 89362306a36Sopenharmony_ci int ret; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci nsinfo__mountns_enter(nsi, &nsc); 89662306a36Sopenharmony_ci ret = filename__read_build_id(filename, bid); 89762306a36Sopenharmony_ci nsinfo__mountns_exit(&nsc); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return ret; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic bool dso__build_id_mismatch(struct dso *dso, const char *name) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci struct build_id bid; 90562306a36Sopenharmony_ci bool ret = false; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci mutex_lock(&dso->lock); 90862306a36Sopenharmony_ci if (filename__read_build_id_ns(name, &bid, dso->nsinfo) >= 0) 90962306a36Sopenharmony_ci ret = !dso__build_id_equal(dso, &bid); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci mutex_unlock(&dso->lock); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci return ret; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int dso__cache_build_id(struct dso *dso, struct machine *machine, 91762306a36Sopenharmony_ci void *priv __maybe_unused) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci bool is_kallsyms = dso__is_kallsyms(dso); 92062306a36Sopenharmony_ci bool is_vdso = dso__is_vdso(dso); 92162306a36Sopenharmony_ci const char *name = dso->long_name; 92262306a36Sopenharmony_ci const char *proper_name = NULL; 92362306a36Sopenharmony_ci const char *root_dir = NULL; 92462306a36Sopenharmony_ci char *allocated_name = NULL; 92562306a36Sopenharmony_ci int ret = 0; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (!dso->has_build_id) 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (dso__is_kcore(dso)) { 93162306a36Sopenharmony_ci is_kallsyms = true; 93262306a36Sopenharmony_ci name = machine->mmap_name; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (!machine__is_host(machine)) { 93662306a36Sopenharmony_ci if (*machine->root_dir) { 93762306a36Sopenharmony_ci root_dir = machine->root_dir; 93862306a36Sopenharmony_ci ret = asprintf(&allocated_name, "%s/%s", root_dir, name); 93962306a36Sopenharmony_ci if (ret < 0) 94062306a36Sopenharmony_ci return ret; 94162306a36Sopenharmony_ci proper_name = name; 94262306a36Sopenharmony_ci name = allocated_name; 94362306a36Sopenharmony_ci } else if (is_kallsyms) { 94462306a36Sopenharmony_ci /* Cannot get guest kallsyms */ 94562306a36Sopenharmony_ci return 0; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (!is_kallsyms && dso__build_id_mismatch(dso, name)) 95062306a36Sopenharmony_ci goto out_free; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci mutex_lock(&dso->lock); 95362306a36Sopenharmony_ci ret = build_id_cache__add_b(&dso->bid, name, dso->nsinfo, 95462306a36Sopenharmony_ci is_kallsyms, is_vdso, proper_name, root_dir); 95562306a36Sopenharmony_ci mutex_unlock(&dso->lock); 95662306a36Sopenharmony_ciout_free: 95762306a36Sopenharmony_ci free(allocated_name); 95862306a36Sopenharmony_ci return ret; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic int 96262306a36Sopenharmony_cimachines__for_each_dso(struct machines *machines, machine__dso_t fn, void *priv) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci int ret = machine__for_each_dso(&machines->host, fn, priv); 96562306a36Sopenharmony_ci struct rb_node *nd; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci for (nd = rb_first_cached(&machines->guests); nd; 96862306a36Sopenharmony_ci nd = rb_next(nd)) { 96962306a36Sopenharmony_ci struct machine *pos = rb_entry(nd, struct machine, rb_node); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci ret |= machine__for_each_dso(pos, fn, priv); 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci return ret ? -1 : 0; 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ciint __perf_session__cache_build_ids(struct perf_session *session, 97762306a36Sopenharmony_ci machine__dso_t fn, void *priv) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci if (no_buildid_cache) 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (mkdir(buildid_dir, 0755) != 0 && errno != EEXIST) 98362306a36Sopenharmony_ci return -1; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return machines__for_each_dso(&session->machines, fn, priv) ? -1 : 0; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ciint perf_session__cache_build_ids(struct perf_session *session) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci return __perf_session__cache_build_ids(session, dso__cache_build_id, NULL); 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic bool machine__read_build_ids(struct machine *machine, bool with_hits) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci return __dsos__read_build_ids(&machine->dsos.head, with_hits); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cibool perf_session__read_build_ids(struct perf_session *session, bool with_hits) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct rb_node *nd; 100162306a36Sopenharmony_ci bool ret = machine__read_build_ids(&session->machines.host, with_hits); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci for (nd = rb_first_cached(&session->machines.guests); nd; 100462306a36Sopenharmony_ci nd = rb_next(nd)) { 100562306a36Sopenharmony_ci struct machine *pos = rb_entry(nd, struct machine, rb_node); 100662306a36Sopenharmony_ci ret |= machine__read_build_ids(pos, with_hits); 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return ret; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_civoid build_id__init(struct build_id *bid, const u8 *data, size_t size) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci WARN_ON(size > BUILD_ID_SIZE); 101562306a36Sopenharmony_ci memcpy(bid->data, data, size); 101662306a36Sopenharmony_ci bid->size = size; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cibool build_id__is_defined(const struct build_id *bid) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci return bid && bid->size ? !!memchr_inv(bid->data, 0, bid->size) : false; 102262306a36Sopenharmony_ci} 1023