// SPDX-License-Identifier: GPL-2.0#include <dirent.h>#include <errno.h>#include <inttypes.h>#include <regex.h>#include <stdlib.h>#include <string.h>#include "callchain.h"#include "debug.h"#include "dso.h"#include "env.h"#include "dwarf-regs.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 "mem-info.h"#include "path.h"#include "srcline.h"#include "symbol.h"#include "synthetic-events.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 <api/io_dir.h> #include <linux/ctype.h>#include <symbol/kallsyms.h>#include <linux/mman.h>#include <linux/string.h>#include <linux/zalloc.h> static struct dso *machine__kernel_dso(struct machine *machine){ return map__dso(machine->vmlinux_map);} 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)); RB_CLEAR_NODE(&machine->rb_node); dsos__init(&machine->dsos); threads__init(&machine->threads); machine->kmaps = maps__new(machine); if (machine->kmaps == NULL) goto out; 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; /* There is no initial context switch in, so we start at 1. */ machine->parallelism = 1; 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) { maps__zput(machine->kmaps); zfree(&machine->root_dir); zfree(&machine->mmap_name); } return err;} static struct machine *__machine__new_host(struct perf_env *host_env, bool kernel_maps){ struct machine *machine = malloc(sizeof(*machine)); if (!machine) return NULL; if (machine__init(machine, "", HOST_KERNEL_ID) != 0) { free(machine); return NULL; } if (kernel_maps && machine__create_kernel_maps(machine) < 0) { free(machine); return NULL; } machine->env = host_env; return machine;} struct machine *machine__new_host(struct perf_env *host_env){ return __machine__new_host(host_env, /*kernel_maps=*/true);} static int mmap_handler(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine){ return machine__process_mmap2_event(machine, event, sample);} static int machine__init_live(struct machine *machine, pid_t pid){ union perf_event event; memset(&event, 0, sizeof(event)); return perf_event__synthesize_mmap_events(NULL, &event, pid, pid, mmap_handler, machine, true);} struct machine *machine__new_live(struct perf_env *host_env, bool kernel_maps, pid_t pid){ struct machine *machine = __machine__new_host(host_env, kernel_maps); if (!machine) return NULL; if (machine__init_live(machine, pid)) { machine__delete(machine); return NULL; } return machine;} struct machine *machine__new_kallsyms(struct perf_env *host_env){ struct machine *machine = machine__new_host(host_env); /* * 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;} void machine__delete_threads(struct machine *machine){ threads__remove_all_threads(&machine->threads);} void machine__exit(struct machine *machine){ if (machine == NULL) return; machine__destroy_kernel_maps(machine); maps__zput(machine->kmaps); dsos__exit(&machine->dsos); machine__exit_vdso(machine); zfree(&machine->root_dir); zfree(&machine->mmap_name); zfree(&machine->current_tid); zfree(&machine->kallsyms_filename); threads__exit(&machine->threads);} void machine__delete(struct machine *machine){ if (machine) { machine__exit(machine); free(machine); }} int machines__init(struct machines *machines){ int err = machine__init(&machines->host, "", HOST_KERNEL_ID); machines->guests = RB_ROOT_CACHED; return err;} 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)) { snprintf(path, sizeof(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 (!maps__empty(thread__maps(thread))) 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__copy_from(thread__maps(thread), thread__maps(host_thread)); 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; node = rb_next(node)) { machine = rb_entry(node, struct machine, rb_node); machine->id_hdr_size = id_hdr_size; } return;} static void machine__update_thread_pid(struct machine *machine, struct thread *th, pid_t pid){ struct thread *leader; if (pid == thread__pid(th) || pid == -1 || thread__pid(th) != -1) return; thread__set_pid(th, pid); if (thread__pid(th) == thread__tid(th)) return; leader = machine__findnew_thread(machine, thread__pid(th), thread__pid(th)); if (!leader) goto out_err; if (!thread__maps(leader)) thread__set_maps(leader, maps__new(machine)); Lines 1–500 of 3369. Load more
// SPDX-License-Identifier: GPL-2.0 #include <dirent.h> #include <errno.h> #include <inttypes.h> #include <regex.h> #include <stdlib.h> #include <string.h> #include "callchain.h" #include "debug.h" #include "dso.h" #include "env.h" #include "dwarf-regs.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 "mem-info.h" #include "path.h" #include "srcline.h" #include "symbol.h" #include "synthetic-events.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 <api/io_dir.h> #include <linux/ctype.h> #include <symbol/kallsyms.h> #include <linux/mman.h> #include <linux/string.h> #include <linux/zalloc.h> static struct dso *machine__kernel_dso(struct machine *machine) { return map__dso(machine->vmlinux_map); } 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)); RB_CLEAR_NODE(&machine->rb_node); dsos__init(&machine->dsos); threads__init(&machine->threads); machine->kmaps = maps__new(machine); if (machine->kmaps == NULL) goto out; 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; /* There is no initial context switch in, so we start at 1. */ machine->parallelism = 1; 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) { maps__zput(machine->kmaps); zfree(&machine->root_dir); zfree(&machine->mmap_name); } return err; } static struct machine *__machine__new_host(struct perf_env *host_env, bool kernel_maps) { struct machine *machine = malloc(sizeof(*machine)); if (!machine) return NULL; if (machine__init(machine, "", HOST_KERNEL_ID) != 0) { free(machine); return NULL; } if (kernel_maps && machine__create_kernel_maps(machine) < 0) { free(machine); return NULL; } machine->env = host_env; return machine; } struct machine *machine__new_host(struct perf_env *host_env) { return __machine__new_host(host_env, /*kernel_maps=*/true); } static int mmap_handler(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) { return machine__process_mmap2_event(machine, event, sample); } static int machine__init_live(struct machine *machine, pid_t pid) { union perf_event event; memset(&event, 0, sizeof(event)); return perf_event__synthesize_mmap_events(NULL, &event, pid, pid, mmap_handler, machine, true); } struct machine *machine__new_live(struct perf_env *host_env, bool kernel_maps, pid_t pid) { struct machine *machine = __machine__new_host(host_env, kernel_maps); if (!machine) return NULL; if (machine__init_live(machine, pid)) { machine__delete(machine); return NULL; } return machine; } struct machine *machine__new_kallsyms(struct perf_env *host_env) { struct machine *machine = machine__new_host(host_env); /* * 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; } void machine__delete_threads(struct machine *machine) { threads__remove_all_threads(&machine->threads); } void machine__exit(struct machine *machine) { if (machine == NULL) return; machine__destroy_kernel_maps(machine); maps__zput(machine->kmaps); dsos__exit(&machine->dsos); machine__exit_vdso(machine); zfree(&machine->root_dir); zfree(&machine->mmap_name); zfree(&machine->current_tid); zfree(&machine->kallsyms_filename); threads__exit(&machine->threads); } void machine__delete(struct machine *machine) { if (machine) { machine__exit(machine); free(machine); } } int machines__init(struct machines *machines) { int err = machine__init(&machines->host, "", HOST_KERNEL_ID); machines->guests = RB_ROOT_CACHED; return err; } 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)) { snprintf(path, sizeof(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 (!maps__empty(thread__maps(thread))) 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__copy_from(thread__maps(thread), thread__maps(host_thread)); 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; node = rb_next(node)) { machine = rb_entry(node, struct machine, rb_node); machine->id_hdr_size = id_hdr_size; } return; } static void machine__update_thread_pid(struct machine *machine, struct thread *th, pid_t pid) { struct thread *leader; if (pid == thread__pid(th) || pid == -1 || thread__pid(th) != -1) return; thread__set_pid(th, pid); if (thread__pid(th) == thread__tid(th)) return; leader = machine__findnew_thread(machine, thread__pid(th), thread__pid(th)); if (!leader) goto out_err; if (!thread__maps(leader)) thread__set_maps(leader, maps__new(machine));