linux/tools/perf/builtin-buildid-cache.c
<<
>>
Prefs
   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 "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            filename__read_build_id(filename, &bid) == -1) {
 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->bid.data, bid.data, bid.size)) {
 288                pr_warning("Problems with %s file, consider removing it from the cache\n",
 289                           filename);
 290        }
 291
 292        return true;
 293}
 294
 295static 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
 301static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi)
 302{
 303        char sbuild_id[SBUILD_ID_SIZE];
 304        struct build_id bid;
 305        struct nscookie nsc;
 306
 307        int err;
 308
 309        nsinfo__mountns_enter(nsi, &nsc);
 310        err = filename__read_build_id(filename, &bid);
 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(&bid, 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
 332static 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
 352static int perf_buildid_cache_config(const char *var, const char *value, void *cb)
 353{
 354        const char **debuginfod = cb;
 355
 356        if (!strcmp(var, "buildid-cache.debuginfod"))
 357                *debuginfod = strdup(value);
 358
 359        return 0;
 360}
 361
 362int cmd_buildid_cache(int argc, const char **argv)
 363{
 364        struct strlist *list;
 365        struct str_node *pos;
 366        int ret, ns_id = -1;
 367        bool force = false;
 368        bool list_files = false;
 369        bool opts_flag = false;
 370        bool purge_all = false;
 371        char const *add_name_list_str = NULL,
 372                   *remove_name_list_str = NULL,
 373                   *purge_name_list_str = NULL,
 374                   *missing_filename = NULL,
 375                   *update_name_list_str = NULL,
 376                   *kcore_filename = NULL,
 377                   *debuginfod = NULL;
 378        char sbuf[STRERR_BUFSIZE];
 379
 380        struct perf_data data = {
 381                .mode  = PERF_DATA_MODE_READ,
 382        };
 383        struct perf_session *session = NULL;
 384        struct nsinfo *nsi = NULL;
 385
 386        const struct option buildid_cache_options[] = {
 387        OPT_STRING('a', "add", &add_name_list_str,
 388                   "file list", "file(s) to add"),
 389        OPT_STRING('k', "kcore", &kcore_filename,
 390                   "file", "kcore file to add"),
 391        OPT_STRING('r', "remove", &remove_name_list_str, "file list",
 392                    "file(s) to remove"),
 393        OPT_STRING('p', "purge", &purge_name_list_str, "file list",
 394                    "file(s) to remove (remove old caches too)"),
 395        OPT_BOOLEAN('P', "purge-all", &purge_all, "purge all cached files"),
 396        OPT_BOOLEAN('l', "list", &list_files, "list all cached files"),
 397        OPT_STRING('M', "missing", &missing_filename, "file",
 398                   "to find missing build ids in the cache"),
 399        OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
 400        OPT_STRING('u', "update", &update_name_list_str, "file list",
 401                    "file(s) to update"),
 402        OPT_STRING(0, "debuginfod", &debuginfod, "debuginfod url",
 403                    "set debuginfod url"),
 404        OPT_INCR('v', "verbose", &verbose, "be more verbose"),
 405        OPT_INTEGER(0, "target-ns", &ns_id, "target pid for namespace context"),
 406        OPT_END()
 407        };
 408        const char * const buildid_cache_usage[] = {
 409                "perf buildid-cache [<options>]",
 410                NULL
 411        };
 412
 413        ret = perf_config(perf_buildid_cache_config, &debuginfod);
 414        if (ret)
 415                return ret;
 416
 417        argc = parse_options(argc, argv, buildid_cache_options,
 418                             buildid_cache_usage, 0);
 419
 420        opts_flag = add_name_list_str || kcore_filename ||
 421                remove_name_list_str || purge_name_list_str ||
 422                missing_filename || update_name_list_str ||
 423                purge_all;
 424
 425        if (argc || !(list_files || opts_flag))
 426                usage_with_options(buildid_cache_usage, buildid_cache_options);
 427
 428        if (debuginfod) {
 429                pr_debug("DEBUGINFOD_URLS=%s\n", debuginfod);
 430                setenv("DEBUGINFOD_URLS", debuginfod, 1);
 431        }
 432
 433        /* -l is exclusive. It can not be used with other options. */
 434        if (list_files && opts_flag) {
 435                usage_with_options_msg(buildid_cache_usage,
 436                        buildid_cache_options, "-l is exclusive.\n");
 437        }
 438
 439        if (ns_id > 0)
 440                nsi = nsinfo__new(ns_id);
 441
 442        if (missing_filename) {
 443                data.path  = missing_filename;
 444                data.force = force;
 445
 446                session = perf_session__new(&data, NULL);
 447                if (IS_ERR(session))
 448                        return PTR_ERR(session);
 449        }
 450
 451        if (symbol__init(session ? &session->header.env : NULL) < 0)
 452                goto out;
 453
 454        setup_pager();
 455
 456        if (list_files) {
 457                ret = build_id_cache__show_all();
 458                goto out;
 459        }
 460
 461        if (add_name_list_str) {
 462                list = strlist__new(add_name_list_str, NULL);
 463                if (list) {
 464                        strlist__for_each_entry(pos, list)
 465                                if (build_id_cache__add_file(pos->s, nsi)) {
 466                                        if (errno == EEXIST) {
 467                                                pr_debug("%s already in the cache\n",
 468                                                         pos->s);
 469                                                continue;
 470                                        }
 471                                        pr_warning("Couldn't add %s: %s\n",
 472                                                   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
 473                                }
 474
 475                        strlist__delete(list);
 476                }
 477        }
 478
 479        if (remove_name_list_str) {
 480                list = strlist__new(remove_name_list_str, NULL);
 481                if (list) {
 482                        strlist__for_each_entry(pos, list)
 483                                if (build_id_cache__remove_file(pos->s, nsi)) {
 484                                        if (errno == ENOENT) {
 485                                                pr_debug("%s wasn't in the cache\n",
 486                                                         pos->s);
 487                                                continue;
 488                                        }
 489                                        pr_warning("Couldn't remove %s: %s\n",
 490                                                   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
 491                                }
 492
 493                        strlist__delete(list);
 494                }
 495        }
 496
 497        if (purge_name_list_str) {
 498                list = strlist__new(purge_name_list_str, NULL);
 499                if (list) {
 500                        strlist__for_each_entry(pos, list)
 501                                if (build_id_cache__purge_path(pos->s, nsi)) {
 502                                        if (errno == ENOENT) {
 503                                                pr_debug("%s wasn't in the cache\n",
 504                                                         pos->s);
 505                                                continue;
 506                                        }
 507                                        pr_warning("Couldn't remove %s: %s\n",
 508                                                   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
 509                                }
 510
 511                        strlist__delete(list);
 512                }
 513        }
 514
 515        if (purge_all) {
 516                if (build_id_cache__purge_all()) {
 517                        pr_warning("Couldn't remove some caches. Error: %s.\n",
 518                                str_error_r(errno, sbuf, sizeof(sbuf)));
 519                }
 520        }
 521
 522        if (missing_filename)
 523                ret = build_id_cache__fprintf_missing(session, stdout);
 524
 525        if (update_name_list_str) {
 526                list = strlist__new(update_name_list_str, NULL);
 527                if (list) {
 528                        strlist__for_each_entry(pos, list)
 529                                if (build_id_cache__update_file(pos->s, nsi)) {
 530                                        if (errno == ENOENT) {
 531                                                pr_debug("%s wasn't in the cache\n",
 532                                                         pos->s);
 533                                                continue;
 534                                        }
 535                                        pr_warning("Couldn't update %s: %s\n",
 536                                                   pos->s, str_error_r(errno, sbuf, sizeof(sbuf)));
 537                                }
 538
 539                        strlist__delete(list);
 540                }
 541        }
 542
 543        if (kcore_filename && build_id_cache__add_kcore(kcore_filename, force))
 544                pr_warning("Couldn't add %s\n", kcore_filename);
 545
 546out:
 547        perf_session__delete(session);
 548        nsinfo__zput(nsi);
 549
 550        return ret;
 551}
 552