builtin-buildid-cache.c 12.4 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3 4 5 6 7 8 9
/*
 * builtin-buildid-cache.c
 *
 * Builtin buildid-cache command: Manages build-id cache
 *
 * Copyright (C) 2010, Red Hat Inc.
 * Copyright (C) 2010, Arnaldo Carvalho de Melo <acme@redhat.com>
 */
10 11 12 13
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <dirent.h>
14
#include <errno.h>
15
#include <unistd.h>
16
#include "builtin.h"
17
#include "namespaces.h"
18 19
#include "util/debug.h"
#include "util/header.h"
20
#include <subcmd/pager.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
#include <linux/string.h>
31
#include <linux/err.h>
32

33 34 35 36 37 38 39 40 41 42 43
static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid)
{
	char root_dir[PATH_MAX];
	char *p;

	strlcpy(root_dir, proc_dir, sizeof(root_dir));

	p = strrchr(root_dir, '/');
	if (!p)
		return -1;
	*p = '\0';
44
	return sysfs__sprintf_build_id(root_dir, sbuildid);
45 46 47 48
}

static int build_id_cache__kcore_dir(char *dir, size_t sz)
{
49
	return fetch_current_timestamp(dir, sz);
50 51
}

52 53 54 55 56 57
static bool same_kallsyms_reloc(const char *from_dir, char *to_dir)
{
	char from[PATH_MAX];
	char to[PATH_MAX];
	const char *name;
	u64 addr1 = 0, addr2 = 0;
58
	int i, err = -1;
59 60 61 62 63

	scnprintf(from, sizeof(from), "%s/kallsyms", from_dir);
	scnprintf(to, sizeof(to), "%s/kallsyms", to_dir);

	for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) {
64 65
		err = kallsyms__get_function_start(from, name, &addr1);
		if (!err)
66 67 68
			break;
	}

69 70 71 72 73
	if (err)
		return false;

	if (kallsyms__get_function_start(to, name, &addr2))
		return false;
74 75 76 77

	return addr1 == addr2;
}

78 79 80 81 82
static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir,
					  size_t to_dir_sz)
{
	char from[PATH_MAX];
	char to[PATH_MAX];
83
	char to_subdir[PATH_MAX];
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	struct dirent *dent;
	int ret = -1;
	DIR *d;

	d = opendir(to_dir);
	if (!d)
		return -1;

	scnprintf(from, sizeof(from), "%s/modules", from_dir);

	while (1) {
		dent = readdir(d);
		if (!dent)
			break;
		if (dent->d_type != DT_DIR)
			continue;
		scnprintf(to, sizeof(to), "%s/%s/modules", to_dir,
			  dent->d_name);
102 103 104 105 106
		scnprintf(to_subdir, sizeof(to_subdir), "%s/%s",
			  to_dir, dent->d_name);
		if (!compare_proc_modules(from, to) &&
		    same_kallsyms_reloc(from_dir, to_subdir)) {
			strlcpy(to_dir, to_subdir, to_dir_sz);
107 108 109 110 111 112 113 114 115 116
			ret = 0;
			break;
		}
	}

	closedir(d);

	return ret;
}

117
static int build_id_cache__add_kcore(const char *filename, bool force)
118
{
119
	char dir[32], sbuildid[SBUILD_ID_SIZE];
120 121 122 123 124 125 126 127 128 129
	char from_dir[PATH_MAX], to_dir[PATH_MAX];
	char *p;

	strlcpy(from_dir, filename, sizeof(from_dir));

	p = strrchr(from_dir, '/');
	if (!p || strcmp(p + 1, "kcore"))
		return -1;
	*p = '\0';

130
	if (build_id_cache__kcore_buildid(from_dir, sbuildid) < 0)
131 132
		return -1;

133 134
	scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s",
		  buildid_dir, DSO__NAME_KCORE, sbuildid);
135

136 137
	if (!force &&
	    !build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) {
138 139 140 141 142 143 144
		pr_debug("same kcore found in %s\n", to_dir);
		return 0;
	}

	if (build_id_cache__kcore_dir(dir, sizeof(dir)))
		return -1;

