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