3329 lines · 81.2 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 "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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t