145 146
	scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s/%s",
		  buildid_dir, DSO__NAME_KCORE, sbuildid, dir);
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

	if (mkdir_p(to_dir, 0755))
		return -1;

	if (kcore_copy(from_dir, to_dir)) {
		/* Remove YYYYmmddHHMMSShh directory */
		if (!rmdir(to_dir)) {
			p = strrchr(to_dir, '/');
			if (p)
				*p = '\0';
			/* Try to remove buildid directory */
			if (!rmdir(to_dir)) {
				p = strrchr(to_dir, '/');
				if (p)
					*p = '\0';
				/* Try to remove [kernel.kcore] directory */
				rmdir(to_dir);
			}
		}
		return -1;
	}

	pr_debug("kcore added to build-id cache directory %s\n", to_dir);

	return 0;
}

174
static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi)
175
{
176
	char sbuild_id[SBUILD_ID_SIZE];
177
	struct build_id bid;
178
	int err;
179
	struct nscookie nsc;
180

181
	nsinfo__mountns_enter(nsi, &nsc);
182
	err = filename__read_build_id(filename, &bid);
183 184
	nsinfo__mountns_exit(&nsc);
	if (err < 0) {
185 186 187 188
		pr_debug("Couldn't read a build-id in %s\n", filename);
		return -1;
	}

189
	build_id__sprintf(bid.data, sizeof(bid.data), sbuild_id);
190
	err = build_id_cache__add_s(sbuild_id, filename, nsi,
191
				    false, false);
192 193
	pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
		 err ? "FAIL" : "Ok");
194 195 196
	return err;
}

197
static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi)
198
{
199
	char sbuild_id[SBUILD_ID_SIZE];
200
	struct build_id bid;
201
	struct nscookie nsc;
202 203 204

	int err;

205
	nsinfo__mountns_enter(nsi, &nsc);
206
	err = filename__read_build_id(filename, &bid);
207 208
	nsinfo__mountns_exit(&nsc);
	if (err < 0) {
209 210 211 212
		pr_debug("Couldn't read a build-id in %s\n", filename);
		return -1;
	}

213
	build_id__sprintf(bid.data, sizeof(bid.data), sbuild_id);
214
	err = build_id_cache__remove_s(sbuild_id);
215 216
	pr_debug("Removing %s %s: %s\n", sbuild_id, filename,
		 err ? "FAIL" : "Ok");
217 218 219 220

	return err;
}

221
static int build_id_cache__purge_path(const char *pathname, struct nsinfo *nsi)
222 223 224 225 226
{
	struct strlist *list;
	struct str_node *pos;
	int err;

227
	err = build_id_cache__list_build_ids(pathname, nsi, &list);
228 229 230
	if (err)
		goto out;

231
	strlist__for_each_entry(pos, list) {
232
		err = build_id_cache__remove_s(pos->s);
233 234
		pr_debug("Removing %s %s: %s\n", pos->s, pathname,
			 err ? "FAIL" : "Ok");
235 236 237 238 239 240
		if (err)
			break;
	}
	strlist__delete(list);

out:
241
	pr_debug("Purging %s: %s\n", pathname, err ? "FAIL" : "Ok");
242 243 244 245

	return err;
}

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
static int build_id_cache__purge_all(void)
{
	struct strlist *list;
	struct str_node *pos;
	int err = 0;
	char *buf;

	list = build_id_cache__list_all(false);
	if (!list) {
		pr_debug("Failed to get buildids: -%d\n", errno);
		return -EINVAL;
	}

	strlist__for_each_entry(pos, list) {
		buf = build_id_cache__origname(pos->s);
		err = build_id_cache__remove_s(pos->s);
		pr_debug("Removing %s (%s): %s\n", buf, pos->s,
			 err ? "FAIL" : "Ok");
		free(buf);
		if (err)
			break;
	}
	strlist__delete(list);

	pr_debug("Purged all: %s\n", err ? "FAIL" : "Ok");
	return err;
}

