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