// 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 "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 <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;} int machine__init(struct machine *machine, const char *root_dir, pid_t pid){ int err = -ENOMEM; memset(machine, 0, sizeof(*machine)); maps__init(&machine->kmaps, machine); 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) return -ENOMEM; if (machine__set_mmap_name(machine)) goto out; if (pid != HOST_KERNEL_ID) { struct thread *thread = machine__findnew_thread(machine, -1, pid); char comm[64]; if (thread == NULL) goto out; snprintf(comm, sizeof(comm), "[guest/%d]", pid); thread__set_comm(thread, comm, 0); thread__put(thread); } machine->current_tid = NULL; err = 0; out: if (err) { 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__exit(&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); 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;} 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 == th->pid_ || pid == -1 || th->pid_ != -1) return; th->pid_ = pid; if (th->pid_ == th->tid) return; leader = __machine__findnew_thread(machine, th->pid_, th->pid_); if (!leader) goto out_err; if (!leader->maps) leader->maps = maps__new(machine); if (!leader->maps) goto out_err; if (th->maps == leader->maps) return; if (th->maps) { /* * Maps are created from MMAP events which provide the pid and * tid. Consequently there never should be any maps on a thread * with an unknown pid. Just print an error if there are. */ if (!maps__empty(th->maps)) pr_err("Discarding thread maps for %d:%d\n", th->pid_, th->tid); maps__put(th->maps); } th->maps = maps__get(leader->maps);out_put: thread__put(leader); return;out_err: pr_err("Failed to join map groups for %d:%d\n", th->pid_, th->tid); goto out_put;} /* * Front-end cache - TID lookups come in blocks, * so most of the time we dont have to look up * the full rbtree: */static struct thread*__threads__get_last_match(struct threads *threads, struct machine *machine, int pid, int tid){ struct thread *th; th = threads->last_match; if (th != NULL) { if (th->tid == tid) { machine__update_thread_pid(machine, th, pid); return thread__get(th); } threads->last_match = NULL; } return NULL;} static struct thread*threads__get_last_match(struct threads *threads, struct machine *machine, int pid, int tid){ struct thread *th = NULL; if (perf_singlethreaded) th = __threads__get_last_match(threads, machine, pid, tid); return th;} static void__threads__set_last_match(struct threads *threads, struct thread *th){ threads->last_match = th;} static voidthreads__set_last_match(struct threads *threads, struct thread *th){ if (perf_singlethreaded) __threads__set_last_match(threads, th);} /* * Caller must eventually drop thread->refcnt returned with a successful * lookup/new thread inserted. */static struct thread *____machine__findnew_thread(struct machine *machine, struct threads *threads,Lines 1–500 of 3108. 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 "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 <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; } int machine__init(struct machine *machine, const char *root_dir, pid_t pid) { int err = -ENOMEM; memset(machine, 0, sizeof(*machine)); maps__init(&machine->kmaps, machine); 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) return -ENOMEM; if (machine__set_mmap_name(machine)) goto out; if (pid != HOST_KERNEL_ID) { struct thread *thread = machine__findnew_thread(machine, -1, pid); char comm[64]; if (thread == NULL) goto out; snprintf(comm, sizeof(comm), "[guest/%d]", pid); thread__set_comm(thread, comm, 0); thread__put(thread); } machine->current_tid = NULL; err = 0; out: if (err) { 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__exit(&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); 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; } 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 == th->pid_ || pid == -1 || th->pid_ != -1) return; th->pid_ = pid; if (th->pid_ == th->tid) return; leader = __machine__findnew_thread(machine, th->pid_, th->pid_); if (!leader) goto out_err; if (!leader->maps) leader->maps = maps__new(machine); if (!leader->maps) goto out_err; if (th->maps == leader->maps) return; if (th->maps) { /* * Maps are created from MMAP events which provide the pid and * tid. Consequently there never should be any maps on a thread * with an unknown pid. Just print an error if there are. */ if (!maps__empty(th->maps)) pr_err("Discarding thread maps for %d:%d\n", th->pid_, th->tid); maps__put(th->maps); } th->maps = maps__get(leader->maps); out_put: thread__put(leader); return; out_err: pr_err("Failed to join map groups for %d:%d\n", th->pid_, th->tid); goto out_put; } /* * Front-end cache - TID lookups come in blocks, * so most of the time we dont have to look up * the full rbtree: */ static struct thread* __threads__get_last_match(struct threads *threads, struct machine *machine, int pid, int tid) { struct thread *th; th = threads->last_match; if (th != NULL) { if (th->tid == tid) { machine__update_thread_pid(machine, th, pid); return thread__get(th); } threads->last_match = NULL; } return NULL; } static struct thread* threads__get_last_match(struct threads *threads, struct machine *machine, int pid, int tid) { struct thread *th = NULL; if (perf_singlethreaded) th = __threads__get_last_match(threads, machine, pid, tid); return th; } static void __threads__set_last_match(struct threads *threads, struct thread *th) { threads->last_match = th; } static void threads__set_last_match(struct threads *threads, struct thread *th) { if (perf_singlethreaded) __threads__set_last_match(threads, th); } /* * Caller must eventually drop thread->refcnt returned with a successful * lookup/new thread inserted. */ static struct thread *____machine__findnew_thread(struct machine *machine, struct threads *threads,