builtin-buildid-cache.c 9.5 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * 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>
 */
9 10 11 12 13
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <dirent.h>
#include <unistd.h>
14 15 16 17 18 19 20
#include "builtin.h"
#include "perf.h"
#include "util/cache.h"
#include "util/debug.h"
#include "util/header.h"
#include "util/parse-options.h"
#include "util/strlist.h"
21
#include "util/build-id.h"
22
#include "util/session.h"
23
#include "util/symbol.h"
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid)
{
	char root_dir[PATH_MAX];
	char notes[PATH_MAX];
	u8 build_id[BUILD_ID_SIZE];
	char *p;

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

	p = strrchr(root_dir, '/');
	if (!p)
		return -1;
	*p = '\0';

	scnprintf(notes, sizeof(notes), "%s/sys/kernel/notes", root_dir);

	if (sysfs__read_build_id(notes, build_id, sizeof(build_id)))
		return -1;

	build_id__sprintf(build_id, sizeof(build_id), sbuildid);

	return 0;
}

static int build_id_cache__kcore_dir(char *dir, size_t sz)
{
	struct timeval tv;
	struct tm tm;
	char dt[32];

	if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm))
		return -1;

	if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm))
		return -1;

	scnprintf(dir, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000);

	return 0;
}

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
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;
	int i;

	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++) {
		addr1 = kallsyms__get_function_start(from, name);
		if (addr1)
			break;
	}

	if (name)
		addr2 = kallsyms__get_function_start(to, name);

	return addr1 == addr2;
}

89 90 91 92 93
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];
94
	char to_subdir[PATH_MAX];
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	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);
113 114 115 116 117
		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);
118 119 120 121 122 123 124 125 126 127
			ret = 0;
			break;
		}
	}

	closedir(d);

	return ret;
}

128 129
static int build_id_cache__add_kcore(const char *filename, const char *debugdir,
				     bool force)
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
{
	char dir[32], sbuildid[BUILD_ID_SIZE * 2 + 1];
	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';

	if (build_id_cache__kcore_buildid(from_dir, sbuildid))
		return -1;

	scnprintf(to_dir, sizeof(to_dir), "%s/[kernel.kcore]/%s",
		  debugdir, sbuildid);

148 149
	if (!force &&
	    !build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) {
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
		pr_debug("same kcore found in %s\n", to_dir);
		return 0;
	}

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

	scnprintf(to_dir, sizeof(to_dir), "%s/[kernel.kcore]/%s/%s",
		  debugdir, sbuildid, dir);

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

186 187 188 189 190 191 192 193 194 195 196 197
static int build_id_cache__add_file(const char *filename, const char *debugdir)
{
	char sbuild_id[BUILD_ID_SIZE * 2 + 1];
	u8 build_id[BUILD_ID_SIZE];
	int err;

	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
		pr_debug("Couldn't read a build-id in %s\n", filename);
		return -1;
	}

	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
198 199
	err = build_id_cache__add_s(sbuild_id, debugdir, filename,
				    false, false);
200 201 202 203 204 205
	if (verbose)
		pr_info("Adding %s %s: %s\n", sbuild_id, filename,
			err ? "FAIL" : "Ok");
	return err;
}

206 207
static int build_id_cache__remove_file(const char *filename,
				       const char *debugdir)
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
{
	u8 build_id[BUILD_ID_SIZE];
	char sbuild_id[BUILD_ID_SIZE * 2 + 1];

	int err;

	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
		pr_debug("Couldn't read a build-id in %s\n", filename);
		return -1;
	}

	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
	err = build_id_cache__remove_s(sbuild_id, debugdir);
	if (verbose)
		pr_info("Removing %s %s: %s\n", sbuild_id, filename,
			err ? "FAIL" : "Ok");

	return err;
}

228 229 230 231 232 233 234 235 236 237 238
static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
{
	char filename[PATH_MAX];
	u8 build_id[BUILD_ID_SIZE];

	if (dso__build_id_filename(dso, filename, sizeof(filename)) &&
	    filename__read_build_id(filename, build_id,
				    sizeof(build_id)) != sizeof(build_id)) {
		if (errno == ENOENT)
			return false;

239
		pr_warning("Problems with %s file, consider removing it from the cache\n",
240 241
			   filename);
	} else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) {
242
		pr_warning("Problems with %s file, consider removing it from the cache\n",
243 244 245 246 247 248
			   filename);
	}

	return true;
}

