xref: /linux/tools/perf/builtin-buildid-cache.c (revision 7ae811b12e419fd70b7d7159f20ed8519bbe18cc)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * builtin-buildid-cache.c
4  *
5  * Builtin buildid-cache command: Manages build-id cache
6  *
7  * Copyright (C) 2010, Red Hat Inc.
8  * Copyright (C) 2010, Arnaldo Carvalho de Melo <acme@redhat.com>
9  */
10 #include <sys/types.h>
11 #include <sys/time.h>
12 #include <time.h>
13 #include <dirent.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include "builtin.h"
17 #include "namespaces.h"
18 #include "util/cache.h"
19 #include "util/debug.h"
20 #include "util/header.h"
21 #include <subcmd/parse-options.h>
22 #include "util/strlist.h"
23 #include "util/build-id.h"
24 #include "util/session.h"
25 #include "util/dso.h"
26 #include "util/symbol.h"
27 #include "util/time-utils.h"
28 #include "util/util.h"
29 #include "util/probe-file.h"
30 
31 static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid)
32 {
33 	char root_dir[PATH_MAX];
34 	char *p;
35 
36 	strlcpy(root_dir, proc_dir, sizeof(root_dir));
37 
38 	p = strrchr(root_dir, '/');
39 	if (!p)
40 		return -1;
41 	*p = '\0';
42 	return sysfs__sprintf_build_id(root_dir, sbuildid);
43 }
44 
45 static int build_id_cache__kcore_dir(char *dir, size_t sz)
46 {
47 	return fetch_current_timestamp(dir, sz);
48 }
49 
50 static bool same_kallsyms_reloc(const char *from_dir, char *to_dir)
51 {
52 	char from[PATH_MAX];
53 	char to[PATH_MAX];
54 	const char *name;
55 	u64 addr1 = 0, addr2 = 0;
56 	int i, err = -1;
57 
58 	scnprintf(from, sizeof(from), "%s/kallsyms", from_dir);
59 	scnprintf(to, sizeof(to), "%s/kallsyms", to_dir);
60 
61 	for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) {
62 		err = kallsyms__get_function_start(from, name, &addr1);
63 		if (!err)
64 			break;
65 	}
66 
67 	if (err)
68 		return false;
69 
70 	if (kallsyms__get_function_start(to, name, &addr2))
71 		return false;
72 
73 	return addr1 == addr2;
74 }
75 
76 static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir,
77 					  size_t to_dir_sz)
78 {
79 	char from[PATH_MAX];
80 	char to[PATH_MAX];
81 	char to_subdir[PATH_MAX];
82 	struct dirent *dent;
83 	int ret = -1;
84 	DIR *d;
85 
86 	d = opendir(to_dir);
87 	if (!d)
88 		return -1;
89 
90 	scnprintf(from, sizeof(from), "%s/modules", from_dir);
91 
92 	while (1) {
93 		dent = readdir(d);
94 		if (!dent)
95 			break;
96 		if (dent->d_type != DT_DIR)
97 			continue;
98 		scnprintf(to, sizeof(to), "%s/%s/modules", to_dir,
99 			  dent->d_name);
100 		scnprintf(to_subdir, sizeof(to_subdir), "%s/%s",
101 			  to_dir, dent->d_name);
102 		if (!compare_proc_modules(from, to) &&
103 		    same_kallsyms_reloc(from_dir, to_subdir)) {
104 			strlcpy(to_dir, to_subdir, to_dir_sz);
105 			ret = 0;
106 			break;
107 		}
108 	}
109 
110 	closedir(d);
111 
112 	return ret;
113 }
114 
115 static int build_id_cache__add_kcore(const char *filename, bool force)
116 {
117 	char dir[32], sbuildid[SBUILD_ID_SIZE];
118 	char from_dir[PATH_MAX], to_dir[PATH_MAX];
119 	char *p;
120 
121 	strlcpy(from_dir, filename, sizeof(from_dir));
122 
123 	p = strrchr(from_dir, '/');
124 	if (!p || strcmp(p + 1, "kcore"))
125 		return -1;
126 	*p = '\0';
127 
128 	if (build_id_cache__kcore_buildid(from_dir, sbuildid) < 0)
129 		return -1;
130 
131 	scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s",
132 		  buildid_dir, DSO__NAME_KCORE, sbuildid);
133 
134 	if (!force &&
135 	    !build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) {
136 		pr_debug("same kcore found in %s\n", to_dir);
137 		return 0;
138 	}
139 
140 	if (build_id_cache__kcore_dir(dir, sizeof(dir)))
141 		return -1;
142 
143 	scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s/%s",
144 		  buildid_dir, DSO__NAME_KCORE, sbuildid, dir);
145 
146 	if (mkdir_p(to_dir, 0755))
147 		return -1;
148 
149 	if (kcore_copy(from_dir, to_dir)) {
150 		/* Remove YYYYmmddHHMMSShh directory */
151 		if (!rmdir(to_dir)) {
152 			p = strrchr(to_dir, '/');
153 			if (p)
154 				*p = '\0';
155 			/* Try to remove buildid directory */
156 			if (!rmdir(to_dir)) {
157 				p = strrchr(to_dir, '/');
158 				if (p)
159 					*p = '\0';
160 				/* Try to remove [kernel.kcore] directory */
161 				rmdir(to_dir);
162 			}
163 		}
164 		return -1;
165 	}
166 
167 	pr_debug("kcore added to build-id cache directory %s\n", to_dir);
168 
169 	return 0;
170 }
171 
172 static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi)
173 {
174 	char sbuild_id[SBUILD_ID_SIZE];
175 	u8 build_id[BUILD_ID_SIZE];
176 	int err;
177 	struct nscookie nsc;
178 
179 	nsinfo__mountns_enter(nsi, &nsc);
180 	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
181 	nsinfo__mountns_exit(&nsc);
182 	if (err < 0) {
183 		pr_debug("Couldn't read a build-id in %s\n", filename);
184 		return -1;
185 	}
186 
187 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
188 	err = build_id_cache__add_s(sbuild_id, filename, nsi,
189 				    false, false);
190 	pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
191 		 err ? "FAIL" : "Ok");
192 	return err;
193 }
194 
195 static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi)
196 {
197 	u8 build_id[BUILD_ID_SIZE];
198 	char sbuild_id[SBUILD_ID_SIZE];
199 	struct nscookie nsc;
200 
201 	int err;
202 
203 	nsinfo__mountns_enter(nsi, &nsc);
204 	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
205 	nsinfo__mountns_exit(&nsc);
206 	if (err < 0) {
207 		pr_debug("Couldn't read a build-id in %s\n", filename);
208 		return -1;
209 	}
210 
211 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
212 	err = build_id_cache__remove_s(sbuild_id);
213 	pr_debug("Removing %s %s: %s\n", sbuild_id, filename,
214 		 err ? "FAIL" : "Ok");
215 
216 	return err;
217 }
218 
219 static int build_id_cache__purge_path(const char *pathname, struct nsinfo *nsi)
220 {
221 	struct strlist *list;
222 	struct str_node *pos;
223 	int err;
224 
225 	err = build_id_cache__list_build_ids(pathname, nsi, &list);
226 	if (err)
227 		goto out;
228 
229 	strlist__for_each_entry(pos, list) {
230 		err = build_id_cache__remove_s(pos->s);
231 		pr_debug("Removing %s %s: %s\n", pos->s, pathname,
232 			 err ? "FAIL" : "Ok");
233 		if (err)
234 			break;
235 	}
236 	strlist__delete(list);
237 
238 out:
239 	pr_debug("Purging %s: %s\n", pathname, err ? "FAIL" : "Ok");
240 
241 	return err;
242 }
243 
244 static int build_id_cache__purge_all(void)
245 {
246 	struct strlist *list;
247 	struct str_node *pos;
248 	int err = 0;
249 	char *buf;
250 
251 	list = build_id_cache__list_all(false);
252 	if (!list) {
253 		pr_debug("Failed to get buildids: -%d\n", errno);
254 		return -EINVAL;
255 	}
256 
257 	strlist__for_each_entry(pos, list) {
258 		buf = build_id_cache__origname(pos->s);
259 		err = build_id_cache__remove_s(pos->s);
260 		pr_debug("Removing %s (%s): %s\n", buf, pos->s,
261 			 err ? "FAIL" : "Ok");
262 		free(buf);
263 		if (err)
264 			break;
265 	}
266 	strlist__delete(list);
267 
268 	pr_debug("Purged all: %s\n", err ? "FAIL" : "Ok");
269 	return err;
270 }
271 
272 static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
273 {
274 	char filename[PATH_MAX];
275 	u8 build_id[BUILD_ID_SIZE];
276 
277 	if (dso__build_id_filename(dso, filename, sizeof(filename), false) &&
278 	    filename__read_build_id(filename, build_id,
279 				    sizeof(build_id)) != sizeof(build_id)) {
280 		if (errno == ENOENT)
281 			return false;
282 
283 		pr_warning("Problems with %s file, consider removing it from the cache\n",
284 			   filename);
285 	} else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) {
286 		pr_warning("Problems with %s file, consider removing it from the cache\n",
287 			   filename);
288 	}
289 
290 	return true;
291 }
292 
293 static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp)
294 {
295 	perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0);
296 	return 0;
297 }
298 
299 static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi)
300 {
301 	u8 build_id[BUILD_ID_SIZE];
302 	char sbuild_id[SBUILD_ID_SIZE];
303 	struct nscookie nsc;
304 
305 	int err;
306 
307 	nsinfo__mountns_enter(nsi, &nsc);
308 	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
309 	nsinfo__mountns_exit(&nsc);
310 	if (err < 0) {
311 		pr_debug("Couldn't read a build-id in %s\n", filename);
312 		return -1;
313 	}
314 	err = 0;
315 
316 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
317 	if (build_id_cache__cached(sbuild_id))
318 		err = build_id_cache__remove_s(sbuild_id);
319 
320 	if (!err)
321 		err = build_id_cache__add_s(sbuild_id, filename, nsi, false,
322 					    false);
323 
324 	pr_debug("Updating %s %s: %s\n", sbuild_id, filename,
325 		 err ? "FAIL" : "Ok");
326 
327 	return err;
328 }
329 
330 static int build_id_cache__show_all(void)
331 {
332 	struct strlist *bidlist;
333 	struct str_node *nd;
334 	char *buf;
335 
336 	bidlist = build_id_cache__list_all(true);
337 	if (!bidlist) {
338 		pr_debug("Failed to get buildids: -%d\n", errno);
339 		return -1;
340 	}
341 	strlist__for_each_entry(nd, bidlist) {
342 		buf = build_id_cache__origname(nd->s);
343 		fprintf(stdout, "%s %s\n", nd->s, buf);
344 		free(buf);
345 	}
346 	strlist__delete(bidlist);
347 	return 0;
348 }
349 
350 int cmd_buildid_cache(int argc, const char **argv)
351 {
352 	struct strlist *list;
353 	struct str_node *pos;
354 	int ret = 0;
355 	int ns_id = -1;
356 	bool force = false;
357 	bool list_files = false;
358 	bool opts_flag = false;
359 	bool purge_all = false;
360 	char const *add_name_list_str = NULL,
361 		   *remove_name_list_str = NULL,
362 		   *purge_name_list_str = NULL,
363 		   *missing_filename = NULL,
364 		   *update_name_list_str = NULL,
365 		   *kcore_filename = NULL;
366 	char sbuf[STRERR_BUFSIZE];
367 
368 	struct perf_data data = {
369 		.mode  = PERF_DATA_MODE_READ,
370 	};
371 	struct perf_session *session = NULL;
372 	struct nsinfo *nsi = NULL;
373 
374 	const struct option buildid_cache_options[] = {
375 	OPT_STRING('a', "add", &add_name_list_str,
376 		   "file list", "file(s) to add"),
377 	OPT_STRING('k', "kcore", &kcore_filename,
378 		   "file", "kcore file to add"),
379 	OPT_STRING('r', "remove", &remove_name_list_str, "file list",
380 		    "file(s) to remove"),
381 	OPT_STRING('p', "purge", &purge_name_list_str, "file list",
382 		    "file(s) to remove (remove old caches too)"),
383 	OPT_BOOLEAN('P', "purge-all", &purge_all, "purge all cached files"),
384 	OPT_BOOLEAN('l', "list", &list_files, "list all cached files"),
385 	OPT_STRING('M', "missing", &missing_filename, "file",
386 		   "to find missing build ids in the cache"),
387 	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
388 	OPT_STRING('u', "update", &update_name_list_str, "file list",
389 		    "file(s) to update"),
390 	OPT_INCR('v', "verbose", &verbose, "be more verbose"),
391 	OPT_INTEGER(0, "target-ns", &ns_id, "target pid for namespace context"),
392 	OPT_END()
393 	};
394 	const char * const buildid_cache_usage[] = {
395 		"perf buildid-cache [<options>]",
396 		NULL
397 	};
398 
399 	argc = parse_options(argc, argv, buildid_cache_options,
400 			     buildid_cache_usage, 0);
401 
402 	opts_flag = add_name_list_str || kcore_filename ||
403 		remove_name_list_str || purge_name_list_str ||
404 		missing_filename || update_name_list_str ||
405 		purge_all;
406 
407 	if (argc || !(list_files || opts_flag))
408 		usage_with_options(buildid_cache_usage, buildid_cache_options);
409 
410 	/* -l is exclusive. It can not be used with other options. */
411 	if (list_files && opts_flag) {
412 		usage_with_options_msg(buildid_cache_usage,
413 			buildid_cache_options, "-l is exclusive.\n");
414 	}
415 
416 	if (ns_id > 0)
417 		nsi = nsinfo__new(ns_id);
418 
419 	if (missing_filename) {
420 		data.path  = missing_filename;
421 		data.force = force;
422 
423 		session = perf_session__new(&data, false, NULL);
424 		if (session == NULL)
425 			return -1;
426 	}
427 
428 	if (symbol__init(session ? &session->header.env : NULL) < 0)
429 		goto out;
430 
431 	setup_pager();
432 
433 	if (list_files) {
434 		ret = build_id_cache__show_all();
435 		goto out;
436 	}
437 
438 	if (add_name_list_str) {
439 		list = strlist__new(add_name_list_str, NULL);
440 		if (list) {
441 			strlist__for_each_entry(pos, list)
442 				if (build_id_cache__add_file(pos->s, nsi)) {
443 					if (errno == EEXIST) {
444 						pr_debug("%s already in the cache\n",
445 							 pos->s);
446 						continue;
447 					}
448 					pr_warning("Couldn't add %s: %s\n",
449 						   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
450 				}
451 
452 			strlist__delete(list);
453 		}
454 	}
455 
456 	if (remove_name_list_str) {
457 		list = strlist__new(remove_name_list_str, NULL);
458 		if (list) {
459 			strlist__for_each_entry(pos, list)
460 				if (build_id_cache__remove_file(pos->s, nsi)) {
461 					if (errno == ENOENT) {
462 						pr_debug("%s wasn't in the cache\n",
463 							 pos->s);
464 						continue;
465 					}
466 					pr_warning("Couldn't remove %s: %s\n",
467 						   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
468 				}
469 
470 			strlist__delete(list);
471 		}
472 	}
473 
474 	if (purge_name_list_str) {
475 		list = strlist__new(purge_name_list_str, NULL);
476 		if (list) {
477 			strlist__for_each_entry(pos, list)
478 				if (build_id_cache__purge_path(pos->s, nsi)) {
479 					if (errno == ENOENT) {
480 						pr_debug("%s wasn't in the cache\n",
481 							 pos->s);
482 						continue;
483 					}
484 					pr_warning("Couldn't remove %s: %s\n",
485 						   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
486 				}
487 
488 			strlist__delete(list);
489 		}
490 	}
491 
492 	if (purge_all) {
493 		if (build_id_cache__purge_all()) {
494 			pr_warning("Couldn't remove some caches. Error: %s.\n",
495 				str_error_r(errno, sbuf, sizeof(sbuf)));
496 		}
497 	}
498 
499 	if (missing_filename)
500 		ret = build_id_cache__fprintf_missing(session, stdout);
501 
502 	if (update_name_list_str) {
503 		list = strlist__new(update_name_list_str, NULL);
504 		if (list) {
505 			strlist__for_each_entry(pos, list)
506 				if (build_id_cache__update_file(pos->s, nsi)) {
507 					if (errno == ENOENT) {
508 						pr_debug("%s wasn't in the cache\n",
509 							 pos->s);
510 						continue;
511 					}
512 					pr_warning("Couldn't update %s: %s\n",
513 						   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
514 				}
515 
516 			strlist__delete(list);
517 		}
518 	}
519 
520 	if (kcore_filename && build_id_cache__add_kcore(kcore_filename, force))
521 		pr_warning("Couldn't add %s\n", kcore_filename);
522 
523 out:
524 	perf_session__delete(session);
525 	nsinfo__zput(nsi);
526 
527 	return ret;
528 }
529