3369 lines · 85.0 KB · C
RawDownload
// 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.