diff options
Diffstat (limited to 'tools/perf/util/symbol.c')
| -rw-r--r-- | tools/perf/util/symbol.c | 441 | 
1 files changed, 360 insertions, 81 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 78c2efde01b..5c0f42e6b33 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -9,6 +9,16 @@  const char *sym_hist_filter; +enum dso_origin { +	DSO__ORIG_KERNEL = 0, +	DSO__ORIG_JAVA_JIT, +	DSO__ORIG_FEDORA, +	DSO__ORIG_UBUNTU, +	DSO__ORIG_BUILDID, +	DSO__ORIG_DSO, +	DSO__ORIG_NOT_FOUND, +}; +  static struct symbol *symbol__new(u64 start, u64 len,  				  const char *name, unsigned int priv_size,  				  u64 obj_start, int verbose) @@ -35,7 +45,7 @@ static struct symbol *symbol__new(u64 start, u64 len,  		self = ((void *)self) + priv_size;  	}  	self->start = start; -	self->end   = start + len - 1; +	self->end   = len ? start + len - 1 : start;  	memcpy(self->name, name, namelen);  	return self; @@ -48,8 +58,12 @@ static void symbol__delete(struct symbol *self, unsigned int priv_size)  static size_t symbol__fprintf(struct symbol *self, FILE *fp)  { -	return fprintf(fp, " %llx-%llx %s\n", +	if (!self->module) +		return fprintf(fp, " %llx-%llx %s\n",  		       self->start, self->end, self->name); +	else +		return fprintf(fp, " %llx-%llx %s \t[%s]\n", +		       self->start, self->end, self->name, self->module->name);  }  struct dso *dso__new(const char *name, unsigned int sym_priv_size) @@ -61,6 +75,8 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size)  		self->syms = RB_ROOT;  		self->sym_priv_size = sym_priv_size;  		self->find_symbol = dso__find_symbol; +		self->slen_calculated = 0; +		self->origin = DSO__ORIG_NOT_FOUND;  	}  	return self; @@ -146,6 +162,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb  	char *line = NULL;  	size_t n;  	FILE *file = fopen("/proc/kallsyms", "r"); +	int count = 0;  	if (file == NULL)  		goto out_failure; @@ -188,8 +205,10 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb  		if (filter && filter(self, sym))  			symbol__delete(sym, self->sym_priv_size); -		else +		else {  			dso__insert_symbol(self, sym); +			count++; +		}  	}  	/* @@ -212,7 +231,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb  	free(line);  	fclose(file); -	return 0; +	return count;  out_delete_line:  	free(line); @@ -307,6 +326,26 @@ static inline int elf_sym__is_function(const GElf_Sym *sym)  	       sym->st_size != 0;  } +static inline int elf_sym__is_label(const GElf_Sym *sym) +{ +	return elf_sym__type(sym) == STT_NOTYPE && +		sym->st_name != 0 && +		sym->st_shndx != SHN_UNDEF && +		sym->st_shndx != SHN_ABS; +} + +static inline const char *elf_sec__name(const GElf_Shdr *shdr, +					const Elf_Data *secstrs) +{ +	return secstrs->d_buf + shdr->sh_name; +} + +static inline int elf_sec__is_text(const GElf_Shdr *shdr, +					const Elf_Data *secstrs) +{ +	return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; +} +  static inline const char *elf_sym__name(const GElf_Sym *sym,  					const Elf_Data *symstrs)  { @@ -346,36 +385,61 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,  	     idx < nr_entries; \  	     ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) -static int dso__synthesize_plt_symbols(struct  dso *self, Elf *elf, -				       GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, -				       GElf_Shdr *shdr_dynsym, -				       size_t dynsym_idx, int verbose) +/* + * We need to check if we have a .dynsym, so that we can handle the + * .plt, synthesizing its symbols, that aren't on the symtabs (be it + * .dynsym or .symtab). + * And always look at the original dso, not at debuginfo packages, that + * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). + */ +static int dso__synthesize_plt_symbols(struct  dso *self, int verbose)  {  	uint32_t nr_rel_entries, idx;  	GElf_Sym sym;  	u64 plt_offset;  	GElf_Shdr shdr_plt;  	struct symbol *f; -	GElf_Shdr shdr_rel_plt; +	GElf_Shdr shdr_rel_plt, shdr_dynsym;  	Elf_Data *reldata, *syms, *symstrs; -	Elf_Scn *scn_plt_rel, *scn_symstrs; +	Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; +	size_t dynsym_idx; +	GElf_Ehdr ehdr;  	char sympltname[1024]; -	int nr = 0, symidx; +	Elf *elf; +	int nr = 0, symidx, fd, err = 0; -	scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, +	fd = open(self->name, O_RDONLY); +	if (fd < 0) +		goto out; + +	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); +	if (elf == NULL) +		goto out_close; + +	if (gelf_getehdr(elf, &ehdr) == NULL) +		goto out_elf_end; + +	scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, +					 ".dynsym", &dynsym_idx); +	if (scn_dynsym == NULL) +		goto out_elf_end; + +	scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,  					  ".rela.plt", NULL);  	if (scn_plt_rel == NULL) { -		scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, +		scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,  						  ".rel.plt", NULL);  		if (scn_plt_rel == NULL) -			return 0; +			goto out_elf_end;  	} +	err = -1; +  	if (shdr_rel_plt.sh_link != dynsym_idx) -		return 0; +		goto out_elf_end; -	if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) -		return 0; +	if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) +		goto out_elf_end;  	/*  	 * Fetch the relocation section to find the indexes to the GOT @@ -383,19 +447,19 @@ static int dso__synthesize_plt_symbols(struct  dso *self, Elf *elf,  	 */  	reldata = elf_getdata(scn_plt_rel, NULL);  	if (reldata == NULL) -		return -1; +		goto out_elf_end;  	syms = elf_getdata(scn_dynsym, NULL);  	if (syms == NULL) -		return -1; +		goto out_elf_end; -	scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); +	scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);  	if (scn_symstrs == NULL) -		return -1; +		goto out_elf_end;  	symstrs = elf_getdata(scn_symstrs, NULL);  	if (symstrs == NULL) -		return -1; +		goto out_elf_end;  	nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;  	plt_offset = shdr_plt.sh_offset; @@ -414,7 +478,7 @@ static int dso__synthesize_plt_symbols(struct  dso *self, Elf *elf,  			f = symbol__new(plt_offset, shdr_plt.sh_entsize,  					sympltname, self->sym_priv_size, 0, verbose);  			if (!f) -				return -1; +				goto out_elf_end;  			dso__insert_symbol(self, f);  			++nr; @@ -432,25 +496,31 @@ static int dso__synthesize_plt_symbols(struct  dso *self, Elf *elf,  			f = symbol__new(plt_offset, shdr_plt.sh_entsize,  					sympltname, self->sym_priv_size, 0, verbose);  			if (!f) -				return -1; +				goto out_elf_end;  			dso__insert_symbol(self, f);  			++nr;  		} -	} else { -		/* -		 * TODO: There are still one more shdr_rel_plt.sh_type -		 * I have to investigate, but probably should be ignored. -		 */  	} -	return nr; +	err = 0; +out_elf_end: +	elf_end(elf); +out_close: +	close(fd); + +	if (err == 0) +		return nr; +out: +	fprintf(stderr, "%s: problems reading %s PLT info.\n", +		__func__, self->name); +	return 0;  }  static int dso__load_sym(struct dso *self, int fd, const char *name, -			 symbol_filter_t filter, int verbose) +			 symbol_filter_t filter, int verbose, struct module *mod)  { -	Elf_Data *symstrs; +	Elf_Data *symstrs, *secstrs;  	uint32_t nr_syms;  	int err = -1;  	uint32_t index; @@ -458,10 +528,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  	GElf_Shdr shdr;  	Elf_Data *syms;  	GElf_Sym sym; -	Elf_Scn *sec, *sec_dynsym; +	Elf_Scn *sec, *sec_strndx;  	Elf *elf; -	size_t dynsym_idx; -	int nr = 0; +	int nr = 0, kernel = !strcmp("[kernel]", self->name);  	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);  	if (elf == NULL) { @@ -477,32 +546,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  		goto out_elf_end;  	} -	/* -	 * We need to check if we have a .dynsym, so that we can handle the -	 * .plt, synthesizing its symbols, that aren't on the symtabs (be it -	 * .dynsym or .symtab) -	 */ -	sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr, -					 ".dynsym", &dynsym_idx); -	if (sec_dynsym != NULL) { -		nr = dso__synthesize_plt_symbols(self, elf, &ehdr, -						 sec_dynsym, &shdr, -						 dynsym_idx, verbose); -		if (nr < 0) -			goto out_elf_end; -	} - -	/* -	 * But if we have a full .symtab (that is a superset of .dynsym) we -	 * should add the symbols not in the .dynsyn -	 */  	sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);  	if (sec == NULL) { -		if (sec_dynsym == NULL) +		sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); +		if (sec == NULL)  			goto out_elf_end; - -		sec = sec_dynsym; -		gelf_getshdr(sec, &shdr);  	}  	syms = elf_getdata(sec, NULL); @@ -517,17 +565,34 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  	if (symstrs == NULL)  		goto out_elf_end; +	sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); +	if (sec_strndx == NULL) +		goto out_elf_end; + +	secstrs = elf_getdata(sec_strndx, NULL); +	if (secstrs == NULL) +		goto out_elf_end; +  	nr_syms = shdr.sh_size / shdr.sh_entsize;  	memset(&sym, 0, sizeof(sym)); -	self->prelinked = elf_section_by_name(elf, &ehdr, &shdr, -					      ".gnu.prelink_undo", -					      NULL) != NULL; +	if (!kernel) { +		self->adjust_symbols = (ehdr.e_type == ET_EXEC || +				elf_section_by_name(elf, &ehdr, &shdr, +						     ".gnu.prelink_undo", +						     NULL) != NULL); +	} else self->adjust_symbols = 0; +  	elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {  		struct symbol *f; +		const char *name; +		char *demangled;  		u64 obj_start; +		struct section *section = NULL; +		int is_label = elf_sym__is_label(&sym); +		const char *section_name; -		if (!elf_sym__is_function(&sym)) +		if (!is_label && !elf_sym__is_function(&sym))  			continue;  		sec = elf_getscn(elf, sym.st_shndx); @@ -535,9 +600,14 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  			goto out_elf_end;  		gelf_getshdr(sec, &shdr); + +		if (is_label && !elf_sec__is_text(&shdr, secstrs)) +			continue; + +		section_name = elf_sec__name(&shdr, secstrs);  		obj_start = sym.st_value; -		if (self->prelinked) { +		if (self->adjust_symbols) {  			if (verbose >= 2)  				printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n",  					(u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); @@ -545,15 +615,36 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  			sym.st_value -= shdr.sh_addr - shdr.sh_offset;  		} -		f = symbol__new(sym.st_value, sym.st_size, -				elf_sym__name(&sym, symstrs), +		if (mod) { +			section = mod->sections->find_section(mod->sections, section_name); +			if (section) +				sym.st_value += section->vma; +			else { +				fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n", +					mod->name, section_name); +				goto out_elf_end; +			} +		} +		/* +		 * We need to figure out if the object was created from C++ sources +		 * DWARF DW_compile_unit has this, but we don't always have access +		 * to it... +		 */ +		name = elf_sym__name(&sym, symstrs); +		demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); +		if (demangled != NULL) +			name = demangled; + +		f = symbol__new(sym.st_value, sym.st_size, name,  				self->sym_priv_size, obj_start, verbose); +		free(demangled);  		if (!f)  			goto out_elf_end;  		if (filter && filter(self, f))  			symbol__delete(f, self->sym_priv_size);  		else { +			f->module = mod;  			dso__insert_symbol(self, f);  			nr++;  		} @@ -566,44 +657,135 @@ out_close:  	return err;  } +#define BUILD_ID_SIZE 128 + +static char *dso__read_build_id(struct dso *self, int verbose) +{ +	int i; +	GElf_Ehdr ehdr; +	GElf_Shdr shdr; +	Elf_Data *build_id_data; +	Elf_Scn *sec; +	char *build_id = NULL, *bid; +	unsigned char *raw; +	Elf *elf; +	int fd = open(self->name, O_RDONLY); + +	if (fd < 0) +		goto out; + +	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); +	if (elf == NULL) { +		if (verbose) +			fprintf(stderr, "%s: cannot read %s ELF file.\n", +				__func__, self->name); +		goto out_close; +	} + +	if (gelf_getehdr(elf, &ehdr) == NULL) { +		if (verbose) +			fprintf(stderr, "%s: cannot get elf header.\n", __func__); +		goto out_elf_end; +	} + +	sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); +	if (sec == NULL) +		goto out_elf_end; + +	build_id_data = elf_getdata(sec, NULL); +	if (build_id_data == NULL) +		goto out_elf_end; +	build_id = malloc(BUILD_ID_SIZE); +	if (build_id == NULL) +		goto out_elf_end; +	raw = build_id_data->d_buf + 16; +	bid = build_id; + +	for (i = 0; i < 20; ++i) { +		sprintf(bid, "%02x", *raw); +		++raw; +		bid += 2; +	} +	if (verbose >= 2) +		printf("%s(%s): %s\n", __func__, self->name, build_id); +out_elf_end: +	elf_end(elf); +out_close: +	close(fd); +out: +	return build_id; +} + +char dso__symtab_origin(const struct dso *self) +{ +	static const char origin[] = { +		[DSO__ORIG_KERNEL] =   'k', +		[DSO__ORIG_JAVA_JIT] = 'j', +		[DSO__ORIG_FEDORA] =   'f', +		[DSO__ORIG_UBUNTU] =   'u', +		[DSO__ORIG_BUILDID] =  'b', +		[DSO__ORIG_DSO] =      'd', +	}; + +	if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) +		return '!'; +	return origin[self->origin]; +} +  int dso__load(struct dso *self, symbol_filter_t filter, int verbose)  { -	int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); -	char *name = malloc(size); -	int variant = 0; +	int size = PATH_MAX; +	char *name = malloc(size), *build_id = NULL;  	int ret = -1;  	int fd;  	if (!name)  		return -1; -	self->prelinked = 0; +	self->adjust_symbols = 0; + +	if (strncmp(self->name, "/tmp/perf-", 10) == 0) { +		ret = dso__load_perf_map(self, filter, verbose); +		self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : +					 DSO__ORIG_NOT_FOUND; +		return ret; +	} -	if (strncmp(self->name, "/tmp/perf-", 10) == 0) -		return dso__load_perf_map(self, filter, verbose); +	self->origin = DSO__ORIG_FEDORA - 1;  more:  	do { -		switch (variant) { -		case 0: /* Fedora */ +		self->origin++; +		switch (self->origin) { +		case DSO__ORIG_FEDORA:  			snprintf(name, size, "/usr/lib/debug%s.debug", self->name);  			break; -		case 1: /* Ubuntu */ +		case DSO__ORIG_UBUNTU:  			snprintf(name, size, "/usr/lib/debug%s", self->name);  			break; -		case 2: /* Sane people */ +		case DSO__ORIG_BUILDID: +			build_id = dso__read_build_id(self, verbose); +			if (build_id != NULL) { +				snprintf(name, size, +					 "/usr/lib/debug/.build-id/%.2s/%s.debug", +					build_id, build_id + 2); +				free(build_id); +				break; +			} +			self->origin++; +			/* Fall thru */ +		case DSO__ORIG_DSO:  			snprintf(name, size, "%s", self->name);  			break;  		default:  			goto out;  		} -		variant++;  		fd = open(name, O_RDONLY);  	} while (fd < 0); -	ret = dso__load_sym(self, fd, name, filter, verbose); +	ret = dso__load_sym(self, fd, name, filter, verbose, NULL);  	close(fd);  	/* @@ -612,11 +794,98 @@ more:  	if (!ret)  		goto more; +	if (ret > 0) { +		int nr_plt = dso__synthesize_plt_symbols(self, verbose); +		if (nr_plt > 0) +			ret += nr_plt; +	}  out:  	free(name); +	if (ret < 0 && strstr(self->name, " (deleted)") != NULL) +		return 0;  	return ret;  } +static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, +			     symbol_filter_t filter, int verbose) +{ +	struct module *mod = mod_dso__find_module(mods, name); +	int err = 0, fd; + +	if (mod == NULL || !mod->active) +		return err; + +	fd = open(mod->path, O_RDONLY); + +	if (fd < 0) +		return err; + +	err = dso__load_sym(self, fd, name, filter, verbose, mod); +	close(fd); + +	return err; +} + +int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose) +{ +	struct mod_dso *mods = mod_dso__new_dso("modules"); +	struct module *pos; +	struct rb_node *next; +	int err; + +	err = mod_dso__load_modules(mods); + +	if (err <= 0) +		return err; + +	/* +	 * Iterate over modules, and load active symbols. +	 */ +	next = rb_first(&mods->mods); +	while (next) { +		pos = rb_entry(next, struct module, rb_node); +		err = dso__load_module(self, mods, pos->name, filter, verbose); + +		if (err < 0) +			break; + +		next = rb_next(&pos->rb_node); +	} + +	if (err < 0) { +		mod_dso__delete_modules(mods); +		mod_dso__delete_self(mods); +	} + +	return err; +} + +static inline void dso__fill_symbol_holes(struct dso *self) +{ +	struct symbol *prev = NULL; +	struct rb_node *nd; + +	for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) { +		struct symbol *pos = rb_entry(nd, struct symbol, rb_node); + +		if (prev) { +			u64 hole = 0; +			int alias = pos->start == prev->start; + +			if (!alias) +				hole = prev->start - pos->end - 1; + +			if (hole || alias) { +				if (alias) +					pos->end = prev->end; +				else if (hole) +					pos->end = prev->start - 1; +			} +		} +		prev = pos; +	} +} +  static int dso__load_vmlinux(struct dso *self, const char *vmlinux,  			     symbol_filter_t filter, int verbose)  { @@ -625,23 +894,33 @@ static int dso__load_vmlinux(struct dso *self, const char *vmlinux,  	if (fd < 0)  		return -1; -	err = dso__load_sym(self, fd, vmlinux, filter, verbose); +	err = dso__load_sym(self, fd, vmlinux, filter, verbose, NULL); + +	if (err > 0) +		dso__fill_symbol_holes(self); +  	close(fd);  	return err;  }  int dso__load_kernel(struct dso *self, const char *vmlinux, -		     symbol_filter_t filter, int verbose) +		     symbol_filter_t filter, int verbose, int modules)  {  	int err = -1; -	if (vmlinux) +	if (vmlinux) {  		err = dso__load_vmlinux(self, vmlinux, filter, verbose); +		if (err > 0 && modules) +			err = dso__load_modules(self, filter, verbose); +	} -	if (err < 0) +	if (err <= 0)  		err = dso__load_kallsyms(self, filter, verbose); +	if (err > 0) +		self->origin = DSO__ORIG_KERNEL; +  	return err;  }  |