diff options
Diffstat (limited to 'tools/perf/util/event.c')
| -rw-r--r-- | tools/perf/util/event.c | 1136 | 
1 files changed, 584 insertions, 552 deletions
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index dab9e754a28..d0281bdfa58 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1,119 +1,174 @@  #include <linux/types.h> +#include <sys/mman.h>  #include "event.h"  #include "debug.h" -#include "session.h" +#include "hist.h" +#include "machine.h"  #include "sort.h"  #include "string.h"  #include "strlist.h"  #include "thread.h" +#include "thread_map.h" +#include "symbol/kallsyms.h" + +static const char *perf_event__names[] = { +	[0]					= "TOTAL", +	[PERF_RECORD_MMAP]			= "MMAP", +	[PERF_RECORD_MMAP2]			= "MMAP2", +	[PERF_RECORD_LOST]			= "LOST", +	[PERF_RECORD_COMM]			= "COMM", +	[PERF_RECORD_EXIT]			= "EXIT", +	[PERF_RECORD_THROTTLE]			= "THROTTLE", +	[PERF_RECORD_UNTHROTTLE]		= "UNTHROTTLE", +	[PERF_RECORD_FORK]			= "FORK", +	[PERF_RECORD_READ]			= "READ", +	[PERF_RECORD_SAMPLE]			= "SAMPLE", +	[PERF_RECORD_HEADER_ATTR]		= "ATTR", +	[PERF_RECORD_HEADER_EVENT_TYPE]		= "EVENT_TYPE", +	[PERF_RECORD_HEADER_TRACING_DATA]	= "TRACING_DATA", +	[PERF_RECORD_HEADER_BUILD_ID]		= "BUILD_ID", +	[PERF_RECORD_FINISHED_ROUND]		= "FINISHED_ROUND", +}; + +const char *perf_event__name(unsigned int id) +{ +	if (id >= ARRAY_SIZE(perf_event__names)) +		return "INVALID"; +	if (!perf_event__names[id]) +		return "UNKNOWN"; +	return perf_event__names[id]; +} -const char *event__name[] = { -	[0]			 = "TOTAL", -	[PERF_RECORD_MMAP]	 = "MMAP", -	[PERF_RECORD_LOST]	 = "LOST", -	[PERF_RECORD_COMM]	 = "COMM", -	[PERF_RECORD_EXIT]	 = "EXIT", -	[PERF_RECORD_THROTTLE]	 = "THROTTLE", -	[PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", -	[PERF_RECORD_FORK]	 = "FORK", -	[PERF_RECORD_READ]	 = "READ", -	[PERF_RECORD_SAMPLE]	 = "SAMPLE", -	[PERF_RECORD_HEADER_ATTR]	 = "ATTR", -	[PERF_RECORD_HEADER_EVENT_TYPE]	 = "EVENT_TYPE", -	[PERF_RECORD_HEADER_TRACING_DATA]	 = "TRACING_DATA", -	[PERF_RECORD_HEADER_BUILD_ID]	 = "BUILD_ID", +static struct perf_sample synth_sample = { +	.pid	   = -1, +	.tid	   = -1, +	.time	   = -1, +	.stream_id = -1, +	.cpu	   = -1, +	.period	   = 1,  }; -static pid_t event__synthesize_comm(pid_t pid, int full, -				    event__handler_t process, -				    struct perf_session *session) +static pid_t perf_event__get_comm_tgid(pid_t pid, char *comm, size_t len)  { -	event_t ev;  	char filename[PATH_MAX];  	char bf[BUFSIZ];  	FILE *fp;  	size_t size = 0; -	DIR *tasks; -	struct dirent dirent, *next; -	pid_t tgid = 0; +	pid_t tgid = -1;  	snprintf(filename, sizeof(filename), "/proc/%d/status", pid);  	fp = fopen(filename, "r");  	if (fp == NULL) { -out_race: -		/* -		 * We raced with a task exiting - just return: -		 */  		pr_debug("couldn't open %s\n", filename);  		return 0;  	} -	memset(&ev.comm, 0, sizeof(ev.comm)); -	while (!ev.comm.comm[0] || !ev.comm.pid) { -		if (fgets(bf, sizeof(bf), fp) == NULL) -			goto out_failure; +	while (!comm[0] || (tgid < 0)) { +		if (fgets(bf, sizeof(bf), fp) == NULL) { +			pr_warning("couldn't get COMM and pgid, malformed %s\n", +				   filename); +			break; +		}  		if (memcmp(bf, "Name:", 5) == 0) {  			char *name = bf + 5;  			while (*name && isspace(*name))  				++name;  			size = strlen(name) - 1; -			memcpy(ev.comm.comm, name, size++); +			if (size >= len) +				size = len - 1; +			memcpy(comm, name, size); +			comm[size] = '\0'; +  		} else if (memcmp(bf, "Tgid:", 5) == 0) {  			char *tgids = bf + 5;  			while (*tgids && isspace(*tgids))  				++tgids; -			tgid = ev.comm.pid = atoi(tgids); +			tgid = atoi(tgids);  		}  	} -	ev.comm.header.type = PERF_RECORD_COMM; -	size = ALIGN(size, sizeof(u64)); -	ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); +	fclose(fp); -	if (!full) { -		ev.comm.tid = pid; +	return tgid; +} -		process(&ev, session); -		goto out_fclose; -	} +static pid_t perf_event__synthesize_comm(struct perf_tool *tool, +					 union perf_event *event, pid_t pid, +					 perf_event__handler_t process, +					 struct machine *machine) +{ +	size_t size; +	pid_t tgid; -	snprintf(filename, sizeof(filename), "/proc/%d/task", pid); +	memset(&event->comm, 0, sizeof(event->comm)); -	tasks = opendir(filename); -	if (tasks == NULL) -		goto out_race; +	if (machine__is_host(machine)) +		tgid = perf_event__get_comm_tgid(pid, event->comm.comm, +						 sizeof(event->comm.comm)); +	else +		tgid = machine->pid; -	while (!readdir_r(tasks, &dirent, &next) && next) { -		char *end; -		pid = strtol(dirent.d_name, &end, 10); -		if (*end) -			continue; +	if (tgid < 0) +		goto out; -		ev.comm.tid = pid; +	event->comm.pid = tgid; +	event->comm.header.type = PERF_RECORD_COMM; -		process(&ev, session); -	} -	closedir(tasks); +	size = strlen(event->comm.comm) + 1; +	size = PERF_ALIGN(size, sizeof(u64)); +	memset(event->comm.comm + size, 0, machine->id_hdr_size); +	event->comm.header.size = (sizeof(event->comm) - +				(sizeof(event->comm.comm) - size) + +				machine->id_hdr_size); +	event->comm.tid = pid; -out_fclose: -	fclose(fp); +	if (process(tool, event, &synth_sample, machine) != 0) +		return -1; + +out:  	return tgid; +} + +static int perf_event__synthesize_fork(struct perf_tool *tool, +				       union perf_event *event, pid_t pid, +				       pid_t tgid, perf_event__handler_t process, +				       struct machine *machine) +{ +	memset(&event->fork, 0, sizeof(event->fork) + machine->id_hdr_size); + +	/* this is really a clone event but we use fork to synthesize it */ +	event->fork.ppid = tgid; +	event->fork.ptid = tgid; +	event->fork.pid  = tgid; +	event->fork.tid  = pid; +	event->fork.header.type = PERF_RECORD_FORK; + +	event->fork.header.size = (sizeof(event->fork) + machine->id_hdr_size); + +	if (process(tool, event, &synth_sample, machine) != 0) +		return -1; -out_failure: -	pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); -	return -1; +	return 0;  } -static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, -					 event__handler_t process, -					 struct perf_session *session) +int perf_event__synthesize_mmap_events(struct perf_tool *tool, +				       union perf_event *event, +				       pid_t pid, pid_t tgid, +				       perf_event__handler_t process, +				       struct machine *machine, +				       bool mmap_data)  {  	char filename[PATH_MAX];  	FILE *fp; +	int rc = 0; + +	if (machine__is_default_guest(machine)) +		return 0; -	snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); +	snprintf(filename, sizeof(filename), "%s/proc/%d/maps", +		 machine->root_dir, pid);  	fp = fopen(filename, "r");  	if (fp == NULL) { @@ -124,123 +179,315 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,  		return -1;  	} +	event->header.type = PERF_RECORD_MMAP2; +  	while (1) { -		char bf[BUFSIZ], *pbf = bf; -		event_t ev = { -			.header = { -				.type = PERF_RECORD_MMAP, -				/* -				 * Just like the kernel, see __perf_event_mmap -				 * in kernel/perf_event.c -				 */ -				.misc = PERF_RECORD_MISC_USER, -			 }, -		}; -		int n; +		char bf[BUFSIZ]; +		char prot[5]; +		char execname[PATH_MAX]; +		char anonstr[] = "//anon"; +		unsigned int ino;  		size_t size; +		ssize_t n; +  		if (fgets(bf, sizeof(bf), fp) == NULL)  			break; +		/* ensure null termination since stack will be reused. */ +		strcpy(execname, ""); +  		/* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */ -		n = hex2u64(pbf, &ev.mmap.start); -		if (n < 0) -			continue; -		pbf += n + 1; -		n = hex2u64(pbf, &ev.mmap.len); -		if (n < 0) +		n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n", +		       &event->mmap2.start, &event->mmap2.len, prot, +		       &event->mmap2.pgoff, &event->mmap2.maj, +		       &event->mmap2.min, +		       &ino, execname); + +		/* + 		 * Anon maps don't have the execname. + 		 */ +		if (n < 7)  			continue; -		pbf += n + 3; -		if (*pbf == 'x') { /* vm_exec */ -			char *execname = strchr(bf, '/'); -			/* Catch VDSO */ -			if (execname == NULL) -				execname = strstr(bf, "[vdso]"); +		event->mmap2.ino = (u64)ino; -			if (execname == NULL) -				continue; +		/* +		 * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c +		 */ +		if (machine__is_host(machine)) +			event->header.misc = PERF_RECORD_MISC_USER; +		else +			event->header.misc = PERF_RECORD_MISC_GUEST_USER; + +		/* map protection and flags bits */ +		event->mmap2.prot = 0; +		event->mmap2.flags = 0; +		if (prot[0] == 'r') +			event->mmap2.prot |= PROT_READ; +		if (prot[1] == 'w') +			event->mmap2.prot |= PROT_WRITE; +		if (prot[2] == 'x') +			event->mmap2.prot |= PROT_EXEC; + +		if (prot[3] == 's') +			event->mmap2.flags |= MAP_SHARED; +		else +			event->mmap2.flags |= MAP_PRIVATE; -			pbf += 3; -			n = hex2u64(pbf, &ev.mmap.pgoff); +		if (prot[2] != 'x') { +			if (!mmap_data || prot[0] != 'r') +				continue; -			size = strlen(execname); -			execname[size - 1] = '\0'; /* Remove \n */ -			memcpy(ev.mmap.filename, execname, size); -			size = ALIGN(size, sizeof(u64)); -			ev.mmap.len -= ev.mmap.start; -			ev.mmap.header.size = (sizeof(ev.mmap) - -					       (sizeof(ev.mmap.filename) - size)); -			ev.mmap.pid = tgid; -			ev.mmap.tid = pid; +			event->header.misc |= PERF_RECORD_MISC_MMAP_DATA; +		} -			process(&ev, session); +		if (!strcmp(execname, "")) +			strcpy(execname, anonstr); + +		size = strlen(execname) + 1; +		memcpy(event->mmap2.filename, execname, size); +		size = PERF_ALIGN(size, sizeof(u64)); +		event->mmap2.len -= event->mmap.start; +		event->mmap2.header.size = (sizeof(event->mmap2) - +					(sizeof(event->mmap2.filename) - size)); +		memset(event->mmap2.filename + size, 0, machine->id_hdr_size); +		event->mmap2.header.size += machine->id_hdr_size; +		event->mmap2.pid = tgid; +		event->mmap2.tid = pid; + +		if (process(tool, event, &synth_sample, machine) != 0) { +			rc = -1; +			break;  		}  	}  	fclose(fp); -	return 0; +	return rc;  } -int event__synthesize_modules(event__handler_t process, -			      struct perf_session *session, -			      struct machine *machine) +int perf_event__synthesize_modules(struct perf_tool *tool, +				   perf_event__handler_t process, +				   struct machine *machine)  { +	int rc = 0;  	struct rb_node *nd;  	struct map_groups *kmaps = &machine->kmaps; -	u16 misc; +	union perf_event *event = zalloc((sizeof(event->mmap) + +					  machine->id_hdr_size)); +	if (event == NULL) { +		pr_debug("Not enough memory synthesizing mmap event " +			 "for kernel modules\n"); +		return -1; +	} + +	event->header.type = PERF_RECORD_MMAP;  	/*  	 * kernel uses 0 for user space maps, see kernel/perf_event.c  	 * __perf_event_mmap  	 */  	if (machine__is_host(machine)) -		misc = PERF_RECORD_MISC_KERNEL; +		event->header.misc = PERF_RECORD_MISC_KERNEL;  	else -		misc = PERF_RECORD_MISC_GUEST_KERNEL; +		event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;  	for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]);  	     nd; nd = rb_next(nd)) { -		event_t ev;  		size_t size;  		struct map *pos = rb_entry(nd, struct map, rb_node);  		if (pos->dso->kernel)  			continue; -		size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); -		memset(&ev, 0, sizeof(ev)); -		ev.mmap.header.misc = misc; -		ev.mmap.header.type = PERF_RECORD_MMAP; -		ev.mmap.header.size = (sizeof(ev.mmap) - -				        (sizeof(ev.mmap.filename) - size)); -		ev.mmap.start = pos->start; -		ev.mmap.len   = pos->end - pos->start; -		ev.mmap.pid   = machine->pid; - -		memcpy(ev.mmap.filename, pos->dso->long_name, +		size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); +		event->mmap.header.type = PERF_RECORD_MMAP; +		event->mmap.header.size = (sizeof(event->mmap) - +				        (sizeof(event->mmap.filename) - size)); +		memset(event->mmap.filename + size, 0, machine->id_hdr_size); +		event->mmap.header.size += machine->id_hdr_size; +		event->mmap.start = pos->start; +		event->mmap.len   = pos->end - pos->start; +		event->mmap.pid   = machine->pid; + +		memcpy(event->mmap.filename, pos->dso->long_name,  		       pos->dso->long_name_len + 1); -		process(&ev, session); +		if (process(tool, event, &synth_sample, machine) != 0) { +			rc = -1; +			break; +		} +	} + +	free(event); +	return rc; +} + +static int __event__synthesize_thread(union perf_event *comm_event, +				      union perf_event *mmap_event, +				      union perf_event *fork_event, +				      pid_t pid, int full, +					  perf_event__handler_t process, +				      struct perf_tool *tool, +				      struct machine *machine, bool mmap_data) +{ +	char filename[PATH_MAX]; +	DIR *tasks; +	struct dirent dirent, *next; +	pid_t tgid; + +	/* special case: only send one comm event using passed in pid */ +	if (!full) { +		tgid = perf_event__synthesize_comm(tool, comm_event, pid, +						   process, machine); + +		if (tgid == -1) +			return -1; + +		return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, +							  process, machine, mmap_data); +	} + +	if (machine__is_default_guest(machine)) +		return 0; + +	snprintf(filename, sizeof(filename), "%s/proc/%d/task", +		 machine->root_dir, pid); + +	tasks = opendir(filename); +	if (tasks == NULL) { +		pr_debug("couldn't open %s\n", filename); +		return 0; +	} + +	while (!readdir_r(tasks, &dirent, &next) && next) { +		char *end; +		int rc = 0; +		pid_t _pid; + +		_pid = strtol(dirent.d_name, &end, 10); +		if (*end) +			continue; + +		tgid = perf_event__synthesize_comm(tool, comm_event, _pid, +						   process, machine); +		if (tgid == -1) +			return -1; + +		if (_pid == pid) { +			/* process the parent's maps too */ +			rc = perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, +						process, machine, mmap_data); +		} else { +			/* only fork the tid's map, to save time */ +			rc = perf_event__synthesize_fork(tool, fork_event, _pid, tgid, +						 process, machine); +		} + +		if (rc) +			return rc;  	} +	closedir(tasks);  	return 0;  } -int event__synthesize_thread(pid_t pid, event__handler_t process, -			     struct perf_session *session) +int perf_event__synthesize_thread_map(struct perf_tool *tool, +				      struct thread_map *threads, +				      perf_event__handler_t process, +				      struct machine *machine, +				      bool mmap_data)  { -	pid_t tgid = event__synthesize_comm(pid, 1, process, session); -	if (tgid == -1) -		return -1; -	return event__synthesize_mmap_events(pid, tgid, process, session); +	union perf_event *comm_event, *mmap_event, *fork_event; +	int err = -1, thread, j; + +	comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); +	if (comm_event == NULL) +		goto out; + +	mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size); +	if (mmap_event == NULL) +		goto out_free_comm; + +	fork_event = malloc(sizeof(fork_event->fork) + machine->id_hdr_size); +	if (fork_event == NULL) +		goto out_free_mmap; + +	err = 0; +	for (thread = 0; thread < threads->nr; ++thread) { +		if (__event__synthesize_thread(comm_event, mmap_event, +					       fork_event, +					       threads->map[thread], 0, +					       process, tool, machine, +					       mmap_data)) { +			err = -1; +			break; +		} + +		/* +		 * comm.pid is set to thread group id by +		 * perf_event__synthesize_comm +		 */ +		if ((int) comm_event->comm.pid != threads->map[thread]) { +			bool need_leader = true; + +			/* is thread group leader in thread_map? */ +			for (j = 0; j < threads->nr; ++j) { +				if ((int) comm_event->comm.pid == threads->map[j]) { +					need_leader = false; +					break; +				} +			} + +			/* if not, generate events for it */ +			if (need_leader && +			    __event__synthesize_thread(comm_event, mmap_event, +						       fork_event, +						       comm_event->comm.pid, 0, +						       process, tool, machine, +						       mmap_data)) { +				err = -1; +				break; +			} +		} +	} +	free(fork_event); +out_free_mmap: +	free(mmap_event); +out_free_comm: +	free(comm_event); +out: +	return err;  } -void event__synthesize_threads(event__handler_t process, -			       struct perf_session *session) +int perf_event__synthesize_threads(struct perf_tool *tool, +				   perf_event__handler_t process, +				   struct machine *machine, bool mmap_data)  {  	DIR *proc; +	char proc_path[PATH_MAX];  	struct dirent dirent, *next; +	union perf_event *comm_event, *mmap_event, *fork_event; +	int err = -1; + +	if (machine__is_default_guest(machine)) +		return 0; + +	comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); +	if (comm_event == NULL) +		goto out; + +	mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size); +	if (mmap_event == NULL) +		goto out_free_comm; + +	fork_event = malloc(sizeof(fork_event->fork) + machine->id_hdr_size); +	if (fork_event == NULL) +		goto out_free_mmap; + +	snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir); +	proc = opendir(proc_path); -	proc = opendir("/proc"); +	if (proc == NULL) +		goto out_free_fork;  	while (!readdir_r(proc, &dirent, &next) && next) {  		char *end; @@ -248,11 +495,24 @@ void event__synthesize_threads(event__handler_t process,  		if (*end) /* only interested in proper numerical dirents */  			continue; - -		event__synthesize_thread(pid, process, session); +		/* + 		 * We may race with exiting thread, so don't stop just because + 		 * one thread couldn't be synthesized. + 		 */ +		__event__synthesize_thread(comm_event, mmap_event, fork_event, pid, +					   1, process, tool, machine, mmap_data);  	} +	err = 0;  	closedir(proc); +out_free_fork: +	free(fork_event); +out_free_mmap: +	free(mmap_event); +out_free_comm: +	free(comm_event); +out: +	return err;  }  struct process_symbol_args { @@ -260,7 +520,8 @@ struct process_symbol_args {  	u64	   start;  }; -static int find_symbol_cb(void *arg, const char *name, char type, u64 start) +static int find_symbol_cb(void *arg, const char *name, char type, +			  u64 start)  {  	struct process_symbol_args *args = arg; @@ -276,28 +537,39 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start)  	return 1;  } -int event__synthesize_kernel_mmap(event__handler_t process, -				  struct perf_session *session, -				  struct machine *machine, -				  const char *symbol_name) +u64 kallsyms__get_function_start(const char *kallsyms_filename, +				 const char *symbol_name) +{ +	struct process_symbol_args args = { .name = symbol_name, }; + +	if (kallsyms__parse(kallsyms_filename, &args, find_symbol_cb) <= 0) +		return 0; + +	return args.start; +} + +int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, +				       perf_event__handler_t process, +				       struct machine *machine)  {  	size_t size; -	const char *filename, *mmap_name; -	char path[PATH_MAX]; +	const char *mmap_name;  	char name_buff[PATH_MAX];  	struct map *map; - -	event_t ev = { -		.header = { -			.type = PERF_RECORD_MMAP, -		}, -	}; +	struct kmap *kmap; +	int err;  	/*  	 * We should get this from /sys/kernel/sections/.text, but till that is  	 * available use this, and after it is use this as a fallback for older  	 * kernels.  	 */ -	struct process_symbol_args args = { .name = symbol_name, }; +	union perf_event *event = zalloc((sizeof(event->mmap) + +					  machine->id_hdr_size)); +	if (event == NULL) { +		pr_debug("Not enough memory synthesizing mmap event " +			 "for kernel modules\n"); +		return -1; +	}  	mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff));  	if (machine__is_host(machine)) { @@ -305,320 +577,191 @@ int event__synthesize_kernel_mmap(event__handler_t process,  		 * kernel uses PERF_RECORD_MISC_USER for user space maps,  		 * see kernel/perf_event.c __perf_event_mmap  		 */ -		ev.header.misc = PERF_RECORD_MISC_KERNEL; -		filename = "/proc/kallsyms"; +		event->header.misc = PERF_RECORD_MISC_KERNEL;  	} else { -		ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; -		if (machine__is_default_guest(machine)) -			filename = (char *) symbol_conf.default_guest_kallsyms; -		else { -			sprintf(path, "%s/proc/kallsyms", machine->root_dir); -			filename = path; -		} +		event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;  	} -	if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) -		return -ENOENT; -  	map = machine->vmlinux_maps[MAP__FUNCTION]; -	size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), -			"%s%s", mmap_name, symbol_name) + 1; -	size = ALIGN(size, sizeof(u64)); -	ev.mmap.header.size = (sizeof(ev.mmap) - -			(sizeof(ev.mmap.filename) - size)); -	ev.mmap.pgoff = args.start; -	ev.mmap.start = map->start; -	ev.mmap.len   = map->end - ev.mmap.start; -	ev.mmap.pid   = machine->pid; - -	return process(&ev, session); +	kmap = map__kmap(map); +	size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), +			"%s%s", mmap_name, kmap->ref_reloc_sym->name) + 1; +	size = PERF_ALIGN(size, sizeof(u64)); +	event->mmap.header.type = PERF_RECORD_MMAP; +	event->mmap.header.size = (sizeof(event->mmap) - +			(sizeof(event->mmap.filename) - size) + machine->id_hdr_size); +	event->mmap.pgoff = kmap->ref_reloc_sym->addr; +	event->mmap.start = map->start; +	event->mmap.len   = map->end - event->mmap.start; +	event->mmap.pid   = machine->pid; + +	err = process(tool, event, &synth_sample, machine); +	free(event); + +	return err;  } -static void thread__comm_adjust(struct thread *self, struct hists *hists) +size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)  { -	char *comm = self->comm; - -	if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && -	    (!symbol_conf.comm_list || -	     strlist__has_entry(symbol_conf.comm_list, comm))) { -		u16 slen = strlen(comm); - -		if (hists__new_col_len(hists, HISTC_COMM, slen)) -			hists__set_col_len(hists, HISTC_THREAD, slen + 6); -	} +	return fprintf(fp, ": %s:%d\n", event->comm.comm, event->comm.tid);  } -static int thread__set_comm_adjust(struct thread *self, const char *comm, -				   struct hists *hists) +int perf_event__process_comm(struct perf_tool *tool __maybe_unused, +			     union perf_event *event, +			     struct perf_sample *sample, +			     struct machine *machine)  { -	int ret = thread__set_comm(self, comm); - -	if (ret) -		return ret; - -	thread__comm_adjust(self, hists); - -	return 0; +	return machine__process_comm_event(machine, event, sample);  } -int event__process_comm(event_t *self, struct perf_session *session) +int perf_event__process_lost(struct perf_tool *tool __maybe_unused, +			     union perf_event *event, +			     struct perf_sample *sample, +			     struct machine *machine)  { -	struct thread *thread = perf_session__findnew(session, self->comm.tid); - -	dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); - -	if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, -						      &session->hists)) { -		dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); -		return -1; -	} - -	return 0; +	return machine__process_lost_event(machine, event, sample);  } -int event__process_lost(event_t *self, struct perf_session *session) +size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)  { -	dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); -	session->hists.stats.total_lost += self->lost.lost; -	return 0; +	return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n", +		       event->mmap.pid, event->mmap.tid, event->mmap.start, +		       event->mmap.len, event->mmap.pgoff, +		       (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', +		       event->mmap.filename);  } -static void event_set_kernel_mmap_len(struct map **maps, event_t *self) +size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)  { -	maps[MAP__FUNCTION]->start = self->mmap.start; -	maps[MAP__FUNCTION]->end   = self->mmap.start + self->mmap.len; -	/* -	 * Be a bit paranoid here, some perf.data file came with -	 * a zero sized synthesized MMAP event for the kernel. -	 */ -	if (maps[MAP__FUNCTION]->end == 0) -		maps[MAP__FUNCTION]->end = ~0UL; +	return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 +			   " %02x:%02x %"PRIu64" %"PRIu64"]: %c%c%c%c %s\n", +		       event->mmap2.pid, event->mmap2.tid, event->mmap2.start, +		       event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, +		       event->mmap2.min, event->mmap2.ino, +		       event->mmap2.ino_generation, +		       (event->mmap2.prot & PROT_READ) ? 'r' : '-', +		       (event->mmap2.prot & PROT_WRITE) ? 'w' : '-', +		       (event->mmap2.prot & PROT_EXEC) ? 'x' : '-', +		       (event->mmap2.flags & MAP_SHARED) ? 's' : 'p', +		       event->mmap2.filename);  } -static int event__process_kernel_mmap(event_t *self, -			struct perf_session *session) +int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, +			     union perf_event *event, +			     struct perf_sample *sample, +			     struct machine *machine)  { -	struct map *map; -	char kmmap_prefix[PATH_MAX]; -	struct machine *machine; -	enum dso_kernel_type kernel_type; -	bool is_kernel_mmap; - -	machine = perf_session__findnew_machine(session, self->mmap.pid); -	if (!machine) { -		pr_err("Can't find id %d's machine\n", self->mmap.pid); -		goto out_problem; -	} - -	machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); -	if (machine__is_host(machine)) -		kernel_type = DSO_TYPE_KERNEL; -	else -		kernel_type = DSO_TYPE_GUEST_KERNEL; - -	is_kernel_mmap = memcmp(self->mmap.filename, -				kmmap_prefix, -				strlen(kmmap_prefix)) == 0; -	if (self->mmap.filename[0] == '/' || -	    (!is_kernel_mmap && self->mmap.filename[0] == '[')) { - -		char short_module_name[1024]; -		char *name, *dot; - -		if (self->mmap.filename[0] == '/') { -			name = strrchr(self->mmap.filename, '/'); -			if (name == NULL) -				goto out_problem; - -			++name; /* skip / */ -			dot = strrchr(name, '.'); -			if (dot == NULL) -				goto out_problem; -			snprintf(short_module_name, sizeof(short_module_name), -					"[%.*s]", (int)(dot - name), name); -			strxfrchar(short_module_name, '-', '_'); -		} else -			strcpy(short_module_name, self->mmap.filename); - -		map = machine__new_module(machine, self->mmap.start, -					  self->mmap.filename); -		if (map == NULL) -			goto out_problem; - -		name = strdup(short_module_name); -		if (name == NULL) -			goto out_problem; - -		map->dso->short_name = name; -		map->dso->sname_alloc = 1; -		map->end = map->start + self->mmap.len; -	} else if (is_kernel_mmap) { -		const char *symbol_name = (self->mmap.filename + -				strlen(kmmap_prefix)); -		/* -		 * Should be there already, from the build-id table in -		 * the header. -		 */ -		struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, -						     kmmap_prefix); -		if (kernel == NULL) -			goto out_problem; - -		kernel->kernel = kernel_type; -		if (__machine__create_kernel_maps(machine, kernel) < 0) -			goto out_problem; - -		event_set_kernel_mmap_len(machine->vmlinux_maps, self); -		perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, -							 symbol_name, -							 self->mmap.pgoff); -		if (machine__is_default_guest(machine)) { -			/* -			 * preload dso of guest kernel and modules -			 */ -			dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], -				  NULL); -		} -	} -	return 0; -out_problem: -	return -1; +	return machine__process_mmap_event(machine, event, sample);  } -int event__process_mmap(event_t *self, struct perf_session *session) +int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, +			     union perf_event *event, +			     struct perf_sample *sample, +			     struct machine *machine)  { -	struct machine *machine; -	struct thread *thread; -	struct map *map; -	u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; -	int ret = 0; - -	dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", -			self->mmap.pid, self->mmap.tid, self->mmap.start, -			self->mmap.len, self->mmap.pgoff, self->mmap.filename); - -	if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || -	    cpumode == PERF_RECORD_MISC_KERNEL) { -		ret = event__process_kernel_mmap(self, session); -		if (ret < 0) -			goto out_problem; -		return 0; -	} - -	machine = perf_session__find_host_machine(session); -	if (machine == NULL) -		goto out_problem; -	thread = perf_session__findnew(session, self->mmap.pid); -	if (thread == NULL) -		goto out_problem; -	map = map__new(&machine->user_dsos, self->mmap.start, -			self->mmap.len, self->mmap.pgoff, -			self->mmap.pid, self->mmap.filename, -			MAP__FUNCTION); -	if (map == NULL) -		goto out_problem; - -	thread__insert_map(thread, map); -	return 0; - -out_problem: -	dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); -	return 0; +	return machine__process_mmap2_event(machine, event, sample);  } -int event__process_task(event_t *self, struct perf_session *session) +size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)  { -	struct thread *thread = perf_session__findnew(session, self->fork.tid); -	struct thread *parent = perf_session__findnew(session, self->fork.ptid); - -	dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, -		    self->fork.ppid, self->fork.ptid); - -	if (self->header.type == PERF_RECORD_EXIT) { -		perf_session__remove_thread(session, thread); -		return 0; -	} +	return fprintf(fp, "(%d:%d):(%d:%d)\n", +		       event->fork.pid, event->fork.tid, +		       event->fork.ppid, event->fork.ptid); +} -	if (thread == NULL || parent == NULL || -	    thread__fork(thread, parent) < 0) { -		dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); -		return -1; -	} +int perf_event__process_fork(struct perf_tool *tool __maybe_unused, +			     union perf_event *event, +			     struct perf_sample *sample, +			     struct machine *machine) +{ +	return machine__process_fork_event(machine, event, sample); +} -	return 0; +int perf_event__process_exit(struct perf_tool *tool __maybe_unused, +			     union perf_event *event, +			     struct perf_sample *sample, +			     struct machine *machine) +{ +	return machine__process_exit_event(machine, event, sample);  } -int event__process(event_t *event, struct perf_session *session) +size_t perf_event__fprintf(union perf_event *event, FILE *fp)  { +	size_t ret = fprintf(fp, "PERF_RECORD_%s", +			     perf_event__name(event->header.type)); +  	switch (event->header.type) {  	case PERF_RECORD_COMM: -		event__process_comm(event, session); -		break; -	case PERF_RECORD_MMAP: -		event__process_mmap(event, session); +		ret += perf_event__fprintf_comm(event, fp);  		break;  	case PERF_RECORD_FORK:  	case PERF_RECORD_EXIT: -		event__process_task(event, session); +		ret += perf_event__fprintf_task(event, fp);  		break; -	default: +	case PERF_RECORD_MMAP: +		ret += perf_event__fprintf_mmap(event, fp); +		break; +	case PERF_RECORD_MMAP2: +		ret += perf_event__fprintf_mmap2(event, fp);  		break; +	default: +		ret += fprintf(fp, "\n");  	} -	return 0; +	return ret;  } -void thread__find_addr_map(struct thread *self, -			   struct perf_session *session, u8 cpumode, -			   enum map_type type, pid_t pid, u64 addr, +int perf_event__process(struct perf_tool *tool __maybe_unused, +			union perf_event *event, +			struct perf_sample *sample, +			struct machine *machine) +{ +	return machine__process_event(machine, event, sample); +} + +void thread__find_addr_map(struct thread *thread, +			   struct machine *machine, u8 cpumode, +			   enum map_type type, u64 addr,  			   struct addr_location *al)  { -	struct map_groups *mg = &self->mg; -	struct machine *machine = NULL; +	struct map_groups *mg = thread->mg; +	bool load_map = false; -	al->thread = self; +	al->machine = machine; +	al->thread = thread;  	al->addr = addr;  	al->cpumode = cpumode; -	al->filtered = false; +	al->filtered = 0; + +	if (machine == NULL) { +		al->map = NULL; +		return; +	}  	if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {  		al->level = 'k'; -		machine = perf_session__find_host_machine(session); -		if (machine == NULL) { -			al->map = NULL; -			return; -		}  		mg = &machine->kmaps; +		load_map = true;  	} else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {  		al->level = '.'; -		machine = perf_session__find_host_machine(session);  	} else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {  		al->level = 'g'; -		machine = perf_session__find_machine(session, pid); -		if (machine == NULL) { -			al->map = NULL; -			return; -		}  		mg = &machine->kmaps; +		load_map = true; +	} else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) { +		al->level = 'u';  	} else { -		/* -		 * 'u' means guest os user space. -		 * TODO: We don't support guest user space. Might support late. -		 */ -		if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) -			al->level = 'u'; -		else -			al->level = 'H'; +		al->level = 'H';  		al->map = NULL;  		if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||  			cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&  			!perf_guest) -			al->filtered = true; +			al->filtered |= (1 << HIST_FILTER__GUEST);  		if ((cpumode == PERF_RECORD_MISC_USER ||  			cpumode == PERF_RECORD_MISC_KERNEL) &&  			!perf_host) -			al->filtered = true; +			al->filtered |= (1 << HIST_FILTER__HOST);  		return;  	} @@ -635,201 +778,90 @@ try_again:  		 * in the whole kernel symbol list.  		 */  		if ((long long)al->addr < 0 && -		    cpumode == PERF_RECORD_MISC_KERNEL && +		    cpumode == PERF_RECORD_MISC_USER &&  		    machine && mg != &machine->kmaps) {  			mg = &machine->kmaps;  			goto try_again;  		} -	} else +	} else { +		/* +		 * Kernel maps might be changed when loading symbols so loading +		 * must be done prior to using kernel maps. +		 */ +		if (load_map) +			map__load(al->map, machine->symbol_filter);  		al->addr = al->map->map_ip(al->map, al->addr); +	}  } -void thread__find_addr_location(struct thread *self, -				struct perf_session *session, u8 cpumode, -				enum map_type type, pid_t pid, u64 addr, -				struct addr_location *al, -				symbol_filter_t filter) +void thread__find_addr_location(struct thread *thread, struct machine *machine, +				u8 cpumode, enum map_type type, u64 addr, +				struct addr_location *al)  { -	thread__find_addr_map(self, session, cpumode, type, pid, addr, al); +	thread__find_addr_map(thread, machine, cpumode, type, addr, al);  	if (al->map != NULL) -		al->sym = map__find_symbol(al->map, al->addr, filter); +		al->sym = map__find_symbol(al->map, al->addr, +					   machine->symbol_filter);  	else  		al->sym = NULL;  } -static void dso__calc_col_width(struct dso *self, struct hists *hists) -{ -	if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && -	    (!symbol_conf.dso_list || -	     strlist__has_entry(symbol_conf.dso_list, self->name))) { -		u16 slen = dso__name_len(self); -		hists__new_col_len(hists, HISTC_DSO, slen); -	} - -	self->slen_calculated = 1; -} - -int event__preprocess_sample(const event_t *self, struct perf_session *session, -			     struct addr_location *al, struct sample_data *data, -			     symbol_filter_t filter) +int perf_event__preprocess_sample(const union perf_event *event, +				  struct machine *machine, +				  struct addr_location *al, +				  struct perf_sample *sample)  { -	u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; -	struct thread *thread; +	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; +	struct thread *thread = machine__findnew_thread(machine, sample->pid, +							sample->tid); -	event__parse_sample(self, session->sample_type, data); - -	dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n", -		    self->header.misc, data->pid, data->tid, data->ip, -		    data->period, data->cpu); - -	if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { -		unsigned int i; - -		dump_printf("... chain: nr:%Lu\n", data->callchain->nr); - -		if (!ip_callchain__valid(data->callchain, self)) { -			pr_debug("call-chain problem with event, " -				 "skipping it.\n"); -			goto out_filtered; -		} - -		if (dump_trace) { -			for (i = 0; i < data->callchain->nr; i++) -				dump_printf("..... %2d: %016Lx\n", -					    i, data->callchain->ips[i]); -		} -	} -	thread = perf_session__findnew(session, self->ip.pid);  	if (thread == NULL)  		return -1; -	if (symbol_conf.comm_list && -	    !strlist__has_entry(symbol_conf.comm_list, thread->comm)) -		goto out_filtered; - -	dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); +	dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);  	/* -	 * Have we already created the kernel maps for the host machine? +	 * Have we already created the kernel maps for this machine?  	 *  	 * This should have happened earlier, when we processed the kernel MMAP  	 * events, but for older perf.data files there was no such thing, so do  	 * it now.  	 */  	if (cpumode == PERF_RECORD_MISC_KERNEL && -	    session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL) -		machine__create_kernel_maps(&session->host_machine); +	    machine->vmlinux_maps[MAP__FUNCTION] == NULL) +		machine__create_kernel_maps(machine); -	thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, -			      self->ip.pid, self->ip.ip, al); +	thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, +			      sample->ip, al);  	dump_printf(" ...... dso: %s\n",  		    al->map ? al->map->dso->long_name :  			al->level == 'H' ? "[hypervisor]" : "<not found>"); -	al->sym = NULL; -	al->cpu = data->cpu; - -	if (al->map) { -		if (symbol_conf.dso_list && -		    (!al->map || !al->map->dso || -		     !(strlist__has_entry(symbol_conf.dso_list, -					  al->map->dso->short_name) || -		       (al->map->dso->short_name != al->map->dso->long_name && -			strlist__has_entry(symbol_conf.dso_list, -					   al->map->dso->long_name))))) -			goto out_filtered; -		/* -		 * We have to do this here as we may have a dso with no symbol -		 * hit that has a name longer than the ones with symbols -		 * sampled. -		 */ -		if (!sort_dso.elide && !al->map->dso->slen_calculated) -			dso__calc_col_width(al->map->dso, &session->hists); -		al->sym = map__find_symbol(al->map, al->addr, filter); -	} else { -		const unsigned int unresolved_col_width = BITS_PER_LONG / 4; - -		if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && -		    !symbol_conf.col_width_list_str && !symbol_conf.field_sep && -		    !symbol_conf.dso_list) -			hists__set_col_len(&session->hists, HISTC_DSO, -					   unresolved_col_width); -	} - -	if (symbol_conf.sym_list && al->sym && -	    !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) -		goto out_filtered; - -	return 0; - -out_filtered: -	al->filtered = true; -	return 0; -} - -int event__parse_sample(const event_t *event, u64 type, struct sample_data *data) -{ -	const u64 *array = event->sample.array; - -	if (type & PERF_SAMPLE_IP) { -		data->ip = event->ip.ip; -		array++; -	} - -	if (type & PERF_SAMPLE_TID) { -		u32 *p = (u32 *)array; -		data->pid = p[0]; -		data->tid = p[1]; -		array++; -	} +	if (thread__is_filtered(thread)) +		al->filtered |= (1 << HIST_FILTER__THREAD); -	if (type & PERF_SAMPLE_TIME) { -		data->time = *array; -		array++; -	} - -	if (type & PERF_SAMPLE_ADDR) { -		data->addr = *array; -		array++; -	} - -	data->id = -1ULL; -	if (type & PERF_SAMPLE_ID) { -		data->id = *array; -		array++; -	} - -	if (type & PERF_SAMPLE_STREAM_ID) { -		data->stream_id = *array; -		array++; -	} - -	if (type & PERF_SAMPLE_CPU) { -		u32 *p = (u32 *)array; -		data->cpu = *p; -		array++; -	} else -		data->cpu = -1; +	al->sym = NULL; +	al->cpu = sample->cpu; -	if (type & PERF_SAMPLE_PERIOD) { -		data->period = *array; -		array++; -	} +	if (al->map) { +		struct dso *dso = al->map->dso; -	if (type & PERF_SAMPLE_READ) { -		pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); -		return -1; -	} +		if (symbol_conf.dso_list && +		    (!dso || !(strlist__has_entry(symbol_conf.dso_list, +						  dso->short_name) || +			       (dso->short_name != dso->long_name && +				strlist__has_entry(symbol_conf.dso_list, +						   dso->long_name))))) { +			al->filtered |= (1 << HIST_FILTER__DSO); +		} -	if (type & PERF_SAMPLE_CALLCHAIN) { -		data->callchain = (struct ip_callchain *)array; -		array += 1 + data->callchain->nr; +		al->sym = map__find_symbol(al->map, al->addr, +					   machine->symbol_filter);  	} -	if (type & PERF_SAMPLE_RAW) { -		u32 *p = (u32 *)array; -		data->raw_size = *p; -		p++; -		data->raw_data = p; +	if (symbol_conf.sym_list && +		(!al->sym || !strlist__has_entry(symbol_conf.sym_list, +						al->sym->name))) { +		al->filtered |= (1 << HIST_FILTER__SYMBOL);  	}  	return 0;  | 
