#include <dirent.h>#include <errno.h>#include <inttypes.h>#include <regex.h>#include "callchain.h"#include "debug.h"#include "event.h"#include "evsel.h"#include "hist.h"#include "machine.h"#include "map.h"#include "sort.h"#include "strlist.h"#include "thread.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 "sane_ctype.h"#include <symbol/kallsyms.h> static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock); static void dsos__init(struct dsos *dsos){ INIT_LIST_HEAD(&dsos->head); dsos->root = RB_ROOT; pthread_rwlock_init(&dsos->lock, NULL);} int machine__init(struct machine *machine, const char *root_dir, pid_t pid){ memset(machine, 0, sizeof(*machine)); map_groups__init(&machine->kmaps, machine); RB_CLEAR_NODE(&machine->rb_node); dsos__init(&machine->dsos); machine->threads = RB_ROOT; pthread_rwlock_init(&machine->threads_lock, NULL); machine->nr_threads = 0; INIT_LIST_HEAD(&machine->dead_threads); machine->last_match = NULL; 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; memset(machine->vmlinux_maps, 0, sizeof(machine->vmlinux_maps)); 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, -1, pid); char comm[64]; if (thread == NULL) return -ENOMEM; snprintf(comm, sizeof(comm), "[guest/%d]", pid); thread__set_comm(thread, comm, 0); thread__put(thread); } machine->current_tid = NULL; 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) MAP__FUNCTION will go away when we stop loading separate maps for * functions and data objects. * 2) We should switch to machine__load_kallsyms(), i.e. not explicitely * 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", MAP__FUNCTION, true) <= 0) { machine__delete(machine); machine = NULL; } return machine;} static void dsos__purge(struct dsos *dsos){ struct dso *pos, *n; pthread_rwlock_wrlock(&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); } pthread_rwlock_unlock(&dsos->lock);} static void dsos__exit(struct dsos *dsos){ dsos__purge(dsos); pthread_rwlock_destroy(&dsos->lock);} void machine__delete_threads(struct machine *machine){ struct rb_node *nd; pthread_rwlock_wrlock(&machine->threads_lock); nd = rb_first(&machine->threads); while (nd) { struct thread *t = rb_entry(nd, struct thread, rb_node); nd = rb_next(nd); __machine__remove_thread(machine, t, false); } pthread_rwlock_unlock(&machine->threads_lock);} void machine__exit(struct machine *machine){ machine__destroy_kernel_maps(machine); map_groups__exit(&machine->kmaps); dsos__exit(&machine->dsos); machine__exit_vdso(machine); zfree(&machine->root_dir); zfree(&machine->current_tid); pthread_rwlock_destroy(&machine->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;} 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_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->guests); 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(&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_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(&machines->guests); 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 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(&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->mg) leader->mg = map_groups__new(machine); if (!leader->mg) goto out_err; if (th->mg == leader->mg) return; if (th->mg) { /* * 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 (!map_groups__empty(th->mg)) pr_err("Discarding thread maps for %d:%d\n", th->pid_, th->tid); map_groups__put(th->mg); } th->mg = map_groups__get(leader->mg);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;} /* * Caller must eventually drop thread->refcnt returned with a successful * lookup/new thread inserted. */static struct thread *____machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid, bool create){ struct rb_node **p = &machine->threads.rb_node; struct rb_node *parent = NULL; struct thread *th; /* * Front-end cache - TID lookups come in blocks, * so most of the time we dont have to look up * the full rbtree: */ th = machine->last_match; if (th != NULL) { if (th->tid == tid) { machine__update_thread_pid(machine, th, pid); return thread__get(th); } machine->last_match = NULL; } while (*p != NULL) { parent = *p; th = rb_entry(parent, struct thread, rb_node); if (th->tid == tid) { machine->last_match = th; machine__update_thread_pid(machine, th, pid); return thread__get(th); } if (tid < th->tid) p = &(*p)->rb_left; else p = &(*p)->rb_right; } if (!create) return NULL; th = thread__new(pid, tid); if (th != NULL) { rb_link_node(&th->rb_node, parent, p); rb_insert_color(&th->rb_node, &machine->threads); /* * We have to initialize map_groups separately * after rb tree is updated. * * The reason is that we call machine__findnew_thread * within thread__init_map_groups to find the thread * leader and that would screwed the rb tree. */ if (thread__init_map_groups(th, machine)) { rb_erase_init(&th->rb_node, &machine->threads); RB_CLEAR_NODE(&th->rb_node); thread__put(th); return NULL; } /* * It is now in the rbtree, get a ref */ thread__get(th); machine->last_match = th; ++machine->nr_threads; } return th;} struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid){ return ____machine__findnew_thread(machine, pid, tid, true);} struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid){ struct thread *th; pthread_rwlock_wrlock(&machine->threads_lock); th = __machine__findnew_thread(machine, pid, tid); pthread_rwlock_unlock(&machine->threads_lock); return th;} struct thread *machine__find_thread(struct machine *machine, pid_t pid, pid_t tid){ struct thread *th; pthread_rwlock_rdlock(&machine->threads_lock); th = ____machine__findnew_thread(machine, pid, tid, false); pthread_rwlock_unlock(&machine->threads_lock); return th;} struct comm *machine__thread_exec_comm(struct machine *machine, struct thread *thread){ if (machine->comm_exec) return thread__exec_comm(thread); else return thread__comm(thread);} int machine__process_comm_event(struct machine *machine, union perf_event *event, struct perf_sample *sample){ struct thread *thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid); bool exec = event->header.misc & PERF_RECORD_MISC_COMM_EXEC; int err = 0; if (exec) machine->comm_exec = true; if (dump_trace)Lines 1–500 of 2235. Load more
#include <dirent.h> #include <errno.h> #include <inttypes.h> #include <regex.h> #include "callchain.h" #include "debug.h" #include "event.h" #include "evsel.h" #include "hist.h" #include "machine.h" #include "map.h" #include "sort.h" #include "strlist.h" #include "thread.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 "sane_ctype.h" #include <symbol/kallsyms.h> static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock); static void dsos__init(struct dsos *dsos) { INIT_LIST_HEAD(&dsos->head); dsos->root = RB_ROOT; pthread_rwlock_init(&dsos->lock, NULL); } int machine__init(struct machine *machine, const char *root_dir, pid_t pid) { memset(machine, 0, sizeof(*machine)); map_groups__init(&machine->kmaps, machine); RB_CLEAR_NODE(&machine->rb_node); dsos__init(&machine->dsos); machine->threads = RB_ROOT; pthread_rwlock_init(&machine->threads_lock, NULL); machine->nr_threads = 0; INIT_LIST_HEAD(&machine->dead_threads); machine->last_match = NULL; 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; memset(machine->vmlinux_maps, 0, sizeof(machine->vmlinux_maps)); 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, -1, pid); char comm[64]; if (thread == NULL) return -ENOMEM; snprintf(comm, sizeof(comm), "[guest/%d]", pid); thread__set_comm(thread, comm, 0); thread__put(thread); } machine->current_tid = NULL; 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) MAP__FUNCTION will go away when we stop loading separate maps for * functions and data objects. * 2) We should switch to machine__load_kallsyms(), i.e. not explicitely * 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", MAP__FUNCTION, true) <= 0) { machine__delete(machine); machine = NULL; } return machine; } static void dsos__purge(struct dsos *dsos) { struct dso *pos, *n; pthread_rwlock_wrlock(&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); } pthread_rwlock_unlock(&dsos->lock); } static void dsos__exit(struct dsos *dsos) { dsos__purge(dsos); pthread_rwlock_destroy(&dsos->lock); } void machine__delete_threads(struct machine *machine) { struct rb_node *nd; pthread_rwlock_wrlock(&machine->threads_lock); nd = rb_first(&machine->threads); while (nd) { struct thread *t = rb_entry(nd, struct thread, rb_node); nd = rb_next(nd); __machine__remove_thread(machine, t, false); } pthread_rwlock_unlock(&machine->threads_lock); } void machine__exit(struct machine *machine) { machine__destroy_kernel_maps(machine); map_groups__exit(&machine->kmaps); dsos__exit(&machine->dsos); machine__exit_vdso(machine); zfree(&machine->root_dir); zfree(&machine->current_tid); pthread_rwlock_destroy(&machine->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; } 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_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->guests); 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(&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_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(&machines->guests); 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 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(&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->mg) leader->mg = map_groups__new(machine); if (!leader->mg) goto out_err; if (th->mg == leader->mg) return; if (th->mg) { /* * 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 (!map_groups__empty(th->mg)) pr_err("Discarding thread maps for %d:%d\n", th->pid_, th->tid); map_groups__put(th->mg); } th->mg = map_groups__get(leader->mg); 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; } /* * Caller must eventually drop thread->refcnt returned with a successful * lookup/new thread inserted. */ static struct thread *____machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid, bool create) { struct rb_node **p = &machine->threads.rb_node; struct rb_node *parent = NULL; struct thread *th; /* * Front-end cache - TID lookups come in blocks, * so most of the time we dont have to look up * the full rbtree: */ th = machine->last_match; if (th != NULL) { if (th->tid == tid) { machine__update_thread_pid(machine, th, pid); return thread__get(th); } machine->last_match = NULL; } while (*p != NULL) { parent = *p; th = rb_entry(parent, struct thread, rb_node); if (th->tid == tid) { machine->last_match = th; machine__update_thread_pid(machine, th, pid); return thread__get(th); } if (tid < th->tid) p = &(*p)->rb_left; else p = &(*p)->rb_right; } if (!create) return NULL; th = thread__new(pid, tid); if (th != NULL) { rb_link_node(&th->rb_node, parent, p); rb_insert_color(&th->rb_node, &machine->threads); /* * We have to initialize map_groups separately * after rb tree is updated. * * The reason is that we call machine__findnew_thread * within thread__init_map_groups to find the thread * leader and that would screwed the rb tree. */ if (thread__init_map_groups(th, machine)) { rb_erase_init(&th->rb_node, &machine->threads); RB_CLEAR_NODE(&th->rb_node); thread__put(th); return NULL; } /* * It is now in the rbtree, get a ref */ thread__get(th); machine->last_match = th; ++machine->nr_threads; } return th; } struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid) { return ____machine__findnew_thread(machine, pid, tid, true); } struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid) { struct thread *th; pthread_rwlock_wrlock(&machine->threads_lock); th = __machine__findnew_thread(machine, pid, tid); pthread_rwlock_unlock(&machine->threads_lock); return th; } struct thread *machine__find_thread(struct machine *machine, pid_t pid, pid_t tid) { struct thread *th; pthread_rwlock_rdlock(&machine->threads_lock); th = ____machine__findnew_thread(machine, pid, tid, false); pthread_rwlock_unlock(&machine->threads_lock); return th; } struct comm *machine__thread_exec_comm(struct machine *machine, struct thread *thread) { if (machine->comm_exec) return thread__exec_comm(thread); else return thread__comm(thread); } int machine__process_comm_event(struct machine *machine, union perf_event *event, struct perf_sample *sample) { struct thread *thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid); bool exec = event->header.misc & PERF_RECORD_MISC_COMM_EXEC; int err = 0; if (exec) machine->comm_exec = true; if (dump_trace)