1// SPDX-License-Identifier: GPL-2.0
2/*
3 * build-id.c
4 *
5 * build-id support
6 *
7 * Copyright (C) 2009, 2010 Red Hat Inc.
8 * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
9 */
10#include "util.h" // lsdir(), mkdir_p(), rm_rf()
11#include <dirent.h>
12#include <errno.h>
13#include <stdio.h>
14#include <sys/stat.h>
15#include <sys/types.h>
16#include "util/copyfile.h"
17#include "dso.h"
18#include "build-id.h"
19#include "event.h"
20#include "namespaces.h"
21#include "map.h"
22#include "symbol.h"
23#include "thread.h"
24#include <linux/kernel.h>
25#include "debug.h"
26#include "session.h"
27#include "tool.h"
28#include "header.h"
29#include "vdso.h"
30#include "path.h"
31#include "probe-file.h"
32#include "strlist.h"
33
34#ifdef HAVE_DEBUGINFOD_SUPPORT
35#include <elfutils/debuginfod.h>
36#endif
37
38#include <linux/ctype.h>
39#include <linux/zalloc.h>
40#include <asm/bug.h>
41
42static bool no_buildid_cache;
43
44int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
45			   union perf_event *event,
46			   struct perf_sample *sample,
47			   struct evsel *evsel __maybe_unused,
48			   struct machine *machine)
49{
50	struct addr_location al;
51	struct thread *thread = machine__findnew_thread(machine, sample->pid,
52							sample->tid);
53
54	if (thread == NULL) {
55		pr_err("problem processing %d event, skipping it.\n",
56			event->header.type);
57		return -1;
58	}
59
60	if (thread__find_map(thread, sample->cpumode, sample->ip, &al))
61		al.map->dso->hit = 1;
62
63	thread__put(thread);
64	return 0;
65}
66
67static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused,
68				       union perf_event *event,
69				       struct perf_sample *sample
70				       __maybe_unused,
71				       struct machine *machine)
72{
73	struct thread *thread = machine__findnew_thread(machine,
74							event->fork.pid,
75							event->fork.tid);
76
77	dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid,
78		    event->fork.ppid, event->fork.ptid);
79
80	if (thread) {
81		machine__remove_thread(machine, thread);
82		thread__put(thread);
83	}
84
85	return 0;
86}
87
88struct perf_tool build_id__mark_dso_hit_ops = {
89	.sample	= build_id__mark_dso_hit,
90	.mmap	= perf_event__process_mmap,
91	.mmap2	= perf_event__process_mmap2,
92	.fork	= perf_event__process_fork,
93	.exit	= perf_event__exit_del_thread,
94	.attr		 = perf_event__process_attr,
95	.build_id	 = perf_event__process_build_id,
96	.ordered_events	 = true,
97};
98
99int build_id__sprintf(const struct build_id *build_id, char *bf)
100{
101	char *bid = bf;
102	const u8 *raw = build_id->data;
103	size_t i;
104
105	bf[0] = 0x0;
106
107	for (i = 0; i < build_id->size; ++i) {
108		sprintf(bid, "%02x", *raw);
109		++raw;
110		bid += 2;
111	}
112
113	return (bid - bf) + 1;
114}
115
116int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id)
117{
118	char notes[PATH_MAX];
119	struct build_id bid;
120	int ret;
121
122	if (!root_dir)
123		root_dir = "";
124
125	scnprintf(notes, sizeof(notes), "%s/sys/kernel/notes", root_dir);
126
127	ret = sysfs__read_build_id(notes, &bid);
128	if (ret < 0)
129		return ret;
130
131	return build_id__sprintf(&bid, sbuild_id);
132}
133
134int filename__sprintf_build_id(const char *pathname, char *sbuild_id)
135{
136	struct build_id bid;
137	int ret;
138
139	ret = filename__read_build_id(pathname, &bid);
140	if (ret < 0)
141		return ret;
142
143	return build_id__sprintf(&bid, sbuild_id);
144}
145
146/* asnprintf consolidates asprintf and snprintf */
147static int asnprintf(char **strp, size_t size, const char *fmt, ...)
148{
149	va_list ap;
150	int ret;
151
152	if (!strp)
153		return -EINVAL;
154
155	va_start(ap, fmt);
156	if (*strp)
157		ret = vsnprintf(*strp, size, fmt, ap);
158	else
159		ret = vasprintf(strp, fmt, ap);
160	va_end(ap);
161
162	return ret;
163}
164
165char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf,
166				    size_t size)
167{
168	bool retry_old = true;
169
170	snprintf(bf, size, "%s/%s/%s/kallsyms",
171		 buildid_dir, DSO__NAME_KALLSYMS, sbuild_id);
172retry:
173	if (!access(bf, F_OK))
174		return bf;
175	if (retry_old) {
176		/* Try old style kallsyms cache */
177		snprintf(bf, size, "%s/%s/%s",
178			 buildid_dir, DSO__NAME_KALLSYMS, sbuild_id);
179		retry_old = false;
180		goto retry;
181	}
182
183	return NULL;
184}
185
186char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size)
187{
188	char *tmp = bf;
189	int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
190			    sbuild_id, sbuild_id + 2);
191	if (ret < 0 || (tmp && size < (unsigned int)ret))
192		return NULL;
193	return bf;
194}
195
196/* The caller is responsible to free the returned buffer. */
197char *build_id_cache__origname(const char *sbuild_id)
198{
199	char *linkname;
200	char buf[PATH_MAX];
201	char *ret = NULL, *p;
202	size_t offs = 5;	/* == strlen("../..") */
203	ssize_t len;
204
205	linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
206	if (!linkname)
207		return NULL;
208
209	len = readlink(linkname, buf, sizeof(buf) - 1);
210	if (len <= 0)
211		goto out;
212	buf[len] = '\0';
213
214	/* The link should be "../..<origpath>/<sbuild_id>" */
215	p = strrchr(buf, '/');	/* Cut off the "/<sbuild_id>" */
216	if (p && (p > buf + offs)) {
217		*p = '\0';
218		if (buf[offs + 1] == '[')
219			offs++;	/*
220				 * This is a DSO name, like [kernel.kallsyms].
221				 * Skip the first '/', since this is not the
222				 * cache of a regular file.
223				 */
224		ret = strdup(buf + offs);	/* Skip "../..[/]" */
225	}
226out:
227	free(linkname);
228	return ret;
229}
230
231/* Check if the given build_id cache is valid on current running system */
232static bool build_id_cache__valid_id(char *sbuild_id)
233{
234	char real_sbuild_id[SBUILD_ID_SIZE] = "";
235	char *pathname;
236	int ret = 0;
237	bool result = false;
238
239	pathname = build_id_cache__origname(sbuild_id);
240	if (!pathname)
241		return false;
242
243	if (!strcmp(pathname, DSO__NAME_KALLSYMS))
244		ret = sysfs__sprintf_build_id("/", real_sbuild_id);
245	else if (pathname[0] == '/')
246		ret = filename__sprintf_build_id(pathname, real_sbuild_id);
247	else
248		ret = -EINVAL;	/* Should we support other special DSO cache? */
249	if (ret >= 0)
250		result = (strcmp(sbuild_id, real_sbuild_id) == 0);
251	free(pathname);
252
253	return result;
254}
255
256static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso,
257					    bool is_debug)
258{
259	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : (is_debug ?
260	    "debug" : "elf"));
261}
262
263char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size,
264			     bool is_debug)
265{
266	bool is_kallsyms = dso__is_kallsyms((struct dso *)dso);
267	bool is_vdso = dso__is_vdso((struct dso *)dso);
268	char sbuild_id[SBUILD_ID_SIZE];
269	char *linkname;
270	bool alloc = (bf == NULL);
271	int ret;
272
273	if (!dso->has_build_id)
274		return NULL;
275
276	build_id__sprintf(&dso->bid, sbuild_id);
277	linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
278	if (!linkname)
279		return NULL;
280
281	/* Check if old style build_id cache */
282	if (is_regular_file(linkname))
283		ret = asnprintf(&bf, size, "%s", linkname);
284	else
285		ret = asnprintf(&bf, size, "%s/%s", linkname,
286			 build_id_cache__basename(is_kallsyms, is_vdso,
287						  is_debug));
288	if (ret < 0 || (!alloc && size < (unsigned int)ret))
289		bf = NULL;
290	free(linkname);
291
292	return bf;
293}
294
295#define dsos__for_each_with_build_id(pos, head)	\
296	list_for_each_entry(pos, head, node)	\
297		if (!pos->has_build_id)		\
298			continue;		\
299		else
300
301static int write_buildid(const char *name, size_t name_len, struct build_id *bid,
302			 pid_t pid, u16 misc, struct feat_fd *fd)
303{
304	int err;
305	struct perf_record_header_build_id b;
306	size_t len;
307
308	len = name_len + 1;
309	len = PERF_ALIGN(len, NAME_ALIGN);
310
311	memset(&b, 0, sizeof(b));
312	memcpy(&b.data, bid->data, bid->size);
313	b.size = (u8) bid->size;
314	misc |= PERF_RECORD_MISC_BUILD_ID_SIZE;
315	b.pid = pid;
316	b.header.misc = misc;
317	b.header.size = sizeof(b) + len;
318
319	err = do_write(fd, &b, sizeof(b));
320	if (err < 0)
321		return err;
322
323	return write_padded(fd, name, name_len + 1, len);
324}
325
326static int machine__write_buildid_table(struct machine *machine,
327					struct feat_fd *fd)
328{
329	int err = 0;
330	struct dso *pos;
331	u16 kmisc = PERF_RECORD_MISC_KERNEL,
332	    umisc = PERF_RECORD_MISC_USER;
333
334	if (!machine__is_host(machine)) {
335		kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
336		umisc = PERF_RECORD_MISC_GUEST_USER;
337	}
338
339	dsos__for_each_with_build_id(pos, &machine->dsos.head) {
340		const char *name;
341		size_t name_len;
342		bool in_kernel = false;
343
344		if (!pos->hit && !dso__is_vdso(pos))
345			continue;
346
347		if (dso__is_vdso(pos)) {
348			name = pos->short_name;
349			name_len = pos->short_name_len;
350		} else if (dso__is_kcore(pos)) {
351			name = machine->mmap_name;
352			name_len = strlen(name);
353		} else {
354			name = pos->long_name;
355			name_len = pos->long_name_len;
356		}
357
358		in_kernel = pos->kernel ||
359				is_kernel_module(name,
360					PERF_RECORD_MISC_CPUMODE_UNKNOWN);
361		err = write_buildid(name, name_len, &pos->bid, machine->pid,
362				    in_kernel ? kmisc : umisc, fd);
363		if (err)
364			break;
365	}
366
367	return err;
368}
369
370int perf_session__write_buildid_table(struct perf_session *session,
371				      struct feat_fd *fd)
372{
373	struct rb_node *nd;
374	int err = machine__write_buildid_table(&session->machines.host, fd);
375
376	if (err)
377		return err;
378
379	for (nd = rb_first_cached(&session->machines.guests); nd;
380	     nd = rb_next(nd)) {
381		struct machine *pos = rb_entry(nd, struct machine, rb_node);
382		err = machine__write_buildid_table(pos, fd);
383		if (err)
384			break;
385	}
386	return err;
387}
388
389static int __dsos__hit_all(struct list_head *head)
390{
391	struct dso *pos;
392
393	list_for_each_entry(pos, head, node)
394		pos->hit = true;
395
396	return 0;
397}
398
399static int machine__hit_all_dsos(struct machine *machine)
400{
401	return __dsos__hit_all(&machine->dsos.head);
402}
403
404int dsos__hit_all(struct perf_session *session)
405{
406	struct rb_node *nd;
407	int err;
408
409	err = machine__hit_all_dsos(&session->machines.host);
410	if (err)
411		return err;
412
413	for (nd = rb_first_cached(&session->machines.guests); nd;
414	     nd = rb_next(nd)) {
415		struct machine *pos = rb_entry(nd, struct machine, rb_node);
416
417		err = machine__hit_all_dsos(pos);
418		if (err)
419			return err;
420	}
421
422	return 0;
423}
424
425void disable_buildid_cache(void)
426{
427	no_buildid_cache = true;
428}
429
430static bool lsdir_bid_head_filter(const char *name __maybe_unused,
431				  struct dirent *d)
432{
433	return (strlen(d->d_name) == 2) &&
434		isxdigit(d->d_name[0]) && isxdigit(d->d_name[1]);
435}
436
437static bool lsdir_bid_tail_filter(const char *name __maybe_unused,
438				  struct dirent *d)
439{
440	int i = 0;
441	while (isxdigit(d->d_name[i]) && i < SBUILD_ID_SIZE - 3)
442		i++;
443	return (i == SBUILD_ID_SIZE - 3) && (d->d_name[i] == '\0');
444}
445
446struct strlist *build_id_cache__list_all(bool validonly)
447{
448	struct strlist *toplist, *linklist = NULL, *bidlist;
449	struct str_node *nd, *nd2;
450	char *topdir, *linkdir = NULL;
451	char sbuild_id[SBUILD_ID_SIZE];
452
453	/* for filename__ functions */
454	if (validonly)
455		symbol__init(NULL);
456
457	/* Open the top-level directory */
458	if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0)
459		return NULL;
460
461	bidlist = strlist__new(NULL, NULL);
462	if (!bidlist)
463		goto out;
464
465	toplist = lsdir(topdir, lsdir_bid_head_filter);
466	if (!toplist) {
467		pr_debug("Error in lsdir(%s): %d\n", topdir, errno);
468		/* If there is no buildid cache, return an empty list */
469		if (errno == ENOENT)
470			goto out;
471		goto err_out;
472	}
473
474	strlist__for_each_entry(nd, toplist) {
475		if (asprintf(&linkdir, "%s/%s", topdir, nd->s) < 0)
476			goto err_out;
477		/* Open the lower-level directory */
478		linklist = lsdir(linkdir, lsdir_bid_tail_filter);
479		if (!linklist) {
480			pr_debug("Error in lsdir(%s): %d\n", linkdir, errno);
481			goto err_out;
482		}
483		strlist__for_each_entry(nd2, linklist) {
484			if (snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s",
485				     nd->s, nd2->s) != SBUILD_ID_SIZE - 1)
486				goto err_out;
487			if (validonly && !build_id_cache__valid_id(sbuild_id))
488				continue;
489			if (strlist__add(bidlist, sbuild_id) < 0)
490				goto err_out;
491		}
492		strlist__delete(linklist);
493		zfree(&linkdir);
494	}
495
496out_free:
497	strlist__delete(toplist);
498out:
499	free(topdir);
500
501	return bidlist;
502
503err_out:
504	strlist__delete(linklist);
505	zfree(&linkdir);
506	strlist__delete(bidlist);
507	bidlist = NULL;
508	goto out_free;
509}
510
511static bool str_is_build_id(const char *maybe_sbuild_id, size_t len)
512{
513	size_t i;
514
515	for (i = 0; i < len; i++) {
516		if (!isxdigit(maybe_sbuild_id[i]))
517			return false;
518	}
519	return true;
520}
521
522/* Return the valid complete build-id */
523char *build_id_cache__complement(const char *incomplete_sbuild_id)
524{
525	struct strlist *bidlist;
526	struct str_node *nd, *cand = NULL;
527	char *sbuild_id = NULL;
528	size_t len = strlen(incomplete_sbuild_id);
529
530	if (len >= SBUILD_ID_SIZE ||
531	    !str_is_build_id(incomplete_sbuild_id, len))
532		return NULL;
533
534	bidlist = build_id_cache__list_all(true);
535	if (!bidlist)
536		return NULL;
537
538	strlist__for_each_entry(nd, bidlist) {
539		if (strncmp(nd->s, incomplete_sbuild_id, len) != 0)
540			continue;
541		if (cand) {	/* Error: There are more than 2 candidates. */
542			cand = NULL;
543			break;
544		}
545		cand = nd;
546	}
547	if (cand)
548		sbuild_id = strdup(cand->s);
549	strlist__delete(bidlist);
550
551	return sbuild_id;
552}
553
554char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
555			       struct nsinfo *nsi, bool is_kallsyms,
556			       bool is_vdso)
557{
558	char *realname = (char *)name, *filename;
559	bool slash = is_kallsyms || is_vdso;
560
561	if (!slash) {
562		realname = nsinfo__realpath(name, nsi);
563		if (!realname)
564			return NULL;
565	}
566
567	if (asprintf(&filename, "%s%s%s%s%s", buildid_dir, slash ? "/" : "",
568		     is_vdso ? DSO__NAME_VDSO : realname,
569		     sbuild_id ? "/" : "", sbuild_id ?: "") < 0)
570		filename = NULL;
571
572	if (!slash)
573		free(realname);
574
575	return filename;
576}
577
578int build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi,
579				   struct strlist **result)
580{
581	char *dir_name;
582	int ret = 0;
583
584	dir_name = build_id_cache__cachedir(NULL, pathname, nsi, false, false);
585	if (!dir_name)
586		return -ENOMEM;
587
588	*result = lsdir(dir_name, lsdir_no_dot_filter);
589	if (!*result)
590		ret = -errno;
591	free(dir_name);
592
593	return ret;
594}
595
596#if defined(HAVE_LIBELF_SUPPORT) && defined(HAVE_GELF_GETNOTE_SUPPORT)
597static int build_id_cache__add_sdt_cache(const char *sbuild_id,
598					  const char *realname,
599					  struct nsinfo *nsi)
600{
601	struct probe_cache *cache;
602	int ret;
603	struct nscookie nsc;
604
605	cache = probe_cache__new(sbuild_id, nsi);
606	if (!cache)
607		return -1;
608
609	nsinfo__mountns_enter(nsi, &nsc);
610	ret = probe_cache__scan_sdt(cache, realname);
611	nsinfo__mountns_exit(&nsc);
612	if (ret >= 0) {
613		pr_debug4("Found %d SDTs in %s\n", ret, realname);
614		if (probe_cache__commit(cache) < 0)
615			ret = -1;
616	}
617	probe_cache__delete(cache);
618	return ret;
619}
620#else
621#define build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) (0)
622#endif
623
624static char *build_id_cache__find_debug(const char *sbuild_id,
625					struct nsinfo *nsi)
626{
627	char *realname = NULL;
628	char *debugfile;
629	struct nscookie nsc;
630	size_t len = 0;
631
632	debugfile = calloc(1, PATH_MAX);
633	if (!debugfile)
634		goto out;
635
636	len = __symbol__join_symfs(debugfile, PATH_MAX,
637				   "/usr/lib/debug/.build-id/");
638	snprintf(debugfile + len, PATH_MAX - len, "%.2s/%s.debug", sbuild_id,
639		 sbuild_id + 2);
640
641	nsinfo__mountns_enter(nsi, &nsc);
642	realname = realpath(debugfile, NULL);
643	if (realname && access(realname, R_OK))
644		zfree(&realname);
645	nsinfo__mountns_exit(&nsc);
646
647#ifdef HAVE_DEBUGINFOD_SUPPORT
648        if (realname == NULL) {
649                debuginfod_client* c = debuginfod_begin();
650                if (c != NULL) {
651                        int fd = debuginfod_find_debuginfo(c,
652                                                           (const unsigned char*)sbuild_id, 0,
653                                                           &realname);
654                        if (fd >= 0)
655                                close(fd); /* retaining reference by realname */
656                        debuginfod_end(c);
657                }
658        }
659#endif
660
661out:
662	free(debugfile);
663	return realname;
664}
665
666int build_id_cache__add_s(const char *sbuild_id, const char *name,
667			  struct nsinfo *nsi, bool is_kallsyms, bool is_vdso)
668{
669	const size_t size = PATH_MAX;
670	char *realname = NULL, *filename = NULL, *dir_name = NULL,
671	     *linkname = zalloc(size), *tmp;
672	char *debugfile = NULL;
673	int err = -1;
674
675	if (!is_kallsyms) {
676		if (!is_vdso)
677			realname = nsinfo__realpath(name, nsi);
678		else
679			realname = realpath(name, NULL);
680		if (!realname)
681			goto out_free;
682	}
683
684	dir_name = build_id_cache__cachedir(sbuild_id, name, nsi, is_kallsyms,
685					    is_vdso);
686	if (!dir_name)
687		goto out_free;
688
689	/* Remove old style build-id cache */
690	if (is_regular_file(dir_name))
691		if (unlink(dir_name))
692			goto out_free;
693
694	if (mkdir_p(dir_name, 0755))
695		goto out_free;
696
697	/* Save the allocated buildid dirname */
698	if (asprintf(&filename, "%s/%s", dir_name,
699		     build_id_cache__basename(is_kallsyms, is_vdso,
700		     false)) < 0) {
701		filename = NULL;
702		goto out_free;
703	}
704
705	if (access(filename, F_OK)) {
706		if (is_kallsyms) {
707			if (copyfile("/proc/kallsyms", filename))
708				goto out_free;
709		} else if (nsi && nsi->need_setns) {
710			if (copyfile_ns(name, filename, nsi))
711				goto out_free;
712		} else if (link(realname, filename) && errno != EEXIST &&
713				copyfile(name, filename))
714			goto out_free;
715	}
716
717	/* Some binaries are stripped, but have .debug files with their symbol
718	 * table.  Check to see if we can locate one of those, since the elf
719	 * file itself may not be very useful to users of our tools without a
720	 * symtab.
721	 */
722	if (!is_kallsyms && !is_vdso &&
723	    strncmp(".ko", name + strlen(name) - 3, 3)) {
724		debugfile = build_id_cache__find_debug(sbuild_id, nsi);
725		if (debugfile) {
726			zfree(&filename);
727			if (asprintf(&filename, "%s/%s", dir_name,
728			    build_id_cache__basename(false, false, true)) < 0) {
729				filename = NULL;
730				goto out_free;
731			}
732			if (access(filename, F_OK)) {
733				if (nsi && nsi->need_setns) {
734					if (copyfile_ns(debugfile, filename,
735							nsi))
736						goto out_free;
737				} else if (link(debugfile, filename) &&
738						errno != EEXIST &&
739						copyfile(debugfile, filename))
740					goto out_free;
741			}
742		}
743	}
744
745	if (!build_id_cache__linkname(sbuild_id, linkname, size))
746		goto out_free;
747	tmp = strrchr(linkname, '/');
748	*tmp = '\0';
749
750	if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
751		goto out_free;
752
753	*tmp = '/';
754	tmp = dir_name + strlen(buildid_dir) - 5;
755	memcpy(tmp, "../..", 5);
756
757	if (symlink(tmp, linkname) == 0)
758		err = 0;
759
760	/* Update SDT cache : error is just warned */
761	if (realname &&
762	    build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) < 0)
763		pr_debug4("Failed to update/scan SDT cache for %s\n", realname);
764
765out_free:
766	if (!is_kallsyms)
767		free(realname);
768	free(filename);
769	free(debugfile);
770	free(dir_name);
771	free(linkname);
772	return err;
773}
774
775static int build_id_cache__add_b(const struct build_id *bid,
776				 const char *name, struct nsinfo *nsi,
777				 bool is_kallsyms, bool is_vdso)
778{
779	char sbuild_id[SBUILD_ID_SIZE];
780
781	build_id__sprintf(bid, sbuild_id);
782
783	return build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms,
784				     is_vdso);
785}
786
787bool build_id_cache__cached(const char *sbuild_id)
788{
789	bool ret = false;
790	char *filename = build_id_cache__linkname(sbuild_id, NULL, 0);
791
792	if (filename && !access(filename, F_OK))
793		ret = true;
794	free(filename);
795
796	return ret;
797}
798
799int build_id_cache__remove_s(const char *sbuild_id)
800{
801	const size_t size = PATH_MAX;
802	char *filename = zalloc(size),
803	     *linkname = zalloc(size), *tmp;
804	int err = -1;
805
806	if (filename == NULL || linkname == NULL)
807		goto out_free;
808
809	if (!build_id_cache__linkname(sbuild_id, linkname, size))
810		goto out_free;
811
812	if (access(linkname, F_OK))
813		goto out_free;
814
815	if (readlink(linkname, filename, size - 1) < 0)
816		goto out_free;
817
818	if (unlink(linkname))
819		goto out_free;
820
821	/*
822	 * Since the link is relative, we must make it absolute:
823	 */
824	tmp = strrchr(linkname, '/') + 1;
825	snprintf(tmp, size - (tmp - linkname), "%s", filename);
826
827	if (rm_rf(linkname))
828		goto out_free;
829
830	err = 0;
831out_free:
832	free(filename);
833	free(linkname);
834	return err;
835}
836
837static int dso__cache_build_id(struct dso *dso, struct machine *machine)
838{
839	bool is_kallsyms = dso__is_kallsyms(dso);
840	bool is_vdso = dso__is_vdso(dso);
841	const char *name = dso->long_name;
842
843	if (dso__is_kcore(dso)) {
844		is_kallsyms = true;
845		name = machine->mmap_name;
846	}
847	return build_id_cache__add_b(&dso->bid, name, dso->nsinfo,
848				     is_kallsyms, is_vdso);
849}
850
851static int __dsos__cache_build_ids(struct list_head *head,
852				   struct machine *machine)
853{
854	struct dso *pos;
855	int err = 0;
856
857	dsos__for_each_with_build_id(pos, head)
858		if (dso__cache_build_id(pos, machine))
859			err = -1;
860
861	return err;
862}
863
864static int machine__cache_build_ids(struct machine *machine)
865{
866	return __dsos__cache_build_ids(&machine->dsos.head, machine);
867}
868
869int perf_session__cache_build_ids(struct perf_session *session)
870{
871	struct rb_node *nd;
872	int ret;
873
874	if (no_buildid_cache)
875		return 0;
876
877	if (mkdir(buildid_dir, 0755) != 0 && errno != EEXIST)
878		return -1;
879
880	ret = machine__cache_build_ids(&session->machines.host);
881
882	for (nd = rb_first_cached(&session->machines.guests); nd;
883	     nd = rb_next(nd)) {
884		struct machine *pos = rb_entry(nd, struct machine, rb_node);
885		ret |= machine__cache_build_ids(pos);
886	}
887	return ret ? -1 : 0;
888}
889
890static bool machine__read_build_ids(struct machine *machine, bool with_hits)
891{
892	return __dsos__read_build_ids(&machine->dsos.head, with_hits);
893}
894
895bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
896{
897	struct rb_node *nd;
898	bool ret = machine__read_build_ids(&session->machines.host, with_hits);
899
900	for (nd = rb_first_cached(&session->machines.guests); nd;
901	     nd = rb_next(nd)) {
902		struct machine *pos = rb_entry(nd, struct machine, rb_node);
903		ret |= machine__read_build_ids(pos, with_hits);
904	}
905
906	return ret;
907}
908
909void build_id__init(struct build_id *bid, const u8 *data, size_t size)
910{
911	WARN_ON(size > BUILD_ID_SIZE);
912	memcpy(bid->data, data, size);
913	bid->size = size;
914}
915