1 /* 2 * build-id.c 3 * 4 * build-id support 5 * 6 * Copyright (C) 2009, 2010 Red Hat Inc. 7 * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com> 8 */ 9 #include "util.h" 10 #include <stdio.h> 11 #include "build-id.h" 12 #include "event.h" 13 #include "symbol.h" 14 #include <linux/kernel.h> 15 #include "debug.h" 16 #include "session.h" 17 #include "tool.h" 18 #include "header.h" 19 #include "vdso.h" 20 21 22 static bool no_buildid_cache; 23 24 int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, 25 union perf_event *event, 26 struct perf_sample *sample, 27 struct perf_evsel *evsel __maybe_unused, 28 struct machine *machine) 29 { 30 struct addr_location al; 31 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 32 struct thread *thread = machine__findnew_thread(machine, sample->pid, 33 sample->tid); 34 35 if (thread == NULL) { 36 pr_err("problem processing %d event, skipping it.\n", 37 event->header.type); 38 return -1; 39 } 40 41 thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al); 42 43 if (al.map != NULL) 44 al.map->dso->hit = 1; 45 46 return 0; 47 } 48 49 static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, 50 union perf_event *event, 51 struct perf_sample *sample 52 __maybe_unused, 53 struct machine *machine) 54 { 55 struct thread *thread = machine__findnew_thread(machine, 56 event->fork.pid, 57 event->fork.tid); 58 59 dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, 60 event->fork.ppid, event->fork.ptid); 61 62 if (thread) 63 machine__remove_thread(machine, thread); 64 65 return 0; 66 } 67 68 struct perf_tool build_id__mark_dso_hit_ops = { 69 .sample = build_id__mark_dso_hit, 70 .mmap = perf_event__process_mmap, 71 .mmap2 = perf_event__process_mmap2, 72 .fork = perf_event__process_fork, 73 .exit = perf_event__exit_del_thread, 74 .attr = perf_event__process_attr, 75 .build_id = perf_event__process_build_id, 76 }; 77 78 int build_id__sprintf(const u8 *build_id, int len, char *bf) 79 { 80 char *bid = bf; 81 const u8 *raw = build_id; 82 int i; 83 84 for (i = 0; i < len; ++i) { 85 sprintf(bid, "%02x", *raw); 86 ++raw; 87 bid += 2; 88 } 89 90 return raw - build_id; 91 } 92 93 /* asnprintf consolidates asprintf and snprintf */ 94 static int asnprintf(char **strp, size_t size, const char *fmt, ...) 95 { 96 va_list ap; 97 int ret; 98 99 if (!strp) 100 return -EINVAL; 101 102 va_start(ap, fmt); 103 if (*strp) 104 ret = vsnprintf(*strp, size, fmt, ap); 105 else 106 ret = vasprintf(strp, fmt, ap); 107 va_end(ap); 108 109 return ret; 110 } 111 112 static char *build_id__filename(const char *sbuild_id, char *bf, size_t size) 113 { 114 char *tmp = bf; 115 int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir, 116 sbuild_id, sbuild_id + 2); 117 if (ret < 0 || (tmp && size < (unsigned int)ret)) 118 return NULL; 119 return bf; 120 } 121 122 char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) 123 { 124 char build_id_hex[BUILD_ID_SIZE * 2 + 1]; 125 126 if (!dso->has_build_id) 127 return NULL; 128 129 build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); 130 return build_id__filename(build_id_hex, bf, size); 131 } 132 133 #define dsos__for_each_with_build_id(pos, head) \ 134 list_for_each_entry(pos, head, node) \ 135 if (!pos->has_build_id) \ 136 continue; \ 137 else 138 139 static int write_buildid(const char *name, size_t name_len, u8 *build_id, 140 pid_t pid, u16 misc, int fd) 141 { 142 int err; 143 struct build_id_event b; 144 size_t len; 145 146 len = name_len + 1; 147 len = PERF_ALIGN(len, NAME_ALIGN); 148 149 memset(&b, 0, sizeof(b)); 150 memcpy(&b.build_id, build_id, BUILD_ID_SIZE); 151 b.pid = pid; 152 b.header.misc = misc; 153 b.header.size = sizeof(b) + len; 154 155 err = writen(fd, &b, sizeof(b)); 156 if (err < 0) 157 return err; 158 159 return write_padded(fd, name, name_len + 1, len); 160 } 161 162 static int __dsos__write_buildid_table(struct list_head *head, 163 struct machine *machine, 164 pid_t pid, u16 misc, int fd) 165 { 166 char nm[PATH_MAX]; 167 struct dso *pos; 168 169 dsos__for_each_with_build_id(pos, head) { 170 int err; 171 const char *name; 172 size_t name_len; 173 174 if (!pos->hit) 175 continue; 176 177 if (dso__is_vdso(pos)) { 178 name = pos->short_name; 179 name_len = pos->short_name_len + 1; 180 } else if (dso__is_kcore(pos)) { 181 machine__mmap_name(machine, nm, sizeof(nm)); 182 name = nm; 183 name_len = strlen(nm) + 1; 184 } else { 185 name = pos->long_name; 186 name_len = pos->long_name_len + 1; 187 } 188 189 err = write_buildid(name, name_len, pos->build_id, 190 pid, misc, fd); 191 if (err) 192 return err; 193 } 194 195 return 0; 196 } 197 198 static int machine__write_buildid_table(struct machine *machine, int fd) 199 { 200 int err; 201 u16 kmisc = PERF_RECORD_MISC_KERNEL, 202 umisc = PERF_RECORD_MISC_USER; 203 204 if (!machine__is_host(machine)) { 205 kmisc = PERF_RECORD_MISC_GUEST_KERNEL; 206 umisc = PERF_RECORD_MISC_GUEST_USER; 207 } 208 209 err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, 210 machine->pid, kmisc, fd); 211 if (err == 0) 212 err = __dsos__write_buildid_table(&machine->user_dsos.head, 213 machine, machine->pid, umisc, 214 fd); 215 return err; 216 } 217 218 int perf_session__write_buildid_table(struct perf_session *session, int fd) 219 { 220 struct rb_node *nd; 221 int err = machine__write_buildid_table(&session->machines.host, fd); 222 223 if (err) 224 return err; 225 226 for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { 227 struct machine *pos = rb_entry(nd, struct machine, rb_node); 228 err = machine__write_buildid_table(pos, fd); 229 if (err) 230 break; 231 } 232 return err; 233 } 234 235 static int __dsos__hit_all(struct list_head *head) 236 { 237 struct dso *pos; 238 239 list_for_each_entry(pos, head, node) 240 pos->hit = true; 241 242 return 0; 243 } 244 245 static int machine__hit_all_dsos(struct machine *machine) 246 { 247 int err; 248 249 err = __dsos__hit_all(&machine->kernel_dsos.head); 250 if (err) 251 return err; 252 253 return __dsos__hit_all(&machine->user_dsos.head); 254 } 255 256 int dsos__hit_all(struct perf_session *session) 257 { 258 struct rb_node *nd; 259 int err; 260 261 err = machine__hit_all_dsos(&session->machines.host); 262 if (err) 263 return err; 264 265 for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { 266 struct machine *pos = rb_entry(nd, struct machine, rb_node); 267 268 err = machine__hit_all_dsos(pos); 269 if (err) 270 return err; 271 } 272 273 return 0; 274 } 275 276 void disable_buildid_cache(void) 277 { 278 no_buildid_cache = true; 279 } 280 281 static char *build_id_cache__dirname_from_path(const char *name, 282 bool is_kallsyms, bool is_vdso) 283 { 284 char *realname = (char *)name, *filename; 285 bool slash = is_kallsyms || is_vdso; 286 287 if (!slash) { 288 realname = realpath(name, NULL); 289 if (!realname) 290 return NULL; 291 } 292 293 if (asprintf(&filename, "%s%s%s", buildid_dir, slash ? "/" : "", 294 is_vdso ? DSO__NAME_VDSO : realname) < 0) 295 filename = NULL; 296 297 if (!slash) 298 free(realname); 299 300 return filename; 301 } 302 303 int build_id_cache__list_build_ids(const char *pathname, 304 struct strlist **result) 305 { 306 struct strlist *list; 307 char *dir_name; 308 DIR *dir; 309 struct dirent *d; 310 int ret = 0; 311 312 list = strlist__new(true, NULL); 313 dir_name = build_id_cache__dirname_from_path(pathname, false, false); 314 if (!list || !dir_name) { 315 ret = -ENOMEM; 316 goto out; 317 } 318 319 /* List up all dirents */ 320 dir = opendir(dir_name); 321 if (!dir) { 322 ret = -errno; 323 goto out; 324 } 325 326 while ((d = readdir(dir)) != NULL) { 327 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) 328 continue; 329 strlist__add(list, d->d_name); 330 } 331 closedir(dir); 332 333 out: 334 free(dir_name); 335 if (ret) 336 strlist__delete(list); 337 else 338 *result = list; 339 340 return ret; 341 } 342 343 int build_id_cache__add_s(const char *sbuild_id, const char *name, 344 bool is_kallsyms, bool is_vdso) 345 { 346 const size_t size = PATH_MAX; 347 char *realname = NULL, *filename = NULL, *dir_name = NULL, 348 *linkname = zalloc(size), *targetname, *tmp; 349 int err = -1; 350 351 if (!is_kallsyms) { 352 realname = realpath(name, NULL); 353 if (!realname) 354 goto out_free; 355 } 356 357 dir_name = build_id_cache__dirname_from_path(name, is_kallsyms, is_vdso); 358 if (!dir_name) 359 goto out_free; 360 361 if (mkdir_p(dir_name, 0755)) 362 goto out_free; 363 364 if (asprintf(&filename, "%s/%s", dir_name, sbuild_id) < 0) { 365 filename = NULL; 366 goto out_free; 367 } 368 369 if (access(filename, F_OK)) { 370 if (is_kallsyms) { 371 if (copyfile("/proc/kallsyms", filename)) 372 goto out_free; 373 } else if (link(realname, filename) && errno != EEXIST && 374 copyfile(name, filename)) 375 goto out_free; 376 } 377 378 if (!build_id__filename(sbuild_id, linkname, size)) 379 goto out_free; 380 tmp = strrchr(linkname, '/'); 381 *tmp = '\0'; 382 383 if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) 384 goto out_free; 385 386 *tmp = '/'; 387 targetname = filename + strlen(buildid_dir) - 5; 388 memcpy(targetname, "../..", 5); 389 390 if (symlink(targetname, linkname) == 0) 391 err = 0; 392 out_free: 393 if (!is_kallsyms) 394 free(realname); 395 free(filename); 396 free(dir_name); 397 free(linkname); 398 return err; 399 } 400 401 static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, 402 const char *name, bool is_kallsyms, 403 bool is_vdso) 404 { 405 char sbuild_id[BUILD_ID_SIZE * 2 + 1]; 406 407 build_id__sprintf(build_id, build_id_size, sbuild_id); 408 409 return build_id_cache__add_s(sbuild_id, name, is_kallsyms, is_vdso); 410 } 411 412 bool build_id_cache__cached(const char *sbuild_id) 413 { 414 bool ret = false; 415 char *filename = build_id__filename(sbuild_id, NULL, 0); 416 417 if (filename && !access(filename, F_OK)) 418 ret = true; 419 free(filename); 420 421 return ret; 422 } 423 424 int build_id_cache__remove_s(const char *sbuild_id) 425 { 426 const size_t size = PATH_MAX; 427 char *filename = zalloc(size), 428 *linkname = zalloc(size), *tmp; 429 int err = -1; 430 431 if (filename == NULL || linkname == NULL) 432 goto out_free; 433 434 if (!build_id__filename(sbuild_id, linkname, size)) 435 goto out_free; 436 437 if (access(linkname, F_OK)) 438 goto out_free; 439 440 if (readlink(linkname, filename, size - 1) < 0) 441 goto out_free; 442 443 if (unlink(linkname)) 444 goto out_free; 445 446 /* 447 * Since the link is relative, we must make it absolute: 448 */ 449 tmp = strrchr(linkname, '/') + 1; 450 snprintf(tmp, size - (tmp - linkname), "%s", filename); 451 452 if (unlink(linkname)) 453 goto out_free; 454 455 err = 0; 456 out_free: 457 free(filename); 458 free(linkname); 459 return err; 460 } 461 462 static int dso__cache_build_id(struct dso *dso, struct machine *machine) 463 { 464 bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; 465 bool is_vdso = dso__is_vdso(dso); 466 const char *name = dso->long_name; 467 char nm[PATH_MAX]; 468 469 if (dso__is_kcore(dso)) { 470 is_kallsyms = true; 471 machine__mmap_name(machine, nm, sizeof(nm)); 472 name = nm; 473 } 474 return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, 475 is_kallsyms, is_vdso); 476 } 477 478 static int __dsos__cache_build_ids(struct list_head *head, 479 struct machine *machine) 480 { 481 struct dso *pos; 482 int err = 0; 483 484 dsos__for_each_with_build_id(pos, head) 485 if (dso__cache_build_id(pos, machine)) 486 err = -1; 487 488 return err; 489 } 490 491 static int machine__cache_build_ids(struct machine *machine) 492 { 493 int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine); 494 ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine); 495 return ret; 496 } 497 498 int perf_session__cache_build_ids(struct perf_session *session) 499 { 500 struct rb_node *nd; 501 int ret; 502 503 if (no_buildid_cache) 504 return 0; 505 506 if (mkdir(buildid_dir, 0755) != 0 && errno != EEXIST) 507 return -1; 508 509 ret = machine__cache_build_ids(&session->machines.host); 510 511 for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { 512 struct machine *pos = rb_entry(nd, struct machine, rb_node); 513 ret |= machine__cache_build_ids(pos); 514 } 515 return ret ? -1 : 0; 516 } 517 518 static bool machine__read_build_ids(struct machine *machine, bool with_hits) 519 { 520 bool ret; 521 522 ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); 523 ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); 524 return ret; 525 } 526 527 bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) 528 { 529 struct rb_node *nd; 530 bool ret = machine__read_build_ids(&session->machines.host, with_hits); 531 532 for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { 533 struct machine *pos = rb_entry(nd, struct machine, rb_node); 534 ret |= machine__read_build_ids(pos, with_hits); 535 } 536 537 return ret; 538 } 539