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