brintos

brintos / linux-shallow public Read only

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