// SPDX-License-Identifier: GPL-2.0#include <dirent.h>#include <errno.h>#include <inttypes.h>#include <regex.h>#include <stdlib.h>#include "callchain.h"#include "debug.h"#include "dso.h"#include "env.h"#include "event.h"#include "evsel.h"#include "hist.h"#include "machine.h"#include "map.h"#include "map_symbol.h"#include "branch.h"#include "mem-events.h"#include "path.h"#include "srcline.h"#include "symbol.h"#include "sort.h"#include "strlist.h"#include "target.h"#include "thread.h"#include "util.h"#include "vdso.h"#include <stdbool.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include "unwind.h"#include "linux/hash.h"#include "asm/bug.h"#include "bpf-event.h"#include <internal/lib.h> // page_size#include "cgroup.h"#include "arm64-frame-pointer-unwind-support.h" #include <linux/ctype.h>#include <symbol/kallsyms.h>#include <linux/mman.h>#include <linux/string.h>#include <linux/zalloc.h> static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock); static struct dso *machine__kernel_dso(struct machine *machine){ return machine->vmlinux_map->dso;} static void dsos__init(struct dsos *dsos){ INIT_LIST_HEAD(&dsos->head); dsos->root = RB_ROOT; init_rwsem(&dsos->lock);} static void machine__threads_init(struct machine *machine){ int i; for (i = 0; i < THREADS__TABLE_SIZE; i++) { struct threads *threads = &machine->threads[i]; threads->entries = RB_ROOT_CACHED; init_rwsem(&threads->lock); threads->nr = 0; INIT_LIST_HEAD(&threads->dead); threads->last_match = NULL; }} static int machine__set_mmap_name(struct machine *machine){ if (machine__is_host(machine)) machine->mmap_name = strdup("[kernel.kallsyms]"); else if (machine__is_default_guest(machine)) machine->mmap_name = strdup("[guest.kernel.kallsyms]"); else if (asprintf(&machine->mmap_name, "[guest.kernel.kallsyms.%d]", machine->pid) < 0) machine->mmap_name = NULL; return machine->mmap_name ? 0 : -ENOMEM;} static void thread__set_guest_comm(struct thread *thread, pid_t pid){ char comm[64]; snprintf(comm, sizeof(comm), "[guest/%d]", pid); thread__set_comm(thread, comm, 0);} int machine__init(struct machine *machine, const char *root_dir, pid_t pid){ int err = -ENOMEM; memset(machine, 0, sizeof(*machine)); machine->kmaps = maps__new(machine); if (machine->kmaps == NULL) return -ENOMEM; RB_CLEAR_NODE(&machine->rb_node); dsos__init(&machine->dsos); machine__threads_init(machine); machine->vdso_info = NULL; machine->env = NULL; machine->pid = pid; machine->id_hdr_size = 0; machine->kptr_restrict_warned = false; machine->comm_exec = false; machine->kernel_start = 0; machine->vmlinux_map = NULL; machine->root_dir = strdup(root_dir); if (machine->root_dir == NULL) goto out; if (machine__set_mmap_name(machine)) goto out; if (pid != HOST_KERNEL_ID) { struct thread *thread = machine__findnew_thread(machine, -1, pid); if (thread == NULL) goto out; thread__set_guest_comm(thread, pid); thread__put(thread); } machine->current_tid = NULL; err = 0; out: if (err) { zfree(&machine->kmaps); zfree(&machine->root_dir); zfree(&machine->mmap_name); } return 0;} struct machine *machine__new_host(void){ struct machine *machine = malloc(sizeof(*machine)); if (machine != NULL) { machine__init(machine, "", HOST_KERNEL_ID); if (machine__create_kernel_maps(machine) < 0) goto out_delete; } return machine;out_delete: free(machine); return NULL;} struct machine *machine__new_kallsyms(void){ struct machine *machine = machine__new_host(); /* * FIXME: * 1) We should switch to machine__load_kallsyms(), i.e. not explicitly * ask for not using the kcore parsing code, once this one is fixed * to create a map per module. */ if (machine && machine__load_kallsyms(machine, "/proc/kallsyms") <= 0) { machine__delete(machine); machine = NULL; } return machine;} static void dsos__purge(struct dsos *dsos){ struct dso *pos, *n; down_write(&dsos->lock); list_for_each_entry_safe(pos, n, &dsos->head, node) { RB_CLEAR_NODE(&pos->rb_node); pos->root = NULL; list_del_init(&pos->node); dso__put(pos); } up_write(&dsos->lock);} static void dsos__exit(struct dsos *dsos){ dsos__purge(dsos); exit_rwsem(&dsos->lock);} void machine__delete_threads(struct machine *machine){ struct rb_node *nd; int i; for (i = 0; i < THREADS__TABLE_SIZE; i++) { struct threads *threads = &machine->threads[i]; down_write(&threads->lock); nd = rb_first_cached(&threads->entries); while (nd) { struct thread *t = rb_entry(nd, struct thread, rb_node); nd = rb_next(nd); __machine__remove_thread(machine, t, false); } up_write(&threads->lock); }} void machine__exit(struct machine *machine){ int i; if (machine == NULL) return; machine__destroy_kernel_maps(machine); maps__delete(machine->kmaps); dsos__exit(&machine->dsos); machine__exit_vdso(machine); zfree(&machine->root_dir); zfree(&machine->mmap_name); zfree(&machine->current_tid); for (i = 0; i < THREADS__TABLE_SIZE; i++) { struct threads *threads = &machine->threads[i]; struct thread *thread, *n; /* * Forget about the dead, at this point whatever threads were * left in the dead lists better have a reference count taken * by who is using them, and then, when they drop those references * and it finally hits zero, thread__put() will check and see that * its not in the dead threads list and will not try to remove it * from there, just calling thread__delete() straight away. */ list_for_each_entry_safe(thread, n, &threads->dead, node) list_del_init(&thread->node); exit_rwsem(&threads->lock); }} void machine__delete(struct machine *machine){ if (machine) { machine__exit(machine); free(machine); }} void machines__init(struct machines *machines){ machine__init(&machines->host, "", HOST_KERNEL_ID); machines->guests = RB_ROOT_CACHED;} void machines__exit(struct machines *machines){ machine__exit(&machines->host); /* XXX exit guest */} struct machine *machines__add(struct machines *machines, pid_t pid, const char *root_dir){ struct rb_node **p = &machines->guests.rb_root.rb_node; struct rb_node *parent = NULL; struct machine *pos, *machine = malloc(sizeof(*machine)); bool leftmost = true; 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; leftmost = false; } } rb_link_node(&machine->rb_node, parent, p); rb_insert_color_cached(&machine->rb_node, &machines->guests, leftmost); machine->machines = machines; return machine;} void machines__set_comm_exec(struct machines *machines, bool comm_exec){ struct rb_node *nd; machines->host.comm_exec = comm_exec; for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) { struct machine *machine = rb_entry(nd, struct machine, rb_node); machine->comm_exec = comm_exec; }} struct machine *machines__find(struct machines *machines, pid_t pid){ struct rb_node **p = &machines->guests.rb_root.rb_node; struct rb_node *parent = NULL; struct machine *machine; struct machine *default_machine = NULL; if (pid == HOST_KERNEL_ID) return &machines->host; 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 machines *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(NULL, 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;} struct machine *machines__find_guest(struct machines *machines, pid_t pid){ struct machine *machine = machines__find(machines, pid); if (!machine) machine = machines__findnew(machines, DEFAULT_GUEST_KERNEL_ID); return machine;} /* * A common case for KVM test programs is that the test program acts as the * hypervisor, creating, running and destroying the virtual machine, and * providing the guest object code from its own object code. In this case, * the VM is not running an OS, but only the functions loaded into it by the * hypervisor test program, and conveniently, loaded at the same virtual * addresses. * * Normally to resolve addresses, MMAP events are needed to map addresses * back to the object code and debug symbols for that object code. * * Currently, there is no way to get such mapping information from guests * but, in the scenario described above, the guest has the same mappings * as the hypervisor, so support for that scenario can be achieved. * * To support that, copy the host thread's maps to the guest thread's maps. * Note, we do not discover the guest until we encounter a guest event, * which works well because it is not until then that we know that the host * thread's maps have been set up. * * This function returns the guest thread. Apart from keeping the data * structures sane, using a thread belonging to the guest machine, instead * of the host thread, allows it to have its own comm (refer * thread__set_guest_comm()). */static struct thread *findnew_guest_code(struct machine *machine, struct machine *host_machine, pid_t pid){ struct thread *host_thread; struct thread *thread; int err; if (!machine) return NULL; thread = machine__findnew_thread(machine, -1, pid); if (!thread) return NULL; /* Assume maps are set up if there are any */ if (thread->maps->nr_maps) return thread; host_thread = machine__find_thread(host_machine, -1, pid); if (!host_thread) goto out_err; thread__set_guest_comm(thread, pid); /* * Guest code can be found in hypervisor process at the same address * so copy host maps. */ err = maps__clone(thread, host_thread->maps); thread__put(host_thread); if (err) goto out_err; return thread; out_err: thread__zput(thread); return NULL;} struct thread *machines__findnew_guest_code(struct machines *machines, pid_t pid){ struct machine *host_machine = machines__find(machines, HOST_KERNEL_ID); struct machine *machine = machines__findnew(machines, pid); return findnew_guest_code(machine, host_machine, pid);} struct thread *machine__findnew_guest_code(struct machine *machine, pid_t pid){ struct machines *machines = machine->machines; struct machine *host_machine; if (!machines) return NULL; host_machine = machines__find(machines, HOST_KERNEL_ID); return findnew_guest_code(machine, host_machine, pid);} void machines__process_guests(struct machines *machines, machine__process_t process, void *data){ struct rb_node *nd; for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); process(pos, data); }} void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size){ struct rb_node *node; struct machine *machine; machines->host.id_hdr_size = id_hdr_size; for (node = rb_first_cached(&machines->guests); node;Lines 1–500 of 3329. Load more
// SPDX-License-Identifier: GPL-2.0 #include <dirent.h> #include <errno.h> #include <inttypes.h> #include <regex.h> #include <stdlib.h> #include "callchain.h" #include "debug.h" #include "dso.h" #include "env.h" #include "event.h" #include "evsel.h" #include "hist.h" #include "machine.h" #include "map.h" #include "map_symbol.h" #include "branch.h" #include "mem-events.h" #include "path.h" #include "srcline.h" #include "symbol.h" #include "sort.h" #include "strlist.h" #include "target.h" #include "thread.h" #include "util.h" #include "vdso.h" #include <stdbool.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "unwind.h" #include "linux/hash.h" #include "asm/bug.h" #include "bpf-event.h" #include <internal/lib.h> // page_size #include "cgroup.h" #include "arm64-frame-pointer-unwind-support.h" #include <linux/ctype.h> #include <symbol/kallsyms.h> #include <linux/mman.h> #include <linux/string.h> #include <linux/zalloc.h> static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock); static struct dso *machine__kernel_dso(struct machine *machine) { return machine->vmlinux_map->dso; } static void dsos__init(struct dsos *dsos) { INIT_LIST_HEAD(&dsos->head); dsos->root = RB_ROOT; init_rwsem(&dsos->lock); } static void machine__threads_init(struct machine *machine) { int i; for (i = 0; i < THREADS__TABLE_SIZE; i++) { struct threads *threads = &machine->threads[i]; threads->entries = RB_ROOT_CACHED; init_rwsem(&threads->lock); threads->nr = 0; INIT_LIST_HEAD(&threads->dead); threads->last_match = NULL; } } static int machine__set_mmap_name(struct machine *machine) { if (machine__is_host(machine)) machine->mmap_name = strdup("[kernel.kallsyms]"); else if (machine__is_default_guest(machine)) machine->mmap_name = strdup("[guest.kernel.kallsyms]"); else if (asprintf(&machine->mmap_name, "[guest.kernel.kallsyms.%d]", machine->pid) < 0) machine->mmap_name = NULL; return machine->mmap_name ? 0 : -ENOMEM; } static void thread__set_guest_comm(struct thread *thread, pid_t pid) { char comm[64]; snprintf(comm, sizeof(comm), "[guest/%d]", pid); thread__set_comm(thread, comm, 0); } int machine__init(struct machine *machine, const char *root_dir, pid_t pid) { int err = -ENOMEM; memset(machine, 0, sizeof(*machine)); machine->kmaps = maps__new(machine); if (machine->kmaps == NULL) return -ENOMEM; RB_CLEAR_NODE(&machine->rb_node); dsos__init(&machine->dsos); machine__threads_init(machine); machine->vdso_info = NULL; machine->env = NULL; machine->pid = pid; machine->id_hdr_size = 0; machine->kptr_restrict_warned = false; machine->comm_exec = false; machine->kernel_start = 0; machine->vmlinux_map = NULL; machine->root_dir = strdup(root_dir); if (machine->root_dir == NULL) goto out; if (machine__set_mmap_name(machine)) goto out; if (pid != HOST_KERNEL_ID) { struct thread *thread = machine__findnew_thread(machine, -1, pid); if (thread == NULL) goto out; thread__set_guest_comm(thread, pid); thread__put(thread); } machine->current_tid = NULL; err = 0; out: if (err) { zfree(&machine->kmaps); zfree(&machine->root_dir); zfree(&machine->mmap_name); } return 0; } struct machine *machine__new_host(void) { struct machine *machine = malloc(sizeof(*machine)); if (machine != NULL) { machine__init(machine, "", HOST_KERNEL_ID); if (machine__create_kernel_maps(machine) < 0) goto out_delete; } return machine; out_delete: free(machine); return NULL; } struct machine *machine__new_kallsyms(void) { struct machine *machine = machine__new_host(); /* * FIXME: * 1) We should switch to machine__load_kallsyms(), i.e. not explicitly * ask for not using the kcore parsing code, once this one is fixed * to create a map per module. */ if (machine && machine__load_kallsyms(machine, "/proc/kallsyms") <= 0) { machine__delete(machine); machine = NULL; } return machine; } static void dsos__purge(struct dsos *dsos) { struct dso *pos, *n; down_write(&dsos->lock); list_for_each_entry_safe(pos, n, &dsos->head, node) { RB_CLEAR_NODE(&pos->rb_node); pos->root = NULL; list_del_init(&pos->node); dso__put(pos); } up_write(&dsos->lock); } static void dsos__exit(struct dsos *dsos) { dsos__purge(dsos); exit_rwsem(&dsos->lock); } void machine__delete_threads(struct machine *machine) { struct rb_node *nd; int i; for (i = 0; i < THREADS__TABLE_SIZE; i++) { struct threads *threads = &machine->threads[i]; down_write(&threads->lock); nd = rb_first_cached(&threads->entries); while (nd) { struct thread *t = rb_entry(nd, struct thread, rb_node); nd = rb_next(nd); __machine__remove_thread(machine, t, false); } up_write(&threads->lock); } } void machine__exit(struct machine *machine) { int i; if (machine == NULL) return; machine__destroy_kernel_maps(machine); maps__delete(machine->kmaps); dsos__exit(&machine->dsos); machine__exit_vdso(machine); zfree(&machine->root_dir); zfree(&machine->mmap_name); zfree(&machine->current_tid); for (i = 0; i < THREADS__TABLE_SIZE; i++) { struct threads *threads = &machine->threads[i]; struct thread *thread, *n; /* * Forget about the dead, at this point whatever threads were * left in the dead lists better have a reference count taken * by who is using them, and then, when they drop those references * and it finally hits zero, thread__put() will check and see that * its not in the dead threads list and will not try to remove it * from there, just calling thread__delete() straight away. */ list_for_each_entry_safe(thread, n, &threads->dead, node) list_del_init(&thread->node); exit_rwsem(&threads->lock); } } void machine__delete(struct machine *machine) { if (machine) { machine__exit(machine); free(machine); } } void machines__init(struct machines *machines) { machine__init(&machines->host, "", HOST_KERNEL_ID); machines->guests = RB_ROOT_CACHED; } void machines__exit(struct machines *machines) { machine__exit(&machines->host); /* XXX exit guest */ } struct machine *machines__add(struct machines *machines, pid_t pid, const char *root_dir) { struct rb_node **p = &machines->guests.rb_root.rb_node; struct rb_node *parent = NULL; struct machine *pos, *machine = malloc(sizeof(*machine)); bool leftmost = true; 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; leftmost = false; } } rb_link_node(&machine->rb_node, parent, p); rb_insert_color_cached(&machine->rb_node, &machines->guests, leftmost); machine->machines = machines; return machine; } void machines__set_comm_exec(struct machines *machines, bool comm_exec) { struct rb_node *nd; machines->host.comm_exec = comm_exec; for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) { struct machine *machine = rb_entry(nd, struct machine, rb_node); machine->comm_exec = comm_exec; } } struct machine *machines__find(struct machines *machines, pid_t pid) { struct rb_node **p = &machines->guests.rb_root.rb_node; struct rb_node *parent = NULL; struct machine *machine; struct machine *default_machine = NULL; if (pid == HOST_KERNEL_ID) return &machines->host; 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 machines *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(NULL, 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; } struct machine *machines__find_guest(struct machines *machines, pid_t pid) { struct machine *machine = machines__find(machines, pid); if (!machine) machine = machines__findnew(machines, DEFAULT_GUEST_KERNEL_ID); return machine; } /* * A common case for KVM test programs is that the test program acts as the * hypervisor, creating, running and destroying the virtual machine, and * providing the guest object code from its own object code. In this case, * the VM is not running an OS, but only the functions loaded into it by the * hypervisor test program, and conveniently, loaded at the same virtual * addresses. * * Normally to resolve addresses, MMAP events are needed to map addresses * back to the object code and debug symbols for that object code. * * Currently, there is no way to get such mapping information from guests * but, in the scenario described above, the guest has the same mappings * as the hypervisor, so support for that scenario can be achieved. * * To support that, copy the host thread's maps to the guest thread's maps. * Note, we do not discover the guest until we encounter a guest event, * which works well because it is not until then that we know that the host * thread's maps have been set up. * * This function returns the guest thread. Apart from keeping the data * structures sane, using a thread belonging to the guest machine, instead * of the host thread, allows it to have its own comm (refer * thread__set_guest_comm()). */ static struct thread *findnew_guest_code(struct machine *machine, struct machine *host_machine, pid_t pid) { struct thread *host_thread; struct thread *thread; int err; if (!machine) return NULL; thread = machine__findnew_thread(machine, -1, pid); if (!thread) return NULL; /* Assume maps are set up if there are any */ if (thread->maps->nr_maps) return thread; host_thread = machine__find_thread(host_machine, -1, pid); if (!host_thread) goto out_err; thread__set_guest_comm(thread, pid); /* * Guest code can be found in hypervisor process at the same address * so copy host maps. */ err = maps__clone(thread, host_thread->maps); thread__put(host_thread); if (err) goto out_err; return thread; out_err: thread__zput(thread); return NULL; } struct thread *machines__findnew_guest_code(struct machines *machines, pid_t pid) { struct machine *host_machine = machines__find(machines, HOST_KERNEL_ID); struct machine *machine = machines__findnew(machines, pid); return findnew_guest_code(machine, host_machine, pid); } struct thread *machine__findnew_guest_code(struct machine *machine, pid_t pid) { struct machines *machines = machine->machines; struct machine *host_machine; if (!machines) return NULL; host_machine = machines__find(machines, HOST_KERNEL_ID); return findnew_guest_code(machine, host_machine, pid); } void machines__process_guests(struct machines *machines, machine__process_t process, void *data) { struct rb_node *nd; for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); process(pos, data); } } void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size) { struct rb_node *node; struct machine *machine; machines->host.id_hdr_size = id_hdr_size; for (node = rb_first_cached(&machines->guests); node;