diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
| -rw-r--r-- | tools/perf/util/annotate.c | 307 | 
1 files changed, 189 insertions, 118 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e42bf572358c..f5e77ed237e8 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -23,7 +23,7 @@  #include "dso.h"  #include "env.h"  #include "map.h" -#include "map_groups.h" +#include "maps.h"  #include "symbol.h"  #include "srcline.h"  #include "units.h" @@ -43,6 +43,7 @@  #include <linux/string.h>  #include <bpf/libbpf.h>  #include <subcmd/parse-options.h> +#include <subcmd/run-command.h>  /* FIXME: For the HE_COLORSET */  #include "ui/browser.h" @@ -242,7 +243,7 @@ static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_s  	char *endptr, *tok, *name;  	struct map *map = ms->map;  	struct addr_map_symbol target = { -		.map = map, +		.ms = { .map = map, },  	};  	ops->target.addr = strtoull(ops->raw, &endptr, 16); @@ -270,9 +271,9 @@ static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_s  find_target:  	target.addr = map__objdump_2mem(map, ops->target.addr); -	if (map_groups__find_ams(&target) == 0 && -	    map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr) -		ops->target.sym = target.sym; +	if (maps__find_ams(ms->maps, &target) == 0 && +	    map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr) +		ops->target.sym = target.ms.sym;  	return 0; @@ -331,7 +332,7 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s  	struct map *map = ms->map;  	struct symbol *sym = ms->sym;  	struct addr_map_symbol target = { -		.map = map, +		.ms = { .map = map, },  	};  	const char *c = strchr(ops->raw, ',');  	u64 start, end; @@ -390,9 +391,9 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s  	 * Actual navigation will come next, with further understanding of how  	 * the symbol searching and disassembly should be done.  	 */ -	if (map_groups__find_ams(&target) == 0 && -	    map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr) -		ops->target.sym = target.sym; +	if (maps__find_ams(ms->maps, &target) == 0 && +	    map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr) +		ops->target.sym = target.ms.sym;  	if (!ops->target.outside) {  		ops->target.offset = target.addr - start; @@ -853,6 +854,10 @@ static int __symbol__account_cycles(struct cyc_hist *ch,  			   ch[offset].start < start)  			return 0;  	} + +	if (ch[offset].num < NUM_SPARKS) +		ch[offset].cycles_spark[ch[offset].num] = cycles; +  	ch[offset].have_start = have_start;  	ch[offset].start = start;  	ch[offset].cycles += cycles; @@ -860,14 +865,15 @@ static int __symbol__account_cycles(struct cyc_hist *ch,  	return 0;  } -static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, +static int __symbol__inc_addr_samples(struct map_symbol *ms,  				      struct annotated_source *src, int evidx, u64 addr,  				      struct perf_sample *sample)  { +	struct symbol *sym = ms->sym;  	unsigned offset;  	struct sym_hist *h; -	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); +	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, ms->map->unmap_ip(ms->map, addr));  	if ((addr < sym->start || addr >= sym->end) &&  	    (addr != sym->end || sym->start != sym->end)) { @@ -934,17 +940,17 @@ alloc_histograms:  	return notes->src;  } -static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, +static int symbol__inc_addr_samples(struct map_symbol *ms,  				    struct evsel *evsel, u64 addr,  				    struct perf_sample *sample)  { +	struct symbol *sym = ms->sym;  	struct annotated_source *src;  	if (sym == NULL)  		return 0;  	src = symbol__hists(sym, evsel->evlist->core.nr_entries); -	return (src) ?  __symbol__inc_addr_samples(sym, map, src, evsel->idx, -						   addr, sample) : 0; +	return src ? __symbol__inc_addr_samples(ms, src, evsel->idx, addr, sample) : 0;  }  static int symbol__account_cycles(u64 addr, u64 start, @@ -992,17 +998,17 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,  	 * it starts on the function start.  	 */  	if (start && -		(start->sym == ams->sym || -		 (ams->sym && -		   start->addr == ams->sym->start + ams->map->start))) +		(start->ms.sym == ams->ms.sym || +		 (ams->ms.sym && +		   start->addr == ams->ms.sym->start + ams->ms.map->start)))  		saddr = start->al_addr;  	if (saddr == 0)  		pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n",  			ams->addr,  			start ? start->addr : 0, -			ams->sym ? ams->sym->start + ams->map->start : 0, +			ams->ms.sym ? ams->ms.sym->start + ams->ms.map->start : 0,  			saddr); -	err = symbol__account_cycles(ams->al_addr, saddr, ams->sym, cycles); +	err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles);  	if (err)  		pr_debug2("account_cycles failed %d\n", err);  	return err; @@ -1088,13 +1094,13 @@ void annotation__compute_ipc(struct annotation *notes, size_t size)  int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,  				 struct evsel *evsel)  { -	return symbol__inc_addr_samples(ams->sym, ams->map, evsel, ams->al_addr, sample); +	return symbol__inc_addr_samples(&ams->ms, evsel, ams->al_addr, sample);  }  int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample,  				 struct evsel *evsel, u64 ip)  { -	return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evsel, ip, sample); +	return symbol__inc_addr_samples(&he->ms, evsel, ip, sample);  }  static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms) @@ -1485,44 +1491,26 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start   * means that it's not a disassembly line so should be treated differently.   * The ops.raw part will be parsed further according to type of the instruction.   */ -static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, +static int symbol__parse_objdump_line(struct symbol *sym,  				      struct annotate_args *args, -				      int *line_nr) +				      char *parsed_line, int *line_nr)  {  	struct map *map = args->ms.map;  	struct annotation *notes = symbol__annotation(sym);  	struct disasm_line *dl; -	char *line = NULL, *parsed_line, *tmp, *tmp2; -	size_t line_len; +	char *tmp;  	s64 line_ip, offset = -1;  	regmatch_t match[2]; -	if (getline(&line, &line_len, file) < 0) -		return -1; - -	if (!line) -		return -1; - -	line_ip = -1; -	parsed_line = strim(line); -  	/* /filename:linenr ? Save line number and ignore. */  	if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) {  		*line_nr = atoi(parsed_line + match[1].rm_so);  		return 0;  	} -	tmp = skip_spaces(parsed_line); -	if (*tmp) { -		/* -		 * Parse hexa addresses followed by ':' -		 */ -		line_ip = strtoull(tmp, &tmp2, 16); -		if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') -			line_ip = -1; -	} - -	if (line_ip != -1) { +	/* Process hex address followed by ':'. */ +	line_ip = strtoull(parsed_line, &tmp, 16); +	if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') {  		u64 start = map__rip_2objdump(map, sym->start),  		    end = map__rip_2objdump(map, sym->end); @@ -1530,7 +1518,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,  		if ((u64)line_ip < start || (u64)line_ip >= end)  			offset = -1;  		else -			parsed_line = tmp2 + 1; +			parsed_line = tmp + 1;  	}  	args->offset  = offset; @@ -1539,7 +1527,6 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,  	args->ms.sym  = sym;  	dl = disasm_line__new(args); -	free(line);  	(*line_nr)++;  	if (dl == NULL) @@ -1554,13 +1541,13 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,  	/* kcore has no symbols, so add the call target symbol */  	if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.sym) {  		struct addr_map_symbol target = { -			.map = map,  			.addr = dl->ops.target.addr, +			.ms = { .map = map, },  		}; -		if (!map_groups__find_ams(&target) && -		    target.sym->start == target.al_addr) -			dl->ops.target.sym = target.sym; +		if (!maps__find_ams(args->ms.maps, &target) && +		    target.ms.sym->start == target.al_addr) +			dl->ops.target.sym = target.ms.sym;  	}  	annotation_line__add(&dl->al, ¬es->src->source); @@ -1597,10 +1584,9 @@ static void delete_last_nop(struct symbol *sym)  	}  } -int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map *map, -			      int errnum, char *buf, size_t buflen) +int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, size_t buflen)  { -	struct dso *dso = map->dso; +	struct dso *dso = ms->map->dso;  	BUG_ON(buflen == 0); @@ -1857,6 +1843,67 @@ static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused,  }  #endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) +/* + * Possibly create a new version of line with tabs expanded. Returns the + * existing or new line, storage is updated if a new line is allocated. If + * allocation fails then NULL is returned. + */ +static char *expand_tabs(char *line, char **storage, size_t *storage_len) +{ +	size_t i, src, dst, len, new_storage_len, num_tabs; +	char *new_line; +	size_t line_len = strlen(line); + +	for (num_tabs = 0, i = 0; i < line_len; i++) +		if (line[i] == '\t') +			num_tabs++; + +	if (num_tabs == 0) +		return line; + +	/* +	 * Space for the line and '\0', less the leading and trailing +	 * spaces. Each tab may introduce 7 additional spaces. +	 */ +	new_storage_len = line_len + 1 + (num_tabs * 7); + +	new_line = malloc(new_storage_len); +	if (new_line == NULL) { +		pr_err("Failure allocating memory for tab expansion\n"); +		return NULL; +	} + +	/* +	 * Copy regions starting at src and expand tabs. If there are two +	 * adjacent tabs then 'src == i', the memcpy is of size 0 and the spaces +	 * are inserted. +	 */ +	for (i = 0, src = 0, dst = 0; i < line_len && num_tabs; i++) { +		if (line[i] == '\t') { +			len = i - src; +			memcpy(&new_line[dst], &line[src], len); +			dst += len; +			new_line[dst++] = ' '; +			while (dst % 8 != 0) +				new_line[dst++] = ' '; +			src = i + 1; +			num_tabs--; +		} +	} + +	/* Expand the last region. */ +	len = line_len - src; +	memcpy(&new_line[dst], &line[src], len); +	dst += len; +	new_line[dst] = '\0'; + +	free(*storage); +	*storage = new_line; +	*storage_len = new_storage_len; +	return new_line; + +} +  static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)  {  	struct annotation_options *opts = args->options; @@ -1868,10 +1915,19 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)  	struct kcore_extract kce;  	bool delete_extract = false;  	bool decomp = false; -	int stdout_fd[2];  	int lineno = 0;  	int nline; -	pid_t pid; +	char *line; +	size_t line_len; +	const char *objdump_argv[] = { +		"/bin/sh", +		"-c", +		NULL, /* Will be the objdump command to run. */ +		"--", +		NULL, /* Will be the symfs path. */ +		NULL, +	}; +	struct child_process objdump_process;  	int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));  	if (err) @@ -1901,7 +1957,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)  		if (dso__decompress_kmodule_path(dso, symfs_filename,  						 tmp, sizeof(tmp)) < 0) -			goto out; +			return -1;  		decomp = true;  		strcpy(symfs_filename, tmp); @@ -1910,13 +1966,13 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)  	err = asprintf(&command,  		 "%s %s%s --start-address=0x%016" PRIx64  		 " --stop-address=0x%016" PRIx64 -		 " -l -d %s %s -C \"$1\" 2>/dev/null|grep -v \"$1:\"|expand", +		 " -l -d %s %s -C \"$1\"",  		 opts->objdump_path ?: "objdump",  		 opts->disassembler_style ? "-M " : "",  		 opts->disassembler_style ?: "",  		 map__rip_2objdump(map, sym->start),  		 map__rip_2objdump(map, sym->end), -		 opts->show_asm_raw ? "" : "--no-show-raw", +		 opts->show_asm_raw ? "" : "--no-show-raw-insn",  		 opts->annotate_src ? "-S" : "");  	if (err < 0) { @@ -1926,55 +1982,73 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)  	pr_debug("Executing: %s\n", command); -	err = -1; -	if (pipe(stdout_fd) < 0) { -		pr_err("Failure creating the pipe to run %s\n", command); -		goto out_free_command; -	} - -	pid = fork(); -	if (pid < 0) { -		pr_err("Failure forking to run %s\n", command); -		goto out_close_stdout; -	} +	objdump_argv[2] = command; +	objdump_argv[4] = symfs_filename; -	if (pid == 0) { -		close(stdout_fd[0]); -		dup2(stdout_fd[1], 1); -		close(stdout_fd[1]); -		execl("/bin/sh", "sh", "-c", command, "--", symfs_filename, -		      NULL); -		perror(command); -		exit(-1); +	/* Create a pipe to read from for stdout */ +	memset(&objdump_process, 0, sizeof(objdump_process)); +	objdump_process.argv = objdump_argv; +	objdump_process.out = -1; +	if (start_command(&objdump_process)) { +		pr_err("Failure starting to run %s\n", command); +		err = -1; +		goto out_free_command;  	} -	close(stdout_fd[1]); - -	file = fdopen(stdout_fd[0], "r"); +	file = fdopen(objdump_process.out, "r");  	if (!file) {  		pr_err("Failure creating FILE stream for %s\n", command);  		/*  		 * If we were using debug info should retry with  		 * original binary.  		 */ -		goto out_free_command; +		err = -1; +		goto out_close_stdout;  	} +	/* Storage for getline. */ +	line = NULL; +	line_len = 0; +  	nline = 0;  	while (!feof(file)) { +		const char *match; +		char *expanded_line; + +		if (getline(&line, &line_len, file) < 0 || !line) +			break; + +		/* Skip lines containing "filename:" */ +		match = strstr(line, symfs_filename); +		if (match && match[strlen(symfs_filename)] == ':') +			continue; + +		expanded_line = strim(line); +		expanded_line = expand_tabs(expanded_line, &line, &line_len); +		if (!expanded_line) +			break; +  		/*  		 * The source code line number (lineno) needs to be kept in  		 * across calls to symbol__parse_objdump_line(), so that it  		 * can associate it with the instructions till the next one.  		 * See disasm_line__new() and struct disasm_line::line_nr.  		 */ -		if (symbol__parse_objdump_line(sym, file, args, &lineno) < 0) +		if (symbol__parse_objdump_line(sym, args, expanded_line, +					       &lineno) < 0)  			break;  		nline++;  	} +	free(line); + +	err = finish_command(&objdump_process); +	if (err) +		pr_err("Error running %s\n", command); -	if (nline == 0) +	if (nline == 0) { +		err = -1;  		pr_err("No output from %s\n", command); +	}  	/*  	 * kallsyms does not have symbol sizes so there may a nop at the end. @@ -1984,23 +2058,21 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)  		delete_last_nop(sym);  	fclose(file); -	err = 0; + +out_close_stdout: +	close(objdump_process.out); +  out_free_command:  	free(command); -out_remove_tmp: -	close(stdout_fd[0]); +out_remove_tmp:  	if (decomp)  		unlink(symfs_filename);  	if (delete_extract)  		kcore_extract__delete(&kce); -out: -	return err; -out_close_stdout: -	close(stdout_fd[1]); -	goto out_free_command; +	return err;  }  static void calc_percent(struct sym_hist *sym_hist, @@ -2071,11 +2143,10 @@ void symbol__calc_percent(struct symbol *sym, struct evsel *evsel)  	annotation__calc_percent(notes, evsel, symbol__size(sym));  } -int symbol__annotate(struct symbol *sym, struct map *map, -		     struct evsel *evsel, size_t privsize, -		     struct annotation_options *options, -		     struct arch **parch) +int symbol__annotate(struct map_symbol *ms, struct evsel *evsel, size_t privsize, +		     struct annotation_options *options, struct arch **parch)  { +	struct symbol *sym = ms->sym;  	struct annotation *notes = symbol__annotation(sym);  	struct annotate_args args = {  		.privsize	= privsize, @@ -2105,9 +2176,8 @@ int symbol__annotate(struct symbol *sym, struct map *map,  		}  	} -	args.ms.map = map; -	args.ms.sym = sym; -	notes->start = map__rip_2objdump(map, sym->start); +	args.ms = *ms; +	notes->start = map__rip_2objdump(ms->map, sym->start);  	return symbol__disassemble(sym, &args);  } @@ -2263,10 +2333,11 @@ static int annotated_source__addr_fmt_width(struct list_head *lines, u64 start)  	return 0;  } -int symbol__annotate_printf(struct symbol *sym, struct map *map, -			    struct evsel *evsel, +int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel,  			    struct annotation_options *opts)  { +	struct map *map = ms->map; +	struct symbol *sym = ms->sym;  	struct dso *dso = map->dso;  	char *filename;  	const char *d_filename; @@ -2670,30 +2741,29 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map,  	resort_source_line(root, &tmp_root);  } -static void symbol__calc_lines(struct symbol *sym, struct map *map, -			       struct rb_root *root, +static void symbol__calc_lines(struct map_symbol *ms, struct rb_root *root,  			       struct annotation_options *opts)  { -	struct annotation *notes = symbol__annotation(sym); +	struct annotation *notes = symbol__annotation(ms->sym); -	annotation__calc_lines(notes, map, root, opts); +	annotation__calc_lines(notes, ms->map, root, opts);  } -int symbol__tty_annotate2(struct symbol *sym, struct map *map, -			  struct evsel *evsel, +int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel,  			  struct annotation_options *opts)  { -	struct dso *dso = map->dso; +	struct dso *dso = ms->map->dso; +	struct symbol *sym = ms->sym;  	struct rb_root source_line = RB_ROOT;  	struct hists *hists = evsel__hists(evsel);  	char buf[1024]; -	if (symbol__annotate2(sym, map, evsel, opts, NULL) < 0) +	if (symbol__annotate2(ms, evsel, opts, NULL) < 0)  		return -1;  	if (opts->print_lines) {  		srcline_full_filename = opts->full_path; -		symbol__calc_lines(sym, map, &source_line, opts); +		symbol__calc_lines(ms, &source_line, opts);  		print_summary(&source_line, dso->long_name);  	} @@ -2707,25 +2777,25 @@ int symbol__tty_annotate2(struct symbol *sym, struct map *map,  	return 0;  } -int symbol__tty_annotate(struct symbol *sym, struct map *map, -			 struct evsel *evsel, +int symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel,  			 struct annotation_options *opts)  { -	struct dso *dso = map->dso; +	struct dso *dso = ms->map->dso; +	struct symbol *sym = ms->sym;  	struct rb_root source_line = RB_ROOT; -	if (symbol__annotate(sym, map, evsel, 0, opts, NULL) < 0) +	if (symbol__annotate(ms, evsel, 0, opts, NULL) < 0)  		return -1;  	symbol__calc_percent(sym, evsel);  	if (opts->print_lines) {  		srcline_full_filename = opts->full_path; -		symbol__calc_lines(sym, map, &source_line, opts); +		symbol__calc_lines(ms, &source_line, opts);  		print_summary(&source_line, dso->long_name);  	} -	symbol__annotate_printf(sym, map, evsel, opts); +	symbol__annotate_printf(ms, evsel, opts);  	annotated_source__purge(symbol__annotation(sym)->src); @@ -2979,9 +3049,10 @@ void annotation_line__write(struct annotation_line *al, struct annotation *notes  				 wops->write_graph);  } -int symbol__annotate2(struct symbol *sym, struct map *map, struct evsel *evsel, +int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel,  		      struct annotation_options *options, struct arch **parch)  { +	struct symbol *sym = ms->sym;  	struct annotation *notes = symbol__annotation(sym);  	size_t size = symbol__size(sym);  	int nr_pcnt = 1, err; @@ -2993,7 +3064,7 @@ int symbol__annotate2(struct symbol *sym, struct map *map, struct evsel *evsel,  	if (perf_evsel__is_group_event(evsel))  		nr_pcnt = evsel->core.nr_members; -	err = symbol__annotate(sym, map, evsel, 0, options, parch); +	err = symbol__annotate(ms, evsel, 0, options, parch);  	if (err)  		goto out_free_offsets;  | 
