123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- #include "debug.h"
- #include "event.h"
- #include "machine.h"
- #include "map.h"
- #include "strlist.h"
- #include "thread.h"
- #include <stdbool.h>
- int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
- {
- map_groups__init(&machine->kmaps);
- RB_CLEAR_NODE(&machine->rb_node);
- INIT_LIST_HEAD(&machine->user_dsos);
- INIT_LIST_HEAD(&machine->kernel_dsos);
- machine->threads = RB_ROOT;
- INIT_LIST_HEAD(&machine->dead_threads);
- machine->last_match = NULL;
- machine->kmaps.machine = machine;
- machine->pid = pid;
- machine->root_dir = strdup(root_dir);
- if (machine->root_dir == NULL)
- return -ENOMEM;
- if (pid != HOST_KERNEL_ID) {
- struct thread *thread = machine__findnew_thread(machine, pid);
- char comm[64];
- if (thread == NULL)
- return -ENOMEM;
- snprintf(comm, sizeof(comm), "[guest/%d]", pid);
- thread__set_comm(thread, comm);
- }
- return 0;
- }
- static void dsos__delete(struct list_head *dsos)
- {
- struct dso *pos, *n;
- list_for_each_entry_safe(pos, n, dsos, node) {
- list_del(&pos->node);
- dso__delete(pos);
- }
- }
- void machine__exit(struct machine *machine)
- {
- map_groups__exit(&machine->kmaps);
- dsos__delete(&machine->user_dsos);
- dsos__delete(&machine->kernel_dsos);
- free(machine->root_dir);
- machine->root_dir = NULL;
- }
- void machine__delete(struct machine *machine)
- {
- machine__exit(machine);
- free(machine);
- }
- struct machine *machines__add(struct rb_root *machines, pid_t pid,
- const char *root_dir)
- {
- struct rb_node **p = &machines->rb_node;
- struct rb_node *parent = NULL;
- struct machine *pos, *machine = malloc(sizeof(*machine));
- if (machine == NULL)
- return NULL;
- if (machine__init(machine, root_dir, pid) != 0) {
- free(machine);
- return NULL;
- }
- while (*p != NULL) {
- parent = *p;
- pos = rb_entry(parent, struct machine, rb_node);
- if (pid < pos->pid)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
- rb_link_node(&machine->rb_node, parent, p);
- rb_insert_color(&machine->rb_node, machines);
- return machine;
- }
- struct machine *machines__find(struct rb_root *machines, pid_t pid)
- {
- struct rb_node **p = &machines->rb_node;
- struct rb_node *parent = NULL;
- struct machine *machine;
- struct machine *default_machine = NULL;
- while (*p != NULL) {
- parent = *p;
- machine = rb_entry(parent, struct machine, rb_node);
- if (pid < machine->pid)
- p = &(*p)->rb_left;
- else if (pid > machine->pid)
- p = &(*p)->rb_right;
- else
- return machine;
- if (!machine->pid)
- default_machine = machine;
- }
- return default_machine;
- }
- struct machine *machines__findnew(struct rb_root *machines, pid_t pid)
- {
- char path[PATH_MAX];
- const char *root_dir = "";
- struct machine *machine = machines__find(machines, pid);
- if (machine && (machine->pid == pid))
- goto out;
- if ((pid != HOST_KERNEL_ID) &&
- (pid != DEFAULT_GUEST_KERNEL_ID) &&
- (symbol_conf.guestmount)) {
- sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
- if (access(path, R_OK)) {
- static struct strlist *seen;
- if (!seen)
- seen = strlist__new(true, NULL);
- if (!strlist__has_entry(seen, path)) {
- pr_err("Can't access file %s\n", path);
- strlist__add(seen, path);
- }
- machine = NULL;
- goto out;
- }
- root_dir = path;
- }
- machine = machines__add(machines, pid, root_dir);
- out:
- return machine;
- }
- void machines__process(struct rb_root *machines,
- machine__process_t process, void *data)
- {
- struct rb_node *nd;
- for (nd = rb_first(machines); nd; nd = rb_next(nd)) {
- struct machine *pos = rb_entry(nd, struct machine, rb_node);
- process(pos, data);
- }
- }
- char *machine__mmap_name(struct machine *machine, char *bf, size_t size)
- {
- if (machine__is_host(machine))
- snprintf(bf, size, "[%s]", "kernel.kallsyms");
- else if (machine__is_default_guest(machine))
- snprintf(bf, size, "[%s]", "guest.kernel.kallsyms");
- else {
- snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms",
- machine->pid);
- }
- return bf;
- }
- void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size)
- {
- struct rb_node *node;
- struct machine *machine;
- for (node = rb_first(machines); node; node = rb_next(node)) {
- machine = rb_entry(node, struct machine, rb_node);
- machine->id_hdr_size = id_hdr_size;
- }
- return;
- }
- static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid,
- bool create)
- {
- struct rb_node **p = &machine->threads.rb_node;
- struct rb_node *parent = NULL;
- struct thread *th;
- /*
- * Font-end cache - PID lookups come in blocks,
- * so most of the time we dont have to look up
- * the full rbtree:
- */
- if (machine->last_match && machine->last_match->pid == pid)
- return machine->last_match;
- while (*p != NULL) {
- parent = *p;
- th = rb_entry(parent, struct thread, rb_node);
- if (th->pid == pid) {
- machine->last_match = th;
- return th;
- }
- if (pid < th->pid)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
- if (!create)
- return NULL;
- th = thread__new(pid);
- if (th != NULL) {
- rb_link_node(&th->rb_node, parent, p);
- rb_insert_color(&th->rb_node, &machine->threads);
- machine->last_match = th;
- }
- return th;
- }
- struct thread *machine__findnew_thread(struct machine *machine, pid_t pid)
- {
- return __machine__findnew_thread(machine, pid, true);
- }
- struct thread *machine__find_thread(struct machine *machine, pid_t pid)
- {
- return __machine__findnew_thread(machine, pid, false);
- }
- int machine__process_comm_event(struct machine *machine, union perf_event *event)
- {
- struct thread *thread = machine__findnew_thread(machine, event->comm.tid);
- if (dump_trace)
- perf_event__fprintf_comm(event, stdout);
- if (thread == NULL || thread__set_comm(thread, event->comm.comm)) {
- dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
- return -1;
- }
- return 0;
- }
- int machine__process_lost_event(struct machine *machine __maybe_unused,
- union perf_event *event)
- {
- dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
- event->lost.id, event->lost.lost);
- return 0;
- }
- static void machine__set_kernel_mmap_len(struct machine *machine,
- union perf_event *event)
- {
- int i;
- for (i = 0; i < MAP__NR_TYPES; i++) {
- machine->vmlinux_maps[i]->start = event->mmap.start;
- machine->vmlinux_maps[i]->end = (event->mmap.start +
- event->mmap.len);
- /*
- * Be a bit paranoid here, some perf.data file came with
- * a zero sized synthesized MMAP event for the kernel.
- */
- if (machine->vmlinux_maps[i]->end == 0)
- machine->vmlinux_maps[i]->end = ~0ULL;
- }
- }
- static int machine__process_kernel_mmap_event(struct machine *machine,
- union perf_event *event)
- {
- struct map *map;
- char kmmap_prefix[PATH_MAX];
- enum dso_kernel_type kernel_type;
- bool is_kernel_mmap;
- machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
- if (machine__is_host(machine))
- kernel_type = DSO_TYPE_KERNEL;
- else
- kernel_type = DSO_TYPE_GUEST_KERNEL;
- is_kernel_mmap = memcmp(event->mmap.filename,
- kmmap_prefix,
- strlen(kmmap_prefix) - 1) == 0;
- if (event->mmap.filename[0] == '/' ||
- (!is_kernel_mmap && event->mmap.filename[0] == '[')) {
- char short_module_name[1024];
- char *name, *dot;
- if (event->mmap.filename[0] == '/') {
- name = strrchr(event->mmap.filename, '/');
- if (name == NULL)
- goto out_problem;
- ++name; /* skip / */
- dot = strrchr(name, '.');
- if (dot == NULL)
- goto out_problem;
- snprintf(short_module_name, sizeof(short_module_name),
- "[%.*s]", (int)(dot - name), name);
- strxfrchar(short_module_name, '-', '_');
- } else
- strcpy(short_module_name, event->mmap.filename);
- map = machine__new_module(machine, event->mmap.start,
- event->mmap.filename);
- if (map == NULL)
- goto out_problem;
- name = strdup(short_module_name);
- if (name == NULL)
- goto out_problem;
- map->dso->short_name = name;
- map->dso->sname_alloc = 1;
- map->end = map->start + event->mmap.len;
- } else if (is_kernel_mmap) {
- const char *symbol_name = (event->mmap.filename +
- strlen(kmmap_prefix));
- /*
- * Should be there already, from the build-id table in
- * the header.
- */
- struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
- kmmap_prefix);
- if (kernel == NULL)
- goto out_problem;
- kernel->kernel = kernel_type;
- if (__machine__create_kernel_maps(machine, kernel) < 0)
- goto out_problem;
- machine__set_kernel_mmap_len(machine, event);
- /*
- * Avoid using a zero address (kptr_restrict) for the ref reloc
- * symbol. Effectively having zero here means that at record
- * time /proc/sys/kernel/kptr_restrict was non zero.
- */
- if (event->mmap.pgoff != 0) {
- maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
- symbol_name,
- event->mmap.pgoff);
- }
- if (machine__is_default_guest(machine)) {
- /*
- * preload dso of guest kernel and modules
- */
- dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
- NULL);
- }
- }
- return 0;
- out_problem:
- return -1;
- }
- int machine__process_mmap_event(struct machine *machine, union perf_event *event)
- {
- u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- struct thread *thread;
- struct map *map;
- int ret = 0;
- if (dump_trace)
- perf_event__fprintf_mmap(event, stdout);
- if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
- cpumode == PERF_RECORD_MISC_KERNEL) {
- ret = machine__process_kernel_mmap_event(machine, event);
- if (ret < 0)
- goto out_problem;
- return 0;
- }
- thread = machine__findnew_thread(machine, event->mmap.pid);
- if (thread == NULL)
- goto out_problem;
- map = map__new(&machine->user_dsos, event->mmap.start,
- event->mmap.len, event->mmap.pgoff,
- event->mmap.pid, event->mmap.filename,
- MAP__FUNCTION);
- if (map == NULL)
- goto out_problem;
- thread__insert_map(thread, map);
- return 0;
- out_problem:
- dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
- return 0;
- }
- int machine__process_fork_event(struct machine *machine, union perf_event *event)
- {
- struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
- struct thread *parent = machine__findnew_thread(machine, event->fork.ptid);
- if (dump_trace)
- perf_event__fprintf_task(event, stdout);
- if (thread == NULL || parent == NULL ||
- thread__fork(thread, parent) < 0) {
- dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
- return -1;
- }
- return 0;
- }
- int machine__process_exit_event(struct machine *machine, union perf_event *event)
- {
- struct thread *thread = machine__find_thread(machine, event->fork.tid);
- if (dump_trace)
- perf_event__fprintf_task(event, stdout);
- if (thread != NULL)
- machine__remove_thread(machine, thread);
- return 0;
- }
- int machine__process_event(struct machine *machine, union perf_event *event)
- {
- int ret;
- switch (event->header.type) {
- case PERF_RECORD_COMM:
- ret = machine__process_comm_event(machine, event); break;
- case PERF_RECORD_MMAP:
- ret = machine__process_mmap_event(machine, event); break;
- case PERF_RECORD_FORK:
- ret = machine__process_fork_event(machine, event); break;
- case PERF_RECORD_EXIT:
- ret = machine__process_exit_event(machine, event); break;
- case PERF_RECORD_LOST:
- ret = machine__process_lost_event(machine, event); break;
- default:
- ret = -1;
- break;
- }
- return ret;
- }
|