2235 lines · 53.4 KB · C
RawDownload
#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.