1 #include "debug.h" 2 #include "event.h" 3 #include "machine.h" 4 #include "map.h" 5 #include "strlist.h" 6 #include "thread.h" 7 #include <stdbool.h> 8 9 int machine__init(struct machine *machine, const char *root_dir, pid_t pid) 10 { 11 map_groups__init(&machine->kmaps); 12 RB_CLEAR_NODE(&machine->rb_node); 13 INIT_LIST_HEAD(&machine->user_dsos); 14 INIT_LIST_HEAD(&machine->kernel_dsos); 15 16 machine->threads = RB_ROOT; 17 INIT_LIST_HEAD(&machine->dead_threads); 18 machine->last_match = NULL; 19 20 machine->kmaps.machine = machine; 21 machine->pid = pid; 22 23 machine->root_dir = strdup(root_dir); 24 if (machine->root_dir == NULL) 25 return -ENOMEM; 26 27 if (pid != HOST_KERNEL_ID) { 28 struct thread *thread = machine__findnew_thread(machine, pid); 29 char comm[64]; 30 31 if (thread == NULL) 32 return -ENOMEM; 33 34 snprintf(comm, sizeof(comm), "[guest/%d]", pid); 35 thread__set_comm(thread, comm); 36 } 37 38 return 0; 39 } 40 41 static void dsos__delete(struct list_head *dsos) 42 { 43 struct dso *pos, *n; 44 45 list_for_each_entry_safe(pos, n, dsos, node) { 46 list_del(&pos->node); 47 dso__delete(pos); 48 } 49 } 50 51 void machine__exit(struct machine *machine) 52 { 53 map_groups__exit(&machine->kmaps); 54 dsos__delete(&machine->user_dsos); 55 dsos__delete(&machine->kernel_dsos); 56 free(machine->root_dir); 57 machine->root_dir = NULL; 58 } 59 60 void machine__delete(struct machine *machine) 61 { 62 machine__exit(machine); 63 free(machine); 64 } 65 66 struct machine *machines__add(struct rb_root *machines, pid_t pid, 67 const char *root_dir) 68 { 69 struct rb_node **p = &machines->rb_node; 70 struct rb_node *parent = NULL; 71 struct machine *pos, *machine = malloc(sizeof(*machine)); 72 73 if (machine == NULL) 74 return NULL; 75 76 if (machine__init(machine, root_dir, pid) != 0) { 77 free(machine); 78 return NULL; 79 } 80 81 while (*p != NULL) { 82 parent = *p; 83 pos = rb_entry(parent, struct machine, rb_node); 84 if (pid < pos->pid) 85 p = &(*p)->rb_left; 86 else 87 p = &(*p)->rb_right; 88 } 89 90 rb_link_node(&machine->rb_node, parent, p); 91 rb_insert_color(&machine->rb_node, machines); 92 93 return machine; 94 } 95 96 struct machine *machines__find(struct rb_root *machines, pid_t pid) 97 { 98 struct rb_node **p = &machines->rb_node; 99 struct rb_node *parent = NULL; 100 struct machine *machine; 101 struct machine *default_machine = NULL; 102 103 while (*p != NULL) { 104 parent = *p; 105 machine = rb_entry(parent, struct machine, rb_node); 106 if (pid < machine->pid) 107 p = &(*p)->rb_left; 108 else if (pid > machine->pid) 109 p = &(*p)->rb_right; 110 else 111 return machine; 112 if (!machine->pid) 113 default_machine = machine; 114 } 115 116 return default_machine; 117 } 118 119 struct machine *machines__findnew(struct rb_root *machines, pid_t pid) 120 { 121 char path[PATH_MAX]; 122 const char *root_dir = ""; 123 struct machine *machine = machines__find(machines, pid); 124 125 if (machine && (machine->pid == pid)) 126 goto out; 127 128 if ((pid != HOST_KERNEL_ID) && 129 (pid != DEFAULT_GUEST_KERNEL_ID) && 130 (symbol_conf.guestmount)) { 131 sprintf(path, "%s/%d", symbol_conf.guestmount, pid); 132 if (access(path, R_OK)) { 133 static struct strlist *seen; 134 135 if (!seen) 136 seen = strlist__new(true, NULL); 137 138 if (!strlist__has_entry(seen, path)) { 139 pr_err("Can't access file %s\n", path); 140 strlist__add(seen, path); 141 } 142 machine = NULL; 143 goto out; 144 } 145 root_dir = path; 146 } 147 148 machine = machines__add(machines, pid, root_dir); 149 out: 150 return machine; 151 } 152 153 void machines__process(struct rb_root *machines, 154 machine__process_t process, void *data) 155 { 156 struct rb_node *nd; 157 158 for (nd = rb_first(machines); nd; nd = rb_next(nd)) { 159 struct machine *pos = rb_entry(nd, struct machine, rb_node); 160 process(pos, data); 161 } 162 } 163 164 char *machine__mmap_name(struct machine *machine, char *bf, size_t size) 165 { 166 if (machine__is_host(machine)) 167 snprintf(bf, size, "[%s]", "kernel.kallsyms"); 168 else if (machine__is_default_guest(machine)) 169 snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); 170 else { 171 snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", 172 machine->pid); 173 } 174 175 return bf; 176 } 177 178 void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size) 179 { 180 struct rb_node *node; 181 struct machine *machine; 182 183 for (node = rb_first(machines); node; node = rb_next(node)) { 184 machine = rb_entry(node, struct machine, rb_node); 185 machine->id_hdr_size = id_hdr_size; 186 } 187 188 return; 189 } 190 191 static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, 192 bool create) 193 { 194 struct rb_node **p = &machine->threads.rb_node; 195 struct rb_node *parent = NULL; 196 struct thread *th; 197 198 /* 199 * Font-end cache - PID lookups come in blocks, 200 * so most of the time we dont have to look up 201 * the full rbtree: 202 */ 203 if (machine->last_match && machine->last_match->pid == pid) 204 return machine->last_match; 205 206 while (*p != NULL) { 207 parent = *p; 208 th = rb_entry(parent, struct thread, rb_node); 209 210 if (th->pid == pid) { 211 machine->last_match = th; 212 return th; 213 } 214 215 if (pid < th->pid) 216 p = &(*p)->rb_left; 217 else 218 p = &(*p)->rb_right; 219 } 220 221 if (!create) 222 return NULL; 223 224 th = thread__new(pid); 225 if (th != NULL) { 226 rb_link_node(&th->rb_node, parent, p); 227 rb_insert_color(&th->rb_node, &machine->threads); 228 machine->last_match = th; 229 } 230 231 return th; 232 } 233 234 struct thread *machine__findnew_thread(struct machine *machine, pid_t pid) 235 { 236 return __machine__findnew_thread(machine, pid, true); 237 } 238 239 struct thread *machine__find_thread(struct machine *machine, pid_t pid) 240 { 241 return __machine__findnew_thread(machine, pid, false); 242 } 243 244 int machine__process_comm_event(struct machine *machine, union perf_event *event) 245 { 246 struct thread *thread = machine__findnew_thread(machine, event->comm.tid); 247 248 if (dump_trace) 249 perf_event__fprintf_comm(event, stdout); 250 251 if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { 252 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); 253 return -1; 254 } 255 256 return 0; 257 } 258 259 int machine__process_lost_event(struct machine *machine __maybe_unused, 260 union perf_event *event) 261 { 262 dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", 263 event->lost.id, event->lost.lost); 264 return 0; 265 } 266 267 static void machine__set_kernel_mmap_len(struct machine *machine, 268 union perf_event *event) 269 { 270 int i; 271 272 for (i = 0; i < MAP__NR_TYPES; i++) { 273 machine->vmlinux_maps[i]->start = event->mmap.start; 274 machine->vmlinux_maps[i]->end = (event->mmap.start + 275 event->mmap.len); 276 /* 277 * Be a bit paranoid here, some perf.data file came with 278 * a zero sized synthesized MMAP event for the kernel. 279 */ 280 if (machine->vmlinux_maps[i]->end == 0) 281 machine->vmlinux_maps[i]->end = ~0ULL; 282 } 283 } 284 285 static int machine__process_kernel_mmap_event(struct machine *machine, 286 union perf_event *event) 287 { 288 struct map *map; 289 char kmmap_prefix[PATH_MAX]; 290 enum dso_kernel_type kernel_type; 291 bool is_kernel_mmap; 292 293 machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); 294 if (machine__is_host(machine)) 295 kernel_type = DSO_TYPE_KERNEL; 296 else 297 kernel_type = DSO_TYPE_GUEST_KERNEL; 298 299 is_kernel_mmap = memcmp(event->mmap.filename, 300 kmmap_prefix, 301 strlen(kmmap_prefix) - 1) == 0; 302 if (event->mmap.filename[0] == '/' || 303 (!is_kernel_mmap && event->mmap.filename[0] == '[')) { 304 305 char short_module_name[1024]; 306 char *name, *dot; 307 308 if (event->mmap.filename[0] == '/') { 309 name = strrchr(event->mmap.filename, '/'); 310 if (name == NULL) 311 goto out_problem; 312 313 ++name; /* skip / */ 314 dot = strrchr(name, '.'); 315 if (dot == NULL) 316 goto out_problem; 317 snprintf(short_module_name, sizeof(short_module_name), 318 "[%.*s]", (int)(dot - name), name); 319 strxfrchar(short_module_name, '-', '_'); 320 } else 321 strcpy(short_module_name, event->mmap.filename); 322 323 map = machine__new_module(machine, event->mmap.start, 324 event->mmap.filename); 325 if (map == NULL) 326 goto out_problem; 327 328 name = strdup(short_module_name); 329 if (name == NULL) 330 goto out_problem; 331 332 map->dso->short_name = name; 333 map->dso->sname_alloc = 1; 334 map->end = map->start + event->mmap.len; 335 } else if (is_kernel_mmap) { 336 const char *symbol_name = (event->mmap.filename + 337 strlen(kmmap_prefix)); 338 /* 339 * Should be there already, from the build-id table in 340 * the header. 341 */ 342 struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, 343 kmmap_prefix); 344 if (kernel == NULL) 345 goto out_problem; 346 347 kernel->kernel = kernel_type; 348 if (__machine__create_kernel_maps(machine, kernel) < 0) 349 goto out_problem; 350 351 machine__set_kernel_mmap_len(machine, event); 352 353 /* 354 * Avoid using a zero address (kptr_restrict) for the ref reloc 355 * symbol. Effectively having zero here means that at record 356 * time /proc/sys/kernel/kptr_restrict was non zero. 357 */ 358 if (event->mmap.pgoff != 0) { 359 maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, 360 symbol_name, 361 event->mmap.pgoff); 362 } 363 364 if (machine__is_default_guest(machine)) { 365 /* 366 * preload dso of guest kernel and modules 367 */ 368 dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], 369 NULL); 370 } 371 } 372 return 0; 373 out_problem: 374 return -1; 375 } 376 377 int machine__process_mmap_event(struct machine *machine, union perf_event *event) 378 { 379 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 380 struct thread *thread; 381 struct map *map; 382 int ret = 0; 383 384 if (dump_trace) 385 perf_event__fprintf_mmap(event, stdout); 386 387 if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || 388 cpumode == PERF_RECORD_MISC_KERNEL) { 389 ret = machine__process_kernel_mmap_event(machine, event); 390 if (ret < 0) 391 goto out_problem; 392 return 0; 393 } 394 395 thread = machine__findnew_thread(machine, event->mmap.pid); 396 if (thread == NULL) 397 goto out_problem; 398 map = map__new(&machine->user_dsos, event->mmap.start, 399 event->mmap.len, event->mmap.pgoff, 400 event->mmap.pid, event->mmap.filename, 401 MAP__FUNCTION); 402 if (map == NULL) 403 goto out_problem; 404 405 thread__insert_map(thread, map); 406 return 0; 407 408 out_problem: 409 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); 410 return 0; 411 } 412 413 int machine__process_fork_event(struct machine *machine, union perf_event *event) 414 { 415 struct thread *thread = machine__findnew_thread(machine, event->fork.tid); 416 struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); 417 418 if (dump_trace) 419 perf_event__fprintf_task(event, stdout); 420 421 if (thread == NULL || parent == NULL || 422 thread__fork(thread, parent) < 0) { 423 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); 424 return -1; 425 } 426 427 return 0; 428 } 429 430 int machine__process_exit_event(struct machine *machine, union perf_event *event) 431 { 432 struct thread *thread = machine__find_thread(machine, event->fork.tid); 433 434 if (dump_trace) 435 perf_event__fprintf_task(event, stdout); 436 437 if (thread != NULL) 438 machine__remove_thread(machine, thread); 439 440 return 0; 441 } 442 443 int machine__process_event(struct machine *machine, union perf_event *event) 444 { 445 int ret; 446 447 switch (event->header.type) { 448 case PERF_RECORD_COMM: 449 ret = machine__process_comm_event(machine, event); break; 450 case PERF_RECORD_MMAP: 451 ret = machine__process_mmap_event(machine, event); break; 452 case PERF_RECORD_FORK: 453 ret = machine__process_fork_event(machine, event); break; 454 case PERF_RECORD_EXIT: 455 ret = machine__process_exit_event(machine, event); break; 456 case PERF_RECORD_LOST: 457 ret = machine__process_lost_event(machine, event); break; 458 default: 459 ret = -1; 460 break; 461 } 462 463 return ret; 464 } 465