diff options
Diffstat (limited to 'tools/perf/util')
37 files changed, 2896 insertions, 895 deletions
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c new file mode 100644 index 00000000000..04904b35ba8 --- /dev/null +++ b/tools/perf/util/build-id.c @@ -0,0 +1,39 @@ +/* + * build-id.c + * + * build-id support + * + * Copyright (C) 2009, 2010 Red Hat Inc. + * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com> + */ +#include "build-id.h" +#include "event.h" +#include "symbol.h" +#include <linux/kernel.h> + +static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) +{ +	struct addr_location al; +	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; +	struct thread *thread = perf_session__findnew(session, event->ip.pid); + +	if (thread == NULL) { +		pr_err("problem processing %d event, skipping it.\n", +			event->header.type); +		return -1; +	} + +	thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, +			      event->ip.ip, &al); + +	if (al.map != NULL) +		al.map->dso->hit = 1; + +	return 0; +} + +struct perf_event_ops build_id__mark_dso_hit_ops = { +	.sample	= build_id__mark_dso_hit, +	.mmap	= event__process_mmap, +	.fork	= event__process_task, +}; diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h new file mode 100644 index 00000000000..1d981d63cf9 --- /dev/null +++ b/tools/perf/util/build-id.h @@ -0,0 +1,8 @@ +#ifndef PERF_BUILD_ID_H_ +#define PERF_BUILD_ID_H_ 1 + +#include "session.h" + +extern struct perf_event_ops build_id__mark_dso_hit_ops; + +#endif diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c deleted file mode 100644 index b557b836de3..00000000000 --- a/tools/perf/util/data_map.c +++ /dev/null @@ -1,252 +0,0 @@ -#include "symbol.h" -#include "util.h" -#include "debug.h" -#include "thread.h" -#include "session.h" - -static int process_event_stub(event_t *event __used, -			      struct perf_session *session __used) -{ -	dump_printf(": unhandled!\n"); -	return 0; -} - -static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) -{ -	if (!handler->process_sample_event) -		handler->process_sample_event = process_event_stub; -	if (!handler->process_mmap_event) -		handler->process_mmap_event = process_event_stub; -	if (!handler->process_comm_event) -		handler->process_comm_event = process_event_stub; -	if (!handler->process_fork_event) -		handler->process_fork_event = process_event_stub; -	if (!handler->process_exit_event) -		handler->process_exit_event = process_event_stub; -	if (!handler->process_lost_event) -		handler->process_lost_event = process_event_stub; -	if (!handler->process_read_event) -		handler->process_read_event = process_event_stub; -	if (!handler->process_throttle_event) -		handler->process_throttle_event = process_event_stub; -	if (!handler->process_unthrottle_event) -		handler->process_unthrottle_event = process_event_stub; -} - -static 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", -}; - -unsigned long event__total[PERF_RECORD_MAX]; - -void event__print_totals(void) -{ -	int i; -	for (i = 0; i < PERF_RECORD_MAX; ++i) -		pr_info("%10s events: %10ld\n", -			event__name[i], event__total[i]); -} - -static int process_event(event_t *event, struct perf_session *session, -			 struct perf_event_ops *ops, -			 unsigned long offset, unsigned long head) -{ -	trace_event(event); - -	if (event->header.type < PERF_RECORD_MAX) { -		dump_printf("%p [%p]: PERF_RECORD_%s", -			    (void *)(offset + head), -			    (void *)(long)(event->header.size), -			    event__name[event->header.type]); -		++event__total[0]; -		++event__total[event->header.type]; -	} - -	switch (event->header.type) { -	case PERF_RECORD_SAMPLE: -		return ops->process_sample_event(event, session); -	case PERF_RECORD_MMAP: -		return ops->process_mmap_event(event, session); -	case PERF_RECORD_COMM: -		return ops->process_comm_event(event, session); -	case PERF_RECORD_FORK: -		return ops->process_fork_event(event, session); -	case PERF_RECORD_EXIT: -		return ops->process_exit_event(event, session); -	case PERF_RECORD_LOST: -		return ops->process_lost_event(event, session); -	case PERF_RECORD_READ: -		return ops->process_read_event(event, session); -	case PERF_RECORD_THROTTLE: -		return ops->process_throttle_event(event, session); -	case PERF_RECORD_UNTHROTTLE: -		return ops->process_unthrottle_event(event, session); -	default: -		ops->total_unknown++; -		return -1; -	} -} - -int perf_header__read_build_ids(int input, u64 offset, u64 size) -{ -	struct build_id_event bev; -	char filename[PATH_MAX]; -	u64 limit = offset + size; -	int err = -1; - -	while (offset < limit) { -		struct dso *dso; -		ssize_t len; - -		if (read(input, &bev, sizeof(bev)) != sizeof(bev)) -			goto out; - -		len = bev.header.size - sizeof(bev); -		if (read(input, filename, len) != len) -			goto out; - -		dso = dsos__findnew(filename); -		if (dso != NULL) -			dso__set_build_id(dso, &bev.build_id); - -		offset += bev.header.size; -	} -	err = 0; -out: -	return err; -} - -static struct thread *perf_session__register_idle_thread(struct perf_session *self) -{ -	struct thread *thread = perf_session__findnew(self, 0); - -	if (!thread || thread__set_comm(thread, "swapper")) { -		pr_err("problem inserting idle task.\n"); -		thread = NULL; -	} - -	return thread; -} - -int perf_session__process_events(struct perf_session *self, -				 struct perf_event_ops *ops) -{ -	int err; -	unsigned long head, shift; -	unsigned long offset = 0; -	size_t	page_size; -	event_t *event; -	uint32_t size; -	char *buf; - -	if (perf_session__register_idle_thread(self) == NULL) -		return -ENOMEM; - -	perf_event_ops__fill_defaults(ops); - -	page_size = getpagesize(); - -	head = self->header.data_offset; -	self->sample_type = perf_header__sample_type(&self->header); - -	err = -EINVAL; -	if (ops->sample_type_check && ops->sample_type_check(self) < 0) -		goto out_err; - -	if (!ops->full_paths) { -		char bf[PATH_MAX]; - -		if (getcwd(bf, sizeof(bf)) == NULL) { -			err = -errno; -out_getcwd_err: -			pr_err("failed to get the current directory\n"); -			goto out_err; -		} -		self->cwd = strdup(bf); -		if (self->cwd == NULL) { -			err = -ENOMEM; -			goto out_getcwd_err; -		} -		self->cwdlen = strlen(self->cwd); -	} - -	shift = page_size * (head / page_size); -	offset += shift; -	head -= shift; - -remap: -	buf = mmap(NULL, page_size * self->mmap_window, PROT_READ, -		   MAP_SHARED, self->fd, offset); -	if (buf == MAP_FAILED) { -		pr_err("failed to mmap file\n"); -		err = -errno; -		goto out_err; -	} - -more: -	event = (event_t *)(buf + head); - -	size = event->header.size; -	if (!size) -		size = 8; - -	if (head + event->header.size >= page_size * self->mmap_window) { -		int munmap_ret; - -		shift = page_size * (head / page_size); - -		munmap_ret = munmap(buf, page_size * self->mmap_window); -		assert(munmap_ret == 0); - -		offset += shift; -		head -= shift; -		goto remap; -	} - -	size = event->header.size; - -	dump_printf("\n%p [%p]: event: %d\n", -			(void *)(offset + head), -			(void *)(long)event->header.size, -			event->header.type); - -	if (!size || process_event(event, self, ops, offset, head) < 0) { - -		dump_printf("%p [%p]: skipping unknown header type: %d\n", -			(void *)(offset + head), -			(void *)(long)(event->header.size), -			event->header.type); - -		/* -		 * assume we lost track of the stream, check alignment, and -		 * increment a single u64 in the hope to catch on again 'soon'. -		 */ - -		if (unlikely(head & 7)) -			head &= ~7ULL; - -		size = 8; -	} - -	head += size; - -	if (offset + head >= self->header.data_offset + self->header.data_size) -		goto done; - -	if (offset + head < self->size) -		goto more; - -done: -	err = 0; -out_err: -	return err; -} diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 28d520d5a1f..0905600c385 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -9,6 +9,7 @@  #include "color.h"  #include "event.h"  #include "debug.h" +#include "util.h"  int verbose = 0;  int dump_trace = 0; diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c index 06b73ee02c4..a88fefc0cc0 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/perf/util/debugfs.c @@ -106,16 +106,14 @@ int debugfs_valid_entry(const char *path)  	return 0;  } -/* mount the debugfs somewhere */ +/* mount the debugfs somewhere if it's not mounted */ -int debugfs_mount(const char *mountpoint) +char *debugfs_mount(const char *mountpoint)  { -	char mountcmd[128]; -  	/* see if it's already mounted */  	if (debugfs_find_mountpoint()) {  		debugfs_premounted = 1; -		return 0; +		return debugfs_mountpoint;  	}  	/* if not mounted and no argument */ @@ -127,13 +125,14 @@ int debugfs_mount(const char *mountpoint)  			mountpoint = "/sys/kernel/debug";  	} +	if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) +		return NULL; +  	/* save the mountpoint */  	strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); +	debugfs_found = 1; -	/* mount it */ -	snprintf(mountcmd, sizeof(mountcmd), -		 "/bin/mount -t debugfs debugfs %s", mountpoint); -	return system(mountcmd); +	return debugfs_mountpoint;  }  /* umount the debugfs */ diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h index 3cd14f9ae78..83a02879745 100644 --- a/tools/perf/util/debugfs.h +++ b/tools/perf/util/debugfs.h @@ -15,7 +15,7 @@  extern const char *debugfs_find_mountpoint(void);  extern int debugfs_valid_mountpoint(const char *debugfs);  extern int debugfs_valid_entry(const char *path); -extern int debugfs_mount(const char *mountpoint); +extern char *debugfs_mount(const char *mountpoint);  extern int debugfs_umount(void);  extern int debugfs_write(const char *entry, const char *value);  extern int debugfs_read(const char *entry, char *buffer, size_t size); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index bb0fd6da2d5..705ec63548b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -8,8 +8,7 @@  #include "thread.h"  static pid_t event__synthesize_comm(pid_t pid, int full, -				    int (*process)(event_t *event, -						   struct perf_session *session), +				    event__handler_t process,  				    struct perf_session *session)  {  	event_t ev; @@ -91,8 +90,7 @@ out_failure:  }  static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, -					 int (*process)(event_t *event, -							struct perf_session *session), +					 event__handler_t process,  					 struct perf_session *session)  {  	char filename[PATH_MAX]; @@ -112,7 +110,10 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,  	while (1) {  		char bf[BUFSIZ], *pbf = bf;  		event_t ev = { -			.header = { .type = PERF_RECORD_MMAP }, +			.header = { +				.type = PERF_RECORD_MMAP, +				.misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ +			 },  		};  		int n;  		size_t size; @@ -156,9 +157,38 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,  	return 0;  } -int event__synthesize_thread(pid_t pid, -			     int (*process)(event_t *event, -					    struct perf_session *session), +int event__synthesize_modules(event__handler_t process, +			      struct perf_session *session) +{ +	struct rb_node *nd; + +	for (nd = rb_first(&session->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 = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ +		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; + +		memcpy(ev.mmap.filename, pos->dso->long_name, +		       pos->dso->long_name_len + 1); +		process(&ev, session); +	} + +	return 0; +} + +int event__synthesize_thread(pid_t pid, event__handler_t process,  			     struct perf_session *session)  {  	pid_t tgid = event__synthesize_comm(pid, 1, process, session); @@ -167,8 +197,7 @@ int event__synthesize_thread(pid_t pid,  	return event__synthesize_mmap_events(pid, tgid, process, session);  } -void event__synthesize_threads(int (*process)(event_t *event, -					      struct perf_session *session), +void event__synthesize_threads(event__handler_t process,  			       struct perf_session *session)  {  	DIR *proc; @@ -189,6 +218,59 @@ void event__synthesize_threads(int (*process)(event_t *event,  	closedir(proc);  } +struct process_symbol_args { +	const char *name; +	u64	   start; +}; + +static int find_symbol_cb(void *arg, const char *name, char type, u64 start) +{ +	struct process_symbol_args *args = arg; + +	/* +	 * Must be a function or at least an alias, as in PARISC64, where "_text" is +	 * an 'A' to the same address as "_stext". +	 */ +	if (!(symbol_type__is_a(type, MAP__FUNCTION) || +	      type == 'A') || strcmp(name, args->name)) +		return 0; + +	args->start = start; +	return 1; +} + +int event__synthesize_kernel_mmap(event__handler_t process, +				  struct perf_session *session, +				  const char *symbol_name) +{ +	size_t size; +	event_t ev = { +		.header = { +			.type = PERF_RECORD_MMAP, +			.misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ +		}, +	}; +	/* +	 * 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, }; + +	if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) +		return -ENOENT; + +	size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), +			"[kernel.kallsyms.%s]", 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 = session->vmlinux_maps[MAP__FUNCTION]->start; +	ev.mmap.len   = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; + +	return process(&ev, session); +} +  static void thread__comm_adjust(struct thread *self)  {  	char *comm = self->comm; @@ -240,22 +322,88 @@ int event__process_lost(event_t *self, struct perf_session *session)  int event__process_mmap(event_t *self, struct perf_session *session)  { -	struct thread *thread = perf_session__findnew(session, self->mmap.pid); -	struct map *map = map__new(&self->mmap, MAP__FUNCTION, -				   session->cwd, session->cwdlen); +	struct thread *thread; +	struct map *map; + +	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 (self->mmap.pid == 0) { +		static const char kmmap_prefix[] = "[kernel.kallsyms."; + +		if (self->mmap.filename[0] == '/') { +			char short_module_name[1024]; +			char *name = strrchr(self->mmap.filename, '/'), *dot; -	dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", -		    self->mmap.pid, self->mmap.tid, -		    (void *)(long)self->mmap.start, -		    (void *)(long)self->mmap.len, -		    (void *)(long)self->mmap.pgoff, -		    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, '-', '_'); + +			map = perf_session__new_module_map(session, +							   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->end = map->start + self->mmap.len; +		} else if (memcmp(self->mmap.filename, kmmap_prefix, +				sizeof(kmmap_prefix) - 1) == 0) { +			const char *symbol_name = (self->mmap.filename + +						   sizeof(kmmap_prefix) - 1); +			/* +			 * Should be there already, from the build-id table in +			 * the header. +			 */ +			struct dso *kernel = __dsos__findnew(&dsos__kernel, +							     "[kernel.kallsyms]"); +			if (kernel == NULL) +				goto out_problem; + +			kernel->kernel = 1; +			if (__perf_session__create_kernel_maps(session, kernel) < 0) +				goto out_problem; + +			session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; +			session->vmlinux_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 (session->vmlinux_maps[MAP__FUNCTION]->end == 0) +				session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; + +			perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, +								 self->mmap.pgoff); +		} +		return 0; +	} + +	thread = perf_session__findnew(session, self->mmap.pid); +	map = map__new(&self->mmap, MAP__FUNCTION, +		       session->cwd, session->cwdlen);  	if (thread == NULL || map == NULL) -		dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); -	else -		thread__insert_map(thread, map); +		goto out_problem; +	thread__insert_map(thread, map); +	return 0; + +out_problem: +	dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");  	return 0;  } @@ -284,26 +432,24 @@ int event__process_task(event_t *self, struct perf_session *session)  	return 0;  } -void thread__find_addr_location(struct thread *self, -				struct perf_session *session, u8 cpumode, -				enum map_type type, u64 addr, -				struct addr_location *al, -				symbol_filter_t filter) +void thread__find_addr_map(struct thread *self, +			   struct perf_session *session, u8 cpumode, +			   enum map_type type, u64 addr, +			   struct addr_location *al)  {  	struct map_groups *mg = &self->mg;  	al->thread = self;  	al->addr = addr; -	if (cpumode & PERF_RECORD_MISC_KERNEL) { +	if (cpumode == PERF_RECORD_MISC_KERNEL) {  		al->level = 'k';  		mg = &session->kmaps; -	} else if (cpumode & PERF_RECORD_MISC_USER) +	} else if (cpumode == PERF_RECORD_MISC_USER)  		al->level = '.';  	else {  		al->level = 'H';  		al->map = NULL; -		al->sym = NULL;  		return;  	}  try_again: @@ -322,11 +468,21 @@ try_again:  			mg = &session->kmaps;  			goto try_again;  		} -		al->sym = NULL; -	} else { +	} else  		al->addr = al->map->map_ip(al->map, al->addr); -		al->sym = map__find_symbol(al->map, session, al->addr, filter); -	} +} + +void thread__find_addr_location(struct thread *self, +				struct perf_session *session, u8 cpumode, +				enum map_type type, u64 addr, +				struct addr_location *al, +				symbol_filter_t filter) +{ +	thread__find_addr_map(self, session, cpumode, type, addr, al); +	if (al->map != NULL) +		al->sym = map__find_symbol(al->map, al->addr, filter); +	else +		al->sym = NULL;  }  static void dso__calc_col_width(struct dso *self) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 690a96d0467..50a7132887f 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -1,10 +1,10 @@  #ifndef __PERF_RECORD_H  #define __PERF_RECORD_H +#include <limits.h> +  #include "../perf.h" -#include "util.h" -#include <linux/list.h> -#include <linux/rbtree.h> +#include "map.h"  /*   * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * @@ -101,74 +101,19 @@ struct events_stats {  void event__print_totals(void); -enum map_type { -	MAP__FUNCTION = 0, -	MAP__VARIABLE, -}; - -#define MAP__NR_TYPES (MAP__VARIABLE + 1) - -struct map { -	union { -		struct rb_node	rb_node; -		struct list_head node; -	}; -	u64			start; -	u64			end; -	enum map_type		type; -	u64			pgoff; -	u64			(*map_ip)(struct map *, u64); -	u64			(*unmap_ip)(struct map *, u64); -	struct dso		*dso; -}; - -static inline u64 map__map_ip(struct map *map, u64 ip) -{ -	return ip - map->start + map->pgoff; -} - -static inline u64 map__unmap_ip(struct map *map, u64 ip) -{ -	return ip + map->start - map->pgoff; -} - -static inline u64 identity__map_ip(struct map *map __used, u64 ip) -{ -	return ip; -} - -struct symbol; - -typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); - -void map__init(struct map *self, enum map_type type, -	       u64 start, u64 end, u64 pgoff, struct dso *dso); -struct map *map__new(struct mmap_event *event, enum map_type, -		     char *cwd, int cwdlen); -void map__delete(struct map *self); -struct map *map__clone(struct map *self); -int map__overlap(struct map *l, struct map *r); -size_t map__fprintf(struct map *self, FILE *fp); -  struct perf_session; -int map__load(struct map *self, struct perf_session *session, -	      symbol_filter_t filter); -struct symbol *map__find_symbol(struct map *self, struct perf_session *session, -				u64 addr, symbol_filter_t filter); -struct symbol *map__find_symbol_by_name(struct map *self, const char *name, -					struct perf_session *session, -					symbol_filter_t filter); -void map__fixup_start(struct map *self); -void map__fixup_end(struct map *self); +typedef int (*event__handler_t)(event_t *event, struct perf_session *session); -int event__synthesize_thread(pid_t pid, -			     int (*process)(event_t *event, -					    struct perf_session *session), +int event__synthesize_thread(pid_t pid, event__handler_t process,  			     struct perf_session *session); -void event__synthesize_threads(int (*process)(event_t *event, -					      struct perf_session *session), +void event__synthesize_threads(event__handler_t process,  			       struct perf_session *session); +int event__synthesize_kernel_mmap(event__handler_t process, +				  struct perf_session *session, +				  const char *symbol_name); +int event__synthesize_modules(event__handler_t process, +			      struct perf_session *session);  int event__process_comm(event_t *self, struct perf_session *session);  int event__process_lost(event_t *self, struct perf_session *session); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 8a0bca55106..6c9aa16ee51 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1,8 +1,12 @@ +#define _FILE_OFFSET_BITS 64 +  #include <sys/types.h> +#include <byteswap.h>  #include <unistd.h>  #include <stdio.h>  #include <stdlib.h>  #include <linux/list.h> +#include <linux/kernel.h>  #include "util.h"  #include "header.h" @@ -105,24 +109,28 @@ struct perf_trace_event_type {  static int event_count;  static struct perf_trace_event_type *events; -void perf_header__push_event(u64 id, const char *name) +int perf_header__push_event(u64 id, const char *name)  {  	if (strlen(name) > MAX_EVENT_NAME)  		pr_warning("Event %s will be truncated\n", name);  	if (!events) {  		events = malloc(sizeof(struct perf_trace_event_type)); -		if (!events) -			die("nomem"); +		if (events == NULL) +			return -ENOMEM;  	} else { -		events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type)); -		if (!events) -			die("nomem"); +		struct perf_trace_event_type *nevents; + +		nevents = realloc(events, (event_count + 1) * sizeof(*events)); +		if (nevents == NULL) +			return -ENOMEM; +		events = nevents;  	}  	memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));  	events[event_count].event_id = id;  	strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);  	event_count++; +	return 0;  }  char *perf_header__find_event(u64 id) @@ -169,31 +177,48 @@ static int do_write(int fd, const void *buf, size_t size)  	return 0;  } -static int __dsos__write_buildid_table(struct list_head *head, int fd) +#define NAME_ALIGN 64 + +static int write_padded(int fd, const void *bf, size_t count, +			size_t count_aligned)  { -#define NAME_ALIGN	64 -	struct dso *pos;  	static const char zero_buf[NAME_ALIGN]; +	int err = do_write(fd, bf, count); + +	if (!err) +		err = do_write(fd, zero_buf, count_aligned - count); + +	return err; +} -	list_for_each_entry(pos, head, node) { +#define dsos__for_each_with_build_id(pos, head)	\ +	list_for_each_entry(pos, head, node)	\ +		if (!pos->has_build_id)		\ +			continue;		\ +		else + +static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) +{ +	struct dso *pos; + +	dsos__for_each_with_build_id(pos, head) {  		int err;  		struct build_id_event b;  		size_t len; -		if (!pos->has_build_id) +		if (!pos->hit)  			continue;  		len = pos->long_name_len + 1;  		len = ALIGN(len, NAME_ALIGN);  		memset(&b, 0, sizeof(b));  		memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); +		b.header.misc = misc;  		b.header.size = sizeof(b) + len;  		err = do_write(fd, &b, sizeof(b));  		if (err < 0)  			return err; -		err = do_write(fd, pos->long_name, pos->long_name_len + 1); -		if (err < 0) -			return err; -		err = do_write(fd, zero_buf, len - pos->long_name_len - 1); +		err = write_padded(fd, pos->long_name, +				   pos->long_name_len + 1, len);  		if (err < 0)  			return err;  	} @@ -203,12 +228,143 @@ static int __dsos__write_buildid_table(struct list_head *head, int fd)  static int dsos__write_buildid_table(int fd)  { -	int err = __dsos__write_buildid_table(&dsos__kernel, fd); +	int err = __dsos__write_buildid_table(&dsos__kernel, +					      PERF_RECORD_MISC_KERNEL, fd);  	if (err == 0) -		err = __dsos__write_buildid_table(&dsos__user, fd); +		err = __dsos__write_buildid_table(&dsos__user, +						  PERF_RECORD_MISC_USER, fd);  	return err;  } +int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, +			  const char *name, bool is_kallsyms) +{ +	const size_t size = PATH_MAX; +	char *filename = malloc(size), +	     *linkname = malloc(size), *targetname; +	int len, err = -1; + +	if (filename == NULL || linkname == NULL) +		goto out_free; + +	len = snprintf(filename, size, "%s%s%s", +		       debugdir, is_kallsyms ? "/" : "", name); +	if (mkdir_p(filename, 0755)) +		goto out_free; + +	snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); + +	if (access(filename, F_OK)) { +		if (is_kallsyms) { +			 if (copyfile("/proc/kallsyms", filename)) +				goto out_free; +		} else if (link(name, filename) && copyfile(name, filename)) +			goto out_free; +	} + +	len = snprintf(linkname, size, "%s/.build-id/%.2s", +		       debugdir, sbuild_id); + +	if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) +		goto out_free; + +	snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); +	targetname = filename + strlen(debugdir) - 5; +	memcpy(targetname, "../..", 5); + +	if (symlink(targetname, linkname) == 0) +		err = 0; +out_free: +	free(filename); +	free(linkname); +	return err; +} + +static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, +				 const char *name, const char *debugdir, +				 bool is_kallsyms) +{ +	char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + +	build_id__sprintf(build_id, build_id_size, sbuild_id); + +	return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); +} + +int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) +{ +	const size_t size = PATH_MAX; +	char *filename = malloc(size), +	     *linkname = malloc(size); +	int err = -1; + +	if (filename == NULL || linkname == NULL) +		goto out_free; + +	snprintf(linkname, size, "%s/.build-id/%.2s/%s", +		 debugdir, sbuild_id, sbuild_id + 2); + +	if (access(linkname, F_OK)) +		goto out_free; + +	if (readlink(linkname, filename, size) < 0) +		goto out_free; + +	if (unlink(linkname)) +		goto out_free; + +	/* +	 * Since the link is relative, we must make it absolute: +	 */ +	snprintf(linkname, size, "%s/.build-id/%.2s/%s", +		 debugdir, sbuild_id, filename); + +	if (unlink(linkname)) +		goto out_free; + +	err = 0; +out_free: +	free(filename); +	free(linkname); +	return err; +} + +static int dso__cache_build_id(struct dso *self, const char *debugdir) +{ +	bool is_kallsyms = self->kernel && self->long_name[0] != '/'; + +	return build_id_cache__add_b(self->build_id, sizeof(self->build_id), +				     self->long_name, debugdir, is_kallsyms); +} + +static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) +{ +	struct dso *pos; +	int err = 0; + +	dsos__for_each_with_build_id(pos, head) +		if (dso__cache_build_id(pos, debugdir)) +			err = -1; + +	return err; +} + +static int dsos__cache_build_ids(void) +{ +	int err_kernel, err_user; +	char debugdir[PATH_MAX]; + +	snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), +		 DEBUG_CACHE_DIR); + +	if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) +		return -1; + +	err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); +	err_user   = __dsos__cache_build_ids(&dsos__user, debugdir); +	return err_kernel || err_user ? -1 : 0; +} +  static int perf_header__adds_write(struct perf_header *self, int fd)  {  	int nr_sections; @@ -217,7 +373,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd)  	u64 sec_start;  	int idx = 0, err; -	if (dsos__read_build_ids()) +	if (dsos__read_build_ids(true))  		perf_header__set_feat(self, HEADER_BUILD_ID);  	nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); @@ -257,7 +413,9 @@ static int perf_header__adds_write(struct perf_header *self, int fd)  			pr_debug("failed to write buildid table\n");  			goto out_free;  		} -		buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; +		buildid_sec->size = lseek(fd, 0, SEEK_CUR) - +					  buildid_sec->offset; +		dsos__cache_build_ids();  	}  	lseek(fd, sec_start, SEEK_SET); @@ -360,30 +518,43 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit)  	return 0;  } -static void do_read(int fd, void *buf, size_t size) +static int do_read(int fd, void *buf, size_t size)  {  	while (size) {  		int ret = read(fd, buf, size); -		if (ret < 0) -			die("failed to read"); -		if (ret == 0) -			die("failed to read: missing data"); +		if (ret <= 0) +			return -1;  		size -= ret;  		buf += ret;  	} + +	return 0; +} + +static int perf_header__getbuffer64(struct perf_header *self, +				    int fd, void *buf, size_t size) +{ +	if (do_read(fd, buf, size)) +		return -1; + +	if (self->needs_swap) +		mem_bswap_64(buf, size); + +	return 0;  }  int perf_header__process_sections(struct perf_header *self, int fd,  				  int (*process)(struct perf_file_section *self, +						 struct perf_header *ph,  						 int feat, int fd))  {  	struct perf_file_section *feat_sec;  	int nr_sections;  	int sec_size;  	int idx = 0; -	int err = 0, feat = 1; +	int err = -1, feat = 1;  	nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);  	if (!nr_sections) @@ -397,33 +568,45 @@ int perf_header__process_sections(struct perf_header *self, int fd,  	lseek(fd, self->data_offset + self->data_size, SEEK_SET); -	do_read(fd, feat_sec, sec_size); +	if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) +		goto out_free; +	err = 0;  	while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {  		if (perf_header__has_feat(self, feat)) {  			struct perf_file_section *sec = &feat_sec[idx++]; -			err = process(sec, feat, fd); +			err = process(sec, self, feat, fd);  			if (err < 0)  				break;  		}  		++feat;  	} - +out_free:  	free(feat_sec);  	return err; -}; +}  int perf_file_header__read(struct perf_file_header *self,  			   struct perf_header *ph, int fd)  {  	lseek(fd, 0, SEEK_SET); -	do_read(fd, self, sizeof(*self)); -	if (self->magic     != PERF_MAGIC || -	    self->attr_size != sizeof(struct perf_file_attr)) +	if (do_read(fd, self, sizeof(*self)) || +	    memcmp(&self->magic, __perf_magic, sizeof(self->magic)))  		return -1; +	if (self->attr_size != sizeof(struct perf_file_attr)) { +		u64 attr_size = bswap_64(self->attr_size); + +		if (attr_size != sizeof(struct perf_file_attr)) +			return -1; + +		mem_bswap_64(self, offsetof(struct perf_file_header, +					    adds_features)); +		ph->needs_swap = true; +	} +  	if (self->size != sizeof(*self)) {  		/* Support the previous format */  		if (self->size == offsetof(typeof(*self), adds_features)) @@ -433,19 +616,31 @@ int perf_file_header__read(struct perf_file_header *self,  	}  	memcpy(&ph->adds_features, &self->adds_features, -	       sizeof(self->adds_features)); +	       sizeof(ph->adds_features)); +	/* +	 * FIXME: hack that assumes that if we need swap the perf.data file +	 * may be coming from an arch with a different word-size, ergo different +	 * DEFINE_BITMAP format, investigate more later, but for now its mostly +	 * safe to assume that we have a build-id section. Trace files probably +	 * have several other issues in this realm anyway... +	 */ +	if (ph->needs_swap) { +		memset(&ph->adds_features, 0, sizeof(ph->adds_features)); +		perf_header__set_feat(ph, HEADER_BUILD_ID); +	}  	ph->event_offset = self->event_types.offset; -	ph->event_size	 = self->event_types.size; -	ph->data_offset	 = self->data.offset; +	ph->event_size   = self->event_types.size; +	ph->data_offset  = self->data.offset;  	ph->data_size	 = self->data.size;  	return 0;  }  static int perf_file_section__process(struct perf_file_section *self, +				      struct perf_header *ph,  				      int feat, int fd)  { -	if (lseek(fd, self->offset, SEEK_SET) < 0) { +	if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) {  		pr_debug("Failed to lseek to %Ld offset for feature %d, "  			 "continuing...\n", self->offset, feat);  		return 0; @@ -457,7 +652,7 @@ static int perf_file_section__process(struct perf_file_section *self,  		break;  	case HEADER_BUILD_ID: -		if (perf_header__read_build_ids(fd, self->offset, self->size)) +		if (perf_header__read_build_ids(ph, fd, self->offset, self->size))  			pr_debug("Failed to read buildids, continuing...\n");  		break;  	default: @@ -469,7 +664,7 @@ static int perf_file_section__process(struct perf_file_section *self,  int perf_header__read(struct perf_header *self, int fd)  { -	struct perf_file_header f_header; +	struct perf_file_header	f_header;  	struct perf_file_attr	f_attr;  	u64			f_id;  	int nr_attrs, nr_ids, i, j; @@ -486,7 +681,9 @@ int perf_header__read(struct perf_header *self, int fd)  		struct perf_header_attr *attr;  		off_t tmp; -		do_read(fd, &f_attr, sizeof(f_attr)); +		if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) +			goto out_errno; +  		tmp = lseek(fd, 0, SEEK_CUR);  		attr = perf_header_attr__new(&f_attr.attr); @@ -497,7 +694,8 @@ int perf_header__read(struct perf_header *self, int fd)  		lseek(fd, f_attr.ids.offset, SEEK_SET);  		for (j = 0; j < nr_ids; j++) { -			do_read(fd, &f_id, sizeof(f_id)); +			if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) +				goto out_errno;  			if (perf_header_attr__add_id(attr, f_id) < 0) {  				perf_header_attr__delete(attr); @@ -517,7 +715,9 @@ int perf_header__read(struct perf_header *self, int fd)  		events = malloc(f_header.event_types.size);  		if (events == NULL)  			return -ENOMEM; -		do_read(fd, events, f_header.event_types.size); +		if (perf_header__getbuffer64(self, fd, events, +					     f_header.event_types.size)) +			goto out_errno;  		event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);  	} @@ -527,6 +727,8 @@ int perf_header__read(struct perf_header *self, int fd)  	self->frozen = 1;  	return 0; +out_errno: +	return -errno;  }  u64 perf_header__sample_type(struct perf_header *header) diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index d118d05d3ab..82a6af72d4c 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -5,6 +5,7 @@  #include <sys/types.h>  #include <stdbool.h>  #include "types.h" +#include "event.h"  #include <linux/bitmap.h> @@ -52,6 +53,7 @@ struct perf_header {  	u64			data_size;  	u64			event_offset;  	u64			event_size; +	bool			needs_swap;  	DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);  }; @@ -64,7 +66,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit);  int perf_header__add_attr(struct perf_header *self,  			  struct perf_header_attr *attr); -void perf_header__push_event(u64 id, const char *name); +int perf_header__push_event(u64 id, const char *name);  char *perf_header__find_event(u64 id);  struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); @@ -80,6 +82,11 @@ bool perf_header__has_feat(const struct perf_header *self, int feat);  int perf_header__process_sections(struct perf_header *self, int fd,  				  int (*process)(struct perf_file_section *self, +						 struct perf_header *ph,  						 int feat, int fd)); +int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, +			  const char *name, bool is_kallsyms); +int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); +  #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/include/linux/hash.h b/tools/perf/util/include/linux/hash.h new file mode 100644 index 00000000000..201f5739799 --- /dev/null +++ b/tools/perf/util/include/linux/hash.h @@ -0,0 +1,5 @@ +#include "../../../../include/linux/hash.h" + +#ifndef PERF_HASH_H +#define PERF_HASH_H +#endif diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 21c0274c02f..f2611655ab5 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h @@ -101,5 +101,6 @@ simple_strtoul(const char *nptr, char **endptr, int base)  	eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)  #define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)  #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)  #endif diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index c4d55a0da2e..e509cd59c67 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -5,6 +5,11 @@  #include <stdio.h>  #include "debug.h" +const char *map_type__name[MAP__NR_TYPES] = { +	[MAP__FUNCTION] = "Functions", +	[MAP__VARIABLE] = "Variables", +}; +  static inline int is_anon_memory(const char *filename)  {  	return strcmp(filename, "//anon") == 0; @@ -68,8 +73,13 @@ struct map *map__new(struct mmap_event *event, enum map_type type,  		map__init(self, type, event->start, event->start + event->len,  			  event->pgoff, dso); -		if (self->dso == vdso || anon) +		if (anon) { +set_identity:  			self->map_ip = self->unmap_ip = identity__map_ip; +		} else if (strcmp(filename, "[vdso]") == 0) { +			dso__set_loaded(dso, self->type); +			goto set_identity; +		}  	}  	return self;  out_delete: @@ -104,8 +114,7 @@ void map__fixup_end(struct map *self)  #define DSO__DELETED "(deleted)" -int map__load(struct map *self, struct perf_session *session, -	      symbol_filter_t filter) +int map__load(struct map *self, symbol_filter_t filter)  {  	const char *name = self->dso->long_name;  	int nr; @@ -113,7 +122,7 @@ int map__load(struct map *self, struct perf_session *session,  	if (dso__loaded(self->dso, self->type))  		return 0; -	nr = dso__load(self->dso, self, session, filter); +	nr = dso__load(self->dso, self, filter);  	if (nr < 0) {  		if (self->dso->has_build_id) {  			char sbuild_id[BUILD_ID_SIZE * 2 + 1]; @@ -144,24 +153,29 @@ int map__load(struct map *self, struct perf_session *session,  		return -1;  	} +	/* +	 * Only applies to the kernel, as its symtabs aren't relative like the +	 * module ones. +	 */ +	if (self->dso->kernel) +		map__reloc_vmlinux(self);  	return 0;  } -struct symbol *map__find_symbol(struct map *self, struct perf_session *session, -				u64 addr, symbol_filter_t filter) +struct symbol *map__find_symbol(struct map *self, u64 addr, +				symbol_filter_t filter)  { -	if (map__load(self, session, filter) < 0) +	if (map__load(self, filter) < 0)  		return NULL;  	return dso__find_symbol(self->dso, self->type, addr);  }  struct symbol *map__find_symbol_by_name(struct map *self, const char *name, -					struct perf_session *session,  					symbol_filter_t filter)  { -	if (map__load(self, session, filter) < 0) +	if (map__load(self, filter) < 0)  		return NULL;  	if (!dso__sorted_by_name(self->dso, self->type)) @@ -201,3 +215,23 @@ size_t map__fprintf(struct map *self, FILE *fp)  	return fprintf(fp, " %Lx-%Lx %Lx %s\n",  		       self->start, self->end, self->pgoff, self->dso->name);  } + +/* + * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. + * map->dso->adjust_symbols==1 for ET_EXEC-like cases. + */ +u64 map__rip_2objdump(struct map *map, u64 rip) +{ +	u64 addr = map->dso->adjust_symbols ? +			map->unmap_ip(map, rip) :	/* RIP -> IP */ +			rip; +	return addr; +} + +u64 map__objdump_2ip(struct map *map, u64 addr) +{ +	u64 ip = map->dso->adjust_symbols ? +			addr : +			map->unmap_ip(map, addr);	/* RIP -> IP */ +	return ip; +} diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h new file mode 100644 index 00000000000..b756368076c --- /dev/null +++ b/tools/perf/util/map.h @@ -0,0 +1,94 @@ +#ifndef __PERF_MAP_H +#define __PERF_MAP_H + +#include <linux/compiler.h> +#include <linux/list.h> +#include <linux/rbtree.h> +#include <linux/types.h> + +enum map_type { +	MAP__FUNCTION = 0, +	MAP__VARIABLE, +}; + +#define MAP__NR_TYPES (MAP__VARIABLE + 1) + +extern const char *map_type__name[MAP__NR_TYPES]; + +struct dso; +struct ref_reloc_sym; +struct map_groups; + +struct map { +	union { +		struct rb_node	rb_node; +		struct list_head node; +	}; +	u64			start; +	u64			end; +	enum map_type		type; +	u64			pgoff; + +	/* ip -> dso rip */ +	u64			(*map_ip)(struct map *, u64); +	/* dso rip -> ip */ +	u64			(*unmap_ip)(struct map *, u64); + +	struct dso		*dso; +}; + +struct kmap { +	struct ref_reloc_sym	*ref_reloc_sym; +	struct map_groups	*kmaps; +}; + +static inline struct kmap *map__kmap(struct map *self) +{ +	return (struct kmap *)(self + 1); +} + +static inline u64 map__map_ip(struct map *map, u64 ip) +{ +	return ip - map->start + map->pgoff; +} + +static inline u64 map__unmap_ip(struct map *map, u64 ip) +{ +	return ip + map->start - map->pgoff; +} + +static inline u64 identity__map_ip(struct map *map __used, u64 ip) +{ +	return ip; +} + + +/* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ +u64 map__rip_2objdump(struct map *map, u64 rip); +u64 map__objdump_2ip(struct map *map, u64 addr); + +struct symbol; +struct mmap_event; + +typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); + +void map__init(struct map *self, enum map_type type, +	       u64 start, u64 end, u64 pgoff, struct dso *dso); +struct map *map__new(struct mmap_event *event, enum map_type, +		     char *cwd, int cwdlen); +void map__delete(struct map *self); +struct map *map__clone(struct map *self); +int map__overlap(struct map *l, struct map *r); +size_t map__fprintf(struct map *self, FILE *fp); + +int map__load(struct map *self, symbol_filter_t filter); +struct symbol *map__find_symbol(struct map *self, +				u64 addr, symbol_filter_t filter); +struct symbol *map__find_symbol_by_name(struct map *self, const char *name, +					symbol_filter_t filter); +void map__fixup_start(struct map *self); +void map__fixup_end(struct map *self); + +void map__reloc_vmlinux(struct map *self); + +#endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index e5bc0fb016b..05d0c5c2030 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -450,7 +450,8 @@ parse_single_tracepoint_event(char *sys_name,  /* sys + ':' + event + ':' + flags*/  #define MAX_EVOPT_LEN	(MAX_EVENT_LENGTH * 2 + 2 + 128)  static enum event_result -parse_subsystem_tracepoint_event(char *sys_name, char *flags) +parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, +				char *flags)  {  	char evt_path[MAXPATHLEN];  	struct dirent *evt_ent; @@ -474,6 +475,9 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags)  		    || !strcmp(evt_ent->d_name, "filter"))  			continue; +		if (!strglobmatch(evt_ent->d_name, evt_exp)) +			continue; +  		len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,  			       evt_ent->d_name, flags ? ":" : "",  			       flags ?: ""); @@ -522,9 +526,10 @@ static enum event_result parse_tracepoint_event(const char **strp,  	if (evt_length >= MAX_EVENT_LENGTH)  		return EVT_FAILED; -	if (!strcmp(evt_name, "*")) { +	if (strpbrk(evt_name, "*?")) {  		*strp = evt_name + evt_length; -		return parse_subsystem_tracepoint_event(sys_name, flags); +		return parse_multiple_tracepoint_event(sys_name, evt_name, +						       flags);  	} else  		return parse_single_tracepoint_event(sys_name, evt_name,  						     evt_length, flags, @@ -753,11 +758,11 @@ modifier:  	return ret;  } -static void store_event_type(const char *orgname) +static int store_event_type(const char *orgname)  {  	char filename[PATH_MAX], *c;  	FILE *file; -	int id; +	int id, n;  	sprintf(filename, "%s/", debugfs_path);  	strncat(filename, orgname, strlen(orgname)); @@ -769,11 +774,14 @@ static void store_event_type(const char *orgname)  	file = fopen(filename, "r");  	if (!file) -		return; -	if (fscanf(file, "%i", &id) < 1) -		die("cannot store event ID"); +		return 0; +	n = fscanf(file, "%i", &id);  	fclose(file); -	perf_header__push_event(id, orgname); +	if (n < 1) { +		pr_err("cannot store event ID\n"); +		return -EINVAL; +	} +	return perf_header__push_event(id, orgname);  }  int parse_events(const struct option *opt __used, const char *str, int unset __used) @@ -782,7 +790,8 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u  	enum event_result ret;  	if (strchr(str, ':')) -		store_event_type(str); +		if (store_event_type(str) < 0) +			return -1;  	for (;;) {  		if (nr_counters == MAX_COUNTERS) @@ -835,11 +844,12 @@ int parse_filter(const struct option *opt __used, const char *str,  }  static const char * const event_type_descriptors[] = { -	"",  	"Hardware event",  	"Software event",  	"Tracepoint event",  	"Hardware cache event", +	"Raw hardware event descriptor", +	"Hardware breakpoint",  };  /* @@ -872,7 +882,7 @@ static void print_tracepoint_events(void)  			snprintf(evt_path, MAXPATHLEN, "%s:%s",  				 sys_dirent.d_name, evt_dirent.d_name);  			printf("  %-42s [%s]\n", evt_path, -				event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); +				event_type_descriptors[PERF_TYPE_TRACEPOINT]);  		}  		closedir(evt_dir);  	} @@ -892,9 +902,7 @@ void print_events(void)  	printf("List of pre-defined events (to be used in -e):\n");  	for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { -		type = syms->type + 1; -		if (type >= ARRAY_SIZE(event_type_descriptors)) -			type = 0; +		type = syms->type;  		if (type != prev_type)  			printf("\n"); @@ -919,17 +927,19 @@ void print_events(void)  			for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {  				printf("  %-42s [%s]\n",  					event_cache_name(type, op, i), -					event_type_descriptors[4]); +					event_type_descriptors[PERF_TYPE_HW_CACHE]);  			}  		}  	}  	printf("\n"); -	printf("  %-42s [raw hardware event descriptor]\n", -		"rNNN"); +	printf("  %-42s [%s]\n", +		"rNNN", event_type_descriptors[PERF_TYPE_RAW]);  	printf("\n"); -	printf("  %-42s [hardware breakpoint]\n", "mem:<addr>[:access]"); +	printf("  %-42s [%s]\n", +			"mem:<addr>[:access]", +			event_type_descriptors[PERF_TYPE_BREAKPOINT]);  	printf("\n");  	print_tracepoint_events(); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 29465d44004..8f056884969 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -37,6 +37,8 @@  #include "string.h"  #include "strlist.h"  #include "debug.h" +#include "cache.h" +#include "color.h"  #include "parse-events.h"  /* For debugfs_path */  #include "probe-event.h" @@ -62,6 +64,42 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)  	return ret;  } +void parse_line_range_desc(const char *arg, struct line_range *lr) +{ +	const char *ptr; +	char *tmp; +	/* +	 * <Syntax> +	 * SRC:SLN[+NUM|-ELN] +	 * FUNC[:SLN[+NUM|-ELN]] +	 */ +	ptr = strchr(arg, ':'); +	if (ptr) { +		lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); +		if (*tmp == '+') +			lr->end = lr->start + (unsigned int)strtoul(tmp + 1, +								    &tmp, 0); +		else if (*tmp == '-') +			lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); +		else +			lr->end = 0; +		pr_debug("Line range is %u to %u\n", lr->start, lr->end); +		if (lr->end && lr->start > lr->end) +			semantic_error("Start line must be smaller" +				       " than end line."); +		if (*tmp != '\0') +			semantic_error("Tailing with invalid character '%d'.", +				       *tmp); +		tmp = strndup(arg, (ptr - arg)); +	} else +		tmp = strdup(arg); + +	if (strchr(tmp, '.')) +		lr->file = tmp; +	else +		lr->function = tmp; +} +  /* Check the name is good for event/group */  static bool check_event_name(const char *name)  { @@ -272,6 +310,7 @@ int synthesize_perf_probe_point(struct probe_point *pp)  	int ret;  	pp->probes[0] = buf = zalloc(MAX_CMDLEN); +	pp->found = 1;  	if (!buf)  		die("Failed to allocate memory by zalloc.");  	if (pp->offset) { @@ -294,6 +333,7 @@ int synthesize_perf_probe_point(struct probe_point *pp)  error:  		free(pp->probes[0]);  		pp->probes[0] = NULL; +		pp->found = 0;  	}  	return ret;  } @@ -368,7 +408,7 @@ static int open_kprobe_events(int flags, int mode)  	if (ret < 0) {  		if (errno == ENOENT)  			die("kprobe_events file does not exist -" -			    " please rebuild with CONFIG_KPROBE_TRACER."); +			    " please rebuild with CONFIG_KPROBE_EVENT.");  		else  			die("Could not open kprobe_events file: %s",  			    strerror(errno)); @@ -455,6 +495,9 @@ void show_perf_probe_events(void)  	struct strlist *rawlist;  	struct str_node *ent; +	setup_pager(); + +	memset(&pp, 0, sizeof(pp));  	fd = open_kprobe_events(O_RDONLY, 0);  	rawlist = get_trace_kprobe_event_rawlist(fd);  	close(fd); @@ -675,3 +718,66 @@ void del_trace_kprobe_events(struct strlist *dellist)  	close(fd);  } +#define LINEBUF_SIZE 256 + +static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) +{ +	char buf[LINEBUF_SIZE]; +	const char *color = PERF_COLOR_BLUE; + +	if (fgets(buf, LINEBUF_SIZE, fp) == NULL) +		goto error; +	if (!skip) { +		if (show_num) +			fprintf(stdout, "%7u  %s", l, buf); +		else +			color_fprintf(stdout, color, "         %s", buf); +	} + +	while (strlen(buf) == LINEBUF_SIZE - 1 && +	       buf[LINEBUF_SIZE - 2] != '\n') { +		if (fgets(buf, LINEBUF_SIZE, fp) == NULL) +			goto error; +		if (!skip) { +			if (show_num) +				fprintf(stdout, "%s", buf); +			else +				color_fprintf(stdout, color, "%s", buf); +		} +	} +	return; +error: +	if (feof(fp)) +		die("Source file is shorter than expected."); +	else +		die("File read error: %s", strerror(errno)); +} + +void show_line_range(struct line_range *lr) +{ +	unsigned int l = 1; +	struct line_node *ln; +	FILE *fp; + +	setup_pager(); + +	if (lr->function) +		fprintf(stdout, "<%s:%d>\n", lr->function, +			lr->start - lr->offset); +	else +		fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); + +	fp = fopen(lr->path, "r"); +	if (fp == NULL) +		die("Failed to open %s: %s", lr->path, strerror(errno)); +	/* Skip to starting line number */ +	while (l < lr->start) +		show_one_line(fp, l++, true, false); + +	list_for_each_entry(ln, &lr->line_list, list) { +		while (ln->line > l) +			show_one_line(fp, (l++) - lr->offset, false, false); +		show_one_line(fp, (l++) - lr->offset, false, true); +	} +	fclose(fp); +} diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 7f1d499118c..711287d4bae 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -5,6 +5,7 @@  #include "probe-finder.h"  #include "strlist.h" +extern void parse_line_range_desc(const char *arg, struct line_range *lr);  extern void parse_perf_probe_event(const char *str, struct probe_point *pp,  				   bool *need_dwarf);  extern int synthesize_perf_probe_point(struct probe_point *pp); @@ -15,6 +16,7 @@ extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,  				    bool force_add);  extern void del_trace_kprobe_events(struct strlist *dellist);  extern void show_perf_probe_events(void); +extern void show_line_range(struct line_range *lr);  /* Maximum index number of event-name postfix */  #define MAX_EVENT_INDEX	1024 diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 4b852c0d16a..1b2124d12f6 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -140,6 +140,31 @@ static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname)  	return found;  } +static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf) +{ +	Dwarf_Signed cnt, i; +	char **srcs; +	int ret = 0; + +	if (!buf || !fno) +		return -EINVAL; + +	ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); +	if (ret == DW_DLV_OK) { +		if ((Dwarf_Unsigned)cnt > fno - 1) { +			*buf = strdup(srcs[fno - 1]); +			ret = 0; +			pr_debug("found filename: %s\n", *buf); +		} else +			ret = -ENOENT; +		for (i = 0; i < cnt; i++) +			dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); +		dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); +	} else +		ret = -EINVAL; +	return ret; +} +  /* Compare diename and tname */  static int die_compare_name(Dwarf_Die dw_die, const char *tname)  { @@ -402,11 +427,11 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)  	} else if (op == DW_OP_regx) {  		regn = loc->lr_number;  	} else -		die("Dwarf_OP %d is not supported.\n", op); +		die("Dwarf_OP %d is not supported.", op);  	regs = get_arch_regstr(regn);  	if (!regs) -		die("%lld exceeds max register number.\n", regn); +		die("%lld exceeds max register number.", regn);  	if (deref)  		ret = snprintf(pf->buf, pf->len, @@ -438,7 +463,7 @@ static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)  	return ;  error:  	die("Failed to find the location of %s at this address.\n" -	    " Perhaps, it has been optimized out.\n", pf->var); +	    " Perhaps, it has been optimized out.", pf->var);  }  static int variable_callback(struct die_link *dlink, void *data) @@ -476,7 +501,7 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)  	/* Search child die for local variables and parameters. */  	ret = search_die_from_children(sp_die, variable_callback, pf);  	if (!ret) -		die("Failed to find '%s' in this function.\n", pf->var); +		die("Failed to find '%s' in this function.", pf->var);  }  /* Get a frame base on the address */ @@ -567,7 +592,7 @@ static int probeaddr_callback(struct die_link *dlink, void *data)  }  /* Find probe point from its line number */ -static void find_by_line(struct probe_finder *pf) +static void find_probe_point_by_line(struct probe_finder *pf)  {  	Dwarf_Signed cnt, i, clm;  	Dwarf_Line *lines; @@ -602,7 +627,7 @@ static void find_by_line(struct probe_finder *pf)  		ret = search_die_from_children(pf->cu_die,  					       probeaddr_callback, pf);  		if (ret == 0) -			die("Probe point is not found in subprograms.\n"); +			die("Probe point is not found in subprograms.");  		/* Continuing, because target line might be inlined. */  	}  	dwarf_srclines_dealloc(__dw_debug, lines, cnt); @@ -626,7 +651,7 @@ static int probefunc_callback(struct die_link *dlink, void *data)  				pf->fno = die_get_decl_file(dlink->die);  				pf->lno = die_get_decl_line(dlink->die)  					 + pp->line; -				find_by_line(pf); +				find_probe_point_by_line(pf);  				return 1;  			}  			if (die_inlined_subprogram(dlink->die)) { @@ -661,7 +686,7 @@ static int probefunc_callback(struct die_link *dlink, void *data)  				    !die_inlined_subprogram(lk->die))  					goto found;  			} -			die("Failed to find real subprogram.\n"); +			die("Failed to find real subprogram.");  found:  			/* Get offset from subprogram */  			ret = die_within_subprogram(lk->die, pf->addr, &offs); @@ -673,7 +698,7 @@ found:  	return 0;  } -static void find_by_func(struct probe_finder *pf) +static void find_probe_point_by_func(struct probe_finder *pf)  {  	search_die_from_children(pf->cu_die, probefunc_callback, pf);  } @@ -714,10 +739,10 @@ int find_probepoint(int fd, struct probe_point *pp)  			if (ret == DW_DLV_NO_ENTRY)  				pf.cu_base = 0;  			if (pp->function) -				find_by_func(&pf); +				find_probe_point_by_func(&pf);  			else {  				pf.lno = pp->line; -				find_by_line(&pf); +				find_probe_point_by_line(&pf);  			}  		}  		dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); @@ -728,3 +753,159 @@ int find_probepoint(int fd, struct probe_point *pp)  	return pp->found;  } + +static void line_range_add_line(struct line_range *lr, unsigned int line) +{ +	struct line_node *ln; +	struct list_head *p; + +	/* Reverse search, because new line will be the last one */ +	list_for_each_entry_reverse(ln, &lr->line_list, list) { +		if (ln->line < line) { +			p = &ln->list; +			goto found; +		} else if (ln->line == line)	/* Already exist */ +			return ; +	} +	/* List is empty, or the smallest entry */ +	p = &lr->line_list; +found: +	pr_debug("Debug: add a line %u\n", line); +	ln = zalloc(sizeof(struct line_node)); +	DIE_IF(ln == NULL); +	ln->line = line; +	INIT_LIST_HEAD(&ln->list); +	list_add(&ln->list, p); +} + +/* Find line range from its line number */ +static void find_line_range_by_line(struct line_finder *lf) +{ +	Dwarf_Signed cnt, i; +	Dwarf_Line *lines; +	Dwarf_Unsigned lineno = 0; +	Dwarf_Unsigned fno; +	Dwarf_Addr addr; +	int ret; + +	ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error); +	DIE_IF(ret != DW_DLV_OK); + +	for (i = 0; i < cnt; i++) { +		ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); +		DIE_IF(ret != DW_DLV_OK); +		if (fno != lf->fno) +			continue; + +		ret = dwarf_lineno(lines[i], &lineno, &__dw_error); +		DIE_IF(ret != DW_DLV_OK); +		if (lf->lno_s > lineno || lf->lno_e < lineno) +			continue; + +		/* Filter line in the function address range */ +		if (lf->addr_s && lf->addr_e) { +			ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); +			DIE_IF(ret != DW_DLV_OK); +			if (lf->addr_s > addr || lf->addr_e <= addr) +				continue; +		} +		line_range_add_line(lf->lr, (unsigned int)lineno); +	} +	dwarf_srclines_dealloc(__dw_debug, lines, cnt); +	if (!list_empty(&lf->lr->line_list)) +		lf->found = 1; +} + +/* Search function from function name */ +static int linefunc_callback(struct die_link *dlink, void *data) +{ +	struct line_finder *lf = (struct line_finder *)data; +	struct line_range *lr = lf->lr; +	Dwarf_Half tag; +	int ret; + +	ret = dwarf_tag(dlink->die, &tag, &__dw_error); +	DIE_IF(ret == DW_DLV_ERROR); +	if (tag == DW_TAG_subprogram && +	    die_compare_name(dlink->die, lr->function) == 0) { +		/* Get the address range of this function */ +		ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error); +		if (ret == DW_DLV_OK) +			ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error); +		DIE_IF(ret == DW_DLV_ERROR); +		if (ret == DW_DLV_NO_ENTRY) { +			lf->addr_s = 0; +			lf->addr_e = 0; +		} + +		lf->fno = die_get_decl_file(dlink->die); +		lr->offset = die_get_decl_line(dlink->die);; +		lf->lno_s = lr->offset + lr->start; +		if (!lr->end) +			lf->lno_e = (Dwarf_Unsigned)-1; +		else +			lf->lno_e = lr->offset + lr->end; +		lr->start = lf->lno_s; +		lr->end = lf->lno_e; +		find_line_range_by_line(lf); +		/* If we find a target function, this should be end. */ +		lf->found = 1; +		return 1; +	} +	return 0; +} + +static void find_line_range_by_func(struct line_finder *lf) +{ +	search_die_from_children(lf->cu_die, linefunc_callback, lf); +} + +int find_line_range(int fd, struct line_range *lr) +{ +	Dwarf_Half addr_size = 0; +	Dwarf_Unsigned next_cuh = 0; +	int ret; +	struct line_finder lf = {.lr = lr}; + +	ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); +	if (ret != DW_DLV_OK) +		return -ENOENT; + +	while (!lf.found) { +		/* Search CU (Compilation Unit) */ +		ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, +			&addr_size, &next_cuh, &__dw_error); +		DIE_IF(ret == DW_DLV_ERROR); +		if (ret == DW_DLV_NO_ENTRY) +			break; + +		/* Get the DIE(Debugging Information Entry) of this CU */ +		ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error); +		DIE_IF(ret != DW_DLV_OK); + +		/* Check if target file is included. */ +		if (lr->file) +			lf.fno = cu_find_fileno(lf.cu_die, lr->file); + +		if (!lr->file || lf.fno) { +			if (lr->function) +				find_line_range_by_func(&lf); +			else { +				lf.lno_s = lr->start; +				if (!lr->end) +					lf.lno_e = (Dwarf_Unsigned)-1; +				else +					lf.lno_e = lr->end; +				find_line_range_by_line(&lf); +			} +			/* Get the real file path */ +			if (lf.found) +				cu_get_filename(lf.cu_die, lf.fno, &lr->path); +		} +		dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE); +	} +	ret = dwarf_finish(__dw_debug, &__dw_error); +	DIE_IF(ret != DW_DLV_OK); +	return lf.found; +} + diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index a4086aaddb7..972b386116f 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -1,6 +1,8 @@  #ifndef _PROBE_FINDER_H  #define _PROBE_FINDER_H +#include "util.h" +  #define MAX_PATH_LEN		 256  #define MAX_PROBE_BUFFER	1024  #define MAX_PROBES		 128 @@ -32,8 +34,26 @@ struct probe_point {  	char			*probes[MAX_PROBES];	/* Output buffers (will be allocated)*/  }; +/* Line number container */ +struct line_node { +	struct list_head	list; +	unsigned int		line; +}; + +/* Line range */ +struct line_range { +	char			*file;			/* File name */ +	char			*function;		/* Function name */ +	unsigned int		start;			/* Start line number */ +	unsigned int		end;			/* End line number */ +	unsigned int		offset;			/* Start line offset */ +	char			*path;			/* Real path name */ +	struct list_head	line_list;		/* Visible lines */ +}; +  #ifndef NO_LIBDWARF  extern int find_probepoint(int fd, struct probe_point *pp); +extern int find_line_range(int fd, struct line_range *lr);  /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */  #ifndef _MIPS_SZLONG @@ -60,6 +80,19 @@ struct probe_finder {  	char			*buf;			/* Current output buffer */  	int			len;			/* Length of output buffer */  }; + +struct line_finder { +	struct line_range	*lr;			/* Target line range */ + +	Dwarf_Unsigned		fno;			/* File number */ +	Dwarf_Unsigned		lno_s;			/* Start line number */ +	Dwarf_Unsigned		lno_e;			/* End line number */ +	Dwarf_Addr		addr_s;			/* Start address */ +	Dwarf_Addr		addr_e;			/* End address */ +	Dwarf_Die		cu_die;			/* Current CU */ +	int			found; +}; +  #endif /* NO_LIBDWARF */  #endif /*_PROBE_FINDER_H */ diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 6d6d76b8a21..5376378e0cf 100644 --- a/tools/perf/util/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -25,10 +25,16 @@  #include <ctype.h>  #include <errno.h> -#include "../perf.h" -#include "util.h" -#include "trace-event.h" -#include "trace-event-perl.h" +#include "../../perf.h" +#include "../util.h" +#include "../trace-event.h" + +#include <EXTERN.h> +#include <perl.h> + +void boot_Perf__Trace__Context(pTHX_ CV *cv); +void boot_DynaLoader(pTHX_ CV *cv); +typedef PerlInterpreter * INTERP;  void xs_init(pTHX); @@ -49,7 +55,7 @@ INTERP my_perl;  struct event *events[FTRACE_MAX_EVENT]; -static struct scripting_context *scripting_context; +extern struct scripting_context *scripting_context;  static char *cur_field_name;  static int zero_flag_atom; @@ -239,33 +245,6 @@ static inline struct event *find_cache_event(int type)  	return event;  } -int common_pc(struct scripting_context *context) -{ -	int pc; - -	pc = parse_common_pc(context->event_data); - -	return pc; -} - -int common_flags(struct scripting_context *context) -{ -	int flags; - -	flags = parse_common_flags(context->event_data); - -	return flags; -} - -int common_lock_depth(struct scripting_context *context) -{ -	int lock_depth; - -	lock_depth = parse_common_lock_depth(context->event_data); - -	return lock_depth; -} -  static void perl_process_event(int cpu, void *data,  			       int size __unused,  			       unsigned long long nsecs, char *comm) @@ -587,75 +566,3 @@ struct scripting_ops perl_scripting_ops = {  	.process_event = perl_process_event,  	.generate_script = perl_generate_script,  }; - -static void print_unsupported_msg(void) -{ -	fprintf(stderr, "Perl scripting not supported." -		"  Install libperl and rebuild perf to enable it.\n" -		"For example:\n  # apt-get install libperl-dev (ubuntu)" -		"\n  # yum install perl-ExtUtils-Embed (Fedora)" -		"\n  etc.\n"); -} - -static int perl_start_script_unsupported(const char *script __unused, -					 int argc __unused, -					 const char **argv __unused) -{ -	print_unsupported_msg(); - -	return -1; -} - -static int perl_stop_script_unsupported(void) -{ -	return 0; -} - -static void perl_process_event_unsupported(int cpu __unused, -					   void *data __unused, -					   int size __unused, -					   unsigned long long nsecs __unused, -					   char *comm __unused) -{ -} - -static int perl_generate_script_unsupported(const char *outfile __unused) -{ -	print_unsupported_msg(); - -	return -1; -} - -struct scripting_ops perl_scripting_unsupported_ops = { -	.name = "Perl", -	.start_script = perl_start_script_unsupported, -	.stop_script = perl_stop_script_unsupported, -	.process_event = perl_process_event_unsupported, -	.generate_script = perl_generate_script_unsupported, -}; - -static void register_perl_scripting(struct scripting_ops *scripting_ops) -{ -	int err; -	err = script_spec_register("Perl", scripting_ops); -	if (err) -		die("error registering Perl script extension"); - -	err = script_spec_register("pl", scripting_ops); -	if (err) -		die("error registering pl script extension"); - -	scripting_context = malloc(sizeof(struct scripting_context)); -} - -#ifdef NO_LIBPERL -void setup_perl_scripting(void) -{ -	register_perl_scripting(&perl_scripting_unsupported_ops); -} -#else -void setup_perl_scripting(void) -{ -	register_perl_scripting(&perl_scripting_ops); -} -#endif diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c new file mode 100644 index 00000000000..33a414bbba3 --- /dev/null +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -0,0 +1,573 @@ +/* + * trace-event-python.  Feed trace events to an embedded Python interpreter. + * + * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include <Python.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "../../perf.h" +#include "../util.h" +#include "../trace-event.h" + +PyMODINIT_FUNC initperf_trace_context(void); + +#define FTRACE_MAX_EVENT				\ +	((1 << (sizeof(unsigned short) * 8)) - 1) + +struct event *events[FTRACE_MAX_EVENT]; + +#define MAX_FIELDS	64 +#define N_COMMON_FIELDS	7 + +extern struct scripting_context *scripting_context; + +static char *cur_field_name; +static int zero_flag_atom; + +static PyObject *main_module, *main_dict; + +static void handler_call_die(const char *handler_name) +{ +	PyErr_Print(); +	Py_FatalError("problem in Python trace event handler"); +} + +static void define_value(enum print_arg_type field_type, +			 const char *ev_name, +			 const char *field_name, +			 const char *field_value, +			 const char *field_str) +{ +	const char *handler_name = "define_flag_value"; +	PyObject *handler, *t, *retval; +	unsigned long long value; +	unsigned n = 0; + +	if (field_type == PRINT_SYMBOL) +		handler_name = "define_symbolic_value"; + +	t = PyTuple_New(4); +	if (!t) +		Py_FatalError("couldn't create Python tuple"); + +	value = eval_flag(field_value); + +	PyTuple_SetItem(t, n++, PyString_FromString(ev_name)); +	PyTuple_SetItem(t, n++, PyString_FromString(field_name)); +	PyTuple_SetItem(t, n++, PyInt_FromLong(value)); +	PyTuple_SetItem(t, n++, PyString_FromString(field_str)); + +	handler = PyDict_GetItemString(main_dict, handler_name); +	if (handler && PyCallable_Check(handler)) { +		retval = PyObject_CallObject(handler, t); +		if (retval == NULL) +			handler_call_die(handler_name); +	} + +	Py_DECREF(t); +} + +static void define_values(enum print_arg_type field_type, +			  struct print_flag_sym *field, +			  const char *ev_name, +			  const char *field_name) +{ +	define_value(field_type, ev_name, field_name, field->value, +		     field->str); + +	if (field->next) +		define_values(field_type, field->next, ev_name, field_name); +} + +static void define_field(enum print_arg_type field_type, +			 const char *ev_name, +			 const char *field_name, +			 const char *delim) +{ +	const char *handler_name = "define_flag_field"; +	PyObject *handler, *t, *retval; +	unsigned n = 0; + +	if (field_type == PRINT_SYMBOL) +		handler_name = "define_symbolic_field"; + +	if (field_type == PRINT_FLAGS) +		t = PyTuple_New(3); +	else +		t = PyTuple_New(2); +	if (!t) +		Py_FatalError("couldn't create Python tuple"); + +	PyTuple_SetItem(t, n++, PyString_FromString(ev_name)); +	PyTuple_SetItem(t, n++, PyString_FromString(field_name)); +	if (field_type == PRINT_FLAGS) +		PyTuple_SetItem(t, n++, PyString_FromString(delim)); + +	handler = PyDict_GetItemString(main_dict, handler_name); +	if (handler && PyCallable_Check(handler)) { +		retval = PyObject_CallObject(handler, t); +		if (retval == NULL) +			handler_call_die(handler_name); +	} + +	Py_DECREF(t); +} + +static void define_event_symbols(struct event *event, +				 const char *ev_name, +				 struct print_arg *args) +{ +	switch (args->type) { +	case PRINT_NULL: +		break; +	case PRINT_ATOM: +		define_value(PRINT_FLAGS, ev_name, cur_field_name, "0", +			     args->atom.atom); +		zero_flag_atom = 0; +		break; +	case PRINT_FIELD: +		if (cur_field_name) +			free(cur_field_name); +		cur_field_name = strdup(args->field.name); +		break; +	case PRINT_FLAGS: +		define_event_symbols(event, ev_name, args->flags.field); +		define_field(PRINT_FLAGS, ev_name, cur_field_name, +			     args->flags.delim); +		define_values(PRINT_FLAGS, args->flags.flags, ev_name, +			      cur_field_name); +		break; +	case PRINT_SYMBOL: +		define_event_symbols(event, ev_name, args->symbol.field); +		define_field(PRINT_SYMBOL, ev_name, cur_field_name, NULL); +		define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name, +			      cur_field_name); +		break; +	case PRINT_STRING: +		break; +	case PRINT_TYPE: +		define_event_symbols(event, ev_name, args->typecast.item); +		break; +	case PRINT_OP: +		if (strcmp(args->op.op, ":") == 0) +			zero_flag_atom = 1; +		define_event_symbols(event, ev_name, args->op.left); +		define_event_symbols(event, ev_name, args->op.right); +		break; +	default: +		/* we should warn... */ +		return; +	} + +	if (args->next) +		define_event_symbols(event, ev_name, args->next); +} + +static inline struct event *find_cache_event(int type) +{ +	static char ev_name[256]; +	struct event *event; + +	if (events[type]) +		return events[type]; + +	events[type] = event = trace_find_event(type); +	if (!event) +		return NULL; + +	sprintf(ev_name, "%s__%s", event->system, event->name); + +	define_event_symbols(event, ev_name, event->print_fmt.args); + +	return event; +} + +static void python_process_event(int cpu, void *data, +				 int size __unused, +				 unsigned long long nsecs, char *comm) +{ +	PyObject *handler, *retval, *context, *t; +	static char handler_name[256]; +	struct format_field *field; +	unsigned long long val; +	unsigned long s, ns; +	struct event *event; +	unsigned n = 0; +	int type; +	int pid; + +	t = PyTuple_New(MAX_FIELDS); +	if (!t) +		Py_FatalError("couldn't create Python tuple"); + +	type = trace_parse_common_type(data); + +	event = find_cache_event(type); +	if (!event) +		die("ug! no event found for type %d", type); + +	pid = trace_parse_common_pid(data); + +	sprintf(handler_name, "%s__%s", event->system, event->name); + +	s = nsecs / NSECS_PER_SEC; +	ns = nsecs - s * NSECS_PER_SEC; + +	scripting_context->event_data = data; + +	context = PyCObject_FromVoidPtr(scripting_context, NULL); + +	PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); +	PyTuple_SetItem(t, n++, +			PyCObject_FromVoidPtr(scripting_context, NULL)); +	PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); +	PyTuple_SetItem(t, n++, PyInt_FromLong(s)); +	PyTuple_SetItem(t, n++, PyInt_FromLong(ns)); +	PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); +	PyTuple_SetItem(t, n++, PyString_FromString(comm)); + +	for (field = event->format.fields; field; field = field->next) { +		if (field->flags & FIELD_IS_STRING) { +			int offset; +			if (field->flags & FIELD_IS_DYNAMIC) { +				offset = *(int *)(data + field->offset); +				offset &= 0xffff; +			} else +				offset = field->offset; +			PyTuple_SetItem(t, n++, +				PyString_FromString((char *)data + offset)); +		} else { /* FIELD_IS_NUMERIC */ +			val = read_size(data + field->offset, field->size); +			if (field->flags & FIELD_IS_SIGNED) { +				PyTuple_SetItem(t, n++, PyInt_FromLong(val)); +			} else { +				PyTuple_SetItem(t, n++, PyInt_FromLong(val)); +			} +		} +	} + +	if (_PyTuple_Resize(&t, n) == -1) +		Py_FatalError("error resizing Python tuple"); + +	handler = PyDict_GetItemString(main_dict, handler_name); +	if (handler && PyCallable_Check(handler)) { +		retval = PyObject_CallObject(handler, t); +		if (retval == NULL) +			handler_call_die(handler_name); +	} else { +		handler = PyDict_GetItemString(main_dict, "trace_unhandled"); +		if (handler && PyCallable_Check(handler)) { +			if (_PyTuple_Resize(&t, N_COMMON_FIELDS) == -1) +				Py_FatalError("error resizing Python tuple"); + +			retval = PyObject_CallObject(handler, t); +			if (retval == NULL) +				handler_call_die("trace_unhandled"); +		} +	} + +	Py_DECREF(t); +} + +static int run_start_sub(void) +{ +	PyObject *handler, *retval; +	int err = 0; + +	main_module = PyImport_AddModule("__main__"); +	if (main_module == NULL) +		return -1; +	Py_INCREF(main_module); + +	main_dict = PyModule_GetDict(main_module); +	if (main_dict == NULL) { +		err = -1; +		goto error; +	} +	Py_INCREF(main_dict); + +	handler = PyDict_GetItemString(main_dict, "trace_begin"); +	if (handler == NULL || !PyCallable_Check(handler)) +		goto out; + +	retval = PyObject_CallObject(handler, NULL); +	if (retval == NULL) +		handler_call_die("trace_begin"); + +	Py_DECREF(retval); +	return err; +error: +	Py_XDECREF(main_dict); +	Py_XDECREF(main_module); +out: +	return err; +} + +/* + * Start trace script + */ +static int python_start_script(const char *script, int argc, const char **argv) +{ +	const char **command_line; +	char buf[PATH_MAX]; +	int i, err = 0; +	FILE *fp; + +	command_line = malloc((argc + 1) * sizeof(const char *)); +	command_line[0] = script; +	for (i = 1; i < argc + 1; i++) +		command_line[i] = argv[i - 1]; + +	Py_Initialize(); + +	initperf_trace_context(); + +	PySys_SetArgv(argc + 1, (char **)command_line); + +	fp = fopen(script, "r"); +	if (!fp) { +		sprintf(buf, "Can't open python script \"%s\"", script); +		perror(buf); +		err = -1; +		goto error; +	} + +	err = PyRun_SimpleFile(fp, script); +	if (err) { +		fprintf(stderr, "Error running python script %s\n", script); +		goto error; +	} + +	err = run_start_sub(); +	if (err) { +		fprintf(stderr, "Error starting python script %s\n", script); +		goto error; +	} + +	free(command_line); +	fprintf(stderr, "perf trace started with Python script %s\n\n", +		script); + +	return err; +error: +	Py_Finalize(); +	free(command_line); + +	return err; +} + +/* + * Stop trace script + */ +static int python_stop_script(void) +{ +	PyObject *handler, *retval; +	int err = 0; + +	handler = PyDict_GetItemString(main_dict, "trace_end"); +	if (handler == NULL || !PyCallable_Check(handler)) +		goto out; + +	retval = PyObject_CallObject(handler, NULL); +	if (retval == NULL) +		handler_call_die("trace_end"); +	else +		Py_DECREF(retval); +out: +	Py_XDECREF(main_dict); +	Py_XDECREF(main_module); +	Py_Finalize(); + +	fprintf(stderr, "\nperf trace Python script stopped\n"); + +	return err; +} + +static int python_generate_script(const char *outfile) +{ +	struct event *event = NULL; +	struct format_field *f; +	char fname[PATH_MAX]; +	int not_first, count; +	FILE *ofp; + +	sprintf(fname, "%s.py", outfile); +	ofp = fopen(fname, "w"); +	if (ofp == NULL) { +		fprintf(stderr, "couldn't open %s\n", fname); +		return -1; +	} +	fprintf(ofp, "# perf trace event handlers, " +		"generated by perf trace -g python\n"); + +	fprintf(ofp, "# Licensed under the terms of the GNU GPL" +		" License version 2\n\n"); + +	fprintf(ofp, "# The common_* event handler fields are the most useful " +		"fields common to\n"); + +	fprintf(ofp, "# all events.  They don't necessarily correspond to " +		"the 'common_*' fields\n"); + +	fprintf(ofp, "# in the format files.  Those fields not available as " +		"handler params can\n"); + +	fprintf(ofp, "# be retrieved using Python functions of the form " +		"common_*(context).\n"); + +	fprintf(ofp, "# See the perf-trace-python Documentation for the list " +		"of available functions.\n\n"); + +	fprintf(ofp, "import os\n"); +	fprintf(ofp, "import sys\n\n"); + +	fprintf(ofp, "sys.path.append(os.environ['PERF_EXEC_PATH'] + \\\n"); +	fprintf(ofp, "\t'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')\n"); +	fprintf(ofp, "\nfrom perf_trace_context import *\n"); +	fprintf(ofp, "from Core import *\n\n\n"); + +	fprintf(ofp, "def trace_begin():\n"); +	fprintf(ofp, "\tprint \"in trace_begin\"\n\n"); + +	fprintf(ofp, "def trace_end():\n"); +	fprintf(ofp, "\tprint \"in trace_end\"\n\n"); + +	while ((event = trace_find_next_event(event))) { +		fprintf(ofp, "def %s__%s(", event->system, event->name); +		fprintf(ofp, "event_name, "); +		fprintf(ofp, "context, "); +		fprintf(ofp, "common_cpu,\n"); +		fprintf(ofp, "\tcommon_secs, "); +		fprintf(ofp, "common_nsecs, "); +		fprintf(ofp, "common_pid, "); +		fprintf(ofp, "common_comm,\n\t"); + +		not_first = 0; +		count = 0; + +		for (f = event->format.fields; f; f = f->next) { +			if (not_first++) +				fprintf(ofp, ", "); +			if (++count % 5 == 0) +				fprintf(ofp, "\n\t"); + +			fprintf(ofp, "%s", f->name); +		} +		fprintf(ofp, "):\n"); + +		fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " +			"common_secs, common_nsecs,\n\t\t\t" +			"common_pid, common_comm)\n\n"); + +		fprintf(ofp, "\t\tprint \""); + +		not_first = 0; +		count = 0; + +		for (f = event->format.fields; f; f = f->next) { +			if (not_first++) +				fprintf(ofp, ", "); +			if (count && count % 3 == 0) { +				fprintf(ofp, "\" \\\n\t\t\""); +			} +			count++; + +			fprintf(ofp, "%s=", f->name); +			if (f->flags & FIELD_IS_STRING || +			    f->flags & FIELD_IS_FLAG || +			    f->flags & FIELD_IS_SYMBOLIC) +				fprintf(ofp, "%%s"); +			else if (f->flags & FIELD_IS_SIGNED) +				fprintf(ofp, "%%d"); +			else +				fprintf(ofp, "%%u"); +		} + +		fprintf(ofp, "\\n\" %% \\\n\t\t("); + +		not_first = 0; +		count = 0; + +		for (f = event->format.fields; f; f = f->next) { +			if (not_first++) +				fprintf(ofp, ", "); + +			if (++count % 5 == 0) +				fprintf(ofp, "\n\t\t"); + +			if (f->flags & FIELD_IS_FLAG) { +				if ((count - 1) % 5 != 0) { +					fprintf(ofp, "\n\t\t"); +					count = 4; +				} +				fprintf(ofp, "flag_str(\""); +				fprintf(ofp, "%s__%s\", ", event->system, +					event->name); +				fprintf(ofp, "\"%s\", %s)", f->name, +					f->name); +			} else if (f->flags & FIELD_IS_SYMBOLIC) { +				if ((count - 1) % 5 != 0) { +					fprintf(ofp, "\n\t\t"); +					count = 4; +				} +				fprintf(ofp, "symbol_str(\""); +				fprintf(ofp, "%s__%s\", ", event->system, +					event->name); +				fprintf(ofp, "\"%s\", %s)", f->name, +					f->name); +			} else +				fprintf(ofp, "%s", f->name); +		} + +		fprintf(ofp, "),\n\n"); +	} + +	fprintf(ofp, "def trace_unhandled(event_name, context, " +		"common_cpu, common_secs, common_nsecs,\n\t\t" +		"common_pid, common_comm):\n"); + +	fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " +		"common_secs, common_nsecs,\n\t\tcommon_pid, " +		"common_comm)\n\n"); + +	fprintf(ofp, "def print_header(" +		"event_name, cpu, secs, nsecs, pid, comm):\n" +		"\tprint \"%%-20s %%5u %%05u.%%09u %%8u %%-20s \" %% \\\n\t" +		"(event_name, cpu, secs, nsecs, pid, comm),\n"); + +	fclose(ofp); + +	fprintf(stderr, "generated Python script: %s\n", fname); + +	return 0; +} + +struct scripting_ops python_scripting_ops = { +	.name = "Python", +	.start_script = python_start_script, +	.stop_script = python_stop_script, +	.process_event = python_process_event, +	.generate_script = python_generate_script, +}; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ce3a6c8abe7..0de7258e70a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1,5 +1,8 @@ +#define _FILE_OFFSET_BITS 64 +  #include <linux/kernel.h> +#include <byteswap.h>  #include <unistd.h>  #include <sys/types.h> @@ -49,6 +52,11 @@ out_close:  	return -1;  } +static inline int perf_session__create_kernel_maps(struct perf_session *self) +{ +	return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); +} +  struct perf_session *perf_session__new(const char *filename, int mode, bool force)  {  	size_t len = filename ? strlen(filename) + 1 : 0; @@ -66,13 +74,22 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc  	self->mmap_window = 32;  	self->cwd = NULL;  	self->cwdlen = 0; +	self->unknown_events = 0;  	map_groups__init(&self->kmaps); -	if (perf_session__create_kernel_maps(self) < 0) -		goto out_delete; +	if (mode == O_RDONLY) { +		if (perf_session__open(self, force) < 0) +			goto out_delete; +	} else if (mode == O_WRONLY) { +		/* +		 * In O_RDONLY mode this will be performed when reading the +		 * kernel MMAP event, in event__process_mmap(). +		 */ +		if (perf_session__create_kernel_maps(self) < 0) +			goto out_delete; +	} -	if (mode == O_RDONLY && perf_session__open(self, force) < 0) -		goto out_delete; +	self->sample_type = perf_header__sample_type(&self->header);  out:  	return self;  out_free: @@ -148,3 +165,409 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self,  	return syms;  } + +static int process_event_stub(event_t *event __used, +			      struct perf_session *session __used) +{ +	dump_printf(": unhandled!\n"); +	return 0; +} + +static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) +{ +	if (handler->sample == NULL) +		handler->sample = process_event_stub; +	if (handler->mmap == NULL) +		handler->mmap = process_event_stub; +	if (handler->comm == NULL) +		handler->comm = process_event_stub; +	if (handler->fork == NULL) +		handler->fork = process_event_stub; +	if (handler->exit == NULL) +		handler->exit = process_event_stub; +	if (handler->lost == NULL) +		handler->lost = process_event_stub; +	if (handler->read == NULL) +		handler->read = process_event_stub; +	if (handler->throttle == NULL) +		handler->throttle = process_event_stub; +	if (handler->unthrottle == NULL) +		handler->unthrottle = process_event_stub; +} + +static 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", +}; + +unsigned long event__total[PERF_RECORD_MAX]; + +void event__print_totals(void) +{ +	int i; +	for (i = 0; i < PERF_RECORD_MAX; ++i) +		pr_info("%10s events: %10ld\n", +			event__name[i], event__total[i]); +} + +void mem_bswap_64(void *src, int byte_size) +{ +	u64 *m = src; + +	while (byte_size > 0) { +		*m = bswap_64(*m); +		byte_size -= sizeof(u64); +		++m; +	} +} + +static void event__all64_swap(event_t *self) +{ +	struct perf_event_header *hdr = &self->header; +	mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); +} + +static void event__comm_swap(event_t *self) +{ +	self->comm.pid = bswap_32(self->comm.pid); +	self->comm.tid = bswap_32(self->comm.tid); +} + +static void event__mmap_swap(event_t *self) +{ +	self->mmap.pid	 = bswap_32(self->mmap.pid); +	self->mmap.tid	 = bswap_32(self->mmap.tid); +	self->mmap.start = bswap_64(self->mmap.start); +	self->mmap.len	 = bswap_64(self->mmap.len); +	self->mmap.pgoff = bswap_64(self->mmap.pgoff); +} + +static void event__task_swap(event_t *self) +{ +	self->fork.pid	= bswap_32(self->fork.pid); +	self->fork.tid	= bswap_32(self->fork.tid); +	self->fork.ppid	= bswap_32(self->fork.ppid); +	self->fork.ptid	= bswap_32(self->fork.ptid); +	self->fork.time	= bswap_64(self->fork.time); +} + +static void event__read_swap(event_t *self) +{ +	self->read.pid		= bswap_32(self->read.pid); +	self->read.tid		= bswap_32(self->read.tid); +	self->read.value	= bswap_64(self->read.value); +	self->read.time_enabled	= bswap_64(self->read.time_enabled); +	self->read.time_running	= bswap_64(self->read.time_running); +	self->read.id		= bswap_64(self->read.id); +} + +typedef void (*event__swap_op)(event_t *self); + +static event__swap_op event__swap_ops[] = { +	[PERF_RECORD_MMAP]   = event__mmap_swap, +	[PERF_RECORD_COMM]   = event__comm_swap, +	[PERF_RECORD_FORK]   = event__task_swap, +	[PERF_RECORD_EXIT]   = event__task_swap, +	[PERF_RECORD_LOST]   = event__all64_swap, +	[PERF_RECORD_READ]   = event__read_swap, +	[PERF_RECORD_SAMPLE] = event__all64_swap, +	[PERF_RECORD_MAX]    = NULL, +}; + +static int perf_session__process_event(struct perf_session *self, +				       event_t *event, +				       struct perf_event_ops *ops, +				       u64 offset, u64 head) +{ +	trace_event(event); + +	if (event->header.type < PERF_RECORD_MAX) { +		dump_printf("%#Lx [%#x]: PERF_RECORD_%s", +			    offset + head, event->header.size, +			    event__name[event->header.type]); +		++event__total[0]; +		++event__total[event->header.type]; +	} + +	if (self->header.needs_swap && event__swap_ops[event->header.type]) +		event__swap_ops[event->header.type](event); + +	switch (event->header.type) { +	case PERF_RECORD_SAMPLE: +		return ops->sample(event, self); +	case PERF_RECORD_MMAP: +		return ops->mmap(event, self); +	case PERF_RECORD_COMM: +		return ops->comm(event, self); +	case PERF_RECORD_FORK: +		return ops->fork(event, self); +	case PERF_RECORD_EXIT: +		return ops->exit(event, self); +	case PERF_RECORD_LOST: +		return ops->lost(event, self); +	case PERF_RECORD_READ: +		return ops->read(event, self); +	case PERF_RECORD_THROTTLE: +		return ops->throttle(event, self); +	case PERF_RECORD_UNTHROTTLE: +		return ops->unthrottle(event, self); +	default: +		self->unknown_events++; +		return -1; +	} +} + +void perf_event_header__bswap(struct perf_event_header *self) +{ +	self->type = bswap_32(self->type); +	self->misc = bswap_16(self->misc); +	self->size = bswap_16(self->size); +} + +int perf_header__read_build_ids(struct perf_header *self, +				int input, u64 offset, u64 size) +{ +	struct build_id_event bev; +	char filename[PATH_MAX]; +	u64 limit = offset + size; +	int err = -1; + +	while (offset < limit) { +		struct dso *dso; +		ssize_t len; +		struct list_head *head = &dsos__user; + +		if (read(input, &bev, sizeof(bev)) != sizeof(bev)) +			goto out; + +		if (self->needs_swap) +			perf_event_header__bswap(&bev.header); + +		len = bev.header.size - sizeof(bev); +		if (read(input, filename, len) != len) +			goto out; + +		if (bev.header.misc & PERF_RECORD_MISC_KERNEL) +			head = &dsos__kernel; + +		dso = __dsos__findnew(head, filename); +		if (dso != NULL) { +			dso__set_build_id(dso, &bev.build_id); +			if (head == &dsos__kernel && filename[0] == '[') +				dso->kernel = 1; +		} + +		offset += bev.header.size; +	} +	err = 0; +out: +	return err; +} + +static struct thread *perf_session__register_idle_thread(struct perf_session *self) +{ +	struct thread *thread = perf_session__findnew(self, 0); + +	if (thread == NULL || thread__set_comm(thread, "swapper")) { +		pr_err("problem inserting idle task.\n"); +		thread = NULL; +	} + +	return thread; +} + +int __perf_session__process_events(struct perf_session *self, +				   u64 data_offset, u64 data_size, +				   u64 file_size, struct perf_event_ops *ops) +{ +	int err, mmap_prot, mmap_flags; +	u64 head, shift; +	u64 offset = 0; +	size_t	page_size; +	event_t *event; +	uint32_t size; +	char *buf; + +	perf_event_ops__fill_defaults(ops); + +	page_size = sysconf(_SC_PAGESIZE); + +	head = data_offset; +	shift = page_size * (head / page_size); +	offset += shift; +	head -= shift; + +	mmap_prot  = PROT_READ; +	mmap_flags = MAP_SHARED; + +	if (self->header.needs_swap) { +		mmap_prot  |= PROT_WRITE; +		mmap_flags = MAP_PRIVATE; +	} +remap: +	buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, +		   mmap_flags, self->fd, offset); +	if (buf == MAP_FAILED) { +		pr_err("failed to mmap file\n"); +		err = -errno; +		goto out_err; +	} + +more: +	event = (event_t *)(buf + head); + +	if (self->header.needs_swap) +		perf_event_header__bswap(&event->header); +	size = event->header.size; +	if (size == 0) +		size = 8; + +	if (head + event->header.size >= page_size * self->mmap_window) { +		int munmap_ret; + +		shift = page_size * (head / page_size); + +		munmap_ret = munmap(buf, page_size * self->mmap_window); +		assert(munmap_ret == 0); + +		offset += shift; +		head -= shift; +		goto remap; +	} + +	size = event->header.size; + +	dump_printf("\n%#Lx [%#x]: event: %d\n", +		    offset + head, event->header.size, event->header.type); + +	if (size == 0 || +	    perf_session__process_event(self, event, ops, offset, head) < 0) { +		dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", +			    offset + head, event->header.size, +			    event->header.type); +		/* +		 * assume we lost track of the stream, check alignment, and +		 * increment a single u64 in the hope to catch on again 'soon'. +		 */ +		if (unlikely(head & 7)) +			head &= ~7ULL; + +		size = 8; +	} + +	head += size; + +	if (offset + head >= data_offset + data_size) +		goto done; + +	if (offset + head < file_size) +		goto more; +done: +	err = 0; +out_err: +	return err; +} + +int perf_session__process_events(struct perf_session *self, +				 struct perf_event_ops *ops) +{ +	int err; + +	if (perf_session__register_idle_thread(self) == NULL) +		return -ENOMEM; + +	if (!symbol_conf.full_paths) { +		char bf[PATH_MAX]; + +		if (getcwd(bf, sizeof(bf)) == NULL) { +			err = -errno; +out_getcwd_err: +			pr_err("failed to get the current directory\n"); +			goto out_err; +		} +		self->cwd = strdup(bf); +		if (self->cwd == NULL) { +			err = -ENOMEM; +			goto out_getcwd_err; +		} +		self->cwdlen = strlen(self->cwd); +	} + +	err = __perf_session__process_events(self, self->header.data_offset, +					     self->header.data_size, +					     self->size, ops); +out_err: +	return err; +} + +bool perf_session__has_traces(struct perf_session *self, const char *msg) +{ +	if (!(self->sample_type & PERF_SAMPLE_RAW)) { +		pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); +		return false; +	} + +	return true; +} + +int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, +					     const char *symbol_name, +					     u64 addr) +{ +	char *bracket; +	enum map_type i; + +	self->ref_reloc_sym.name = strdup(symbol_name); +	if (self->ref_reloc_sym.name == NULL) +		return -ENOMEM; + +	bracket = strchr(self->ref_reloc_sym.name, ']'); +	if (bracket) +		*bracket = '\0'; + +	self->ref_reloc_sym.addr = addr; + +	for (i = 0; i < MAP__NR_TYPES; ++i) { +		struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); +		kmap->ref_reloc_sym = &self->ref_reloc_sym; +	} + +	return 0; +} + +static u64 map__reloc_map_ip(struct map *map, u64 ip) +{ +	return ip + (s64)map->pgoff; +} + +static u64 map__reloc_unmap_ip(struct map *map, u64 ip) +{ +	return ip - (s64)map->pgoff; +} + +void map__reloc_vmlinux(struct map *self) +{ +	struct kmap *kmap = map__kmap(self); +	s64 reloc; + +	if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) +		return; + +	reloc = (kmap->ref_reloc_sym->unrelocated_addr - +		 kmap->ref_reloc_sym->addr); + +	if (!reloc) +		return; + +	self->map_ip   = map__reloc_map_ip; +	self->unmap_ip = map__reloc_unmap_ip; +	self->pgoff    = reloc; +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 32eaa1bada0..31950fcd8a4 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -3,13 +3,13 @@  #include "event.h"  #include "header.h" +#include "symbol.h"  #include "thread.h"  #include <linux/rbtree.h>  #include "../../../include/linux/perf_event.h"  struct ip_callchain;  struct thread; -struct symbol;  struct perf_session {  	struct perf_header	header; @@ -18,10 +18,13 @@ struct perf_session {  	struct map_groups	kmaps;  	struct rb_root		threads;  	struct thread		*last_match; +	struct map		*vmlinux_maps[MAP__NR_TYPES];  	struct events_stats	events_stats;  	unsigned long		event_total[PERF_RECORD_MAX]; +	unsigned long		unknown_events;  	struct rb_root		hists;  	u64			sample_type; +	struct ref_reloc_sym	ref_reloc_sym;  	int			fd;  	int			cwdlen;  	char			*cwd; @@ -31,23 +34,25 @@ struct perf_session {  typedef int (*event_op)(event_t *self, struct perf_session *session);  struct perf_event_ops { -	event_op	process_sample_event; -	event_op	process_mmap_event; -	event_op	process_comm_event; -	event_op	process_fork_event; -	event_op	process_exit_event; -	event_op	process_lost_event; -	event_op	process_read_event; -	event_op	process_throttle_event; -	event_op	process_unthrottle_event; -	int		(*sample_type_check)(struct perf_session *session); -	unsigned long	total_unknown; -	bool		full_paths; +	event_op sample, +		 mmap, +		 comm, +		 fork, +		 exit, +		 lost, +		 read, +		 throttle, +		 unthrottle;  };  struct perf_session *perf_session__new(const char *filename, int mode, bool force);  void perf_session__delete(struct perf_session *self); +void perf_event_header__bswap(struct perf_event_header *self); + +int __perf_session__process_events(struct perf_session *self, +				   u64 data_offset, u64 data_size, u64 size, +				   struct perf_event_ops *ops);  int perf_session__process_events(struct perf_session *self,  				 struct perf_event_ops *event_ops); @@ -56,6 +61,28 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self,  						struct ip_callchain *chain,  						struct symbol **parent); -int perf_header__read_build_ids(int input, u64 offset, u64 file_size); +bool perf_session__has_traces(struct perf_session *self, const char *msg); + +int perf_header__read_build_ids(struct perf_header *self, int input, +				u64 offset, u64 file_size); + +int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, +					     const char *symbol_name, +					     u64 addr); + +void mem_bswap_64(void *src, int byte_size); + +static inline int __perf_session__create_kernel_maps(struct perf_session *self, +						struct dso *kernel) +{ +	return __map_groups__create_kernel_maps(&self->kmaps, +						self->vmlinux_maps, kernel); +} +static inline struct map * +	perf_session__new_module_map(struct perf_session *self, +				     u64 start, const char *filename) +{ +	return map_groups__new_module(&self->kmaps, start, filename); +}  #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 5352d7dccc6..c397d4f6f74 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -227,16 +227,73 @@ fail:  	return NULL;  } -/* Glob expression pattern matching */ +/* Character class matching */ +static bool __match_charclass(const char *pat, char c, const char **npat) +{ +	bool complement = false, ret = true; + +	if (*pat == '!') { +		complement = true; +		pat++; +	} +	if (*pat++ == c)	/* First character is special */ +		goto end; + +	while (*pat && *pat != ']') {	/* Matching */ +		if (*pat == '-' && *(pat + 1) != ']') {	/* Range */ +			if (*(pat - 1) <= c && c <= *(pat + 1)) +				goto end; +			if (*(pat - 1) > *(pat + 1)) +				goto error; +			pat += 2; +		} else if (*pat++ == c) +			goto end; +	} +	if (!*pat) +		goto error; +	ret = false; + +end: +	while (*pat && *pat != ']')	/* Searching closing */ +		pat++; +	if (!*pat) +		goto error; +	*npat = pat + 1; +	return complement ? !ret : ret; + +error: +	return false; +} + +/** + * strglobmatch - glob expression pattern matching + * @str: the target string to match + * @pat: the pattern string to match + * + * This returns true if the @str matches @pat. @pat can includes wildcards + * ('*','?') and character classes ([CHARS], complementation and ranges are + * also supported). Also, this supports escape character ('\') to use special + * characters as normal character. + * + * Note: if @pat syntax is broken, this always returns false. + */  bool strglobmatch(const char *str, const char *pat)  {  	while (*str && *pat && *pat != '*') { -		if (*pat == '?') { +		if (*pat == '?') {	/* Matches any single character */  			str++;  			pat++; -		} else -			if (*str++ != *pat++) +			continue; +		} else if (*pat == '[')	/* Character classes/Ranges */ +			if (__match_charclass(pat + 1, *str, &pat)) { +				str++; +				continue; +			} else  				return false; +		else if (*pat == '\\') /* Escaped char match as normal char */ +			pat++; +		if (*str++ != *pat++) +			return false;  	}  	/* Check wild card */  	if (*pat == '*') { diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index ab92763edb0..323c0aea0a9 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1,6 +1,5 @@  #include "util.h"  #include "../perf.h" -#include "session.h"  #include "sort.h"  #include "string.h"  #include "symbol.h" @@ -22,6 +21,7 @@  enum dso_origin {  	DSO__ORIG_KERNEL = 0,  	DSO__ORIG_JAVA_JIT, +	DSO__ORIG_BUILD_ID_CACHE,  	DSO__ORIG_FEDORA,  	DSO__ORIG_UBUNTU,  	DSO__ORIG_BUILDID, @@ -33,7 +33,7 @@ enum dso_origin {  static void dsos__add(struct list_head *head, struct dso *dso);  static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);  static int dso__load_kernel_sym(struct dso *self, struct map *map, -				struct perf_session *session, symbol_filter_t filter); +				symbol_filter_t filter);  static int vmlinux_path__nr_entries;  static char **vmlinux_path; @@ -53,17 +53,12 @@ bool dso__sorted_by_name(const struct dso *self, enum map_type type)  	return self->sorted_by_name & (1 << type);  } -static void dso__set_loaded(struct dso *self, enum map_type type) -{ -	self->loaded |= (1 << type); -} -  static void dso__set_sorted_by_name(struct dso *self, enum map_type type)  {  	self->sorted_by_name |= (1 << type);  } -static bool symbol_type__is_a(char symbol_type, enum map_type map_type) +bool symbol_type__is_a(char symbol_type, enum map_type map_type)  {  	switch (map_type) {  	case MAP__FUNCTION: @@ -142,14 +137,14 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)  	self->start = start;  	self->end   = len ? start + len - 1 : start; -	pr_debug3("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); +	pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);  	memcpy(self->name, name, namelen);  	return self;  } -static void symbol__delete(struct symbol *self) +void symbol__delete(struct symbol *self)  {  	free(((void *)self) - symbol_conf.priv_size);  } @@ -160,7 +155,7 @@ static size_t symbol__fprintf(struct symbol *self, FILE *fp)  		       self->start, self->end, self->name);  } -static void dso__set_long_name(struct dso *self, char *name) +void dso__set_long_name(struct dso *self, char *name)  {  	if (name == NULL)  		return; @@ -175,7 +170,7 @@ static void dso__set_basename(struct dso *self)  struct dso *dso__new(const char *name)  { -	struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); +	struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1);  	if (self != NULL) {  		int i; @@ -344,10 +339,10 @@ void dso__sort_by_name(struct dso *self, enum map_type type)  				     &self->symbols[type]);  } -int build_id__sprintf(u8 *self, int len, char *bf) +int build_id__sprintf(const u8 *self, int len, char *bf)  {  	char *bid = bf; -	u8 *raw = self; +	const u8 *raw = self;  	int i;  	for (i = 0; i < len; ++i) { @@ -372,6 +367,10 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)  	struct rb_node *nd;  	size_t ret = fprintf(fp, "dso: %s (", self->short_name); +	if (self->short_name != self->long_name) +		ret += fprintf(fp, "%s, ", self->long_name); +	ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], +		       self->loaded ? "" : "NOT ");  	ret += dso__fprintf_buildid(self, fp);  	ret += fprintf(fp, ")\n");  	for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) { @@ -382,24 +381,20 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)  	return ret;  } -/* - * Loads the function entries in /proc/kallsyms into kernel_map->dso, - * so that we can in the next step set the symbol ->end address and then - * call kernel_maps__split_kallsyms. - */ -static int dso__load_all_kallsyms(struct dso *self, struct map *map) +int kallsyms__parse(const char *filename, void *arg, +		    int (*process_symbol)(void *arg, const char *name, +						     char type, u64 start))  {  	char *line = NULL;  	size_t n; -	struct rb_root *root = &self->symbols[map->type]; -	FILE *file = fopen("/proc/kallsyms", "r"); +	int err = 0; +	FILE *file = fopen(filename, "r");  	if (file == NULL)  		goto out_failure;  	while (!feof(file)) {  		u64 start; -		struct symbol *sym;  		int line_len, len;  		char symbol_type;  		char *symbol_name; @@ -420,43 +415,72 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map)  			continue;  		symbol_type = toupper(line[len]); -		if (!symbol_type__is_a(symbol_type, map->type)) -			continue; -  		symbol_name = line + len + 2; -		/* -		 * Will fix up the end later, when we have all symbols sorted. -		 */ -		sym = symbol__new(start, 0, symbol_name); -		if (sym == NULL) -			goto out_delete_line; -		/* -		 * We will pass the symbols to the filter later, in -		 * map__split_kallsyms, when we have split the maps per module -		 */ -		symbols__insert(root, sym); +		err = process_symbol(arg, symbol_name, symbol_type, start); +		if (err) +			break;  	}  	free(line);  	fclose(file); +	return err; -	return 0; - -out_delete_line: -	free(line);  out_failure:  	return -1;  } +struct process_kallsyms_args { +	struct map *map; +	struct dso *dso; +}; + +static int map__process_kallsym_symbol(void *arg, const char *name, +				       char type, u64 start) +{ +	struct symbol *sym; +	struct process_kallsyms_args *a = arg; +	struct rb_root *root = &a->dso->symbols[a->map->type]; + +	if (!symbol_type__is_a(type, a->map->type)) +		return 0; + +	/* +	 * Will fix up the end later, when we have all symbols sorted. +	 */ +	sym = symbol__new(start, 0, name); + +	if (sym == NULL) +		return -ENOMEM; +	/* +	 * We will pass the symbols to the filter later, in +	 * map__split_kallsyms, when we have split the maps per module +	 */ +	symbols__insert(root, sym); +	return 0; +} + +/* + * Loads the function entries in /proc/kallsyms into kernel_map->dso, + * so that we can in the next step set the symbol ->end address and then + * call kernel_maps__split_kallsyms. + */ +static int dso__load_all_kallsyms(struct dso *self, const char *filename, +				  struct map *map) +{ +	struct process_kallsyms_args args = { .map = map, .dso = self, }; +	return kallsyms__parse(filename, &args, map__process_kallsym_symbol); +} +  /*   * Split the symbols into maps, making sure there are no overlaps, i.e. the   * kernel range is broken in several maps, named [kernel].N, as we don't have   * the original ELF section names vmlinux have.   */  static int dso__split_kallsyms(struct dso *self, struct map *map, -			       struct perf_session *session, symbol_filter_t filter) +			       symbol_filter_t filter)  { +	struct map_groups *kmaps = map__kmap(map)->kmaps;  	struct map *curr_map = map;  	struct symbol *pos;  	int count = 0; @@ -477,13 +501,17 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,  			*module++ = '\0'; -			if (strcmp(self->name, module)) { -				curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); +			if (strcmp(curr_map->dso->short_name, module)) { +				curr_map = map_groups__find_by_name(kmaps, map->type, module);  				if (curr_map == NULL) {  					pr_debug("/proc/{kallsyms,modules} " -					         "inconsistency!\n"); +					         "inconsistency while looking " +						 "for \"%s\" module!\n", module);  					return -1;  				} + +				if (curr_map->dso->loaded) +					goto discard_symbol;  			}  			/*  			 * So that we look just like we get from .ko files, @@ -503,13 +531,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,  				return -1;  			curr_map = map__new2(pos->start, dso, map->type); -			if (map == NULL) { +			if (curr_map == NULL) {  				dso__delete(dso);  				return -1;  			}  			curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; -			map_groups__insert(&session->kmaps, curr_map); +			map_groups__insert(kmaps, curr_map);  			++kernel_range;  		} @@ -528,17 +556,16 @@ discard_symbol:		rb_erase(&pos->rb_node, root);  	return count;  } - -static int dso__load_kallsyms(struct dso *self, struct map *map, -			      struct perf_session *session, symbol_filter_t filter) +int dso__load_kallsyms(struct dso *self, const char *filename, +		       struct map *map, symbol_filter_t filter)  { -	if (dso__load_all_kallsyms(self, map) < 0) +	if (dso__load_all_kallsyms(self, filename, map) < 0)  		return -1;  	symbols__fixup_end(&self->symbols[map->type]);  	self->origin = DSO__ORIG_KERNEL; -	return dso__split_kallsyms(self, map, session, filter); +	return dso__split_kallsyms(self, map, filter);  }  static int dso__load_perf_map(struct dso *self, struct map *map, @@ -864,10 +891,10 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type  	}  } -static int dso__load_sym(struct dso *self, struct map *map, -			 struct perf_session *session, const char *name, int fd, -			 symbol_filter_t filter, int kernel, int kmodule) +static int dso__load_sym(struct dso *self, struct map *map, const char *name, +			 int fd, symbol_filter_t filter, int kmodule)  { +	struct kmap *kmap = self->kernel ? map__kmap(map) : NULL;  	struct map *curr_map = map;  	struct dso *curr_dso = self;  	size_t dso_name_len = strlen(self->short_name); @@ -924,7 +951,7 @@ static int dso__load_sym(struct dso *self, struct map *map,  	nr_syms = shdr.sh_size / shdr.sh_entsize;  	memset(&sym, 0, sizeof(sym)); -	if (!kernel) { +	if (!self->kernel) {  		self->adjust_symbols = (ehdr.e_type == ET_EXEC ||  				elf_section_by_name(elf, &ehdr, &shdr,  						     ".gnu.prelink_undo", @@ -933,11 +960,15 @@ static int dso__load_sym(struct dso *self, struct map *map,  	elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {  		struct symbol *f; -		const char *elf_name; +		const char *elf_name = elf_sym__name(&sym, symstrs);  		char *demangled = NULL;  		int is_label = elf_sym__is_label(&sym);  		const char *section_name; +		if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && +		    strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) +			kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; +  		if (!is_label && !elf_sym__is_a(&sym, map->type))  			continue; @@ -950,10 +981,9 @@ static int dso__load_sym(struct dso *self, struct map *map,  		if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))  			continue; -		elf_name = elf_sym__name(&sym, symstrs);  		section_name = elf_sec__name(&shdr, secstrs); -		if (kernel || kmodule) { +		if (self->kernel || kmodule) {  			char dso_name[PATH_MAX];  			if (strcmp(section_name, @@ -969,7 +999,7 @@ static int dso__load_sym(struct dso *self, struct map *map,  			snprintf(dso_name, sizeof(dso_name),  				 "%s%s", self->short_name, section_name); -			curr_map = map_groups__find_by_name(&session->kmaps, map->type, dso_name); +			curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);  			if (curr_map == NULL) {  				u64 start = sym.st_value; @@ -980,7 +1010,7 @@ static int dso__load_sym(struct dso *self, struct map *map,  				if (curr_dso == NULL)  					goto out_elf_end;  				curr_map = map__new2(start, curr_dso, -						     MAP__FUNCTION); +						     map->type);  				if (curr_map == NULL) {  					dso__delete(curr_dso);  					goto out_elf_end; @@ -988,8 +1018,9 @@ static int dso__load_sym(struct dso *self, struct map *map,  				curr_map->map_ip = identity__map_ip;  				curr_map->unmap_ip = identity__map_ip;  				curr_dso->origin = DSO__ORIG_KERNEL; -				map_groups__insert(&session->kmaps, curr_map); +				map_groups__insert(kmap->kmaps, curr_map);  				dsos__add(&dsos__kernel, curr_dso); +				dso__set_loaded(curr_dso, map->type);  			} else  				curr_dso = curr_map->dso; @@ -997,9 +1028,10 @@ static int dso__load_sym(struct dso *self, struct map *map,  		}  		if (curr_dso->adjust_symbols) { -			pr_debug2("adjusting symbol: st_value: %Lx sh_addr: " -				  "%Lx sh_offset: %Lx\n", (u64)sym.st_value, -				  (u64)shdr.sh_addr, (u64)shdr.sh_offset); +			pr_debug4("%s: adjusting symbol: st_value: %#Lx " +				  "sh_addr: %#Lx sh_offset: %#Lx\n", __func__, +				  (u64)sym.st_value, (u64)shdr.sh_addr, +				  (u64)shdr.sh_offset);  			sym.st_value -= shdr.sh_addr - shdr.sh_offset;  		}  		/* @@ -1027,8 +1059,16 @@ new_symbol:  	/*  	 * For misannotated, zeroed, ASM function sizes.  	 */ -	if (nr > 0) +	if (nr > 0) {  		symbols__fixup_end(&self->symbols[map->type]); +		if (kmap) { +			/* +			 * We need to fixup this here too because we create new +			 * maps here, for things like vsyscall sections. +			 */ +			__map_groups__fixup_end(kmap->kmaps, map->type); +		} +	}  	err = nr;  out_elf_end:  	elf_end(elf); @@ -1041,25 +1081,28 @@ static bool dso__build_id_equal(const struct dso *self, u8 *build_id)  	return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0;  } -static bool __dsos__read_build_ids(struct list_head *head) +static bool __dsos__read_build_ids(struct list_head *head, bool with_hits)  {  	bool have_build_id = false;  	struct dso *pos; -	list_for_each_entry(pos, head, node) +	list_for_each_entry(pos, head, node) { +		if (with_hits && !pos->hit) +			continue;  		if (filename__read_build_id(pos->long_name, pos->build_id,  					    sizeof(pos->build_id)) > 0) {  			have_build_id	  = true;  			pos->has_build_id = true;  		} +	}  	return have_build_id;  } -bool dsos__read_build_ids(void) +bool dsos__read_build_ids(bool with_hits)  { -	bool kbuildids = __dsos__read_build_ids(&dsos__kernel), -	     ubuildids = __dsos__read_build_ids(&dsos__user); +	bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits), +	     ubuildids = __dsos__read_build_ids(&dsos__user, with_hits);  	return kbuildids || ubuildids;  } @@ -1191,6 +1234,7 @@ char dso__symtab_origin(const struct dso *self)  	static const char origin[] = {  		[DSO__ORIG_KERNEL] =   'k',  		[DSO__ORIG_JAVA_JIT] = 'j', +		[DSO__ORIG_BUILD_ID_CACHE] = 'B',  		[DSO__ORIG_FEDORA] =   'f',  		[DSO__ORIG_UBUNTU] =   'u',  		[DSO__ORIG_BUILDID] =  'b', @@ -1203,19 +1247,19 @@ char dso__symtab_origin(const struct dso *self)  	return origin[self->origin];  } -int dso__load(struct dso *self, struct map *map, struct perf_session *session, -	      symbol_filter_t filter) +int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)  {  	int size = PATH_MAX;  	char *name;  	u8 build_id[BUILD_ID_SIZE]; +	char build_id_hex[BUILD_ID_SIZE * 2 + 1];  	int ret = -1;  	int fd;  	dso__set_loaded(self, map->type);  	if (self->kernel) -		return dso__load_kernel_sym(self, map, session, filter); +		return dso__load_kernel_sym(self, map, filter);  	name = malloc(size);  	if (!name) @@ -1230,8 +1274,16 @@ int dso__load(struct dso *self, struct map *map, struct perf_session *session,  		return ret;  	} -	self->origin = DSO__ORIG_FEDORA - 1; +	self->origin = DSO__ORIG_BUILD_ID_CACHE; +	if (self->has_build_id) { +		build_id__sprintf(self->build_id, sizeof(self->build_id), +				  build_id_hex); +		snprintf(name, size, "%s/%s/.build-id/%.2s/%s", +			 getenv("HOME"), DEBUG_CACHE_DIR, +			 build_id_hex, build_id_hex + 2); +		goto open_file; +	}  more:  	do {  		self->origin++; @@ -1247,8 +1299,6 @@ more:  		case DSO__ORIG_BUILDID:  			if (filename__read_build_id(self->long_name, build_id,  						    sizeof(build_id))) { -				char build_id_hex[BUILD_ID_SIZE * 2 + 1]; -  				build_id__sprintf(build_id, sizeof(build_id),  						  build_id_hex);  				snprintf(name, size, @@ -1276,11 +1326,11 @@ compare_build_id:  			if (!dso__build_id_equal(self, build_id))  				goto more;  		} - +open_file:  		fd = open(name, O_RDONLY);  	} while (fd < 0); -	ret = dso__load_sym(self, map, NULL, name, fd, filter, 0, 0); +	ret = dso__load_sym(self, map, name, fd, filter, 0);  	close(fd);  	/* @@ -1309,14 +1359,34 @@ struct map *map_groups__find_by_name(struct map_groups *self,  	for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {  		struct map *map = rb_entry(nd, struct map, rb_node); -		if (map->dso && strcmp(map->dso->name, name) == 0) +		if (map->dso && strcmp(map->dso->short_name, name) == 0)  			return map;  	}  	return NULL;  } -static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) +static int dso__kernel_module_get_build_id(struct dso *self) +{ +	char filename[PATH_MAX]; +	/* +	 * kernel module short names are of the form "[module]" and +	 * we need just "module" here. +	 */ +	const char *name = self->short_name + 1; + +	snprintf(filename, sizeof(filename), +		 "/sys/module/%.*s/notes/.note.gnu.build-id", +		 (int)strlen(name - 1), name); + +	if (sysfs__read_build_id(filename, self->build_id, +				 sizeof(self->build_id)) == 0) +		self->has_build_id = true; + +	return 0; +} + +static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirname)  {  	struct dirent *dent;  	DIR *dir = opendir(dirname); @@ -1336,7 +1406,7 @@ static int perf_session__set_modules_path_dir(struct perf_session *self, char *d  			snprintf(path, sizeof(path), "%s/%s",  				 dirname, dent->d_name); -			if (perf_session__set_modules_path_dir(self, path) < 0) +			if (map_groups__set_modules_path_dir(self, path) < 0)  				goto failure;  		} else {  			char *dot = strrchr(dent->d_name, '.'), @@ -1350,7 +1420,7 @@ static int perf_session__set_modules_path_dir(struct perf_session *self, char *d  				 (int)(dot - dent->d_name), dent->d_name);  			strxfrchar(dso_name, '-', '_'); -			map = map_groups__find_by_name(&self->kmaps, MAP__FUNCTION, dso_name); +			map = map_groups__find_by_name(self, MAP__FUNCTION, dso_name);  			if (map == NULL)  				continue; @@ -1361,6 +1431,7 @@ static int perf_session__set_modules_path_dir(struct perf_session *self, char *d  			if (long_name == NULL)  				goto failure;  			dso__set_long_name(map->dso, long_name); +			dso__kernel_module_get_build_id(map->dso);  		}  	} @@ -1370,7 +1441,7 @@ failure:  	return -1;  } -static int perf_session__set_modules_path(struct perf_session *self) +static int map_groups__set_modules_path(struct map_groups *self)  {  	struct utsname uts;  	char modules_path[PATH_MAX]; @@ -1381,7 +1452,7 @@ static int perf_session__set_modules_path(struct perf_session *self)  	snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel",  		 uts.release); -	return perf_session__set_modules_path_dir(self, modules_path); +	return map_groups__set_modules_path_dir(self, modules_path);  }  /* @@ -1391,8 +1462,8 @@ static int perf_session__set_modules_path(struct perf_session *self)   */  static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)  { -	struct map *self = malloc(sizeof(*self)); - +	struct map *self = zalloc(sizeof(*self) + +				  (dso->kernel ? sizeof(struct kmap) : 0));  	if (self != NULL) {  		/*  		 * ->end will be filled after we load all the symbols @@ -1403,7 +1474,25 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)  	return self;  } -static int perf_session__create_module_maps(struct perf_session *self) +struct map *map_groups__new_module(struct map_groups *self, u64 start, +				   const char *filename) +{ +	struct map *map; +	struct dso *dso = __dsos__findnew(&dsos__kernel, filename); + +	if (dso == NULL) +		return NULL; + +	map = map__new2(start, dso, MAP__FUNCTION); +	if (map == NULL) +		return NULL; + +	dso->origin = DSO__ORIG_KMODULE; +	map_groups__insert(self, map); +	return map; +} + +static int map_groups__create_modules(struct map_groups *self)  {  	char *line = NULL;  	size_t n; @@ -1416,7 +1505,6 @@ static int perf_session__create_module_maps(struct perf_session *self)  	while (!feof(file)) {  		char name[PATH_MAX];  		u64 start; -		struct dso *dso;  		char *sep;  		int line_len; @@ -1442,32 +1530,16 @@ static int perf_session__create_module_maps(struct perf_session *self)  		*sep = '\0';  		snprintf(name, sizeof(name), "[%s]", line); -		dso = dso__new(name); - -		if (dso == NULL) +		map = map_groups__new_module(self, start, name); +		if (map == NULL)  			goto out_delete_line; - -		map = map__new2(start, dso, MAP__FUNCTION); -		if (map == NULL) { -			dso__delete(dso); -			goto out_delete_line; -		} - -		snprintf(name, sizeof(name), -			 "/sys/module/%s/notes/.note.gnu.build-id", line); -		if (sysfs__read_build_id(name, dso->build_id, -					 sizeof(dso->build_id)) == 0) -			dso->has_build_id = true; - -		dso->origin = DSO__ORIG_KMODULE; -		map_groups__insert(&self->kmaps, map); -		dsos__add(&dsos__kernel, dso); +		dso__kernel_module_get_build_id(map->dso);  	}  	free(line);  	fclose(file); -	return perf_session__set_modules_path(self); +	return map_groups__set_modules_path(self);  out_delete_line:  	free(line); @@ -1476,7 +1548,6 @@ out_failure:  }  static int dso__load_vmlinux(struct dso *self, struct map *map, -			     struct perf_session *session,  			     const char *vmlinux, symbol_filter_t filter)  {  	int err = -1, fd; @@ -1510,51 +1581,124 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,  		return -1;  	dso__set_loaded(self, map->type); -	err = dso__load_sym(self, map, session, self->long_name, fd, filter, 1, 0); +	err = dso__load_sym(self, map, vmlinux, fd, filter, 0);  	close(fd); +	if (err > 0) +		pr_debug("Using %s for symbols\n", vmlinux); + +	return err; +} + +int dso__load_vmlinux_path(struct dso *self, struct map *map, +			   symbol_filter_t filter) +{ +	int i, err = 0; + +	pr_debug("Looking at the vmlinux_path (%d entries long)\n", +		 vmlinux_path__nr_entries); + +	for (i = 0; i < vmlinux_path__nr_entries; ++i) { +		err = dso__load_vmlinux(self, map, vmlinux_path[i], filter); +		if (err > 0) { +			dso__set_long_name(self, strdup(vmlinux_path[i])); +			break; +		} +	} +  	return err;  }  static int dso__load_kernel_sym(struct dso *self, struct map *map, -				struct perf_session *session, symbol_filter_t filter) +				symbol_filter_t filter)  {  	int err; -	bool is_kallsyms; +	const char *kallsyms_filename = NULL; +	char *kallsyms_allocated_filename = NULL; +	/* +	 * Step 1: if the user specified a vmlinux filename, use it and only +	 * it, reporting errors to the user if it cannot be used. +	 * +	 * For instance, try to analyse an ARM perf.data file _without_ a +	 * build-id, or if the user specifies the wrong path to the right +	 * vmlinux file, obviously we can't fallback to another vmlinux (a +	 * x86_86 one, on the machine where analysis is being performed, say), +	 * or worse, /proc/kallsyms. +	 * +	 * If the specified file _has_ a build-id and there is a build-id +	 * section in the perf.data file, we will still do the expected +	 * validation in dso__load_vmlinux and will bail out if they don't +	 * match. +	 */ +	if (symbol_conf.vmlinux_name != NULL) { +		err = dso__load_vmlinux(self, map, +					symbol_conf.vmlinux_name, filter); +		goto out_try_fixup; +	}  	if (vmlinux_path != NULL) { -		int i; -		pr_debug("Looking at the vmlinux_path (%d entries long)\n", -			 vmlinux_path__nr_entries); -		for (i = 0; i < vmlinux_path__nr_entries; ++i) { -			err = dso__load_vmlinux(self, map, session, -						vmlinux_path[i], filter); -			if (err > 0) { -				pr_debug("Using %s for symbols\n", -					 vmlinux_path[i]); -				dso__set_long_name(self, -						   strdup(vmlinux_path[i])); -				goto out_fixup; +		err = dso__load_vmlinux_path(self, map, filter); +		if (err > 0) +			goto out_fixup; +	} + +	/* +	 * Say the kernel DSO was created when processing the build-id header table, +	 * we have a build-id, so check if it is the same as the running kernel, +	 * using it if it is. +	 */ +	if (self->has_build_id) { +		u8 kallsyms_build_id[BUILD_ID_SIZE]; +		char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + +		if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, +					 sizeof(kallsyms_build_id)) == 0) { +			if (dso__build_id_equal(self, kallsyms_build_id)) { +				kallsyms_filename = "/proc/kallsyms"; +				goto do_kallsyms;  			}  		} -	} +		/* +		 * Now look if we have it on the build-id cache in +		 * $HOME/.debug/[kernel.kallsyms]. +		 */ +		build_id__sprintf(self->build_id, sizeof(self->build_id), +				  sbuild_id); -	is_kallsyms = self->long_name[0] == '['; -	if (is_kallsyms) -		goto do_kallsyms; +		if (asprintf(&kallsyms_allocated_filename, +			     "%s/.debug/[kernel.kallsyms]/%s", +			     getenv("HOME"), sbuild_id) == -1) { +			pr_err("Not enough memory for kallsyms file lookup\n"); +			return -1; +		} -	err = dso__load_vmlinux(self, map, session, self->long_name, filter); -	if (err <= 0) { -		pr_info("The file %s cannot be used, " -			"trying to use /proc/kallsyms...", self->long_name); -do_kallsyms: -		err = dso__load_kallsyms(self, map, session, filter); -		if (err > 0 && !is_kallsyms) -                        dso__set_long_name(self, strdup("[kernel.kallsyms]")); +		kallsyms_filename = kallsyms_allocated_filename; + +		if (access(kallsyms_filename, F_OK)) { +			pr_err("No kallsyms or vmlinux with build-id %s " +			       "was found\n", sbuild_id); +			free(kallsyms_allocated_filename); +			return -1; +		} +	} else { +		/* +		 * Last resort, if we don't have a build-id and couldn't find +		 * any vmlinux file, try the running kernel kallsyms table. +		 */ +		kallsyms_filename = "/proc/kallsyms";  	} +do_kallsyms: +	err = dso__load_kallsyms(self, kallsyms_filename, map, filter); +	if (err > 0) +		pr_debug("Using %s for symbols\n", kallsyms_filename); +	free(kallsyms_allocated_filename); + +out_try_fixup:  	if (err > 0) {  out_fixup: +		if (kallsyms_filename != NULL) +			dso__set_long_name(self, strdup("[kernel.kallsyms]"));  		map__fixup_start(map);  		map__fixup_end(map);  	} @@ -1564,7 +1708,6 @@ out_fixup:  LIST_HEAD(dsos__user);  LIST_HEAD(dsos__kernel); -struct dso *vdso;  static void dsos__add(struct list_head *head, struct dso *dso)  { @@ -1576,19 +1719,19 @@ static struct dso *dsos__find(struct list_head *head, const char *name)  	struct dso *pos;  	list_for_each_entry(pos, head, node) -		if (strcmp(pos->name, name) == 0) +		if (strcmp(pos->long_name, name) == 0)  			return pos;  	return NULL;  } -struct dso *dsos__findnew(const char *name) +struct dso *__dsos__findnew(struct list_head *head, const char *name)  { -	struct dso *dso = dsos__find(&dsos__user, name); +	struct dso *dso = dsos__find(head, name);  	if (!dso) {  		dso = dso__new(name);  		if (dso != NULL) { -			dsos__add(&dsos__user, dso); +			dsos__add(head, dso);  			dso__set_basename(dso);  		}  	} @@ -1613,75 +1756,78 @@ void dsos__fprintf(FILE *fp)  	__dsos__fprintf(&dsos__user, fp);  } -static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp) +static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, +				      bool with_hits)  {  	struct dso *pos;  	size_t ret = 0;  	list_for_each_entry(pos, head, node) { +		if (with_hits && !pos->hit) +			continue;  		ret += dso__fprintf_buildid(pos, fp);  		ret += fprintf(fp, " %s\n", pos->long_name);  	}  	return ret;  } -size_t dsos__fprintf_buildid(FILE *fp) +size_t dsos__fprintf_buildid(FILE *fp, bool with_hits)  { -	return (__dsos__fprintf_buildid(&dsos__kernel, fp) + -		__dsos__fprintf_buildid(&dsos__user, fp)); +	return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + +		__dsos__fprintf_buildid(&dsos__user, fp, with_hits));  } -static struct dso *dsos__create_kernel( const char *vmlinux) +struct dso *dso__new_kernel(const char *name)  { -	struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); +	struct dso *self = dso__new(name ?: "[kernel.kallsyms]"); -	if (kernel == NULL) -		return NULL; +	if (self != NULL) { +		self->short_name = "[kernel]"; +		self->kernel	 = 1; +	} -	kernel->short_name = "[kernel]"; -	kernel->kernel	   = 1; +	return self; +} -	vdso = dso__new("[vdso]"); -	if (vdso == NULL) -		goto out_delete_kernel_dso; -	dso__set_loaded(vdso, MAP__FUNCTION); +void dso__read_running_kernel_build_id(struct dso *self) +{ +	if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, +				 sizeof(self->build_id)) == 0) +		self->has_build_id = true; +} -	if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, -				 sizeof(kernel->build_id)) == 0) -		kernel->has_build_id = true; +static struct dso *dsos__create_kernel(const char *vmlinux) +{ +	struct dso *kernel = dso__new_kernel(vmlinux); -	dsos__add(&dsos__kernel, kernel); -	dsos__add(&dsos__user, vdso); +	if (kernel != NULL) { +		dso__read_running_kernel_build_id(kernel); +		dsos__add(&dsos__kernel, kernel); +	}  	return kernel; - -out_delete_kernel_dso: -	dso__delete(kernel); -	return NULL;  } -static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux) +int __map_groups__create_kernel_maps(struct map_groups *self, +				     struct map *vmlinux_maps[MAP__NR_TYPES], +				     struct dso *kernel)  { -	struct map *functions, *variables; -	struct dso *kernel = dsos__create_kernel(vmlinux); +	enum map_type type; -	if (kernel == NULL) -		return -1; +	for (type = 0; type < MAP__NR_TYPES; ++type) { +		struct kmap *kmap; -	functions = map__new2(0, kernel, MAP__FUNCTION); -	if (functions == NULL) -		return -1; +		vmlinux_maps[type] = map__new2(0, kernel, type); +		if (vmlinux_maps[type] == NULL) +			return -1; -	variables = map__new2(0, kernel, MAP__VARIABLE); -	if (variables == NULL) { -		map__delete(functions); -		return -1; -	} +		vmlinux_maps[type]->map_ip = +			vmlinux_maps[type]->unmap_ip = identity__map_ip; -	functions->map_ip = functions->unmap_ip = -		variables->map_ip = variables->unmap_ip = identity__map_ip; -	map_groups__insert(self, functions); -	map_groups__insert(self, variables); +		kmap = map__kmap(vmlinux_maps[type]); +		kmap->kmaps = self; +		map_groups__insert(self, vmlinux_maps[type]); +	}  	return 0;  } @@ -1791,19 +1937,22 @@ out_free_comm_list:  	return -1;  } -int perf_session__create_kernel_maps(struct perf_session *self) +int map_groups__create_kernel_maps(struct map_groups *self, +				   struct map *vmlinux_maps[MAP__NR_TYPES])  { -	if (map_groups__create_kernel_maps(&self->kmaps, -					   symbol_conf.vmlinux_name) < 0) +	struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); + +	if (kernel == NULL) +		return -1; + +	if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0)  		return -1; -	if (symbol_conf.use_modules && -	    perf_session__create_module_maps(self) < 0) -		pr_debug("Failed to load list of modules for session %s, " -			 "continuing...\n", self->filename); +	if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) +		pr_debug("Problems creating module maps, continuing anyway...\n");  	/*  	 * Now that we have all the maps created, just set the ->end of them:  	 */ -	map_groups__fixup_end(&self->kmaps); +	map_groups__fixup_end(self);  	return 0;  } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 8aded2356f7..280dadd32a0 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -8,6 +8,8 @@  #include <linux/rbtree.h>  #include "event.h" +#define DEBUG_CACHE_DIR ".debug" +  #ifdef HAVE_CPLUS_DEMANGLE  extern char *cplus_demangle(const char *, int); @@ -49,6 +51,8 @@ struct symbol {  	char		name[0];  }; +void symbol__delete(struct symbol *self); +  struct strlist;  struct symbol_conf { @@ -58,7 +62,8 @@ struct symbol_conf {  			sort_by_name,  			show_nr_samples,  			use_callchain, -			exclude_other; +			exclude_other, +			full_paths;  	const char	*vmlinux_name,  			*field_sep;  	char            *dso_list_str, @@ -77,6 +82,12 @@ static inline void *symbol__priv(struct symbol *self)  	return ((void *)self) - symbol_conf.priv_size;  } +struct ref_reloc_sym { +	const char	*name; +	u64		addr; +	u64		unrelocated_addr; +}; +  struct addr_location {  	struct thread *thread;  	struct map    *map; @@ -94,6 +105,7 @@ struct dso {  	u8		 slen_calculated:1;  	u8		 has_build_id:1;  	u8		 kernel:1; +	u8		 hit:1;  	unsigned char	 origin;  	u8		 sorted_by_name;  	u8		 loaded; @@ -105,37 +117,55 @@ struct dso {  };  struct dso *dso__new(const char *name); +struct dso *dso__new_kernel(const char *name);  void dso__delete(struct dso *self);  bool dso__loaded(const struct dso *self, enum map_type type);  bool dso__sorted_by_name(const struct dso *self, enum map_type type); +static inline void dso__set_loaded(struct dso *self, enum map_type type) +{ +	self->loaded |= (1 << type); +} +  void dso__sort_by_name(struct dso *self, enum map_type type); -struct perf_session; +extern struct list_head dsos__user, dsos__kernel; + +struct dso *__dsos__findnew(struct list_head *head, const char *name); + +static inline struct dso *dsos__findnew(const char *name) +{ +	return __dsos__findnew(&dsos__user, name); +} -struct dso *dsos__findnew(const char *name); -int dso__load(struct dso *self, struct map *map, struct perf_session *session, -	      symbol_filter_t filter); +int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); +int dso__load_vmlinux_path(struct dso *self, struct map *map, +			   symbol_filter_t filter); +int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, +		       symbol_filter_t filter);  void dsos__fprintf(FILE *fp); -size_t dsos__fprintf_buildid(FILE *fp); +size_t dsos__fprintf_buildid(FILE *fp, bool with_hits);  size_t dso__fprintf_buildid(struct dso *self, FILE *fp);  size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);  char dso__symtab_origin(const struct dso *self); +void dso__set_long_name(struct dso *self, char *name);  void dso__set_build_id(struct dso *self, void *build_id); +void dso__read_running_kernel_build_id(struct dso *self);  struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);  struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,  					const char *name);  int filename__read_build_id(const char *filename, void *bf, size_t size);  int sysfs__read_build_id(const char *filename, void *bf, size_t size); -bool dsos__read_build_ids(void); -int build_id__sprintf(u8 *self, int len, char *bf); +bool dsos__read_build_ids(bool with_hits); +int build_id__sprintf(const u8 *self, int len, char *bf); +int kallsyms__parse(const char *filename, void *arg, +		    int (*process_symbol)(void *arg, const char *name, +					  char type, u64 start));  int symbol__init(void); -int perf_session__create_kernel_maps(struct perf_session *self); +bool symbol_type__is_a(char symbol_type, enum map_type map_type); -extern struct list_head dsos__user, dsos__kernel; -extern struct dso *vdso;  #endif /* __PERF_SYMBOL */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 4a08dcf50b6..21b92162282 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -31,12 +31,41 @@ static struct thread *thread__new(pid_t pid)  	return self;  } +static void map_groups__flush(struct map_groups *self) +{ +	int type; + +	for (type = 0; type < MAP__NR_TYPES; type++) { +		struct rb_root *root = &self->maps[type]; +		struct rb_node *next = rb_first(root); + +		while (next) { +			struct map *pos = rb_entry(next, struct map, rb_node); +			next = rb_next(&pos->rb_node); +			rb_erase(&pos->rb_node, root); +			/* +			 * We may have references to this map, for +			 * instance in some hist_entry instances, so +			 * just move them to a separate list. +			 */ +			list_add_tail(&pos->node, &self->removed_maps[pos->type]); +		} +	} +} +  int thread__set_comm(struct thread *self, const char *comm)  { +	int err; +  	if (self->comm)  		free(self->comm);  	self->comm = strdup(comm); -	return self->comm ? 0 : -ENOMEM; +	err = self->comm == NULL ? -ENOMEM : 0; +	if (!err) { +		self->comm_set = true; +		map_groups__flush(&self->mg); +	} +	return err;  }  int thread__comm_len(struct thread *self) @@ -50,11 +79,6 @@ int thread__comm_len(struct thread *self)  	return self->comm_len;  } -static const char *map_type__name[MAP__NR_TYPES] = { -	[MAP__FUNCTION] = "Functions", -	[MAP__VARIABLE] = "Variables", -}; -  static size_t __map_groups__fprintf_maps(struct map_groups *self,  					 enum map_type type, FILE *fp)  { @@ -255,11 +279,14 @@ int thread__fork(struct thread *self, struct thread *parent)  {  	int i; -	if (self->comm) -		free(self->comm); -	self->comm = strdup(parent->comm); -	if (!self->comm) -		return -ENOMEM; +	if (parent->comm_set) { +		if (self->comm) +			free(self->comm); +		self->comm = strdup(parent->comm); +		if (!self->comm) +			return -ENOMEM; +		self->comm_set = true; +	}  	for (i = 0; i < MAP__NR_TYPES; ++i)  		if (map_groups__clone(&self->mg, &parent->mg, i) < 0) @@ -282,14 +309,13 @@ size_t perf_session__fprintf(struct perf_session *self, FILE *fp)  }  struct symbol *map_groups__find_symbol(struct map_groups *self, -				       struct perf_session *session,  				       enum map_type type, u64 addr,  				       symbol_filter_t filter)  {  	struct map *map = map_groups__find(self, type, addr);  	if (map != NULL) -		return map__find_symbol(map, session, map->map_ip(map, addr), filter); +		return map__find_symbol(map, map->map_ip(map, addr), filter);  	return NULL;  } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index c206f72c888..0a28f39de54 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -15,6 +15,7 @@ struct thread {  	struct map_groups	mg;  	pid_t			pid;  	char			shortname[3]; +	bool			comm_set;  	char			*comm;  	int			comm_len;  }; @@ -48,23 +49,36 @@ static inline struct map *thread__find_map(struct thread *self,  	return self ? map_groups__find(&self->mg, type, addr) : NULL;  } +void thread__find_addr_map(struct thread *self, +			   struct perf_session *session, u8 cpumode, +			   enum map_type type, u64 addr, +			   struct addr_location *al); +  void thread__find_addr_location(struct thread *self,  				struct perf_session *session, u8 cpumode,  				enum map_type type, u64 addr,  				struct addr_location *al,  				symbol_filter_t filter);  struct symbol *map_groups__find_symbol(struct map_groups *self, -				       struct perf_session *session,  				       enum map_type type, u64 addr,  				       symbol_filter_t filter); -static inline struct symbol * -map_groups__find_function(struct map_groups *self, struct perf_session *session, -			  u64 addr, symbol_filter_t filter) +static inline struct symbol *map_groups__find_function(struct map_groups *self, +						       u64 addr, +						       symbol_filter_t filter)  { -	return map_groups__find_symbol(self, session, MAP__FUNCTION, addr, filter); +	return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter);  }  struct map *map_groups__find_by_name(struct map_groups *self,  				     enum map_type type, const char *name); + +int __map_groups__create_kernel_maps(struct map_groups *self, +				     struct map *vmlinux_maps[MAP__NR_TYPES], +				     struct dso *kernel); +int map_groups__create_kernel_maps(struct map_groups *self, +				   struct map *vmlinux_maps[MAP__NR_TYPES]); + +struct map *map_groups__new_module(struct map_groups *self, u64 start, +				   const char *filename);  #endif	/* __PERF_THREAD_H */ diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index cace3559553..5ea8973ad33 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -20,6 +20,7 @@   */  #define _GNU_SOURCE  #include <dirent.h> +#include <mntent.h>  #include <stdio.h>  #include <stdlib.h>  #include <string.h> @@ -37,6 +38,7 @@  #include "../perf.h"  #include "trace-event.h" +#include "debugfs.h"  #define VERSION "0.5" @@ -101,32 +103,12 @@ void *malloc_or_die(unsigned int size)  static const char *find_debugfs(void)  { -	static char debugfs[MAX_PATH+1]; -	static int debugfs_found; -	char type[100]; -	FILE *fp; +	const char *path = debugfs_mount(NULL); -	if (debugfs_found) -		return debugfs; +	if (!path) +		die("Your kernel not support debugfs filesystem"); -	if ((fp = fopen("/proc/mounts","r")) == NULL) -		die("Can't open /proc/mounts for read"); - -	while (fscanf(fp, "%*s %" -		      STR(MAX_PATH) -		      "s %99s %*s %*d %*d\n", -		      debugfs, type) == 2) { -		if (strcmp(type, "debugfs") == 0) -			break; -	} -	fclose(fp); - -	if (strcmp(type, "debugfs") != 0) -		die("debugfs not mounted, please mount"); - -	debugfs_found = 1; - -	return debugfs; +	return path;  }  /* @@ -271,6 +253,8 @@ static void read_header_files(void)  	write_or_die("header_page", 12);  	write_or_die(&size, 8);  	check_size = copy_file_fd(fd); +	close(fd); +  	if (size != check_size)  		die("wrong size for '%s' size=%lld read=%lld",  		    path, size, check_size); @@ -289,6 +273,7 @@ static void read_header_files(void)  	if (size != check_size)  		die("wrong size for '%s'", path);  	put_tracing_file(path); +	close(fd);  }  static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) @@ -317,7 +302,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)  		die("can't read directory '%s'", sys);  	while ((dent = readdir(dir))) { -		if (strcmp(dent->d_name, ".") == 0 || +		if (dent->d_type != DT_DIR || +		    strcmp(dent->d_name, ".") == 0 ||  		    strcmp(dent->d_name, "..") == 0 ||  		    !name_in_tp_list(dent->d_name, tps))  			continue; @@ -334,7 +320,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)  	rewinddir(dir);  	while ((dent = readdir(dir))) { -		if (strcmp(dent->d_name, ".") == 0 || +		if (dent->d_type != DT_DIR || +		    strcmp(dent->d_name, ".") == 0 ||  		    strcmp(dent->d_name, "..") == 0 ||  		    !name_in_tp_list(dent->d_name, tps))  			continue; @@ -353,6 +340,7 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)  		free(format);  	} +	closedir(dir);  }  static void read_ftrace_files(struct tracepoint_path *tps) @@ -394,26 +382,21 @@ static void read_event_files(struct tracepoint_path *tps)  		die("can't read directory '%s'", path);  	while ((dent = readdir(dir))) { -		if (strcmp(dent->d_name, ".") == 0 || +		if (dent->d_type != DT_DIR || +		    strcmp(dent->d_name, ".") == 0 ||  		    strcmp(dent->d_name, "..") == 0 ||  		    strcmp(dent->d_name, "ftrace") == 0 ||  		    !system_in_tp_list(dent->d_name, tps))  			continue; -		sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); -		sprintf(sys, "%s/%s", path, dent->d_name); -		ret = stat(sys, &st); -		free(sys); -		if (ret < 0) -			continue; -		if (S_ISDIR(st.st_mode)) -			count++; +		count++;  	}  	write_or_die(&count, 4);  	rewinddir(dir);  	while ((dent = readdir(dir))) { -		if (strcmp(dent->d_name, ".") == 0 || +		if (dent->d_type != DT_DIR || +		    strcmp(dent->d_name, ".") == 0 ||  		    strcmp(dent->d_name, "..") == 0 ||  		    strcmp(dent->d_name, "ftrace") == 0 ||  		    !system_in_tp_list(dent->d_name, tps)) @@ -422,14 +405,13 @@ static void read_event_files(struct tracepoint_path *tps)  		sprintf(sys, "%s/%s", path, dent->d_name);  		ret = stat(sys, &st);  		if (ret >= 0) { -			if (S_ISDIR(st.st_mode)) { -				write_or_die(dent->d_name, strlen(dent->d_name) + 1); -				copy_event_system(sys, tps); -			} +			write_or_die(dent->d_name, strlen(dent->d_name) + 1); +			copy_event_system(sys, tps);  		}  		free(sys);  	} +	closedir(dir);  	put_tracing_file(path);  } @@ -533,7 +515,7 @@ int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events)  	write_or_die(buf, 1);  	/* save page_size */ -	page_size = getpagesize(); +	page_size = sysconf(_SC_PAGESIZE);  	write_or_die(&page_size, 4);  	read_header_files(); diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index c5c32be040b..9b3c20f42f9 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -1925,6 +1925,15 @@ void *raw_field_ptr(struct event *event, const char *name, void *data)  	if (!field)  		return NULL; +	if (field->flags & FIELD_IS_STRING) { +		int offset; + +		offset = *(int *)(data + field->offset); +		offset &= 0xffff; + +		return data + offset; +	} +  	return data + field->offset;  } @@ -3277,3 +3286,18 @@ void parse_set_info(int nr_cpus, int long_sz)  	cpus = nr_cpus;  	long_size = long_sz;  } + +int common_pc(struct scripting_context *context) +{ +	return parse_common_pc(context->event_data); +} + +int common_flags(struct scripting_context *context) +{ +	return parse_common_flags(context->event_data); +} + +int common_lock_depth(struct scripting_context *context) +{ +	return parse_common_lock_depth(context->event_data); +} diff --git a/tools/perf/util/trace-event-perl.h b/tools/perf/util/trace-event-perl.h deleted file mode 100644 index e88fb26137b..00000000000 --- a/tools/perf/util/trace-event-perl.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef __PERF_TRACE_EVENT_PERL_H -#define __PERF_TRACE_EVENT_PERL_H -#ifdef NO_LIBPERL -typedef int INTERP; -#define dSP -#define ENTER -#define SAVETMPS -#define PUTBACK -#define SPAGAIN -#define FREETMPS -#define LEAVE -#define SP -#define ERRSV -#define G_SCALAR		(0) -#define G_DISCARD		(0) -#define G_NOARGS		(0) -#define PUSHMARK(a) -#define SvTRUE(a)		(0) -#define XPUSHs(s) -#define sv_2mortal(a) -#define newSVpv(a,b) -#define newSVuv(a) -#define newSViv(a) -#define get_cv(a,b)		(0) -#define call_pv(a,b)		(0) -#define perl_alloc()		(0) -#define perl_construct(a)	(0) -#define perl_parse(a,b,c,d,e)	(0) -#define perl_run(a)		(0) -#define perl_destruct(a)	(0) -#define perl_free(a)		(0) -#define pTHX			void -#define CV			void -#define dXSUB_SYS -#define pTHX_ -static inline void newXS(const char *a, void *b, const char *c) {} -static void boot_Perf__Trace__Context(pTHX_ CV *cv) {} -static void boot_DynaLoader(pTHX_ CV *cv) {} -#else -#include <EXTERN.h> -#include <perl.h> -void boot_Perf__Trace__Context(pTHX_ CV *cv); -void boot_DynaLoader(pTHX_ CV *cv); -typedef PerlInterpreter * INTERP; -#endif - -struct scripting_context { -	void *event_data; -}; - -int common_pc(struct scripting_context *context); -int common_flags(struct scripting_context *context); -int common_lock_depth(struct scripting_context *context); - -#endif /* __PERF_TRACE_EVENT_PERL_H */ diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 1744422cafc..7cd1193918c 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -18,7 +18,7 @@   *   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   */ -#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64  #include <dirent.h>  #include <stdio.h> @@ -83,7 +83,7 @@ static char *read_string(void)  	char *str = NULL;  	int size = 0;  	int i; -	int r; +	off_t r;  	for (;;) {  		r = read(input_fd, buf, BUFSIZ); @@ -118,7 +118,7 @@ static char *read_string(void)  	/* move the file descriptor to the end of the string */  	r = lseek(input_fd, -(r - i), SEEK_CUR); -	if (r < 0) +	if (r == (off_t)-1)  		die("lseek");  	if (str) { @@ -282,8 +282,8 @@ static void update_cpu_data_index(int cpu)  static void get_next_page(int cpu)  { -	off64_t save_seek; -	off64_t ret; +	off_t save_seek; +	off_t ret;  	if (!cpu_data[cpu].page)  		return; @@ -298,17 +298,17 @@ static void get_next_page(int cpu)  		update_cpu_data_index(cpu);  		/* other parts of the code may expect the pointer to not move */ -		save_seek = lseek64(input_fd, 0, SEEK_CUR); +		save_seek = lseek(input_fd, 0, SEEK_CUR); -		ret = lseek64(input_fd, cpu_data[cpu].offset, SEEK_SET); -		if (ret < 0) +		ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET); +		if (ret == (off_t)-1)  			die("failed to lseek");  		ret = read(input_fd, cpu_data[cpu].page, page_size);  		if (ret < 0)  			die("failed to read page");  		/* reset the file pointer back */ -		lseek64(input_fd, save_seek, SEEK_SET); +		lseek(input_fd, save_seek, SEEK_SET);  		return;  	} diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c new file mode 100644 index 00000000000..7ea983acfae --- /dev/null +++ b/tools/perf/util/trace-event-scripting.c @@ -0,0 +1,167 @@ +/* + * trace-event-scripting.  Scripting engine common and initialization code. + * + * Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "../perf.h" +#include "util.h" +#include "trace-event.h" + +struct scripting_context *scripting_context; + +static int stop_script_unsupported(void) +{ +	return 0; +} + +static void process_event_unsupported(int cpu __unused, +				      void *data __unused, +				      int size __unused, +				      unsigned long long nsecs __unused, +				      char *comm __unused) +{ +} + +static void print_python_unsupported_msg(void) +{ +	fprintf(stderr, "Python scripting not supported." +		"  Install libpython and rebuild perf to enable it.\n" +		"For example:\n  # apt-get install python-dev (ubuntu)" +		"\n  # yum install python-devel (Fedora)" +		"\n  etc.\n"); +} + +static int python_start_script_unsupported(const char *script __unused, +					   int argc __unused, +					   const char **argv __unused) +{ +	print_python_unsupported_msg(); + +	return -1; +} + +static int python_generate_script_unsupported(const char *outfile __unused) +{ +	print_python_unsupported_msg(); + +	return -1; +} + +struct scripting_ops python_scripting_unsupported_ops = { +	.name = "Python", +	.start_script = python_start_script_unsupported, +	.stop_script = stop_script_unsupported, +	.process_event = process_event_unsupported, +	.generate_script = python_generate_script_unsupported, +}; + +static void register_python_scripting(struct scripting_ops *scripting_ops) +{ +	int err; +	err = script_spec_register("Python", scripting_ops); +	if (err) +		die("error registering Python script extension"); + +	err = script_spec_register("py", scripting_ops); +	if (err) +		die("error registering py script extension"); + +	scripting_context = malloc(sizeof(struct scripting_context)); +} + +#ifdef NO_LIBPYTHON +void setup_python_scripting(void) +{ +	register_python_scripting(&python_scripting_unsupported_ops); +} +#else +struct scripting_ops python_scripting_ops; + +void setup_python_scripting(void) +{ +	register_python_scripting(&python_scripting_ops); +} +#endif + +static void print_perl_unsupported_msg(void) +{ +	fprintf(stderr, "Perl scripting not supported." +		"  Install libperl and rebuild perf to enable it.\n" +		"For example:\n  # apt-get install libperl-dev (ubuntu)" +		"\n  # yum install 'perl(ExtUtils::Embed)' (Fedora)" +		"\n  etc.\n"); +} + +static int perl_start_script_unsupported(const char *script __unused, +					 int argc __unused, +					 const char **argv __unused) +{ +	print_perl_unsupported_msg(); + +	return -1; +} + +static int perl_generate_script_unsupported(const char *outfile __unused) +{ +	print_perl_unsupported_msg(); + +	return -1; +} + +struct scripting_ops perl_scripting_unsupported_ops = { +	.name = "Perl", +	.start_script = perl_start_script_unsupported, +	.stop_script = stop_script_unsupported, +	.process_event = process_event_unsupported, +	.generate_script = perl_generate_script_unsupported, +}; + +static void register_perl_scripting(struct scripting_ops *scripting_ops) +{ +	int err; +	err = script_spec_register("Perl", scripting_ops); +	if (err) +		die("error registering Perl script extension"); + +	err = script_spec_register("pl", scripting_ops); +	if (err) +		die("error registering pl script extension"); + +	scripting_context = malloc(sizeof(struct scripting_context)); +} + +#ifdef NO_LIBPERL +void setup_perl_scripting(void) +{ +	register_perl_scripting(&perl_scripting_unsupported_ops); +} +#else +struct scripting_ops perl_scripting_ops; + +void setup_perl_scripting(void) +{ +	register_perl_scripting(&perl_scripting_ops); +} +#endif diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 6ad405620c9..c3269b937db 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -279,7 +279,15 @@ struct scripting_ops {  int script_spec_register(const char *spec, struct scripting_ops *ops); -extern struct scripting_ops perl_scripting_ops;  void setup_perl_scripting(void); +void setup_python_scripting(void); + +struct scripting_context { +	void *event_data; +}; + +int common_pc(struct scripting_context *context); +int common_flags(struct scripting_context *context); +int common_lock_depth(struct scripting_context *context);  #endif /* __PERF_TRACE_EVENTS_H */ diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c new file mode 100644 index 00000000000..f9b890fde68 --- /dev/null +++ b/tools/perf/util/util.c @@ -0,0 +1,94 @@ +#include "util.h" +#include <sys/mman.h> + +int mkdir_p(char *path, mode_t mode) +{ +	struct stat st; +	int err; +	char *d = path; + +	if (*d != '/') +		return -1; + +	if (stat(path, &st) == 0) +		return 0; + +	while (*++d == '/'); + +	while ((d = strchr(d, '/'))) { +		*d = '\0'; +		err = stat(path, &st) && mkdir(path, mode); +		*d++ = '/'; +		if (err) +			return -1; +		while (*d == '/') +			++d; +	} +	return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; +} + +static int slow_copyfile(const char *from, const char *to) +{ +	int err = 0; +	char *line = NULL; +	size_t n; +	FILE *from_fp = fopen(from, "r"), *to_fp; + +	if (from_fp == NULL) +		goto out; + +	to_fp = fopen(to, "w"); +	if (to_fp == NULL) +		goto out_fclose_from; + +	while (getline(&line, &n, from_fp) > 0) +		if (fputs(line, to_fp) == EOF) +			goto out_fclose_to; +	err = 0; +out_fclose_to: +	fclose(to_fp); +	free(line); +out_fclose_from: +	fclose(from_fp); +out: +	return err; +} + +int copyfile(const char *from, const char *to) +{ +	int fromfd, tofd; +	struct stat st; +	void *addr; +	int err = -1; + +	if (stat(from, &st)) +		goto out; + +	if (st.st_size == 0) /* /proc? do it slowly... */ +		return slow_copyfile(from, to); + +	fromfd = open(from, O_RDONLY); +	if (fromfd < 0) +		goto out; + +	tofd = creat(to, 0755); +	if (tofd < 0) +		goto out_close_from; + +	addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0); +	if (addr == MAP_FAILED) +		goto out_close_to; + +	if (write(tofd, addr, st.st_size) == st.st_size) +		err = 0; + +	munmap(addr, st.st_size); +out_close_to: +	close(tofd); +	if (err) +		unlink(to); +out_close_from: +	close(fromfd); +out: +	return err; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c673d882588..0f5b2a6f108 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -403,4 +403,7 @@ void git_qsort(void *base, size_t nmemb, size_t size,  #endif  #endif +int mkdir_p(char *path, mode_t mode); +int copyfile(const char *from, const char *to); +  #endif diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 1c15e39f99e..cfa55d686e3 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c @@ -169,6 +169,7 @@ static void perf_read_values__display_pretty(FILE *fp,  				counterwidth[j], values->value[i][j]);  		fprintf(fp, "\n");  	} +	free(counterwidth);  }  static void perf_read_values__display_raw(FILE *fp,  |