274 275 276
static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
{
	char filename[PATH_MAX];
277
	struct build_id bid;
278

279
	if (dso__build_id_filename(dso, filename, sizeof(filename), false) &&
280
	    filename__read_build_id(filename, &bid) == -1) {
281 282 283
		if (errno == ENOENT)
			return false;

284
		pr_warning("Problems with %s file, consider removing it from the cache\n",
285
			   filename);
286
	} else if (memcmp(dso->bid.data, bid.data, bid.size)) {
287
		pr_warning("Problems with %s file, consider removing it from the cache\n",
288 289 290 291 292 293
			   filename);
	}

	return true;
}

294
static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp)
295 296 297 298 299
{
	perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0);
	return 0;
}

300
static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi)
301
{
302
	char sbuild_id[SBUILD_ID_SIZE];
303
	struct build_id bid;
304
	struct nscookie nsc;
305

306
	int err;
307

308
	nsinfo__mountns_enter(nsi, &nsc);
309
	err = filename__read_build_id(filename, &bid);
310 311
	nsinfo__mountns_exit(&nsc);
	if (err < 0) {
312 313 314
		pr_debug("Couldn't read a build-id in %s\n", filename);
		return -1;
	}
315
	err = 0;
316

317
	build_id__sprintf(bid.data, sizeof(bid.data), sbuild_id);
318 319 320
	if (build_id_cache__cached(sbuild_id))
		err = build_id_cache__remove_s(sbuild_id);

321
	if (!err)
322 323
		err = build_id_cache__add_s(sbuild_id, filename, nsi, false,
					    false);
324

325 326
	pr_debug("Updating %s %s: %s\n", sbuild_id, filename,
		 err ? "FAIL" : "Ok");
327 328 329 330

	return err;
}

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
static int build_id_cache__show_all(void)
{
	struct strlist *bidlist;
	struct str_node *nd;
	char *buf;

	bidlist = build_id_cache__list_all(true);
	if (!bidlist) {
		pr_debug("Failed to get buildids: -%d\n", errno);
		return -1;
	}
	strlist__for_each_entry(nd, bidlist) {
		buf = build_id_cache__origname(nd->s);
		fprintf(stdout, "%s %s\n", nd->s, buf);
		free(buf);
	}
	strlist__delete(bidlist);
	return 0;
}

