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 rb_erase(&thread->rb_node, &machine->threads); 64 machine->last_match = NULL; 65 thread__delete(thread); 66 } 67 68 return 0; 69 } 70 71 struct perf_tool build_id__mark_dso_hit_ops = { 72 .sample = build_id__mark_dso_hit, 73 .mmap = perf_event__process_mmap, 74 .mmap2 = perf_event__process_mmap2, 75 .fork = perf_event__process_fork, 76 .exit = perf_event__exit_del_thread, 77 .attr = perf_event__process_attr, 78 .build_id = perf_event__process_build_id, 79 }; 80 81 int build_id__sprintf(const u8 *build_id, int len, char *bf) 82 { 83 char *bid = bf; 84 const u8 *raw = build_id; 85 int i; 86 87 for (i = 0; i < len; ++i) { 88 sprintf(bid, "%02x", *raw); 89 ++raw; 90 bid += 2; 91 } 92 93 return raw - build_id; 94 } 95 96 char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) 97 { 98 char build_id_hex[BUILD_ID_SIZE * 2 + 1]; 99 100 if (!dso->has_build_id) 101 return NULL; 102 103 build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); 104 if (bf == NULL) { 105 if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir, 106 build_id_hex, build_id_hex + 2) < 0) 107 return NULL; 108 } else 109 snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir, 110 build_id_hex, build_id_hex + 2); 111 return bf; 112 } 113 114 #define dsos__for_each_with_build_id(pos, head) \ 115 list_for_each_entry(pos, head, node) \ 116 if (!pos->has_build_id) \ 117 continue; \ 118 else 119 120 static int write_buildid(const char *name, size_t name_len, u8 *build_id, 121 pid_t pid, u16 misc, int fd) 122 { 123 int err; 124 struct build_id_event b; 125 size_t len; 126 127 len = name_len + 1; 128 len = PERF_ALIGN(len, NAME_ALIGN); 129 130 memset(&b, 0, sizeof(b)); 131 memcpy(&b.build_id, build_id, BUILD_ID_SIZE); 132 b.pid = pid; 133 b.header.misc = misc; 134 b.header.size = sizeof(b) + len; 135 136 err = writen(fd, &b, sizeof(b)); 137 if (err < 0) 138 return err; 139 140 return write_padded(fd, name, name_len + 1, len); 141 } 142 143 static int __dsos__write_buildid_table(struct list_head *head, 144 struct machine *machine, 145 pid_t pid, u16 misc, int fd) 146 { 147 char nm[PATH_MAX]; 148 struct dso *pos; 149 150 dsos__for_each_with_build_id(pos, head) { 151 int err; 152 const char *name; 153 size_t name_len; 154 155 if (!pos->hit) 156 continue; 157 158 if (dso__is_vdso(pos)) { 159 name = pos->short_name; 160 name_len = pos->short_name_len + 1; 161 } else if (dso__is_kcore(pos)) { 162 machine__mmap_name(machine, nm, sizeof(nm)); 163 name = nm; 164 name_len = strlen(nm) + 1; 165 } else { 166 name = pos->long_name; 167 name_len = pos->long_name_len + 1; 168 } 169 170 err = write_buildid(name, name_len, pos->build_id, 171 pid, misc, fd); 172 if (err) 173 return err; 174 } 175 176 return 0; 177 } 178 179 static int machine__write_buildid_table(struct machine *machine, int fd) 180 { 181 int err; 182 u16 kmisc = PERF_RECORD_MISC_KERNEL, 183 umisc = PERF_RECORD_MISC_USER; 184 185 if (!machine__is_host(machine)) { 186 kmisc = PERF_RECORD_MISC_GUEST_KERNEL; 187 umisc = PERF_RECORD_MISC_GUEST_USER; 188 } 189 190 err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, 191 machine->pid, kmisc, fd); 192 if (err == 0) 193 err = __dsos__write_buildid_table(&machine->user_dsos.head, 194 machine, machine->pid, umisc, 195 fd); 196 return err; 197 } 198 199 int perf_session__write_buildid_table(struct perf_session *session, int fd) 200 { 201 struct rb_node *nd; 202 int err = machine__write_buildid_table(&session->machines.host, fd); 203 204 if (err) 205 return err; 206 207 for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { 208 struct machine *pos = rb_entry(nd, struct machine, rb_node); 209 err = machine__write_buildid_table(pos, fd); 210 if (err) 211 break; 212 } 213 return err; 214 } 215 216 static int __dsos__hit_all(struct list_head *head) 217 { 218 struct dso *pos; 219 220 list_for_each_entry(pos, head, node) 221 pos->hit = true; 222 223 return 0; 224 } 225 226 static int machine__hit_all_dsos(struct machine *machine) 227 { 228 int err; 229 230 err = __dsos__hit_all(&machine->kernel_dsos.head); 231 if (err) 232 return err; 233 234 return __dsos__hit_all(&machine->user_dsos.head); 235 } 236 237 int dsos__hit_all(struct perf_session *session) 238 { 239 struct rb_node *nd; 240 int err; 241 242 err = machine__hit_all_dsos(&session->machines.host); 243 if (err) 244 return err; 245 246 for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { 247 struct machine *pos = rb_entry(nd, struct machine, rb_node); 248 249 err = machine__hit_all_dsos(pos); 250 if (err) 251 return err; 252 } 253 254 return 0; 255 } 256 257 void disable_buildid_cache(void) 258 { 259 no_buildid_cache = true; 260 } 261 262 int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, 263 const char *name, bool is_kallsyms, bool is_vdso) 264 { 265 const size_t size = PATH_MAX; 266 char *realname, *filename = zalloc(size), 267 *linkname = zalloc(size), *targetname; 268 int len, err = -1; 269 bool slash = is_kallsyms || is_vdso; 270 271 if (is_kallsyms) { 272 if (symbol_conf.kptr_restrict) { 273 pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); 274 err = 0; 275 goto out_free; 276 } 277 realname = (char *) name; 278 } else 279 realname = realpath(name, NULL); 280 281 if (realname == NULL || filename == NULL || linkname == NULL) 282 goto out_free; 283 284 len = scnprintf(filename, size, "%s%s%s", 285 debugdir, slash ? "/" : "", 286 is_vdso ? DSO__NAME_VDSO : realname); 287 if (mkdir_p(filename, 0755)) 288 goto out_free; 289 290 snprintf(filename + len, size - len, "/%s", sbuild_id); 291 292 if (access(filename, F_OK)) { 293 if (is_kallsyms) { 294 if (copyfile("/proc/kallsyms", filename)) 295 goto out_free; 296 } else if (link(realname, filename) && copyfile(name, filename)) 297 goto out_free; 298 } 299 300 len = scnprintf(linkname, size, "%s/.build-id/%.2s", 301 debugdir, sbuild_id); 302 303 if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) 304 goto out_free; 305 306 snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); 307 targetname = filename + strlen(debugdir) - 5; 308 memcpy(targetname, "../..", 5); 309 310 if (symlink(targetname, linkname) == 0) 311 err = 0; 312 out_free: 313 if (!is_kallsyms) 314 free(realname); 315 free(filename); 316 free(linkname); 317 return err; 318 } 319 320 static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, 321 const char *name, const char *debugdir, 322 bool is_kallsyms, bool is_vdso) 323 { 324 char sbuild_id[BUILD_ID_SIZE * 2 + 1]; 325 326 build_id__sprintf(build_id, build_id_size, sbuild_id); 327 328 return build_id_cache__add_s(sbuild_id, debugdir, name, 329 is_kallsyms, is_vdso); 330 } 331 332 int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) 333 { 334 const size_t size = PATH_MAX; 335 char *filename = zalloc(size), 336 *linkname = zalloc(size); 337 int err = -1; 338 339 if (filename == NULL || linkname == NULL) 340 goto out_free; 341 342 snprintf(linkname, size, "%s/.build-id/%.2s/%s", 343 debugdir, sbuild_id, sbuild_id + 2); 344 345 if (access(linkname, F_OK)) 346 goto out_free; 347 348 if (readlink(linkname, filename, size - 1) < 0) 349 goto out_free; 350 351 if (unlink(linkname)) 352 goto out_free; 353 354 /* 355 * Since the link is relative, we must make it absolute: 356 */ 357 snprintf(linkname, size, "%s/.build-id/%.2s/%s", 358 debugdir, sbuild_id, filename); 359 360 if (unlink(linkname)) 361 goto out_free; 362 363 err = 0; 364 out_free: 365 free(filename); 366 free(linkname); 367 return err; 368 } 369 370 static int dso__cache_build_id(struct dso *dso, struct machine *machine, 371 const char *debugdir) 372 { 373 bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; 374 bool is_vdso = dso__is_vdso(dso); 375 const char *name = dso->long_name; 376 char nm[PATH_MAX]; 377 378 if (dso__is_kcore(dso)) { 379 is_kallsyms = true; 380 machine__mmap_name(machine, nm, sizeof(nm)); 381 name = nm; 382 } 383 return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, 384 debugdir, is_kallsyms, is_vdso); 385 } 386 387 static int __dsos__cache_build_ids(struct list_head *head, 388 struct machine *machine, const char *debugdir) 389 { 390 struct dso *pos; 391 int err = 0; 392 393 dsos__for_each_with_build_id(pos, head) 394 if (dso__cache_build_id(pos, machine, debugdir)) 395 err = -1; 396 397 return err; 398 } 399 400 static int machine__cache_build_ids(struct machine *machine, const char *debugdir) 401 { 402 int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine, 403 debugdir); 404 ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine, 405 debugdir); 406 return ret; 407 } 408 409 int perf_session__cache_build_ids(struct perf_session *session) 410 { 411 struct rb_node *nd; 412 int ret; 413 414 if (no_buildid_cache) 415 return 0; 416 417 if (mkdir(buildid_dir, 0755) != 0 && errno != EEXIST) 418 return -1; 419 420 ret = machine__cache_build_ids(&session->machines.host, buildid_dir); 421 422 for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { 423 struct machine *pos = rb_entry(nd, struct machine, rb_node); 424 ret |= machine__cache_build_ids(pos, buildid_dir); 425 } 426 return ret ? -1 : 0; 427 } 428 429 static bool machine__read_build_ids(struct machine *machine, bool with_hits) 430 { 431 bool ret; 432 433 ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); 434 ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); 435 return ret; 436 } 437 438 bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) 439 { 440 struct rb_node *nd; 441 bool ret = machine__read_build_ids(&session->machines.host, with_hits); 442 443 for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { 444 struct machine *pos = rb_entry(nd, struct machine, rb_node); 445 ret |= machine__read_build_ids(pos, with_hits); 446 } 447 448 return ret; 449 } 450