perf symbols: Cache /proc/kallsyms files by build-id
So that when we don't have a vmlinux handy we can store the kallsyms for later use by 'perf report'. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1263501006-14185-3-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
		
					parent
					
						
							
								8d0591f6ad
							
						
					
				
			
			
				commit
				
					
						9e201442de
					
				
			
		
					 5 changed files with 80 additions and 20 deletions
				
			
		|  | @ -245,7 +245,7 @@ int event__synthesize_kernel_mmap(event__handler_t process, | ||||||
| 	 */ | 	 */ | ||||||
| 	struct process_symbol_args args = { .name = symbol_name, }; | 	struct process_symbol_args args = { .name = symbol_name, }; | ||||||
| 
 | 
 | ||||||
| 	if (kallsyms__parse(&args, find_symbol_cb) <= 0) | 	if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) | ||||||
| 		return -ENOENT; | 		return -ENOENT; | ||||||
| 
 | 
 | ||||||
| 	size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | 	size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | ||||||
|  |  | ||||||
|  | @ -237,11 +237,13 @@ static int dso__cache_build_id(struct dso *self, const char *debugdir) | ||||||
| 	char *filename = malloc(size), | 	char *filename = malloc(size), | ||||||
| 	     *linkname = malloc(size), *targetname, *sbuild_id; | 	     *linkname = malloc(size), *targetname, *sbuild_id; | ||||||
| 	int len, err = -1; | 	int len, err = -1; | ||||||
|  | 	bool is_kallsyms = self->kernel && self->long_name[0] != '/'; | ||||||
| 
 | 
 | ||||||
| 	if (filename == NULL || linkname == NULL) | 	if (filename == NULL || linkname == NULL) | ||||||
| 		goto out_free; | 		goto out_free; | ||||||
| 
 | 
 | ||||||
| 	len = snprintf(filename, size, "%s%s", debugdir, self->long_name); | 	len = snprintf(filename, size, "%s%s%s", | ||||||
|  | 		       debugdir, is_kallsyms ? "/" : "", self->long_name); | ||||||
| 	if (mkdir_p(filename, 0755)) | 	if (mkdir_p(filename, 0755)) | ||||||
| 		goto out_free; | 		goto out_free; | ||||||
| 
 | 
 | ||||||
|  | @ -249,9 +251,14 @@ static int dso__cache_build_id(struct dso *self, const char *debugdir) | ||||||
| 	sbuild_id = filename + len; | 	sbuild_id = filename + len; | ||||||
| 	build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id); | 	build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id); | ||||||
| 
 | 
 | ||||||
