root/tools/perf/builtin-buildid-cache.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. build_id_cache__kcore_buildid
  2. build_id_cache__kcore_dir
  3. same_kallsyms_reloc
  4. build_id_cache__kcore_existing
  5. build_id_cache__add_kcore
  6. build_id_cache__add_file
  7. build_id_cache__remove_file
  8. build_id_cache__purge_path
  9. build_id_cache__purge_all
  10. dso__missing_buildid_cache
  11. build_id_cache__fprintf_missing
  12. build_id_cache__update_file
  13. build_id_cache__show_all
  14. cmd_buildid_cache

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

/* [<][>][^][v][top][bottom][index][help] */