builtin-buildid-cache.c 12.5 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 17
#include "builtin.h"
#include "perf.h"
18
#include "namespaces.h"
19 20 21
#include "util/cache.h"
#include "util/debug.h"
#include "util/header.h"
22
#include <subcmd/parse-options.h>
23
#include "util/strlist.h"
24
#include "util/build-id.h"
25
#include "util/session.h"
26
#include "util/symbol.h"
27
#include "util/time-utils.h"
28
#include "util/probe-file.h"
29

30 31 32 33 34 35 36 37 38 39 40
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';
41
	return sysfs__sprintf_build_id(root_dir, sbuildid);
42 43 44 45
}

static int build_id_cache__kcore_dir(char *dir, size_t sz)
{
46
	return fetch_current_timestamp(dir, sz);
47 48
}

49 50 51 52 53 54
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;
55
	int i, err = -1;
56 57 58 59 60

	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++) {
61 62
		err = kallsyms__get_function_start(from, name, &addr1);
		if (!err)
63 64 65
			break;
	}

66 67 68 69 70
	if (err)
		return false;

	if (kallsyms__get_function_start(to, name, &addr2))
		return false;
71 72 73 74

	return addr1 == addr2;
}

75 76 77 78 79
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];
80
	char to_subdir[PATH_MAX];
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	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);
99 100 101 102 103
		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);
104 105 106 107 108 109 110 111 112 113
			ret = 0;
			break;
		}
	}

	closedir(d);

	return ret;
}

114
static int build_id_cache__add_kcore(const char *filename, bool force)
115
{
116
	char dir[32], sbuildid[SBUILD_ID_SIZE];
117 118 119 120 121 122 123 124 125 126
	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';

127
	if (build_id_cache__kcore_buildid(from_dir, sbuildid) < 0)
128 129
		return -1;

130 131
	scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s",
		  buildid_dir, DSO__NAME_KCORE, sbuildid);
132

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

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

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

	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;
}

171
static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi)
172
{
173
	char sbuild_id[SBUILD_ID_SIZE];
174 175
	u8 build_id[BUILD_ID_SIZE];
	int err;
176
	struct nscookie nsc;
177

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

	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
187
	err = build_id_cache__add_s(sbuild_id, filename, nsi,
188
				    false, false);
189 190
	pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
		 err ? "FAIL" : "Ok");
191 192 193
	return err;
}

194
static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi)
195 196
{
	u8 build_id[BUILD_ID_SIZE];
197
	char sbuild_id[SBUILD_ID_SIZE];
198
	struct nscookie nsc;
199 200 201

	int err;

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

	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
211
	err = build_id_cache__remove_s(sbuild_id);
212 213
	pr_debug("Removing %s %s: %s\n", sbuild_id, filename,
		 err ? "FAIL" : "Ok");
214 215 216 217

	return err;
}

218
static int build_id_cache__purge_path(const char *pathname, struct nsinfo *nsi)
219 220 221 222 223
{
	struct strlist *list;
	struct str_node *pos;
	int err;

224
	err = build_id_cache__list_build_ids(pathname, nsi, &list);
225 226 227
	if (err)
		goto out;

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

out:
238
	pr_debug("Purging %s: %s\n", pathname, err ? "FAIL" : "Ok");
239 240 241 242

	return err;
}

243 244 245 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
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;
}

271 272 273 274 275
static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
{
	char filename[PATH_MAX];
	u8 build_id[BUILD_ID_SIZE];

276
	if (dso__build_id_filename(dso, filename, sizeof(filename), false) &&
277 278 279 280 281
	    filename__read_build_id(filename, build_id,
				    sizeof(build_id)) != sizeof(build_id)) {
		if (errno == ENOENT)
			return false;

282
		pr_warning("Problems with %s file, consider removing it from the cache\n",
283 284
			   filename);
	} else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) {
285
		pr_warning("Problems with %s file, consider removing it from the cache\n",
286 287 288 289 290 291
			   filename);
	}

	return true;
}

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

298
static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi)
299 300
{
	u8 build_id[BUILD_ID_SIZE];
301
	char sbuild_id[SBUILD_ID_SIZE];
302
	struct nscookie nsc;
303

304
	int err;
305

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

	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
316 317 318
	if (build_id_cache__cached(sbuild_id))
		err = build_id_cache__remove_s(sbuild_id);

319
	if (!err)
320 321
		err = build_id_cache__add_s(sbuild_id, filename, nsi, false,
					    false);
322

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

	return err;
}

329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
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;
}

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

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

373 374 375
	const struct option buildid_cache_options[] = {
	OPT_STRING('a', "add", &add_name_list_str,
		   "file list", "file(s) to add"),
376 377
	OPT_STRING('k', "kcore", &kcore_filename,
		   "file", "kcore file to add"),
378 379
	OPT_STRING('r', "remove", &remove_name_list_str, "file list",
		    "file(s) to remove"),
380 381
	OPT_STRING('p', "purge", &purge_name_list_str, "file list",
		    "file(s) to remove (remove old caches too)"),
382
	OPT_BOOLEAN('P', "purge-all", &purge_all, "purge all cached files"),
383
	OPT_BOOLEAN('l', "list", &list_files, "list all cached files"),
384 385 386
	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"),
387 388
	OPT_STRING('u', "update", &update_name_list_str, "file list",
		    "file(s) to update"),
389
	OPT_INCR('v', "verbose", &verbose, "be more verbose"),
390
	OPT_INTEGER(0, "target-ns", &ns_id, "target pid for namespace context"),
391 392 393 394 395 396 397 398 399 400
	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);

401 402
	opts_flag = add_name_list_str || kcore_filename ||
		remove_name_list_str || purge_name_list_str ||
403 404
		missing_filename || update_name_list_str ||
		purge_all;
405 406

	if (argc || !(list_files || opts_flag))
407 408
		usage_with_options(buildid_cache_usage, buildid_cache_options);

409 410 411 412 413 414
	/* -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");
	}

415 416 417
	if (ns_id > 0)
		nsi = nsinfo__new(ns_id);

418
	if (missing_filename) {
419 420
		data.file.path = missing_filename;
		data.force     = force;
421

422
		session = perf_session__new(&data, false, NULL);
423 424 425 426
		if (session == NULL)
			return -1;
	}

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

	setup_pager();
431

432 433 434 435 436
	if (list_files) {
		ret = build_id_cache__show_all();
		goto out;
	}

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

			strlist__delete(list);
		}
	}

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

			strlist__delete(list);
		}
	}

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

			strlist__delete(list);
		}
	}

491 492 493 494 495 496
	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)));
		}
	}
497

498
	if (missing_filename)
499
		ret = build_id_cache__fprintf_missing(session, stdout);
500

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

			strlist__delete(list);
		}
	}

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

522
out:
523
	perf_session__delete(session);
524
	nsinfo__zput(nsi);
525

526
	return ret;
527
}