3108 lines · 74.8 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 "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.