249
static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp)
250 251 252 253 254
{
	perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0);
	return 0;
}

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
static int build_id_cache__update_file(const char *filename,
				       const char *debugdir)
{
	u8 build_id[BUILD_ID_SIZE];
	char sbuild_id[BUILD_ID_SIZE * 2 + 1];

	int err;

	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
		pr_debug("Couldn't read a build-id in %s\n", filename);
		return -1;
	}

	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
	err = build_id_cache__remove_s(sbuild_id, debugdir);
	if (!err) {
		err = build_id_cache__add_s(sbuild_id, debugdir, filename,
					    false, false);
	}
	if (verbose)
		pr_info("Updating %s %s: %s\n", sbuild_id, filename,
			err ? "FAIL" : "Ok");

	return err;
}

281 282
int cmd_buildid_cache(int argc, const char **argv,
		      const char *prefix __maybe_unused)
283 284 285
{
	struct strlist *list;
	struct str_node *pos;
286 287
	int ret = 0;
	bool force = false;
288
	char const *add_name_list_str = NULL,
289
		   *remove_name_list_str = NULL,
290
		   *missing_filename = NULL,
291
		   *update_name_list_str = NULL,
292
		   *kcore_filename = NULL;
293
	char sbuf[STRERR_BUFSIZE];
294

295 296 297 298 299
	struct perf_data_file file = {
		.mode  = PERF_DATA_MODE_READ,
	};
	struct perf_session *session = NULL;

300 301 302
	const struct option buildid_cache_options[] = {
	OPT_STRING('a', "add", &add_name_list_str,
		   "file list", "file(s) to add"),
303 304
	OPT_STRING('k', "kcore", &kcore_filename,
		   "file", "kcore file to add"),
305 306
	OPT_STRING('r', "remove", &remove_name_list_str, "file list",
		    "file(s) to remove"),
307 308 309
	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"),
310 311
	OPT_STRING('u', "update", &update_name_list_str, "file list",
		    "file(s) to update"),
312 313 314 315 316 317 318 319 320 321 322
	OPT_INCR('v', "verbose", &verbose, "be more verbose"),
	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);

323 324 325 326 327 328 329 330 331
	if (missing_filename) {
		file.path = missing_filename;
		file.force = force;

		session = perf_session__new(&file, false, NULL);
		if (session == NULL)
			return -1;
	}

332
	if (symbol__init(session ? &session->header.env : NULL) < 0)
333
		goto out;
334 335

	setup_pager();
336 337 338 339 340

	if (add_name_list_str) {
		list = strlist__new(true, add_name_list_str);
		if (list) {
			strlist__for_each(pos, list)
341
				if (build_id_cache__add_file(pos->s, buildid_dir)) {
342 343 344 345 346 347
					if (errno == EEXIST) {
						pr_debug("%s already in the cache\n",
							 pos->s);
						continue;
					}
					pr_warning("Couldn't add %s: %s\n",
348
						   pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
349 350 351 352 353 354 355 356 357 358
				}

			strlist__delete(list);
		}
	}

	if (remove_name_list_str) {
		list = strlist__new(true, remove_name_list_str);
		if (list) {
			strlist__for_each(pos, list)
359
				if (build_id_cache__remove_file(pos->s, buildid_dir)) {
360 361 362 363 364 365
					if (errno == ENOENT) {
						pr_debug("%s wasn't in the cache\n",
							 pos->s);
						continue;
					}
					pr_warning("Couldn't remove %s: %s\n",
366
						   pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
367 368 369 370 371 372
				}

			strlist__delete(list);
		}
	}

373
	if (missing_filename)
374
		ret = build_id_cache__fprintf_missing(session, stdout);
375

376 377 378 379
	if (update_name_list_str) {
		list = strlist__new(true, update_name_list_str);
		if (list) {
			strlist__for_each(pos, list)
380
				if (build_id_cache__update_file(pos->s, buildid_dir)) {
381 382 383 384 385 386
					if (errno == ENOENT) {
						pr_debug("%s wasn't in the cache\n",
							 pos->s);
						continue;
					}
					pr_warning("Couldn't update %s: %s\n",
387
						   pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
388 389 390 391 392 393
				}

			strlist__delete(list);
		}
	}

394
	if (kcore_filename &&
395
	    build_id_cache__add_kcore(kcore_filename, buildid_dir, force))
396 397
		pr_warning("Couldn't add %s\n", kcore_filename);

398 399 400 401
out:
	if (session)
		perf_session__delete(session);

402
	return ret;
403
}