351
int cmd_buildid_cache(int argc, const char **argv)
352 353 354
{
	struct strlist *list;
	struct str_node *pos;
355
	int ret = 0;
356
	int ns_id = -1;
357
	bool force = false;
358 359
	bool list_files = false;
	bool opts_flag = false;
360
	bool purge_all = false;
361
	char const *add_name_list_str = NULL,
362
		   *remove_name_list_str = NULL,
363
		   *purge_name_list_str = NULL,
364
		   *missing_filename = NULL,
365
		   *update_name_list_str = NULL,
366
		   *kcore_filename = NULL;
367
	char sbuf[STRERR_BUFSIZE];
368

369
	struct perf_data data = {
370 371 372
		.mode  = PERF_DATA_MODE_READ,
	};
	struct perf_session *session = NULL;
373
	struct nsinfo *nsi = NULL;
374

375 376 377
	const struct option buildid_cache_options[] = {
	OPT_STRING('a', "add", &add_name_list_str,
		   "file list", "file(s) to add"),
378 379
	OPT_STRING('k', "kcore", &kcore_filename,
		   "file", "kcore file to add"),
380 381
	OPT_STRING('r', "remove", &remove_name_list_str, "file list",
		    "file(s) to remove"),
382 383
	OPT_STRING('p', "purge", &purge_name_list_str, "file list",
		    "file(s) to remove (remove old caches too)"),
384
	OPT_BOOLEAN('P', "purge-all", &purge_all, "purge all cached files"),
385
	OPT_BOOLEAN('l', "list", &list_files, "list all cached files"),
386 387 388
	OPT_STRING('M', "missing", &missing_filename, "file",
		   "to find missing build ids in the cache"),
	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
389 390
	OPT_STRING('u', "update", &update_name_list_str, "file list",
		    "file(s) to update"),
391
	OPT_INCR('v', "verbose", &verbose, "be more verbose"),
392
	OPT_INTEGER(0, "target-ns", &ns_id, "target pid for namespace context"),
393 394 395 396 397 398 399 400 401 402
	OPT_END()
	};
	const char * const buildid_cache_usage[] = {
		"perf buildid-cache [<options>]",
		NULL
	};

	argc = parse_options(argc, argv, buildid_cache_options,
			     buildid_cache_usage, 0);

403 404
	opts_flag = add_name_list_str || kcore_filename ||
		remove_name_list_str || purge_name_list_str ||
405 406
		missing_filename || update_name_list_str ||
		purge_all;
407 408

	if (argc || !(list_files || opts_flag))
409 410
		usage_with_options(buildid_cache_usage, buildid_cache_options);

411 412 413 414 415 416
	/* -l is exclusive. It can not be used with other options. */
	if (list_files && opts_flag) {
		usage_with_options_msg(buildid_cache_usage,
			buildid_cache_options, "-l is exclusive.\n");
	}

417 418 419
	if (ns_id > 0)
		nsi = nsinfo__new(ns_id);

420
	if (missing_filename) {
J
Jiri Olsa 已提交
421 422
		data.path  = missing_filename;
		data.force = force;
423

424
		session = perf_session__new(&data, false, NULL);
425 426
		if (IS_ERR(session))
			return PTR_ERR(session);
427 428
	}

429
	if (symbol__init(session ? &session->header.env : NULL) < 0)
430
		goto out;
431 432

	setup_pager();
433

434 435 436 437 438
	if (list_files) {
		ret = build_id_cache__show_all();
		goto out;
	}

439
	if (add_name_list_str) {
440
		list = strlist__new(add_name_list_str, NULL);
441
		if (list) {
442
			strlist__for_each_entry(pos, list)
443
				if (build_id_cache__add_file(pos->s, nsi)) {
444 445 446 447 448 449
					if (errno == EEXIST) {
						pr_debug("%s already in the cache\n",
							 pos->s);
						continue;
					}
					pr_warning("Couldn't add %s: %s\n",
450
						   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
451 452 453 454 455 456 457
				}

			strlist__delete(list);
		}
	}

	if (remove_name_list_str) {
458
		list = strlist__new(remove_name_list_str, NULL);
459
		if (list) {
460
			strlist__for_each_entry(pos, list)
461
				if (build_id_cache__remove_file(pos->s, nsi)) {
462 463 464 465 466 467
					if (errno == ENOENT) {
						pr_debug("%s wasn't in the cache\n",
							 pos->s);
						continue;
					}
					pr_warning("Couldn't remove %s: %s\n",
468
						   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
469 470 471 472 473 474
				}

			strlist__delete(list);
		}
	}

475
	if (purge_name_list_str) {
476
		list = strlist__new(purge_name_list_str, NULL);
477
		if (list) {
478
			strlist__for_each_entry(pos, list)
479
				if (build_id_cache__purge_path(pos->s, nsi)) {
480 481 482 483 484 485
					if (errno == ENOENT) {
						pr_debug("%s wasn't in the cache\n",
							 pos->s);
						continue;
					}
					pr_warning("Couldn't remove %s: %s\n",
486
						   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
487 488 489 490 491 492
				}

			strlist__delete(list);
		}
	}

493 494 495 496 497 498
	if (purge_all) {
		if (build_id_cache__purge_all()) {
			pr_warning("Couldn't remove some caches. Error: %s.\n",
				str_error_r(errno, sbuf, sizeof(sbuf)));
		}
	}
499

500
	if (missing_filename)
501
		ret = build_id_cache__fprintf_missing(session, stdout);
502

503
	if (update_name_list_str) {
504
		list = strlist__new(update_name_list_str, NULL);
505
		if (list) {
506
			strlist__for_each_entry(pos, list)
507
				if (build_id_cache__update_file(pos->s, nsi)) {
508 509 510 511 512 513
					if (errno == ENOENT) {
						pr_debug("%s wasn't in the cache\n",
							 pos->s);
						continue;
					}
					pr_warning("Couldn't update %s: %s\n",
514
						   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
515 516 517 518 519 520
				}

			strlist__delete(list);
		}
	}

521
	if (kcore_filename && build_id_cache__add_kcore(kcore_filename, force))
522 523
		pr_warning("Couldn't add %s\n", kcore_filename);

524
out:
525
	perf_session__delete(session);
526
	nsinfo__zput(nsi);
527

528
	return ret;
529
}