| 	if (access(filename, F_OK) && link(self->long_name, filename) && | 	if (access(filename, F_OK)) { | ||||||
| 	    copyfile(self->long_name, filename)) | 		if (is_kallsyms) { | ||||||
| 		goto out_free; | 			 if (copyfile("/proc/kallsyms", filename)) | ||||||
|  | 				goto out_free; | ||||||
|  | 		} else if (link(self->long_name, filename) && | ||||||
|  | 			   copyfile(self->long_name, filename)) | ||||||
|  | 			goto out_free; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	len = snprintf(linkname, size, "%s/.build-id/%.2s", | 	len = snprintf(linkname, size, "%s/.build-id/%.2s", | ||||||
| 		       debugdir, sbuild_id); | 		       debugdir, sbuild_id); | ||||||
|  |  | ||||||
|  | @ -383,13 +383,14 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int kallsyms__parse(void *arg, int (*process_symbol)(void *arg, const char *name, | int kallsyms__parse(const char *filename, void *arg, | ||||||
|  | 		    int (*process_symbol)(void *arg, const char *name, | ||||||
| 						     char type, u64 start)) | 						     char type, u64 start)) | ||||||
| { | { | ||||||
| 	char *line = NULL; | 	char *line = NULL; | ||||||
| 	size_t n; | 	size_t n; | ||||||
| 	int err = 0; | 	int err = 0; | ||||||
| 	FILE *file = fopen("/proc/kallsyms", "r"); | 	FILE *file = fopen(filename, "r"); | ||||||
| 
 | 
 | ||||||
| 	if (file == NULL) | 	if (file == NULL) | ||||||
| 		goto out_failure; | 		goto out_failure; | ||||||
|  | @ -466,10 +467,11 @@ static int map__process_kallsym_symbol(void *arg, const char *name, | ||||||
|  * so that we can in the next step set the symbol ->end address and then |  * so that we can in the next step set the symbol ->end address and then | ||||||
|  * call kernel_maps__split_kallsyms. |  * call kernel_maps__split_kallsyms. | ||||||
|  */ |  */ | ||||||
| static int dso__load_all_kallsyms(struct dso *self, struct map *map) | static int dso__load_all_kallsyms(struct dso *self, const char *filename, | ||||||
|  | 				  struct map *map) | ||||||
| { | { | ||||||
| 	struct process_kallsyms_args args = { .map = map, .dso = self, }; | 	struct process_kallsyms_args args = { .map = map, .dso = self, }; | ||||||
| 	return kallsyms__parse(&args, map__process_kallsym_symbol); | 	return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -556,10 +558,10 @@ discard_symbol:		rb_erase(&pos->rb_node, root); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static int dso__load_kallsyms(struct dso *self, struct map *map, | static int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, | ||||||
| 			      struct perf_session *session, symbol_filter_t filter) | 			      struct perf_session *session, symbol_filter_t filter) | ||||||
| { | { | ||||||
| 	if (dso__load_all_kallsyms(self, map) < 0) | 	if (dso__load_all_kallsyms(self, filename, map) < 0) | ||||||
| 		return -1; | 		return -1; | ||||||
| 
 | 
 | ||||||
| 	symbols__fixup_end(&self->symbols[map->type]); | 	symbols__fixup_end(&self->symbols[map->type]); | ||||||
|  | @ -1580,7 +1582,8 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | ||||||
| 				struct perf_session *session, symbol_filter_t filter) | 				struct perf_session *session, symbol_filter_t filter) | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
| 	bool is_kallsyms; | 	const char *kallsyms_filename = NULL; | ||||||
|  | 	char *kallsyms_allocated_filename = NULL; | ||||||
| 
 | 
 | ||||||
| 	if (vmlinux_path != NULL) { | 	if (vmlinux_path != NULL) { | ||||||
| 		int i; | 		int i; | ||||||
|  | @ -1606,19 +1609,37 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | ||||||
| 	 */ | 	 */ | ||||||
| 	if (self->has_build_id) { | 	if (self->has_build_id) { | ||||||
| 		u8 kallsyms_build_id[BUILD_ID_SIZE]; | 		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, | 		if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||||||
| 					 sizeof(kallsyms_build_id)) == 0) { | 					 sizeof(kallsyms_build_id)) == 0) { | ||||||
| 			is_kallsyms = dso__build_id_equal(self, kallsyms_build_id); | 			if (dso__build_id_equal(self, kallsyms_build_id)) { | ||||||
| 			if (is_kallsyms) | 				kallsyms_filename = "/proc/kallsyms"; | ||||||
| 				goto do_kallsyms; | 				goto do_kallsyms; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		build_id__sprintf(self->build_id, sizeof(self->build_id), | ||||||
|  | 				  sbuild_id); | ||||||
|  | 
 | ||||||
|  | 		if (asprintf(&kallsyms_allocated_filename, | ||||||
|  | 			     "%s/.debug/[kernel.kallsyms]/%s", | ||||||
|  | 			     getenv("HOME"), sbuild_id) != -1) { | ||||||
|  | 			if (access(kallsyms_filename, F_OK)) { | ||||||
|  | 				kallsyms_filename = kallsyms_allocated_filename; | ||||||
|  | 				goto do_kallsyms; | ||||||
|  | 			} | ||||||
|  | 			free(kallsyms_allocated_filename); | ||||||
|  | 			kallsyms_allocated_filename = NULL; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		goto do_vmlinux; | 		goto do_vmlinux; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	is_kallsyms = self->long_name[0] == '['; | 	if (self->long_name[0] == '[') { | ||||||
| 	if (is_kallsyms) | 		kallsyms_filename = "/proc/kallsyms"; | ||||||
| 		goto do_kallsyms; | 		goto do_kallsyms; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| do_vmlinux: | do_vmlinux: | ||||||
| 	err = dso__load_vmlinux(self, map, session, self->long_name, filter); | 	err = dso__load_vmlinux(self, map, session, self->long_name, filter); | ||||||
|  | @ -1629,9 +1650,10 @@ do_vmlinux: | ||||||
| 		pr_info("The file %s cannot be used, " | 		pr_info("The file %s cannot be used, " | ||||||
| 			"trying to use /proc/kallsyms...", self->long_name); | 			"trying to use /proc/kallsyms...", self->long_name); | ||||||
| do_kallsyms: | do_kallsyms: | ||||||
| 		err = dso__load_kallsyms(self, map, session, filter); | 		err = dso__load_kallsyms(self, kallsyms_filename, map, session, filter); | ||||||
| 		if (err > 0 && !is_kallsyms) | 		if (err > 0 && kallsyms_filename == NULL) | ||||||
|                         dso__set_long_name(self, strdup("[kernel.kallsyms]")); |                         dso__set_long_name(self, strdup("[kernel.kallsyms]")); | ||||||
|  | 		free(kallsyms_allocated_filename); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (err > 0) { | 	if (err > 0) { | ||||||
|  |  | ||||||
|  | @ -144,8 +144,9 @@ 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); | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | ||||||
| bool dsos__read_build_ids(void); | bool dsos__read_build_ids(void); | ||||||
| int build_id__sprintf(u8 *self, int len, char *bf); | int build_id__sprintf(u8 *self, int len, char *bf); | ||||||
| int kallsyms__parse(void *arg, int (*process_symbol)(void *arg, const char *name, | int kallsyms__parse(const char *filename, void *arg, | ||||||
| 						     char type, u64 start)); | 		    int (*process_symbol)(void *arg, const char *name, | ||||||
|  | 					  char type, u64 start)); | ||||||
| 
 | 
 | ||||||
| int symbol__init(void); | int symbol__init(void); | ||||||
| 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); | ||||||
|  |  | ||||||
|  | @ -32,6 +32,33 @@ int mkdir_p(char *path, mode_t mode) | ||||||
| 	return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; | 	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 copyfile(const char *from, const char *to) | ||||||
| { | { | ||||||
| 	int fromfd, tofd; | 	int fromfd, tofd; | ||||||
|  | @ -42,6 +69,9 @@ int copyfile(const char *from, const char *to) | ||||||
| 	if (stat(from, &st)) | 	if (stat(from, &st)) | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
|  | 	if (st.st_size == 0) /* /proc? do it slowly... */ | ||||||
|  | 		return slow_copyfile(from, to); | ||||||
|  | 
 | ||||||
| 	fromfd = open(from, O_RDONLY); | 	fromfd = open(from, O_RDONLY); | ||||||
| 	if (fromfd < 0) | 	if (fromfd < 0) | ||||||
| 		goto out; | 		goto out; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Arnaldo Carvalho de Melo
				Arnaldo Carvalho de Melo