diff options
Diffstat (limited to 'tools/perf/util')
123 files changed, 9204 insertions, 3287 deletions
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 15a77b7c0e3..39f17507578 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -19,6 +19,9 @@ if test -d ../../.git -o -f ../../.git  then  	TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null )  	CID=$(git log -1 --abbrev=4 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID" +elif test -f ../../PERF-VERSION-FILE +then +	TAG=$(cut -d' ' -f3 ../../PERF-VERSION-FILE | sed -e 's/\"//g')  fi  if test -z "$TAG"  then @@ -40,7 +43,7 @@ else  	VC=unset  fi  test "$VN" = "$VC" || { -	echo >&2 "PERF_VERSION = $VN" +	echo >&2 "  PERF_VERSION = $VN"  	echo "#define PERF_VERSION \"$VN\"" >$GVF  } diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c index e6d134773d0..c0b43ee40d9 100644 --- a/tools/perf/util/alias.c +++ b/tools/perf/util/alias.c @@ -55,8 +55,7 @@ int split_cmdline(char *cmdline, const char ***argv)  				src++;  				c = cmdline[src];  				if (!c) { -					free(*argv); -					*argv = NULL; +					zfree(argv);  					return error("cmdline ends with \\");  				}  			} @@ -68,8 +67,7 @@ int split_cmdline(char *cmdline, const char ***argv)  	cmdline[dst] = 0;  	if (quoted) { -		free(*argv); -		*argv = NULL; +		zfree(argv);  		return error("unclosed quote");  	} diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index bfc5a27597d..809b4c50bea 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -8,6 +8,8 @@   */  #include "util.h" +#include "ui/ui.h" +#include "sort.h"  #include "build-id.h"  #include "color.h"  #include "cache.h" @@ -26,10 +28,10 @@ static int disasm_line__parse(char *line, char **namep, char **rawp);  static void ins__delete(struct ins_operands *ops)  { -	free(ops->source.raw); -	free(ops->source.name); -	free(ops->target.raw); -	free(ops->target.name); +	zfree(&ops->source.raw); +	zfree(&ops->source.name); +	zfree(&ops->target.raw); +	zfree(&ops->target.name);  }  static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, @@ -185,8 +187,7 @@ static int lock__parse(struct ins_operands *ops)  	return 0;  out_free_ops: -	free(ops->locked.ops); -	ops->locked.ops = NULL; +	zfree(&ops->locked.ops);  	return 0;  } @@ -205,9 +206,9 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size,  static void lock__delete(struct ins_operands *ops)  { -	free(ops->locked.ops); -	free(ops->target.raw); -	free(ops->target.name); +	zfree(&ops->locked.ops); +	zfree(&ops->target.raw); +	zfree(&ops->target.name);  }  static struct ins_ops lock_ops = { @@ -256,8 +257,7 @@ static int mov__parse(struct ins_operands *ops)  	return 0;  out_free_source: -	free(ops->source.raw); -	ops->source.raw = NULL; +	zfree(&ops->source.raw);  	return -1;  } @@ -464,17 +464,12 @@ void symbol__annotate_zero_histograms(struct symbol *sym)  	pthread_mutex_unlock(¬es->lock);  } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, -			     int evidx, u64 addr) +static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, +				      struct annotation *notes, int evidx, u64 addr)  {  	unsigned offset; -	struct annotation *notes;  	struct sym_hist *h; -	notes = symbol__annotation(sym); -	if (notes->src == NULL) -		return -ENOMEM; -  	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));  	if (addr < sym->start || addr > sym->end) @@ -491,6 +486,33 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,  	return 0;  } +static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, +				    int evidx, u64 addr) +{ +	struct annotation *notes; + +	if (sym == NULL) +		return 0; + +	notes = symbol__annotation(sym); +	if (notes->src == NULL) { +		if (symbol__alloc_hist(sym) < 0) +			return -ENOMEM; +	} + +	return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); +} + +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) +{ +	return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); +} + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) +{ +	return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); +} +  static void disasm_line__init_ins(struct disasm_line *dl)  {  	dl->ins = ins__find(dl->name); @@ -538,8 +560,7 @@ static int disasm_line__parse(char *line, char **namep, char **rawp)  	return 0;  out_free_name: -	free(*namep); -	*namep = NULL; +	zfree(namep);  	return -1;  } @@ -564,7 +585,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs  	return dl;  out_free_line: -	free(dl->line); +	zfree(&dl->line);  out_delete:  	free(dl);  	return NULL; @@ -572,8 +593,8 @@ out_delete:  void disasm_line__free(struct disasm_line *dl)  { -	free(dl->line); -	free(dl->name); +	zfree(&dl->line); +	zfree(&dl->name);  	if (dl->ins && dl->ins->ops->free)  		dl->ins->ops->free(&dl->ops);  	else @@ -809,7 +830,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,  		    end = map__rip_2objdump(map, sym->end);  		offset = line_ip - start; -		if (offset < 0 || (u64)line_ip > end) +		if ((u64)line_ip < start || (u64)line_ip > end)  			offset = -1;  		else  			parsed_line = tmp2 + 1; @@ -825,20 +846,16 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,  		dl->ops.target.offset = dl->ops.target.addr -  					map__rip_2objdump(map, sym->start); -	/* -	 * kcore has no symbols, so add the call target name if it is on the -	 * same map. -	 */ +	/* kcore has no symbols, so add the call target name */  	if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { -		struct symbol *s; -		u64 ip = dl->ops.target.addr; - -		if (ip >= map->start && ip <= map->end) { -			ip = map->map_ip(map, ip); -			s = map__find_symbol(map, ip, NULL); -			if (s && s->start == ip) -				dl->ops.target.name = strdup(s->name); -		} +		struct addr_map_symbol target = { +			.map = map, +			.addr = dl->ops.target.addr, +		}; + +		if (!map_groups__find_ams(&target, NULL) && +		    target.sym->start == target.al_addr) +			dl->ops.target.name = strdup(target.sym->name);  	}  	disasm__add(¬es->src->source, dl); @@ -879,6 +896,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)  	FILE *file;  	int err = 0;  	char symfs_filename[PATH_MAX]; +	struct kcore_extract kce; +	bool delete_extract = false;  	if (filename) {  		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", @@ -902,7 +921,7 @@ fallback:  		 * cache, or is just a kallsyms file, well, lets hope that this  		 * DSO is the same as when 'perf record' ran.  		 */ -		filename = dso->long_name; +		filename = (char *)dso->long_name;  		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",  			 symbol_conf.symfs, filename);  		free_filename = false; @@ -940,6 +959,23 @@ fallback:  	pr_debug("annotating [%p] %30s : [%p] %30s\n",  		 dso, dso->long_name, sym, sym->name); +	if (dso__is_kcore(dso)) { +		kce.kcore_filename = symfs_filename; +		kce.addr = map__rip_2objdump(map, sym->start); +		kce.offs = sym->start; +		kce.len = sym->end + 1 - sym->start; +		if (!kcore_extract__create(&kce)) { +			delete_extract = true; +			strlcpy(symfs_filename, kce.extract_filename, +				sizeof(symfs_filename)); +			if (free_filename) { +				free(filename); +				free_filename = false; +			} +			filename = symfs_filename; +		} +	} +  	snprintf(command, sizeof(command),  		 "%s %s%s --start-address=0x%016" PRIx64  		 " --stop-address=0x%016" PRIx64 @@ -972,6 +1008,8 @@ fallback:  	pclose(file);  out_free_filename: +	if (delete_extract) +		kcore_extract__delete(&kce);  	if (free_filename)  		free(filename);  	return err; @@ -1070,24 +1108,21 @@ static void symbol__free_source_line(struct symbol *sym, int len)  			  (sizeof(src_line->p) * (src_line->nr_pcnt - 1));  	for (i = 0; i < len; i++) { -		free(src_line->path); +		free_srcline(src_line->path);  		src_line = (void *)src_line + sizeof_src_line;  	} -	free(notes->src->lines); -	notes->src->lines = NULL; +	zfree(¬es->src->lines);  }  /* Get the filename:line for the colored entries */  static int symbol__get_source_line(struct symbol *sym, struct map *map,  				   struct perf_evsel *evsel, -				   struct rb_root *root, int len, -				   const char *filename) +				   struct rb_root *root, int len)  {  	u64 start;  	int i, k;  	int evidx = evsel->idx; -	char cmd[PATH_MAX * 2];  	struct source_line *src_line;  	struct annotation *notes = symbol__annotation(sym);  	struct sym_hist *h = annotation__histogram(notes, evidx); @@ -1115,10 +1150,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,  	start = map__rip_2objdump(map, sym->start);  	for (i = 0; i < len; i++) { -		char *path = NULL; -		size_t line_len;  		u64 offset; -		FILE *fp;  		double percent_max = 0.0;  		src_line->nr_pcnt = nr_pcnt; @@ -1135,23 +1167,9 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,  			goto next;  		offset = start + i; -		sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); -		fp = popen(cmd, "r"); -		if (!fp) -			goto next; - -		if (getline(&path, &line_len, fp) < 0 || !line_len) -			goto next_close; - -		src_line->path = malloc(sizeof(char) * line_len + 1); -		if (!src_line->path) -			goto next_close; - -		strcpy(src_line->path, path); +		src_line->path = get_srcline(map->dso, offset);  		insert_source_line(&tmp_root, src_line); -	next_close: -		pclose(fp);  	next:  		src_line = (void *)src_line + sizeof_src_line;  	} @@ -1192,7 +1210,7 @@ static void print_summary(struct rb_root *root, const char *filename)  		path = src_line->path;  		color = get_percent_color(percent_max); -		color_fprintf(stdout, color, " %s", path); +		color_fprintf(stdout, color, " %s\n", path);  		node = rb_next(node);  	} @@ -1218,6 +1236,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,  	struct dso *dso = map->dso;  	char *filename;  	const char *d_filename; +	const char *evsel_name = perf_evsel__name(evsel);  	struct annotation *notes = symbol__annotation(sym);  	struct disasm_line *pos, *queue = NULL;  	u64 start = map__rip_2objdump(map, sym->start); @@ -1225,7 +1244,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,  	int more = 0;  	u64 len;  	int width = 8; -	int namelen; +	int namelen, evsel_name_len, graph_dotted_len;  	filename = strdup(dso->long_name);  	if (!filename) @@ -1238,14 +1257,17 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,  	len = symbol__size(sym);  	namelen = strlen(d_filename); +	evsel_name_len = strlen(evsel_name);  	if (perf_evsel__is_group_event(evsel))  		width *= evsel->nr_members; -	printf(" %-*.*s|	Source code & Disassembly of %s\n", -	       width, width, "Percent", d_filename); -	printf("-%-*.*s-------------------------------------\n", -	       width+namelen, width+namelen, graph_dotted_line); +	printf(" %-*.*s|	Source code & Disassembly of %s for %s\n", +	       width, width, "Percent", d_filename, evsel_name); + +	graph_dotted_len = width + namelen + evsel_name_len; +	printf("-%-*.*s-----------------------------------------\n", +	       graph_dotted_len, graph_dotted_len, graph_dotted_line);  	if (verbose)  		symbol__annotate_hits(sym, evsel); @@ -1356,7 +1378,6 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map,  			 bool full_paths, int min_pcnt, int max_lines)  {  	struct dso *dso = map->dso; -	const char *filename = dso->long_name;  	struct rb_root source_line = RB_ROOT;  	u64 len; @@ -1366,9 +1387,8 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map,  	len = symbol__size(sym);  	if (print_lines) { -		symbol__get_source_line(sym, map, evsel, &source_line, -					len, filename); -		print_summary(&source_line, filename); +		symbol__get_source_line(sym, map, evsel, &source_line, len); +		print_summary(&source_line, dso->long_name);  	}  	symbol__annotate_printf(sym, map, evsel, full_paths, @@ -1380,3 +1400,13 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map,  	return 0;  } + +int hist_entry__annotate(struct hist_entry *he, size_t privsize) +{ +	return symbol__annotate(he->ms.sym, he->ms.map, privsize); +} + +bool ui__has_annotation(void) +{ +	return use_browser == 1 && sort__has_sym; +} diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index af755156d27..112d6e26815 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -3,7 +3,7 @@  #include <stdbool.h>  #include <stdint.h> -#include "types.h" +#include <linux/types.h>  #include "symbol.h"  #include "hist.h"  #include "sort.h" @@ -132,12 +132,17 @@ static inline struct annotation *symbol__annotation(struct symbol *sym)  	return &a->annotation;  } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, -			     int evidx, u64 addr); +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx); + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); +  int symbol__alloc_hist(struct symbol *sym);  void symbol__annotate_zero_histograms(struct symbol *sym);  int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); + +int hist_entry__annotate(struct hist_entry *he, size_t privsize); +  int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym);  int symbol__annotate_printf(struct symbol *sym, struct map *map,  			    struct perf_evsel *evsel, bool full_paths, @@ -146,11 +151,13 @@ void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);  void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);  void disasm__purge(struct list_head *head); +bool ui__has_annotation(void); +  int symbol__tty_annotate(struct symbol *sym, struct map *map,  			 struct perf_evsel *evsel, bool print_lines,  			 bool full_paths, int min_pcnt, int max_lines); -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT  int symbol__tui_annotate(struct symbol *sym, struct map *map,  			 struct perf_evsel *evsel,  			 struct hist_browser_timer *hbt); @@ -165,30 +172,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,  }  #endif -#ifdef GTK2_SUPPORT -int symbol__gtk_annotate(struct symbol *sym, struct map *map, -			 struct perf_evsel *evsel, -			 struct hist_browser_timer *hbt); - -static inline int hist_entry__gtk_annotate(struct hist_entry *he, -					   struct perf_evsel *evsel, -					   struct hist_browser_timer *hbt) -{ -	return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); -} - -void perf_gtk__show_annotations(void); -#else -static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, -				struct perf_evsel *evsel __maybe_unused, -				struct hist_browser_timer *hbt __maybe_unused) -{ -	return 0; -} - -static inline void perf_gtk__show_annotations(void) {} -#endif -  extern const char	*disassembler_style;  #endif	/* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 7ded71d19d7..a904a4cfe7d 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -25,7 +25,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,  	struct addr_location al;  	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;  	struct thread *thread = machine__findnew_thread(machine, sample->pid, -							sample->pid); +							sample->tid);  	if (thread == NULL) {  		pr_err("problem processing %d event, skipping it.\n", @@ -89,14 +89,14 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf)  	return raw - build_id;  } -char *dso__build_id_filename(struct dso *self, char *bf, size_t size) +char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)  {  	char build_id_hex[BUILD_ID_SIZE * 2 + 1]; -	if (!self->has_build_id) +	if (!dso->has_build_id)  		return NULL; -	build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); +	build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex);  	if (bf == NULL) {  		if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,  			     build_id_hex, build_id_hex + 2) < 0) diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a811f5c62e1..ae392561470 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -4,16 +4,15 @@  #define BUILD_ID_SIZE 20  #include "tool.h" -#include "types.h" +#include <linux/types.h>  extern struct perf_tool build_id__mark_dso_hit_ops;  struct dso;  int build_id__sprintf(const u8 *build_id, int len, char *bf); -char *dso__build_id_filename(struct dso *self, char *bf, size_t size); +char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size);  int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,  			   struct perf_sample *sample, struct perf_evsel *evsel,  			   struct machine *machine); -  #endif diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 26e36723987..7b176dd02e1 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -70,8 +70,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2  extern char *perf_pathdup(const char *fmt, ...)  	__attribute__((format (printf, 1, 2))); -#ifndef HAVE_STRLCPY +/* Matches the libc/libbsd function attribute so we declare this unconditionally: */  extern size_t strlcpy(char *dest, const char *src, size_t size); -#endif  #endif /* __PERF_CACHE_H */ diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 482f68081cd..48b6d3f5001 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -15,17 +15,93 @@  #include <errno.h>  #include <math.h> +#include "asm/bug.h" +  #include "hist.h"  #include "util.h" +#include "sort.h" +#include "machine.h"  #include "callchain.h"  __thread struct callchain_cursor callchain_cursor; -#define chain_for_each_child(child, parent)	\ -	list_for_each_entry(child, &parent->children, siblings) +int +parse_callchain_report_opt(const char *arg) +{ +	char *tok, *tok2; +	char *endptr; + +	symbol_conf.use_callchain = true; + +	if (!arg) +		return 0; + +	tok = strtok((char *)arg, ","); +	if (!tok) +		return -1; -#define chain_for_each_child_safe(child, next, parent)	\ -	list_for_each_entry_safe(child, next, &parent->children, siblings) +	/* get the output mode */ +	if (!strncmp(tok, "graph", strlen(arg))) { +		callchain_param.mode = CHAIN_GRAPH_ABS; + +	} else if (!strncmp(tok, "flat", strlen(arg))) { +		callchain_param.mode = CHAIN_FLAT; +	} else if (!strncmp(tok, "fractal", strlen(arg))) { +		callchain_param.mode = CHAIN_GRAPH_REL; +	} else if (!strncmp(tok, "none", strlen(arg))) { +		callchain_param.mode = CHAIN_NONE; +		symbol_conf.use_callchain = false; +		return 0; +	} else { +		return -1; +	} + +	/* get the min percentage */ +	tok = strtok(NULL, ","); +	if (!tok) +		goto setup; + +	callchain_param.min_percent = strtod(tok, &endptr); +	if (tok == endptr) +		return -1; + +	/* get the print limit */ +	tok2 = strtok(NULL, ","); +	if (!tok2) +		goto setup; + +	if (tok2[0] != 'c') { +		callchain_param.print_limit = strtoul(tok2, &endptr, 0); +		tok2 = strtok(NULL, ","); +		if (!tok2) +			goto setup; +	} + +	/* get the call chain order */ +	if (!strncmp(tok2, "caller", strlen("caller"))) +		callchain_param.order = ORDER_CALLER; +	else if (!strncmp(tok2, "callee", strlen("callee"))) +		callchain_param.order = ORDER_CALLEE; +	else +		return -1; + +	/* Get the sort key */ +	tok2 = strtok(NULL, ","); +	if (!tok2) +		goto setup; +	if (!strncmp(tok2, "function", strlen("function"))) +		callchain_param.key = CCKEY_FUNCTION; +	else if (!strncmp(tok2, "address", strlen("address"))) +		callchain_param.key = CCKEY_ADDRESS; +	else +		return -1; +setup: +	if (callchain_register_param(&callchain_param) < 0) { +		pr_err("Can't register callchain params\n"); +		return -1; +	} +	return 0; +}  static void  rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, @@ -71,10 +147,16 @@ static void  __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,  		  u64 min_hit)  { +	struct rb_node *n;  	struct callchain_node *child; -	chain_for_each_child(child, node) +	n = rb_first(&node->rb_root_in); +	while (n) { +		child = rb_entry(n, struct callchain_node, rb_node_in); +		n = rb_next(n); +  		__sort_chain_flat(rb_root, child, min_hit); +	}  	if (node->hit && node->hit >= min_hit)  		rb_insert_callchain(rb_root, node, CHAIN_FLAT); @@ -94,11 +176,16 @@ sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,  static void __sort_chain_graph_abs(struct callchain_node *node,  				   u64 min_hit)  { +	struct rb_node *n;  	struct callchain_node *child;  	node->rb_root = RB_ROOT; +	n = rb_first(&node->rb_root_in); + +	while (n) { +		child = rb_entry(n, struct callchain_node, rb_node_in); +		n = rb_next(n); -	chain_for_each_child(child, node) {  		__sort_chain_graph_abs(child, min_hit);  		if (callchain_cumul_hits(child) >= min_hit)  			rb_insert_callchain(&node->rb_root, child, @@ -117,13 +204,18 @@ sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,  static void __sort_chain_graph_rel(struct callchain_node *node,  				   double min_percent)  { +	struct rb_node *n;  	struct callchain_node *child;  	u64 min_hit;  	node->rb_root = RB_ROOT;  	min_hit = ceil(node->children_hit * min_percent); -	chain_for_each_child(child, node) { +	n = rb_first(&node->rb_root_in); +	while (n) { +		child = rb_entry(n, struct callchain_node, rb_node_in); +		n = rb_next(n); +  		__sort_chain_graph_rel(child, min_percent);  		if (callchain_cumul_hits(child) >= min_hit)  			rb_insert_callchain(&node->rb_root, child, @@ -173,19 +265,26 @@ create_child(struct callchain_node *parent, bool inherit_children)  		return NULL;  	}  	new->parent = parent; -	INIT_LIST_HEAD(&new->children);  	INIT_LIST_HEAD(&new->val);  	if (inherit_children) { -		struct callchain_node *next; +		struct rb_node *n; +		struct callchain_node *child; + +		new->rb_root_in = parent->rb_root_in; +		parent->rb_root_in = RB_ROOT; -		list_splice(&parent->children, &new->children); -		INIT_LIST_HEAD(&parent->children); +		n = rb_first(&new->rb_root_in); +		while (n) { +			child = rb_entry(n, struct callchain_node, rb_node_in); +			child->parent = new; +			n = rb_next(n); +		} -		chain_for_each_child(next, new) -			next->parent = new; +		/* make it the first child */ +		rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node); +		rb_insert_color(&new->rb_node_in, &parent->rb_root_in);  	} -	list_add_tail(&new->siblings, &parent->children);  	return new;  } @@ -223,7 +322,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)  	}  } -static void +static struct callchain_node *  add_child(struct callchain_node *parent,  	  struct callchain_cursor *cursor,  	  u64 period) @@ -235,6 +334,19 @@ add_child(struct callchain_node *parent,  	new->children_hit = 0;  	new->hit = period; +	return new; +} + +static s64 match_chain(struct callchain_cursor_node *node, +		      struct callchain_list *cnode) +{ +	struct symbol *sym = node->sym; + +	if (cnode->ms.sym && sym && +	    callchain_param.key == CCKEY_FUNCTION) +		return cnode->ms.sym->start - sym->start; +	else +		return cnode->ip - node->ip;  }  /* @@ -272,9 +384,33 @@ split_add_child(struct callchain_node *parent,  	/* create a new child for the new branch if any */  	if (idx_total < cursor->nr) { +		struct callchain_node *first; +		struct callchain_list *cnode; +		struct callchain_cursor_node *node; +		struct rb_node *p, **pp; +  		parent->hit = 0; -		add_child(parent, cursor, period);  		parent->children_hit += period; + +		node = callchain_cursor_current(cursor); +		new = add_child(parent, cursor, period); + +		/* +		 * This is second child since we moved parent's children +		 * to new (first) child above. +		 */ +		p = parent->rb_root_in.rb_node; +		first = rb_entry(p, struct callchain_node, rb_node_in); +		cnode = list_first_entry(&first->val, struct callchain_list, +					 list); + +		if (match_chain(node, cnode) < 0) +			pp = &p->rb_left; +		else +			pp = &p->rb_right; + +		rb_link_node(&new->rb_node_in, p, pp); +		rb_insert_color(&new->rb_node_in, &parent->rb_root_in);  	} else {  		parent->hit = period;  	} @@ -291,16 +427,35 @@ append_chain_children(struct callchain_node *root,  		      u64 period)  {  	struct callchain_node *rnode; +	struct callchain_cursor_node *node; +	struct rb_node **p = &root->rb_root_in.rb_node; +	struct rb_node *parent = NULL; + +	node = callchain_cursor_current(cursor); +	if (!node) +		return;  	/* lookup in childrens */ -	chain_for_each_child(rnode, root) { -		unsigned int ret = append_chain(rnode, cursor, period); +	while (*p) { +		s64 ret; + +		parent = *p; +		rnode = rb_entry(parent, struct callchain_node, rb_node_in); -		if (!ret) +		/* If at least first entry matches, rely to children */ +		ret = append_chain(rnode, cursor, period); +		if (ret == 0)  			goto inc_children_hit; + +		if (ret < 0) +			p = &parent->rb_left; +		else +			p = &parent->rb_right;  	}  	/* nothing in children, add to the current node */ -	add_child(root, cursor, period); +	rnode = add_child(root, cursor, period); +	rb_link_node(&rnode->rb_node_in, parent, p); +	rb_insert_color(&rnode->rb_node_in, &root->rb_root_in);  inc_children_hit:  	root->children_hit += period; @@ -311,11 +466,11 @@ append_chain(struct callchain_node *root,  	     struct callchain_cursor *cursor,  	     u64 period)  { -	struct callchain_cursor_node *curr_snap = cursor->curr;  	struct callchain_list *cnode;  	u64 start = cursor->pos;  	bool found = false;  	u64 matches; +	int cmp = 0;  	/*  	 * Lookup in the current node @@ -325,32 +480,24 @@ append_chain(struct callchain_node *root,  	 */  	list_for_each_entry(cnode, &root->val, list) {  		struct callchain_cursor_node *node; -		struct symbol *sym;  		node = callchain_cursor_current(cursor);  		if (!node)  			break; -		sym = node->sym; - -		if (cnode->ms.sym && sym && -		    callchain_param.key == CCKEY_FUNCTION) { -			if (cnode->ms.sym->start != sym->start) -				break; -		} else if (cnode->ip != node->ip) +		cmp = match_chain(node, cnode); +		if (cmp)  			break; -		if (!found) -			found = true; +		found = true;  		callchain_cursor_advance(cursor);  	} -	/* matches not, relay on the parent */ +	/* matches not, relay no the parent */  	if (!found) { -		cursor->curr = curr_snap; -		cursor->pos = start; -		return -1; +		WARN_ONCE(!cmp, "Chain comparison error\n"); +		return cmp;  	}  	matches = cursor->pos - start; @@ -395,8 +542,9 @@ merge_chain_branch(struct callchain_cursor *cursor,  		   struct callchain_node *dst, struct callchain_node *src)  {  	struct callchain_cursor_node **old_last = cursor->last; -	struct callchain_node *child, *next_child; +	struct callchain_node *child;  	struct callchain_list *list, *next_list; +	struct rb_node *n;  	int old_pos = cursor->nr;  	int err = 0; @@ -412,12 +560,16 @@ merge_chain_branch(struct callchain_cursor *cursor,  		append_chain_children(dst, cursor, src->hit);  	} -	chain_for_each_child_safe(child, next_child, src) { +	n = rb_first(&src->rb_root_in); +	while (n) { +		child = container_of(n, struct callchain_node, rb_node_in); +		n = rb_next(n); +		rb_erase(&child->rb_node_in, &src->rb_root_in); +  		err = merge_chain_branch(cursor, dst, child);  		if (err)  			break; -		list_del(&child->siblings);  		free(child);  	} @@ -456,3 +608,67 @@ int callchain_cursor_append(struct callchain_cursor *cursor,  	return 0;  } + +int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, +			      struct perf_evsel *evsel, struct addr_location *al, +			      int max_stack) +{ +	if (sample->callchain == NULL) +		return 0; + +	if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || +	    sort__has_parent) { +		return machine__resolve_callchain(al->machine, evsel, al->thread, +						  sample, parent, al, max_stack); +	} +	return 0; +} + +int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample) +{ +	if (!symbol_conf.use_callchain) +		return 0; +	return callchain_append(he->callchain, &callchain_cursor, sample->period); +} + +int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, +			bool hide_unresolved) +{ +	al->map = node->map; +	al->sym = node->sym; +	if (node->map) +		al->addr = node->map->map_ip(node->map, node->ip); +	else +		al->addr = node->ip; + +	if (al->sym == NULL) { +		if (hide_unresolved) +			return 0; +		if (al->map == NULL) +			goto out; +	} + +	if (al->map->groups == &al->machine->kmaps) { +		if (machine__is_host(al->machine)) { +			al->cpumode = PERF_RECORD_MISC_KERNEL; +			al->level = 'k'; +		} else { +			al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL; +			al->level = 'g'; +		} +	} else { +		if (machine__is_host(al->machine)) { +			al->cpumode = PERF_RECORD_MISC_USER; +			al->level = '.'; +		} else if (perf_guest) { +			al->cpumode = PERF_RECORD_MISC_GUEST_USER; +			al->level = 'u'; +		} else { +			al->cpumode = PERF_RECORD_MISC_HYPERVISOR; +			al->level = 'H'; +		} +	} + +out: +	return 1; +} diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 2b585bc308c..8f84423a75d 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -7,6 +7,13 @@  #include "event.h"  #include "symbol.h" +enum perf_call_graph_mode { +	CALLCHAIN_NONE, +	CALLCHAIN_FP, +	CALLCHAIN_DWARF, +	CALLCHAIN_MAX +}; +  enum chain_mode {  	CHAIN_NONE,  	CHAIN_FLAT, @@ -21,11 +28,11 @@ enum chain_order {  struct callchain_node {  	struct callchain_node	*parent; -	struct list_head	siblings; -	struct list_head	children;  	struct list_head	val; -	struct rb_node		rb_node; /* to sort nodes in an rbtree */ -	struct rb_root		rb_root; /* sorted tree of children */ +	struct rb_node		rb_node_in; /* to insert nodes in an rbtree */ +	struct rb_node		rb_node;    /* to sort nodes in an output tree */ +	struct rb_root		rb_root_in; /* input tree of children */ +	struct rb_root		rb_root;    /* sorted output tree of children */  	unsigned int		val_nr;  	u64			hit;  	u64			children_hit; @@ -86,13 +93,12 @@ extern __thread struct callchain_cursor callchain_cursor;  static inline void callchain_init(struct callchain_root *root)  { -	INIT_LIST_HEAD(&root->node.siblings); -	INIT_LIST_HEAD(&root->node.children);  	INIT_LIST_HEAD(&root->node.val);  	root->node.parent = NULL;  	root->node.hit = 0;  	root->node.children_hit = 0; +	root->node.rb_root_in = RB_ROOT;  	root->max_depth = 0;  } @@ -146,7 +152,28 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)  }  struct option; +struct hist_entry; +int record_parse_callchain(const char *arg, struct record_opts *opts);  int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); +int record_callchain_opt(const struct option *opt, const char *arg, int unset); + +int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, +			      struct perf_evsel *evsel, struct addr_location *al, +			      int max_stack); +int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); +int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, +			bool hide_unresolved); +  extern const char record_callchain_help[]; +int parse_callchain_report_opt(const char *arg); + +static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, +					     struct callchain_cursor *src) +{ +	*dest = *src; + +	dest->first = src->curr; +	dest->nr -= src->pos; +}  #endif	/* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 96bbda1ddb8..88f7be39943 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -81,7 +81,7 @@ static int add_cgroup(struct perf_evlist *evlist, char *str)  	/*  	 * check if cgrp is already defined, if so we reuse it  	 */ -	list_for_each_entry(counter, &evlist->entries, node) { +	evlist__for_each(evlist, counter) {  		cgrp = counter->cgrp;  		if (!cgrp)  			continue; @@ -110,7 +110,7 @@ static int add_cgroup(struct perf_evlist *evlist, char *str)  	 * if add cgroup N, then need to find event N  	 */  	n = 0; -	list_for_each_entry(counter, &evlist->entries, node) { +	evlist__for_each(evlist, counter) {  		if (n == nr_cgroups)  			goto found;  		n++; @@ -133,7 +133,7 @@ void close_cgroup(struct cgroup_sel *cgrp)  	/* XXX: not reentrant */  	if (--cgrp->refcnt == 0) {  		close(cgrp->fd); -		free(cgrp->name); +		zfree(&cgrp->name);  		free(cgrp);  	}  } diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 11e46da17bb..87b8672eb41 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -1,6 +1,7 @@  #include <linux/kernel.h>  #include "cache.h"  #include "color.h" +#include <math.h>  int perf_use_color_default = -1; @@ -298,10 +299,10 @@ const char *get_percent_color(double percent)  	 * entries in green - and keep the low overhead places  	 * normal:  	 */ -	if (percent >= MIN_RED) +	if (fabs(percent) >= MIN_RED)  		color = PERF_COLOR_RED;  	else { -		if (percent > MIN_GREEN) +		if (fabs(percent) > MIN_GREEN)  			color = PERF_COLOR_GREEN;  	}  	return color; @@ -318,8 +319,19 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent)  	return r;  } -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) +int value_color_snprintf(char *bf, size_t size, const char *fmt, double value)  { -	const char *color = get_percent_color(percent); -	return color_snprintf(bf, size, color, fmt, percent); +	const char *color = get_percent_color(value); +	return color_snprintf(bf, size, color, fmt, value); +} + +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) +{ +	va_list args; +	double percent; + +	va_start(args, fmt); +	percent = va_arg(args, double); +	va_end(args); +	return value_color_snprintf(bf, size, fmt, percent);  } diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index dea082b7960..7ff30a62a13 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -39,7 +39,8 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);  int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...);  int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);  int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); +int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...);  int percent_color_fprintf(FILE *fp, const char *fmt, double percent);  const char *get_percent_color(double percent); diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c new file mode 100644 index 00000000000..f9e777629e2 --- /dev/null +++ b/tools/perf/util/comm.c @@ -0,0 +1,122 @@ +#include "comm.h" +#include "util.h" +#include <stdlib.h> +#include <stdio.h> + +struct comm_str { +	char *str; +	struct rb_node rb_node; +	int ref; +}; + +/* Should perhaps be moved to struct machine */ +static struct rb_root comm_str_root; + +static void comm_str__get(struct comm_str *cs) +{ +	cs->ref++; +} + +static void comm_str__put(struct comm_str *cs) +{ +	if (!--cs->ref) { +		rb_erase(&cs->rb_node, &comm_str_root); +		zfree(&cs->str); +		free(cs); +	} +} + +static struct comm_str *comm_str__alloc(const char *str) +{ +	struct comm_str *cs; + +	cs = zalloc(sizeof(*cs)); +	if (!cs) +		return NULL; + +	cs->str = strdup(str); +	if (!cs->str) { +		free(cs); +		return NULL; +	} + +	return cs; +} + +static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) +{ +	struct rb_node **p = &root->rb_node; +	struct rb_node *parent = NULL; +	struct comm_str *iter, *new; +	int cmp; + +	while (*p != NULL) { +		parent = *p; +		iter = rb_entry(parent, struct comm_str, rb_node); + +		cmp = strcmp(str, iter->str); +		if (!cmp) +			return iter; + +		if (cmp < 0) +			p = &(*p)->rb_left; +		else +			p = &(*p)->rb_right; +	} + +	new = comm_str__alloc(str); +	if (!new) +		return NULL; + +	rb_link_node(&new->rb_node, parent, p); +	rb_insert_color(&new->rb_node, root); + +	return new; +} + +struct comm *comm__new(const char *str, u64 timestamp) +{ +	struct comm *comm = zalloc(sizeof(*comm)); + +	if (!comm) +		return NULL; + +	comm->start = timestamp; + +	comm->comm_str = comm_str__findnew(str, &comm_str_root); +	if (!comm->comm_str) { +		free(comm); +		return NULL; +	} + +	comm_str__get(comm->comm_str); + +	return comm; +} + +int comm__override(struct comm *comm, const char *str, u64 timestamp) +{ +	struct comm_str *new, *old = comm->comm_str; + +	new = comm_str__findnew(str, &comm_str_root); +	if (!new) +		return -ENOMEM; + +	comm_str__get(new); +	comm_str__put(old); +	comm->comm_str = new; +	comm->start = timestamp; + +	return 0; +} + +void comm__free(struct comm *comm) +{ +	comm_str__put(comm->comm_str); +	free(comm); +} + +const char *comm__str(const struct comm *comm) +{ +	return comm->comm_str->str; +} diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h new file mode 100644 index 00000000000..fac5bd51bef --- /dev/null +++ b/tools/perf/util/comm.h @@ -0,0 +1,21 @@ +#ifndef __PERF_COMM_H +#define __PERF_COMM_H + +#include "../perf.h" +#include <linux/rbtree.h> +#include <linux/list.h> + +struct comm_str; + +struct comm { +	struct comm_str *comm_str; +	u64 start; +	struct list_head list; +}; + +void comm__free(struct comm *comm); +struct comm *comm__new(const char *str, u64 timestamp); +const char *comm__str(const struct comm *comm); +int comm__override(struct comm *comm, const char *str, u64 timestamp); + +#endif  /* __PERF_COMM_H */ diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 3e0fdd369cc..24519e14ac5 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -11,6 +11,7 @@  #include "util.h"  #include "cache.h"  #include "exec_cmd.h" +#include "util/hist.h"  /* perf_hist_config */  #define MAXNAME (256) @@ -355,6 +356,9 @@ int perf_default_config(const char *var, const char *value,  	if (!prefixcmp(var, "core."))  		return perf_default_core_config(var, value); +	if (!prefixcmp(var, "hist.")) +		return perf_hist_config(var, value); +  	/* Add other config variables here. */  	return 0;  } diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index beb8cf9f997..c4e55b71010 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -1,5 +1,5 @@  #include "util.h" -#include "sysfs.h" +#include <api/fs/fs.h>  #include "../perf.h"  #include "cpumap.h"  #include <assert.h> @@ -216,7 +216,7 @@ int cpu_map__get_socket(struct cpu_map *map, int idx)  	cpu = map->map[idx]; -	mnt = sysfs_find_mountpoint(); +	mnt = sysfs__mountpoint();  	if (!mnt)  		return -1; @@ -279,7 +279,7 @@ int cpu_map__get_core(struct cpu_map *map, int idx)  	cpu = map->map[idx]; -	mnt = sysfs_find_mountpoint(); +	mnt = sysfs__mountpoint();  	if (!mnt)  		return -1; @@ -317,3 +317,163 @@ int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)  {  	return cpu_map__build_map(cpus, corep, cpu_map__get_core);  } + +/* setup simple routines to easily access node numbers given a cpu number */ +static int get_max_num(char *path, int *max) +{ +	size_t num; +	char *buf; +	int err = 0; + +	if (filename__read_str(path, &buf, &num)) +		return -1; + +	buf[num] = '\0'; + +	/* start on the right, to find highest node num */ +	while (--num) { +		if ((buf[num] == ',') || (buf[num] == '-')) { +			num++; +			break; +		} +	} +	if (sscanf(&buf[num], "%d", max) < 1) { +		err = -1; +		goto out; +	} + +	/* convert from 0-based to 1-based */ +	(*max)++; + +out: +	free(buf); +	return err; +} + +/* Determine highest possible cpu in the system for sparse allocation */ +static void set_max_cpu_num(void) +{ +	const char *mnt; +	char path[PATH_MAX]; +	int ret = -1; + +	/* set up default */ +	max_cpu_num = 4096; + +	mnt = sysfs__mountpoint(); +	if (!mnt) +		goto out; + +	/* get the highest possible cpu number for a sparse allocation */ +	ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt); +	if (ret == PATH_MAX) { +		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); +		goto out; +	} + +	ret = get_max_num(path, &max_cpu_num); + +out: +	if (ret) +		pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num); +} + +/* Determine highest possible node in the system for sparse allocation */ +static void set_max_node_num(void) +{ +	const char *mnt; +	char path[PATH_MAX]; +	int ret = -1; + +	/* set up default */ +	max_node_num = 8; + +	mnt = sysfs__mountpoint(); +	if (!mnt) +		goto out; + +	/* get the highest possible cpu number for a sparse allocation */ +	ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt); +	if (ret == PATH_MAX) { +		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); +		goto out; +	} + +	ret = get_max_num(path, &max_node_num); + +out: +	if (ret) +		pr_err("Failed to read max nodes, using default of %d\n", max_node_num); +} + +static int init_cpunode_map(void) +{ +	int i; + +	set_max_cpu_num(); +	set_max_node_num(); + +	cpunode_map = calloc(max_cpu_num, sizeof(int)); +	if (!cpunode_map) { +		pr_err("%s: calloc failed\n", __func__); +		return -1; +	} + +	for (i = 0; i < max_cpu_num; i++) +		cpunode_map[i] = -1; + +	return 0; +} + +int cpu__setup_cpunode_map(void) +{ +	struct dirent *dent1, *dent2; +	DIR *dir1, *dir2; +	unsigned int cpu, mem; +	char buf[PATH_MAX]; +	char path[PATH_MAX]; +	const char *mnt; +	int n; + +	/* initialize globals */ +	if (init_cpunode_map()) +		return -1; + +	mnt = sysfs__mountpoint(); +	if (!mnt) +		return 0; + +	n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt); +	if (n == PATH_MAX) { +		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); +		return -1; +	} + +	dir1 = opendir(path); +	if (!dir1) +		return 0; + +	/* walk tree and setup map */ +	while ((dent1 = readdir(dir1)) != NULL) { +		if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1) +			continue; + +		n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name); +		if (n == PATH_MAX) { +			pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); +			continue; +		} + +		dir2 = opendir(buf); +		if (!dir2) +			continue; +		while ((dent2 = readdir(dir2)) != NULL) { +			if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) +				continue; +			cpunode_map[cpu] = mem; +		} +		closedir(dir2); +	} +	closedir(dir1); +	return 0; +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index b123bb9d6f5..61a65484900 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -4,6 +4,9 @@  #include <stdio.h>  #include <stdbool.h> +#include "perf.h" +#include "util/debug.h" +  struct cpu_map {  	int nr;  	int map[]; @@ -46,4 +49,36 @@ static inline bool cpu_map__empty(const struct cpu_map *map)  	return map ? map->map[0] == -1 : true;  } +int max_cpu_num; +int max_node_num; +int *cpunode_map; + +int cpu__setup_cpunode_map(void); + +static inline int cpu__max_node(void) +{ +	if (unlikely(!max_node_num)) +		pr_debug("cpu_map not initialized\n"); + +	return max_node_num; +} + +static inline int cpu__max_cpu(void) +{ +	if (unlikely(!max_cpu_num)) +		pr_debug("cpu_map not initialized\n"); + +	return max_cpu_num; +} + +static inline int cpu__get_node(int cpu) +{ +	if (unlikely(cpunode_map == NULL)) { +		pr_debug("cpu_map not initialized\n"); +		return -1; +	} + +	return cpunode_map[cpu]; +} +  #endif /* __PERF_CPUMAP_H */ diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c new file mode 100644 index 00000000000..55de44ecebe --- /dev/null +++ b/tools/perf/util/data.c @@ -0,0 +1,133 @@ +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> + +#include "data.h" +#include "util.h" + +static bool check_pipe(struct perf_data_file *file) +{ +	struct stat st; +	bool is_pipe = false; +	int fd = perf_data_file__is_read(file) ? +		 STDIN_FILENO : STDOUT_FILENO; + +	if (!file->path) { +		if (!fstat(fd, &st) && S_ISFIFO(st.st_mode)) +			is_pipe = true; +	} else { +		if (!strcmp(file->path, "-")) +			is_pipe = true; +	} + +	if (is_pipe) +		file->fd = fd; + +	return file->is_pipe = is_pipe; +} + +static int check_backup(struct perf_data_file *file) +{ +	struct stat st; + +	if (!stat(file->path, &st) && st.st_size) { +		/* TODO check errors properly */ +		char oldname[PATH_MAX]; +		snprintf(oldname, sizeof(oldname), "%s.old", +			 file->path); +		unlink(oldname); +		rename(file->path, oldname); +	} + +	return 0; +} + +static int open_file_read(struct perf_data_file *file) +{ +	struct stat st; +	int fd; + +	fd = open(file->path, O_RDONLY); +	if (fd < 0) { +		int err = errno; + +		pr_err("failed to open %s: %s", file->path, strerror(err)); +		if (err == ENOENT && !strcmp(file->path, "perf.data")) +			pr_err("  (try 'perf record' first)"); +		pr_err("\n"); +		return -err; +	} + +	if (fstat(fd, &st) < 0) +		goto out_close; + +	if (!file->force && st.st_uid && (st.st_uid != geteuid())) { +		pr_err("file %s not owned by current user or root\n", +		       file->path); +		goto out_close; +	} + +	if (!st.st_size) { +		pr_info("zero-sized file (%s), nothing to do!\n", +			file->path); +		goto out_close; +	} + +	file->size = st.st_size; +	return fd; + + out_close: +	close(fd); +	return -1; +} + +static int open_file_write(struct perf_data_file *file) +{ +	int fd; + +	if (check_backup(file)) +		return -1; + +	fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); + +	if (fd < 0) +		pr_err("failed to open %s : %s\n", file->path, strerror(errno)); + +	return fd; +} + +static int open_file(struct perf_data_file *file) +{ +	int fd; + +	fd = perf_data_file__is_read(file) ? +	     open_file_read(file) : open_file_write(file); + +	file->fd = fd; +	return fd < 0 ? -1 : 0; +} + +int perf_data_file__open(struct perf_data_file *file) +{ +	if (check_pipe(file)) +		return 0; + +	if (!file->path) +		file->path = "perf.data"; + +	return open_file(file); +} + +void perf_data_file__close(struct perf_data_file *file) +{ +	close(file->fd); +} + +ssize_t perf_data_file__write(struct perf_data_file *file, +			      void *buf, size_t size) +{ +	return writen(file->fd, buf, size); +} diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h new file mode 100644 index 00000000000..2b15d0c95c7 --- /dev/null +++ b/tools/perf/util/data.h @@ -0,0 +1,50 @@ +#ifndef __PERF_DATA_H +#define __PERF_DATA_H + +#include <stdbool.h> + +enum perf_data_mode { +	PERF_DATA_MODE_WRITE, +	PERF_DATA_MODE_READ, +}; + +struct perf_data_file { +	const char		*path; +	int			 fd; +	bool			 is_pipe; +	bool			 force; +	unsigned long		 size; +	enum perf_data_mode	 mode; +}; + +static inline bool perf_data_file__is_read(struct perf_data_file *file) +{ +	return file->mode == PERF_DATA_MODE_READ; +} + +static inline bool perf_data_file__is_write(struct perf_data_file *file) +{ +	return file->mode == PERF_DATA_MODE_WRITE; +} + +static inline int perf_data_file__is_pipe(struct perf_data_file *file) +{ +	return file->is_pipe; +} + +static inline int perf_data_file__fd(struct perf_data_file *file) +{ +	return file->fd; +} + +static inline unsigned long perf_data_file__size(struct perf_data_file *file) +{ +	return file->size; +} + +int perf_data_file__open(struct perf_data_file *file); +void perf_data_file__close(struct perf_data_file *file); +ssize_t perf_data_file__write(struct perf_data_file *file, +			      void *buf, size_t size); + +#endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 399e74c34c1..299b5558650 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -16,23 +16,46 @@  int verbose;  bool dump_trace = false, quiet = false; -int eprintf(int level, const char *fmt, ...) +static int _eprintf(int level, const char *fmt, va_list args)  { -	va_list args;  	int ret = 0;  	if (verbose >= level) { -		va_start(args, fmt);  		if (use_browser >= 1)  			ui_helpline__vshow(fmt, args);  		else  			ret = vfprintf(stderr, fmt, args); -		va_end(args);  	}  	return ret;  } +int eprintf(int level, const char *fmt, ...) +{ +	va_list args; +	int ret; + +	va_start(args, fmt); +	ret = _eprintf(level, fmt, args); +	va_end(args); + +	return ret; +} + +/* + * Overloading libtraceevent standard info print + * function, display with -v in perf. + */ +void pr_stat(const char *fmt, ...) +{ +	va_list args; + +	va_start(args, fmt); +	_eprintf(1, fmt, args); +	va_end(args); +	eprintf(1, "\n"); +} +  int dump_printf(const char *fmt, ...)  {  	va_list args; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index efbd98805ad..443694c36b0 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -17,4 +17,6 @@ void trace_event(union perf_event *event);  int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));  int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); +void pr_stat(const char *fmt, ...); +  #endif	/* __PERF_DEBUG_H */ diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index e3c1ff8512c..819f10414f0 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1,3 +1,6 @@ +#include <asm/bug.h> +#include <sys/time.h> +#include <sys/resource.h>  #include "symbol.h"  #include "dso.h"  #include "machine.h" @@ -7,19 +10,20 @@  char dso__symtab_origin(const struct dso *dso)  {  	static const char origin[] = { -		[DSO_BINARY_TYPE__KALLSYMS]		= 'k', -		[DSO_BINARY_TYPE__VMLINUX]		= 'v', -		[DSO_BINARY_TYPE__JAVA_JIT]		= 'j', -		[DSO_BINARY_TYPE__DEBUGLINK]		= 'l', -		[DSO_BINARY_TYPE__BUILD_ID_CACHE]	= 'B', -		[DSO_BINARY_TYPE__FEDORA_DEBUGINFO]	= 'f', -		[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]	= 'u', -		[DSO_BINARY_TYPE__BUILDID_DEBUGINFO]	= 'b', -		[DSO_BINARY_TYPE__SYSTEM_PATH_DSO]	= 'd', -		[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE]	= 'K', -		[DSO_BINARY_TYPE__GUEST_KALLSYMS]	= 'g', -		[DSO_BINARY_TYPE__GUEST_KMODULE]	= 'G', -		[DSO_BINARY_TYPE__GUEST_VMLINUX]	= 'V', +		[DSO_BINARY_TYPE__KALLSYMS]			= 'k', +		[DSO_BINARY_TYPE__VMLINUX]			= 'v', +		[DSO_BINARY_TYPE__JAVA_JIT]			= 'j', +		[DSO_BINARY_TYPE__DEBUGLINK]			= 'l', +		[DSO_BINARY_TYPE__BUILD_ID_CACHE]		= 'B', +		[DSO_BINARY_TYPE__FEDORA_DEBUGINFO]		= 'f', +		[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]		= 'u', +		[DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO]	= 'o', +		[DSO_BINARY_TYPE__BUILDID_DEBUGINFO]		= 'b', +		[DSO_BINARY_TYPE__SYSTEM_PATH_DSO]		= 'd', +		[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE]		= 'K', +		[DSO_BINARY_TYPE__GUEST_KALLSYMS]		= 'g', +		[DSO_BINARY_TYPE__GUEST_KMODULE]		= 'G', +		[DSO_BINARY_TYPE__GUEST_VMLINUX]		= 'V',  	};  	if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) @@ -27,8 +31,9 @@ char dso__symtab_origin(const struct dso *dso)  	return origin[dso->symtab_type];  } -int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, -			  char *root_dir, char *file, size_t size) +int dso__read_binary_type_filename(const struct dso *dso, +				   enum dso_binary_type type, +				   char *root_dir, char *filename, size_t size)  {  	char build_id_hex[BUILD_ID_SIZE * 2 + 1];  	int ret = 0; @@ -37,33 +42,55 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,  	case DSO_BINARY_TYPE__DEBUGLINK: {  		char *debuglink; -		strncpy(file, dso->long_name, size); -		debuglink = file + dso->long_name_len; -		while (debuglink != file && *debuglink != '/') +		strncpy(filename, dso->long_name, size); +		debuglink = filename + dso->long_name_len; +		while (debuglink != filename && *debuglink != '/')  			debuglink--;  		if (*debuglink == '/')  			debuglink++; -		filename__read_debuglink(dso->long_name, debuglink, -					 size - (debuglink - file)); +		ret = filename__read_debuglink(dso->long_name, debuglink, +					       size - (debuglink - filename));  		}  		break;  	case DSO_BINARY_TYPE__BUILD_ID_CACHE:  		/* skip the locally configured cache if a symfs is given */  		if (symbol_conf.symfs[0] || -		    (dso__build_id_filename(dso, file, size) == NULL)) +		    (dso__build_id_filename(dso, filename, size) == NULL))  			ret = -1;  		break;  	case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: -		snprintf(file, size, "%s/usr/lib/debug%s.debug", +		snprintf(filename, size, "%s/usr/lib/debug%s.debug",  			 symbol_conf.symfs, dso->long_name);  		break;  	case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: -		snprintf(file, size, "%s/usr/lib/debug%s", +		snprintf(filename, size, "%s/usr/lib/debug%s",  			 symbol_conf.symfs, dso->long_name);  		break; +	case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: +	{ +		const char *last_slash; +		size_t len; +		size_t dir_size; + +		last_slash = dso->long_name + dso->long_name_len; +		while (last_slash != dso->long_name && *last_slash != '/') +			last_slash--; + +		len = scnprintf(filename, size, "%s", symbol_conf.symfs); +		dir_size = last_slash - dso->long_name + 2; +		if (dir_size > (size - len)) { +			ret = -1; +			break; +		} +		len += scnprintf(filename + len, dir_size, "%s",  dso->long_name); +		len += scnprintf(filename + len , size - len, ".debug%s", +								last_slash); +		break; +	} +  	case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:  		if (!dso->has_build_id) {  			ret = -1; @@ -73,7 +100,7 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,  		build_id__sprintf(dso->build_id,  				  sizeof(dso->build_id),  				  build_id_hex); -		snprintf(file, size, +		snprintf(filename, size,  			 "%s/usr/lib/debug/.build-id/%.2s/%s.debug",  			 symbol_conf.symfs, build_id_hex, build_id_hex + 2);  		break; @@ -81,23 +108,23 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,  	case DSO_BINARY_TYPE__VMLINUX:  	case DSO_BINARY_TYPE__GUEST_VMLINUX:  	case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: -		snprintf(file, size, "%s%s", +		snprintf(filename, size, "%s%s",  			 symbol_conf.symfs, dso->long_name);  		break;  	case DSO_BINARY_TYPE__GUEST_KMODULE: -		snprintf(file, size, "%s%s%s", symbol_conf.symfs, +		snprintf(filename, size, "%s%s%s", symbol_conf.symfs,  			 root_dir, dso->long_name);  		break;  	case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: -		snprintf(file, size, "%s%s", symbol_conf.symfs, +		snprintf(filename, size, "%s%s", symbol_conf.symfs,  			 dso->long_name);  		break;  	case DSO_BINARY_TYPE__KCORE:  	case DSO_BINARY_TYPE__GUEST_KCORE: -		snprintf(file, size, "%s", dso->long_name); +		snprintf(filename, size, "%s", dso->long_name);  		break;  	default: @@ -112,52 +139,216 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,  	return ret;  } -static int open_dso(struct dso *dso, struct machine *machine) +/* + * Global list of open DSOs and the counter. + */ +static LIST_HEAD(dso__data_open); +static long dso__data_open_cnt; + +static void dso__list_add(struct dso *dso) +{ +	list_add_tail(&dso->data.open_entry, &dso__data_open); +	dso__data_open_cnt++; +} + +static void dso__list_del(struct dso *dso) +{ +	list_del(&dso->data.open_entry); +	WARN_ONCE(dso__data_open_cnt <= 0, +		  "DSO data fd counter out of bounds."); +	dso__data_open_cnt--; +} + +static void close_first_dso(void); + +static int do_open(char *name)  { -	char *root_dir = (char *) ""; -	char *name;  	int fd; -	name = malloc(PATH_MAX); +	do { +		fd = open(name, O_RDONLY); +		if (fd >= 0) +			return fd; + +		pr_debug("dso open failed, mmap: %s\n", strerror(errno)); +		if (!dso__data_open_cnt || errno != EMFILE) +			break; + +		close_first_dso(); +	} while (1); + +	return -1; +} + +static int __open_dso(struct dso *dso, struct machine *machine) +{ +	int fd; +	char *root_dir = (char *)""; +	char *name = malloc(PATH_MAX); +  	if (!name)  		return -ENOMEM;  	if (machine)  		root_dir = machine->root_dir; -	if (dso__binary_type_file(dso, dso->data_type, -				  root_dir, name, PATH_MAX)) { +	if (dso__read_binary_type_filename(dso, dso->binary_type, +					    root_dir, name, PATH_MAX)) {  		free(name);  		return -EINVAL;  	} -	fd = open(name, O_RDONLY); +	fd = do_open(name);  	free(name);  	return fd;  } +static void check_data_close(void); + +/** + * dso_close - Open DSO data file + * @dso: dso object + * + * Open @dso's data file descriptor and updates + * list/count of open DSO objects. + */ +static int open_dso(struct dso *dso, struct machine *machine) +{ +	int fd = __open_dso(dso, machine); + +	if (fd > 0) { +		dso__list_add(dso); +		/* +		 * Check if we crossed the allowed number +		 * of opened DSOs and close one if needed. +		 */ +		check_data_close(); +	} + +	return fd; +} + +static void close_data_fd(struct dso *dso) +{ +	if (dso->data.fd >= 0) { +		close(dso->data.fd); +		dso->data.fd = -1; +		dso->data.file_size = 0; +		dso__list_del(dso); +	} +} + +/** + * dso_close - Close DSO data file + * @dso: dso object + * + * Close @dso's data file descriptor and updates + * list/count of open DSO objects. + */ +static void close_dso(struct dso *dso) +{ +	close_data_fd(dso); +} + +static void close_first_dso(void) +{ +	struct dso *dso; + +	dso = list_first_entry(&dso__data_open, struct dso, data.open_entry); +	close_dso(dso); +} + +static rlim_t get_fd_limit(void) +{ +	struct rlimit l; +	rlim_t limit = 0; + +	/* Allow half of the current open fd limit. */ +	if (getrlimit(RLIMIT_NOFILE, &l) == 0) { +		if (l.rlim_cur == RLIM_INFINITY) +			limit = l.rlim_cur; +		else +			limit = l.rlim_cur / 2; +	} else { +		pr_err("failed to get fd limit\n"); +		limit = 1; +	} + +	return limit; +} + +static bool may_cache_fd(void) +{ +	static rlim_t limit; + +	if (!limit) +		limit = get_fd_limit(); + +	if (limit == RLIM_INFINITY) +		return true; + +	return limit > (rlim_t) dso__data_open_cnt; +} + +/* + * Check and close LRU dso if we crossed allowed limit + * for opened dso file descriptors. The limit is half + * of the RLIMIT_NOFILE files opened. +*/ +static void check_data_close(void) +{ +	bool cache_fd = may_cache_fd(); + +	if (!cache_fd) +		close_first_dso(); +} + +/** + * dso__data_close - Close DSO data file + * @dso: dso object + * + * External interface to close @dso's data file descriptor. + */ +void dso__data_close(struct dso *dso) +{ +	close_dso(dso); +} + +/** + * dso__data_fd - Get dso's data file descriptor + * @dso: dso object + * @machine: machine object + * + * External interface to find dso's file, open it and + * returns file descriptor. + */  int dso__data_fd(struct dso *dso, struct machine *machine)  { -	static enum dso_binary_type binary_type_data[] = { +	enum dso_binary_type binary_type_data[] = {  		DSO_BINARY_TYPE__BUILD_ID_CACHE,  		DSO_BINARY_TYPE__SYSTEM_PATH_DSO,  		DSO_BINARY_TYPE__NOT_FOUND,  	};  	int i = 0; -	if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) -		return open_dso(dso, machine); +	if (dso->data.fd >= 0) +		return dso->data.fd; + +	if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) { +		dso->data.fd = open_dso(dso, machine); +		return dso->data.fd; +	}  	do {  		int fd; -		dso->data_type = binary_type_data[i++]; +		dso->binary_type = binary_type_data[i++];  		fd = open_dso(dso, machine);  		if (fd >= 0) -			return fd; +			return dso->data.fd = fd; -	} while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); +	} while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND);  	return -EINVAL;  } @@ -177,11 +368,10 @@ dso_cache__free(struct rb_root *root)  	}  } -static struct dso_cache* -dso_cache__find(struct rb_root *root, u64 offset) +static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)  { -	struct rb_node **p = &root->rb_node; -	struct rb_node *parent = NULL; +	struct rb_node * const *p = &root->rb_node; +	const struct rb_node *parent = NULL;  	struct dso_cache *cache;  	while (*p != NULL) { @@ -238,16 +428,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset,  }  static ssize_t -dso_cache__read(struct dso *dso, struct machine *machine, -		 u64 offset, u8 *data, ssize_t size) +dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)  {  	struct dso_cache *cache;  	ssize_t ret; -	int fd; - -	fd = dso__data_fd(dso, machine); -	if (fd < 0) -		return -1;  	do {  		u64 cache_offset; @@ -261,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine,  		cache_offset = offset & DSO__DATA_CACHE_MASK;  		ret = -EINVAL; -		if (-1 == lseek(fd, cache_offset, SEEK_SET)) +		if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET))  			break; -		ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); +		ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE);  		if (ret <= 0)  			break;  		cache->offset = cache_offset;  		cache->size   = ret; -		dso_cache__insert(&dso->cache, cache); +		dso_cache__insert(&dso->data.cache, cache);  		ret = dso_cache__memcpy(cache, offset, data, size); @@ -279,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine,  	if (ret <= 0)  		free(cache); -	close(fd);  	return ret;  } -static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, -			      u64 offset, u8 *data, ssize_t size) +static ssize_t dso_cache_read(struct dso *dso, u64 offset, +			      u8 *data, ssize_t size)  {  	struct dso_cache *cache; -	cache = dso_cache__find(&dso->cache, offset); +	cache = dso_cache__find(&dso->data.cache, offset);  	if (cache)  		return dso_cache__memcpy(cache, offset, data, size);  	else -		return dso_cache__read(dso, machine, offset, data, size); +		return dso_cache__read(dso, offset, data, size);  } -ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, -			      u64 offset, u8 *data, ssize_t size) +/* + * Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks + * in the rb_tree. Any read to already cached data is served + * by cached data. + */ +static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)  {  	ssize_t r = 0;  	u8 *p = data; @@ -304,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,  	do {  		ssize_t ret; -		ret = dso_cache_read(dso, machine, offset, p, size); +		ret = dso_cache_read(dso, offset, p, size);  		if (ret < 0)  			return ret; @@ -324,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,  	return r;  } +static int data_file_size(struct dso *dso) +{ +	struct stat st; + +	if (!dso->data.file_size) { +		if (fstat(dso->data.fd, &st)) { +			pr_err("dso mmap failed, fstat: %s\n", strerror(errno)); +			return -1; +		} +		dso->data.file_size = st.st_size; +	} + +	return 0; +} + +static ssize_t data_read_offset(struct dso *dso, u64 offset, +				u8 *data, ssize_t size) +{ +	if (data_file_size(dso)) +		return -1; + +	/* Check the offset sanity. */ +	if (offset > dso->data.file_size) +		return -1; + +	if (offset + size < offset) +		return -1; + +	return cached_read(dso, offset, data, size); +} + +/** + * dso__data_read_offset - Read data from dso file offset + * @dso: dso object + * @machine: machine object + * @offset: file offset + * @data: buffer to store data + * @size: size of the @data buffer + * + * External interface to read data from dso file offset. Open + * dso data file and use cached_read to get the data. + */ +ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, +			      u64 offset, u8 *data, ssize_t size) +{ +	if (dso__data_fd(dso, machine) < 0) +		return -1; + +	return data_read_offset(dso, offset, data, size); +} + +/** + * dso__data_read_addr - Read data from dso address + * @dso: dso object + * @machine: machine object + * @add: virtual memory address + * @data: buffer to store data + * @size: size of the @data buffer + * + * External interface to read data from dso address. + */  ssize_t dso__data_read_addr(struct dso *dso, struct map *map,  			    struct machine *machine, u64 addr,  			    u8 *data, ssize_t size) @@ -356,32 +604,63 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,  	 * processing we had no idea this was the kernel dso.  	 */  	if (dso != NULL) { -		dso__set_short_name(dso, short_name); +		dso__set_short_name(dso, short_name, false);  		dso->kernel = dso_type;  	}  	return dso;  } -void dso__set_long_name(struct dso *dso, char *name) +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)  {  	if (name == NULL)  		return; -	dso->long_name = name; -	dso->long_name_len = strlen(name); + +	if (dso->long_name_allocated) +		free((char *)dso->long_name); + +	dso->long_name		 = name; +	dso->long_name_len	 = strlen(name); +	dso->long_name_allocated = name_allocated;  } -void dso__set_short_name(struct dso *dso, const char *name) +void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated)  {  	if (name == NULL)  		return; -	dso->short_name = name; -	dso->short_name_len = strlen(name); + +	if (dso->short_name_allocated) +		free((char *)dso->short_name); + +	dso->short_name		  = name; +	dso->short_name_len	  = strlen(name); +	dso->short_name_allocated = name_allocated;  }  static void dso__set_basename(struct dso *dso)  { -	dso__set_short_name(dso, basename(dso->long_name)); +       /* +        * basename() may modify path buffer, so we must pass +        * a copy. +        */ +       char *base, *lname = strdup(dso->long_name); + +       if (!lname) +               return; + +       /* +        * basename() may return a pointer to internal +        * storage which is reused in subsequent calls +        * so copy the result. +        */ +       base = strdup(basename(lname)); + +       free(lname); + +       if (!base) +               return; + +       dso__set_short_name(dso, base, true);  }  int dso__name_len(const struct dso *dso) @@ -416,20 +695,24 @@ struct dso *dso__new(const char *name)  	if (dso != NULL) {  		int i;  		strcpy(dso->name, name); -		dso__set_long_name(dso, dso->name); -		dso__set_short_name(dso, dso->name); +		dso__set_long_name(dso, dso->name, false); +		dso__set_short_name(dso, dso->name, false);  		for (i = 0; i < MAP__NR_TYPES; ++i)  			dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; -		dso->cache = RB_ROOT; +		dso->data.cache = RB_ROOT; +		dso->data.fd = -1;  		dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; -		dso->data_type   = DSO_BINARY_TYPE__NOT_FOUND; +		dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;  		dso->loaded = 0;  		dso->rel = 0;  		dso->sorted_by_name = 0;  		dso->has_build_id = 0; +		dso->has_srcline = 1; +		dso->a2l_fails = 1;  		dso->kernel = DSO_TYPE_USER;  		dso->needs_swap = DSO_SWAP__UNSET;  		INIT_LIST_HEAD(&dso->node); +		INIT_LIST_HEAD(&dso->data.open_entry);  	}  	return dso; @@ -440,11 +723,21 @@ void dso__delete(struct dso *dso)  	int i;  	for (i = 0; i < MAP__NR_TYPES; ++i)  		symbols__delete(&dso->symbols[i]); -	if (dso->sname_alloc) -		free((char *)dso->short_name); -	if (dso->lname_alloc) -		free(dso->long_name); -	dso_cache__free(&dso->cache); + +	if (dso->short_name_allocated) { +		zfree((char **)&dso->short_name); +		dso->short_name_allocated = false; +	} + +	if (dso->long_name_allocated) { +		zfree((char **)&dso->long_name); +		dso->long_name_allocated = false; +	} + +	dso__data_close(dso); +	dso_cache__free(&dso->data.cache); +	dso__free_a2l(dso); +	zfree(&dso->symsrc_filename);  	free(dso);  } @@ -519,7 +812,7 @@ void dsos__add(struct list_head *head, struct dso *dso)  	list_add_tail(&dso->node, head);  } -struct dso *dsos__find(struct list_head *head, const char *name, bool cmp_short) +struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short)  {  	struct dso *pos; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index b793053335d..ad553ba257b 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -4,8 +4,9 @@  #include <linux/types.h>  #include <linux/rbtree.h>  #include <stdbool.h> -#include "types.h" +#include <linux/types.h>  #include "map.h" +#include "build-id.h"  enum dso_binary_type {  	DSO_BINARY_TYPE__KALLSYMS = 0, @@ -23,6 +24,7 @@ enum dso_binary_type {  	DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,  	DSO_BINARY_TYPE__KCORE,  	DSO_BINARY_TYPE__GUEST_KCORE, +	DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,  	DSO_BINARY_TYPE__NOT_FOUND,  }; @@ -74,28 +76,50 @@ struct dso {  	struct list_head node;  	struct rb_root	 symbols[MAP__NR_TYPES];  	struct rb_root	 symbol_names[MAP__NR_TYPES]; -	struct rb_root	 cache; +	void		 *a2l; +	char		 *symsrc_filename; +	unsigned int	 a2l_fails;  	enum dso_kernel_type	kernel;  	enum dso_swap_type	needs_swap;  	enum dso_binary_type	symtab_type; -	enum dso_binary_type	data_type; +	enum dso_binary_type	binary_type;  	u8		 adjust_symbols:1;  	u8		 has_build_id:1; +	u8		 has_srcline:1;  	u8		 hit:1;  	u8		 annotate_warned:1; -	u8		 sname_alloc:1; -	u8		 lname_alloc:1; +	u8		 short_name_allocated:1; +	u8		 long_name_allocated:1;  	u8		 sorted_by_name;  	u8		 loaded;  	u8		 rel;  	u8		 build_id[BUILD_ID_SIZE];  	const char	 *short_name; -	char		 *long_name; +	const char	 *long_name;  	u16		 long_name_len;  	u16		 short_name_len; + +	/* dso data file */ +	struct { +		struct rb_root	 cache; +		int		 fd; +		size_t		 file_size; +		struct list_head open_entry; +	} data; +  	char		 name[0];  }; +/* dso__for_each_symbol - iterate over the symbols of given type + * + * @dso: the 'struct dso *' in which symbols itereated + * @pos: the 'struct symbol *' to use as a loop cursor + * @n: the 'struct rb_node *' to use as a temporary storage + * @type: the 'enum map_type' type of symbols + */ +#define dso__for_each_symbol(dso, pos, n, type)	\ +	symbols__for_each_entry(&(dso)->symbols[(type)], pos, n) +  static inline void dso__set_loaded(struct dso *dso, enum map_type type)  {  	dso->loaded |= (1 << type); @@ -104,8 +128,8 @@ static inline void dso__set_loaded(struct dso *dso, enum map_type type)  struct dso *dso__new(const char *name);  void dso__delete(struct dso *dso); -void dso__set_short_name(struct dso *dso, const char *name); -void dso__set_long_name(struct dso *dso, char *name); +void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated); +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated);  int dso__name_len(const struct dso *dso); @@ -122,10 +146,50 @@ void dso__read_running_kernel_build_id(struct dso *dso,  int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir);  char dso__symtab_origin(const struct dso *dso); -int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, -			  char *root_dir, char *file, size_t size); - +int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, +				   char *root_dir, char *filename, size_t size); + +/* + * The dso__data_* external interface provides following functions: + *   dso__data_fd + *   dso__data_close + *   dso__data_read_offset + *   dso__data_read_addr + * + * Please refer to the dso.c object code for each function and + * arguments documentation. Following text tries to explain the + * dso file descriptor caching. + * + * The dso__data* interface allows caching of opened file descriptors + * to speed up the dso data accesses. The idea is to leave the file + * descriptor opened ideally for the whole life of the dso object. + * + * The current usage of the dso__data_* interface is as follows: + * + * Get DSO's fd: + *   int fd = dso__data_fd(dso, machine); + *   USE 'fd' SOMEHOW + * + * Read DSO's data: + *   n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE); + *   n = dso__data_read_addr(dso_0, &machine, 0, buf, BUFSIZE); + * + * Eventually close DSO's fd: + *   dso__data_close(dso); + * + * It is not necessary to close the DSO object data file. Each time new + * DSO data file is opened, the limit (RLIMIT_NOFILE/2) is checked. Once + * it is crossed, the oldest opened DSO object is closed. + * + * The dso__delete function calls close_dso function to ensure the + * data file descriptor gets closed/unmapped before the dso object + * is freed. + * + * TODO +*/  int dso__data_fd(struct dso *dso, struct machine *machine); +void dso__data_close(struct dso *dso); +  ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,  			      u64 offset, u8 *data, ssize_t size);  ssize_t dso__data_read_addr(struct dso *dso, struct map *map, @@ -137,7 +201,7 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,  				const char *short_name, int dso_type);  void dsos__add(struct list_head *head, struct dso *dso); -struct dso *dsos__find(struct list_head *head, const char *name, +struct dso *dsos__find(const struct list_head *head, const char *name,  		       bool cmp_short);  struct dso *__dsos__findnew(struct list_head *head, const char *name);  bool __dsos__read_build_ids(struct list_head *head, bool with_hits); @@ -153,14 +217,16 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp);  static inline bool dso__is_vmlinux(struct dso *dso)  { -	return dso->data_type == DSO_BINARY_TYPE__VMLINUX || -	       dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX; +	return dso->binary_type == DSO_BINARY_TYPE__VMLINUX || +	       dso->binary_type == DSO_BINARY_TYPE__GUEST_VMLINUX;  }  static inline bool dso__is_kcore(struct dso *dso)  { -	return dso->data_type == DSO_BINARY_TYPE__KCORE || -	       dso->data_type == DSO_BINARY_TYPE__GUEST_KCORE; +	return dso->binary_type == DSO_BINARY_TYPE__KCORE || +	       dso->binary_type == DSO_BINARY_TYPE__GUEST_KCORE;  } +void dso__free_a2l(struct dso *dso); +  #endif /* __PERF_DSO */ diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 3e5f5430a28..cc66c4049e0 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -263,6 +263,21 @@ bool die_is_signed_type(Dwarf_Die *tp_die)  }  /** + * die_is_func_def - Ensure that this DIE is a subprogram and definition + * @dw_die: a DIE + * + * Ensure that this DIE is a subprogram and NOT a declaration. This + * returns true if @dw_die is a function definition. + **/ +bool die_is_func_def(Dwarf_Die *dw_die) +{ +	Dwarf_Attribute attr; + +	return (dwarf_tag(dw_die) == DW_TAG_subprogram && +		dwarf_attr(dw_die, DW_AT_declaration, &attr) == NULL); +} + +/**   * die_get_data_member_location - Get the data-member offset   * @mb_die: a DIE of a member of a data structure   * @offs: The offset of the member in the data structure @@ -392,6 +407,10 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)  {  	struct __addr_die_search_param *ad = data; +	/* +	 * Since a declaration entry doesn't has given pc, this always returns +	 * function definition entry. +	 */  	if (dwarf_tag(fn_die) == DW_TAG_subprogram &&  	    dwarf_haspc(fn_die, ad->addr)) {  		memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); @@ -407,7 +426,7 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)   * @die_mem: a buffer for result DIE   *   * Search a non-inlined function DIE which includes @addr. Stores the - * DIE to @die_mem and returns it if found. Returns NULl if failed. + * DIE to @die_mem and returns it if found. Returns NULL if failed.   */  Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,  				    Dwarf_Die *die_mem) @@ -435,15 +454,32 @@ static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)  }  /** + * die_find_top_inlinefunc - Search the top inlined function at given address + * @sp_die: a subprogram DIE which including @addr + * @addr: target address + * @die_mem: a buffer for result DIE + * + * Search an inlined function DIE which includes @addr. Stores the + * DIE to @die_mem and returns it if found. Returns NULL if failed. + * Even if several inlined functions are expanded recursively, this + * doesn't trace it down, and returns the topmost one. + */ +Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, +				   Dwarf_Die *die_mem) +{ +	return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); +} + +/**   * die_find_inlinefunc - Search an inlined function at given address - * @cu_die: a CU DIE which including @addr + * @sp_die: a subprogram DIE which including @addr   * @addr: target address   * @die_mem: a buffer for result DIE   *   * Search an inlined function DIE which includes @addr. Stores the - * DIE to @die_mem and returns it if found. Returns NULl if failed. + * DIE to @die_mem and returns it if found. Returns NULL if failed.   * If several inlined functions are expanded recursively, this trace - * it and returns deepest one. + * it down and returns deepest one.   */  Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,  			       Dwarf_Die *die_mem) @@ -711,14 +747,17 @@ struct __find_variable_param {  static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)  {  	struct __find_variable_param *fvp = data; +	Dwarf_Attribute attr;  	int tag;  	tag = dwarf_tag(die_mem);  	if ((tag == DW_TAG_formal_parameter ||  	     tag == DW_TAG_variable) && -	    die_compare_name(die_mem, fvp->name)) +	    die_compare_name(die_mem, fvp->name) && +	/* Does the DIE have location information or external instance? */ +	    (dwarf_attr(die_mem, DW_AT_external, &attr) || +	     dwarf_attr(die_mem, DW_AT_location, &attr)))  		return DIE_FIND_CB_END; -  	if (dwarf_haspc(die_mem, fvp->addr))  		return DIE_FIND_CB_CONTINUE;  	else diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index 6ce1717784b..b4fe90c6cb2 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -38,6 +38,9 @@ extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,  extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,  			int (*callback)(Dwarf_Die *, void *), void *data); +/* Ensure that this DIE is a subprogram and definition (not declaration) */ +extern bool die_is_func_def(Dwarf_Die *dw_die); +  /* Compare diename and tname */  extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); @@ -76,7 +79,11 @@ extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die,  extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,  				    Dwarf_Die *die_mem); -/* Search an inlined function including given address */ +/* Search the top inlined function including given address */ +extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, +					  Dwarf_Die *die_mem); + +/* Search the deepest inlined function including given address */  extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,  				      Dwarf_Die *die_mem); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9b393e7dca6..d0281bdfa58 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1,12 +1,15 @@  #include <linux/types.h> +#include <sys/mman.h>  #include "event.h"  #include "debug.h" +#include "hist.h"  #include "machine.h"  #include "sort.h"  #include "string.h"  #include "strlist.h"  #include "thread.h"  #include "thread_map.h" +#include "symbol/kallsyms.h"  static const char *perf_event__names[] = {  	[0]					= "TOTAL", @@ -93,20 +96,20 @@ static pid_t perf_event__get_comm_tgid(pid_t pid, char *comm, size_t len)  static pid_t perf_event__synthesize_comm(struct perf_tool *tool,  					 union perf_event *event, pid_t pid, -					 int full,  					 perf_event__handler_t process,  					 struct machine *machine)  { -	char filename[PATH_MAX];  	size_t size; -	DIR *tasks; -	struct dirent dirent, *next;  	pid_t tgid;  	memset(&event->comm, 0, sizeof(event->comm)); -	tgid = perf_event__get_comm_tgid(pid, event->comm.comm, -					 sizeof(event->comm.comm)); +	if (machine__is_host(machine)) +		tgid = perf_event__get_comm_tgid(pid, event->comm.comm, +						 sizeof(event->comm.comm)); +	else +		tgid = machine->pid; +  	if (tgid < 0)  		goto out; @@ -119,64 +122,53 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool,  	event->comm.header.size = (sizeof(event->comm) -  				(sizeof(event->comm.comm) - size) +  				machine->id_hdr_size); -	if (!full) { -		event->comm.tid = pid; - -		if (process(tool, event, &synth_sample, machine) != 0) -			return -1; - -		goto out; -	} +	event->comm.tid = pid; -	snprintf(filename, sizeof(filename), "/proc/%d/task", pid); - -	tasks = opendir(filename); -	if (tasks == NULL) { -		pr_debug("couldn't open %s\n", filename); -		return 0; -	} +	if (process(tool, event, &synth_sample, machine) != 0) +		return -1; -	while (!readdir_r(tasks, &dirent, &next) && next) { -		char *end; -		pid = strtol(dirent.d_name, &end, 10); -		if (*end) -			continue; +out: +	return tgid; +} -		/* already have tgid; jut want to update the comm */ -		(void) perf_event__get_comm_tgid(pid, event->comm.comm, -					 sizeof(event->comm.comm)); +static int perf_event__synthesize_fork(struct perf_tool *tool, +				       union perf_event *event, pid_t pid, +				       pid_t tgid, perf_event__handler_t process, +				       struct machine *machine) +{ +	memset(&event->fork, 0, sizeof(event->fork) + machine->id_hdr_size); -		size = strlen(event->comm.comm) + 1; -		size = PERF_ALIGN(size, sizeof(u64)); -		memset(event->comm.comm + size, 0, machine->id_hdr_size); -		event->comm.header.size = (sizeof(event->comm) - -					  (sizeof(event->comm.comm) - size) + -					  machine->id_hdr_size); +	/* this is really a clone event but we use fork to synthesize it */ +	event->fork.ppid = tgid; +	event->fork.ptid = tgid; +	event->fork.pid  = tgid; +	event->fork.tid  = pid; +	event->fork.header.type = PERF_RECORD_FORK; -		event->comm.tid = pid; +	event->fork.header.size = (sizeof(event->fork) + machine->id_hdr_size); -		if (process(tool, event, &synth_sample, machine) != 0) { -			tgid = -1; -			break; -		} -	} +	if (process(tool, event, &synth_sample, machine) != 0) +		return -1; -	closedir(tasks); -out: -	return tgid; +	return 0;  } -static int perf_event__synthesize_mmap_events(struct perf_tool *tool, -					      union perf_event *event, -					      pid_t pid, pid_t tgid, -					      perf_event__handler_t process, -					      struct machine *machine) +int perf_event__synthesize_mmap_events(struct perf_tool *tool, +				       union perf_event *event, +				       pid_t pid, pid_t tgid, +				       perf_event__handler_t process, +				       struct machine *machine, +				       bool mmap_data)  {  	char filename[PATH_MAX];  	FILE *fp;  	int rc = 0; -	snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); +	if (machine__is_default_guest(machine)) +		return 0; + +	snprintf(filename, sizeof(filename), "%s/proc/%d/maps", +		 machine->root_dir, pid);  	fp = fopen(filename, "r");  	if (fp == NULL) { @@ -188,10 +180,6 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,  	}  	event->header.type = PERF_RECORD_MMAP2; -	/* -	 * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c -	 */ -	event->header.misc = PERF_RECORD_MISC_USER;  	while (1) {  		char bf[BUFSIZ]; @@ -215,13 +203,43 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,  		       &event->mmap2.min,  		       &ino, execname); +		/* + 		 * Anon maps don't have the execname. + 		 */ +		if (n < 7) +			continue; +  		event->mmap2.ino = (u64)ino; -		if (n != 8) -			continue; +		/* +		 * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c +		 */ +		if (machine__is_host(machine)) +			event->header.misc = PERF_RECORD_MISC_USER; +		else +			event->header.misc = PERF_RECORD_MISC_GUEST_USER; + +		/* map protection and flags bits */ +		event->mmap2.prot = 0; +		event->mmap2.flags = 0; +		if (prot[0] == 'r') +			event->mmap2.prot |= PROT_READ; +		if (prot[1] == 'w') +			event->mmap2.prot |= PROT_WRITE; +		if (prot[2] == 'x') +			event->mmap2.prot |= PROT_EXEC; + +		if (prot[3] == 's') +			event->mmap2.flags |= MAP_SHARED; +		else +			event->mmap2.flags |= MAP_PRIVATE; -		if (prot[2] != 'x') -			continue; +		if (prot[2] != 'x') { +			if (!mmap_data || prot[0] != 'r') +				continue; + +			event->header.misc |= PERF_RECORD_MISC_MMAP_DATA; +		}  		if (!strcmp(execname, ""))  			strcpy(execname, anonstr); @@ -305,25 +323,80 @@ int perf_event__synthesize_modules(struct perf_tool *tool,  static int __event__synthesize_thread(union perf_event *comm_event,  				      union perf_event *mmap_event, +				      union perf_event *fork_event,  				      pid_t pid, int full,  					  perf_event__handler_t process,  				      struct perf_tool *tool, -				      struct machine *machine) +				      struct machine *machine, bool mmap_data)  { -	pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, +	char filename[PATH_MAX]; +	DIR *tasks; +	struct dirent dirent, *next; +	pid_t tgid; + +	/* special case: only send one comm event using passed in pid */ +	if (!full) { +		tgid = perf_event__synthesize_comm(tool, comm_event, pid, +						   process, machine); + +		if (tgid == -1) +			return -1; + +		return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, +							  process, machine, mmap_data); +	} + +	if (machine__is_default_guest(machine)) +		return 0; + +	snprintf(filename, sizeof(filename), "%s/proc/%d/task", +		 machine->root_dir, pid); + +	tasks = opendir(filename); +	if (tasks == NULL) { +		pr_debug("couldn't open %s\n", filename); +		return 0; +	} + +	while (!readdir_r(tasks, &dirent, &next) && next) { +		char *end; +		int rc = 0; +		pid_t _pid; + +		_pid = strtol(dirent.d_name, &end, 10); +		if (*end) +			continue; + +		tgid = perf_event__synthesize_comm(tool, comm_event, _pid, +						   process, machine); +		if (tgid == -1) +			return -1; + +		if (_pid == pid) { +			/* process the parent's maps too */ +			rc = perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, +						process, machine, mmap_data); +		} else { +			/* only fork the tid's map, to save time */ +			rc = perf_event__synthesize_fork(tool, fork_event, _pid, tgid,  						 process, machine); -	if (tgid == -1) -		return -1; -	return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, -						  process, machine); +		} + +		if (rc) +			return rc; +	} + +	closedir(tasks); +	return 0;  }  int perf_event__synthesize_thread_map(struct perf_tool *tool,  				      struct thread_map *threads,  				      perf_event__handler_t process, -				      struct machine *machine) +				      struct machine *machine, +				      bool mmap_data)  { -	union perf_event *comm_event, *mmap_event; +	union perf_event *comm_event, *mmap_event, *fork_event;  	int err = -1, thread, j;  	comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); @@ -334,11 +407,17 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,  	if (mmap_event == NULL)  		goto out_free_comm; +	fork_event = malloc(sizeof(fork_event->fork) + machine->id_hdr_size); +	if (fork_event == NULL) +		goto out_free_mmap; +  	err = 0;  	for (thread = 0; thread < threads->nr; ++thread) {  		if (__event__synthesize_thread(comm_event, mmap_event, +					       fork_event,  					       threads->map[thread], 0, -					       process, tool, machine)) { +					       process, tool, machine, +					       mmap_data)) {  			err = -1;  			break;  		} @@ -360,15 +439,18 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,  			/* if not, generate events for it */  			if (need_leader && -			    __event__synthesize_thread(comm_event, -						      mmap_event, -						      comm_event->comm.pid, 0, -						      process, tool, machine)) { +			    __event__synthesize_thread(comm_event, mmap_event, +						       fork_event, +						       comm_event->comm.pid, 0, +						       process, tool, machine, +						       mmap_data)) {  				err = -1;  				break;  			}  		}  	} +	free(fork_event); +out_free_mmap:  	free(mmap_event);  out_free_comm:  	free(comm_event); @@ -378,13 +460,17 @@ out:  int perf_event__synthesize_threads(struct perf_tool *tool,  				   perf_event__handler_t process, -				   struct machine *machine) +				   struct machine *machine, bool mmap_data)  {  	DIR *proc; +	char proc_path[PATH_MAX];  	struct dirent dirent, *next; -	union perf_event *comm_event, *mmap_event; +	union perf_event *comm_event, *mmap_event, *fork_event;  	int err = -1; +	if (machine__is_default_guest(machine)) +		return 0; +  	comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size);  	if (comm_event == NULL)  		goto out; @@ -393,10 +479,16 @@ int perf_event__synthesize_threads(struct perf_tool *tool,  	if (mmap_event == NULL)  		goto out_free_comm; -	proc = opendir("/proc"); -	if (proc == NULL) +	fork_event = malloc(sizeof(fork_event->fork) + machine->id_hdr_size); +	if (fork_event == NULL)  		goto out_free_mmap; +	snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir); +	proc = opendir(proc_path); + +	if (proc == NULL) +		goto out_free_fork; +  	while (!readdir_r(proc, &dirent, &next) && next) {  		char *end;  		pid_t pid = strtol(dirent.d_name, &end, 10); @@ -407,12 +499,14 @@ int perf_event__synthesize_threads(struct perf_tool *tool,   		 * We may race with exiting thread, so don't stop just because   		 * one thread couldn't be synthesized.   		 */ -		__event__synthesize_thread(comm_event, mmap_event, pid, 1, -					   process, tool, machine); +		__event__synthesize_thread(comm_event, mmap_event, fork_event, pid, +					   1, process, tool, machine, mmap_data);  	}  	err = 0;  	closedir(proc); +out_free_fork: +	free(fork_event);  out_free_mmap:  	free(mmap_event);  out_free_comm: @@ -443,23 +537,32 @@ static int find_symbol_cb(void *arg, const char *name, char type,  	return 1;  } +u64 kallsyms__get_function_start(const char *kallsyms_filename, +				 const char *symbol_name) +{ +	struct process_symbol_args args = { .name = symbol_name, }; + +	if (kallsyms__parse(kallsyms_filename, &args, find_symbol_cb) <= 0) +		return 0; + +	return args.start; +} +  int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,  				       perf_event__handler_t process, -				       struct machine *machine, -				       const char *symbol_name) +				       struct machine *machine)  {  	size_t size; -	const char *filename, *mmap_name; -	char path[PATH_MAX]; +	const char *mmap_name;  	char name_buff[PATH_MAX];  	struct map *map; +	struct kmap *kmap;  	int err;  	/*  	 * We should get this from /sys/kernel/sections/.text, but till that is  	 * available use this, and after it is use this as a fallback for older  	 * kernels.  	 */ -	struct process_symbol_args args = { .name = symbol_name, };  	union perf_event *event = zalloc((sizeof(event->mmap) +  					  machine->id_hdr_size));  	if (event == NULL) { @@ -475,30 +578,19 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,  		 * see kernel/perf_event.c __perf_event_mmap  		 */  		event->header.misc = PERF_RECORD_MISC_KERNEL; -		filename = "/proc/kallsyms";  	} else {  		event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; -		if (machine__is_default_guest(machine)) -			filename = (char *) symbol_conf.default_guest_kallsyms; -		else { -			sprintf(path, "%s/proc/kallsyms", machine->root_dir); -			filename = path; -		} -	} - -	if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) { -		free(event); -		return -ENOENT;  	}  	map = machine->vmlinux_maps[MAP__FUNCTION]; +	kmap = map__kmap(map);  	size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), -			"%s%s", mmap_name, symbol_name) + 1; +			"%s%s", mmap_name, kmap->ref_reloc_sym->name) + 1;  	size = PERF_ALIGN(size, sizeof(u64));  	event->mmap.header.type = PERF_RECORD_MMAP;  	event->mmap.header.size = (sizeof(event->mmap) -  			(sizeof(event->mmap.filename) - size) + machine->id_hdr_size); -	event->mmap.pgoff = args.start; +	event->mmap.pgoff = kmap->ref_reloc_sym->addr;  	event->mmap.start = map->start;  	event->mmap.len   = map->end - event->mmap.start;  	event->mmap.pid   = machine->pid; @@ -516,52 +608,58 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)  int perf_event__process_comm(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_comm_event(machine, event); +	return machine__process_comm_event(machine, event, sample);  }  int perf_event__process_lost(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_lost_event(machine, event); +	return machine__process_lost_event(machine, event, sample);  }  size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)  { -	return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", +	return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n",  		       event->mmap.pid, event->mmap.tid, event->mmap.start, -		       event->mmap.len, event->mmap.pgoff, event->mmap.filename); +		       event->mmap.len, event->mmap.pgoff, +		       (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', +		       event->mmap.filename);  }  size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)  {  	return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 -			   " %02x:%02x %"PRIu64" %"PRIu64"]: %s\n", +			   " %02x:%02x %"PRIu64" %"PRIu64"]: %c%c%c%c %s\n",  		       event->mmap2.pid, event->mmap2.tid, event->mmap2.start,  		       event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj,  		       event->mmap2.min, event->mmap2.ino,  		       event->mmap2.ino_generation, +		       (event->mmap2.prot & PROT_READ) ? 'r' : '-', +		       (event->mmap2.prot & PROT_WRITE) ? 'w' : '-', +		       (event->mmap2.prot & PROT_EXEC) ? 'x' : '-', +		       (event->mmap2.flags & MAP_SHARED) ? 's' : 'p',  		       event->mmap2.filename);  }  int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_mmap_event(machine, event); +	return machine__process_mmap_event(machine, event, sample);  }  int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_mmap2_event(machine, event); +	return machine__process_mmap2_event(machine, event, sample);  }  size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) @@ -573,18 +671,18 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)  int perf_event__process_fork(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_fork_event(machine, event); +	return machine__process_fork_event(machine, event, sample);  }  int perf_event__process_exit(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_exit_event(machine, event); +	return machine__process_exit_event(machine, event, sample);  }  size_t perf_event__fprintf(union perf_event *event, FILE *fp) @@ -615,24 +713,25 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)  int perf_event__process(struct perf_tool *tool __maybe_unused,  			union perf_event *event, -			struct perf_sample *sample __maybe_unused, +			struct perf_sample *sample,  			struct machine *machine)  { -	return machine__process_event(machine, event); +	return machine__process_event(machine, event, sample);  } -void thread__find_addr_map(struct thread *self, +void thread__find_addr_map(struct thread *thread,  			   struct machine *machine, u8 cpumode,  			   enum map_type type, u64 addr,  			   struct addr_location *al)  { -	struct map_groups *mg = &self->mg; +	struct map_groups *mg = thread->mg;  	bool load_map = false; -	al->thread = self; +	al->machine = machine; +	al->thread = thread;  	al->addr = addr;  	al->cpumode = cpumode; -	al->filtered = false; +	al->filtered = 0;  	if (machine == NULL) {  		al->map = NULL; @@ -649,25 +748,20 @@ void thread__find_addr_map(struct thread *self,  		al->level = 'g';  		mg = &machine->kmaps;  		load_map = true; +	} else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) { +		al->level = 'u';  	} else { -		/* -		 * 'u' means guest os user space. -		 * TODO: We don't support guest user space. Might support late. -		 */ -		if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) -			al->level = 'u'; -		else -			al->level = 'H'; +		al->level = 'H';  		al->map = NULL;  		if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||  			cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&  			!perf_guest) -			al->filtered = true; +			al->filtered |= (1 << HIST_FILTER__GUEST);  		if ((cpumode == PERF_RECORD_MISC_USER ||  			cpumode == PERF_RECORD_MISC_KERNEL) &&  			!perf_host) -			al->filtered = true; +			al->filtered |= (1 << HIST_FILTER__HOST);  		return;  	} @@ -719,16 +813,12 @@ int perf_event__preprocess_sample(const union perf_event *event,  {  	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;  	struct thread *thread = machine__findnew_thread(machine, sample->pid, -							sample->pid); +							sample->tid);  	if (thread == NULL)  		return -1; -	if (symbol_conf.comm_list && -	    !strlist__has_entry(symbol_conf.comm_list, thread->comm)) -		goto out_filtered; - -	dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); +	dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);  	/*  	 * Have we already created the kernel maps for this machine?  	 * @@ -745,6 +835,10 @@ int perf_event__preprocess_sample(const union perf_event *event,  	dump_printf(" ...... dso: %s\n",  		    al->map ? al->map->dso->long_name :  			al->level == 'H' ? "[hypervisor]" : "<not found>"); + +	if (thread__is_filtered(thread)) +		al->filtered |= (1 << HIST_FILTER__THREAD); +  	al->sym = NULL;  	al->cpu = sample->cpu; @@ -756,8 +850,9 @@ int perf_event__preprocess_sample(const union perf_event *event,  						  dso->short_name) ||  			       (dso->short_name != dso->long_name &&  				strlist__has_entry(symbol_conf.dso_list, -						   dso->long_name))))) -			goto out_filtered; +						   dso->long_name))))) { +			al->filtered |= (1 << HIST_FILTER__DSO); +		}  		al->sym = map__find_symbol(al->map, al->addr,  					   machine->symbol_filter); @@ -765,12 +860,9 @@ int perf_event__preprocess_sample(const union perf_event *event,  	if (symbol_conf.sym_list &&  		(!al->sym || !strlist__has_entry(symbol_conf.sym_list, -						al->sym->name))) -		goto out_filtered; - -	return 0; +						al->sym->name))) { +		al->filtered |= (1 << HIST_FILTER__SYMBOL); +	} -out_filtered: -	al->filtered = true;  	return 0;  } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c67ecc457d2..e5dd40addb3 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -7,6 +7,7 @@  #include "../perf.h"  #include "map.h"  #include "build-id.h" +#include "perf_regs.h"  struct mmap_event {  	struct perf_event_header header; @@ -27,6 +28,8 @@ struct mmap2_event {  	u32 min;  	u64 ino;  	u64 ino_generation; +	u32 prot; +	u32 flags;  	char filename[PATH_MAX];  }; @@ -61,6 +64,12 @@ struct read_event {  	u64 id;  }; +struct throttle_event { +	struct perf_event_header header; +	u64 time; +	u64 id; +	u64 stream_id; +};  #define PERF_SAMPLE_MASK				\  	(PERF_SAMPLE_IP | PERF_SAMPLE_TID |		\ @@ -69,6 +78,9 @@ struct read_event {  	 PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD |		\  	 PERF_SAMPLE_IDENTIFIER) +/* perf sample has 16 bits size limit */ +#define PERF_SAMPLE_MAX_SIZE (1 << 16) +  struct sample_event {  	struct perf_event_header        header;  	u64 array[]; @@ -76,7 +88,12 @@ struct sample_event {  struct regs_dump {  	u64 abi; +	u64 mask;  	u64 *regs; + +	/* Cached values/mask filled by first register access. */ +	u64 cache_regs[PERF_REGS_MAX]; +	u64 cache_mask;  };  struct stack_dump { @@ -102,6 +119,30 @@ struct sample_read {  	};  }; +struct ip_callchain { +	u64 nr; +	u64 ips[0]; +}; + +struct branch_flags { +	u64 mispred:1; +	u64 predicted:1; +	u64 in_tx:1; +	u64 abort:1; +	u64 reserved:60; +}; + +struct branch_entry { +	u64			from; +	u64			to; +	struct branch_flags	flags; +}; + +struct branch_stack { +	u64			nr; +	struct branch_entry	entries[0]; +}; +  struct perf_sample {  	u64 ip;  	u32 pid, tid; @@ -111,6 +152,7 @@ struct perf_sample {  	u64 stream_id;  	u64 period;  	u64 weight; +	u64 transaction;  	u32 cpu;  	u32 raw_size;  	u64 data_src; @@ -177,6 +219,7 @@ union perf_event {  	struct fork_event		fork;  	struct lost_event		lost;  	struct read_event		read; +	struct throttle_event		throttle;  	struct sample_event		sample;  	struct attr_event		attr;  	struct event_type_event		event_type; @@ -197,14 +240,13 @@ typedef int (*perf_event__handler_t)(struct perf_tool *tool,  int perf_event__synthesize_thread_map(struct perf_tool *tool,  				      struct thread_map *threads,  				      perf_event__handler_t process, -				      struct machine *machine); +				      struct machine *machine, bool mmap_data);  int perf_event__synthesize_threads(struct perf_tool *tool,  				   perf_event__handler_t process, -				   struct machine *machine); +				   struct machine *machine, bool mmap_data);  int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,  				       perf_event__handler_t process, -				       struct machine *machine, -				       const char *symbol_name); +				       struct machine *machine);  int perf_event__synthesize_modules(struct perf_tool *tool,  				   perf_event__handler_t process, @@ -240,7 +282,8 @@ int perf_event__process(struct perf_tool *tool,  			struct machine *machine);  struct addr_location; -int perf_event__preprocess_sample(const union perf_event *self, + +int perf_event__preprocess_sample(const union perf_event *event,  				  struct machine *machine,  				  struct addr_location *al,  				  struct perf_sample *sample); @@ -248,16 +291,26 @@ int perf_event__preprocess_sample(const union perf_event *self,  const char *perf_event__name(unsigned int id);  size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, -				     u64 sample_regs_user, u64 read_format); +				     u64 read_format);  int perf_event__synthesize_sample(union perf_event *event, u64 type, -				  u64 sample_regs_user, u64 read_format, +				  u64 read_format,  				  const struct perf_sample *sample,  				  bool swapped); +int perf_event__synthesize_mmap_events(struct perf_tool *tool, +				       union perf_event *event, +				       pid_t pid, pid_t tgid, +				       perf_event__handler_t process, +				       struct machine *machine, +				       bool mmap_data); +  size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp);  size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp);  size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp);  size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);  size_t perf_event__fprintf(union perf_event *event, FILE *fp); +u64 kallsyms__get_function_start(const char *kallsyms_filename, +				 const char *symbol_name); +  #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f9f77bee0b1..59ef2802fcf 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -7,7 +7,7 @@   * Released under the GPL v2. (and only v2, not any later version)   */  #include "util.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include <poll.h>  #include "cpumap.h"  #include "thread_map.h" @@ -18,6 +18,7 @@  #include <unistd.h>  #include "parse-events.h" +#include "parse-options.h"  #include <sys/mman.h> @@ -49,6 +50,18 @@ struct perf_evlist *perf_evlist__new(void)  	return evlist;  } +struct perf_evlist *perf_evlist__new_default(void) +{ +	struct perf_evlist *evlist = perf_evlist__new(); + +	if (evlist && perf_evlist__add_default(evlist)) { +		perf_evlist__delete(evlist); +		evlist = NULL; +	} + +	return evlist; +} +  /**   * perf_evlist__set_id_pos - set the positions of event ids.   * @evlist: selected event list @@ -68,7 +81,7 @@ static void perf_evlist__update_id_pos(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) +	evlist__for_each(evlist, evsel)  		perf_evsel__calc_id_pos(evsel);  	perf_evlist__set_id_pos(evlist); @@ -78,7 +91,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist)  {  	struct perf_evsel *pos, *n; -	list_for_each_entry_safe(pos, n, &evlist->entries, node) { +	evlist__for_each_safe(evlist, n, pos) {  		list_del_init(&pos->node);  		perf_evsel__delete(pos);  	} @@ -88,14 +101,18 @@ static void perf_evlist__purge(struct perf_evlist *evlist)  void perf_evlist__exit(struct perf_evlist *evlist)  { -	free(evlist->mmap); -	free(evlist->pollfd); -	evlist->mmap = NULL; -	evlist->pollfd = NULL; +	zfree(&evlist->mmap); +	zfree(&evlist->pollfd);  }  void perf_evlist__delete(struct perf_evlist *evlist)  { +	perf_evlist__munmap(evlist); +	perf_evlist__close(evlist); +	cpu_map__delete(evlist->cpus); +	thread_map__delete(evlist->threads); +	evlist->cpus = NULL; +	evlist->threads = NULL;  	perf_evlist__purge(evlist);  	perf_evlist__exit(evlist);  	free(evlist); @@ -104,6 +121,8 @@ void perf_evlist__delete(struct perf_evlist *evlist)  void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)  {  	list_add_tail(&entry->node, &evlist->entries); +	entry->idx = evlist->nr_entries; +  	if (!evlist->nr_entries++)  		perf_evlist__set_id_pos(evlist);  } @@ -129,7 +148,7 @@ void __perf_evlist__set_leader(struct list_head *list)  	leader->nr_members = evsel->idx - leader->idx + 1; -	list_for_each_entry(evsel, list, node) { +	__evlist__for_each(list, evsel) {  		evsel->leader = leader;  	}  } @@ -152,7 +171,7 @@ int perf_evlist__add_default(struct perf_evlist *evlist)  	event_attr_init(&attr); -	evsel = perf_evsel__new(&attr, 0); +	evsel = perf_evsel__new(&attr);  	if (evsel == NULL)  		goto error; @@ -177,7 +196,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist,  	size_t i;  	for (i = 0; i < nr_attrs; i++) { -		evsel = perf_evsel__new(attrs + i, evlist->nr_entries + i); +		evsel = perf_evsel__new_idx(attrs + i, evlist->nr_entries + i);  		if (evsel == NULL)  			goto out_delete_partial_list;  		list_add_tail(&evsel->node, &head); @@ -188,7 +207,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist,  	return 0;  out_delete_partial_list: -	list_for_each_entry_safe(evsel, n, &head, node) +	__evlist__for_each_safe(&head, n, evsel)  		perf_evsel__delete(evsel);  	return -1;  } @@ -209,7 +228,7 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (evsel->attr.type   == PERF_TYPE_TRACEPOINT &&  		    (int)evsel->attr.config == id)  			return evsel; @@ -224,7 +243,7 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) &&  		    (strcmp(evsel->name, name) == 0))  			return evsel; @@ -236,13 +255,12 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,  int perf_evlist__add_newtp(struct perf_evlist *evlist,  			   const char *sys, const char *name, void *handler)  { -	struct perf_evsel *evsel; +	struct perf_evsel *evsel = perf_evsel__newtp(sys, name); -	evsel = perf_evsel__newtp(sys, name, evlist->nr_entries);  	if (evsel == NULL)  		return -1; -	evsel->handler.func = handler; +	evsel->handler = handler;  	perf_evlist__add(evlist, evsel);  	return 0;  } @@ -255,7 +273,7 @@ void perf_evlist__disable(struct perf_evlist *evlist)  	int nr_threads = thread_map__nr(evlist->threads);  	for (cpu = 0; cpu < nr_cpus; cpu++) { -		list_for_each_entry(pos, &evlist->entries, node) { +		evlist__for_each(evlist, pos) {  			if (!perf_evsel__is_group_leader(pos) || !pos->fd)  				continue;  			for (thread = 0; thread < nr_threads; thread++) @@ -273,7 +291,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)  	int nr_threads = thread_map__nr(evlist->threads);  	for (cpu = 0; cpu < nr_cpus; cpu++) { -		list_for_each_entry(pos, &evlist->entries, node) { +		evlist__for_each(evlist, pos) {  			if (!perf_evsel__is_group_leader(pos) || !pos->fd)  				continue;  			for (thread = 0; thread < nr_threads; thread++) @@ -527,7 +545,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)  		if ((old & md->mask) + size != ((old + size) & md->mask)) {  			unsigned int offset = old;  			unsigned int len = min(sizeof(*event), size), cpy; -			void *dst = &md->event_copy; +			void *dst = md->event_copy;  			do {  				cpy = min(md->mask + 1 - (offset & md->mask), len); @@ -537,7 +555,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)  				len -= cpy;  			} while (len); -			event = &md->event_copy; +			event = (union perf_event *) md->event_copy;  		}  		old += size; @@ -545,12 +563,19 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)  	md->prev = old; -	if (!evlist->overwrite) -		perf_mmap__write_tail(md, old); -  	return event;  } +void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) +{ +	if (!evlist->overwrite) { +		struct perf_mmap *md = &evlist->mmap[idx]; +		unsigned int old = md->prev; + +		perf_mmap__write_tail(md, old); +	} +} +  static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)  {  	if (evlist->mmap[idx].base != NULL) { @@ -563,11 +588,13 @@ void perf_evlist__munmap(struct perf_evlist *evlist)  {  	int i; +	if (evlist->mmap == NULL) +		return; +  	for (i = 0; i < evlist->nr_mmaps; i++)  		__perf_evlist__munmap(evlist, i); -	free(evlist->mmap); -	evlist->mmap = NULL; +	zfree(&evlist->mmap);  }  static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) @@ -587,6 +614,8 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist,  	evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot,  				      MAP_SHARED, fd, 0);  	if (evlist->mmap[idx].base == MAP_FAILED) { +		pr_debug2("failed to mmap perf event ring buffer, error %d\n", +			  errno);  		evlist->mmap[idx].base = NULL;  		return -1;  	} @@ -595,9 +624,36 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist,  	return 0;  } -static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) +static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, +				       int prot, int mask, int cpu, int thread, +				       int *output)  {  	struct perf_evsel *evsel; + +	evlist__for_each(evlist, evsel) { +		int fd = FD(evsel, cpu, thread); + +		if (*output == -1) { +			*output = fd; +			if (__perf_evlist__mmap(evlist, idx, prot, mask, +						*output) < 0) +				return -1; +		} else { +			if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) +				return -1; +		} + +		if ((evsel->attr.read_format & PERF_FORMAT_ID) && +		    perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) +			return -1; +	} + +	return 0; +} + +static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, +				     int mask) +{  	int cpu, thread;  	int nr_cpus = cpu_map__nr(evlist->cpus);  	int nr_threads = thread_map__nr(evlist->threads); @@ -607,23 +663,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m  		int output = -1;  		for (thread = 0; thread < nr_threads; thread++) { -			list_for_each_entry(evsel, &evlist->entries, node) { -				int fd = FD(evsel, cpu, thread); - -				if (output == -1) { -					output = fd; -					if (__perf_evlist__mmap(evlist, cpu, -								prot, mask, output) < 0) -						goto out_unmap; -				} else { -					if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) -						goto out_unmap; -				} - -				if ((evsel->attr.read_format & PERF_FORMAT_ID) && -				    perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) -					goto out_unmap; -			} +			if (perf_evlist__mmap_per_evsel(evlist, cpu, prot, mask, +							cpu, thread, &output)) +				goto out_unmap;  		}  	} @@ -635,9 +677,9 @@ out_unmap:  	return -1;  } -static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) +static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, +					int mask)  { -	struct perf_evsel *evsel;  	int thread;  	int nr_threads = thread_map__nr(evlist->threads); @@ -645,23 +687,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in  	for (thread = 0; thread < nr_threads; thread++) {  		int output = -1; -		list_for_each_entry(evsel, &evlist->entries, node) { -			int fd = FD(evsel, 0, thread); - -			if (output == -1) { -				output = fd; -				if (__perf_evlist__mmap(evlist, thread, -							prot, mask, output) < 0) -					goto out_unmap; -			} else { -				if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) -					goto out_unmap; -			} - -			if ((evsel->attr.read_format & PERF_FORMAT_ID) && -			    perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0) -				goto out_unmap; -		} +		if (perf_evlist__mmap_per_evsel(evlist, thread, prot, mask, 0, +						thread, &output)) +			goto out_unmap;  	}  	return 0; @@ -672,20 +700,92 @@ out_unmap:  	return -1;  } -/** perf_evlist__mmap - Create per cpu maps to receive events - * - * @evlist - list of events - * @pages - map length in pages - * @overwrite - overwrite older events? - * - * If overwrite is false the user needs to signal event consuption using: - * - *	struct perf_mmap *m = &evlist->mmap[cpu]; - *	unsigned int head = perf_mmap__read_head(m); +static size_t perf_evlist__mmap_size(unsigned long pages) +{ +	/* 512 kiB: default amount of unprivileged mlocked memory */ +	if (pages == UINT_MAX) +		pages = (512 * 1024) / page_size; +	else if (!is_power_of_2(pages)) +		return 0; + +	return (pages + 1) * page_size; +} + +static long parse_pages_arg(const char *str, unsigned long min, +			    unsigned long max) +{ +	unsigned long pages, val; +	static struct parse_tag tags[] = { +		{ .tag  = 'B', .mult = 1       }, +		{ .tag  = 'K', .mult = 1 << 10 }, +		{ .tag  = 'M', .mult = 1 << 20 }, +		{ .tag  = 'G', .mult = 1 << 30 }, +		{ .tag  = 0 }, +	}; + +	if (str == NULL) +		return -EINVAL; + +	val = parse_tag_value(str, tags); +	if (val != (unsigned long) -1) { +		/* we got file size value */ +		pages = PERF_ALIGN(val, page_size) / page_size; +	} else { +		/* we got pages count value */ +		char *eptr; +		pages = strtoul(str, &eptr, 10); +		if (*eptr != '\0') +			return -EINVAL; +	} + +	if (pages == 0 && min == 0) { +		/* leave number of pages at 0 */ +	} else if (!is_power_of_2(pages)) { +		/* round pages up to next power of 2 */ +		pages = next_pow2_l(pages); +		if (!pages) +			return -EINVAL; +		pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", +			pages * page_size, pages); +	} + +	if (pages > max) +		return -EINVAL; + +	return pages; +} + +int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, +				  int unset __maybe_unused) +{ +	unsigned int *mmap_pages = opt->value; +	unsigned long max = UINT_MAX; +	long pages; + +	if (max > SIZE_MAX / page_size) +		max = SIZE_MAX / page_size; + +	pages = parse_pages_arg(str, 1, max); +	if (pages < 0) { +		pr_err("Invalid argument for --mmap_pages/-m\n"); +		return -1; +	} + +	*mmap_pages = pages; +	return 0; +} + +/** + * perf_evlist__mmap - Create mmaps to receive events. + * @evlist: list of events + * @pages: map length in pages + * @overwrite: overwrite older events?   * - *	perf_mmap__write_tail(m, head) + * If @overwrite is %false the user needs to signal event consumption using + * perf_mmap__write_tail().  Using perf_evlist__mmap_read() does this + * automatically.   * - * Using perf_evlist__read_on_cpu does this automatically. + * Return: %0 on success, negative error code otherwise.   */  int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  		      bool overwrite) @@ -695,14 +795,6 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  	const struct thread_map *threads = evlist->threads;  	int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask; -        /* 512 kiB: default amount of unprivileged mlocked memory */ -        if (pages == UINT_MAX) -                pages = (512 * 1024) / page_size; -	else if (!is_power_of_2(pages)) -		return -EINVAL; - -	mask = pages * page_size - 1; -  	if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0)  		return -ENOMEM; @@ -710,9 +802,11 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  		return -ENOMEM;  	evlist->overwrite = overwrite; -	evlist->mmap_len = (pages + 1) * page_size; +	evlist->mmap_len = perf_evlist__mmap_size(pages); +	pr_debug("mmap size %zuB\n", evlist->mmap_len); +	mask = evlist->mmap_len - page_size - 1; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if ((evsel->attr.read_format & PERF_FORMAT_ID) &&  		    evsel->sample_id == NULL &&  		    perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0) @@ -725,8 +819,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  	return perf_evlist__mmap_per_cpu(evlist, prot, mask);  } -int perf_evlist__create_maps(struct perf_evlist *evlist, -			     struct perf_target *target) +int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)  {  	evlist->threads = thread_map__new_str(target->pid, target->tid,  					      target->uid); @@ -734,9 +827,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist,  	if (evlist->threads == NULL)  		return -1; -	if (perf_target__has_task(target)) -		evlist->cpus = cpu_map__dummy_new(); -	else if (!perf_target__has_cpu(target) && !target->uses_mmap) +	if (target__uses_dummy_map(target))  		evlist->cpus = cpu_map__dummy_new();  	else  		evlist->cpus = cpu_map__new(target->cpu_list); @@ -751,14 +842,6 @@ out_delete_threads:  	return -1;  } -void perf_evlist__delete_maps(struct perf_evlist *evlist) -{ -	cpu_map__delete(evlist->cpus); -	thread_map__delete(evlist->threads); -	evlist->cpus	= NULL; -	evlist->threads = NULL; -} -  int perf_evlist__apply_filters(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel; @@ -766,7 +849,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist)  	const int ncpus = cpu_map__nr(evlist->cpus),  		  nthreads = thread_map__nr(evlist->threads); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (evsel->filter == NULL)  			continue; @@ -785,7 +868,7 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)  	const int ncpus = cpu_map__nr(evlist->cpus),  		  nthreads = thread_map__nr(evlist->threads); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter);  		if (err)  			break; @@ -804,7 +887,7 @@ bool perf_evlist__valid_sample_type(struct perf_evlist *evlist)  	if (evlist->id_pos < 0 || evlist->is_pos < 0)  		return false; -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		if (pos->id_pos != evlist->id_pos ||  		    pos->is_pos != evlist->is_pos)  			return false; @@ -820,7 +903,7 @@ u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist)  	if (evlist->combined_sample_type)  		return evlist->combined_sample_type; -	list_for_each_entry(evsel, &evlist->entries, node) +	evlist__for_each(evlist, evsel)  		evlist->combined_sample_type |= evsel->attr.sample_type;  	return evlist->combined_sample_type; @@ -838,7 +921,7 @@ bool perf_evlist__valid_read_format(struct perf_evlist *evlist)  	u64 read_format = first->attr.read_format;  	u64 sample_type = first->attr.sample_type; -	list_for_each_entry_continue(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		if (read_format != pos->attr.read_format)  			return false;  	} @@ -895,7 +978,7 @@ bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist)  {  	struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; -	list_for_each_entry_continue(pos, &evlist->entries, node) { +	evlist__for_each_continue(evlist, pos) {  		if (first->attr.sample_id_all != pos->attr.sample_id_all)  			return false;  	} @@ -920,9 +1003,12 @@ void perf_evlist__close(struct perf_evlist *evlist)  	struct perf_evsel *evsel;  	int ncpus = cpu_map__nr(evlist->cpus);  	int nthreads = thread_map__nr(evlist->threads); +	int n; -	list_for_each_entry_reverse(evsel, &evlist->entries, node) -		perf_evsel__close(evsel, ncpus, nthreads); +	evlist__for_each_reverse(evlist, evsel) { +		n = evsel->cpus ? evsel->cpus->nr : ncpus; +		perf_evsel__close(evsel, n, nthreads); +	}  }  int perf_evlist__open(struct perf_evlist *evlist) @@ -932,7 +1018,7 @@ int perf_evlist__open(struct perf_evlist *evlist)  	perf_evlist__update_id_pos(evlist); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		err = perf_evsel__open(evsel, evlist->cpus, evlist->threads);  		if (err < 0)  			goto out_err; @@ -945,10 +1031,9 @@ out_err:  	return err;  } -int perf_evlist__prepare_workload(struct perf_evlist *evlist, -				  struct perf_target *target, +int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target,  				  const char *argv[], bool pipe_output, -				  bool want_signal) +				  void (*exec_error)(int signo, siginfo_t *info, void *ucontext))  {  	int child_ready_pipe[2], go_pipe[2];  	char bf; @@ -992,13 +1077,26 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,  		execvp(argv[0], (char **)argv); -		perror(argv[0]); -		if (want_signal) -			kill(getppid(), SIGUSR1); +		if (exec_error) { +			union sigval val; + +			val.sival_int = errno; +			if (sigqueue(getppid(), SIGUSR1, val)) +				perror(argv[0]); +		} else +			perror(argv[0]);  		exit(-1);  	} -	if (perf_target__none(target)) +	if (exec_error) { +		struct sigaction act = { +			.sa_flags     = SA_SIGINFO, +			.sa_sigaction = exec_error, +		}; +		sigaction(SIGUSR1, &act, NULL); +	} + +	if (target__none(target))  		evlist->threads->map[0] = evlist->workload.pid;  	close(child_ready_pipe[1]); @@ -1059,10 +1157,89 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)  	struct perf_evsel *evsel;  	size_t printed = 0; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "",  				   perf_evsel__name(evsel));  	} -	return printed + fprintf(fp, "\n");; +	return printed + fprintf(fp, "\n"); +} + +int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, +			     int err, char *buf, size_t size) +{ +	char sbuf[128]; + +	switch (err) { +	case ENOENT: +		scnprintf(buf, size, "%s", +			  "Error:\tUnable to find debugfs\n" +			  "Hint:\tWas your kernel was compiled with debugfs support?\n" +			  "Hint:\tIs the debugfs filesystem mounted?\n" +			  "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); +		break; +	case EACCES: +		scnprintf(buf, size, +			  "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" +			  "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", +			  debugfs_mountpoint, debugfs_mountpoint); +		break; +	default: +		scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); +		break; +	} + +	return 0; +} + +int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, +			       int err, char *buf, size_t size) +{ +	int printed, value; +	char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); + +	switch (err) { +	case EACCES: +	case EPERM: +		printed = scnprintf(buf, size, +				    "Error:\t%s.\n" +				    "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); + +		value = perf_event_paranoid(); + +		printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); + +		if (value >= 2) { +			printed += scnprintf(buf + printed, size - printed, +					     "For your workloads it needs to be <= 1\nHint:\t"); +		} +		printed += scnprintf(buf + printed, size - printed, +				     "For system wide tracing it needs to be set to -1"); + +		printed += scnprintf(buf + printed, size - printed, +				    ".\nHint:\tThe current value is %d.", value); +		break; +	default: +		scnprintf(buf, size, "%s", emsg); +		break; +	} + +	return 0; +} + +void perf_evlist__to_front(struct perf_evlist *evlist, +			   struct perf_evsel *move_evsel) +{ +	struct perf_evsel *evsel, *n; +	LIST_HEAD(move); + +	if (move_evsel == perf_evlist__first(evlist)) +		return; + +	evlist__for_each_safe(evlist, n, evsel) { +		if (evsel->leader == move_evsel->leader) +			list_move_tail(&evsel->node, &move); +	} + +	list_splice(&move, &evlist->entries);  } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 880d7139d2f..f5173cd6369 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -12,7 +12,7 @@  struct pollfd;  struct thread_map;  struct cpu_map; -struct perf_record_opts; +struct record_opts;  #define PERF_EVLIST__HLIST_BITS 8  #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) @@ -21,7 +21,7 @@ struct perf_mmap {  	void		 *base;  	int		 mask;  	unsigned int	 prev; -	union perf_event event_copy; +	char		 event_copy[PERF_SAMPLE_MAX_SIZE];  };  struct perf_evlist { @@ -31,7 +31,7 @@ struct perf_evlist {  	int		 nr_groups;  	int		 nr_fds;  	int		 nr_mmaps; -	int		 mmap_len; +	size_t		 mmap_len;  	int		 id_pos;  	int		 is_pos;  	u64		 combined_sample_type; @@ -53,6 +53,7 @@ struct perf_evsel_str_handler {  };  struct perf_evlist *perf_evlist__new(void); +struct perf_evlist *perf_evlist__new_default(void);  void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,  		       struct thread_map *threads);  void perf_evlist__exit(struct perf_evlist *evlist); @@ -87,22 +88,29 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);  struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); -union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); +union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx); + +void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx);  int perf_evlist__open(struct perf_evlist *evlist);  void perf_evlist__close(struct perf_evlist *evlist);  void perf_evlist__set_id_pos(struct perf_evlist *evlist);  bool perf_can_sample_identifier(void); -void perf_evlist__config(struct perf_evlist *evlist, -			 struct perf_record_opts *opts); +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts); +int record_opts__config(struct record_opts *opts);  int perf_evlist__prepare_workload(struct perf_evlist *evlist, -				  struct perf_target *target, +				  struct target *target,  				  const char *argv[], bool pipe_output, -				  bool want_signal); +				  void (*exec_error)(int signo, siginfo_t *info, +						     void *ucontext));  int perf_evlist__start_workload(struct perf_evlist *evlist); +int perf_evlist__parse_mmap_pages(const struct option *opt, +				  const char *str, +				  int unset); +  int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  		      bool overwrite);  void perf_evlist__munmap(struct perf_evlist *evlist); @@ -126,9 +134,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,  	evlist->threads	= threads;  } -int perf_evlist__create_maps(struct perf_evlist *evlist, -			     struct perf_target *target); -void perf_evlist__delete_maps(struct perf_evlist *evlist); +int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target);  int perf_evlist__apply_filters(struct perf_evlist *evlist);  void __perf_evlist__set_leader(struct list_head *list); @@ -163,10 +169,13 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)  size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); +int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); +int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); +  static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)  {  	struct perf_event_mmap_page *pc = mm->base; -	int head = pc->data_head; +	int head = ACCESS_ONCE(pc->data_head);  	rmb();  	return head;  } @@ -179,8 +188,78 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md,  	/*  	 * ensure all reads are done before we write the tail out.  	 */ -	/* mb(); */ +	mb();  	pc->data_tail = tail;  } +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str); +void perf_evlist__to_front(struct perf_evlist *evlist, +			   struct perf_evsel *move_evsel); + +/** + * __evlist__for_each - iterate thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each(list, evsel) \ +        list_for_each_entry(evsel, list, node) + +/** + * evlist__for_each - iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each(evlist, evsel) \ +	__evlist__for_each(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_continue - continue iteration thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_continue(list, evsel) \ +        list_for_each_entry_continue(evsel, list, node) + +/** + * evlist__for_each_continue - continue iteration thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each_continue(evlist, evsel) \ +	__evlist__for_each_continue(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_reverse - iterate thru all the evsels in reverse order + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_reverse(list, evsel) \ +        list_for_each_entry_reverse(evsel, list, node) + +/** + * evlist__for_each_reverse - iterate thru all the evsels in reverse order + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each_reverse(evlist, evsel) \ +	__evlist__for_each_reverse(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_safe - safely iterate thru all the evsels + * @list: list_head instance to iterate + * @tmp: struct evsel temp iterator + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_safe(list, tmp, evsel) \ +        list_for_each_entry_safe(evsel, tmp, list, node) + +/** + * evlist__for_each_safe - safely iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + * @tmp: struct evsel temp iterator + */ +#define evlist__for_each_safe(evlist, tmp, evsel) \ +	__evlist__for_each_safe(&(evlist)->entries, tmp, evsel) +  #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0ce9febf1ba..8606175fe1e 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -9,7 +9,7 @@  #include <byteswap.h>  #include <linux/bitops.h> -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include <traceevent/event-parse.h>  #include <linux/hw_breakpoint.h>  #include <linux/perf_event.h> @@ -23,6 +23,7 @@  #include "target.h"  #include "perf_regs.h"  #include "debug.h" +#include "trace-event.h"  static struct {  	bool sample_id_all; @@ -162,13 +163,15 @@ void perf_evsel__init(struct perf_evsel *evsel,  	evsel->idx	   = idx;  	evsel->attr	   = *attr;  	evsel->leader	   = evsel; +	evsel->unit	   = ""; +	evsel->scale	   = 1.0;  	INIT_LIST_HEAD(&evsel->node);  	hists__init(&evsel->hists);  	evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);  	perf_evsel__calc_id_pos(evsel);  } -struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) +struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)  {  	struct perf_evsel *evsel = zalloc(sizeof(*evsel)); @@ -178,48 +181,7 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)  	return evsel;  } -struct event_format *event_format__new(const char *sys, const char *name) -{ -	int fd, n; -	char *filename; -	void *bf = NULL, *nbf; -	size_t size = 0, alloc_size = 0; -	struct event_format *format = NULL; - -	if (asprintf(&filename, "%s/%s/%s/format", tracing_events_path, sys, name) < 0) -		goto out; - -	fd = open(filename, O_RDONLY); -	if (fd < 0) -		goto out_free_filename; - -	do { -		if (size == alloc_size) { -			alloc_size += BUFSIZ; -			nbf = realloc(bf, alloc_size); -			if (nbf == NULL) -				goto out_free_bf; -			bf = nbf; -		} - -		n = read(fd, bf + size, alloc_size - size); -		if (n < 0) -			goto out_free_bf; -		size += n; -	} while (n > 0); - -	pevent_parse_format(&format, bf, size, sys); - -out_free_bf: -	free(bf); -	close(fd); -out_free_filename: -	free(filename); -out: -	return format; -} - -struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) +struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)  {  	struct perf_evsel *evsel = zalloc(sizeof(*evsel)); @@ -233,7 +195,7 @@ struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx)  		if (asprintf(&evsel->name, "%s:%s", sys, name) < 0)  			goto out_free; -		evsel->tp_format = event_format__new(sys, name); +		evsel->tp_format = trace_event__tp_format(sys, name);  		if (evsel->tp_format == NULL)  			goto out_free; @@ -246,7 +208,7 @@ struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx)  	return evsel;  out_free: -	free(evsel->name); +	zfree(&evsel->name);  	free(evsel);  	return NULL;  } @@ -538,6 +500,34 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)  	return ret;  } +static void +perf_evsel__config_callgraph(struct perf_evsel *evsel, +			     struct record_opts *opts) +{ +	bool function = perf_evsel__is_function_event(evsel); +	struct perf_event_attr *attr = &evsel->attr; + +	perf_evsel__set_sample_bit(evsel, CALLCHAIN); + +	if (opts->call_graph == CALLCHAIN_DWARF) { +		if (!function) { +			perf_evsel__set_sample_bit(evsel, REGS_USER); +			perf_evsel__set_sample_bit(evsel, STACK_USER); +			attr->sample_regs_user = PERF_REGS_MASK; +			attr->sample_stack_user = opts->stack_dump_size; +			attr->exclude_callchain_user = 1; +		} else { +			pr_info("Cannot use DWARF unwind for function trace event," +				" falling back to framepointers.\n"); +		} +	} + +	if (function) { +		pr_info("Disabling user space callchains for function trace event.\n"); +		attr->exclude_callchain_user = 1; +	} +} +  /*   * The enable_on_exec/disabled value strategy:   * @@ -566,12 +556,12 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)   *     enable/disable events specifically, as there's no   *     initial traced exec call.   */ -void perf_evsel__config(struct perf_evsel *evsel, -			struct perf_record_opts *opts) +void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)  {  	struct perf_evsel *leader = evsel->leader;  	struct perf_event_attr *attr = &evsel->attr;  	int track = !evsel->idx; /* only the first counter needs these */ +	bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread;  	attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;  	attr->inherit	    = !opts->no_inherit; @@ -599,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel,  	}  	/* -	 * We default some events to a 1 default interval. But keep +	 * We default some events to have a default interval. But keep  	 * it a weak assumption overridable by the user.  	 */ -	if (!attr->sample_period || (opts->user_freq != UINT_MAX && +	if (!attr->sample_period || (opts->user_freq != UINT_MAX ||  				     opts->user_interval != ULLONG_MAX)) {  		if (opts->freq) {  			perf_evsel__set_sample_bit(evsel, PERIOD); @@ -633,19 +623,10 @@ void perf_evsel__config(struct perf_evsel *evsel,  		attr->mmap_data = track;  	} -	if (opts->call_graph) { -		perf_evsel__set_sample_bit(evsel, CALLCHAIN); +	if (opts->call_graph_enabled) +		perf_evsel__config_callgraph(evsel, opts); -		if (opts->call_graph == CALLCHAIN_DWARF) { -			perf_evsel__set_sample_bit(evsel, REGS_USER); -			perf_evsel__set_sample_bit(evsel, STACK_USER); -			attr->sample_regs_user = PERF_REGS_MASK; -			attr->sample_stack_user = opts->stack_dump_size; -			attr->exclude_callchain_user = 1; -		} -	} - -	if (perf_target__has_cpu(&opts->target)) +	if (target__has_cpu(&opts->target))  		perf_evsel__set_sample_bit(evsel, CPU);  	if (opts->period) @@ -653,7 +634,7 @@ void perf_evsel__config(struct perf_evsel *evsel,  	if (!perf_missing_features.sample_id_all &&  	    (opts->sample_time || !opts->no_inherit || -	     perf_target__has_cpu(&opts->target))) +	     target__has_cpu(&opts->target) || per_cpu))  		perf_evsel__set_sample_bit(evsel, TIME);  	if (opts->raw_samples) { @@ -663,9 +644,9 @@ void perf_evsel__config(struct perf_evsel *evsel,  	}  	if (opts->sample_address) -		attr->sample_type	|= PERF_SAMPLE_DATA_SRC; +		perf_evsel__set_sample_bit(evsel, DATA_SRC); -	if (opts->no_delay) { +	if (opts->no_buffering) {  		attr->watermark = 0;  		attr->wakeup_events = 1;  	} @@ -675,12 +656,15 @@ void perf_evsel__config(struct perf_evsel *evsel,  	}  	if (opts->sample_weight) -		attr->sample_type	|= PERF_SAMPLE_WEIGHT; +		perf_evsel__set_sample_bit(evsel, WEIGHT);  	attr->mmap  = track;  	attr->mmap2 = track && !perf_missing_features.mmap2;  	attr->comm  = track; +	if (opts->sample_transaction) +		perf_evsel__set_sample_bit(evsel, TRANSACTION); +  	/*  	 * XXX see the function comment above  	 * @@ -694,7 +678,8 @@ void perf_evsel__config(struct perf_evsel *evsel,  	 * Setting enable_on_exec for independent events and  	 * group leaders for traced executed by perf.  	 */ -	if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) +	if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel) && +		!opts->initial_delay)  		attr->enable_on_exec = 1;  } @@ -786,8 +771,7 @@ void perf_evsel__free_id(struct perf_evsel *evsel)  {  	xyarray__delete(evsel->sample_id);  	evsel->sample_id = NULL; -	free(evsel->id); -	evsel->id = NULL; +	zfree(&evsel->id);  }  void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) @@ -803,7 +787,7 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)  void perf_evsel__free_counts(struct perf_evsel *evsel)  { -	free(evsel->counts); +	zfree(&evsel->counts);  }  void perf_evsel__exit(struct perf_evsel *evsel) @@ -817,10 +801,10 @@ void perf_evsel__delete(struct perf_evsel *evsel)  {  	perf_evsel__exit(evsel);  	close_cgroup(evsel->cgrp); -	free(evsel->group_name); +	zfree(&evsel->group_name);  	if (evsel->tp_format)  		pevent_free_format(evsel->tp_format); -	free(evsel->name); +	zfree(&evsel->name);  	free(evsel);  } @@ -983,6 +967,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp)  	ret += PRINT_ATTR2(exclude_host, exclude_guest);  	ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel,  			    "excl.callchain_user", exclude_callchain_user); +	ret += PRINT_ATTR_U32(mmap2);  	ret += PRINT_ATTR_U32(wakeup_events);  	ret += PRINT_ATTR_U32(wakeup_watermark); @@ -1039,7 +1024,7 @@ retry_sample_id:  			group_fd = get_group_fd(evsel, cpu, thread);  retry_open: -			pr_debug2("perf_event_open: pid %d  cpu %d  group_fd %d  flags %#lx\n", +			pr_debug2("sys_perf_event_open: pid %d  cpu %d  group_fd %d  flags %#lx\n",  				  pid, cpus->map[cpu], group_fd, flags);  			FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, @@ -1048,6 +1033,8 @@ retry_open:  								     group_fd, flags);  			if (FD(evsel, cpu, thread) < 0) {  				err = -errno; +				pr_debug2("sys_perf_event_open failed, error %d\n", +					  err);  				goto try_fallback;  			}  			set_rlimit = NO_CHANGE; @@ -1114,7 +1101,6 @@ void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads)  	perf_evsel__close_fd(evsel, ncpus, nthreads);  	perf_evsel__free_fd(evsel); -	evsel->fd = NULL;  }  static struct { @@ -1214,6 +1200,7 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,  		sample->pid = u.val32[0];  		sample->tid = u.val32[1]; +		array--;  	}  	return 0; @@ -1253,7 +1240,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,  	memset(data, 0, sizeof(*data));  	data->cpu = data->pid = data->tid = -1;  	data->stream_id = data->id = data->time = -1ULL; -	data->period = 1; +	data->period = evsel->attr.sample_period;  	data->weight = 0;  	if (event->header.type != PERF_RECORD_SAMPLE) { @@ -1429,10 +1416,11 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,  		array++;  		if (data->user_regs.abi) { -			u64 regs_user = evsel->attr.sample_regs_user; +			u64 mask = evsel->attr.sample_regs_user; -			sz = hweight_long(regs_user) * sizeof(u64); +			sz = hweight_long(mask) * sizeof(u64);  			OVERFLOW_CHECK(array, sz, max_size); +			data->user_regs.mask = mask;  			data->user_regs.regs = (u64 *)array;  			array = (void *)array + sz;  		} @@ -1453,6 +1441,9 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,  			array = (void *)array + sz;  			OVERFLOW_CHECK_u64(array);  			data->user_stack.size = *array++; +			if (WARN_ONCE(data->user_stack.size > sz, +				      "user stack dump failure\n")) +				return -EFAULT;  		}  	} @@ -1470,11 +1461,18 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,  		array++;  	} +	data->transaction = 0; +	if (type & PERF_SAMPLE_TRANSACTION) { +		OVERFLOW_CHECK_u64(array); +		data->transaction = *array; +		array++; +	} +  	return 0;  }  size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, -				     u64 sample_regs_user, u64 read_format) +				     u64 read_format)  {  	size_t sz, result = sizeof(struct sample_event); @@ -1540,7 +1538,7 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,  	if (type & PERF_SAMPLE_REGS_USER) {  		if (sample->user_regs.abi) {  			result += sizeof(u64); -			sz = hweight_long(sample_regs_user) * sizeof(u64); +			sz = hweight_long(sample->user_regs.mask) * sizeof(u64);  			result += sz;  		} else {  			result += sizeof(u64); @@ -1562,11 +1560,14 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,  	if (type & PERF_SAMPLE_DATA_SRC)  		result += sizeof(u64); +	if (type & PERF_SAMPLE_TRANSACTION) +		result += sizeof(u64); +  	return result;  }  int perf_event__synthesize_sample(union perf_event *event, u64 type, -				  u64 sample_regs_user, u64 read_format, +				  u64 read_format,  				  const struct perf_sample *sample,  				  bool swapped)  { @@ -1707,7 +1708,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,  	if (type & PERF_SAMPLE_REGS_USER) {  		if (sample->user_regs.abi) {  			*array++ = sample->user_regs.abi; -			sz = hweight_long(sample_regs_user) * sizeof(u64); +			sz = hweight_long(sample->user_regs.mask) * sizeof(u64);  			memcpy(array, sample->user_regs.regs, sz);  			array = (void *)array + sz;  		} else { @@ -1735,6 +1736,11 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,  		array++;  	} +	if (type & PERF_SAMPLE_TRANSACTION) { +		*array = sample->transaction; +		array++; +	} +  	return 0;  } @@ -1974,16 +1980,14 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,  		evsel->attr.type   = PERF_TYPE_SOFTWARE;  		evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK; -		free(evsel->name); -		evsel->name = NULL; +		zfree(&evsel->name);  		return true;  	}  	return false;  } -int perf_evsel__open_strerror(struct perf_evsel *evsel, -			      struct perf_target *target, +int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,  			      int err, char *msg, size_t size)  {  	switch (err) { diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4a7bdc713ba..a52e9a5bb2d 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -5,12 +5,12 @@  #include <stdbool.h>  #include <stddef.h>  #include <linux/perf_event.h> -#include "types.h" +#include <linux/types.h>  #include "xyarray.h"  #include "cgroup.h"  #include "hist.h"  #include "symbol.h" -  +  struct perf_counts_values {  	union {  		struct { @@ -68,16 +68,15 @@ struct perf_evsel {  	u32			ids;  	struct hists		hists;  	char			*name; +	double			scale; +	const char		*unit;  	struct event_format	*tp_format;  	union {  		void		*priv;  		off_t		id_offset;  	};  	struct cgroup_sel	*cgrp; -	struct { -		void		*func; -		void		*data; -	} handler; +	void			*handler;  	struct cpu_map		*cpus;  	unsigned int		sample_size;  	int			id_pos; @@ -92,15 +91,31 @@ struct perf_evsel {  	char			*group_name;  }; +union u64_swap { +	u64 val64; +	u32 val32[2]; +}; +  #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)  struct cpu_map;  struct thread_map;  struct perf_evlist; -struct perf_record_opts; +struct record_opts; + +struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); + +static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) +{ +	return perf_evsel__new_idx(attr, 0); +} -struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); -struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx); +struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx); + +static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name) +{ +	return perf_evsel__newtp_idx(sys, name, 0); +}  struct event_format *event_format__new(const char *sys, const char *name); @@ -110,7 +125,7 @@ void perf_evsel__exit(struct perf_evsel *evsel);  void perf_evsel__delete(struct perf_evsel *evsel);  void perf_evsel__config(struct perf_evsel *evsel, -			struct perf_record_opts *opts); +			struct record_opts *opts);  int __perf_evsel__sample_size(u64 sample_type);  void perf_evsel__calc_id_pos(struct perf_evsel *evsel); @@ -130,6 +145,7 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];  int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,  					    char *bf, size_t size);  const char *perf_evsel__name(struct perf_evsel *evsel); +  const char *perf_evsel__group_name(struct perf_evsel *evsel);  int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); @@ -197,6 +213,12 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1,  	       (e1->attr.config == e2->attr.config);  } +#define perf_evsel__cmp(a, b)			\ +	((a) &&					\ +	 (b) &&					\ +	 (a)->attr.type == (b)->attr.type &&	\ +	 (a)->attr.config == (b)->attr.config) +  int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,  			      int cpu, int thread, bool scale); @@ -265,6 +287,11 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)  	return list_entry(evsel->node.next, struct perf_evsel, node);  } +static inline struct perf_evsel *perf_evsel__prev(struct perf_evsel *evsel) +{ +	return list_entry(evsel->node.prev, struct perf_evsel, node); +} +  /**   * perf_evsel__is_group_leader - Return whether given evsel is a leader event   * @@ -293,6 +320,24 @@ static inline bool perf_evsel__is_group_event(struct perf_evsel *evsel)  	return perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1;  } +/** + * perf_evsel__is_function_event - Return whether given evsel is a function + * trace event + * + * @evsel - evsel selector to be tested + * + * Return %true if event is function trace event + */ +static inline bool perf_evsel__is_function_event(struct perf_evsel *evsel) +{ +#define FUNCTION_EVENT "ftrace:function" + +	return evsel->name && +	       !strncmp(FUNCTION_EVENT, evsel->name, sizeof(FUNCTION_EVENT)); + +#undef FUNCTION_EVENT +} +  struct perf_attr_details {  	bool freq;  	bool verbose; @@ -304,8 +349,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,  bool perf_evsel__fallback(struct perf_evsel *evsel, int err,  			  char *msg, size_t msgsize); -int perf_evsel__open_strerror(struct perf_evsel *evsel, -			      struct perf_target *target, +int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,  			      int err, char *msg, size_t size);  static inline int perf_evsel__group_idx(struct perf_evsel *evsel) diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index 3ac38031d53..36a885d2cd2 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh @@ -22,7 +22,7 @@ do       }' "Documentation/perf-$cmd.txt"  done -echo "#ifdef LIBELF_SUPPORT" +echo "#ifdef HAVE_LIBELF_SUPPORT"  sed -n -e 's/^perf-\([^ 	]*\)[ 	].* full.*/\1/p' command-list.txt |  sort |  while read cmd @@ -35,5 +35,5 @@ do  	    p       }' "Documentation/perf-$cmd.txt"  done -echo "#endif /* LIBELF_SUPPORT */" +echo "#endif /* HAVE_LIBELF_SUPPORT */"  echo "};" diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 26441d0e571..893f8e2df92 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -22,6 +22,7 @@  #include "vdso.h"  #include "strbuf.h"  #include "build-id.h" +#include "data.h"  static bool no_buildid_cache = false; @@ -176,7 +177,7 @@ perf_header__set_cmdline(int argc, const char **argv)  			continue;		\  		else -static int write_buildid(char *name, size_t name_len, u8 *build_id, +static int write_buildid(const char *name, size_t name_len, u8 *build_id,  			 pid_t pid, u16 misc, int fd)  {  	int err; @@ -199,14 +200,16 @@ static int write_buildid(char *name, size_t name_len, u8 *build_id,  	return write_padded(fd, name, name_len + 1, len);  } -static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, -				u16 misc, int fd) +static int __dsos__write_buildid_table(struct list_head *head, +				       struct machine *machine, +				       pid_t pid, u16 misc, int fd)  { +	char nm[PATH_MAX];  	struct dso *pos;  	dsos__for_each_with_build_id(pos, head) {  		int err; -		char  *name; +		const char *name;  		size_t name_len;  		if (!pos->hit) @@ -215,6 +218,10 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,  		if (is_vdso_map(pos->short_name)) {  			name = (char *) VDSO__MAP_NAME;  			name_len = sizeof(VDSO__MAP_NAME) + 1; +		} else if (dso__is_kcore(pos)) { +			machine__mmap_name(machine, nm, sizeof(nm)); +			name = nm; +			name_len = strlen(nm) + 1;  		} else {  			name = pos->long_name;  			name_len = pos->long_name_len + 1; @@ -240,10 +247,10 @@ static int machine__write_buildid_table(struct machine *machine, int fd)  		umisc = PERF_RECORD_MISC_GUEST_USER;  	} -	err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid, -					  kmisc, fd); +	err = __dsos__write_buildid_table(&machine->kernel_dsos, machine, +					  machine->pid, kmisc, fd);  	if (err == 0) -		err = __dsos__write_buildid_table(&machine->user_dsos, +		err = __dsos__write_buildid_table(&machine->user_dsos, machine,  						  machine->pid, umisc, fd);  	return err;  } @@ -375,23 +382,31 @@ out_free:  	return err;  } -static int dso__cache_build_id(struct dso *dso, const char *debugdir) +static int dso__cache_build_id(struct dso *dso, struct machine *machine, +			       const char *debugdir)  {  	bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';  	bool is_vdso = is_vdso_map(dso->short_name); +	const char *name = dso->long_name; +	char nm[PATH_MAX]; -	return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), -				     dso->long_name, debugdir, -				     is_kallsyms, is_vdso); +	if (dso__is_kcore(dso)) { +		is_kallsyms = true; +		machine__mmap_name(machine, nm, sizeof(nm)); +		name = nm; +	} +	return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, +				     debugdir, is_kallsyms, is_vdso);  } -static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) +static int __dsos__cache_build_ids(struct list_head *head, +				   struct machine *machine, const char *debugdir)  {  	struct dso *pos;  	int err = 0;  	dsos__for_each_with_build_id(pos, head) -		if (dso__cache_build_id(pos, debugdir)) +		if (dso__cache_build_id(pos, machine, debugdir))  			err = -1;  	return err; @@ -399,8 +414,9 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)  static int machine__cache_build_ids(struct machine *machine, const char *debugdir)  { -	int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir); -	ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir); +	int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine, +					  debugdir); +	ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir);  	return ret;  } @@ -627,8 +643,7 @@ static int write_event_desc(int fd, struct perf_header *h __maybe_unused,  	if (ret < 0)  		return ret; -	list_for_each_entry(evsel, &evlist->entries, node) { - +	evlist__for_each(evlist, evsel) {  		ret = do_write(fd, &evsel->attr, sz);  		if (ret < 0)  			return ret; @@ -784,10 +799,10 @@ static void free_cpu_topo(struct cpu_topo *tp)  		return;  	for (i = 0 ; i < tp->core_sib; i++) -		free(tp->core_siblings[i]); +		zfree(&tp->core_siblings[i]);  	for (i = 0 ; i < tp->thread_sib; i++) -		free(tp->thread_siblings[i]); +		zfree(&tp->thread_siblings[i]);  	free(tp);  } @@ -915,7 +930,7 @@ static int write_topo_node(int fd, int node)  		/* skip over invalid lines */  		if (!strchr(buf, ':'))  			continue; -		if (sscanf(buf, "%*s %*d %s %"PRIu64, field, &mem) != 2) +		if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)  			goto done;  		if (!strcmp(field, "MemTotal:"))  			mem_total = mem; @@ -1076,7 +1091,7 @@ static int write_group_desc(int fd, struct perf_header *h __maybe_unused,  	if (ret < 0)  		return ret; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (perf_evsel__is_group_leader(evsel) &&  		    evsel->nr_members > 1) {  			const char *name = evsel->group_name ?: "{anon_group}"; @@ -1216,10 +1231,8 @@ static void free_event_desc(struct perf_evsel *events)  		return;  	for (evsel = events; evsel->attr.size; evsel++) { -		if (evsel->name) -			free(evsel->name); -		if (evsel->id) -			free(evsel->id); +		zfree(&evsel->name); +		zfree(&evsel->id);  	}  	free(events); @@ -1310,8 +1323,7 @@ read_event_desc(struct perf_header *ph, int fd)  		}  	}  out: -	if (buf) -		free(buf); +	free(buf);  	return events;  error:  	if (events) @@ -1474,7 +1486,7 @@ static void print_group_desc(struct perf_header *ph, int fd __maybe_unused,  	session = container_of(ph, struct perf_session, header); -	list_for_each_entry(evsel, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		if (perf_evsel__is_group_leader(evsel) &&  		    evsel->nr_members > 1) {  			fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", @@ -1693,7 +1705,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused,  			  struct perf_header *ph, int fd,  			  void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	u32 nr;  	ret = readn(fd, &nr, sizeof(nr)); @@ -1737,7 +1749,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused,  			     void *data __maybe_unused)  {  	uint64_t mem; -	size_t ret; +	ssize_t ret;  	ret = readn(fd, &mem, sizeof(mem));  	if (ret != sizeof(mem)) @@ -1755,7 +1767,7 @@ perf_evlist__find_by_index(struct perf_evlist *evlist, int idx)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (evsel->idx == idx)  			return evsel;  	} @@ -1806,7 +1818,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused,  			   struct perf_header *ph, int fd,  			   void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	char *str;  	u32 nr, i;  	struct strbuf sb; @@ -1842,7 +1854,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused  				struct perf_header *ph, int fd,  				void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	u32 nr, i;  	char *str;  	struct strbuf sb; @@ -1898,7 +1910,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse  				 struct perf_header *ph, int fd,  				 void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	u32 nr, node, i;  	char *str;  	uint64_t mem_total, mem_free; @@ -1958,7 +1970,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused  				struct perf_header *ph, int fd,  				void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	char *name;  	u32 pmu_num;  	u32 type; @@ -2058,12 +2070,14 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused,  	session->evlist->nr_groups = nr_groups;  	i = nr = 0; -	list_for_each_entry(evsel, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		if (evsel->idx == (int) desc[i].leader_idx) {  			evsel->leader = evsel;  			/* {anon_group} is a dummy name */ -			if (strcmp(desc[i].name, "{anon_group}")) +			if (strcmp(desc[i].name, "{anon_group}")) {  				evsel->group_name = desc[i].name; +				desc[i].name = NULL; +			}  			evsel->nr_members = desc[i].nr_members;  			if (i >= nr_groups || nr > 0) { @@ -2089,8 +2103,8 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused,  	ret = 0;  out_free: -	while ((int) --i >= 0) -		free(desc[i].name); +	for (i = 0; i < nr_groups; i++) +		zfree(&desc[i].name);  	free(desc);  	return ret; @@ -2174,7 +2188,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)  {  	struct header_print_data hd;  	struct perf_header *header = &session->header; -	int fd = session->fd; +	int fd = perf_data_file__fd(session->file);  	hd.fp = fp;  	hd.full = full; @@ -2283,7 +2297,7 @@ int perf_session__write_header(struct perf_session *session,  	lseek(fd, sizeof(f_header), SEEK_SET); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		evsel->id_offset = lseek(fd, 0, SEEK_CUR);  		err = do_write(fd, evsel->id, evsel->ids * sizeof(u64));  		if (err < 0) { @@ -2294,7 +2308,7 @@ int perf_session__write_header(struct perf_session *session,  	attr_offset = lseek(fd, 0, SEEK_CUR); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		f_attr = (struct perf_file_attr){  			.attr = evsel->attr,  			.ids  = { @@ -2309,7 +2323,8 @@ int perf_session__write_header(struct perf_session *session,  		}  	} -	header->data_offset = lseek(fd, 0, SEEK_CUR); +	if (!header->data_offset) +		header->data_offset = lseek(fd, 0, SEEK_CUR);  	header->feat_offset = header->data_offset + header->data_size;  	if (at_exit) { @@ -2516,7 +2531,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz,  int perf_file_header__read(struct perf_file_header *header,  			   struct perf_header *ph, int fd)  { -	int ret; +	ssize_t ret;  	lseek(fd, 0, SEEK_SET); @@ -2610,7 +2625,7 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,  				       struct perf_header *ph, int fd,  				       bool repipe)  { -	int ret; +	ssize_t ret;  	ret = readn(fd, header, sizeof(*header));  	if (ret <= 0) @@ -2635,7 +2650,8 @@ static int perf_header__read_pipe(struct perf_session *session)  	struct perf_header *header = &session->header;  	struct perf_pipe_file_header f_header; -	if (perf_file_header__read_pipe(&f_header, header, session->fd, +	if (perf_file_header__read_pipe(&f_header, header, +					perf_data_file__fd(session->file),  					session->repipe) < 0) {  		pr_debug("incompatible file format\n");  		return -EINVAL; @@ -2650,7 +2666,7 @@ static int read_attr(int fd, struct perf_header *ph,  	struct perf_event_attr *attr = &f_attr->attr;  	size_t sz, left;  	size_t our_sz = sizeof(f_attr->attr); -	int ret; +	ssize_t ret;  	memset(f_attr, 0, sizeof(*f_attr)); @@ -2725,7 +2741,7 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,  {  	struct perf_evsel *pos; -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		if (pos->attr.type == PERF_TYPE_TRACEPOINT &&  		    perf_evsel__prepare_tracepoint_event(pos, pevent))  			return -1; @@ -2736,23 +2752,36 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,  int perf_session__read_header(struct perf_session *session)  { +	struct perf_data_file *file = session->file;  	struct perf_header *header = &session->header;  	struct perf_file_header	f_header;  	struct perf_file_attr	f_attr;  	u64			f_id;  	int nr_attrs, nr_ids, i, j; -	int fd = session->fd; +	int fd = perf_data_file__fd(file);  	session->evlist = perf_evlist__new();  	if (session->evlist == NULL)  		return -ENOMEM; -	if (session->fd_pipe) +	if (perf_data_file__is_pipe(file))  		return perf_header__read_pipe(session);  	if (perf_file_header__read(&f_header, header, fd) < 0)  		return -EINVAL; +	/* +	 * Sanity check that perf.data was written cleanly; data size is +	 * initialized to 0 and updated only if the on_exit function is run. +	 * If data size is still 0 then the file contains only partial +	 * information.  Just warn user and process it as much as it can. +	 */ +	if (f_header.data.size == 0) { +		pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" +			   "Was the 'perf record' command properly terminated?\n", +			   file->path); +	} +  	nr_attrs = f_header.attrs.size / f_header.attr_size;  	lseek(fd, f_header.attrs.offset, SEEK_SET); @@ -2767,7 +2796,7 @@ int perf_session__read_header(struct perf_session *session)  			perf_event__attr_swap(&f_attr.attr);  		tmp = lseek(fd, 0, SEEK_CUR); -		evsel = perf_evsel__new(&f_attr.attr, i); +		evsel = perf_evsel__new(&f_attr.attr);  		if (evsel == NULL)  			goto out_delete_evlist; @@ -2802,11 +2831,11 @@ int perf_session__read_header(struct perf_session *session)  	symbol_conf.nr_events = nr_attrs; -	perf_header__process_sections(header, fd, &session->pevent, +	perf_header__process_sections(header, fd, &session->tevent,  				      perf_file_section__process);  	if (perf_evlist__prepare_tracepoint_events(session->evlist, -						   session->pevent)) +						   session->tevent.pevent))  		goto out_delete_evlist;  	return 0; @@ -2860,7 +2889,7 @@ int perf_event__synthesize_attrs(struct perf_tool *tool,  	struct perf_evsel *evsel;  	int err = 0; -	list_for_each_entry(evsel, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids,  						  evsel->id, process);  		if (err) { @@ -2886,7 +2915,7 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,  			return -ENOMEM;  	} -	evsel = perf_evsel__new(&event->attr.attr, evlist->nr_entries); +	evsel = perf_evsel__new(&event->attr.attr);  	if (evsel == NULL)  		return -ENOMEM; @@ -2963,18 +2992,19 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused,  				     struct perf_session *session)  {  	ssize_t size_read, padding, size = event->tracing_data.size; -	off_t offset = lseek(session->fd, 0, SEEK_CUR); +	int fd = perf_data_file__fd(session->file); +	off_t offset = lseek(fd, 0, SEEK_CUR);  	char buf[BUFSIZ];  	/* setup for reading amidst mmap */ -	lseek(session->fd, offset + sizeof(struct tracing_data_event), +	lseek(fd, offset + sizeof(struct tracing_data_event),  	      SEEK_SET); -	size_read = trace_report(session->fd, &session->pevent, +	size_read = trace_report(fd, &session->tevent,  				 session->repipe);  	padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; -	if (readn(session->fd, buf, padding) < 0) { +	if (readn(fd, buf, padding) < 0) {  		pr_err("%s: reading input file", __func__);  		return -1;  	} @@ -2992,7 +3022,7 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused,  	}  	perf_evlist__prepare_tracepoint_events(session->evlist, -					       session->pevent); +					       session->tevent.pevent);  	return size_read + padding;  } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 307c9aed972..d08cfe49940 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -4,10 +4,10 @@  #include <linux/perf_event.h>  #include <sys/types.h>  #include <stdbool.h> -#include "types.h" +#include <linux/bitmap.h> +#include <linux/types.h>  #include "event.h" -#include <linux/bitmap.h>  enum {  	HEADER_RESERVED		= 0,	/* always cleared */ @@ -77,16 +77,16 @@ struct perf_session_env {  	unsigned long long	total_mem;  	int			nr_cmdline; -	char			*cmdline;  	int			nr_sibling_cores; -	char			*sibling_cores;  	int			nr_sibling_threads; -	char			*sibling_threads;  	int			nr_numa_nodes; -	char			*numa_nodes;  	int			nr_pmu_mappings; -	char			*pmu_mappings;  	int			nr_groups; +	char			*cmdline; +	char			*sibling_cores; +	char			*sibling_threads; +	char			*numa_nodes; +	char			*pmu_mappings;  };  struct perf_header { diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 8b1f6e891b8..86c37c47226 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -22,8 +22,8 @@ static void clean_cmdnames(struct cmdnames *cmds)  	unsigned int i;  	for (i = 0; i < cmds->cnt; ++i) -		free(cmds->names[i]); -	free(cmds->names); +		zfree(&cmds->names[i]); +	zfree(&cmds->names);  	cmds->cnt = 0;  	cmds->alloc = 0;  } @@ -263,9 +263,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)  	for (i = 0; i < old->cnt; i++)  		cmds->names[cmds->cnt++] = old->names[i]; -	free(old->names); +	zfree(&old->names);  	old->cnt = 0; -	old->names = NULL;  }  const char *help_unknown_cmd(const char *cmd) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 46a0d35a05e..30df6187ee0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,10 +1,10 @@ -#include "annotate.h"  #include "util.h"  #include "build-id.h"  #include "hist.h"  #include "session.h"  #include "sort.h"  #include "evsel.h" +#include "annotate.h"  #include <math.h>  static bool hists__filter_entry_by_dso(struct hists *hists, @@ -14,13 +14,6 @@ static bool hists__filter_entry_by_thread(struct hists *hists,  static bool hists__filter_entry_by_symbol(struct hists *hists,  					  struct hist_entry *he); -enum hist_filter { -	HIST_FILTER__DSO, -	HIST_FILTER__THREAD, -	HIST_FILTER__PARENT, -	HIST_FILTER__SYMBOL, -}; -  struct callchain_param	callchain_param = {  	.mode	= CHAIN_GRAPH_REL,  	.min_percent = 0.5, @@ -135,6 +128,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)  			       + unresolved_col_width + 2;  			hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,  					   symlen); +			hists__new_col_len(hists, HISTC_MEM_DCACHELINE, +					   symlen + 1);  		} else {  			symlen = unresolved_col_width + 4 + 2;  			hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, @@ -160,6 +155,10 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)  	hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);  	hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);  	hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); + +	if (h->transaction) +		hists__new_col_len(hists, HISTC_TRANSACTION, +				   hist_entry__transaction_len());  }  void hists__output_recalc_col_len(struct hists *hists, int max_rows) @@ -178,21 +177,21 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows)  	}  } -static void hist_entry__add_cpumode_period(struct hist_entry *he, -					   unsigned int cpumode, u64 period) +static void he_stat__add_cpumode_period(struct he_stat *he_stat, +					unsigned int cpumode, u64 period)  {  	switch (cpumode) {  	case PERF_RECORD_MISC_KERNEL: -		he->stat.period_sys += period; +		he_stat->period_sys += period;  		break;  	case PERF_RECORD_MISC_USER: -		he->stat.period_us += period; +		he_stat->period_us += period;  		break;  	case PERF_RECORD_MISC_GUEST_KERNEL: -		he->stat.period_guest_sys += period; +		he_stat->period_guest_sys += period;  		break;  	case PERF_RECORD_MISC_GUEST_USER: -		he->stat.period_guest_us += period; +		he_stat->period_guest_us += period;  		break;  	default:  		break; @@ -219,24 +218,30 @@ static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src)  	dest->weight		+= src->weight;  } -static void hist_entry__decay(struct hist_entry *he) +static void he_stat__decay(struct he_stat *he_stat)  { -	he->stat.period = (he->stat.period * 7) / 8; -	he->stat.nr_events = (he->stat.nr_events * 7) / 8; +	he_stat->period = (he_stat->period * 7) / 8; +	he_stat->nr_events = (he_stat->nr_events * 7) / 8;  	/* XXX need decay for weight too? */  }  static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)  {  	u64 prev_period = he->stat.period; +	u64 diff;  	if (prev_period == 0)  		return true; -	hist_entry__decay(he); +	he_stat__decay(&he->stat); +	if (symbol_conf.cumulate_callchain) +		he_stat__decay(he->stat_acc); +	diff = prev_period - he->stat.period; + +	hists->stats.total_period -= diff;  	if (!he->filtered) -		hists->stats.total_period -= prev_period - he->stat.period; +		hists->stats.total_non_filtered_period -= diff;  	return he->stat.period == 0;  } @@ -263,8 +268,11 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)  			if (sort__need_collapse)  				rb_erase(&n->rb_node_in, &hists->entries_collapsed); -			hist_entry__free(n);  			--hists->nr_entries; +			if (!n->filtered) +				--hists->nr_non_filtered_entries; + +			hist_entry__free(n);  		}  	}  } @@ -273,25 +281,43 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)   * histogram, sorted on item, collects periods   */ -static struct hist_entry *hist_entry__new(struct hist_entry *template) +static struct hist_entry *hist_entry__new(struct hist_entry *template, +					  bool sample_self)  { -	size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; -	struct hist_entry *he = zalloc(sizeof(*he) + callchain_size); +	size_t callchain_size = 0; +	struct hist_entry *he; + +	if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) +		callchain_size = sizeof(struct callchain_root); + +	he = zalloc(sizeof(*he) + callchain_size);  	if (he != NULL) {  		*he = *template; +		if (symbol_conf.cumulate_callchain) { +			he->stat_acc = malloc(sizeof(he->stat)); +			if (he->stat_acc == NULL) { +				free(he); +				return NULL; +			} +			memcpy(he->stat_acc, &he->stat, sizeof(he->stat)); +			if (!sample_self) +				memset(&he->stat, 0, sizeof(he->stat)); +		} +  		if (he->ms.map)  			he->ms.map->referenced = true;  		if (he->branch_info) {  			/*  			 * This branch info is (a part of) allocated from -			 * machine__resolve_bstack() and will be freed after +			 * sample__resolve_bstack() and will be freed after  			 * adding new entries.  So we need to save a copy.  			 */  			he->branch_info = malloc(sizeof(*he->branch_info));  			if (he->branch_info == NULL) { +				free(he->stat_acc);  				free(he);  				return NULL;  			} @@ -321,15 +347,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)  	return he;  } -void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) -{ -	if (!h->filtered) { -		hists__calc_col_len(hists, h); -		++hists->nr_entries; -		hists->stats.total_period += h->stat.period; -	} -} -  static u8 symbol__parent_filter(const struct symbol *parent)  {  	if (symbol_conf.exclude_other && parent == NULL) @@ -338,15 +355,16 @@ static u8 symbol__parent_filter(const struct symbol *parent)  }  static struct hist_entry *add_hist_entry(struct hists *hists, -				      struct hist_entry *entry, -				      struct addr_location *al, -				      u64 period, -				      u64 weight) +					 struct hist_entry *entry, +					 struct addr_location *al, +					 bool sample_self)  {  	struct rb_node **p;  	struct rb_node *parent = NULL;  	struct hist_entry *he; -	int cmp; +	int64_t cmp; +	u64 period = entry->stat.period; +	u64 weight = entry->stat.weight;  	p = &hists->entries_in->rb_node; @@ -363,13 +381,16 @@ static struct hist_entry *add_hist_entry(struct hists *hists,  		cmp = hist_entry__cmp(he, entry);  		if (!cmp) { -			he_stat__add_period(&he->stat, period, weight); +			if (sample_self) +				he_stat__add_period(&he->stat, period, weight); +			if (symbol_conf.cumulate_callchain) +				he_stat__add_period(he->stat_acc, period, weight);  			/* -			 * This mem info was allocated from machine__resolve_mem +			 * This mem info was allocated from sample__resolve_mem  			 * and will not be used anymore.  			 */ -			free(entry->mem_info); +			zfree(&entry->mem_info);  			/* If the map of an existing hist_entry has  			 * become out-of-date due to an exec() or @@ -391,115 +412,488 @@ static struct hist_entry *add_hist_entry(struct hists *hists,  			p = &(*p)->rb_right;  	} -	he = hist_entry__new(entry); +	he = hist_entry__new(entry, sample_self);  	if (!he)  		return NULL;  	rb_link_node(&he->rb_node_in, parent, p);  	rb_insert_color(&he->rb_node_in, hists->entries_in);  out: -	hist_entry__add_cpumode_period(he, al->cpumode, period); +	if (sample_self) +		he_stat__add_cpumode_period(&he->stat, al->cpumode, period); +	if (symbol_conf.cumulate_callchain) +		he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period);  	return he;  } -struct hist_entry *__hists__add_mem_entry(struct hists *self, -					  struct addr_location *al, -					  struct symbol *sym_parent, -					  struct mem_info *mi, -					  u64 period, -					  u64 weight) +struct hist_entry *__hists__add_entry(struct hists *hists, +				      struct addr_location *al, +				      struct symbol *sym_parent, +				      struct branch_info *bi, +				      struct mem_info *mi, +				      u64 period, u64 weight, u64 transaction, +				      bool sample_self)  {  	struct hist_entry entry = {  		.thread	= al->thread, +		.comm = thread__comm(al->thread),  		.ms = {  			.map	= al->map,  			.sym	= al->sym,  		}, +		.cpu	 = al->cpu, +		.cpumode = al->cpumode, +		.ip	 = al->addr, +		.level	 = al->level,  		.stat = { +			.nr_events = 1,  			.period	= period,  			.weight = weight, -			.nr_events = 1,  		}, -		.cpu	= al->cpu, -		.ip	= al->addr, -		.level	= al->level,  		.parent = sym_parent, -		.filtered = symbol__parent_filter(sym_parent), -		.hists = self, +		.filtered = symbol__parent_filter(sym_parent) | al->filtered, +		.hists	= hists, +		.branch_info = bi,  		.mem_info = mi, -		.branch_info = NULL, +		.transaction = transaction,  	}; -	return add_hist_entry(self, &entry, al, period, weight); + +	return add_hist_entry(hists, &entry, al, sample_self);  } -struct hist_entry *__hists__add_branch_entry(struct hists *self, -					     struct addr_location *al, -					     struct symbol *sym_parent, -					     struct branch_info *bi, -					     u64 period, -					     u64 weight) +static int +iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused, +		    struct addr_location *al __maybe_unused)  { -	struct hist_entry entry = { -		.thread	= al->thread, -		.ms = { -			.map	= bi->to.map, -			.sym	= bi->to.sym, -		}, -		.cpu	= al->cpu, -		.ip	= bi->to.addr, -		.level	= al->level, -		.stat = { -			.period	= period, -			.nr_events = 1, -			.weight = weight, -		}, -		.parent = sym_parent, -		.filtered = symbol__parent_filter(sym_parent), -		.branch_info = bi, -		.hists	= self, -		.mem_info = NULL, -	}; +	return 0; +} + +static int +iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused, +			struct addr_location *al __maybe_unused) +{ +	return 0; +} + +static int +iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct perf_sample *sample = iter->sample; +	struct mem_info *mi; + +	mi = sample__resolve_mem(sample, al); +	if (mi == NULL) +		return -ENOMEM; -	return add_hist_entry(self, &entry, al, period, weight); +	iter->priv = mi; +	return 0;  } -struct hist_entry *__hists__add_entry(struct hists *self, -				      struct addr_location *al, -				      struct symbol *sym_parent, u64 period, -				      u64 weight) +static int +iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)  { -	struct hist_entry entry = { -		.thread	= al->thread, +	u64 cost; +	struct mem_info *mi = iter->priv; +	struct hist_entry *he; + +	if (mi == NULL) +		return -EINVAL; + +	cost = iter->sample->weight; +	if (!cost) +		cost = 1; + +	/* +	 * must pass period=weight in order to get the correct +	 * sorting from hists__collapse_resort() which is solely +	 * based on periods. We want sorting be done on nr_events * weight +	 * and this is indirectly achieved by passing period=weight here +	 * and the he_stat__add_period() function. +	 */ +	he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi, +				cost, cost, 0, true); +	if (!he) +		return -ENOMEM; + +	iter->he = he; +	return 0; +} + +static int +iter_finish_mem_entry(struct hist_entry_iter *iter, +		      struct addr_location *al __maybe_unused) +{ +	struct perf_evsel *evsel = iter->evsel; +	struct hist_entry *he = iter->he; +	int err = -EINVAL; + +	if (he == NULL) +		goto out; + +	hists__inc_nr_samples(&evsel->hists, he->filtered); + +	err = hist_entry__append_callchain(he, iter->sample); + +out: +	/* +	 * We don't need to free iter->priv (mem_info) here since +	 * the mem info was either already freed in add_hist_entry() or +	 * passed to a new hist entry by hist_entry__new(). +	 */ +	iter->priv = NULL; + +	iter->he = NULL; +	return err; +} + +static int +iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct branch_info *bi; +	struct perf_sample *sample = iter->sample; + +	bi = sample__resolve_bstack(sample, al); +	if (!bi) +		return -ENOMEM; + +	iter->curr = 0; +	iter->total = sample->branch_stack->nr; + +	iter->priv = bi; +	return 0; +} + +static int +iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused, +			     struct addr_location *al __maybe_unused) +{ +	/* to avoid calling callback function */ +	iter->he = NULL; + +	return 0; +} + +static int +iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct branch_info *bi = iter->priv; +	int i = iter->curr; + +	if (bi == NULL) +		return 0; + +	if (iter->curr >= iter->total) +		return 0; + +	al->map = bi[i].to.map; +	al->sym = bi[i].to.sym; +	al->addr = bi[i].to.addr; +	return 1; +} + +static int +iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct branch_info *bi; +	struct perf_evsel *evsel = iter->evsel; +	struct hist_entry *he = NULL; +	int i = iter->curr; +	int err = 0; + +	bi = iter->priv; + +	if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) +		goto out; + +	/* +	 * The report shows the percentage of total branches captured +	 * and not events sampled. Thus we use a pseudo period of 1. +	 */ +	he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL, +				1, 1, 0, true); +	if (he == NULL) +		return -ENOMEM; + +	hists__inc_nr_samples(&evsel->hists, he->filtered); + +out: +	iter->he = he; +	iter->curr++; +	return err; +} + +static int +iter_finish_branch_entry(struct hist_entry_iter *iter, +			 struct addr_location *al __maybe_unused) +{ +	zfree(&iter->priv); +	iter->he = NULL; + +	return iter->curr >= iter->total ? 0 : -1; +} + +static int +iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused, +			  struct addr_location *al __maybe_unused) +{ +	return 0; +} + +static int +iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct perf_evsel *evsel = iter->evsel; +	struct perf_sample *sample = iter->sample; +	struct hist_entry *he; + +	he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, +				sample->period, sample->weight, +				sample->transaction, true); +	if (he == NULL) +		return -ENOMEM; + +	iter->he = he; +	return 0; +} + +static int +iter_finish_normal_entry(struct hist_entry_iter *iter, +			 struct addr_location *al __maybe_unused) +{ +	struct hist_entry *he = iter->he; +	struct perf_evsel *evsel = iter->evsel; +	struct perf_sample *sample = iter->sample; + +	if (he == NULL) +		return 0; + +	iter->he = NULL; + +	hists__inc_nr_samples(&evsel->hists, he->filtered); + +	return hist_entry__append_callchain(he, sample); +} + +static int +iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused, +			      struct addr_location *al __maybe_unused) +{ +	struct hist_entry **he_cache; + +	callchain_cursor_commit(&callchain_cursor); + +	/* +	 * This is for detecting cycles or recursions so that they're +	 * cumulated only one time to prevent entries more than 100% +	 * overhead. +	 */ +	he_cache = malloc(sizeof(*he_cache) * (PERF_MAX_STACK_DEPTH + 1)); +	if (he_cache == NULL) +		return -ENOMEM; + +	iter->priv = he_cache; +	iter->curr = 0; + +	return 0; +} + +static int +iter_add_single_cumulative_entry(struct hist_entry_iter *iter, +				 struct addr_location *al) +{ +	struct perf_evsel *evsel = iter->evsel; +	struct perf_sample *sample = iter->sample; +	struct hist_entry **he_cache = iter->priv; +	struct hist_entry *he; +	int err = 0; + +	he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, +				sample->period, sample->weight, +				sample->transaction, true); +	if (he == NULL) +		return -ENOMEM; + +	iter->he = he; +	he_cache[iter->curr++] = he; + +	callchain_append(he->callchain, &callchain_cursor, sample->period); + +	/* +	 * We need to re-initialize the cursor since callchain_append() +	 * advanced the cursor to the end. +	 */ +	callchain_cursor_commit(&callchain_cursor); + +	hists__inc_nr_samples(&evsel->hists, he->filtered); + +	return err; +} + +static int +iter_next_cumulative_entry(struct hist_entry_iter *iter, +			   struct addr_location *al) +{ +	struct callchain_cursor_node *node; + +	node = callchain_cursor_current(&callchain_cursor); +	if (node == NULL) +		return 0; + +	return fill_callchain_info(al, node, iter->hide_unresolved); +} + +static int +iter_add_next_cumulative_entry(struct hist_entry_iter *iter, +			       struct addr_location *al) +{ +	struct perf_evsel *evsel = iter->evsel; +	struct perf_sample *sample = iter->sample; +	struct hist_entry **he_cache = iter->priv; +	struct hist_entry *he; +	struct hist_entry he_tmp = { +		.cpu = al->cpu, +		.thread = al->thread, +		.comm = thread__comm(al->thread), +		.ip = al->addr,  		.ms = { -			.map	= al->map, -			.sym	= al->sym, +			.map = al->map, +			.sym = al->sym,  		}, -		.cpu	= al->cpu, -		.ip	= al->addr, -		.level	= al->level, -		.stat = { -			.period	= period, -			.nr_events = 1, -			.weight = weight, -		}, -		.parent = sym_parent, -		.filtered = symbol__parent_filter(sym_parent), -		.hists	= self, -		.branch_info = NULL, -		.mem_info = NULL, +		.parent = iter->parent,  	}; +	int i; +	struct callchain_cursor cursor; + +	callchain_cursor_snapshot(&cursor, &callchain_cursor); + +	callchain_cursor_advance(&callchain_cursor); + +	/* +	 * Check if there's duplicate entries in the callchain. +	 * It's possible that it has cycles or recursive calls. +	 */ +	for (i = 0; i < iter->curr; i++) { +		if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) { +			/* to avoid calling callback function */ +			iter->he = NULL; +			return 0; +		} +	} + +	he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, +				sample->period, sample->weight, +				sample->transaction, false); +	if (he == NULL) +		return -ENOMEM; + +	iter->he = he; +	he_cache[iter->curr++] = he; + +	callchain_append(he->callchain, &cursor, sample->period); +	return 0; +} -	return add_hist_entry(self, &entry, al, period, weight); +static int +iter_finish_cumulative_entry(struct hist_entry_iter *iter, +			     struct addr_location *al __maybe_unused) +{ +	zfree(&iter->priv); +	iter->he = NULL; + +	return 0; +} + +const struct hist_iter_ops hist_iter_mem = { +	.prepare_entry 		= iter_prepare_mem_entry, +	.add_single_entry 	= iter_add_single_mem_entry, +	.next_entry 		= iter_next_nop_entry, +	.add_next_entry 	= iter_add_next_nop_entry, +	.finish_entry 		= iter_finish_mem_entry, +}; + +const struct hist_iter_ops hist_iter_branch = { +	.prepare_entry 		= iter_prepare_branch_entry, +	.add_single_entry 	= iter_add_single_branch_entry, +	.next_entry 		= iter_next_branch_entry, +	.add_next_entry 	= iter_add_next_branch_entry, +	.finish_entry 		= iter_finish_branch_entry, +}; + +const struct hist_iter_ops hist_iter_normal = { +	.prepare_entry 		= iter_prepare_normal_entry, +	.add_single_entry 	= iter_add_single_normal_entry, +	.next_entry 		= iter_next_nop_entry, +	.add_next_entry 	= iter_add_next_nop_entry, +	.finish_entry 		= iter_finish_normal_entry, +}; + +const struct hist_iter_ops hist_iter_cumulative = { +	.prepare_entry 		= iter_prepare_cumulative_entry, +	.add_single_entry 	= iter_add_single_cumulative_entry, +	.next_entry 		= iter_next_cumulative_entry, +	.add_next_entry 	= iter_add_next_cumulative_entry, +	.finish_entry 		= iter_finish_cumulative_entry, +}; + +int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, +			 struct perf_evsel *evsel, struct perf_sample *sample, +			 int max_stack_depth, void *arg) +{ +	int err, err2; + +	err = sample__resolve_callchain(sample, &iter->parent, evsel, al, +					max_stack_depth); +	if (err) +		return err; + +	iter->evsel = evsel; +	iter->sample = sample; + +	err = iter->ops->prepare_entry(iter, al); +	if (err) +		goto out; + +	err = iter->ops->add_single_entry(iter, al); +	if (err) +		goto out; + +	if (iter->he && iter->add_entry_cb) { +		err = iter->add_entry_cb(iter, al, true, arg); +		if (err) +			goto out; +	} + +	while (iter->ops->next_entry(iter, al)) { +		err = iter->ops->add_next_entry(iter, al); +		if (err) +			break; + +		if (iter->he && iter->add_entry_cb) { +			err = iter->add_entry_cb(iter, al, false, arg); +			if (err) +				goto out; +		} +	} + +out: +	err2 = iter->ops->finish_entry(iter, al); +	if (!err) +		err = err2; + +	return err;  }  int64_t  hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)  { -	struct sort_entry *se; +	struct perf_hpp_fmt *fmt;  	int64_t cmp = 0; -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		cmp = se->se_cmp(left, right); +	perf_hpp__for_each_sort_list(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; + +		cmp = fmt->cmp(left, right);  		if (cmp)  			break;  	} @@ -510,15 +904,14 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)  int64_t  hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)  { -	struct sort_entry *se; +	struct perf_hpp_fmt *fmt;  	int64_t cmp = 0; -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		int64_t (*f)(struct hist_entry *, struct hist_entry *); - -		f = se->se_collapse ?: se->se_cmp; +	perf_hpp__for_each_sort_list(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; -		cmp = f(left, right); +		cmp = fmt->collapse(left, right);  		if (cmp)  			break;  	} @@ -528,8 +921,10 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)  void hist_entry__free(struct hist_entry *he)  { -	free(he->branch_info); -	free(he->mem_info); +	zfree(&he->branch_info); +	zfree(&he->mem_info); +	zfree(&he->stat_acc); +	free_srcline(he->srcline);  	free(he);  } @@ -554,6 +949,8 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,  		if (!cmp) {  			he_stat__add_stat(&iter->stat, &he->stat); +			if (symbol_conf.cumulate_callchain) +				he_stat__add_stat(iter->stat_acc, he->stat_acc);  			if (symbol_conf.use_callchain) {  				callchain_cursor_reset(&callchain_cursor); @@ -598,7 +995,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)  	hists__filter_entry_by_symbol(hists, he);  } -void hists__collapse_resort(struct hists *hists) +void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)  {  	struct rb_root *root;  	struct rb_node *next; @@ -611,6 +1008,8 @@ void hists__collapse_resort(struct hists *hists)  	next = rb_first(root);  	while (next) { +		if (session_done()) +			break;  		n = rb_entry(next, struct hist_entry, rb_node_in);  		next = rb_next(&n->rb_node_in); @@ -623,67 +1022,55 @@ void hists__collapse_resort(struct hists *hists)  			 */  			hists__apply_filters(hists, n);  		} +		if (prog) +			ui_progress__update(prog, 1);  	}  } -/* - * reverse the map, sort on period. - */ - -static int period_cmp(u64 period_a, u64 period_b) -{ -	if (period_a > period_b) -		return 1; -	if (period_a < period_b) -		return -1; -	return 0; -} - -static int hist_entry__sort_on_period(struct hist_entry *a, -				      struct hist_entry *b) +static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)  { -	int ret; -	int i, nr_members; -	struct perf_evsel *evsel; -	struct hist_entry *pair; -	u64 *periods_a, *periods_b; +	struct perf_hpp_fmt *fmt; +	int64_t cmp = 0; -	ret = period_cmp(a->stat.period, b->stat.period); -	if (ret || !symbol_conf.event_group) -		return ret; +	perf_hpp__for_each_sort_list(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; -	evsel = hists_to_evsel(a->hists); -	nr_members = evsel->nr_members; -	if (nr_members <= 1) -		return ret; +		cmp = fmt->sort(a, b); +		if (cmp) +			break; +	} -	periods_a = zalloc(sizeof(periods_a) * nr_members); -	periods_b = zalloc(sizeof(periods_b) * nr_members); +	return cmp; +} -	if (!periods_a || !periods_b) -		goto out; +static void hists__reset_filter_stats(struct hists *hists) +{ +	hists->nr_non_filtered_entries = 0; +	hists->stats.total_non_filtered_period = 0; +} -	list_for_each_entry(pair, &a->pairs.head, pairs.node) { -		evsel = hists_to_evsel(pair->hists); -		periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; -	} +void hists__reset_stats(struct hists *hists) +{ +	hists->nr_entries = 0; +	hists->stats.total_period = 0; -	list_for_each_entry(pair, &b->pairs.head, pairs.node) { -		evsel = hists_to_evsel(pair->hists); -		periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period; -	} +	hists__reset_filter_stats(hists); +} -	for (i = 1; i < nr_members; i++) { -		ret = period_cmp(periods_a[i], periods_b[i]); -		if (ret) -			break; -	} +static void hists__inc_filter_stats(struct hists *hists, struct hist_entry *h) +{ +	hists->nr_non_filtered_entries++; +	hists->stats.total_non_filtered_period += h->stat.period; +} -out: -	free(periods_a); -	free(periods_b); +void hists__inc_stats(struct hists *hists, struct hist_entry *h) +{ +	if (!h->filtered) +		hists__inc_filter_stats(hists, h); -	return ret; +	hists->nr_entries++; +	hists->stats.total_period += h->stat.period;  }  static void __hists__insert_output_entry(struct rb_root *entries, @@ -702,7 +1089,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,  		parent = *p;  		iter = rb_entry(parent, struct hist_entry, rb_node); -		if (hist_entry__sort_on_period(he, iter) > 0) +		if (hist_entry__sort(he, iter) > 0)  			p = &(*p)->rb_left;  		else  			p = &(*p)->rb_right; @@ -729,8 +1116,7 @@ void hists__output_resort(struct hists *hists)  	next = rb_first(root);  	hists->entries = RB_ROOT; -	hists->nr_entries = 0; -	hists->stats.total_period = 0; +	hists__reset_stats(hists);  	hists__reset_col_len(hists);  	while (next) { @@ -738,7 +1124,10 @@ void hists__output_resort(struct hists *hists)  		next = rb_next(&n->rb_node_in);  		__hists__insert_output_entry(&hists->entries, n, min_callchain_hits); -		hists__inc_nr_entries(hists, n); +		hists__inc_stats(hists, n); + +		if (!n->filtered) +			hists__calc_col_len(hists, n);  	}  } @@ -749,13 +1138,13 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h  	if (h->filtered)  		return; -	++hists->nr_entries; -	if (h->ms.unfolded) -		hists->nr_entries += h->nr_rows; +	/* force fold unfiltered entry for simplicity */ +	h->ms.unfolded = false;  	h->row_offset = 0; -	hists->stats.total_period += h->stat.period; -	hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; +	hists->stats.nr_non_filtered_samples += h->stat.nr_events; + +	hists__inc_filter_stats(hists, h);  	hists__calc_col_len(hists, h);  } @@ -776,8 +1165,9 @@ void hists__filter_by_dso(struct hists *hists)  {  	struct rb_node *nd; -	hists->nr_entries = hists->stats.total_period = 0; -	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; +	hists->stats.nr_non_filtered_samples = 0; + +	hists__reset_filter_stats(hists);  	hists__reset_col_len(hists);  	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -809,8 +1199,9 @@ void hists__filter_by_thread(struct hists *hists)  {  	struct rb_node *nd; -	hists->nr_entries = hists->stats.total_period = 0; -	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; +	hists->stats.nr_non_filtered_samples = 0; + +	hists__reset_filter_stats(hists);  	hists__reset_col_len(hists);  	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -840,8 +1231,9 @@ void hists__filter_by_symbol(struct hists *hists)  {  	struct rb_node *nd; -	hists->nr_entries = hists->stats.total_period = 0; -	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; +	hists->stats.nr_non_filtered_samples = 0; + +	hists__reset_filter_stats(hists);  	hists__reset_col_len(hists);  	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -854,16 +1246,6 @@ void hists__filter_by_symbol(struct hists *hists)  	}  } -int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) -{ -	return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); -} - -int hist_entry__annotate(struct hist_entry *he, size_t privsize) -{ -	return symbol__annotate(he->ms.sym, he->ms.map, privsize); -} -  void events_stats__inc(struct events_stats *stats, u32 type)  {  	++stats->nr_events[0]; @@ -875,6 +1257,13 @@ void hists__inc_nr_events(struct hists *hists, u32 type)  	events_stats__inc(&hists->stats, type);  } +void hists__inc_nr_samples(struct hists *hists, bool filtered) +{ +	events_stats__inc(&hists->stats, PERF_RECORD_SAMPLE); +	if (!filtered) +		hists->stats.nr_non_filtered_samples++; +} +  static struct hist_entry *hists__add_dummy_entry(struct hists *hists,  						 struct hist_entry *pair)  { @@ -882,7 +1271,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,  	struct rb_node **p;  	struct rb_node *parent = NULL;  	struct hist_entry *he; -	int cmp; +	int64_t cmp;  	if (sort__need_collapse)  		root = &hists->entries_collapsed; @@ -906,13 +1295,13 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,  			p = &(*p)->rb_right;  	} -	he = hist_entry__new(pair); +	he = hist_entry__new(pair, true);  	if (he) {  		memset(&he->stat, 0, sizeof(he->stat));  		he->hists = hists;  		rb_link_node(&he->rb_node_in, parent, p);  		rb_insert_color(&he->rb_node_in, root); -		hists__inc_nr_entries(hists, he); +		hists__inc_stats(hists, he);  		he->dummy = true;  	}  out: @@ -996,3 +1385,30 @@ int hists__link(struct hists *leader, struct hists *other)  	return 0;  } + +u64 hists__total_period(struct hists *hists) +{ +	return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : +		hists->stats.total_period; +} + +int parse_filter_percentage(const struct option *opt __maybe_unused, +			    const char *arg, int unset __maybe_unused) +{ +	if (!strcmp(arg, "relative")) +		symbol_conf.filter_relative = true; +	else if (!strcmp(arg, "absolute")) +		symbol_conf.filter_relative = false; +	else +		return -1; + +	return 0; +} + +int perf_hist_config(const char *var, const char *value) +{ +	if (!strcmp(var, "hist.percentage")) +		return parse_filter_percentage(NULL, value, 0); + +	return 0; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1329b6b6ffe..742f49a8572 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -5,6 +5,8 @@  #include <pthread.h>  #include "callchain.h"  #include "header.h" +#include "color.h" +#include "ui/progress.h"  extern struct callchain_param callchain_param; @@ -12,6 +14,15 @@ struct hist_entry;  struct addr_location;  struct symbol; +enum hist_filter { +	HIST_FILTER__DSO, +	HIST_FILTER__THREAD, +	HIST_FILTER__PARENT, +	HIST_FILTER__SYMBOL, +	HIST_FILTER__GUEST, +	HIST_FILTER__HOST, +}; +  /*   * The kernel collects the number of events it couldn't send in a stretch and   * when possible sends this number in a PERF_RECORD_LOST event. The number of @@ -26,9 +37,11 @@ struct symbol;   */  struct events_stats {  	u64 total_period; +	u64 total_non_filtered_period;  	u64 total_lost;  	u64 total_invalid_chains;  	u32 nr_events[PERF_RECORD_HEADER_MAX]; +	u32 nr_non_filtered_samples;  	u32 nr_lost_warned;  	u32 nr_unknown_events;  	u32 nr_invalid_chains; @@ -45,6 +58,8 @@ enum hist_column {  	HISTC_CPU,  	HISTC_SRCLINE,  	HISTC_MISPREDICT, +	HISTC_IN_TX, +	HISTC_ABORT,  	HISTC_SYMBOL_FROM,  	HISTC_SYMBOL_TO,  	HISTC_DSO_FROM, @@ -57,6 +72,8 @@ enum hist_column {  	HISTC_MEM_TLB,  	HISTC_MEM_LVL,  	HISTC_MEM_SNOOP, +	HISTC_MEM_DCACHELINE, +	HISTC_TRANSACTION,  	HISTC_NR_COLS, /* Last entry */  }; @@ -69,6 +86,7 @@ struct hists {  	struct rb_root		entries;  	struct rb_root		entries_collapsed;  	u64			nr_entries; +	u64			nr_non_filtered_entries;  	const struct thread	*thread_filter;  	const struct dso	*dso_filter;  	const char		*uid_filter_str; @@ -79,54 +97,87 @@ struct hists {  	u16			col_len[HISTC_NR_COLS];  }; -struct hist_entry *__hists__add_entry(struct hists *self, +struct hist_entry_iter; + +struct hist_iter_ops { +	int (*prepare_entry)(struct hist_entry_iter *, struct addr_location *); +	int (*add_single_entry)(struct hist_entry_iter *, struct addr_location *); +	int (*next_entry)(struct hist_entry_iter *, struct addr_location *); +	int (*add_next_entry)(struct hist_entry_iter *, struct addr_location *); +	int (*finish_entry)(struct hist_entry_iter *, struct addr_location *); +}; + +struct hist_entry_iter { +	int total; +	int curr; + +	bool hide_unresolved; + +	struct perf_evsel *evsel; +	struct perf_sample *sample; +	struct hist_entry *he; +	struct symbol *parent; +	void *priv; + +	const struct hist_iter_ops *ops; +	/* user-defined callback function (optional) */ +	int (*add_entry_cb)(struct hist_entry_iter *iter, +			    struct addr_location *al, bool single, void *arg); +}; + +extern const struct hist_iter_ops hist_iter_normal; +extern const struct hist_iter_ops hist_iter_branch; +extern const struct hist_iter_ops hist_iter_mem; +extern const struct hist_iter_ops hist_iter_cumulative; + +struct hist_entry *__hists__add_entry(struct hists *hists,  				      struct addr_location *al, -				      struct symbol *parent, u64 period, -				      u64 weight); +				      struct symbol *parent, +				      struct branch_info *bi, +				      struct mem_info *mi, u64 period, +				      u64 weight, u64 transaction, +				      bool sample_self); +int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, +			 struct perf_evsel *evsel, struct perf_sample *sample, +			 int max_stack_depth, void *arg); +  int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);  int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); -int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, +int hist_entry__transaction_len(void); +int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size,  			      struct hists *hists);  void hist_entry__free(struct hist_entry *); -struct hist_entry *__hists__add_branch_entry(struct hists *self, -					     struct addr_location *al, -					     struct symbol *sym_parent, -					     struct branch_info *bi, -					     u64 period, -					     u64 weight); - -struct hist_entry *__hists__add_mem_entry(struct hists *self, -					  struct addr_location *al, -					  struct symbol *sym_parent, -					  struct mem_info *mi, -					  u64 period, -					  u64 weight); - -void hists__output_resort(struct hists *self); -void hists__collapse_resort(struct hists *self); +void hists__output_resort(struct hists *hists); +void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);  void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);  void hists__output_recalc_col_len(struct hists *hists, int max_rows); -void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); -void hists__inc_nr_events(struct hists *self, u32 type); +u64 hists__total_period(struct hists *hists); +void hists__reset_stats(struct hists *hists); +void hists__inc_stats(struct hists *hists, struct hist_entry *h); +void hists__inc_nr_events(struct hists *hists, u32 type); +void hists__inc_nr_samples(struct hists *hists, bool filtered);  void events_stats__inc(struct events_stats *stats, u32 type);  size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); -size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, +size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  		      int max_cols, float min_pcnt, FILE *fp); -int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); -int hist_entry__annotate(struct hist_entry *self, size_t privsize); -  void hists__filter_by_dso(struct hists *hists);  void hists__filter_by_thread(struct hists *hists);  void hists__filter_by_symbol(struct hists *hists); -u16 hists__col_len(struct hists *self, enum hist_column col); -void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); -bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); +static inline bool hists__has_filter(struct hists *hists) +{ +	return hists->thread_filter || hists->dso_filter || +		hists->symbol_filter_str; +} + +u16 hists__col_len(struct hists *hists, enum hist_column col); +void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); +bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len);  void hists__reset_col_len(struct hists *hists);  void hists__calc_col_len(struct hists *hists, struct hist_entry *he); @@ -141,21 +192,38 @@ struct perf_hpp {  };  struct perf_hpp_fmt { -	int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp); -	int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp); +	int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +		      struct perf_evsel *evsel); +	int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +		     struct perf_evsel *evsel);  	int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,  		     struct hist_entry *he);  	int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,  		     struct hist_entry *he); +	int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b); +	int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b); +	int64_t (*sort)(struct hist_entry *a, struct hist_entry *b);  	struct list_head list; +	struct list_head sort_list; +	bool elide;  };  extern struct list_head perf_hpp__list; +extern struct list_head perf_hpp__sort_list;  #define perf_hpp__for_each_format(format) \  	list_for_each_entry(format, &perf_hpp__list, list) +#define perf_hpp__for_each_format_safe(format, tmp)	\ +	list_for_each_entry_safe(format, tmp, &perf_hpp__list, list) + +#define perf_hpp__for_each_sort_list(format) \ +	list_for_each_entry(format, &perf_hpp__sort_list, sort_list) + +#define perf_hpp__for_each_sort_list_safe(format, tmp)	\ +	list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list) +  extern struct perf_hpp_fmt perf_hpp__format[];  enum { @@ -165,6 +233,7 @@ enum {  	PERF_HPP__OVERHEAD_US,  	PERF_HPP__OVERHEAD_GUEST_SYS,  	PERF_HPP__OVERHEAD_GUEST_US, +	PERF_HPP__OVERHEAD_ACC,  	PERF_HPP__SAMPLES,  	PERF_HPP__PERIOD, @@ -173,7 +242,54 @@ enum {  void perf_hpp__init(void);  void perf_hpp__column_register(struct perf_hpp_fmt *format); +void perf_hpp__column_unregister(struct perf_hpp_fmt *format);  void perf_hpp__column_enable(unsigned col); +void perf_hpp__column_disable(unsigned col); +void perf_hpp__cancel_cumulate(void); + +void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); +void perf_hpp__setup_output_field(void); +void perf_hpp__reset_output_field(void); +void perf_hpp__append_sort_keys(void); + +bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); +bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); + +static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) +{ +	return format->elide; +} + +void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); + +typedef u64 (*hpp_field_fn)(struct hist_entry *he); +typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); +typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); + +int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, +	       hpp_field_fn get_field, const char *fmt, +	       hpp_snprint_fn print_fn, bool fmt_percent); +int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, +		   hpp_field_fn get_field, const char *fmt, +		   hpp_snprint_fn print_fn, bool fmt_percent); + +static inline void advance_hpp(struct perf_hpp *hpp, int inc) +{ +	hpp->buf  += inc; +	hpp->size -= inc; +} + +static inline size_t perf_hpp__use_color(void) +{ +	return !symbol_conf.field_sep; +} + +static inline size_t perf_hpp__color_overhead(void) +{ +	return perf_hpp__use_color() ? +	       (COLOR_MAXLEN + sizeof(PERF_COLOR_RESET)) * PERF_HPP__MAX_INDEX +	       : 0; +}  struct perf_evlist; @@ -183,7 +299,7 @@ struct hist_browser_timer {  	int refresh;  }; -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT  #include "../ui/keysyms.h"  int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,  			     struct hist_browser_timer *hbt); @@ -204,12 +320,9 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,  	return 0;  } -static inline int hist_entry__tui_annotate(struct hist_entry *self -					   __maybe_unused, -					   struct perf_evsel *evsel -					   __maybe_unused, -					   struct hist_browser_timer *hbt -					   __maybe_unused) +static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused, +					   struct perf_evsel *evsel __maybe_unused, +					   struct hist_browser_timer *hbt __maybe_unused)  {  	return 0;  } @@ -224,20 +337,11 @@ static inline int script_browse(const char *script_opt __maybe_unused)  #define K_SWITCH_INPUT_DATA -3000  #endif -#ifdef GTK2_SUPPORT -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, -				  struct hist_browser_timer *hbt __maybe_unused, -				  float min_pcnt); -#else -static inline -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, -				  const char *help __maybe_unused, -				  struct hist_browser_timer *hbt __maybe_unused, -				  float min_pcnt __maybe_unused) -{ -	return 0; -} -#endif +unsigned int hists__sort_list_width(struct hists *hists); + +struct option; +int parse_filter_percentage(const struct option *opt __maybe_unused, +			    const char *arg, int unset __maybe_unused); +int perf_hist_config(const char *var, const char *value); -unsigned int hists__sort_list_width(struct hists *self);  #endif	/* __PERF_HIST_H */ diff --git a/tools/perf/util/include/asm/bug.h b/tools/perf/util/include/asm/bug.h deleted file mode 100644 index 7fcc6810adc..00000000000 --- a/tools/perf/util/include/asm/bug.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _PERF_ASM_GENERIC_BUG_H -#define _PERF_ASM_GENERIC_BUG_H - -#define __WARN_printf(arg...)	do { fprintf(stderr, arg); } while (0) - -#define WARN(condition, format...) ({		\ -	int __ret_warn_on = !!(condition);	\ -	if (unlikely(__ret_warn_on))		\ -		__WARN_printf(format);		\ -	unlikely(__ret_warn_on);		\ -}) - -#define WARN_ONCE(condition, format...)	({	\ -	static int __warned;			\ -	int __ret_warn_once = !!(condition);	\ -						\ -	if (unlikely(__ret_warn_once))		\ -		if (WARN(!__warned, format)) 	\ -			__warned = 1;		\ -	unlikely(__ret_warn_once);		\ -}) -#endif diff --git a/tools/perf/util/include/asm/hash.h b/tools/perf/util/include/asm/hash.h new file mode 100644 index 00000000000..d82b170bb21 --- /dev/null +++ b/tools/perf/util/include/asm/hash.h @@ -0,0 +1,6 @@ +#ifndef __ASM_GENERIC_HASH_H +#define __ASM_GENERIC_HASH_H + +/* Stub */ + +#endif /* __ASM_GENERIC_HASH_H */ diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h index cf6727e99c4..8f149655f49 100644 --- a/tools/perf/util/include/dwarf-regs.h +++ b/tools/perf/util/include/dwarf-regs.h @@ -1,7 +1,7 @@  #ifndef _PERF_DWARF_REGS_H_  #define _PERF_DWARF_REGS_H_ -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  const char *get_arch_regstr(unsigned int n);  #endif diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index bb162e40c76..01ffd12dc79 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h @@ -4,6 +4,9 @@  #include <string.h>  #include <linux/bitops.h> +#define DECLARE_BITMAP(name,bits) \ +	unsigned long name[BITS_TO_LONGS(bits)] +  int __bitmap_weight(const unsigned long *bitmap, int bits);  void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,  		 const unsigned long *bitmap2, int bits); diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index 45cf10a562b..dadfa7e5428 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h @@ -87,13 +87,15 @@ static __always_inline unsigned long __ffs(unsigned long word)  	return num;  } +typedef const unsigned long __attribute__((__may_alias__)) long_alias_t; +  /*   * Find the first set bit in a memory region.   */  static inline unsigned long  find_first_bit(const unsigned long *addr, unsigned long size)  { -	const unsigned long *p = addr; +	long_alias_t *p = (long_alias_t *) addr;  	unsigned long result = 0;  	unsigned long tmp; diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h deleted file mode 100644 index 96b919dae11..00000000000 --- a/tools/perf/util/include/linux/compiler.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _PERF_LINUX_COMPILER_H_ -#define _PERF_LINUX_COMPILER_H_ - -#ifndef __always_inline -#define __always_inline	inline -#endif -#define __user -#ifndef __attribute_const__ -#define __attribute_const__ -#endif - -#ifndef __maybe_unused -#define __maybe_unused		__attribute__((unused)) -#endif -#define __packed	__attribute__((__packed__)) - -#ifndef __force -#define __force -#endif - -#endif diff --git a/tools/perf/util/include/linux/export.h b/tools/perf/util/include/linux/export.h deleted file mode 100644 index b43e2dc21e0..00000000000 --- a/tools/perf/util/include/linux/export.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PERF_LINUX_MODULE_H -#define PERF_LINUX_MODULE_H - -#define EXPORT_SYMBOL(name) - -#endif diff --git a/tools/perf/util/include/linux/hash.h b/tools/perf/util/include/linux/hash.h deleted file mode 100644 index 201f5739799..00000000000 --- a/tools/perf/util/include/linux/hash.h +++ /dev/null @@ -1,5 +0,0 @@ -#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 d8c927c868e..9844c31b7c2 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h @@ -94,12 +94,6 @@ static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)  	return (i >= ssize) ? (ssize - 1) : i;  } -static inline unsigned long -simple_strtoul(const char *nptr, char **endptr, int base) -{ -	return strtoul(nptr, endptr, base); -} -  int eprintf(int level,  	    const char *fmt, ...) __attribute__((format(printf, 2, 3))); diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index 1d928a0ce99..76ddbc72634 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h @@ -1,5 +1,5 @@  #include <linux/kernel.h> -#include <linux/prefetch.h> +#include <linux/types.h>  #include "../../../../include/linux/list.h" diff --git a/tools/perf/util/include/linux/magic.h b/tools/perf/util/include/linux/magic.h deleted file mode 100644 index 58b64ed4da1..00000000000 --- a/tools/perf/util/include/linux/magic.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _PERF_LINUX_MAGIC_H_ -#define _PERF_LINUX_MAGIC_H_ - -#ifndef DEBUGFS_MAGIC -#define DEBUGFS_MAGIC          0x64626720 -#endif - -#ifndef SYSFS_MAGIC -#define SYSFS_MAGIC            0x62656572 -#endif - -#endif diff --git a/tools/perf/util/include/linux/prefetch.h b/tools/perf/util/include/linux/prefetch.h deleted file mode 100644 index 7841e485d8c..00000000000 --- a/tools/perf/util/include/linux/prefetch.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PERF_LINUX_PREFETCH_H -#define PERF_LINUX_PREFETCH_H - -static inline void prefetch(void *a __attribute__((unused))) { } - -#endif diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h deleted file mode 100644 index eb464786c08..00000000000 --- a/tools/perf/util/include/linux/types.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _PERF_LINUX_TYPES_H_ -#define _PERF_LINUX_TYPES_H_ - -#include <asm/types.h> - -#ifndef __bitwise -#define __bitwise -#endif - -#ifndef __le32 -typedef __u32 __bitwise __le32; -#endif - -#define DECLARE_BITMAP(name,bits) \ -	unsigned long name[BITS_TO_LONGS(bits)] - -struct list_head { -	struct list_head *next, *prev; -}; - -struct hlist_head { -	struct hlist_node *first; -}; - -struct hlist_node { -	struct hlist_node *next, **pprev; -}; - -#endif diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 11a8d86f7fe..89715b64a31 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c @@ -20,6 +20,7 @@ static struct rb_node *intlist__node_new(struct rblist *rblist __maybe_unused,  	if (node != NULL) {  		node->i = i; +		node->priv = NULL;  		rc = &node->rb_node;  	} @@ -57,22 +58,36 @@ void intlist__remove(struct intlist *ilist, struct int_node *node)  	rblist__remove_node(&ilist->rblist, &node->rb_node);  } -struct int_node *intlist__find(struct intlist *ilist, int i) +static struct int_node *__intlist__findnew(struct intlist *ilist, +					   int i, bool create)  { -	struct int_node *node; +	struct int_node *node = NULL;  	struct rb_node *rb_node;  	if (ilist == NULL)  		return NULL; -	node = NULL; -	rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); +	if (create) +		rb_node = rblist__findnew(&ilist->rblist, (void *)((long)i)); +	else +		rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); +  	if (rb_node)  		node = container_of(rb_node, struct int_node, rb_node);  	return node;  } +struct int_node *intlist__find(struct intlist *ilist, int i) +{ +	return __intlist__findnew(ilist, i, false); +} + +struct int_node *intlist__findnew(struct intlist *ilist, int i) +{ +	return __intlist__findnew(ilist, i, true); +} +  static int intlist__parse_list(struct intlist *ilist, const char *s)  {  	char *sep; diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 62351dad848..aa6877d3685 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h @@ -9,6 +9,7 @@  struct int_node {  	struct rb_node rb_node;  	int i; +	void *priv;  };  struct intlist { @@ -23,6 +24,7 @@ int intlist__add(struct intlist *ilist, int i);  struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx);  struct int_node *intlist__find(struct intlist *ilist, int i); +struct int_node *intlist__findnew(struct intlist *ilist, int i);  static inline bool intlist__has_entry(struct intlist *ilist, int i)  { diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 933d14f287c..c73e1fc12e5 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -9,6 +9,7 @@  #include "strlist.h"  #include "thread.h"  #include <stdbool.h> +#include <symbol/kallsyms.h>  #include "unwind.h"  int machine__init(struct machine *machine, const char *root_dir, pid_t pid) @@ -26,6 +27,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)  	machine->pid = pid;  	machine->symbol_filter = NULL; +	machine->id_hdr_size = 0;  	machine->root_dir = strdup(root_dir);  	if (machine->root_dir == NULL) @@ -40,12 +42,29 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)  			return -ENOMEM;  		snprintf(comm, sizeof(comm), "[guest/%d]", pid); -		thread__set_comm(thread, comm); +		thread__set_comm(thread, comm, 0);  	}  	return 0;  } +struct machine *machine__new_host(void) +{ +	struct machine *machine = malloc(sizeof(*machine)); + +	if (machine != NULL) { +		machine__init(machine, "", HOST_KERNEL_ID); + +		if (machine__create_kernel_maps(machine) < 0) +			goto out_delete; +	} + +	return machine; +out_delete: +	free(machine); +	return NULL; +} +  static void dsos__delete(struct list_head *dsos)  {  	struct dso *pos, *n; @@ -84,8 +103,7 @@ void machine__exit(struct machine *machine)  	map_groups__exit(&machine->kmaps);  	dsos__delete(&machine->user_dsos);  	dsos__delete(&machine->kernel_dsos); -	free(machine->root_dir); -	machine->root_dir = NULL; +	zfree(&machine->root_dir);  }  void machine__delete(struct machine *machine) @@ -298,6 +316,17 @@ static struct thread *__machine__findnew_thread(struct machine *machine,  		rb_link_node(&th->rb_node, parent, p);  		rb_insert_color(&th->rb_node, &machine->threads);  		machine->last_match = th; + +		/* +		 * We have to initialize map_groups separately +		 * after rb tree is updated. +		 * +		 * The reason is that we call machine__findnew_thread +		 * within thread__init_map_groups to find the thread +		 * leader and that would screwed the rb tree. +		 */ +		if (thread__init_map_groups(th, machine)) +			return NULL;  	}  	return th; @@ -309,12 +338,14 @@ struct thread *machine__findnew_thread(struct machine *machine, pid_t pid,  	return __machine__findnew_thread(machine, pid, tid, true);  } -struct thread *machine__find_thread(struct machine *machine, pid_t tid) +struct thread *machine__find_thread(struct machine *machine, pid_t pid, +				    pid_t tid)  { -	return __machine__findnew_thread(machine, 0, tid, false); +	return __machine__findnew_thread(machine, pid, tid, false);  } -int machine__process_comm_event(struct machine *machine, union perf_event *event) +int machine__process_comm_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample)  {  	struct thread *thread = machine__findnew_thread(machine,  							event->comm.pid, @@ -323,7 +354,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event  	if (dump_trace)  		perf_event__fprintf_comm(event, stdout); -	if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { +	if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) {  		dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");  		return -1;  	} @@ -332,7 +363,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event  }  int machine__process_lost_event(struct machine *machine __maybe_unused, -				union perf_event *event) +				union perf_event *event, struct perf_sample *sample __maybe_unused)  {  	dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",  		    event->lost.id, event->lost.lost); @@ -465,49 +496,50 @@ struct process_args {  	u64 start;  }; -static int symbol__in_kernel(void *arg, const char *name, -			     char type __maybe_unused, u64 start) +static void machine__get_kallsyms_filename(struct machine *machine, char *buf, +					   size_t bufsz)  { -	struct process_args *args = arg; - -	if (strchr(name, '[')) -		return 0; - -	args->start = start; -	return 1; +	if (machine__is_default_guest(machine)) +		scnprintf(buf, bufsz, "%s", symbol_conf.default_guest_kallsyms); +	else +		scnprintf(buf, bufsz, "%s/proc/kallsyms", machine->root_dir);  } -/* Figure out the start address of kernel map from /proc/kallsyms */ -static u64 machine__get_kernel_start_addr(struct machine *machine) +const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; + +/* Figure out the start address of kernel map from /proc/kallsyms. + * Returns the name of the start symbol in *symbol_name. Pass in NULL as + * symbol_name if it's not that important. + */ +static u64 machine__get_kernel_start_addr(struct machine *machine, +					  const char **symbol_name)  { -	const char *filename; -	char path[PATH_MAX]; -	struct process_args args; +	char filename[PATH_MAX]; +	int i; +	const char *name; +	u64 addr = 0; -	if (machine__is_host(machine)) { -		filename = "/proc/kallsyms"; -	} else { -		if (machine__is_default_guest(machine)) -			filename = (char *)symbol_conf.default_guest_kallsyms; -		else { -			sprintf(path, "%s/proc/kallsyms", machine->root_dir); -			filename = path; -		} -	} +	machine__get_kallsyms_filename(machine, filename, PATH_MAX);  	if (symbol__restricted_filename(filename, "/proc/kallsyms"))  		return 0; -	if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) -		return 0; +	for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) { +		addr = kallsyms__get_function_start(filename, name); +		if (addr) +			break; +	} + +	if (symbol_name) +		*symbol_name = name; -	return args.start; +	return addr;  }  int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)  {  	enum map_type type; -	u64 start = machine__get_kernel_start_addr(machine); +	u64 start = machine__get_kernel_start_addr(machine, NULL);  	for (type = 0; type < MAP__NR_TYPES; ++type) {  		struct kmap *kmap; @@ -547,11 +579,10 @@ void machine__destroy_kernel_maps(struct machine *machine)  			 * on one of them.  			 */  			if (type == MAP__FUNCTION) { -				free((char *)kmap->ref_reloc_sym->name); -				kmap->ref_reloc_sym->name = NULL; -				free(kmap->ref_reloc_sym); -			} -			kmap->ref_reloc_sym = NULL; +				zfree((char **)&kmap->ref_reloc_sym->name); +				zfree(&kmap->ref_reloc_sym); +			} else +				kmap->ref_reloc_sym = NULL;  		}  		map__delete(machine->vmlinux_maps[type]); @@ -699,7 +730,7 @@ static char *get_kernel_version(const char *root_dir)  }  static int map_groups__set_modules_path_dir(struct map_groups *mg, -				const char *dir_name) +				const char *dir_name, int depth)  {  	struct dirent *dent;  	DIR *dir = opendir(dir_name); @@ -724,7 +755,15 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,  			    !strcmp(dent->d_name, ".."))  				continue; -			ret = map_groups__set_modules_path_dir(mg, path); +			/* Do not follow top-level source and build symlinks */ +			if (depth == 0) { +				if (!strcmp(dent->d_name, "source") || +				    !strcmp(dent->d_name, "build")) +					continue; +			} + +			ret = map_groups__set_modules_path_dir(mg, path, +							       depth + 1);  			if (ret < 0)  				goto out;  		} else { @@ -749,8 +788,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,  				ret = -1;  				goto out;  			} -			dso__set_long_name(map->dso, long_name); -			map->dso->lname_alloc = 1; +			dso__set_long_name(map->dso, long_name, true);  			dso__kernel_module_get_build_id(map->dso, "");  		}  	} @@ -769,87 +807,60 @@ static int machine__set_modules_path(struct machine *machine)  	if (!version)  		return -1; -	snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", +	snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s",  		 machine->root_dir, version);  	free(version); -	return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); +	return map_groups__set_modules_path_dir(&machine->kmaps, modules_path, 0);  } -static int machine__create_modules(struct machine *machine) +static int machine__create_module(void *arg, const char *name, u64 start)  { -	char *line = NULL; -	size_t n; -	FILE *file; +	struct machine *machine = arg;  	struct map *map; + +	map = machine__new_module(machine, start, name); +	if (map == NULL) +		return -1; + +	dso__kernel_module_get_build_id(map->dso, machine->root_dir); + +	return 0; +} + +static int machine__create_modules(struct machine *machine) +{  	const char *modules;  	char path[PATH_MAX]; -	if (machine__is_default_guest(machine)) +	if (machine__is_default_guest(machine)) {  		modules = symbol_conf.default_guest_modules; -	else { -		sprintf(path, "%s/proc/modules", machine->root_dir); +	} else { +		snprintf(path, PATH_MAX, "%s/proc/modules", machine->root_dir);  		modules = path;  	} -	if (symbol__restricted_filename(path, "/proc/modules")) +	if (symbol__restricted_filename(modules, "/proc/modules"))  		return -1; -	file = fopen(modules, "r"); -	if (file == NULL) +	if (modules__parse(modules, machine, machine__create_module))  		return -1; -	while (!feof(file)) { -		char name[PATH_MAX]; -		u64 start; -		char *sep; -		int line_len; - -		line_len = getline(&line, &n, file); -		if (line_len < 0) -			break; - -		if (!line) -			goto out_failure; - -		line[--line_len] = '\0'; /* \n */ - -		sep = strrchr(line, 'x'); -		if (sep == NULL) -			continue; - -		hex2u64(sep + 1, &start); - -		sep = strchr(line, ' '); -		if (sep == NULL) -			continue; - -		*sep = '\0'; - -		snprintf(name, sizeof(name), "[%s]", line); -		map = machine__new_module(machine, start, name); -		if (map == NULL) -			goto out_delete_line; -		dso__kernel_module_get_build_id(map->dso, machine->root_dir); -	} +	if (!machine__set_modules_path(machine)) +		return 0; -	free(line); -	fclose(file); +	pr_debug("Problems setting modules path maps, continuing anyway...\n"); -	if (machine__set_modules_path(machine) < 0) { -		pr_debug("Problems setting modules path maps, continuing anyway...\n"); -	}  	return 0; - -out_delete_line: -	free(line); -out_failure: -	return -1;  }  int machine__create_kernel_maps(struct machine *machine)  {  	struct dso *kernel = machine__get_kernel(machine); +	const char *name; +	u64 addr = machine__get_kernel_start_addr(machine, &name); +	if (!addr) +		return -1;  	if (kernel == NULL ||  	    __machine__create_kernel_maps(machine, kernel) < 0) @@ -868,6 +879,13 @@ int machine__create_kernel_maps(struct machine *machine)  	 * Now that we have all the maps created, just set the ->end of them:  	 */  	map_groups__fixup_end(&machine->kmaps); + +	if (maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, name, +					     addr)) { +		machine__destroy_kernel_maps(machine); +		return -1; +	} +  	return 0;  } @@ -952,8 +970,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine,  		if (name == NULL)  			goto out_problem; -		map->dso->short_name = name; -		map->dso->sname_alloc = 1; +		dso__set_short_name(map->dso, name, true);  		map->end = map->start + event->mmap.len;  	} else if (is_kernel_mmap) {  		const char *symbol_name = (event->mmap.filename + @@ -998,7 +1015,8 @@ out_problem:  }  int machine__process_mmap2_event(struct machine *machine, -				 union perf_event *event) +				 union perf_event *event, +				 struct perf_sample *sample __maybe_unused)  {  	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;  	struct thread *thread; @@ -1018,7 +1036,7 @@ int machine__process_mmap2_event(struct machine *machine,  	}  	thread = machine__findnew_thread(machine, event->mmap2.pid, -					event->mmap2.pid); +					event->mmap2.tid);  	if (thread == NULL)  		goto out_problem; @@ -1032,6 +1050,8 @@ int machine__process_mmap2_event(struct machine *machine,  			event->mmap2.pid, event->mmap2.maj,  			event->mmap2.min, event->mmap2.ino,  			event->mmap2.ino_generation, +			event->mmap2.prot, +			event->mmap2.flags,  			event->mmap2.filename, type);  	if (map == NULL) @@ -1045,7 +1065,8 @@ out_problem:  	return 0;  } -int machine__process_mmap_event(struct machine *machine, union perf_event *event) +int machine__process_mmap_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample __maybe_unused)  {  	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;  	struct thread *thread; @@ -1065,7 +1086,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event  	}  	thread = machine__findnew_thread(machine, event->mmap.pid, -					 event->mmap.pid); +					 event->mmap.tid);  	if (thread == NULL)  		goto out_problem; @@ -1076,7 +1097,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event  	map = map__new(&machine->user_dsos, event->mmap.start,  			event->mmap.len, event->mmap.pgoff, -			event->mmap.pid, 0, 0, 0, 0, +			event->mmap.pid, 0, 0, 0, 0, 0, 0,  			event->mmap.filename,  			type); @@ -1102,9 +1123,12 @@ static void machine__remove_thread(struct machine *machine, struct thread *th)  	list_add_tail(&th->node, &machine->dead_threads);  } -int machine__process_fork_event(struct machine *machine, union perf_event *event) +int machine__process_fork_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample)  { -	struct thread *thread = machine__find_thread(machine, event->fork.tid); +	struct thread *thread = machine__find_thread(machine, +						     event->fork.pid, +						     event->fork.tid);  	struct thread *parent = machine__findnew_thread(machine,  							event->fork.ppid,  							event->fork.ptid); @@ -1119,7 +1143,7 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event  		perf_event__fprintf_task(event, stdout);  	if (thread == NULL || parent == NULL || -	    thread__fork(thread, parent) < 0) { +	    thread__fork(thread, parent, sample->time) < 0) {  		dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");  		return -1;  	} @@ -1127,10 +1151,12 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event  	return 0;  } -int machine__process_exit_event(struct machine *machine __maybe_unused, -				union perf_event *event) +int machine__process_exit_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample __maybe_unused)  { -	struct thread *thread = machine__find_thread(machine, event->fork.tid); +	struct thread *thread = machine__find_thread(machine, +						     event->fork.pid, +						     event->fork.tid);  	if (dump_trace)  		perf_event__fprintf_task(event, stdout); @@ -1141,23 +1167,24 @@ int machine__process_exit_event(struct machine *machine __maybe_unused,  	return 0;  } -int machine__process_event(struct machine *machine, union perf_event *event) +int machine__process_event(struct machine *machine, union perf_event *event, +			   struct perf_sample *sample)  {  	int ret;  	switch (event->header.type) {  	case PERF_RECORD_COMM: -		ret = machine__process_comm_event(machine, event); break; +		ret = machine__process_comm_event(machine, event, sample); break;  	case PERF_RECORD_MMAP: -		ret = machine__process_mmap_event(machine, event); break; +		ret = machine__process_mmap_event(machine, event, sample); break;  	case PERF_RECORD_MMAP2: -		ret = machine__process_mmap2_event(machine, event); break; +		ret = machine__process_mmap2_event(machine, event, sample); break;  	case PERF_RECORD_FORK: -		ret = machine__process_fork_event(machine, event); break; +		ret = machine__process_fork_event(machine, event, sample); break;  	case PERF_RECORD_EXIT: -		ret = machine__process_exit_event(machine, event); break; +		ret = machine__process_exit_event(machine, event, sample); break;  	case PERF_RECORD_LOST: -		ret = machine__process_lost_event(machine, event); break; +		ret = machine__process_lost_event(machine, event, sample); break;  	default:  		ret = -1;  		break; @@ -1173,39 +1200,22 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex)  	return 0;  } -static const u8 cpumodes[] = { -	PERF_RECORD_MISC_USER, -	PERF_RECORD_MISC_KERNEL, -	PERF_RECORD_MISC_GUEST_USER, -	PERF_RECORD_MISC_GUEST_KERNEL -}; -#define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) -  static void ip__resolve_ams(struct machine *machine, struct thread *thread,  			    struct addr_map_symbol *ams,  			    u64 ip)  {  	struct addr_location al; -	size_t i; -	u8 m;  	memset(&al, 0, sizeof(al)); +	/* +	 * We cannot use the header.misc hint to determine whether a +	 * branch stack address is user, kernel, guest, hypervisor. +	 * Branches may straddle the kernel/user/hypervisor boundaries. +	 * Thus, we have to try consecutively until we find a match +	 * or else, the symbol is unknown +	 */ +	thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al); -	for (i = 0; i < NCPUMODES; i++) { -		m = cpumodes[i]; -		/* -		 * We cannot use the header.misc hint to determine whether a -		 * branch stack address is user, kernel, guest, hypervisor. -		 * Branches may straddle the kernel/user/hypervisor boundaries. -		 * Thus, we have to try consecutively until we find a match -		 * or else, the symbol is unknown -		 */ -		thread__find_addr_location(thread, machine, m, MAP__FUNCTION, -				ip, &al); -		if (al.sym) -			goto found; -	} -found:  	ams->addr = ip;  	ams->al_addr = al.addr;  	ams->sym = al.sym; @@ -1227,37 +1237,35 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread,  	ams->map = al.map;  } -struct mem_info *machine__resolve_mem(struct machine *machine, -				      struct thread *thr, -				      struct perf_sample *sample, -				      u8 cpumode) +struct mem_info *sample__resolve_mem(struct perf_sample *sample, +				     struct addr_location *al)  {  	struct mem_info *mi = zalloc(sizeof(*mi));  	if (!mi)  		return NULL; -	ip__resolve_ams(machine, thr, &mi->iaddr, sample->ip); -	ip__resolve_data(machine, thr, cpumode, &mi->daddr, sample->addr); +	ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip); +	ip__resolve_data(al->machine, al->thread, al->cpumode, +			 &mi->daddr, sample->addr);  	mi->data_src.val = sample->data_src;  	return mi;  } -struct branch_info *machine__resolve_bstack(struct machine *machine, -					    struct thread *thr, -					    struct branch_stack *bs) +struct branch_info *sample__resolve_bstack(struct perf_sample *sample, +					   struct addr_location *al)  { -	struct branch_info *bi;  	unsigned int i; +	const struct branch_stack *bs = sample->branch_stack; +	struct branch_info *bi = calloc(bs->nr, sizeof(struct branch_info)); -	bi = calloc(bs->nr, sizeof(struct branch_info));  	if (!bi)  		return NULL;  	for (i = 0; i < bs->nr; i++) { -		ip__resolve_ams(machine, thr, &bi[i].to, bs->entries[i].to); -		ip__resolve_ams(machine, thr, &bi[i].from, bs->entries[i].from); +		ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to); +		ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from);  		bi[i].flags = bs->entries[i].flags;  	}  	return bi; @@ -1267,10 +1275,12 @@ static int machine__resolve_callchain_sample(struct machine *machine,  					     struct thread *thread,  					     struct ip_callchain *chain,  					     struct symbol **parent, -					     struct addr_location *root_al) +					     struct addr_location *root_al, +					     int max_stack)  {  	u8 cpumode = PERF_RECORD_MISC_USER; -	unsigned int i; +	int chain_nr = min(max_stack, (int)chain->nr); +	int i;  	int err;  	callchain_cursor_reset(&callchain_cursor); @@ -1280,7 +1290,7 @@ static int machine__resolve_callchain_sample(struct machine *machine,  		return 0;  	} -	for (i = 0; i < chain->nr; i++) { +	for (i = 0; i < chain_nr; i++) {  		u64 ip;  		struct addr_location al; @@ -1313,7 +1323,7 @@ static int machine__resolve_callchain_sample(struct machine *machine,  			continue;  		} -		al.filtered = false; +		al.filtered = 0;  		thread__find_addr_location(thread, machine, cpumode,  					   MAP__FUNCTION, ip, &al);  		if (al.sym != NULL) { @@ -1327,8 +1337,6 @@ static int machine__resolve_callchain_sample(struct machine *machine,  				*root_al = al;  				callchain_cursor_reset(&callchain_cursor);  			} -			if (!symbol_conf.use_callchain) -				break;  		}  		err = callchain_cursor_append(&callchain_cursor, @@ -1352,12 +1360,14 @@ int machine__resolve_callchain(struct machine *machine,  			       struct thread *thread,  			       struct perf_sample *sample,  			       struct symbol **parent, -			       struct addr_location *root_al) +			       struct addr_location *root_al, +			       int max_stack)  {  	int ret;  	ret = machine__resolve_callchain_sample(machine, thread, -						sample->callchain, parent, root_al); +						sample->callchain, parent, +						root_al, max_stack);  	if (ret)  		return ret; @@ -1372,7 +1382,41 @@ int machine__resolve_callchain(struct machine *machine,  		return 0;  	return unwind__get_entries(unwind_entry, &callchain_cursor, machine, -				   thread, evsel->attr.sample_regs_user, -				   sample); +				   thread, sample, max_stack);  } + +int machine__for_each_thread(struct machine *machine, +			     int (*fn)(struct thread *thread, void *p), +			     void *priv) +{ +	struct rb_node *nd; +	struct thread *thread; +	int rc = 0; + +	for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { +		thread = rb_entry(nd, struct thread, rb_node); +		rc = fn(thread, priv); +		if (rc != 0) +			return rc; +	} + +	list_for_each_entry(thread, &machine->dead_threads, node) { +		rc = fn(thread, priv); +		if (rc != 0) +			return rc; +	} +	return rc; +} + +int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, +				  struct target *target, struct thread_map *threads, +				  perf_event__handler_t process, bool data_mmap) +{ +	if (target__has_task(target)) +		return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap); +	else if (target__has_cpu(target)) +		return perf_event__synthesize_threads(tool, process, machine, data_mmap); +	/* command specified */ +	return 0; +} diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 58a6be1fc73..c8c74a11939 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -4,6 +4,7 @@  #include <sys/types.h>  #include <linux/rbtree.h>  #include "map.h" +#include "event.h"  struct addr_location;  struct branch_stack; @@ -17,6 +18,8 @@ union perf_event;  #define	HOST_KERNEL_ID			(-1)  #define	DEFAULT_GUEST_KERNEL_ID		(0) +extern const char *ref_reloc_sym_names[]; +  struct machine {  	struct rb_node	  rb_node;  	pid_t		  pid; @@ -38,15 +41,23 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type)  	return machine->vmlinux_maps[type];  } -struct thread *machine__find_thread(struct machine *machine, pid_t tid); - -int machine__process_comm_event(struct machine *machine, union perf_event *event); -int machine__process_exit_event(struct machine *machine, union perf_event *event); -int machine__process_fork_event(struct machine *machine, union perf_event *event); -int machine__process_lost_event(struct machine *machine, union perf_event *event); -int machine__process_mmap_event(struct machine *machine, union perf_event *event); -int machine__process_mmap2_event(struct machine *machine, union perf_event *event); -int machine__process_event(struct machine *machine, union perf_event *event); +struct thread *machine__find_thread(struct machine *machine, pid_t pid, +				    pid_t tid); + +int machine__process_comm_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_exit_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_fork_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_lost_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_mmap_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_mmap2_event(struct machine *machine, union perf_event *event, +				 struct perf_sample *sample); +int machine__process_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample);  typedef void (*machine__process_t)(struct machine *machine, void *data); @@ -74,24 +85,24 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size);  void machines__set_symbol_filter(struct machines *machines,  				 symbol_filter_t symbol_filter); +struct machine *machine__new_host(void);  int machine__init(struct machine *machine, const char *root_dir, pid_t pid);  void machine__exit(struct machine *machine);  void machine__delete_dead_threads(struct machine *machine);  void machine__delete_threads(struct machine *machine);  void machine__delete(struct machine *machine); -struct branch_info *machine__resolve_bstack(struct machine *machine, -					    struct thread *thread, -					    struct branch_stack *bs); -struct mem_info *machine__resolve_mem(struct machine *machine, -				      struct thread *thread, -				      struct perf_sample *sample, u8 cpumode); +struct branch_info *sample__resolve_bstack(struct perf_sample *sample, +					   struct addr_location *al); +struct mem_info *sample__resolve_mem(struct perf_sample *sample, +				     struct addr_location *al);  int machine__resolve_callchain(struct machine *machine,  			       struct perf_evsel *evsel,  			       struct thread *thread,  			       struct perf_sample *sample,  			       struct symbol **parent, -			       struct addr_location *root_al); +			       struct addr_location *root_al, +			       int max_stack);  /*   * Default guest kernel is defined by parameter --guestkallsyms @@ -165,4 +176,19 @@ void machines__destroy_kernel_maps(struct machines *machines);  size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); +int machine__for_each_thread(struct machine *machine, +			     int (*fn)(struct thread *thread, void *p), +			     void *priv); + +int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, +				  struct target *target, struct thread_map *threads, +				  perf_event__handler_t process, bool data_mmap); +static inline +int machine__synthesize_threads(struct machine *machine, struct target *target, +				struct thread_map *threads, bool data_mmap) +{ +	return __machine__synthesize_threads(machine, NULL, target, threads, +					     perf_event__process, data_mmap); +} +  #endif /* __PERF_MACHINE_H */ diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4f6680d2043..25c571f4cba 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -11,6 +11,7 @@  #include "strlist.h"  #include "vdso.h"  #include "build-id.h" +#include "util.h"  #include <linux/string.h>  const char *map_type__name[MAP__NR_TYPES] = { @@ -31,6 +32,93 @@ static inline int is_no_dso_memory(const char *filename)  	       !strcmp(filename, "[heap]");  } +static inline int is_android_lib(const char *filename) +{ +	return !strncmp(filename, "/data/app-lib", 13) || +	       !strncmp(filename, "/system/lib", 11); +} + +static inline bool replace_android_lib(const char *filename, char *newfilename) +{ +	const char *libname; +	char *app_abi; +	size_t app_abi_length, new_length; +	size_t lib_length = 0; + +	libname  = strrchr(filename, '/'); +	if (libname) +		lib_length = strlen(libname); + +	app_abi = getenv("APP_ABI"); +	if (!app_abi) +		return false; + +	app_abi_length = strlen(app_abi); + +	if (!strncmp(filename, "/data/app-lib", 13)) { +		char *apk_path; + +		if (!app_abi_length) +			return false; + +		new_length = 7 + app_abi_length + lib_length; + +		apk_path = getenv("APK_PATH"); +		if (apk_path) { +			new_length += strlen(apk_path) + 1; +			if (new_length > PATH_MAX) +				return false; +			snprintf(newfilename, new_length, +				 "%s/libs/%s/%s", apk_path, app_abi, libname); +		} else { +			if (new_length > PATH_MAX) +				return false; +			snprintf(newfilename, new_length, +				 "libs/%s/%s", app_abi, libname); +		} +		return true; +	} + +	if (!strncmp(filename, "/system/lib/", 11)) { +		char *ndk, *app; +		const char *arch; +		size_t ndk_length; +		size_t app_length; + +		ndk = getenv("NDK_ROOT"); +		app = getenv("APP_PLATFORM"); + +		if (!(ndk && app)) +			return false; + +		ndk_length = strlen(ndk); +		app_length = strlen(app); + +		if (!(ndk_length && app_length && app_abi_length)) +			return false; + +		arch = !strncmp(app_abi, "arm", 3) ? "arm" : +		       !strncmp(app_abi, "mips", 4) ? "mips" : +		       !strncmp(app_abi, "x86", 3) ? "x86" : NULL; + +		if (!arch) +			return false; + +		new_length = 27 + ndk_length + +			     app_length + lib_length +			   + strlen(arch); + +		if (new_length > PATH_MAX) +			return false; +		snprintf(newfilename, new_length, +			"%s/platforms/%s/arch-%s/usr/lib/%s", +			ndk, app, arch, libname); + +		return true; +	} +	return false; +} +  void map__init(struct map *map, enum map_type type,  	       u64 start, u64 end, u64 pgoff, struct dso *dso)  { @@ -38,6 +126,7 @@ void map__init(struct map *map, enum map_type type,  	map->start    = start;  	map->end      = end;  	map->pgoff    = pgoff; +	map->reloc    = 0;  	map->dso      = dso;  	map->map_ip   = map__map_ip;  	map->unmap_ip = map__unmap_ip; @@ -49,7 +138,7 @@ void map__init(struct map *map, enum map_type type,  struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, -		     u64 ino_gen, char *filename, +		     u64 ino_gen, u32 prot, u32 flags, char *filename,  		     enum map_type type)  {  	struct map *map = malloc(sizeof(*map)); @@ -57,8 +146,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  	if (map != NULL) {  		char newfilename[PATH_MAX];  		struct dso *dso; -		int anon, no_dso, vdso; +		int anon, no_dso, vdso, android; +		android = is_android_lib(filename);  		anon = is_anon_memory(filename);  		vdso = is_vdso_map(filename);  		no_dso = is_no_dso_memory(filename); @@ -67,12 +157,19 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  		map->min = d_min;  		map->ino = ino;  		map->ino_generation = ino_gen; +		map->prot = prot; +		map->flags = flags; -		if (anon) { +		if ((anon || no_dso) && type == MAP__FUNCTION) {  			snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);  			filename = newfilename;  		} +		if (android) { +			if (replace_android_lib(filename, newfilename)) +				filename = newfilename; +		} +  		if (vdso) {  			pgoff = 0;  			dso = vdso__dso_findnew(dsos__list); @@ -92,7 +189,7 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  			 * functions still return NULL, and we avoid the  			 * unnecessary map__load warning.  			 */ -			if (no_dso) +			if (type != MAP__FUNCTION)  				dso__set_loaded(dso, map->type);  		}  	} @@ -172,7 +269,7 @@ int map__load(struct map *map, symbol_filter_t filter)  		pr_warning(", continuing without symbols\n");  		return -1;  	} else if (nr == 0) { -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT  		const size_t len = strlen(name);  		const size_t real_len = len - sizeof(DSO__DELETED); @@ -252,10 +349,32 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp)  	return fprintf(fp, "%s", dsoname);  } -/* +int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, +			 FILE *fp) +{ +	char *srcline; +	int ret = 0; + +	if (map && map->dso) { +		srcline = get_srcline(map->dso, +				      map__rip_2objdump(map, addr)); +		if (srcline != SRCLINE_UNKNOWN) +			ret = fprintf(fp, "%s%s", prefix, srcline); +		free_srcline(srcline); +	} +	return ret; +} + +/** + * map__rip_2objdump - convert symbol start address to objdump address. + * @map: memory map + * @rip: symbol start address + *   * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.   * map->dso->adjust_symbols==1 for ET_EXEC-like cases except ET_REL which is   * relative to section start. + * + * Return: Address suitable for passing to "objdump --start-address="   */  u64 map__rip_2objdump(struct map *map, u64 rip)  { @@ -265,7 +384,30 @@ u64 map__rip_2objdump(struct map *map, u64 rip)  	if (map->dso->rel)  		return rip - map->pgoff; -	return map->unmap_ip(map, rip); +	return map->unmap_ip(map, rip) - map->reloc; +} + +/** + * map__objdump_2mem - convert objdump address to a memory address. + * @map: memory map + * @ip: objdump address + * + * Closely related to map__rip_2objdump(), this function takes an address from + * objdump and converts it to a memory address.  Note this assumes that @map + * contains the address.  To be sure the result is valid, check it forwards + * e.g. map__rip_2objdump(map->map_ip(map, map__objdump_2mem(map, ip))) == ip + * + * Return: Memory address. + */ +u64 map__objdump_2mem(struct map *map, u64 ip) +{ +	if (!map->dso->adjust_symbols) +		return map->unmap_ip(map, ip); + +	if (map->dso->rel) +		return map->unmap_ip(map, ip + map->pgoff); + +	return ip + map->reloc;  }  void map_groups__init(struct map_groups *mg) @@ -276,6 +418,7 @@ void map_groups__init(struct map_groups *mg)  		INIT_LIST_HEAD(&mg->removed_maps[i]);  	}  	mg->machine = NULL; +	mg->refcnt = 1;  }  static void maps__delete(struct rb_root *maps) @@ -311,6 +454,28 @@ void map_groups__exit(struct map_groups *mg)  	}  } +struct map_groups *map_groups__new(void) +{ +	struct map_groups *mg = malloc(sizeof(*mg)); + +	if (mg != NULL) +		map_groups__init(mg); + +	return mg; +} + +void map_groups__delete(struct map_groups *mg) +{ +	map_groups__exit(mg); +	free(mg); +} + +void map_groups__put(struct map_groups *mg) +{ +	if (--mg->refcnt == 0) +		map_groups__delete(mg); +} +  void map_groups__flush(struct map_groups *mg)  {  	int type; @@ -340,7 +505,8 @@ struct symbol *map_groups__find_symbol(struct map_groups *mg,  {  	struct map *map = map_groups__find(mg, type, addr); -	if (map != NULL) { +	/* Ensure map is loaded before using map->map_ip */ +	if (map != NULL && map__load(map, filter) >= 0) {  		if (mapp != NULL)  			*mapp = map;  		return map__find_symbol(map, map->map_ip(map, addr), filter); @@ -371,6 +537,23 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,  	return NULL;  } +int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) +{ +	if (ams->addr < ams->map->start || ams->addr > ams->map->end) { +		if (ams->map->groups == NULL) +			return -1; +		ams->map = map_groups__find(ams->map->groups, ams->map->type, +					    ams->addr); +		if (ams->map == NULL) +			return -1; +	} + +	ams->al_addr = ams->map->map_ip(ams->map, ams->addr); +	ams->sym = map__find_symbol(ams->map, ams->al_addr, filter); + +	return ams->sym ? 0 : -1; +} +  size_t __map_groups__fprintf_maps(struct map_groups *mg,  				  enum map_type type, int verbose, FILE *fp)  { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4886ca28053..7758c72522e 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -6,7 +6,7 @@  #include <linux/rbtree.h>  #include <stdio.h>  #include <stdbool.h> -#include "types.h" +#include <linux/types.h>  enum map_type {  	MAP__FUNCTION = 0, @@ -35,7 +35,10 @@ struct map {  	bool			referenced;  	bool			erange_warned;  	u32			priv; +	u32			prot; +	u32			flags;  	u64			pgoff; +	u64			reloc;  	u32			maj, min; /* only valid for MMAP2 record */  	u64			ino;      /* only valid for MMAP2 record */  	u64			ino_generation;/* only valid for MMAP2 record */ @@ -58,8 +61,20 @@ struct map_groups {  	struct rb_root	 maps[MAP__NR_TYPES];  	struct list_head removed_maps[MAP__NR_TYPES];  	struct machine	 *machine; +	int		 refcnt;  }; +struct map_groups *map_groups__new(void); +void map_groups__delete(struct map_groups *mg); + +static inline struct map_groups *map_groups__get(struct map_groups *mg) +{ +	++mg->refcnt; +	return mg; +} + +void map_groups__put(struct map_groups *mg); +  static inline struct kmap *map__kmap(struct map *map)  {  	return (struct kmap *)(map + 1); @@ -84,15 +99,28 @@ static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip)  /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */  u64 map__rip_2objdump(struct map *map, u64 rip); +/* objdump address -> memory address */ +u64 map__objdump_2mem(struct map *map, u64 ip); +  struct symbol; +/* map__for_each_symbol - iterate over the symbols in the given map + * + * @map: the 'struct map *' in which symbols itereated + * @pos: the 'struct symbol *' to use as a loop cursor + * @n: the 'struct rb_node *' to use as a temporary storage + * Note: caller must ensure map->dso is not NULL (map is loaded). + */ +#define map__for_each_symbol(map, pos, n)	\ +	dso__for_each_symbol(map->dso, pos, n, map->type) +  typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);  void map__init(struct map *map, enum map_type type,  	       u64 start, u64 end, u64 pgoff, struct dso *dso);  struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, -		     u64 ino_gen, +		     u64 ino_gen, u32 prot, u32 flags,  		     char *filename, enum map_type type);  struct map *map__new2(u64 start, struct dso *dso, enum map_type type);  void map__delete(struct map *map); @@ -100,6 +128,8 @@ struct map *map__clone(struct map *map);  int map__overlap(struct map *l, struct map *r);  size_t map__fprintf(struct map *map, FILE *fp);  size_t map__fprintf_dsoname(struct map *map, FILE *fp); +int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, +			 FILE *fp);  int map__load(struct map *map, symbol_filter_t filter);  struct symbol *map__find_symbol(struct map *map, @@ -167,6 +197,10 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,  					       struct map **mapp,  					       symbol_filter_t filter); +struct addr_map_symbol; + +int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter); +  static inline  struct symbol *map_groups__find_function_by_name(struct map_groups *mg,  						 const char *name, struct map **mapp, diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c index 3322b8446e8..31ee02d4e98 100644 --- a/tools/perf/util/pager.c +++ b/tools/perf/util/pager.c @@ -57,13 +57,13 @@ void setup_pager(void)  	}  	if (!pager)  		pager = getenv("PAGER"); -	if (!pager) { -		if (!access("/usr/bin/pager", X_OK)) -			pager = "/usr/bin/pager"; -	} +	if (!(pager || access("/usr/bin/pager", X_OK))) +		pager = "/usr/bin/pager"; +	if (!(pager || access("/usr/bin/less", X_OK))) +		pager = "/usr/bin/less";  	if (!pager) -		pager = "less"; -	else if (!*pager || !strcmp(pager, "cat")) +		pager = "cat"; +	if (!*pager || !strcmp(pager, "cat"))  		return;  	spawned_pager = 1; /* means we are emitting to terminal */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 98125319b15..1e15df10a88 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -10,7 +10,7 @@  #include "symbol.h"  #include "cache.h"  #include "header.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include "parse-events-bison.h"  #define YY_EXTRA_TYPE int  #include "parse-events-flex.h" @@ -204,7 +204,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  				}  				path->name = malloc(MAX_EVENT_LENGTH);  				if (!path->name) { -					free(path->system); +					zfree(&path->system);  					free(path);  					return NULL;  				} @@ -236,8 +236,8 @@ struct tracepoint_path *tracepoint_name_to_path(const char *name)  	path->name = strdup(str+1);  	if (path->system == NULL || path->name == NULL) { -		free(path->system); -		free(path->name); +		zfree(&path->system); +		zfree(&path->name);  		free(path);  		path = NULL;  	} @@ -269,29 +269,30 @@ const char *event_type(int type) -static int __add_event(struct list_head *list, int *idx, -		       struct perf_event_attr *attr, -		       char *name, struct cpu_map *cpus) +static struct perf_evsel * +__add_event(struct list_head *list, int *idx, +	    struct perf_event_attr *attr, +	    char *name, struct cpu_map *cpus)  {  	struct perf_evsel *evsel;  	event_attr_init(attr); -	evsel = perf_evsel__new(attr, (*idx)++); +	evsel = perf_evsel__new_idx(attr, (*idx)++);  	if (!evsel) -		return -ENOMEM; +		return NULL;  	evsel->cpus = cpus;  	if (name)  		evsel->name = strdup(name);  	list_add_tail(&evsel->node, list); -	return 0; +	return evsel;  }  static int add_event(struct list_head *list, int *idx,  		     struct perf_event_attr *attr, char *name)  { -	return __add_event(list, idx, attr, name, NULL); +	return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM;  }  static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) @@ -378,7 +379,7 @@ static int add_tracepoint(struct list_head *list, int *idx,  {  	struct perf_evsel *evsel; -	evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++); +	evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++);  	if (!evsel)  		return -ENOMEM; @@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx,  {  	struct perf_event_attr attr;  	struct perf_pmu *pmu; +	struct perf_evsel *evsel; +	const char *unit; +	double scale;  	pmu = perf_pmu__find(name);  	if (!pmu) @@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx,  	memset(&attr, 0, sizeof(attr)); -	if (perf_pmu__check_alias(pmu, head_config)) +	if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))  		return -EINVAL;  	/* @@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx,  	if (perf_pmu__config(pmu, &attr, head_config))  		return -EINVAL; -	return __add_event(list, idx, &attr, pmu_event_name(head_config), -			   pmu->cpus); +	evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), +			    pmu->cpus); +	if (evsel) { +		evsel->unit = unit; +		evsel->scale = scale; +	} + +	return evsel ? 0 : -ENOMEM;  }  int parse_events__modifier_group(struct list_head *list, @@ -810,8 +820,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)  	if (!add && get_event_modifier(&mod, str, NULL))  		return -EINVAL; -	list_for_each_entry(evsel, list, node) { - +	__evlist__for_each(list, evsel) {  		if (add && get_event_modifier(&mod, str, evsel))  			return -EINVAL; @@ -835,7 +844,7 @@ int parse_events_name(struct list_head *list, char *name)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, list, node) { +	__evlist__for_each(list, evsel) {  		if (!evsel->name)  			evsel->name = strdup(name);  	} @@ -907,7 +916,7 @@ int parse_events_terms(struct list_head *terms, const char *str)  	ret = parse_events__scanner(str, &data, PE_START_TERMS);  	if (!ret) {  		list_splice(data.terms, terms); -		free(data.terms); +		zfree(&data.terms);  		return 0;  	} @@ -998,8 +1007,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,  	char evt_path[MAXPATHLEN];  	char dir_path[MAXPATHLEN]; -	if (debugfs_valid_mountpoint(tracing_events_path)) +	if (debugfs_valid_mountpoint(tracing_events_path)) { +		printf("  [ Tracepoints not available: %s ]\n", strerror(errno));  		return; +	}  	sys_dir = opendir(tracing_events_path);  	if (!sys_dir) @@ -1080,12 +1091,12 @@ int is_valid_tracepoint(const char *event_string)  static bool is_event_supported(u8 type, unsigned config)  {  	bool ret = true; +	int open_return;  	struct perf_evsel *evsel;  	struct perf_event_attr attr = {  		.type = type,  		.config = config,  		.disabled = 1, -		.exclude_kernel = 1,  	};  	struct {  		struct thread_map map; @@ -1095,9 +1106,22 @@ static bool is_event_supported(u8 type, unsigned config)  		.threads = { 0 },  	}; -	evsel = perf_evsel__new(&attr, 0); +	evsel = perf_evsel__new(&attr);  	if (evsel) { -		ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; +		open_return = perf_evsel__open(evsel, NULL, &tmap.map); +		ret = open_return >= 0; + +		if (open_return == -EACCES) { +			/* +			 * This happens if the paranoid value +			 * /proc/sys/kernel/perf_event_paranoid is set to 2 +			 * Re-run with exclude_kernel set; we don't do that +			 * by default as some ARM machines do not support it. +			 * +			 */ +			evsel->attr.exclude_kernel = 1; +			ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; +		}  		perf_evsel__delete(evsel);  	} diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index f1cb4c4b3c7..df094b4ed5e 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -6,9 +6,8 @@  #include <linux/list.h>  #include <stdbool.h> -#include "types.h" +#include <linux/types.h>  #include <linux/perf_event.h> -#include "types.h"  struct list_head;  struct perf_evsel; diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 91346b75396..343299575b3 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -126,6 +126,37 @@ modifier_bp	[rwx]{1,3}  } +<config>{ +config			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } +config1			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } +config2			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } +name			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } +period			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } +branch_type		{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } +,			{ return ','; } +"/"			{ BEGIN(INITIAL); return '/'; } +{name_minus}		{ return str(yyscanner, PE_NAME); } +} + +<mem>{ +{modifier_bp}		{ return str(yyscanner, PE_MODIFIER_BP); } +:			{ return ':'; } +{num_dec}		{ return value(yyscanner, 10); } +{num_hex}		{ return value(yyscanner, 16); } +	/* +	 * We need to separate 'mem:' scanner part, in order to get specific +	 * modifier bits parsed out. Otherwise we would need to handle PE_NAME +	 * and we'd need to parse it manually. During the escape from <mem> +	 * state we need to put the escaping char back, so we dont miss it. +	 */ +.			{ unput(*yytext); BEGIN(INITIAL); } +	/* +	 * We destroy the scanner after reaching EOF, +	 * but anyway just to be sure get back to INIT state. +	 */ +<<EOF>>			{ BEGIN(INITIAL); } +} +  cpu-cycles|cycles				{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }  stalled-cycles-frontend|idle-cycles-frontend	{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }  stalled-cycles-backend|idle-cycles-backend	{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } @@ -162,18 +193,6 @@ speculative-read|speculative-load	|  refs|Reference|ops|access		|  misses|miss				{ return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } -<config>{ -config			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } -config1			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } -config2			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } -name			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } -period			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } -branch_type		{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } -,			{ return ','; } -"/"			{ BEGIN(INITIAL); return '/'; } -{name_minus}		{ return str(yyscanner, PE_NAME); } -} -  mem:			{ BEGIN(mem); return PE_PREFIX_MEM; }  r{num_raw_hex}		{ return raw(yyscanner); }  {num_dec}		{ return value(yyscanner, 10); } @@ -189,25 +208,7 @@ r{num_raw_hex}		{ return raw(yyscanner); }  "}"			{ return '}'; }  =			{ return '='; }  \n			{ } - -<mem>{ -{modifier_bp}		{ return str(yyscanner, PE_MODIFIER_BP); } -:			{ return ':'; } -{num_dec}		{ return value(yyscanner, 10); } -{num_hex}		{ return value(yyscanner, 16); } -	/* -	 * We need to separate 'mem:' scanner part, in order to get specific -	 * modifier bits parsed out. Otherwise we would need to handle PE_NAME -	 * and we'd need to parse it manually. During the escape from <mem> -	 * state we need to put the escaping char back, so we dont miss it. -	 */ -.			{ unput(*yytext); BEGIN(INITIAL); } -	/* -	 * We destroy the scanner after reaching EOF, -	 * but anyway just to be sure get back to INIT state. -	 */ -<<EOF>>			{ BEGIN(INITIAL); } -} +.			{ }  %% diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 4eb67ec333f..0bc87ba46bf 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -9,7 +9,7 @@  #include <linux/compiler.h>  #include <linux/list.h> -#include "types.h" +#include <linux/types.h>  #include "util.h"  #include "parse-events.h"  #include "parse-events-bison.h" @@ -299,6 +299,18 @@ PE_PREFIX_MEM PE_VALUE sep_dc  }  event_legacy_tracepoint: +PE_NAME '-' PE_NAME ':' PE_NAME +{ +	struct parse_events_evlist *data = _data; +	struct list_head *list; +	char sys_name[128]; +	snprintf(&sys_name, 128, "%s-%s", $1, $3); + +	ALLOC_LIST(list); +	ABORT_ON(parse_events_add_tracepoint(list, &data->idx, &sys_name, $5)); +	$$ = list; +} +|  PE_NAME ':' PE_NAME  {  	struct parse_events_evlist *data = _data; diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 2bc9e70df7e..bf48092983c 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -78,6 +78,8 @@ static int get_value(struct parse_opt_ctx_t *p,  	case OPTION_BOOLEAN:  		*(bool *)opt->value = unset ? false : true; +		if (opt->set) +			*(bool *)opt->set = true;  		return 0;  	case OPTION_INCR: @@ -224,6 +226,24 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,  			return 0;  		}  		if (!rest) { +			if (!prefixcmp(options->long_name, "no-")) { +				/* +				 * The long name itself starts with "no-", so +				 * accept the option without "no-" so that users +				 * do not have to enter "no-no-" to get the +				 * negation. +				 */ +				rest = skip_prefix(arg, options->long_name + 3); +				if (rest) { +					flags |= OPT_UNSET; +					goto match; +				} +				/* Abbreviated case */ +				if (!prefixcmp(options->long_name + 3, arg)) { +					flags |= OPT_UNSET; +					goto is_abbreviated; +				} +			}  			/* abbreviated? */  			if (!strncmp(options->long_name, arg, arg_end - arg)) {  is_abbreviated: @@ -259,6 +279,7 @@ is_abbreviated:  			if (!rest)  				continue;  		} +match:  		if (*rest) {  			if (*rest != '=')  				continue; @@ -339,10 +360,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,  		if (arg[1] != '-') {  			ctx->opt = arg + 1;  			if (internal_help && *ctx->opt == 'h') -				return parse_options_usage(usagestr, options); +				return usage_with_options_internal(usagestr, options, 0);  			switch (parse_short_opt(ctx, options)) {  			case -1: -				return parse_options_usage(usagestr, options); +				return parse_options_usage(usagestr, options, arg + 1, 1);  			case -2:  				goto unknown;  			default: @@ -352,10 +373,11 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,  				check_typos(arg + 1, options);  			while (ctx->opt) {  				if (internal_help && *ctx->opt == 'h') -					return parse_options_usage(usagestr, options); +					return usage_with_options_internal(usagestr, options, 0); +				arg = ctx->opt;  				switch (parse_short_opt(ctx, options)) {  				case -1: -					return parse_options_usage(usagestr, options); +					return parse_options_usage(usagestr, options, arg, 1);  				case -2:  					/* fake a short option thing to hide the fact that we may have  					 * started to parse aggregated stuff @@ -383,12 +405,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,  		if (internal_help && !strcmp(arg + 2, "help-all"))  			return usage_with_options_internal(usagestr, options, 1);  		if (internal_help && !strcmp(arg + 2, "help")) -			return parse_options_usage(usagestr, options); +			return usage_with_options_internal(usagestr, options, 0);  		if (!strcmp(arg + 2, "list-opts")) -			return PARSE_OPT_LIST; +			return PARSE_OPT_LIST_OPTS; +		if (!strcmp(arg + 2, "list-cmds")) +			return PARSE_OPT_LIST_SUBCMDS;  		switch (parse_long_opt(ctx, arg + 2, options)) {  		case -1: -			return parse_options_usage(usagestr, options); +			return parse_options_usage(usagestr, options, arg + 2, 0);  		case -2:  			goto unknown;  		default: @@ -411,25 +435,45 @@ int parse_options_end(struct parse_opt_ctx_t *ctx)  	return ctx->cpidx + ctx->argc;  } -int parse_options(int argc, const char **argv, const struct option *options, -		  const char * const usagestr[], int flags) +int parse_options_subcommand(int argc, const char **argv, const struct option *options, +			const char *const subcommands[], const char *usagestr[], int flags)  {  	struct parse_opt_ctx_t ctx;  	perf_header__set_cmdline(argc, argv); +	/* build usage string if it's not provided */ +	if (subcommands && !usagestr[0]) { +		struct strbuf buf = STRBUF_INIT; + +		strbuf_addf(&buf, "perf %s [<options>] {", argv[0]); +		for (int i = 0; subcommands[i]; i++) { +			if (i) +				strbuf_addstr(&buf, "|"); +			strbuf_addstr(&buf, subcommands[i]); +		} +		strbuf_addstr(&buf, "}"); + +		usagestr[0] = strdup(buf.buf); +		strbuf_release(&buf); +	} +  	parse_options_start(&ctx, argc, argv, flags);  	switch (parse_options_step(&ctx, options, usagestr)) {  	case PARSE_OPT_HELP:  		exit(129);  	case PARSE_OPT_DONE:  		break; -	case PARSE_OPT_LIST: +	case PARSE_OPT_LIST_OPTS:  		while (options->type != OPTION_END) {  			printf("--%s ", options->long_name);  			options++;  		}  		exit(130); +	case PARSE_OPT_LIST_SUBCMDS: +		for (int i = 0; subcommands[i]; i++) +			printf("%s ", subcommands[i]); +		exit(130);  	default: /* PARSE_OPT_UNKNOWN */  		if (ctx.argv[0][1] == '-') {  			error("unknown option `%s'", ctx.argv[0] + 2); @@ -442,9 +486,99 @@ int parse_options(int argc, const char **argv, const struct option *options,  	return parse_options_end(&ctx);  } +int parse_options(int argc, const char **argv, const struct option *options, +		  const char * const usagestr[], int flags) +{ +	return parse_options_subcommand(argc, argv, options, NULL, +					(const char **) usagestr, flags); +} +  #define USAGE_OPTS_WIDTH 24  #define USAGE_GAP         2 +static void print_option_help(const struct option *opts, int full) +{ +	size_t pos; +	int pad; + +	if (opts->type == OPTION_GROUP) { +		fputc('\n', stderr); +		if (*opts->help) +			fprintf(stderr, "%s\n", opts->help); +		return; +	} +	if (!full && (opts->flags & PARSE_OPT_HIDDEN)) +		return; + +	pos = fprintf(stderr, "    "); +	if (opts->short_name) +		pos += fprintf(stderr, "-%c", opts->short_name); +	else +		pos += fprintf(stderr, "    "); + +	if (opts->long_name && opts->short_name) +		pos += fprintf(stderr, ", "); +	if (opts->long_name) +		pos += fprintf(stderr, "--%s", opts->long_name); + +	switch (opts->type) { +	case OPTION_ARGUMENT: +		break; +	case OPTION_LONG: +	case OPTION_U64: +	case OPTION_INTEGER: +	case OPTION_UINTEGER: +		if (opts->flags & PARSE_OPT_OPTARG) +			if (opts->long_name) +				pos += fprintf(stderr, "[=<n>]"); +			else +				pos += fprintf(stderr, "[<n>]"); +		else +			pos += fprintf(stderr, " <n>"); +		break; +	case OPTION_CALLBACK: +		if (opts->flags & PARSE_OPT_NOARG) +			break; +		/* FALLTHROUGH */ +	case OPTION_STRING: +		if (opts->argh) { +			if (opts->flags & PARSE_OPT_OPTARG) +				if (opts->long_name) +					pos += fprintf(stderr, "[=<%s>]", opts->argh); +				else +					pos += fprintf(stderr, "[<%s>]", opts->argh); +			else +				pos += fprintf(stderr, " <%s>", opts->argh); +		} else { +			if (opts->flags & PARSE_OPT_OPTARG) +				if (opts->long_name) +					pos += fprintf(stderr, "[=...]"); +				else +					pos += fprintf(stderr, "[...]"); +			else +				pos += fprintf(stderr, " ..."); +		} +		break; +	default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ +	case OPTION_END: +	case OPTION_GROUP: +	case OPTION_BIT: +	case OPTION_BOOLEAN: +	case OPTION_INCR: +	case OPTION_SET_UINT: +	case OPTION_SET_PTR: +		break; +	} + +	if (pos <= USAGE_OPTS_WIDTH) +		pad = USAGE_OPTS_WIDTH - pos; +	else { +		fputc('\n', stderr); +		pad = USAGE_OPTS_WIDTH; +	} +	fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); +} +  int usage_with_options_internal(const char * const *usagestr,  				const struct option *opts, int full)  { @@ -464,87 +598,9 @@ int usage_with_options_internal(const char * const *usagestr,  	if (opts->type != OPTION_GROUP)  		fputc('\n', stderr); -	for (; opts->type != OPTION_END; opts++) { -		size_t pos; -		int pad; - -		if (opts->type == OPTION_GROUP) { -			fputc('\n', stderr); -			if (*opts->help) -				fprintf(stderr, "%s\n", opts->help); -			continue; -		} -		if (!full && (opts->flags & PARSE_OPT_HIDDEN)) -			continue; - -		pos = fprintf(stderr, "    "); -		if (opts->short_name) -			pos += fprintf(stderr, "-%c", opts->short_name); -		else -			pos += fprintf(stderr, "    "); - -		if (opts->long_name && opts->short_name) -			pos += fprintf(stderr, ", "); -		if (opts->long_name) -			pos += fprintf(stderr, "--%s", opts->long_name); +	for (  ; opts->type != OPTION_END; opts++) +		print_option_help(opts, full); -		switch (opts->type) { -		case OPTION_ARGUMENT: -			break; -		case OPTION_LONG: -		case OPTION_U64: -		case OPTION_INTEGER: -		case OPTION_UINTEGER: -			if (opts->flags & PARSE_OPT_OPTARG) -				if (opts->long_name) -					pos += fprintf(stderr, "[=<n>]"); -				else -					pos += fprintf(stderr, "[<n>]"); -			else -				pos += fprintf(stderr, " <n>"); -			break; -		case OPTION_CALLBACK: -			if (opts->flags & PARSE_OPT_NOARG) -				break; -			/* FALLTHROUGH */ -		case OPTION_STRING: -			if (opts->argh) { -				if (opts->flags & PARSE_OPT_OPTARG) -					if (opts->long_name) -						pos += fprintf(stderr, "[=<%s>]", opts->argh); -					else -						pos += fprintf(stderr, "[<%s>]", opts->argh); -				else -					pos += fprintf(stderr, " <%s>", opts->argh); -			} else { -				if (opts->flags & PARSE_OPT_OPTARG) -					if (opts->long_name) -						pos += fprintf(stderr, "[=...]"); -					else -						pos += fprintf(stderr, "[...]"); -				else -					pos += fprintf(stderr, " ..."); -			} -			break; -		default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ -		case OPTION_END: -		case OPTION_GROUP: -		case OPTION_BIT: -		case OPTION_BOOLEAN: -		case OPTION_INCR: -		case OPTION_SET_UINT: -		case OPTION_SET_PTR: -			break; -		} - -		if (pos <= USAGE_OPTS_WIDTH) -			pad = USAGE_OPTS_WIDTH - pos; -		else { -			fputc('\n', stderr); -			pad = USAGE_OPTS_WIDTH; -		} -		fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); -	}  	fputc('\n', stderr);  	return PARSE_OPT_HELP; @@ -559,9 +615,45 @@ void usage_with_options(const char * const *usagestr,  }  int parse_options_usage(const char * const *usagestr, -			const struct option *opts) +			const struct option *opts, +			const char *optstr, bool short_opt)  { -	return usage_with_options_internal(usagestr, opts, 0); +	if (!usagestr) +		goto opt; + +	fprintf(stderr, "\n usage: %s\n", *usagestr++); +	while (*usagestr && **usagestr) +		fprintf(stderr, "    or: %s\n", *usagestr++); +	while (*usagestr) { +		fprintf(stderr, "%s%s\n", +				**usagestr ? "    " : "", +				*usagestr); +		usagestr++; +	} +	fputc('\n', stderr); + +opt: +	for (  ; opts->type != OPTION_END; opts++) { +		if (short_opt) { +			if (opts->short_name == *optstr) +				break; +			continue; +		} + +		if (opts->long_name == NULL) +			continue; + +		if (!prefixcmp(optstr, opts->long_name)) +			break; +		if (!prefixcmp(optstr, "no-") && +		    !prefixcmp(optstr + 3, opts->long_name)) +			break; +	} + +	if (opts->type != OPTION_END) +		print_option_help(opts, 0); + +	return PARSE_OPT_HELP;  } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 7bb5999940c..d8dac8ac5f3 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -82,6 +82,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);   *   OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in   *   the value when met.   *   CALLBACKS can use it like they want. + * + * `set`:: + *   whether an option was set by the user   */  struct option {  	enum parse_opt_type type; @@ -94,6 +97,7 @@ struct option {  	int flags;  	parse_opt_cb *callback;  	intptr_t defval; +	bool *set;  };  #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) @@ -103,6 +107,10 @@ struct option {  #define OPT_GROUP(h)                { .type = OPTION_GROUP, .help = (h) }  #define OPT_BIT(s, l, v, h, b)      { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }  #define OPT_BOOLEAN(s, l, v, h)     { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } +#define OPT_BOOLEAN_SET(s, l, v, os, h) \ +	{ .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ +	.value = check_vtype(v, bool *), .help = (h), \ +	.set = check_vtype(os, bool *)}  #define OPT_INCR(s, l, v, h)        { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }  #define OPT_SET_UINT(s, l, v, h, i)  { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }  #define OPT_SET_PTR(s, l, v, h, p)  { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } @@ -132,6 +140,11 @@ extern int parse_options(int argc, const char **argv,                           const struct option *options,                           const char * const usagestr[], int flags); +extern int parse_options_subcommand(int argc, const char **argv, +				const struct option *options, +				const char *const subcommands[], +				const char *usagestr[], int flags); +  extern NORETURN void usage_with_options(const char * const *usagestr,                                          const struct option *options); @@ -140,7 +153,8 @@ extern NORETURN void usage_with_options(const char * const *usagestr,  enum {  	PARSE_OPT_HELP = -1,  	PARSE_OPT_DONE, -	PARSE_OPT_LIST, +	PARSE_OPT_LIST_OPTS, +	PARSE_OPT_LIST_SUBCMDS,  	PARSE_OPT_UNKNOWN,  }; @@ -158,7 +172,9 @@ struct parse_opt_ctx_t {  };  extern int parse_options_usage(const char * const *usagestr, -			       const struct option *opts); +			       const struct option *opts, +			       const char *optstr, +			       bool short_opt);  extern void parse_options_start(struct parse_opt_ctx_t *ctx,  				int argc, const char **argv, int flags); diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index a8c49548ca4..5d13cb45b31 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -22,19 +22,23 @@ static const char *get_perf_dir(void)  	return ".";  } -#ifndef HAVE_STRLCPY -size_t strlcpy(char *dest, const char *src, size_t size) +/* + * If libc has strlcpy() then that version will override this + * implementation: + */ +size_t __weak strlcpy(char *dest, const char *src, size_t size)  {  	size_t ret = strlen(src);  	if (size) {  		size_t len = (ret >= size) ? size - 1 : ret; +  		memcpy(dest, src, len);  		dest[len] = '\0';  	} +  	return ret;  } -#endif  static char *get_pathname(void)  { diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c new file mode 100644 index 00000000000..43168fb0d9a --- /dev/null +++ b/tools/perf/util/perf_regs.c @@ -0,0 +1,27 @@ +#include <errno.h> +#include "perf_regs.h" +#include "event.h" + +int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) +{ +	int i, idx = 0; +	u64 mask = regs->mask; + +	if (regs->cache_mask & (1 << id)) +		goto out; + +	if (!(mask & (1 << id))) +		return -EINVAL; + +	for (i = 0; i < id; i++) { +		if (mask & (1 << i)) +			idx++; +	} + +	regs->cache_mask |= (1 << id); +	regs->cache_regs[id] = regs->regs[idx]; + +out: +	*valp = regs->cache_regs[id]; +	return 0; +} diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 5a4f2b6f373..980dbf76bc9 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -1,14 +1,29 @@  #ifndef __PERF_REGS_H  #define __PERF_REGS_H -#ifdef HAVE_PERF_REGS +#include <linux/types.h> + +struct regs_dump; + +#ifdef HAVE_PERF_REGS_SUPPORT  #include <perf_regs.h> + +int perf_reg_value(u64 *valp, struct regs_dump *regs, int id); +  #else  #define PERF_REGS_MASK	0 +#define PERF_REGS_MAX	0  static inline const char *perf_reg_name(int id __maybe_unused)  {  	return NULL;  } -#endif /* HAVE_PERF_REGS */ + +static inline int perf_reg_value(u64 *valp __maybe_unused, +				 struct regs_dump *regs __maybe_unused, +				 int id __maybe_unused) +{ +	return 0; +} +#endif /* HAVE_PERF_REGS_SUPPORT */  #endif /* __PERF_REGS_H */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index bc9d8069d37..7a811eb61f7 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1,19 +1,23 @@  #include <linux/list.h>  #include <sys/types.h> -#include <sys/stat.h>  #include <unistd.h>  #include <stdio.h>  #include <dirent.h> -#include "sysfs.h" +#include <api/fs/fs.h> +#include <locale.h>  #include "util.h"  #include "pmu.h"  #include "parse-events.h"  #include "cpumap.h" +#define UNIT_MAX_LEN	31 /* max length for event unit name */ +  struct perf_pmu_alias {  	char *name;  	struct list_head terms;  	struct list_head list; +	char unit[UNIT_MAX_LEN+1]; +	double scale;  };  struct perf_pmu_format { @@ -77,9 +81,8 @@ static int pmu_format(const char *name, struct list_head *format)  {  	struct stat st;  	char path[PATH_MAX]; -	const char *sysfs; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return -1; @@ -95,7 +98,80 @@ static int pmu_format(const char *name, struct list_head *format)  	return 0;  } -static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) +static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +{ +	struct stat st; +	ssize_t sret; +	char scale[128]; +	int fd, ret = -1; +	char path[PATH_MAX]; +	const char *lc; + +	snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); + +	fd = open(path, O_RDONLY); +	if (fd == -1) +		return -1; + +	if (fstat(fd, &st) < 0) +		goto error; + +	sret = read(fd, scale, sizeof(scale)-1); +	if (sret < 0) +		goto error; + +	scale[sret] = '\0'; +	/* +	 * save current locale +	 */ +	lc = setlocale(LC_NUMERIC, NULL); + +	/* +	 * force to C locale to ensure kernel +	 * scale string is converted correctly. +	 * kernel uses default C locale. +	 */ +	setlocale(LC_NUMERIC, "C"); + +	alias->scale = strtod(scale, NULL); + +	/* restore locale */ +	setlocale(LC_NUMERIC, lc); + +	ret = 0; +error: +	close(fd); +	return ret; +} + +static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name) +{ +	char path[PATH_MAX]; +	ssize_t sret; +	int fd; + +	snprintf(path, PATH_MAX, "%s/%s.unit", dir, name); + +	fd = open(path, O_RDONLY); +	if (fd == -1) +		return -1; + +		sret = read(fd, alias->unit, UNIT_MAX_LEN); +	if (sret < 0) +		goto error; + +	close(fd); + +	alias->unit[sret] = '\0'; + +	return 0; +error: +	close(fd); +	alias->unit[0] = '\0'; +	return -1; +} + +static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)  {  	struct perf_pmu_alias *alias;  	char buf[256]; @@ -111,6 +187,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)  		return -ENOMEM;  	INIT_LIST_HEAD(&alias->terms); +	alias->scale = 1.0; +	alias->unit[0] = '\0'; +  	ret = parse_events_terms(&alias->terms, buf);  	if (ret) {  		free(alias); @@ -118,7 +197,14 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)  	}  	alias->name = strdup(name); +	/* +	 * load unit name and scale if available +	 */ +	perf_pmu__parse_unit(alias, dir, name); +	perf_pmu__parse_scale(alias, dir, name); +  	list_add_tail(&alias->list, list); +  	return 0;  } @@ -130,6 +216,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)  {  	struct dirent *evt_ent;  	DIR *event_dir; +	size_t len;  	int ret = 0;  	event_dir = opendir(dir); @@ -144,13 +231,24 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)  		if (!strcmp(name, ".") || !strcmp(name, ".."))  			continue; +		/* +		 * skip .unit and .scale info files +		 * parsed in perf_pmu__new_alias() +		 */ +		len = strlen(name); +		if (len > 5 && !strcmp(name + len - 5, ".unit")) +			continue; +		if (len > 6 && !strcmp(name + len - 6, ".scale")) +			continue; +  		snprintf(path, PATH_MAX, "%s/%s", dir, name);  		ret = -EINVAL;  		file = fopen(path, "r");  		if (!file)  			break; -		ret = perf_pmu__new_alias(head, name, file); + +		ret = perf_pmu__new_alias(head, dir, name, file);  		fclose(file);  	} @@ -166,9 +264,8 @@ static int pmu_aliases(const char *name, struct list_head *head)  {  	struct stat st;  	char path[PATH_MAX]; -	const char *sysfs; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return -1; @@ -187,17 +284,17 @@ static int pmu_aliases(const char *name, struct list_head *head)  static int pmu_alias_terms(struct perf_pmu_alias *alias,  			   struct list_head *terms)  { -	struct parse_events_term *term, *clone; +	struct parse_events_term *term, *cloned;  	LIST_HEAD(list);  	int ret;  	list_for_each_entry(term, &alias->terms, list) { -		ret = parse_events_term__clone(&clone, term); +		ret = parse_events_term__clone(&cloned, term);  		if (ret) {  			parse_events__free_terms(&list);  			return ret;  		} -		list_add_tail(&clone->list, &list); +		list_add_tail(&cloned->list, &list);  	}  	list_splice(&list, terms);  	return 0; @@ -212,11 +309,10 @@ static int pmu_type(const char *name, __u32 *type)  {  	struct stat st;  	char path[PATH_MAX]; -	const char *sysfs;  	FILE *file;  	int ret = 0; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return -1; @@ -241,11 +337,10 @@ static int pmu_type(const char *name, __u32 *type)  static void pmu_read_sysfs(void)  {  	char path[PATH_MAX]; -	const char *sysfs;  	DIR *dir;  	struct dirent *dent; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return; @@ -270,11 +365,10 @@ static struct cpu_map *pmu_cpumask(const char *name)  {  	struct stat st;  	char path[PATH_MAX]; -	const char *sysfs;  	FILE *file;  	struct cpu_map *cpus; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return NULL; @@ -411,7 +505,7 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value)  /*   * Setup one of config[12] attr members based on the - * user input data - temr parameter. + * user input data - term parameter.   */  static int pmu_config_term(struct list_head *formats,  			   struct perf_event_attr *attr, @@ -513,16 +607,46 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,  	return NULL;  } + +static int check_unit_scale(struct perf_pmu_alias *alias, +			    const char **unit, double *scale) +{ +	/* +	 * Only one term in event definition can +	 * define unit and scale, fail if there's +	 * more than one. +	 */ +	if ((*unit && alias->unit) || +	    (*scale && alias->scale)) +		return -EINVAL; + +	if (alias->unit) +		*unit = alias->unit; + +	if (alias->scale) +		*scale = alias->scale; + +	return 0; +} +  /*   * Find alias in the terms list and replace it with the terms   * defined for the alias   */ -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, +			  const char **unit, double *scale)  {  	struct parse_events_term *term, *h;  	struct perf_pmu_alias *alias;  	int ret; +	/* +	 * Mark unit and scale as not set +	 * (different from default values, see below) +	 */ +	*unit   = NULL; +	*scale  = 0.0; +  	list_for_each_entry_safe(term, h, head_terms, list) {  		alias = pmu_find_alias(pmu, term);  		if (!alias) @@ -530,9 +654,26 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)  		ret = pmu_alias_terms(alias, &term->list);  		if (ret)  			return ret; + +		ret = check_unit_scale(alias, unit, scale); +		if (ret) +			return ret; +  		list_del(&term->list);  		free(term);  	} + +	/* +	 * if no unit or scale foundin aliases, then +	 * set defaults as for evsel +	 * unit cannot left to NULL +	 */ +	if (*unit == NULL) +		*unit   = ""; + +	if (*scale == 0.0) +		*scale  = 1.0; +  	return 0;  } @@ -630,10 +771,26 @@ void print_pmu_events(const char *event_glob, bool name_only)  			continue;  		}  		printf("  %-50s [Kernel PMU event]\n", aliases[j]); -		free(aliases[j]); +		zfree(&aliases[j]);  		printed++;  	}  	if (printed)  		printf("\n");  	free(aliases);  } + +bool pmu_have_event(const char *pname, const char *name) +{ +	struct perf_pmu *pmu; +	struct perf_pmu_alias *alias; + +	pmu = NULL; +	while ((pmu = perf_pmu__scan(pmu)) != NULL) { +		if (strcmp(pname, pmu->name)) +			continue; +		list_for_each_entry(alias, &pmu->aliases, list) +			if (!strcmp(alias->name, name)) +				return true; +	} +	return false; +} diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 6b2cbe2d4cc..c14a543ce1f 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -1,7 +1,7 @@  #ifndef __PMU_H  #define __PMU_H -#include <linux/bitops.h> +#include <linux/bitmap.h>  #include <linux/perf_event.h>  #include <stdbool.h> @@ -28,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,  int perf_pmu__config_terms(struct list_head *formats,  			   struct perf_event_attr *attr,  			   struct list_head *head_terms); -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, +			  const char **unit, double *scale);  struct list_head *perf_pmu__alias(struct perf_pmu *pmu,  				  struct list_head *head_terms);  int perf_pmu_wrap(void); @@ -42,6 +43,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head);  struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);  void print_pmu_events(const char *event_glob, bool name_only); +bool pmu_have_event(const char *pname, const char *name);  int perf_pmu__test(void);  #endif /* __PMU_H */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index aa04bf9c9ad..9a0a1839a37 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -40,14 +40,13 @@  #include "color.h"  #include "symbol.h"  #include "thread.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include "trace-event.h"	/* For __maybe_unused */  #include "probe-event.h"  #include "probe-finder.h"  #include "session.h"  #define MAX_CMDLEN 256 -#define MAX_PROBE_ARGS 128  #define PERFPROBE_GROUP "probe"  bool probe_event_dry_run;	/* Dry run flag */ @@ -71,33 +70,32 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)  }  static char *synthesize_perf_probe_point(struct perf_probe_point *pp); -static int convert_name_to_addr(struct perf_probe_event *pev, -				const char *exec); -static struct machine machine; +static void clear_probe_trace_event(struct probe_trace_event *tev); +static struct machine *host_machine;  /* Initialize symbol maps and path of vmlinux/modules */ -static int init_vmlinux(void) +static int init_symbol_maps(bool user_only)  {  	int ret;  	symbol_conf.sort_by_name = true; -	if (symbol_conf.vmlinux_name == NULL) -		symbol_conf.try_vmlinux_path = true; -	else -		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);  	ret = symbol__init();  	if (ret < 0) {  		pr_debug("Failed to init symbol map.\n");  		goto out;  	} -	ret = machine__init(&machine, "", HOST_KERNEL_ID); -	if (ret < 0) -		goto out; +	if (host_machine || user_only)	/* already initialized */ +		return 0; -	if (machine__create_kernel_maps(&machine) < 0) { -		pr_debug("machine__create_kernel_maps() failed.\n"); -		goto out; +	if (symbol_conf.vmlinux_name) +		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); + +	host_machine = machine__new_host(); +	if (!host_machine) { +		pr_debug("machine__new_host() failed.\n"); +		symbol__exit(); +		ret = -1;  	}  out:  	if (ret < 0) @@ -105,21 +103,66 @@ out:  	return ret;  } +static void exit_symbol_maps(void) +{ +	if (host_machine) { +		machine__delete(host_machine); +		host_machine = NULL; +	} +	symbol__exit(); +} +  static struct symbol *__find_kernel_function_by_name(const char *name,  						     struct map **mapp)  { -	return machine__find_kernel_function_by_name(&machine, name, mapp, +	return machine__find_kernel_function_by_name(host_machine, name, mapp,  						     NULL);  } +static struct symbol *__find_kernel_function(u64 addr, struct map **mapp) +{ +	return machine__find_kernel_function(host_machine, addr, mapp, NULL); +} + +static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) +{ +	/* kmap->ref_reloc_sym should be set if host_machine is initialized */ +	struct kmap *kmap; + +	if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0) +		return NULL; + +	kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]); +	return kmap->ref_reloc_sym; +} + +static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) +{ +	struct ref_reloc_sym *reloc_sym; +	struct symbol *sym; +	struct map *map; + +	/* ref_reloc_sym is just a label. Need a special fix*/ +	reloc_sym = kernel_get_ref_reloc_sym(); +	if (reloc_sym && strcmp(name, reloc_sym->name) == 0) +		return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; +	else { +		sym = __find_kernel_function_by_name(name, &map); +		if (sym) +			return map->unmap_ip(map, sym->start) - +				(reloc) ? 0 : map->reloc; +	} +	return 0; +} +  static struct map *kernel_get_module_map(const char *module)  {  	struct rb_node *nd; -	struct map_groups *grp = &machine.kmaps; +	struct map_groups *grp = &host_machine->kmaps;  	/* A file path -- this is an offline module */  	if (module && strchr(module, '/')) -		return machine__new_module(&machine, 0, module); +		return machine__new_module(host_machine, 0, module);  	if (!module)  		module = "kernel"; @@ -141,7 +184,7 @@ static struct dso *kernel_get_module_dso(const char *module)  	const char *vmlinux_name;  	if (module) { -		list_for_each_entry(dso, &machine.kernel_dsos, node) { +		list_for_each_entry(dso, &host_machine->kernel_dsos, node) {  			if (strncmp(dso->short_name + 1, module,  				    dso->short_name_len - 2) == 0)  				goto found; @@ -150,12 +193,12 @@ static struct dso *kernel_get_module_dso(const char *module)  		return NULL;  	} -	map = machine.vmlinux_maps[MAP__FUNCTION]; +	map = host_machine->vmlinux_maps[MAP__FUNCTION];  	dso = map->dso;  	vmlinux_name = symbol_conf.vmlinux_name;  	if (vmlinux_name) { -		if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0) +		if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0)  			return NULL;  	} else {  		if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { @@ -173,46 +216,54 @@ const char *kernel_get_module_path(const char *module)  	return (dso) ? dso->long_name : NULL;  } -static int init_user_exec(void) +static int convert_exec_to_group(const char *exec, char **result)  { -	int ret = 0; +	char *ptr1, *ptr2, *exec_copy; +	char buf[64]; +	int ret; -	symbol_conf.try_vmlinux_path = false; -	symbol_conf.sort_by_name = true; -	ret = symbol__init(); +	exec_copy = strdup(exec); +	if (!exec_copy) +		return -ENOMEM; +	ptr1 = basename(exec_copy); +	if (!ptr1) { +		ret = -EINVAL; +		goto out; +	} + +	ptr2 = strpbrk(ptr1, "-._"); +	if (ptr2) +		*ptr2 = '\0'; +	ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1);  	if (ret < 0) -		pr_debug("Failed to init symbol map.\n"); +		goto out; +	*result = strdup(buf); +	ret = *result ? 0 : -ENOMEM; + +out: +	free(exec_copy);  	return ret;  } -static int convert_to_perf_probe_point(struct probe_trace_point *tp, -					struct perf_probe_point *pp) +static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)  { -	pp->function = strdup(tp->symbol); - -	if (pp->function == NULL) -		return -ENOMEM; - -	pp->offset = tp->offset; -	pp->retprobe = tp->retprobe; +	int i; -	return 0; +	for (i = 0; i < ntevs; i++) +		clear_probe_trace_event(tevs + i);  } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT +  /* Open new debuginfo of given module */  static struct debuginfo *open_debuginfo(const char *module)  { -	const char *path; +	const char *path = module; -	/* A file path -- this is an offline module */ -	if (module && strchr(module, '/')) -		path = module; -	else { +	if (!module || !strchr(module, '/')) {  		path = kernel_get_module_path(module); -  		if (!path) {  			pr_err("Failed to find path of %s module.\n",  			       module ?: "kernel"); @@ -222,44 +273,110 @@ static struct debuginfo *open_debuginfo(const char *module)  	return debuginfo__new(path);  } +static int get_text_start_address(const char *exec, unsigned long *address) +{ +	Elf *elf; +	GElf_Ehdr ehdr; +	GElf_Shdr shdr; +	int fd, ret = -ENOENT; + +	fd = open(exec, O_RDONLY); +	if (fd < 0) +		return -errno; + +	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); +	if (elf == NULL) +		return -EINVAL; + +	if (gelf_getehdr(elf, &ehdr) == NULL) +		goto out; + +	if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL)) +		goto out; + +	*address = shdr.sh_addr - shdr.sh_offset; +	ret = 0; +out: +	elf_end(elf); +	return ret; +} +  /*   * Convert trace point to probe point with debuginfo - * Currently only handles kprobes.   */ -static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, -					struct perf_probe_point *pp) +static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, +					    struct perf_probe_point *pp, +					    bool is_kprobe)  { -	struct symbol *sym; -	struct map *map; -	u64 addr; +	struct debuginfo *dinfo = NULL; +	unsigned long stext = 0; +	u64 addr = tp->address;  	int ret = -ENOENT; -	struct debuginfo *dinfo; -	sym = __find_kernel_function_by_name(tp->symbol, &map); -	if (sym) { -		addr = map->unmap_ip(map, sym->start + tp->offset); -		pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, -			 tp->offset, addr); +	/* convert the address to dwarf address */ +	if (!is_kprobe) { +		if (!addr) { +			ret = -EINVAL; +			goto error; +		} +		ret = get_text_start_address(tp->module, &stext); +		if (ret < 0) +			goto error; +		addr += stext; +	} else { +		addr = kernel_get_symbol_address_by_name(tp->symbol, false); +		if (addr == 0) +			goto error; +		addr += tp->offset; +	} + +	pr_debug("try to find information at %" PRIx64 " in %s\n", addr, +		 tp->module ? : "kernel"); -		dinfo = debuginfo__new_online_kernel(addr); -		if (dinfo) { -			ret = debuginfo__find_probe_point(dinfo, +	dinfo = open_debuginfo(tp->module); +	if (dinfo) { +		ret = debuginfo__find_probe_point(dinfo,  						 (unsigned long)addr, pp); -			debuginfo__delete(dinfo); -		} else { -			pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", -				 addr); -			ret = -ENOENT; -		} +		debuginfo__delete(dinfo); +	} else { +		pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr); +		ret = -ENOENT;  	} -	if (ret <= 0) { -		pr_debug("Failed to find corresponding probes from " -			 "debuginfo. Use kprobe event information.\n"); -		return convert_to_perf_probe_point(tp, pp); + +	if (ret > 0) { +		pp->retprobe = tp->retprobe; +		return 0;  	} -	pp->retprobe = tp->retprobe; +error: +	pr_debug("Failed to find corresponding probes from debuginfo.\n"); +	return ret ? : -ENOENT; +} -	return 0; +static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, +					  int ntevs, const char *exec) +{ +	int i, ret = 0; +	unsigned long stext = 0; + +	if (!exec) +		return 0; + +	ret = get_text_start_address(exec, &stext); +	if (ret < 0) +		return ret; + +	for (i = 0; i < ntevs && ret >= 0; i++) { +		/* point.address is the addres of point.symbol + point.offset */ +		tevs[i].point.address -= stext; +		tevs[i].point.module = strdup(exec); +		if (!tevs[i].point.module) { +			ret = -ENOMEM; +			break; +		} +		tevs[i].uprobes = true; +	} + +	return ret;  }  static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, @@ -291,12 +408,46 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,  		}  	} -	if (tmp) -		free(tmp); - +	free(tmp);  	return ret;  } +/* Post processing the probe events */ +static int post_process_probe_trace_events(struct probe_trace_event *tevs, +					   int ntevs, const char *module, +					   bool uprobe) +{ +	struct ref_reloc_sym *reloc_sym; +	char *tmp; +	int i; + +	if (uprobe) +		return add_exec_to_probe_trace_events(tevs, ntevs, module); + +	/* Note that currently ref_reloc_sym based probe is not for drivers */ +	if (module) +		return add_module_to_probe_trace_events(tevs, ntevs, module); + +	reloc_sym = kernel_get_ref_reloc_sym(); +	if (!reloc_sym) { +		pr_warning("Relocated base symbol is not found!\n"); +		return -EINVAL; +	} + +	for (i = 0; i < ntevs; i++) { +		if (tevs[i].point.address) { +			tmp = strdup(reloc_sym->name); +			if (!tmp) +				return -ENOMEM; +			free(tevs[i].point.symbol); +			tevs[i].point.symbol = tmp; +			tevs[i].point.offset = tevs[i].point.address - +					       reloc_sym->unrelocated_addr; +		} +	} +	return 0; +} +  /* Try to find perf_probe_event with debuginfo */  static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  					  struct probe_trace_event **tevs, @@ -306,15 +457,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  	struct debuginfo *dinfo;  	int ntevs, ret = 0; -	if (pev->uprobes) { -		if (need_dwarf) { -			pr_warning("Debuginfo-analysis is not yet supported" -					" with -x/--exec option.\n"); -			return -ENOSYS; -		} -		return convert_name_to_addr(pev, target); -	} -  	dinfo = open_debuginfo(target);  	if (!dinfo) { @@ -326,16 +468,20 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  		return 0;  	} +	pr_debug("Try to find probe point from debuginfo.\n");  	/* Searching trace events corresponding to a probe event */  	ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);  	debuginfo__delete(dinfo);  	if (ntevs > 0) {	/* Succeeded to find trace events */ -		pr_debug("find %d probe_trace_events.\n", ntevs); -		if (target) -			ret = add_module_to_probe_trace_events(*tevs, ntevs, -							       target); +		pr_debug("Found %d probe_trace_events.\n", ntevs); +		ret = post_process_probe_trace_events(*tevs, ntevs, +							target, pev->uprobes); +		if (ret < 0) { +			clear_probe_trace_events(*tevs, ntevs); +			zfree(tevs); +		}  		return ret < 0 ? ret : ntevs;  	} @@ -402,15 +548,13 @@ static int get_real_path(const char *raw_path, const char *comp_dir,  		case EFAULT:  			raw_path = strchr(++raw_path, '/');  			if (!raw_path) { -				free(*new_path); -				*new_path = NULL; +				zfree(new_path);  				return -ENOENT;  			}  			continue;  		default: -			free(*new_path); -			*new_path = NULL; +			zfree(new_path);  			return -errno;  		}  	} @@ -466,20 +610,16 @@ static int _show_one_line(FILE *fp, int l, bool skip, bool show_num)   * Show line-range always requires debuginfo to find source file and   * line number.   */ -int show_line_range(struct line_range *lr, const char *module) +static int __show_line_range(struct line_range *lr, const char *module)  {  	int l = 1; -	struct line_node *ln; +	struct int_node *ln;  	struct debuginfo *dinfo;  	FILE *fp;  	int ret;  	char *tmp;  	/* Search a line range */ -	ret = init_vmlinux(); -	if (ret < 0) -		return ret; -  	dinfo = open_debuginfo(module);  	if (!dinfo) {  		pr_warning("Failed to open debuginfo file.\n"); @@ -488,11 +628,11 @@ int show_line_range(struct line_range *lr, const char *module)  	ret = debuginfo__find_line_range(dinfo, lr);  	debuginfo__delete(dinfo); -	if (ret == 0) { +	if (ret == 0 || ret == -ENOENT) {  		pr_warning("Specified source line is not found.\n");  		return -ENOENT;  	} else if (ret < 0) { -		pr_warning("Debuginfo analysis failed. (%d)\n", ret); +		pr_warning("Debuginfo analysis failed.\n");  		return ret;  	} @@ -501,7 +641,7 @@ int show_line_range(struct line_range *lr, const char *module)  	ret = get_real_path(tmp, lr->comp_dir, &lr->path);  	free(tmp);	/* Free old path */  	if (ret < 0) { -		pr_warning("Failed to find source file. (%d)\n", ret); +		pr_warning("Failed to find source file path.\n");  		return ret;  	} @@ -526,8 +666,8 @@ int show_line_range(struct line_range *lr, const char *module)  			goto end;  	} -	list_for_each_entry(ln, &lr->line_list, list) { -		for (; ln->line > l; l++) { +	intlist__for_each(ln, lr->line_list) { +		for (; ln->i > l; l++) {  			ret = show_one_line(fp, l - lr->offset);  			if (ret < 0)  				goto end; @@ -549,6 +689,19 @@ end:  	return ret;  } +int show_line_range(struct line_range *lr, const char *module) +{ +	int ret; + +	ret = init_symbol_maps(false); +	if (ret < 0) +		return ret; +	ret = __show_line_range(lr, module); +	exit_symbol_maps(); + +	return ret; +} +  static int show_available_vars_at(struct debuginfo *dinfo,  				  struct perf_probe_event *pev,  				  int max_vls, struct strfilter *_filter, @@ -568,9 +721,14 @@ static int show_available_vars_at(struct debuginfo *dinfo,  	ret = debuginfo__find_available_vars_at(dinfo, pev, &vls,  						max_vls, externs);  	if (ret <= 0) { -		pr_err("Failed to find variables at %s (%d)\n", buf, ret); +		if (ret == 0 || ret == -ENOENT) { +			pr_err("Failed to find the address of %s\n", buf); +			ret = -ENOENT; +		} else +			pr_warning("Debuginfo analysis failed.\n");  		goto end;  	} +  	/* Some variables are found */  	fprintf(stdout, "Available variables at %s\n", buf);  	for (i = 0; i < ret; i++) { @@ -581,7 +739,7 @@ static int show_available_vars_at(struct debuginfo *dinfo,  		 */  		fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,  			vl->point.offset); -		free(vl->point.symbol); +		zfree(&vl->point.symbol);  		nvars = 0;  		if (vl->vars) {  			strlist__for_each(node, vl->vars) { @@ -610,14 +768,15 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,  	int i, ret = 0;  	struct debuginfo *dinfo; -	ret = init_vmlinux(); +	ret = init_symbol_maps(false);  	if (ret < 0)  		return ret;  	dinfo = open_debuginfo(module);  	if (!dinfo) {  		pr_warning("Failed to open debuginfo file.\n"); -		return -ENOENT; +		ret = -ENOENT; +		goto out;  	}  	setup_pager(); @@ -627,37 +786,31 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,  					     externs);  	debuginfo__delete(dinfo); +out: +	exit_symbol_maps();  	return ret;  } -#else	/* !DWARF_SUPPORT */ +#else	/* !HAVE_DWARF_SUPPORT */ -static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, -					struct perf_probe_point *pp) +static int +find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, +				 struct perf_probe_point *pp __maybe_unused, +				 bool is_kprobe __maybe_unused)  { -	struct symbol *sym; - -	sym = __find_kernel_function_by_name(tp->symbol, NULL); -	if (!sym) { -		pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); -		return -ENOENT; -	} - -	return convert_to_perf_probe_point(tp, pp); +	return -ENOSYS;  }  static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  				struct probe_trace_event **tevs __maybe_unused, -				int max_tevs __maybe_unused, const char *target) +				int max_tevs __maybe_unused, +				const char *target __maybe_unused)  {  	if (perf_probe_event_need_dwarf(pev)) {  		pr_warning("Debuginfo-analysis is not supported.\n");  		return -ENOSYS;  	} -	if (pev->uprobes) -		return convert_name_to_addr(pev, target); -  	return 0;  } @@ -679,6 +832,26 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused,  }  #endif +void line_range__clear(struct line_range *lr) +{ +	free(lr->function); +	free(lr->file); +	free(lr->path); +	free(lr->comp_dir); +	intlist__delete(lr->line_list); +	memset(lr, 0, sizeof(*lr)); +} + +int line_range__init(struct line_range *lr) +{ +	memset(lr, 0, sizeof(*lr)); +	lr->line_list = intlist__new(NULL); +	if (!lr->line_list) +		return -ENOMEM; +	else +		return 0; +} +  static int parse_line_num(char **ptr, int *val, const char *what)  {  	const char *start = *ptr; @@ -1150,16 +1323,21 @@ static int parse_probe_trace_command(const char *cmd,  	} else  		p = argv[1];  	fmt1_str = strtok_r(p, "+", &fmt); -	tp->symbol = strdup(fmt1_str); -	if (tp->symbol == NULL) { -		ret = -ENOMEM; -		goto out; +	if (fmt1_str[0] == '0')	/* only the address started with 0x */ +		tp->address = strtoul(fmt1_str, NULL, 0); +	else { +		/* Only the symbol-based probe has offset */ +		tp->symbol = strdup(fmt1_str); +		if (tp->symbol == NULL) { +			ret = -ENOMEM; +			goto out; +		} +		fmt2_str = strtok_r(NULL, "", &fmt); +		if (fmt2_str == NULL) +			tp->offset = 0; +		else +			tp->offset = strtoul(fmt2_str, NULL, 10);  	} -	fmt2_str = strtok_r(NULL, "", &fmt); -	if (fmt2_str == NULL) -		tp->offset = 0; -	else -		tp->offset = strtoul(fmt2_str, NULL, 10);  	tev->nargs = argc - 2;  	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); @@ -1279,8 +1457,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)  error:  	pr_debug("Failed to synthesize perf probe point: %s\n",  		 strerror(-ret)); -	if (buf) -		free(buf); +	free(buf);  	return NULL;  } @@ -1402,20 +1579,27 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)  	if (buf == NULL)  		return NULL; +	len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p', +			 tev->group, tev->event); +	if (len <= 0) +		goto error; + +	/* Uprobes must have tp->address and tp->module */ +	if (tev->uprobes && (!tp->address || !tp->module)) +		goto error; + +	/* Use the tp->address for uprobes */  	if (tev->uprobes) -		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s", -				 tp->retprobe ? 'r' : 'p', -				 tev->group, tev->event, -				 tp->module, tp->symbol); +		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", +				 tp->module, tp->address);  	else -		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", -				 tp->retprobe ? 'r' : 'p', -				 tev->group, tev->event, +		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu",  				 tp->module ?: "", tp->module ? ":" : "",  				 tp->symbol, tp->offset); -	if (len <= 0) +	if (ret <= 0)  		goto error; +	len += ret;  	for (i = 0; i < tev->nargs; i++) {  		ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, @@ -1431,6 +1615,79 @@ error:  	return NULL;  } +static int find_perf_probe_point_from_map(struct probe_trace_point *tp, +					  struct perf_probe_point *pp, +					  bool is_kprobe) +{ +	struct symbol *sym = NULL; +	struct map *map; +	u64 addr; +	int ret = -ENOENT; + +	if (!is_kprobe) { +		map = dso__new_map(tp->module); +		if (!map) +			goto out; +		addr = tp->address; +		sym = map__find_symbol(map, addr, NULL); +	} else { +		addr = kernel_get_symbol_address_by_name(tp->symbol, true); +		if (addr) { +			addr += tp->offset; +			sym = __find_kernel_function(addr, &map); +		} +	} +	if (!sym) +		goto out; + +	pp->retprobe = tp->retprobe; +	pp->offset = addr - map->unmap_ip(map, sym->start); +	pp->function = strdup(sym->name); +	ret = pp->function ? 0 : -ENOMEM; + +out: +	if (map && !is_kprobe) { +		dso__delete(map->dso); +		map__delete(map); +	} + +	return ret; +} + +static int convert_to_perf_probe_point(struct probe_trace_point *tp, +					struct perf_probe_point *pp, +					bool is_kprobe) +{ +	char buf[128]; +	int ret; + +	ret = find_perf_probe_point_from_dwarf(tp, pp, is_kprobe); +	if (!ret) +		return 0; +	ret = find_perf_probe_point_from_map(tp, pp, is_kprobe); +	if (!ret) +		return 0; + +	pr_debug("Failed to find probe point from both of dwarf and map.\n"); + +	if (tp->symbol) { +		pp->function = strdup(tp->symbol); +		pp->offset = tp->offset; +	} else if (!tp->module && !is_kprobe) { +		ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address); +		if (ret < 0) +			return ret; +		pp->function = strdup(buf); +		pp->offset = 0; +	} +	if (pp->function == NULL) +		return -ENOMEM; + +	pp->retprobe = tp->retprobe; + +	return 0; +} +  static int convert_to_perf_probe_event(struct probe_trace_event *tev,  			       struct perf_probe_event *pev, bool is_kprobe)  { @@ -1444,11 +1701,7 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev,  		return -ENOMEM;  	/* Convert trace_point to probe_point */ -	if (is_kprobe) -		ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); -	else -		ret = convert_to_perf_probe_point(&tev->point, &pev->point); - +	ret = convert_to_perf_probe_point(&tev->point, &pev->point, is_kprobe);  	if (ret < 0)  		return ret; @@ -1481,34 +1734,25 @@ void clear_perf_probe_event(struct perf_probe_event *pev)  	struct perf_probe_arg_field *field, *next;  	int i; -	if (pev->event) -		free(pev->event); -	if (pev->group) -		free(pev->group); -	if (pp->file) -		free(pp->file); -	if (pp->function) -		free(pp->function); -	if (pp->lazy_line) -		free(pp->lazy_line); +	free(pev->event); +	free(pev->group); +	free(pp->file); +	free(pp->function); +	free(pp->lazy_line); +  	for (i = 0; i < pev->nargs; i++) { -		if (pev->args[i].name) -			free(pev->args[i].name); -		if (pev->args[i].var) -			free(pev->args[i].var); -		if (pev->args[i].type) -			free(pev->args[i].type); +		free(pev->args[i].name); +		free(pev->args[i].var); +		free(pev->args[i].type);  		field = pev->args[i].field;  		while (field) {  			next = field->next; -			if (field->name) -				free(field->name); +			zfree(&field->name);  			free(field);  			field = next;  		}  	} -	if (pev->args) -		free(pev->args); +	free(pev->args);  	memset(pev, 0, sizeof(*pev));  } @@ -1517,21 +1761,14 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)  	struct probe_trace_arg_ref *ref, *next;  	int i; -	if (tev->event) -		free(tev->event); -	if (tev->group) -		free(tev->group); -	if (tev->point.symbol) -		free(tev->point.symbol); -	if (tev->point.module) -		free(tev->point.module); +	free(tev->event); +	free(tev->group); +	free(tev->point.symbol); +	free(tev->point.module);  	for (i = 0; i < tev->nargs; i++) { -		if (tev->args[i].name) -			free(tev->args[i].name); -		if (tev->args[i].value) -			free(tev->args[i].value); -		if (tev->args[i].type) -			free(tev->args[i].type); +		free(tev->args[i].name); +		free(tev->args[i].value); +		free(tev->args[i].type);  		ref = tev->args[i].ref;  		while (ref) {  			next = ref->next; @@ -1539,8 +1776,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)  			ref = next;  		}  	} -	if (tev->args) -		free(tev->args); +	free(tev->args);  	memset(tev, 0, sizeof(*tev));  } @@ -1632,7 +1868,8 @@ static struct strlist *get_probe_trace_command_rawlist(int fd)  }  /* Show an event */ -static int show_perf_probe_event(struct perf_probe_event *pev) +static int show_perf_probe_event(struct perf_probe_event *pev, +				 const char *module)  {  	int i, ret;  	char buf[128]; @@ -1648,6 +1885,8 @@ static int show_perf_probe_event(struct perf_probe_event *pev)  		return ret;  	printf("  %-20s (on %s", buf, place); +	if (module) +		printf(" in %s", module);  	if (pev->nargs > 0) {  		printf(" with"); @@ -1685,7 +1924,8 @@ static int __show_perf_probe_events(int fd, bool is_kprobe)  			ret = convert_to_perf_probe_event(&tev, &pev,  								is_kprobe);  			if (ret >= 0) -				ret = show_perf_probe_event(&pev); +				ret = show_perf_probe_event(&pev, +							    tev.point.module);  		}  		clear_perf_probe_event(&pev);  		clear_probe_trace_event(&tev); @@ -1708,7 +1948,7 @@ int show_perf_probe_events(void)  	if (fd < 0)  		return fd; -	ret = init_vmlinux(); +	ret = init_symbol_maps(false);  	if (ret < 0)  		return ret; @@ -1721,6 +1961,7 @@ int show_perf_probe_events(void)  		close(fd);  	} +	exit_symbol_maps();  	return ret;  } @@ -1883,7 +2124,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  		group = pev->group;  		pev->event = tev->event;  		pev->group = tev->group; -		show_perf_probe_event(pev); +		show_perf_probe_event(pev, tev->point.module);  		/* Trick here - restore current event/group */  		pev->event = (char *)event;  		pev->group = (char *)group; @@ -1909,98 +2150,175 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  	return ret;  } -static int convert_to_probe_trace_events(struct perf_probe_event *pev, -					  struct probe_trace_event **tevs, -					  int max_tevs, const char *target) +static char *looking_function_name; +static int num_matched_functions; + +static int probe_function_filter(struct map *map __maybe_unused, +				      struct symbol *sym)  { +	if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) && +	    strcmp(looking_function_name, sym->name) == 0) { +		num_matched_functions++; +		return 0; +	} +	return 1; +} + +#define strdup_or_goto(str, label)	\ +	({ char *__p = strdup(str); if (!__p) goto label; __p; }) + +/* + * Find probe function addresses from map. + * Return an error or the number of found probe_trace_event + */ +static int find_probe_trace_events_from_map(struct perf_probe_event *pev, +					    struct probe_trace_event **tevs, +					    int max_tevs, const char *target) +{ +	struct map *map = NULL; +	struct kmap *kmap = NULL; +	struct ref_reloc_sym *reloc_sym = NULL;  	struct symbol *sym; -	int ret = 0, i; +	struct rb_node *nd;  	struct probe_trace_event *tev; +	struct perf_probe_point *pp = &pev->point; +	struct probe_trace_point *tp; +	int ret, i; -	/* Convert perf_probe_event with debuginfo */ -	ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); -	if (ret != 0) -		return ret;	/* Found in debuginfo or got an error */ - -	/* Allocate trace event buffer */ -	tev = *tevs = zalloc(sizeof(struct probe_trace_event)); -	if (tev == NULL) -		return -ENOMEM; +	/* Init maps of given executable or kernel */ +	if (pev->uprobes) +		map = dso__new_map(target); +	else +		map = kernel_get_module_map(target); +	if (!map) { +		ret = -EINVAL; +		goto out; +	} -	/* Copy parameters */ -	tev->point.symbol = strdup(pev->point.function); -	if (tev->point.symbol == NULL) { -		ret = -ENOMEM; -		goto error; +	/* +	 * Load matched symbols: Since the different local symbols may have +	 * same name but different addresses, this lists all the symbols. +	 */ +	num_matched_functions = 0; +	looking_function_name = pp->function; +	ret = map__load(map, probe_function_filter); +	if (ret || num_matched_functions == 0) { +		pr_err("Failed to find symbol %s in %s\n", pp->function, +			target ? : "kernel"); +		ret = -ENOENT; +		goto out; +	} else if (num_matched_functions > max_tevs) { +		pr_err("Too many functions matched in %s\n", +			target ? : "kernel"); +		ret = -E2BIG; +		goto out;  	} -	if (target) { -		tev->point.module = strdup(target); -		if (tev->point.module == NULL) { -			ret = -ENOMEM; -			goto error; +	if (!pev->uprobes) { +		kmap = map__kmap(map); +		reloc_sym = kmap->ref_reloc_sym; +		if (!reloc_sym) { +			pr_warning("Relocated base symbol is not found!\n"); +			ret = -EINVAL; +			goto out;  		}  	} -	tev->point.offset = pev->point.offset; -	tev->point.retprobe = pev->point.retprobe; -	tev->nargs = pev->nargs; -	tev->uprobes = pev->uprobes; +	/* Setup result trace-probe-events */ +	*tevs = zalloc(sizeof(*tev) * num_matched_functions); +	if (!*tevs) { +		ret = -ENOMEM; +		goto out; +	} -	if (tev->nargs) { -		tev->args = zalloc(sizeof(struct probe_trace_arg) -				   * tev->nargs); -		if (tev->args == NULL) { -			ret = -ENOMEM; -			goto error; +	ret = 0; +	map__for_each_symbol(map, sym, nd) { +		tev = (*tevs) + ret; +		tp = &tev->point; +		if (ret == num_matched_functions) { +			pr_warning("Too many symbols are listed. Skip it.\n"); +			break; +		} +		ret++; + +		if (pp->offset > sym->end - sym->start) { +			pr_warning("Offset %ld is bigger than the size of %s\n", +				   pp->offset, sym->name); +			ret = -ENOENT; +			goto err_out; +		} +		/* Add one probe point */ +		tp->address = map->unmap_ip(map, sym->start) + pp->offset; +		if (reloc_sym) { +			tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out); +			tp->offset = tp->address - reloc_sym->addr; +		} else { +			tp->symbol = strdup_or_goto(sym->name, nomem_out); +			tp->offset = pp->offset; +		} +		tp->retprobe = pp->retprobe; +		if (target) +			tev->point.module = strdup_or_goto(target, nomem_out); +		tev->uprobes = pev->uprobes; +		tev->nargs = pev->nargs; +		if (tev->nargs) { +			tev->args = zalloc(sizeof(struct probe_trace_arg) * +					   tev->nargs); +			if (tev->args == NULL) +				goto nomem_out;  		}  		for (i = 0; i < tev->nargs; i++) { -			if (pev->args[i].name) { -				tev->args[i].name = strdup(pev->args[i].name); -				if (tev->args[i].name == NULL) { -					ret = -ENOMEM; -					goto error; -				} -			} -			tev->args[i].value = strdup(pev->args[i].var); -			if (tev->args[i].value == NULL) { -				ret = -ENOMEM; -				goto error; -			} -			if (pev->args[i].type) { -				tev->args[i].type = strdup(pev->args[i].type); -				if (tev->args[i].type == NULL) { -					ret = -ENOMEM; -					goto error; -				} -			} +			if (pev->args[i].name) +				tev->args[i].name = +					strdup_or_goto(pev->args[i].name, +							nomem_out); + +			tev->args[i].value = strdup_or_goto(pev->args[i].var, +							    nomem_out); +			if (pev->args[i].type) +				tev->args[i].type = +					strdup_or_goto(pev->args[i].type, +							nomem_out);  		}  	} -	if (pev->uprobes) -		return 1; +out: +	if (map && pev->uprobes) { +		/* Only when using uprobe(exec) map needs to be released */ +		dso__delete(map->dso); +		map__delete(map); +	} +	return ret; -	/* Currently just checking function name from symbol map */ -	sym = __find_kernel_function_by_name(tev->point.symbol, NULL); -	if (!sym) { -		pr_warning("Kernel symbol \'%s\' not found.\n", -			   tev->point.symbol); -		ret = -ENOENT; -		goto error; -	} else if (tev->point.offset > sym->end - sym->start) { -		pr_warning("Offset specified is greater than size of %s\n", -			   tev->point.symbol); -		ret = -ENOENT; -		goto error; +nomem_out: +	ret = -ENOMEM; +err_out: +	clear_probe_trace_events(*tevs, num_matched_functions); +	zfree(tevs); +	goto out; +} +static int convert_to_probe_trace_events(struct perf_probe_event *pev, +					  struct probe_trace_event **tevs, +					  int max_tevs, const char *target) +{ +	int ret; + +	if (pev->uprobes && !pev->group) { +		/* Replace group name if not given */ +		ret = convert_exec_to_group(target, &pev->group); +		if (ret != 0) { +			pr_warning("Failed to make a group name.\n"); +			return ret; +		}  	} -	return 1; -error: -	clear_probe_trace_event(tev); -	free(tev); -	*tevs = NULL; -	return ret; +	/* Convert perf_probe_event with debuginfo */ +	ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); +	if (ret != 0) +		return ret;	/* Found in debuginfo or got an error */ + +	return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);  }  struct __event_package { @@ -2021,12 +2339,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,  	if (pkgs == NULL)  		return -ENOMEM; -	if (!pevs->uprobes) -		/* Init vmlinux path */ -		ret = init_vmlinux(); -	else -		ret = init_user_exec(); - +	ret = init_symbol_maps(pevs->uprobes);  	if (ret < 0) {  		free(pkgs);  		return ret; @@ -2057,9 +2370,10 @@ end:  	for (i = 0; i < npevs; i++) {  		for (j = 0; j < pkgs[i].ntevs; j++)  			clear_probe_trace_event(&pkgs[i].tevs[j]); -		free(pkgs[i].tevs); +		zfree(&pkgs[i].tevs);  	}  	free(pkgs); +	exit_symbol_maps();  	return ret;  } @@ -2209,166 +2523,51 @@ static struct strfilter *available_func_filter;  static int filter_available_functions(struct map *map __maybe_unused,  				      struct symbol *sym)  { -	if (sym->binding == STB_GLOBAL && +	if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&  	    strfilter__compare(available_func_filter, sym->name))  		return 0;  	return 1;  } -static int __show_available_funcs(struct map *map) -{ -	if (map__load(map, filter_available_functions)) { -		pr_err("Failed to load map.\n"); -		return -EINVAL; -	} -	if (!dso__sorted_by_name(map->dso, map->type)) -		dso__sort_by_name(map->dso, map->type); - -	dso__fprintf_symbols_by_name(map->dso, map->type, stdout); -	return 0; -} - -static int available_kernel_funcs(const char *module) +int show_available_funcs(const char *target, struct strfilter *_filter, +					bool user)  {  	struct map *map;  	int ret; -	ret = init_vmlinux(); +	ret = init_symbol_maps(user);  	if (ret < 0)  		return ret; -	map = kernel_get_module_map(module); +	/* Get a symbol map */ +	if (user) +		map = dso__new_map(target); +	else +		map = kernel_get_module_map(target);  	if (!map) { -		pr_err("Failed to find %s map.\n", (module) ? : "kernel"); +		pr_err("Failed to get a map for %s\n", (target) ? : "kernel");  		return -EINVAL;  	} -	return __show_available_funcs(map); -} -static int available_user_funcs(const char *target) -{ -	struct map *map; -	int ret; - -	ret = init_user_exec(); -	if (ret < 0) -		return ret; - -	map = dso__new_map(target); -	ret = __show_available_funcs(map); -	dso__delete(map->dso); -	map__delete(map); -	return ret; -} - -int show_available_funcs(const char *target, struct strfilter *_filter, -					bool user) -{ -	setup_pager(); +	/* Load symbols with given filter */  	available_func_filter = _filter; - -	if (!user) -		return available_kernel_funcs(target); - -	return available_user_funcs(target); -} - -/* - * uprobe_events only accepts address: - * Convert function and any offset to address - */ -static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) -{ -	struct perf_probe_point *pp = &pev->point; -	struct symbol *sym; -	struct map *map = NULL; -	char *function = NULL, *name = NULL; -	int ret = -EINVAL; -	unsigned long long vaddr = 0; - -	if (!pp->function) { -		pr_warning("No function specified for uprobes"); -		goto out; -	} - -	function = strdup(pp->function); -	if (!function) { -		pr_warning("Failed to allocate memory by strdup.\n"); -		ret = -ENOMEM; -		goto out; -	} - -	name = realpath(exec, NULL); -	if (!name) { -		pr_warning("Cannot find realpath for %s.\n", exec); -		goto out; -	} -	map = dso__new_map(name); -	if (!map) { -		pr_warning("Cannot find appropriate DSO for %s.\n", exec); -		goto out; -	} -	available_func_filter = strfilter__new(function, NULL);  	if (map__load(map, filter_available_functions)) { -		pr_err("Failed to load map.\n"); -		goto out; -	} - -	sym = map__find_symbol_by_name(map, function, NULL); -	if (!sym) { -		pr_warning("Cannot find %s in DSO %s\n", function, exec); -		goto out; -	} - -	if (map->start > sym->start) -		vaddr = map->start; -	vaddr += sym->start + pp->offset + map->pgoff; -	pp->offset = 0; - -	if (!pev->event) { -		pev->event = function; -		function = NULL; -	} -	if (!pev->group) { -		char *ptr1, *ptr2, *exec_copy; - -		pev->group = zalloc(sizeof(char *) * 64); -		exec_copy = strdup(exec); -		if (!exec_copy) { -			ret = -ENOMEM; -			pr_warning("Failed to copy exec string.\n"); -			goto out; -		} - -		ptr1 = strdup(basename(exec_copy)); -		if (ptr1) { -			ptr2 = strpbrk(ptr1, "-._"); -			if (ptr2) -				*ptr2 = '\0'; -			e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP, -					ptr1); -			free(ptr1); -		} -		free(exec_copy); -	} -	free(pp->function); -	pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS); -	if (!pp->function) { -		ret = -ENOMEM; -		pr_warning("Failed to allocate memory by zalloc.\n"); -		goto out; +		pr_err("Failed to load symbols in %s\n", (target) ? : "kernel"); +		goto end;  	} -	e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr); -	ret = 0; +	if (!dso__sorted_by_name(map->dso, map->type)) +		dso__sort_by_name(map->dso, map->type); -out: -	if (map) { +	/* Show all (filtered) symbols */ +	setup_pager(); +	dso__fprintf_symbols_by_name(map->dso, map->type, stdout); +end: +	if (user) {  		dso__delete(map->dso);  		map__delete(map);  	} -	if (function) -		free(function); -	if (name) -		free(name); +	exit_symbol_maps(); +  	return ret;  } + diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index f9f3de8b422..776c9347a3b 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -2,6 +2,7 @@  #define _PROBE_EVENT_H  #include <stdbool.h> +#include "intlist.h"  #include "strlist.h"  #include "strfilter.h" @@ -12,6 +13,7 @@ struct probe_trace_point {  	char		*symbol;	/* Base symbol */  	char		*module;	/* Module name */  	unsigned long	offset;		/* Offset from symbol */ +	unsigned long	address;	/* Actual address of the trace point */  	bool		retprobe;	/* Return probe flag */  }; @@ -75,13 +77,6 @@ struct perf_probe_event {  	struct perf_probe_arg	*args;	/* Arguments */  }; - -/* Line number container */ -struct line_node { -	struct list_head	list; -	int			line; -}; -  /* Line range */  struct line_range {  	char			*file;		/* File name */ @@ -91,7 +86,7 @@ struct line_range {  	int			offset;		/* Start line offset */  	char			*path;		/* Real path name */  	char			*comp_dir;	/* Compile directory */ -	struct list_head	line_list;	/* Visible lines */ +	struct intlist		*line_list;	/* Visible lines */  };  /* List of variables */ @@ -119,6 +114,12 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);  /* Command string to line-range */  extern int parse_line_range_desc(const char *cmd, struct line_range *lr); +/* Release line range members */ +extern void line_range__clear(struct line_range *lr); + +/* Initialize line range */ +extern int line_range__init(struct line_range *lr); +  /* Internal use: Return kernel/module path */  extern const char *kernel_get_module_path(const char *module); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index be0329394d5..98e30476641 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -34,7 +34,9 @@  #include <linux/bitops.h>  #include "event.h" +#include "dso.h"  #include "debug.h" +#include "intlist.h"  #include "util.h"  #include "symbol.h"  #include "probe-finder.h" @@ -42,65 +44,6 @@  /* Kprobe tracer basic type is up to u64 */  #define MAX_BASIC_TYPE_BITS	64 -/* Line number list operations */ - -/* Add a line to line number list */ -static int line_list__add_line(struct list_head *head, 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, head, list) { -		if (ln->line < line) { -			p = &ln->list; -			goto found; -		} else if (ln->line == line)	/* Already exist */ -			return 1; -	} -	/* List is empty, or the smallest entry */ -	p = head; -found: -	pr_debug("line list: add a line %u\n", line); -	ln = zalloc(sizeof(struct line_node)); -	if (ln == NULL) -		return -ENOMEM; -	ln->line = line; -	INIT_LIST_HEAD(&ln->list); -	list_add(&ln->list, p); -	return 0; -} - -/* Check if the line in line number list */ -static int line_list__has_line(struct list_head *head, int line) -{ -	struct line_node *ln; - -	/* Reverse search, because new line will be the last one */ -	list_for_each_entry(ln, head, list) -		if (ln->line == line) -			return 1; - -	return 0; -} - -/* Init line number list */ -static void line_list__init(struct list_head *head) -{ -	INIT_LIST_HEAD(head); -} - -/* Free line number list */ -static void line_list__free(struct list_head *head) -{ -	struct line_node *ln; -	while (!list_empty(head)) { -		ln = list_first_entry(head, struct line_node, list); -		list_del(&ln->list); -		free(ln); -	} -} -  /* Dwarf FL wrappers */  static char *debuginfo_path;	/* Currently dummy */ @@ -115,146 +58,92 @@ static const Dwfl_Callbacks offline_callbacks = {  };  /* Get a Dwarf from offline image */ -static int debuginfo__init_offline_dwarf(struct debuginfo *self, +static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,  					 const char *path)  { -	Dwfl_Module *mod;  	int fd;  	fd = open(path, O_RDONLY);  	if (fd < 0)  		return fd; -	self->dwfl = dwfl_begin(&offline_callbacks); -	if (!self->dwfl) +	dbg->dwfl = dwfl_begin(&offline_callbacks); +	if (!dbg->dwfl)  		goto error; -	mod = dwfl_report_offline(self->dwfl, "", "", fd); -	if (!mod) +	dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); +	if (!dbg->mod)  		goto error; -	self->dbg = dwfl_module_getdwarf(mod, &self->bias); -	if (!self->dbg) +	dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); +	if (!dbg->dbg)  		goto error;  	return 0;  error: -	if (self->dwfl) -		dwfl_end(self->dwfl); +	if (dbg->dwfl) +		dwfl_end(dbg->dwfl);  	else  		close(fd); -	memset(self, 0, sizeof(*self)); +	memset(dbg, 0, sizeof(*dbg));  	return -ENOENT;  } -#if _ELFUTILS_PREREQ(0, 148) -/* This method is buggy if elfutils is older than 0.148 */ -static int __linux_kernel_find_elf(Dwfl_Module *mod, -				   void **userdata, -				   const char *module_name, -				   Dwarf_Addr base, -				   char **file_name, Elf **elfp) -{ -	int fd; -	const char *path = kernel_get_module_path(module_name); - -	pr_debug2("Use file %s for %s\n", path, module_name); -	if (path) { -		fd = open(path, O_RDONLY); -		if (fd >= 0) { -			*file_name = strdup(path); -			return fd; -		} -	} -	/* If failed, try to call standard method */ -	return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base, -					  file_name, elfp); -} - -static const Dwfl_Callbacks kernel_callbacks = { -	.find_debuginfo = dwfl_standard_find_debuginfo, -	.debuginfo_path = &debuginfo_path, - -	.find_elf = __linux_kernel_find_elf, -	.section_address = dwfl_linux_kernel_module_section_address, -}; - -/* Get a Dwarf from live kernel image */ -static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, -					       Dwarf_Addr addr) +static struct debuginfo *__debuginfo__new(const char *path)  { -	self->dwfl = dwfl_begin(&kernel_callbacks); -	if (!self->dwfl) -		return -EINVAL; - -	/* Load the kernel dwarves: Don't care the result here */ -	dwfl_linux_kernel_report_kernel(self->dwfl); -	dwfl_linux_kernel_report_modules(self->dwfl); - -	self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); -	/* Here, check whether we could get a real dwarf */ -	if (!self->dbg) { -		pr_debug("Failed to find kernel dwarf at %lx\n", -			 (unsigned long)addr); -		dwfl_end(self->dwfl); -		memset(self, 0, sizeof(*self)); -		return -ENOENT; -	} +	struct debuginfo *dbg = zalloc(sizeof(*dbg)); +	if (!dbg) +		return NULL; -	return 0; +	if (debuginfo__init_offline_dwarf(dbg, path) < 0) +		zfree(&dbg); +	if (dbg) +		pr_debug("Open Debuginfo file: %s\n", path); +	return dbg;  } -#else -/* With older elfutils, this just support kernel module... */ -static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, -					       Dwarf_Addr addr __maybe_unused) -{ -	const char *path = kernel_get_module_path("kernel"); -	if (!path) { -		pr_err("Failed to find vmlinux path\n"); -		return -ENOENT; -	} - -	pr_debug2("Use file %s for debuginfo\n", path); -	return debuginfo__init_offline_dwarf(self, path); -} -#endif +enum dso_binary_type distro_dwarf_types[] = { +	DSO_BINARY_TYPE__FEDORA_DEBUGINFO, +	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, +	DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, +	DSO_BINARY_TYPE__BUILDID_DEBUGINFO, +	DSO_BINARY_TYPE__NOT_FOUND, +};  struct debuginfo *debuginfo__new(const char *path)  { -	struct debuginfo *self = zalloc(sizeof(struct debuginfo)); -	if (!self) -		return NULL; - -	if (debuginfo__init_offline_dwarf(self, path) < 0) { -		free(self); -		self = NULL; -	} - -	return self; -} - -struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) -{ -	struct debuginfo *self = zalloc(sizeof(struct debuginfo)); -	if (!self) -		return NULL; +	enum dso_binary_type *type; +	char buf[PATH_MAX], nil = '\0'; +	struct dso *dso; +	struct debuginfo *dinfo = NULL; + +	/* Try to open distro debuginfo files */ +	dso = dso__new(path); +	if (!dso) +		goto out; -	if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { -		free(self); -		self = NULL; +	for (type = distro_dwarf_types; +	     !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND; +	     type++) { +		if (dso__read_binary_type_filename(dso, *type, &nil, +						   buf, PATH_MAX) < 0) +			continue; +		dinfo = __debuginfo__new(buf);  	} +	dso__delete(dso); -	return self; +out: +	/* if failed to open all distro debuginfo, open given binary */ +	return dinfo ? : __debuginfo__new(path);  } -void debuginfo__delete(struct debuginfo *self) +void debuginfo__delete(struct debuginfo *dbg)  { -	if (self) { -		if (self->dwfl) -			dwfl_end(self->dwfl); -		free(self); +	if (dbg) { +		if (dbg->dwfl) +			dwfl_end(dbg->dwfl); +		free(dbg);  	}  } @@ -274,12 +163,15 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)  /*   * Convert a location into trace_arg.   * If tvar == NULL, this just checks variable can be converted. + * If fentry == true and vr_die is a parameter, do huristic search + * for the location fuzzed by function entry mcount.   */  static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, -				     Dwarf_Op *fb_ops, +				     Dwarf_Op *fb_ops, Dwarf_Die *sp_die,  				     struct probe_trace_arg *tvar)  {  	Dwarf_Attribute attr; +	Dwarf_Addr tmp = 0;  	Dwarf_Op *op;  	size_t nops;  	unsigned int regn; @@ -292,12 +184,29 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,  		goto static_var;  	/* TODO: handle more than 1 exprs */ -	if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || -	    dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 || -	    nops == 0) { -		/* TODO: Support const_value */ +	if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) +		return -EINVAL;	/* Broken DIE ? */ +	if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { +		ret = dwarf_entrypc(sp_die, &tmp); +		if (ret || addr != tmp || +		    dwarf_tag(vr_die) != DW_TAG_formal_parameter || +		    dwarf_highpc(sp_die, &tmp)) +			return -ENOENT; +		/* +		 * This is fuzzed by fentry mcount. We try to find the +		 * parameter location at the earliest address. +		 */ +		for (addr += 1; addr <= tmp; addr++) { +			if (dwarf_getlocation_addr(&attr, addr, &op, +						   &nops, 1) > 0) +				goto found; +		}  		return -ENOENT;  	} +found: +	if (nops == 0) +		/* TODO: Support const_value */ +		return -ENOENT;  	if (op->atom == DW_OP_addr) {  static_var: @@ -564,7 +473,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,  	}  	if (die_find_member(&type, field->name, die_mem) == NULL) { -		pr_warning("%s(tyep:%s) has no member %s.\n", varname, +		pr_warning("%s(type:%s) has no member %s.\n", varname,  			   dwarf_diename(&type), field->name);  		return -EINVAL;  	} @@ -601,13 +510,13 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)  		 dwarf_diename(vr_die));  	ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, -					pf->tvar); -	if (ret == -ENOENT) +					&pf->sp_die, pf->tvar); +	if (ret == -ENOENT || ret == -EINVAL)  		pr_err("Failed to find the location of %s at this address.\n"  		       " Perhaps, it has been optimized out.\n", pf->pvar->var);  	else if (ret == -ENOTSUP)  		pr_err("Sorry, we don't support this variable location yet.\n"); -	else if (pf->pvar->field) { +	else if (ret == 0 && pf->pvar->field) {  		ret = convert_variable_fields(vr_die, pf->pvar->var,  					      pf->pvar->field, &pf->tvar->ref,  					      &die_mem); @@ -664,49 +573,54 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)  	if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) {  		/* Search again in global variables */  		if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) +			pr_warning("Failed to find '%s' in this function.\n", +				   pf->pvar->var);  			ret = -ENOENT;  	}  	if (ret >= 0)  		ret = convert_variable(&vr_die, pf); -	if (ret < 0) -		pr_warning("Failed to find '%s' in this function.\n", -			   pf->pvar->var);  	return ret;  }  /* Convert subprogram DIE to trace point */ -static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, -				  bool retprobe, struct probe_trace_point *tp) +static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, +				  Dwarf_Addr paddr, bool retprobe, +				  struct probe_trace_point *tp)  {  	Dwarf_Addr eaddr, highaddr; -	const char *name; - -	/* Copy the name of probe point */ -	name = dwarf_diename(sp_die); -	if (name) { -		if (dwarf_entrypc(sp_die, &eaddr) != 0) { -			pr_warning("Failed to get entry address of %s\n", -				   dwarf_diename(sp_die)); -			return -ENOENT; -		} -		if (dwarf_highpc(sp_die, &highaddr) != 0) { -			pr_warning("Failed to get end address of %s\n", -				   dwarf_diename(sp_die)); -			return -ENOENT; -		} -		if (paddr > highaddr) { -			pr_warning("Offset specified is greater than size of %s\n", -				   dwarf_diename(sp_die)); -			return -EINVAL; -		} -		tp->symbol = strdup(name); -		if (tp->symbol == NULL) -			return -ENOMEM; -		tp->offset = (unsigned long)(paddr - eaddr); -	} else -		/* This function has no name. */ -		tp->offset = (unsigned long)paddr; +	GElf_Sym sym; +	const char *symbol; + +	/* Verify the address is correct */ +	if (dwarf_entrypc(sp_die, &eaddr) != 0) { +		pr_warning("Failed to get entry address of %s\n", +			   dwarf_diename(sp_die)); +		return -ENOENT; +	} +	if (dwarf_highpc(sp_die, &highaddr) != 0) { +		pr_warning("Failed to get end address of %s\n", +			   dwarf_diename(sp_die)); +		return -ENOENT; +	} +	if (paddr > highaddr) { +		pr_warning("Offset specified is greater than size of %s\n", +			   dwarf_diename(sp_die)); +		return -EINVAL; +	} + +	/* Get an appropriate symbol from symtab */ +	symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); +	if (!symbol) { +		pr_warning("Failed to find symbol at 0x%lx\n", +			   (unsigned long)paddr); +		return -ENOENT; +	} +	tp->offset = (unsigned long)(paddr - sym.st_value); +	tp->address = (unsigned long)paddr; +	tp->symbol = strdup(symbol); +	if (!tp->symbol) +		return -ENOMEM;  	/* Return probe must be on the head of a subprogram */  	if (retprobe) { @@ -734,7 +648,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)  	}  	/* If not a real subprogram, find a real one */ -	if (dwarf_tag(sc_die) != DW_TAG_subprogram) { +	if (!die_is_func_def(sc_die)) {  		if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {  			pr_warning("Failed to find probe point in any "  				   "functions.\n"); @@ -858,7 +772,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)  }  /* Find lines which match lazy pattern */ -static int find_lazy_match_lines(struct list_head *head, +static int find_lazy_match_lines(struct intlist *list,  				 const char *fname, const char *pat)  {  	FILE *fp; @@ -879,7 +793,7 @@ static int find_lazy_match_lines(struct list_head *head,  			line[len - 1] = '\0';  		if (strlazymatch(line, pat)) { -			line_list__add_line(head, linenum); +			intlist__add(list, linenum);  			count++;  		}  		linenum++; @@ -902,7 +816,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno,  	Dwarf_Die *sc_die, die_mem;  	int ret; -	if (!line_list__has_line(&pf->lcache, lineno) || +	if (!intlist__has_entry(pf->lcache, lineno) ||  	    strtailcmp(fname, pf->fname) != 0)  		return 0; @@ -930,9 +844,9 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)  {  	int ret = 0; -	if (list_empty(&pf->lcache)) { +	if (intlist__empty(pf->lcache)) {  		/* Matching lazy line pattern */ -		ret = find_lazy_match_lines(&pf->lcache, pf->fname, +		ret = find_lazy_match_lines(pf->lcache, pf->fname,  					    pf->pev->point.lazy_line);  		if (ret <= 0)  			return ret; @@ -980,12 +894,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)  	struct dwarf_callback_param *param = data;  	struct probe_finder *pf = param->data;  	struct perf_probe_point *pp = &pf->pev->point; -	Dwarf_Attribute attr;  	/* Check tag and diename */ -	if (dwarf_tag(sp_die) != DW_TAG_subprogram || -	    !die_compare_name(sp_die, pp->function) || -	    dwarf_attr(sp_die, DW_AT_declaration, &attr)) +	if (!die_is_func_def(sp_die) || +	    !die_compare_name(sp_die, pp->function))  		return DWARF_CB_OK;  	/* Check declared file */ @@ -1061,7 +973,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)  }  /* Find probe points from debuginfo */ -static int debuginfo__find_probes(struct debuginfo *self, +static int debuginfo__find_probes(struct debuginfo *dbg,  				  struct probe_finder *pf)  {  	struct perf_probe_point *pp = &pf->pev->point; @@ -1072,11 +984,13 @@ static int debuginfo__find_probes(struct debuginfo *self,  #if _ELFUTILS_PREREQ(0, 142)  	/* Get the call frame information from this dwarf */ -	pf->cfi = dwarf_getcfi(self->dbg); +	pf->cfi = dwarf_getcfi_elf(dwarf_getelf(dbg->dbg));  #endif  	off = 0; -	line_list__init(&pf->lcache); +	pf->lcache = intlist__new(NULL); +	if (!pf->lcache) +		return -ENOMEM;  	/* Fastpath: lookup by function name from .debug_pubnames section */  	if (pp->function) { @@ -1091,7 +1005,7 @@ static int debuginfo__find_probes(struct debuginfo *self,  			.data = pf,  		}; -		dwarf_getpubnames(self->dbg, pubname_search_cb, +		dwarf_getpubnames(dbg->dbg, pubname_search_cb,  				  &pubname_param, 0);  		if (pubname_param.found) {  			ret = probe_point_search_cb(&pf->sp_die, &probe_param); @@ -1101,9 +1015,9 @@ static int debuginfo__find_probes(struct debuginfo *self,  	}  	/* Loop on CUs (Compilation Unit) */ -	while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { +	while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {  		/* Get the DIE(Debugging Information Entry) of this CU */ -		diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); +		diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die);  		if (!diep)  			continue; @@ -1129,17 +1043,86 @@ static int debuginfo__find_probes(struct debuginfo *self,  	}  found: -	line_list__free(&pf->lcache); +	intlist__delete(pf->lcache); +	pf->lcache = NULL;  	return ret;  } +struct local_vars_finder { +	struct probe_finder *pf; +	struct perf_probe_arg *args; +	int max_args; +	int nargs; +	int ret; +}; + +/* Collect available variables in this scope */ +static int copy_variables_cb(Dwarf_Die *die_mem, void *data) +{ +	struct local_vars_finder *vf = data; +	struct probe_finder *pf = vf->pf; +	int tag; + +	tag = dwarf_tag(die_mem); +	if (tag == DW_TAG_formal_parameter || +	    tag == DW_TAG_variable) { +		if (convert_variable_location(die_mem, vf->pf->addr, +					      vf->pf->fb_ops, &pf->sp_die, +					      NULL) == 0) { +			vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); +			if (vf->args[vf->nargs].var == NULL) { +				vf->ret = -ENOMEM; +				return DIE_FIND_CB_END; +			} +			pr_debug(" %s", vf->args[vf->nargs].var); +			vf->nargs++; +		} +	} + +	if (dwarf_haspc(die_mem, vf->pf->addr)) +		return DIE_FIND_CB_CONTINUE; +	else +		return DIE_FIND_CB_SIBLING; +} + +static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, +			     struct perf_probe_arg *args) +{ +	Dwarf_Die die_mem; +	int i; +	int n = 0; +	struct local_vars_finder vf = {.pf = pf, .args = args, +				.max_args = MAX_PROBE_ARGS, .ret = 0}; + +	for (i = 0; i < pf->pev->nargs; i++) { +		/* var never be NULL */ +		if (strcmp(pf->pev->args[i].var, "$vars") == 0) { +			pr_debug("Expanding $vars into:"); +			vf.nargs = n; +			/* Special local variables */ +			die_find_child(sc_die, copy_variables_cb, (void *)&vf, +				       &die_mem); +			pr_debug(" (%d)\n", vf.nargs - n); +			if (vf.ret < 0) +				return vf.ret; +			n = vf.nargs; +		} else { +			/* Copy normal argument */ +			args[n] = pf->pev->args[i]; +			n++; +		} +	} +	return n; +} +  /* Add a found probe point into trace event list */  static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)  {  	struct trace_event_finder *tf =  			container_of(pf, struct trace_event_finder, pf);  	struct probe_trace_event *tev; +	struct perf_probe_arg *args;  	int ret, i;  	/* Check number of tevs */ @@ -1151,7 +1134,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)  	tev = &tf->tevs[tf->ntevs++];  	/* Trace point should be converted from subprogram DIE */ -	ret = convert_to_trace_point(&pf->sp_die, pf->addr, +	ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr,  				     pf->pev->point.retprobe, &tev->point);  	if (ret < 0)  		return ret; @@ -1159,31 +1142,45 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)  	pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,  		 tev->point.offset); -	/* Find each argument */ -	tev->nargs = pf->pev->nargs; -	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); -	if (tev->args == NULL) +	/* Expand special probe argument if exist */ +	args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); +	if (args == NULL)  		return -ENOMEM; -	for (i = 0; i < pf->pev->nargs; i++) { -		pf->pvar = &pf->pev->args[i]; + +	ret = expand_probe_args(sc_die, pf, args); +	if (ret < 0) +		goto end; + +	tev->nargs = ret; +	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); +	if (tev->args == NULL) { +		ret = -ENOMEM; +		goto end; +	} + +	/* Find each argument */ +	for (i = 0; i < tev->nargs; i++) { +		pf->pvar = &args[i];  		pf->tvar = &tev->args[i];  		/* Variable should be found from scope DIE */  		ret = find_variable(sc_die, pf);  		if (ret != 0) -			return ret; +			break;  	} -	return 0; +end: +	free(args); +	return ret;  }  /* Find probe_trace_events specified by perf_probe_event from debuginfo */ -int debuginfo__find_trace_events(struct debuginfo *self, +int debuginfo__find_trace_events(struct debuginfo *dbg,  				 struct perf_probe_event *pev,  				 struct probe_trace_event **tevs, int max_tevs)  {  	struct trace_event_finder tf = {  			.pf = {.pev = pev, .callback = add_probe_trace_event}, -			.max_tevs = max_tevs}; +			.mod = dbg->mod, .max_tevs = max_tevs};  	int ret;  	/* Allocate result tevs array */ @@ -1194,10 +1191,9 @@ int debuginfo__find_trace_events(struct debuginfo *self,  	tf.tevs = *tevs;  	tf.ntevs = 0; -	ret = debuginfo__find_probes(self, &tf.pf); +	ret = debuginfo__find_probes(dbg, &tf.pf);  	if (ret < 0) { -		free(*tevs); -		*tevs = NULL; +		zfree(tevs);  		return ret;  	} @@ -1220,7 +1216,8 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)  	if (tag == DW_TAG_formal_parameter ||  	    tag == DW_TAG_variable) {  		ret = convert_variable_location(die_mem, af->pf.addr, -						af->pf.fb_ops, NULL); +						af->pf.fb_ops, &af->pf.sp_die, +						NULL);  		if (ret == 0) {  			ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);  			pr_debug2("Add new var: %s\n", buf); @@ -1252,7 +1249,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)  	vl = &af->vls[af->nvls++];  	/* Trace point should be converted from subprogram DIE */ -	ret = convert_to_trace_point(&pf->sp_die, pf->addr, +	ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr,  				     pf->pev->point.retprobe, &vl->point);  	if (ret < 0)  		return ret; @@ -1283,14 +1280,19 @@ out:  	return ret;  } -/* Find available variables at given probe point */ -int debuginfo__find_available_vars_at(struct debuginfo *self, +/* + * Find available variables at given probe point + * Return the number of found probe points. Return 0 if there is no + * matched probe point. Return <0 if an error occurs. + */ +int debuginfo__find_available_vars_at(struct debuginfo *dbg,  				      struct perf_probe_event *pev,  				      struct variable_list **vls,  				      int max_vls, bool externs)  {  	struct available_var_finder af = {  			.pf = {.pev = pev, .callback = add_available_vars}, +			.mod = dbg->mod,  			.max_vls = max_vls, .externs = externs};  	int ret; @@ -1302,17 +1304,14 @@ int debuginfo__find_available_vars_at(struct debuginfo *self,  	af.vls = *vls;  	af.nvls = 0; -	ret = debuginfo__find_probes(self, &af.pf); +	ret = debuginfo__find_probes(dbg, &af.pf);  	if (ret < 0) {  		/* Free vlist for error */  		while (af.nvls--) { -			if (af.vls[af.nvls].point.symbol) -				free(af.vls[af.nvls].point.symbol); -			if (af.vls[af.nvls].vars) -				strlist__delete(af.vls[af.nvls].vars); +			zfree(&af.vls[af.nvls].point.symbol); +			strlist__delete(af.vls[af.nvls].vars);  		} -		free(af.vls); -		*vls = NULL; +		zfree(vls);  		return ret;  	} @@ -1320,19 +1319,19 @@ int debuginfo__find_available_vars_at(struct debuginfo *self,  }  /* Reverse search */ -int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, +int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,  				struct perf_probe_point *ppt)  {  	Dwarf_Die cudie, spdie, indie; -	Dwarf_Addr _addr, baseaddr; -	const char *fname = NULL, *func = NULL, *tmp; +	Dwarf_Addr _addr = 0, baseaddr = 0; +	const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp;  	int baseline = 0, lineno = 0, ret = 0;  	/* Adjust address with bias */ -	addr += self->bias; +	addr += dbg->bias;  	/* Find cu die */ -	if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { +	if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr - dbg->bias, &cudie)) {  		pr_warning("Failed to find debug information for address %lx\n",  			   addr);  		ret = -EINVAL; @@ -1346,27 +1345,36 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,  	/* Find a corresponding function (name, baseline and baseaddr) */  	if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {  		/* Get function entry information */ -		tmp = dwarf_diename(&spdie); -		if (!tmp || +		func = basefunc = dwarf_diename(&spdie); +		if (!func ||  		    dwarf_entrypc(&spdie, &baseaddr) != 0 || -		    dwarf_decl_line(&spdie, &baseline) != 0) +		    dwarf_decl_line(&spdie, &baseline) != 0) { +			lineno = 0;  			goto post; -		func = tmp; +		} -		if (addr == (unsigned long)baseaddr) +		fname = dwarf_decl_file(&spdie); +		if (addr == (unsigned long)baseaddr) {  			/* Function entry - Relative line number is 0 */  			lineno = baseline; -		else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, -					     &indie)) { +			goto post; +		} + +		/* Track down the inline functions step by step */ +		while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, +						&indie)) { +			/* There is an inline function */  			if (dwarf_entrypc(&indie, &_addr) == 0 && -			    _addr == addr) +			    _addr == addr) {  				/*  				 * addr is at an inline function entry.  				 * In this case, lineno should be the call-site -				 * line number. +				 * line number. (overwrite lineinfo)  				 */  				lineno = die_get_call_lineno(&indie); -			else { +				fname = die_get_call_file(&indie); +				break; +			} else {  				/*  				 * addr is in an inline function body.  				 * Since lineno points one of the lines @@ -1374,19 +1382,27 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,  				 * be the entry line of the inline function.  				 */  				tmp = dwarf_diename(&indie); -				if (tmp && -				    dwarf_decl_line(&spdie, &baseline) == 0) -					func = tmp; +				if (!tmp || +				    dwarf_decl_line(&indie, &baseline) != 0) +					break; +				func = tmp; +				spdie = indie;  			}  		} +		/* Verify the lineno and baseline are in a same file */ +		tmp = dwarf_decl_file(&spdie); +		if (!tmp || strcmp(tmp, fname) != 0) +			lineno = 0;  	}  post:  	/* Make a relative line number or an offset */  	if (lineno)  		ppt->line = lineno - baseline; -	else if (func) +	else if (basefunc) {  		ppt->offset = addr - (unsigned long)baseaddr; +		func = basefunc; +	}  	/* Duplicate strings */  	if (func) { @@ -1399,10 +1415,7 @@ post:  	if (fname) {  		ppt->file = strdup(fname);  		if (ppt->file == NULL) { -			if (ppt->function) { -				free(ppt->function); -				ppt->function = NULL; -			} +			zfree(&ppt->function);  			ret = -ENOMEM;  			goto end;  		} @@ -1423,7 +1436,7 @@ static int line_range_add_line(const char *src, unsigned int lineno,  		if (lr->path == NULL)  			return -ENOMEM;  	} -	return line_list__add_line(&lr->line_list, lineno); +	return intlist__add(lr->line_list, lineno);  }  static int line_range_walk_cb(const char *fname, int lineno, @@ -1431,13 +1444,15 @@ static int line_range_walk_cb(const char *fname, int lineno,  			      void *data)  {  	struct line_finder *lf = data; +	int err;  	if ((strtailcmp(fname, lf->fname) != 0) ||  	    (lf->lno_s > lineno || lf->lno_e < lineno))  		return 0; -	if (line_range_add_line(fname, lineno, lf->lr) < 0) -		return -EINVAL; +	err = line_range_add_line(fname, lineno, lf->lr); +	if (err < 0 && err != -EEXIST) +		return err;  	return 0;  } @@ -1451,30 +1466,30 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)  	/* Update status */  	if (ret >= 0) -		if (!list_empty(&lf->lr->line_list)) +		if (!intlist__empty(lf->lr->line_list))  			ret = lf->found = 1;  		else  			ret = 0;	/* Lines are not found */  	else { -		free(lf->lr->path); -		lf->lr->path = NULL; +		zfree(&lf->lr->path);  	}  	return ret;  }  static int line_range_inline_cb(Dwarf_Die *in_die, void *data)  { -	find_line_range_by_line(in_die, data); +	int ret = find_line_range_by_line(in_die, data);  	/*  	 * We have to check all instances of inlined function, because  	 * some execution paths can be optimized out depends on the -	 * function argument of instances +	 * function argument of instances. However, if an error occurs, +	 * it should be handled by the caller.  	 */ -	return 0; +	return ret < 0 ? ret : 0;  } -/* Search function from function name */ +/* Search function definition from function name */  static int line_range_search_cb(Dwarf_Die *sp_die, void *data)  {  	struct dwarf_callback_param *param = data; @@ -1485,7 +1500,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)  	if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die)))  		return DWARF_CB_OK; -	if (dwarf_tag(sp_die) == DW_TAG_subprogram && +	if (die_is_func_def(sp_die) &&  	    die_compare_name(sp_die, lr->function)) {  		lf->fname = dwarf_decl_file(sp_die);  		dwarf_decl_line(sp_die, &lr->offset); @@ -1516,7 +1531,7 @@ static int find_line_range_by_func(struct line_finder *lf)  	return param.retval;  } -int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) +int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)  {  	struct line_finder lf = {.lr = lr, .found = 0};  	int ret = 0; @@ -1533,7 +1548,7 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr)  		struct dwarf_callback_param line_range_param = {  			.data = (void *)&lf, .retval = 0}; -		dwarf_getpubnames(self->dbg, pubname_search_cb, +		dwarf_getpubnames(dbg->dbg, pubname_search_cb,  				  &pubname_param, 0);  		if (pubname_param.found) {  			line_range_search_cb(&lf.sp_die, &line_range_param); @@ -1544,12 +1559,12 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr)  	/* Loop on CUs (Compilation Unit) */  	while (!lf.found && ret >= 0) { -		if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, +		if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl,  				 NULL, NULL, NULL) != 0)  			break;  		/* Get the DIE(Debugging Information Entry) of this CU */ -		diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); +		diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die);  		if (!diep)  			continue; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 17e94d0c36f..92590b2c7e1 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -3,10 +3,12 @@  #include <stdbool.h>  #include "util.h" +#include "intlist.h"  #include "probe-event.h"  #define MAX_PROBE_BUFFER	1024  #define MAX_PROBES		 128 +#define MAX_PROBE_ARGS		 128  static inline int is_c_varname(const char *name)  { @@ -14,7 +16,7 @@ static inline int is_c_varname(const char *name)  	return isalpha(name[0]) || name[0] == '_';  } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  #include "dwarf-aux.h" @@ -23,31 +25,32 @@ static inline int is_c_varname(const char *name)  /* debug information structure */  struct debuginfo {  	Dwarf		*dbg; +	Dwfl_Module	*mod;  	Dwfl		*dwfl;  	Dwarf_Addr	bias;  }; +/* This also tries to open distro debuginfo */  extern struct debuginfo *debuginfo__new(const char *path); -extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); -extern void debuginfo__delete(struct debuginfo *self); +extern void debuginfo__delete(struct debuginfo *dbg);  /* Find probe_trace_events specified by perf_probe_event from debuginfo */ -extern int debuginfo__find_trace_events(struct debuginfo *self, +extern int debuginfo__find_trace_events(struct debuginfo *dbg,  					struct perf_probe_event *pev,  					struct probe_trace_event **tevs,  					int max_tevs);  /* Find a perf_probe_point from debuginfo */ -extern int debuginfo__find_probe_point(struct debuginfo *self, +extern int debuginfo__find_probe_point(struct debuginfo *dbg,  				       unsigned long addr,  				       struct perf_probe_point *ppt);  /* Find a line range */ -extern int debuginfo__find_line_range(struct debuginfo *self, +extern int debuginfo__find_line_range(struct debuginfo *dbg,  				      struct line_range *lr);  /* Find available variables */ -extern int debuginfo__find_available_vars_at(struct debuginfo *self, +extern int debuginfo__find_available_vars_at(struct debuginfo *dbg,  					     struct perf_probe_event *pev,  					     struct variable_list **vls,  					     int max_points, bool externs); @@ -64,7 +67,7 @@ struct probe_finder {  	const char		*fname;		/* Real file name */  	Dwarf_Die		cu_die;		/* Current CU */  	Dwarf_Die		sp_die; -	struct list_head	lcache;		/* Line cache for lazy match */ +	struct intlist		*lcache;	/* Line cache for lazy match */  	/* For variable searching */  #if _ELFUTILS_PREREQ(0, 142) @@ -77,6 +80,7 @@ struct probe_finder {  struct trace_event_finder {  	struct probe_finder	pf; +	Dwfl_Module		*mod;		/* For solving symbols */  	struct probe_trace_event *tevs;		/* Found trace events */  	int			ntevs;		/* Number of trace events */  	int			max_tevs;	/* Max number of trace events */ @@ -84,6 +88,7 @@ struct trace_event_finder {  struct available_var_finder {  	struct probe_finder	pf; +	Dwfl_Module		*mod;		/* For solving symbols */  	struct variable_list	*vls;		/* Found variable lists */  	int			nvls;		/* Number of variable lists */  	int			max_vls;	/* Max no. of variable lists */ @@ -102,6 +107,6 @@ struct line_finder {  	int			found;  }; -#endif /* DWARF_SUPPORT */ +#endif /* HAVE_DWARF_SUPPORT */  #endif /*_PROBE_FINDER_H */ diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h index 4cedea59f51..c3cb6584d52 100644 --- a/tools/perf/util/pstack.h +++ b/tools/perf/util/pstack.h @@ -5,10 +5,10 @@  struct pstack;  struct pstack *pstack__new(unsigned short max_nr_entries); -void pstack__delete(struct pstack *self); -bool pstack__empty(const struct pstack *self); -void pstack__remove(struct pstack *self, void *key); -void pstack__push(struct pstack *self, void *key); -void *pstack__pop(struct pstack *self); +void pstack__delete(struct pstack *pstack); +bool pstack__empty(const struct pstack *pstack); +void pstack__remove(struct pstack *pstack, void *key); +void pstack__push(struct pstack *pstack, void *key); +void *pstack__pop(struct pstack *pstack);  #endif /* _PERF_PSTACK_ */ diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index f75ae1b9900..16a475a7d49 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -17,5 +17,6 @@ util/xyarray.c  util/cgroup.c  util/rblist.c  util/strlist.c -util/sysfs.c +../lib/api/fs/fs.c +util/trace-event.c  ../../lib/rbtree.c diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 71b5412bbbb..122669c18ff 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -33,13 +33,6 @@ int eprintf(int level, const char *fmt, ...)  # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,  #endif -struct throttle_event { -	struct perf_event_header header; -	u64			 time; -	u64			 id; -	u64			 stream_id; -}; -  PyMODINIT_FUNC initperf(void);  #define member_def(type, member, ptype, help) \ @@ -822,6 +815,8 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,  		PyObject *pyevent = pyrf_event__new(event);  		struct pyrf_event *pevent = (struct pyrf_event *)pyevent; +		perf_evlist__mmap_consume(evlist, cpu); +  		if (pyevent == NULL)  			return PyErr_NoMemory(); @@ -913,9 +908,10 @@ static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i)  	if (i >= pevlist->evlist.nr_entries)  		return NULL; -	list_for_each_entry(pos, &pevlist->evlist.entries, node) +	evlist__for_each(&pevlist->evlist, pos) {  		if (i-- == 0)  			break; +	}  	return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel));  } @@ -1036,6 +1032,7 @@ PyMODINIT_FUNC initperf(void)  	    pyrf_cpu_map__setup_types() < 0)  		return; +	/* The page_size is placed in util object. */  	page_size = sysconf(_SC_PAGE_SIZE);  	Py_INCREF(&pyrf_evlist__type); diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c index a16cdd2625a..0dfe27d9945 100644 --- a/tools/perf/util/rblist.c +++ b/tools/perf/util/rblist.c @@ -48,10 +48,12 @@ void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node)  	rblist->node_delete(rblist, rb_node);  } -struct rb_node *rblist__find(struct rblist *rblist, const void *entry) +static struct rb_node *__rblist__findnew(struct rblist *rblist, +					 const void *entry, +					 bool create)  {  	struct rb_node **p = &rblist->entries.rb_node; -	struct rb_node *parent = NULL; +	struct rb_node *parent = NULL, *new_node = NULL;  	while (*p != NULL) {  		int rc; @@ -67,7 +69,26 @@ struct rb_node *rblist__find(struct rblist *rblist, const void *entry)  			return parent;  	} -	return NULL; +	if (create) { +		new_node = rblist->node_new(rblist, entry); +		if (new_node) { +			rb_link_node(new_node, parent, p); +			rb_insert_color(new_node, &rblist->entries); +			++rblist->nr_entries; +		} +	} + +	return new_node; +} + +struct rb_node *rblist__find(struct rblist *rblist, const void *entry) +{ +	return __rblist__findnew(rblist, entry, false); +} + +struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry) +{ +	return __rblist__findnew(rblist, entry, true);  }  void rblist__init(struct rblist *rblist) diff --git a/tools/perf/util/rblist.h b/tools/perf/util/rblist.h index 6d0cae5ae83..ff9913b994c 100644 --- a/tools/perf/util/rblist.h +++ b/tools/perf/util/rblist.h @@ -32,6 +32,7 @@ void rblist__delete(struct rblist *rblist);  int rblist__add_node(struct rblist *rblist, const void *new_entry);  void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node);  struct rb_node *rblist__find(struct rblist *rblist, const void *entry); +struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry);  struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx);  static inline bool rblist__empty(const struct rblist *rblist) diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 18d73aa2f0f..049e0a09ccd 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -2,6 +2,8 @@  #include "evsel.h"  #include "cpumap.h"  #include "parse-events.h" +#include <api/fs/fs.h> +#include "util.h"  typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); @@ -72,8 +74,7 @@ bool perf_can_sample_identifier(void)  	return perf_probe_api(perf_probe_sample_identifier);  } -void perf_evlist__config(struct perf_evlist *evlist, -			struct perf_record_opts *opts) +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts)  {  	struct perf_evsel *evsel;  	bool use_sample_identifier = false; @@ -88,21 +89,127 @@ void perf_evlist__config(struct perf_evlist *evlist,  	if (evlist->cpus->map[0] < 0)  		opts->no_inherit = true; -	list_for_each_entry(evsel, &evlist->entries, node) +	evlist__for_each(evlist, evsel)  		perf_evsel__config(evsel, opts);  	if (evlist->nr_entries > 1) {  		struct perf_evsel *first = perf_evlist__first(evlist); -		list_for_each_entry(evsel, &evlist->entries, node) { +		evlist__for_each(evlist, evsel) {  			if (evsel->attr.sample_type == first->attr.sample_type)  				continue;  			use_sample_identifier = perf_can_sample_identifier();  			break;  		} -		list_for_each_entry(evsel, &evlist->entries, node) +		evlist__for_each(evlist, evsel)  			perf_evsel__set_sample_id(evsel, use_sample_identifier);  	}  	perf_evlist__set_id_pos(evlist);  } + +static int get_max_rate(unsigned int *rate) +{ +	char path[PATH_MAX]; +	const char *procfs = procfs__mountpoint(); + +	if (!procfs) +		return -1; + +	snprintf(path, PATH_MAX, +		 "%s/sys/kernel/perf_event_max_sample_rate", procfs); + +	return filename__read_int(path, (int *) rate); +} + +static int record_opts__config_freq(struct record_opts *opts) +{ +	bool user_freq = opts->user_freq != UINT_MAX; +	unsigned int max_rate; + +	if (opts->user_interval != ULLONG_MAX) +		opts->default_interval = opts->user_interval; +	if (user_freq) +		opts->freq = opts->user_freq; + +	/* +	 * User specified count overrides default frequency. +	 */ +	if (opts->default_interval) +		opts->freq = 0; +	else if (opts->freq) { +		opts->default_interval = opts->freq; +	} else { +		pr_err("frequency and count are zero, aborting\n"); +		return -1; +	} + +	if (get_max_rate(&max_rate)) +		return 0; + +	/* +	 * User specified frequency is over current maximum. +	 */ +	if (user_freq && (max_rate < opts->freq)) { +		pr_err("Maximum frequency rate (%u) reached.\n" +		   "Please use -F freq option with lower value or consider\n" +		   "tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n", +		   max_rate); +		return -1; +	} + +	/* +	 * Default frequency is over current maximum. +	 */ +	if (max_rate < opts->freq) { +		pr_warning("Lowering default frequency rate to %u.\n" +			   "Please consider tweaking " +			   "/proc/sys/kernel/perf_event_max_sample_rate.\n", +			   max_rate); +		opts->freq = max_rate; +	} + +	return 0; +} + +int record_opts__config(struct record_opts *opts) +{ +	return record_opts__config_freq(opts); +} + +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) +{ +	struct perf_evlist *temp_evlist; +	struct perf_evsel *evsel; +	int err, fd, cpu; +	bool ret = false; + +	temp_evlist = perf_evlist__new(); +	if (!temp_evlist) +		return false; + +	err = parse_events(temp_evlist, str); +	if (err) +		goto out_delete; + +	evsel = perf_evlist__last(temp_evlist); + +	if (!evlist || cpu_map__empty(evlist->cpus)) { +		struct cpu_map *cpus = cpu_map__new(NULL); + +		cpu =  cpus ? cpus->map[0] : 0; +		cpu_map__delete(cpus); +	} else { +		cpu = evlist->cpus->map[0]; +	} + +	fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); +	if (fd >= 0) { +		close(fd); +		ret = true; +	} + +out_delete: +	perf_evlist__delete(temp_evlist); +	return ret; +} diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index a85e4ae5f3a..af7da565a75 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -194,8 +194,7 @@ static void define_event_symbols(struct event_format *event,  		zero_flag_atom = 0;  		break;  	case PRINT_FIELD: -		if (cur_field_name) -			free(cur_field_name); +		free(cur_field_name);  		cur_field_name = strdup(args->field.name);  		break;  	case PRINT_FLAGS: @@ -216,6 +215,7 @@ static void define_event_symbols(struct event_format *event,  	case PRINT_BSTRING:  	case PRINT_DYNAMIC_ARRAY:  	case PRINT_STRING: +	case PRINT_BITMASK:  		break;  	case PRINT_TYPE:  		define_event_symbols(event, ev_name, args->typecast.item); @@ -257,12 +257,9 @@ static inline struct event_format *find_cache_event(struct perf_evsel *evsel)  	return event;  } -static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, -				    struct perf_sample *sample, +static void perl_process_tracepoint(struct perf_sample *sample,  				    struct perf_evsel *evsel, -				    struct machine *machine __maybe_unused, -				    struct thread *thread, -					struct addr_location *al) +				    struct thread *thread)  {  	struct format_field *field;  	static char handler[256]; @@ -273,7 +270,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,  	int cpu = sample->cpu;  	void *data = sample->raw_data;  	unsigned long long nsecs = sample->time; -	char *comm = thread->comm; +	const char *comm = thread__comm_str(thread);  	dSP; @@ -282,7 +279,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,  	event = find_cache_event(evsel);  	if (!event) -		die("ug! no event found for type %" PRIu64, evsel->attr.config); +		die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);  	pid = raw_field_value(event, "common_pid", data); @@ -349,10 +346,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,  static void perl_process_event_generic(union perf_event *event,  				       struct perf_sample *sample, -				       struct perf_evsel *evsel, -				       struct machine *machine __maybe_unused, -				       struct thread *thread __maybe_unused, -					   struct addr_location *al __maybe_unused) +				       struct perf_evsel *evsel)  {  	dSP; @@ -377,12 +371,11 @@ static void perl_process_event_generic(union perf_event *event,  static void perl_process_event(union perf_event *event,  			       struct perf_sample *sample,  			       struct perf_evsel *evsel, -			       struct machine *machine,  			       struct thread *thread, -				   struct addr_location *al) +			       struct addr_location *al __maybe_unused)  { -	perl_process_tracepoint(event, sample, evsel, machine, thread, al); -	perl_process_event_generic(event, sample, evsel, machine, thread, al); +	perl_process_tracepoint(sample, evsel, thread); +	perl_process_event_generic(event, sample, evsel);  }  static void run_start_sub(void) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cc75a3cef38..1c419321f70 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -56,6 +56,17 @@ static void handler_call_die(const char *handler_name)  	Py_FatalError("problem in Python trace event handler");  } +/* + * Insert val into into the dictionary and decrement the reference counter. + * This is necessary for dictionaries since PyDict_SetItemString() does not  + * steal a reference, as opposed to PyTuple_SetItem(). + */ +static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObject *val) +{ +	PyDict_SetItemString(dict, key, val); +	Py_DECREF(val); +} +  static void define_value(enum print_arg_type field_type,  			 const char *ev_name,  			 const char *field_name, @@ -150,8 +161,7 @@ static void define_event_symbols(struct event_format *event,  		zero_flag_atom = 0;  		break;  	case PRINT_FIELD: -		if (cur_field_name) -			free(cur_field_name); +		free(cur_field_name);  		cur_field_name = strdup(args->field.name);  		break;  	case PRINT_FLAGS: @@ -187,6 +197,7 @@ static void define_event_symbols(struct event_format *event,  	case PRINT_BSTRING:  	case PRINT_DYNAMIC_ARRAY:  	case PRINT_FUNC: +	case PRINT_BITMASK:  		/* we should warn... */  		return;  	} @@ -220,13 +231,10 @@ static inline struct event_format *find_cache_event(struct perf_evsel *evsel)  	return event;  } -static void python_process_tracepoint(union perf_event *perf_event -				      __maybe_unused, -				 struct perf_sample *sample, -				 struct perf_evsel *evsel, -				 struct machine *machine __maybe_unused, -				 struct thread *thread, -				 struct addr_location *al) +static void python_process_tracepoint(struct perf_sample *sample, +				      struct perf_evsel *evsel, +				      struct thread *thread, +				      struct addr_location *al)  {  	PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;  	static char handler_name[256]; @@ -239,7 +247,7 @@ static void python_process_tracepoint(union perf_event *perf_event  	int cpu = sample->cpu;  	void *data = sample->raw_data;  	unsigned long long nsecs = sample->time; -	char *comm = thread->comm; +	const char *comm = thread__comm_str(thread);  	t = PyTuple_New(MAX_FIELDS);  	if (!t) @@ -279,11 +287,11 @@ static void python_process_tracepoint(union perf_event *perf_event  		PyTuple_SetItem(t, n++, PyInt_FromLong(pid));  		PyTuple_SetItem(t, n++, PyString_FromString(comm));  	} else { -		PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu)); -		PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s)); -		PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns)); -		PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid)); -		PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm)); +		pydict_set_item_string_decref(dict, "common_cpu", PyInt_FromLong(cpu)); +		pydict_set_item_string_decref(dict, "common_s", PyInt_FromLong(s)); +		pydict_set_item_string_decref(dict, "common_ns", PyInt_FromLong(ns)); +		pydict_set_item_string_decref(dict, "common_pid", PyInt_FromLong(pid)); +		pydict_set_item_string_decref(dict, "common_comm", PyString_FromString(comm));  	}  	for (field = event->format.fields; field; field = field->next) {  		if (field->flags & FIELD_IS_STRING) { @@ -313,7 +321,7 @@ static void python_process_tracepoint(union perf_event *perf_event  		if (handler)  			PyTuple_SetItem(t, n++, obj);  		else -			PyDict_SetItemString(dict, field->name, obj); +			pydict_set_item_string_decref(dict, field->name, obj);  	}  	if (!handler) @@ -340,11 +348,8 @@ static void python_process_tracepoint(union perf_event *perf_event  	Py_DECREF(t);  } -static void python_process_general_event(union perf_event *perf_event -					 __maybe_unused, -					 struct perf_sample *sample, +static void python_process_general_event(struct perf_sample *sample,  					 struct perf_evsel *evsel, -					 struct machine *machine __maybe_unused,  					 struct thread *thread,  					 struct addr_location *al)  { @@ -370,21 +375,21 @@ static void python_process_general_event(union perf_event *perf_event  	if (!handler || !PyCallable_Check(handler))  		goto exit; -	PyDict_SetItemString(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); -	PyDict_SetItemString(dict, "attr", PyString_FromStringAndSize( +	pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); +	pydict_set_item_string_decref(dict, "attr", PyString_FromStringAndSize(  			(const char *)&evsel->attr, sizeof(evsel->attr))); -	PyDict_SetItemString(dict, "sample", PyString_FromStringAndSize( +	pydict_set_item_string_decref(dict, "sample", PyString_FromStringAndSize(  			(const char *)sample, sizeof(*sample))); -	PyDict_SetItemString(dict, "raw_buf", PyString_FromStringAndSize( +	pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize(  			(const char *)sample->raw_data, sample->raw_size)); -	PyDict_SetItemString(dict, "comm", -			PyString_FromString(thread->comm)); +	pydict_set_item_string_decref(dict, "comm", +			PyString_FromString(thread__comm_str(thread)));  	if (al->map) { -		PyDict_SetItemString(dict, "dso", +		pydict_set_item_string_decref(dict, "dso",  			PyString_FromString(al->map->dso->name));  	}  	if (al->sym) { -		PyDict_SetItemString(dict, "symbol", +		pydict_set_item_string_decref(dict, "symbol",  			PyString_FromString(al->sym->name));  	} @@ -400,22 +405,19 @@ exit:  	Py_DECREF(t);  } -static void python_process_event(union perf_event *perf_event, +static void python_process_event(union perf_event *event __maybe_unused,  				 struct perf_sample *sample,  				 struct perf_evsel *evsel, -				 struct machine *machine,  				 struct thread *thread,  				 struct addr_location *al)  {  	switch (evsel->attr.type) {  	case PERF_TYPE_TRACEPOINT: -		python_process_tracepoint(perf_event, sample, evsel, -					  machine, thread, al); +		python_process_tracepoint(sample, evsel, thread, al);  		break;  	/* Reserve for future process_hw/sw/raw APIs */  	default: -		python_process_general_event(perf_event, sample, evsel, -					     machine, thread, al); +		python_process_general_event(sample, evsel, thread, al);  	}  } @@ -621,6 +623,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)  			fprintf(ofp, "%s=", f->name);  			if (f->flags & FIELD_IS_STRING ||  			    f->flags & FIELD_IS_FLAG || +			    f->flags & FIELD_IS_ARRAY ||  			    f->flags & FIELD_IS_SYMBOLIC)  				fprintf(ofp, "%%s");  			else if (f->flags & FIELD_IS_SIGNED) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 51f5edf2a6d..64a186edc7b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -16,73 +16,34 @@  #include "perf_regs.h"  #include "vdso.h" -static int perf_session__open(struct perf_session *self, bool force) +static int perf_session__open(struct perf_session *session)  { -	struct stat input_stat; +	struct perf_data_file *file = session->file; -	if (!strcmp(self->filename, "-")) { -		self->fd_pipe = true; -		self->fd = STDIN_FILENO; - -		if (perf_session__read_header(self) < 0) -			pr_err("incompatible file format (rerun with -v to learn more)"); - -		return 0; -	} - -	self->fd = open(self->filename, O_RDONLY); -	if (self->fd < 0) { -		int err = errno; - -		pr_err("failed to open %s: %s", self->filename, strerror(err)); -		if (err == ENOENT && !strcmp(self->filename, "perf.data")) -			pr_err("  (try 'perf record' first)"); -		pr_err("\n"); -		return -errno; -	} - -	if (fstat(self->fd, &input_stat) < 0) -		goto out_close; - -	if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { -		pr_err("file %s not owned by current user or root\n", -		       self->filename); -		goto out_close; -	} - -	if (!input_stat.st_size) { -		pr_info("zero-sized file (%s), nothing to do!\n", -			self->filename); -		goto out_close; -	} - -	if (perf_session__read_header(self) < 0) { +	if (perf_session__read_header(session) < 0) {  		pr_err("incompatible file format (rerun with -v to learn more)"); -		goto out_close; +		return -1;  	} -	if (!perf_evlist__valid_sample_type(self->evlist)) { +	if (perf_data_file__is_pipe(file)) +		return 0; + +	if (!perf_evlist__valid_sample_type(session->evlist)) {  		pr_err("non matching sample_type"); -		goto out_close; +		return -1;  	} -	if (!perf_evlist__valid_sample_id_all(self->evlist)) { +	if (!perf_evlist__valid_sample_id_all(session->evlist)) {  		pr_err("non matching sample_id_all"); -		goto out_close; +		return -1;  	} -	if (!perf_evlist__valid_read_format(self->evlist)) { +	if (!perf_evlist__valid_read_format(session->evlist)) {  		pr_err("non matching read_format"); -		goto out_close; +		return -1;  	} -	self->size = input_stat.st_size;  	return 0; - -out_close: -	close(self->fd); -	self->fd = -1; -	return -1;  }  void perf_session__set_id_hdr_size(struct perf_session *session) @@ -92,71 +53,70 @@ void perf_session__set_id_hdr_size(struct perf_session *session)  	machines__set_id_hdr_size(&session->machines, id_hdr_size);  } -int perf_session__create_kernel_maps(struct perf_session *self) +int perf_session__create_kernel_maps(struct perf_session *session)  { -	int ret = machine__create_kernel_maps(&self->machines.host); +	int ret = machine__create_kernel_maps(&session->machines.host);  	if (ret >= 0) -		ret = machines__create_guest_kernel_maps(&self->machines); +		ret = machines__create_guest_kernel_maps(&session->machines);  	return ret;  } -static void perf_session__destroy_kernel_maps(struct perf_session *self) +static void perf_session__destroy_kernel_maps(struct perf_session *session)  { -	machines__destroy_kernel_maps(&self->machines); +	machines__destroy_kernel_maps(&session->machines);  } -struct perf_session *perf_session__new(const char *filename, int mode, -				       bool force, bool repipe, -				       struct perf_tool *tool) +struct perf_session *perf_session__new(struct perf_data_file *file, +				       bool repipe, struct perf_tool *tool)  { -	struct perf_session *self; -	struct stat st; -	size_t len; +	struct perf_session *session = zalloc(sizeof(*session)); -	if (!filename || !strlen(filename)) { -		if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) -			filename = "-"; -		else -			filename = "perf.data"; -	} - -	len = strlen(filename); -	self = zalloc(sizeof(*self) + len); - -	if (self == NULL) +	if (!session)  		goto out; -	memcpy(self->filename, filename, len); -	self->repipe = repipe; -	INIT_LIST_HEAD(&self->ordered_samples.samples); -	INIT_LIST_HEAD(&self->ordered_samples.sample_cache); -	INIT_LIST_HEAD(&self->ordered_samples.to_free); -	machines__init(&self->machines); +	session->repipe = repipe; +	INIT_LIST_HEAD(&session->ordered_samples.samples); +	INIT_LIST_HEAD(&session->ordered_samples.sample_cache); +	INIT_LIST_HEAD(&session->ordered_samples.to_free); +	machines__init(&session->machines); -	if (mode == O_RDONLY) { -		if (perf_session__open(self, force) < 0) +	if (file) { +		if (perf_data_file__open(file))  			goto out_delete; -		perf_session__set_id_hdr_size(self); -	} else if (mode == O_WRONLY) { + +		session->file = file; + +		if (perf_data_file__is_read(file)) { +			if (perf_session__open(session) < 0) +				goto out_close; + +			perf_session__set_id_hdr_size(session); +		} +	} + +	if (!file || perf_data_file__is_write(file)) {  		/*  		 * In O_RDONLY mode this will be performed when reading the  		 * kernel MMAP event, in perf_event__process_mmap().  		 */ -		if (perf_session__create_kernel_maps(self) < 0) +		if (perf_session__create_kernel_maps(session) < 0)  			goto out_delete;  	}  	if (tool && tool->ordering_requires_timestamps && -	    tool->ordered_samples && !perf_evlist__sample_id_all(self->evlist)) { +	    tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) {  		dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");  		tool->ordered_samples = false;  	} -out: -	return self; -out_delete: -	perf_session__delete(self); +	return session; + + out_close: +	perf_data_file__close(file); + out_delete: +	perf_session__delete(session); + out:  	return NULL;  } @@ -172,29 +132,30 @@ static void perf_session__delete_threads(struct perf_session *session)  static void perf_session_env__delete(struct perf_session_env *env)  { -	free(env->hostname); -	free(env->os_release); -	free(env->version); -	free(env->arch); -	free(env->cpu_desc); -	free(env->cpuid); - -	free(env->cmdline); -	free(env->sibling_cores); -	free(env->sibling_threads); -	free(env->numa_nodes); -	free(env->pmu_mappings); -} - -void perf_session__delete(struct perf_session *self) -{ -	perf_session__destroy_kernel_maps(self); -	perf_session__delete_dead_threads(self); -	perf_session__delete_threads(self); -	perf_session_env__delete(&self->header.env); -	machines__exit(&self->machines); -	close(self->fd); -	free(self); +	zfree(&env->hostname); +	zfree(&env->os_release); +	zfree(&env->version); +	zfree(&env->arch); +	zfree(&env->cpu_desc); +	zfree(&env->cpuid); + +	zfree(&env->cmdline); +	zfree(&env->sibling_cores); +	zfree(&env->sibling_threads); +	zfree(&env->numa_nodes); +	zfree(&env->pmu_mappings); +} + +void perf_session__delete(struct perf_session *session) +{ +	perf_session__destroy_kernel_maps(session); +	perf_session__delete_dead_threads(session); +	perf_session__delete_threads(session); +	perf_session_env__delete(&session->header.env); +	machines__exit(&session->machines); +	if (session->file) +		perf_data_file__close(session->file); +	free(session);  	vdso__exit();  } @@ -256,6 +217,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)  		tool->sample = process_event_sample_stub;  	if (tool->mmap == NULL)  		tool->mmap = process_event_stub; +	if (tool->mmap2 == NULL) +		tool->mmap2 = process_event_stub;  	if (tool->comm == NULL)  		tool->comm = process_event_stub;  	if (tool->fork == NULL) @@ -284,27 +247,6 @@ void perf_tool__fill_defaults(struct perf_tool *tool)  	}  } -void mem_bswap_32(void *src, int byte_size) -{ -	u32 *m = src; -	while (byte_size > 0) { -		*m = bswap_32(*m); -		byte_size -= sizeof(u32); -		++m; -	} -} - -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 swap_sample_id_all(union perf_event *event, void *data)  {  	void *end = (void *) event + event->header.size; @@ -395,6 +337,17 @@ static void perf_event__read_swap(union perf_event *event, bool sample_id_all)  		swap_sample_id_all(event, &event->read + 1);  } +static void perf_event__throttle_swap(union perf_event *event, +				      bool sample_id_all) +{ +	event->throttle.time	  = bswap_64(event->throttle.time); +	event->throttle.id	  = bswap_64(event->throttle.id); +	event->throttle.stream_id = bswap_64(event->throttle.stream_id); + +	if (sample_id_all) +		swap_sample_id_all(event, &event->throttle + 1); +} +  static u8 revbyte(u8 b)  {  	int rev = (b >> 4) | ((b & 0xf) << 4); @@ -440,6 +393,9 @@ void perf_event__attr_swap(struct perf_event_attr *attr)  	attr->bp_type		= bswap_32(attr->bp_type);  	attr->bp_addr		= bswap_64(attr->bp_addr);  	attr->bp_len		= bswap_64(attr->bp_len); +	attr->branch_sample_type = bswap_64(attr->branch_sample_type); +	attr->sample_regs_user	 = bswap_64(attr->sample_regs_user); +	attr->sample_stack_user  = bswap_32(attr->sample_stack_user);  	swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64));  } @@ -480,6 +436,8 @@ static perf_event__swap_op perf_event__swap_ops[] = {  	[PERF_RECORD_EXIT]		  = perf_event__task_swap,  	[PERF_RECORD_LOST]		  = perf_event__all64_swap,  	[PERF_RECORD_READ]		  = perf_event__read_swap, +	[PERF_RECORD_THROTTLE]		  = perf_event__throttle_swap, +	[PERF_RECORD_UNTHROTTLE]	  = perf_event__throttle_swap,  	[PERF_RECORD_SAMPLE]		  = perf_event__all64_swap,  	[PERF_RECORD_HEADER_ATTR]	  = perf_event__hdr_attr_swap,  	[PERF_RECORD_HEADER_EVENT_TYPE]	  = perf_event__event_type_swap, @@ -523,14 +481,20 @@ static int flush_sample_queue(struct perf_session *s,  	struct perf_sample sample;  	u64 limit = os->next_flush;  	u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; -	unsigned idx = 0, progress_next = os->nr_samples / 16;  	bool show_progress = limit == ULLONG_MAX; +	struct ui_progress prog;  	int ret;  	if (!tool->ordered_samples || !limit)  		return 0; +	if (show_progress) +		ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); +  	list_for_each_entry_safe(iter, tmp, head, list) { +		if (session_done()) +			return 0; +  		if (iter->timestamp > limit)  			break; @@ -547,11 +511,9 @@ static int flush_sample_queue(struct perf_session *s,  		os->last_flush = iter->timestamp;  		list_del(&iter->list);  		list_add(&iter->list, &os->sample_cache); -		if (show_progress && (++idx >= progress_next)) { -			progress_next += os->nr_samples / 16; -			ui_progress__update(idx, os->nr_samples, -					    "Processing time ordered events..."); -		} + +		if (show_progress) +			ui_progress__update(&prog, 1);  	}  	if (list_empty(head)) { @@ -740,11 +702,12 @@ static void regs_dump__printf(u64 mask, u64 *regs)  	}  } -static void regs_user__printf(struct perf_sample *sample, u64 mask) +static void regs_user__printf(struct perf_sample *sample)  {  	struct regs_dump *user_regs = &sample->user_regs;  	if (user_regs->regs) { +		u64 mask = user_regs->mask;  		printf("... user regs: mask 0x%" PRIx64 "\n", mask);  		regs_dump__printf(mask, user_regs->regs);  	} @@ -831,7 +794,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,  	if (!dump_trace)  		return; -	printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n", +	printf("(IP, 0x%x): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n",  	       event->header.misc, sample->pid, sample->tid, sample->ip,  	       sample->period, sample->addr); @@ -844,7 +807,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,  		branch_stack__printf(sample);  	if (sample_type & PERF_SAMPLE_REGS_USER) -		regs_user__printf(sample, evsel->attr.sample_regs_user); +		regs_user__printf(sample);  	if (sample_type & PERF_SAMPLE_STACK_USER)  		stack_user__printf(&sample->user_stack); @@ -855,6 +818,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,  	if (sample_type & PERF_SAMPLE_DATA_SRC)  		printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); +	if (sample_type & PERF_SAMPLE_TRANSACTION) +		printf("... transaction: %" PRIx64 "\n", sample->transaction); +  	if (sample_type & PERF_SAMPLE_READ)  		sample_read__printf(sample, evsel->attr.read_format);  } @@ -865,6 +831,7 @@ static struct machine *  					       struct perf_sample *sample)  {  	const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; +	struct machine *machine;  	if (perf_guest &&  	    ((cpumode == PERF_RECORD_MISC_GUEST_KERNEL) || @@ -877,7 +844,11 @@ static struct machine *  		else  			pid = sample->pid; -		return perf_session__findnew_machine(session, pid); +		machine = perf_session__find_machine(session, pid); +		if (!machine) +			machine = perf_session__findnew_machine(session, +						DEFAULT_GUEST_KERNEL_ID); +		return machine;  	}  	return &session->machines.host; @@ -1026,6 +997,7 @@ static int perf_session_deliver_event(struct perf_session *session,  static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,  					    struct perf_tool *tool, u64 file_offset)  { +	int fd = perf_data_file__fd(session->file);  	int err;  	dump_event(session, event, file_offset, NULL); @@ -1037,9 +1009,15 @@ static int perf_session__process_user_event(struct perf_session *session, union  		if (err == 0)  			perf_session__set_id_hdr_size(session);  		return err; +	case PERF_RECORD_HEADER_EVENT_TYPE: +		/* +		 * Depreceated, but we need to handle it for sake +		 * of old data files create in pipe mode. +		 */ +		return 0;  	case PERF_RECORD_HEADER_TRACING_DATA:  		/* setup for reading amidst mmap */ -		lseek(session->fd, file_offset, SEEK_SET); +		lseek(fd, file_offset, SEEK_SET);  		return tool->tracing_data(tool, event, session);  	case PERF_RECORD_HEADER_BUILD_ID:  		return tool->build_id(tool, event, session); @@ -1096,11 +1074,11 @@ static int perf_session__process_event(struct perf_session *session,  					  file_offset);  } -void perf_event_header__bswap(struct perf_event_header *self) +void perf_event_header__bswap(struct perf_event_header *hdr)  { -	self->type = bswap_32(self->type); -	self->misc = bswap_16(self->misc); -	self->size = bswap_16(self->size); +	hdr->type = bswap_32(hdr->type); +	hdr->misc = bswap_16(hdr->misc); +	hdr->size = bswap_16(hdr->size);  }  struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) @@ -1108,11 +1086,11 @@ struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)  	return machine__findnew_thread(&session->machines.host, 0, pid);  } -static struct thread *perf_session__register_idle_thread(struct perf_session *self) +static struct thread *perf_session__register_idle_thread(struct perf_session *session)  { -	struct thread *thread = perf_session__findnew(self, 0); +	struct thread *thread = perf_session__findnew(session, 0); -	if (thread == NULL || thread__set_comm(thread, "swapper")) { +	if (thread == NULL || thread__set_comm(thread, "swapper", 0)) {  		pr_err("problem inserting idle task.\n");  		thread = NULL;  	} @@ -1160,18 +1138,18 @@ static void perf_session__warn_about_errors(const struct perf_session *session,  	}  } -#define session_done()	(*(volatile int *)(&session_done))  volatile int session_done; -static int __perf_session__process_pipe_events(struct perf_session *self, +static int __perf_session__process_pipe_events(struct perf_session *session,  					       struct perf_tool *tool)  { +	int fd = perf_data_file__fd(session->file);  	union perf_event *event;  	uint32_t size, cur_size = 0;  	void *buf = NULL;  	int skip = 0;  	u64 head; -	int err; +	ssize_t err;  	void *p;  	perf_tool__fill_defaults(tool); @@ -1184,7 +1162,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,  		return -errno;  more:  	event = buf; -	err = readn(self->fd, event, sizeof(struct perf_event_header)); +	err = readn(fd, event, sizeof(struct perf_event_header));  	if (err <= 0) {  		if (err == 0)  			goto done; @@ -1193,7 +1171,7 @@ more:  		goto out_err;  	} -	if (self->header.needs_swap) +	if (session->header.needs_swap)  		perf_event_header__bswap(&event->header);  	size = event->header.size; @@ -1216,7 +1194,7 @@ more:  	p += sizeof(struct perf_event_header);  	if (size - sizeof(struct perf_event_header)) { -		err = readn(self->fd, p, size - sizeof(struct perf_event_header)); +		err = readn(fd, p, size - sizeof(struct perf_event_header));  		if (err <= 0) {  			if (err == 0) {  				pr_err("unexpected end of event stream\n"); @@ -1228,7 +1206,7 @@ more:  		}  	} -	if ((skip = perf_session__process_event(self, event, tool, head)) < 0) { +	if ((skip = perf_session__process_event(session, event, tool, head)) < 0) {  		pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",  		       head, event->header.size, event->header.type);  		err = -EINVAL; @@ -1243,11 +1221,13 @@ more:  	if (!session_done())  		goto more;  done: -	err = 0; +	/* do the final flush for ordered samples */ +	session->ordered_samples.next_flush = ULLONG_MAX; +	err = flush_sample_queue(session, tool);  out_err:  	free(buf); -	perf_session__warn_about_errors(self, tool); -	perf_session_free_sample_buffers(self); +	perf_session__warn_about_errors(session, tool); +	perf_session_free_sample_buffers(session);  	return err;  } @@ -1295,12 +1275,14 @@ int __perf_session__process_events(struct perf_session *session,  				   u64 data_offset, u64 data_size,  				   u64 file_size, struct perf_tool *tool)  { -	u64 head, page_offset, file_offset, file_pos, progress_next; +	int fd = perf_data_file__fd(session->file); +	u64 head, page_offset, file_offset, file_pos;  	int err, mmap_prot, mmap_flags, map_idx = 0;  	size_t	mmap_size;  	char *buf, *mmaps[NUM_MMAPS];  	union perf_event *event;  	uint32_t size; +	struct ui_progress prog;  	perf_tool__fill_defaults(tool); @@ -1308,10 +1290,10 @@ int __perf_session__process_events(struct perf_session *session,  	file_offset = page_offset;  	head = data_offset - page_offset; -	if (data_offset + data_size < file_size) +	if (data_size && (data_offset + data_size < file_size))  		file_size = data_offset + data_size; -	progress_next = file_size / 16; +	ui_progress__init(&prog, file_size, "Processing events...");  	mmap_size = MMAP_SIZE;  	if (mmap_size > file_size) @@ -1327,7 +1309,7 @@ int __perf_session__process_events(struct perf_session *session,  		mmap_flags = MAP_PRIVATE;  	}  remap: -	buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, +	buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, fd,  		   file_offset);  	if (buf == MAP_FAILED) {  		pr_err("failed to mmap file\n"); @@ -1366,16 +1348,15 @@ more:  	head += size;  	file_pos += size; -	if (file_pos >= progress_next) { -		progress_next += file_size / 16; -		ui_progress__update(file_pos, file_size, -				    "Processing events..."); -	} +	ui_progress__update(&prog, size); + +	if (session_done()) +		goto out;  	if (file_pos < file_size)  		goto more; -	err = 0; +out:  	/* do the final flush for ordered samples */  	session->ordered_samples.next_flush = ULLONG_MAX;  	err = flush_sample_queue(session, tool); @@ -1386,21 +1367,22 @@ out_err:  	return err;  } -int perf_session__process_events(struct perf_session *self, +int perf_session__process_events(struct perf_session *session,  				 struct perf_tool *tool)  { +	u64 size = perf_data_file__size(session->file);  	int err; -	if (perf_session__register_idle_thread(self) == NULL) +	if (perf_session__register_idle_thread(session) == NULL)  		return -ENOMEM; -	if (!self->fd_pipe) -		err = __perf_session__process_events(self, -						     self->header.data_offset, -						     self->header.data_size, -						     self->size, tool); +	if (!perf_data_file__is_pipe(session->file)) +		err = __perf_session__process_events(session, +						     session->header.data_offset, +						     session->header.data_size, +						     size, tool);  	else -		err = __perf_session__process_pipe_events(self, tool); +		err = __perf_session__process_pipe_events(session, tool);  	return err;  } @@ -1409,7 +1391,7 @@ bool perf_session__has_traces(struct perf_session *session, const char *msg)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		if (evsel->attr.type == PERF_TYPE_TRACEPOINT)  			return true;  	} @@ -1449,15 +1431,15 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps,  	return 0;  } -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) +size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp)  { -	return machines__fprintf_dsos(&self->machines, fp); +	return machines__fprintf_dsos(&session->machines, fp);  } -size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, +size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp,  					  bool (skip)(struct dso *dso, int parm), int parm)  { -	return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm); +	return machines__fprintf_dsos_buildid(&session->machines, fp, skip, parm);  }  size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) @@ -1467,7 +1449,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)  	ret += events_stats__fprintf(&session->stats, fp); -	list_for_each_entry(pos, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, pos) {  		ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));  		ret += events_stats__fprintf(&pos->hists.stats, fp);  	} @@ -1489,56 +1471,63 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,  {  	struct perf_evsel *pos; -	list_for_each_entry(pos, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, pos) {  		if (pos->attr.type == type)  			return pos;  	}  	return NULL;  } -void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, -			  struct perf_sample *sample, struct machine *machine, +void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, +			  struct addr_location *al,  			  unsigned int print_opts, unsigned int stack_depth)  { -	struct addr_location al;  	struct callchain_cursor_node *node;  	int print_ip = print_opts & PRINT_IP_OPT_IP;  	int print_sym = print_opts & PRINT_IP_OPT_SYM;  	int print_dso = print_opts & PRINT_IP_OPT_DSO;  	int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET;  	int print_oneline = print_opts & PRINT_IP_OPT_ONELINE; +	int print_srcline = print_opts & PRINT_IP_OPT_SRCLINE;  	char s = print_oneline ? ' ' : '\t'; -	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { -		error("problem processing %d event, skipping it.\n", -			event->header.type); -		return; -	} -  	if (symbol_conf.use_callchain && sample->callchain) { +		struct addr_location node_al; -		if (machine__resolve_callchain(machine, evsel, al.thread, -					       sample, NULL, NULL) != 0) { +		if (machine__resolve_callchain(al->machine, evsel, al->thread, +					       sample, NULL, NULL, +					       PERF_MAX_STACK_DEPTH) != 0) {  			if (verbose)  				error("Failed to resolve callchain. Skipping\n");  			return;  		}  		callchain_cursor_commit(&callchain_cursor); +		if (print_symoffset) +			node_al = *al; +  		while (stack_depth) { +			u64 addr = 0; +  			node = callchain_cursor_current(&callchain_cursor);  			if (!node)  				break; +			if (node->sym && node->sym->ignore) +				goto next; +  			if (print_ip)  				printf("%c%16" PRIx64, s, node->ip); +			if (node->map) +				addr = node->map->map_ip(node->map, node->ip); +  			if (print_sym) {  				printf(" ");  				if (print_symoffset) { -					al.addr = node->ip; -					al.map  = node->map; -					symbol__fprintf_symname_offs(node->sym, &al, stdout); +					node_al.addr = addr; +					node_al.map  = node->map; +					symbol__fprintf_symname_offs(node->sym, &node_al, stdout);  				} else  					symbol__fprintf_symname(node->sym, stdout);  			} @@ -1549,39 +1538,49 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,  				printf(")");  			} +			if (print_srcline) +				map__fprintf_srcline(node->map, addr, "\n  ", +						     stdout); +  			if (!print_oneline)  				printf("\n"); -			callchain_cursor_advance(&callchain_cursor); -  			stack_depth--; +next: +			callchain_cursor_advance(&callchain_cursor);  		}  	} else { +		if (al->sym && al->sym->ignore) +			return; +  		if (print_ip)  			printf("%16" PRIx64, sample->ip);  		if (print_sym) {  			printf(" ");  			if (print_symoffset) -				symbol__fprintf_symname_offs(al.sym, &al, +				symbol__fprintf_symname_offs(al->sym, al,  							     stdout);  			else -				symbol__fprintf_symname(al.sym, stdout); +				symbol__fprintf_symname(al->sym, stdout);  		}  		if (print_dso) {  			printf(" ("); -			map__fprintf_dsoname(al.map, stdout); +			map__fprintf_dsoname(al->map, stdout);  			printf(")");  		} + +		if (print_srcline) +			map__fprintf_srcline(al->map, al->addr, "\n  ", stdout);  	}  }  int perf_session__cpu_bitmap(struct perf_session *session,  			     const char *cpu_list, unsigned long *cpu_bitmap)  { -	int i; +	int i, err = -1;  	struct cpu_map *map;  	for (i = 0; i < PERF_TYPE_MAX; ++i) { @@ -1610,25 +1609,31 @@ int perf_session__cpu_bitmap(struct perf_session *session,  		if (cpu >= MAX_NR_CPUS) {  			pr_err("Requested CPU %d too large. "  			       "Consider raising MAX_NR_CPUS\n", cpu); -			return -1; +			goto out_delete_map;  		}  		set_bit(cpu, cpu_bitmap);  	} -	return 0; +	err = 0; + +out_delete_map: +	cpu_map__delete(map); +	return err;  }  void perf_session__fprintf_info(struct perf_session *session, FILE *fp,  				bool full)  {  	struct stat st; -	int ret; +	int fd, ret;  	if (session == NULL || fp == NULL)  		return; -	ret = fstat(session->fd, &st); +	fd = perf_data_file__fd(session->file); + +	ret = fstat(fd, &st);  	if (ret == -1)  		return; @@ -1657,9 +1662,9 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,  			continue;  		err = -EEXIST; -		if (evsel->handler.func != NULL) +		if (evsel->handler != NULL)  			goto out; -		evsel->handler.func = assocs[i].handler; +		evsel->handler = assocs[i].handler;  	}  	err = 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 3aa75fb2225..3140f8ae614 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -1,12 +1,14 @@  #ifndef __PERF_SESSION_H  #define __PERF_SESSION_H +#include "trace-event.h"  #include "hist.h"  #include "event.h"  #include "header.h"  #include "machine.h"  #include "symbol.h"  #include "thread.h" +#include "data.h"  #include <linux/rbtree.h>  #include <linux/perf_event.h> @@ -29,16 +31,13 @@ struct ordered_samples {  struct perf_session {  	struct perf_header	header; -	unsigned long		size;  	struct machines		machines;  	struct perf_evlist	*evlist; -	struct pevent		*pevent; +	struct trace_event	tevent;  	struct events_stats	stats; -	int			fd; -	bool			fd_pipe;  	bool			repipe;  	struct ordered_samples	ordered_samples; -	char			filename[1]; +	struct perf_data_file	*file;  };  #define PRINT_IP_OPT_IP		(1<<0) @@ -46,20 +45,20 @@ struct perf_session {  #define PRINT_IP_OPT_DSO		(1<<2)  #define PRINT_IP_OPT_SYMOFFSET	(1<<3)  #define PRINT_IP_OPT_ONELINE	(1<<4) +#define PRINT_IP_OPT_SRCLINE	(1<<5)  struct perf_tool; -struct perf_session *perf_session__new(const char *filename, int mode, -				       bool force, bool repipe, -				       struct perf_tool *tool); +struct perf_session *perf_session__new(struct perf_data_file *file, +				       bool repipe, struct perf_tool *tool);  void perf_session__delete(struct perf_session *session); -void perf_event_header__bswap(struct perf_event_header *self); +void perf_event_header__bswap(struct perf_event_header *hdr); -int __perf_session__process_events(struct perf_session *self, +int __perf_session__process_events(struct perf_session *session,  				   u64 data_offset, u64 data_size, u64 size,  				   struct perf_tool *tool); -int perf_session__process_events(struct perf_session *self, +int perf_session__process_events(struct perf_session *session,  				 struct perf_tool *tool);  int perf_session_queue_event(struct perf_session *s, union perf_event *event, @@ -67,37 +66,36 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event,  void perf_tool__fill_defaults(struct perf_tool *tool); -int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel, +int perf_session__resolve_callchain(struct perf_session *session, +				    struct perf_evsel *evsel,  				    struct thread *thread,  				    struct ip_callchain *chain,  				    struct symbol **parent); -bool perf_session__has_traces(struct perf_session *self, const char *msg); +bool perf_session__has_traces(struct perf_session *session, const char *msg); -void mem_bswap_64(void *src, int byte_size); -void mem_bswap_32(void *src, int byte_size);  void perf_event__attr_swap(struct perf_event_attr *attr); -int perf_session__create_kernel_maps(struct perf_session *self); +int perf_session__create_kernel_maps(struct perf_session *session);  void perf_session__set_id_hdr_size(struct perf_session *session);  static inline -struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) +struct machine *perf_session__find_machine(struct perf_session *session, pid_t pid)  { -	return machines__find(&self->machines, pid); +	return machines__find(&session->machines, pid);  }  static inline -struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) +struct machine *perf_session__findnew_machine(struct perf_session *session, pid_t pid)  { -	return machines__findnew(&self->machines, pid); +	return machines__findnew(&session->machines, pid);  } -struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); -size_t perf_session__fprintf(struct perf_session *self, FILE *fp); +struct thread *perf_session__findnew(struct perf_session *session, pid_t pid); +size_t perf_session__fprintf(struct perf_session *session, FILE *fp); -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); +size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp);  size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp,  					  bool (fn)(struct dso *dso, int parm), int parm); @@ -107,8 +105,8 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);  struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,  					    unsigned int type); -void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, -			  struct perf_sample *sample, struct machine *machine, +void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, +			  struct addr_location *al,  			  unsigned int print_opts, unsigned int stack_depth);  int perf_session__cpu_bitmap(struct perf_session *session, @@ -124,4 +122,8 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,  #define perf_session__set_tracepoints_handlers(session, array) \  	__perf_session__set_tracepoints_handlers(session, array, ARRAY_SIZE(array)) + +extern volatile int session_done; + +#define session_done()	(*(volatile int *)(&session_done))  #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 58ea5ca6c25..d0aee4b9dfd 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -25,7 +25,7 @@ cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter'  build_lib = getenv('PYTHON_EXTBUILD_LIB')  build_tmp = getenv('PYTHON_EXTBUILD_TMP')  libtraceevent = getenv('LIBTRACEEVENT') -liblk = getenv('LIBLK') +libapikfs = getenv('LIBAPIKFS')  ext_sources = [f.strip() for f in file('util/python-ext-sources')  				if len(f.strip()) > 0 and f[0] != '#'] @@ -34,7 +34,7 @@ perf = Extension('perf',  		  sources = ext_sources,  		  include_dirs = ['util/include'],  		  extra_compile_args = cflags, -		  extra_objects = [libtraceevent, liblk], +		  extra_objects = [libtraceevent, libapikfs],                   )  setup(name='perf', diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 5f118a08951..1ec57dd8228 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,22 +1,28 @@ +#include <sys/mman.h>  #include "sort.h"  #include "hist.h" +#include "comm.h"  #include "symbol.h" +#include "evsel.h"  regex_t		parent_regex;  const char	default_parent_pattern[] = "^sys_|^do_page_fault";  const char	*parent_pattern = default_parent_pattern;  const char	default_sort_order[] = "comm,dso,symbol"; -const char	*sort_order = default_sort_order; +const char	default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to"; +const char	default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; +const char	default_top_sort_order[] = "dso,symbol"; +const char	default_diff_sort_order[] = "dso,symbol"; +const char	*sort_order; +const char	*field_order;  regex_t		ignore_callees_regex;  int		have_ignore_callees = 0;  int		sort__need_collapse = 0;  int		sort__has_parent = 0;  int		sort__has_sym = 0; +int		sort__has_dso = 0;  enum sort_mode	sort__mode = SORT_MODE__NORMAL; -enum sort_type	sort__first_dimension; - -LIST_HEAD(hist_entry__sort_list);  static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)  { @@ -42,7 +48,7 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)  	return n;  } -static int64_t cmp_null(void *l, void *r) +static int64_t cmp_null(const void *l, const void *r)  {  	if (!l && !r)  		return 0; @@ -60,11 +66,12 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)  	return right->thread->tid - left->thread->tid;  } -static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { +	const char *comm = thread__comm_str(he->thread);  	return repsep_snprintf(bf, size, "%*s:%5d", width - 6, -			      self->thread->comm ?: "", self->thread->tid); +			       comm ?: "", he->thread->tid);  }  struct sort_entry sort_thread = { @@ -79,31 +86,34 @@ struct sort_entry sort_thread = {  static int64_t  sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return right->thread->tid - left->thread->tid; +	/* Compare the addr that should be unique among comm */ +	return comm__str(right->comm) - comm__str(left->comm);  }  static int64_t  sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)  { -	char *comm_l = left->thread->comm; -	char *comm_r = right->thread->comm; - -	if (!comm_l || !comm_r) -		return cmp_null(comm_l, comm_r); +	/* Compare the addr that should be unique among comm */ +	return comm__str(right->comm) - comm__str(left->comm); +} -	return strcmp(comm_l, comm_r); +static int64_t +sort__comm_sort(struct hist_entry *left, struct hist_entry *right) +{ +	return strcmp(comm__str(right->comm), comm__str(left->comm));  } -static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,  				     size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); +	return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm));  }  struct sort_entry sort_comm = {  	.se_header	= "Command",  	.se_cmp		= sort__comm_cmp,  	.se_collapse	= sort__comm_collapse, +	.se_sort	= sort__comm_sort,  	.se_snprintf	= hist_entry__comm_snprintf,  	.se_width_idx	= HISTC_COMM,  }; @@ -117,7 +127,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)  	const char *dso_name_l, *dso_name_r;  	if (!dso_l || !dso_r) -		return cmp_null(dso_l, dso_r); +		return cmp_null(dso_r, dso_l);  	if (verbose) {  		dso_name_l = dso_l->long_name; @@ -133,7 +143,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)  static int64_t  sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return _sort__dso_cmp(left->ms.map, right->ms.map); +	return _sort__dso_cmp(right->ms.map, left->ms.map);  }  static int _hist_entry__dso_snprintf(struct map *map, char *bf, @@ -148,10 +158,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf,  	return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");  } -static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); +	return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);  }  struct sort_entry sort_dso = { @@ -163,6 +173,11 @@ struct sort_entry sort_dso = {  /* --sort symbol */ +static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip) +{ +	return (int64_t)(right_ip - left_ip); +} +  static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)  {  	u64 ip_l, ip_r; @@ -182,12 +197,33 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)  static int64_t  sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)  { +	int64_t ret; +  	if (!left->ms.sym && !right->ms.sym) -		return right->level - left->level; +		return _sort__addr_cmp(left->ip, right->ip); + +	/* +	 * comparing symbol address alone is not enough since it's a +	 * relative address within a dso. +	 */ +	if (!sort__has_dso) { +		ret = sort__dso_cmp(left, right); +		if (ret != 0) +			return ret; +	}  	return _sort__sym_cmp(left->ms.sym, right->ms.sym);  } +static int64_t +sort__sym_sort(struct hist_entry *left, struct hist_entry *right) +{ +	if (!left->ms.sym || !right->ms.sym) +		return cmp_null(left->ms.sym, right->ms.sym); + +	return strcmp(right->ms.sym->name, left->ms.sym->name); +} +  static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,  				     u64 ip, char level, char *bf, size_t size,  				     unsigned int width) @@ -224,16 +260,17 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,  	return ret;  } -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, -					 self->level, bf, size, width); +	return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip, +					 he->level, bf, size, width);  }  struct sort_entry sort_sym = {  	.se_header	= "Symbol",  	.se_cmp		= sort__sym_cmp, +	.se_sort	= sort__sym_sort,  	.se_snprintf	= hist_entry__sym_snprintf,  	.se_width_idx	= HISTC_SYMBOL,  }; @@ -243,50 +280,32 @@ struct sort_entry sort_sym = {  static int64_t  sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return (int64_t)(right->ip - left->ip); +	if (!left->srcline) { +		if (!left->ms.map) +			left->srcline = SRCLINE_UNKNOWN; +		else { +			struct map *map = left->ms.map; +			left->srcline = get_srcline(map->dso, +					    map__rip_2objdump(map, left->ip)); +		} +	} +	if (!right->srcline) { +		if (!right->ms.map) +			right->srcline = SRCLINE_UNKNOWN; +		else { +			struct map *map = right->ms.map; +			right->srcline = get_srcline(map->dso, +					    map__rip_2objdump(map, right->ip)); +		} +	} +	return strcmp(right->srcline, left->srcline);  } -static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,  					size_t size,  					unsigned int width __maybe_unused)  { -	FILE *fp = NULL; -	char cmd[PATH_MAX + 2], *path = self->srcline, *nl; -	size_t line_len; - -	if (path != NULL) -		goto out_path; - -	if (!self->ms.map) -		goto out_ip; - -	if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) -		goto out_ip; - -	snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, -		 self->ms.map->dso->long_name, self->ip); -	fp = popen(cmd, "r"); -	if (!fp) -		goto out_ip; - -	if (getline(&path, &line_len, fp) < 0 || !line_len) -		goto out_ip; -	self->srcline = strdup(path); -	if (self->srcline == NULL) -		goto out_ip; - -	nl = strchr(self->srcline, '\n'); -	if (nl != NULL) -		*nl = '\0'; -	path = self->srcline; -out_path: -	if (fp) -		pclose(fp); -	return repsep_snprintf(bf, size, "%s", path); -out_ip: -	if (fp) -		pclose(fp); -	return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); +	return repsep_snprintf(bf, size, "%s", he->srcline);  }  struct sort_entry sort_srcline = { @@ -307,14 +326,14 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)  	if (!sym_l || !sym_r)  		return cmp_null(sym_l, sym_r); -	return strcmp(sym_l->name, sym_r->name); +	return strcmp(sym_r->name, sym_l->name);  } -static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  {  	return repsep_snprintf(bf, size, "%-*s", width, -			      self->parent ? self->parent->name : "[other]"); +			      he->parent ? he->parent->name : "[other]");  }  struct sort_entry sort_parent = { @@ -332,10 +351,10 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)  	return right->cpu - left->cpu;  } -static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, -				       size_t size, unsigned int width) +static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%*d", width, self->cpu); +	return repsep_snprintf(bf, size, "%*d", width, he->cpu);  }  struct sort_entry sort_cpu = { @@ -354,10 +373,10 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)  			      right->branch_info->from.map);  } -static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->branch_info->from.map, +	return _hist_entry__dso_snprintf(he->branch_info->from.map,  					 bf, size, width);  } @@ -368,10 +387,10 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)  			      right->branch_info->to.map);  } -static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->branch_info->to.map, +	return _hist_entry__dso_snprintf(he->branch_info->to.map,  					 bf, size, width);  } @@ -382,7 +401,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)  	struct addr_map_symbol *from_r = &right->branch_info->from;  	if (!from_l->sym && !from_r->sym) -		return right->level - left->level; +		return _sort__addr_cmp(from_l->addr, from_r->addr);  	return _sort__sym_cmp(from_l->sym, from_r->sym);  } @@ -394,26 +413,26 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)  	struct addr_map_symbol *to_r = &right->branch_info->to;  	if (!to_l->sym && !to_r->sym) -		return right->level - left->level; +		return _sort__addr_cmp(to_l->addr, to_r->addr);  	return _sort__sym_cmp(to_l->sym, to_r->sym);  } -static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,  					 size_t size, unsigned int width)  { -	struct addr_map_symbol *from = &self->branch_info->from; +	struct addr_map_symbol *from = &he->branch_info->from;  	return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, -					 self->level, bf, size, width); +					 he->level, bf, size, width);  } -static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { -	struct addr_map_symbol *to = &self->branch_info->to; +	struct addr_map_symbol *to = &he->branch_info->to;  	return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, -					 self->level, bf, size, width); +					 he->level, bf, size, width);  } @@ -456,13 +475,13 @@ sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)  	return mp || p;  } -static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width){  	static const char *out = "N/A"; -	if (self->branch_info->flags.predicted) +	if (he->branch_info->flags.predicted)  		out = "N"; -	else if (self->branch_info->flags.mispred) +	else if (he->branch_info->flags.mispred)  		out = "Y";  	return repsep_snprintf(bf, size, "%-*s", width, out); @@ -482,19 +501,19 @@ sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)  	return (int64_t)(r - l);  } -static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	uint64_t addr = 0;  	struct map *map = NULL;  	struct symbol *sym = NULL; -	if (self->mem_info) { -		addr = self->mem_info->daddr.addr; -		map = self->mem_info->daddr.map; -		sym = self->mem_info->daddr.sym; +	if (he->mem_info) { +		addr = he->mem_info->daddr.addr; +		map = he->mem_info->daddr.map; +		sym = he->mem_info->daddr.sym;  	} -	return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, +	return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,  					 width);  } @@ -512,13 +531,13 @@ sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)  	return _sort__dso_cmp(map_l, map_r);  } -static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	struct map *map = NULL; -	if (self->mem_info) -		map = self->mem_info->daddr.map; +	if (he->mem_info) +		map = he->mem_info->daddr.map;  	return _hist_entry__dso_snprintf(map, bf, size, width);  } @@ -542,14 +561,14 @@ sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)  	return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);  } -static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	const char *out;  	u64 mask = PERF_MEM_LOCK_NA; -	if (self->mem_info) -		mask = self->mem_info->data_src.mem_lock; +	if (he->mem_info) +		mask = he->mem_info->data_src.mem_lock;  	if (mask & PERF_MEM_LOCK_NA)  		out = "N/A"; @@ -591,7 +610,7 @@ static const char * const tlb_access[] = {  };  #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) -static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -602,8 +621,8 @@ static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,  	out[0] = '\0'; -	if (self->mem_info) -		m = self->mem_info->data_src.mem_dtlb; +	if (he->mem_info) +		m = he->mem_info->data_src.mem_dtlb;  	hit = m & PERF_MEM_TLB_HIT;  	miss = m & PERF_MEM_TLB_MISS; @@ -668,7 +687,7 @@ static const char * const mem_lvl[] = {  };  #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) -static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -677,8 +696,8 @@ static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,  	u64 m =  PERF_MEM_LVL_NA;  	u64 hit, miss; -	if (self->mem_info) -		m  = self->mem_info->data_src.mem_lvl; +	if (he->mem_info) +		m  = he->mem_info->data_src.mem_lvl;  	out[0] = '\0'; @@ -736,7 +755,7 @@ static const char * const snoop_access[] = {  };  #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) -static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -746,8 +765,8 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,  	out[0] = '\0'; -	if (self->mem_info) -		m = self->mem_info->data_src.mem_snoop; +	if (he->mem_info) +		m = he->mem_info->data_src.mem_snoop;  	for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {  		if (!(m & 0x1)) @@ -766,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,  	return repsep_snprintf(bf, size, "%-*s", width, out);  } +static inline  u64 cl_address(u64 address) +{ +	/* return the cacheline of the address */ +	return (address & ~(cacheline_size - 1)); +} + +static int64_t +sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	u64 l, r; +	struct map *l_map, *r_map; + +	if (!left->mem_info)  return -1; +	if (!right->mem_info) return 1; + +	/* group event types together */ +	if (left->cpumode > right->cpumode) return -1; +	if (left->cpumode < right->cpumode) return 1; + +	l_map = left->mem_info->daddr.map; +	r_map = right->mem_info->daddr.map; + +	/* if both are NULL, jump to sort on al_addr instead */ +	if (!l_map && !r_map) +		goto addr; + +	if (!l_map) return -1; +	if (!r_map) return 1; + +	if (l_map->maj > r_map->maj) return -1; +	if (l_map->maj < r_map->maj) return 1; + +	if (l_map->min > r_map->min) return -1; +	if (l_map->min < r_map->min) return 1; + +	if (l_map->ino > r_map->ino) return -1; +	if (l_map->ino < r_map->ino) return 1; + +	if (l_map->ino_generation > r_map->ino_generation) return -1; +	if (l_map->ino_generation < r_map->ino_generation) return 1; + +	/* +	 * Addresses with no major/minor numbers are assumed to be +	 * anonymous in userspace.  Sort those on pid then address. +	 * +	 * The kernel and non-zero major/minor mapped areas are +	 * assumed to be unity mapped.  Sort those on address. +	 */ + +	if ((left->cpumode != PERF_RECORD_MISC_KERNEL) && +	    (!(l_map->flags & MAP_SHARED)) && +	    !l_map->maj && !l_map->min && !l_map->ino && +	    !l_map->ino_generation) { +		/* userspace anonymous */ + +		if (left->thread->pid_ > right->thread->pid_) return -1; +		if (left->thread->pid_ < right->thread->pid_) return 1; +	} + +addr: +	/* al_addr does all the right addr - start + offset calculations */ +	l = cl_address(left->mem_info->daddr.al_addr); +	r = cl_address(right->mem_info->daddr.al_addr); + +	if (l > r) return -1; +	if (l < r) return 1; + +	return 0; +} + +static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, +					  size_t size, unsigned int width) +{ + +	uint64_t addr = 0; +	struct map *map = NULL; +	struct symbol *sym = NULL; +	char level = he->level; + +	if (he->mem_info) { +		addr = cl_address(he->mem_info->daddr.al_addr); +		map = he->mem_info->daddr.map; +		sym = he->mem_info->daddr.sym; + +		/* print [s] for shared data mmaps */ +		if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && +		     map && (map->type == MAP__VARIABLE) && +		    (map->flags & MAP_SHARED) && +		    (map->maj || map->min || map->ino || +		     map->ino_generation)) +			level = 's'; +		else if (!map) +			level = 'X'; +	} +	return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size, +					 width); +} +  struct sort_entry sort_mispredict = {  	.se_header	= "Branch Mispredicted",  	.se_cmp		= sort__mispredict_cmp, @@ -784,10 +901,10 @@ sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)  	return he_weight(left) - he_weight(right);  } -static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); +	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));  }  struct sort_entry sort_local_weight = { @@ -803,10 +920,10 @@ sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)  	return left->stat.weight - right->stat.weight;  } -static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,  					      size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); +	return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);  }  struct sort_entry sort_global_weight = { @@ -858,6 +975,134 @@ struct sort_entry sort_mem_snoop = {  	.se_width_idx	= HISTC_MEM_SNOOP,  }; +struct sort_entry sort_mem_dcacheline = { +	.se_header	= "Data Cacheline", +	.se_cmp		= sort__dcacheline_cmp, +	.se_snprintf	= hist_entry__dcacheline_snprintf, +	.se_width_idx	= HISTC_MEM_DCACHELINE, +}; + +static int64_t +sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->branch_info->flags.abort != +		right->branch_info->flags.abort; +} + +static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width) +{ +	static const char *out = "."; + +	if (he->branch_info->flags.abort) +		out = "A"; +	return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_abort = { +	.se_header	= "Transaction abort", +	.se_cmp		= sort__abort_cmp, +	.se_snprintf	= hist_entry__abort_snprintf, +	.se_width_idx	= HISTC_ABORT, +}; + +static int64_t +sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->branch_info->flags.in_tx != +		right->branch_info->flags.in_tx; +} + +static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width) +{ +	static const char *out = "."; + +	if (he->branch_info->flags.in_tx) +		out = "T"; + +	return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_in_tx = { +	.se_header	= "Branch in transaction", +	.se_cmp		= sort__in_tx_cmp, +	.se_snprintf	= hist_entry__in_tx_snprintf, +	.se_width_idx	= HISTC_IN_TX, +}; + +static int64_t +sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->transaction - right->transaction; +} + +static inline char *add_str(char *p, const char *str) +{ +	strcpy(p, str); +	return p + strlen(str); +} + +static struct txbit { +	unsigned flag; +	const char *name; +	int skip_for_len; +} txbits[] = { +	{ PERF_TXN_ELISION,        "EL ",        0 }, +	{ PERF_TXN_TRANSACTION,    "TX ",        1 }, +	{ PERF_TXN_SYNC,           "SYNC ",      1 }, +	{ PERF_TXN_ASYNC,          "ASYNC ",     0 }, +	{ PERF_TXN_RETRY,          "RETRY ",     0 }, +	{ PERF_TXN_CONFLICT,       "CON ",       0 }, +	{ PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, +	{ PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 }, +	{ 0, NULL, 0 } +}; + +int hist_entry__transaction_len(void) +{ +	int i; +	int len = 0; + +	for (i = 0; txbits[i].name; i++) { +		if (!txbits[i].skip_for_len) +			len += strlen(txbits[i].name); +	} +	len += 4; /* :XX<space> */ +	return len; +} + +static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, +					    size_t size, unsigned int width) +{ +	u64 t = he->transaction; +	char buf[128]; +	char *p = buf; +	int i; + +	buf[0] = 0; +	for (i = 0; txbits[i].name; i++) +		if (txbits[i].flag & t) +			p = add_str(p, txbits[i].name); +	if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) +		p = add_str(p, "NEITHER "); +	if (t & PERF_TXN_ABORT_MASK) { +		sprintf(p, ":%" PRIx64, +			(t & PERF_TXN_ABORT_MASK) >> +			PERF_TXN_ABORT_SHIFT); +		p += strlen(p); +	} + +	return repsep_snprintf(bf, size, "%-*s", width, buf); +} + +struct sort_entry sort_transaction = { +	.se_header	= "Transaction                ", +	.se_cmp		= sort__transaction_cmp, +	.se_snprintf	= hist_entry__transaction_snprintf, +	.se_width_idx	= HISTC_TRANSACTION, +}; +  struct sort_dimension {  	const char		*name;  	struct sort_entry	*entry; @@ -876,6 +1121,7 @@ static struct sort_dimension common_sort_dimensions[] = {  	DIM(SORT_SRCLINE, "srcline", sort_srcline),  	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),  	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), +	DIM(SORT_TRANSACTION, "transaction", sort_transaction),  };  #undef DIM @@ -888,6 +1134,8 @@ static struct sort_dimension bstack_sort_dimensions[] = {  	DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),  	DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),  	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), +	DIM(SORT_IN_TX, "in_tx", sort_in_tx), +	DIM(SORT_ABORT, "abort", sort_abort),  };  #undef DIM @@ -901,23 +1149,199 @@ static struct sort_dimension memory_sort_dimensions[] = {  	DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),  	DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),  	DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), +	DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),  };  #undef DIM -static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) +struct hpp_dimension { +	const char		*name; +	struct perf_hpp_fmt	*fmt; +	int			taken; +}; + +#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], } + +static struct hpp_dimension hpp_sort_dimensions[] = { +	DIM(PERF_HPP__OVERHEAD, "overhead"), +	DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"), +	DIM(PERF_HPP__OVERHEAD_US, "overhead_us"), +	DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"), +	DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"), +	DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"), +	DIM(PERF_HPP__SAMPLES, "sample"), +	DIM(PERF_HPP__PERIOD, "period"), +}; + +#undef DIM + +struct hpp_sort_entry { +	struct perf_hpp_fmt hpp; +	struct sort_entry *se; +}; + +bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)  { -	if (sd->taken) +	struct hpp_sort_entry *hse_a; +	struct hpp_sort_entry *hse_b; + +	if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) +		return false; + +	hse_a = container_of(a, struct hpp_sort_entry, hpp); +	hse_b = container_of(b, struct hpp_sort_entry, hpp); + +	return hse_a->se == hse_b->se; +} + +void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) +{ +	struct hpp_sort_entry *hse; + +	if (!perf_hpp__is_sort_entry(fmt))  		return; +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	hists__new_col_len(hists, hse->se->se_width_idx, +			   strlen(hse->se->se_header)); +} + +static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +			      struct perf_evsel *evsel) +{ +	struct hpp_sort_entry *hse; +	size_t len; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	len = hists__col_len(&evsel->hists, hse->se->se_width_idx); + +	return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header); +} + +static int __sort__hpp_width(struct perf_hpp_fmt *fmt, +			     struct perf_hpp *hpp __maybe_unused, +			     struct perf_evsel *evsel) +{ +	struct hpp_sort_entry *hse; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); + +	return hists__col_len(&evsel->hists, hse->se->se_width_idx); +} + +static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +			     struct hist_entry *he) +{ +	struct hpp_sort_entry *hse; +	size_t len; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	len = hists__col_len(he->hists, hse->se->se_width_idx); + +	return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); +} + +static struct hpp_sort_entry * +__sort_dimension__alloc_hpp(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse; + +	hse = malloc(sizeof(*hse)); +	if (hse == NULL) { +		pr_err("Memory allocation failed\n"); +		return NULL; +	} + +	hse->se = sd->entry; +	hse->hpp.header = __sort__hpp_header; +	hse->hpp.width = __sort__hpp_width; +	hse->hpp.entry = __sort__hpp_entry; +	hse->hpp.color = NULL; + +	hse->hpp.cmp = sd->entry->se_cmp; +	hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp; +	hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse; + +	INIT_LIST_HEAD(&hse->hpp.list); +	INIT_LIST_HEAD(&hse->hpp.sort_list); +	hse->hpp.elide = false; + +	return hse; +} + +bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) +{ +	return format->header == __sort__hpp_header; +} + +static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); + +	if (hse == NULL) +		return -1; + +	perf_hpp__register_sort_field(&hse->hpp); +	return 0; +} + +static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); + +	if (hse == NULL) +		return -1; + +	perf_hpp__column_register(&hse->hpp); +	return 0; +} + +static int __sort_dimension__add(struct sort_dimension *sd) +{ +	if (sd->taken) +		return 0; + +	if (__sort_dimension__add_hpp_sort(sd) < 0) +		return -1; +  	if (sd->entry->se_collapse)  		sort__need_collapse = 1; -	if (list_empty(&hist_entry__sort_list)) -		sort__first_dimension = idx; +	sd->taken = 1; + +	return 0; +} + +static int __hpp_dimension__add(struct hpp_dimension *hd) +{ +	if (!hd->taken) { +		hd->taken = 1; + +		perf_hpp__register_sort_field(hd->fmt); +	} +	return 0; +} + +static int __sort_dimension__add_output(struct sort_dimension *sd) +{ +	if (sd->taken) +		return 0; + +	if (__sort_dimension__add_hpp_output(sd) < 0) +		return -1; -	list_add_tail(&sd->entry->list, &hist_entry__sort_list);  	sd->taken = 1; +	return 0; +} + +static int __hpp_dimension__add_output(struct hpp_dimension *hd) +{ +	if (!hd->taken) { +		hd->taken = 1; + +		perf_hpp__column_register(hd->fmt); +	} +	return 0;  }  int sort_dimension__add(const char *tok) @@ -942,10 +1366,20 @@ int sort_dimension__add(const char *tok)  			sort__has_parent = 1;  		} else if (sd->entry == &sort_sym) {  			sort__has_sym = 1; +		} else if (sd->entry == &sort_dso) { +			sort__has_dso = 1;  		} -		__sort_dimension__add(sd, i); -		return 0; +		return __sort_dimension__add(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { +		struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + +		if (strncasecmp(tok, hd->name, strlen(tok))) +			continue; + +		return __hpp_dimension__add(hd);  	}  	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { @@ -960,7 +1394,7 @@ int sort_dimension__add(const char *tok)  		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)  			sort__has_sym = 1; -		__sort_dimension__add(sd, i + __SORT_BRANCH_STACK); +		__sort_dimension__add(sd);  		return 0;  	} @@ -976,18 +1410,47 @@ int sort_dimension__add(const char *tok)  		if (sd->entry == &sort_mem_daddr_sym)  			sort__has_sym = 1; -		__sort_dimension__add(sd, i + __SORT_MEMORY_MODE); +		__sort_dimension__add(sd);  		return 0;  	}  	return -ESRCH;  } -int setup_sorting(void) +static const char *get_default_sort_order(void) +{ +	const char *default_sort_orders[] = { +		default_sort_order, +		default_branch_sort_order, +		default_mem_sort_order, +		default_top_sort_order, +		default_diff_sort_order, +	}; + +	BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); + +	return default_sort_orders[sort__mode]; +} + +static int __setup_sorting(void)  { -	char *tmp, *tok, *str = strdup(sort_order); +	char *tmp, *tok, *str; +	const char *sort_keys = sort_order;  	int ret = 0; +	if (sort_keys == NULL) { +		if (field_order) { +			/* +			 * If user specified field order but no sort order, +			 * we'll honor it and not add default sort orders. +			 */ +			return 0; +		} + +		sort_keys = get_default_sort_order(); +	} + +	str = strdup(sort_keys);  	if (str == NULL) {  		error("Not enough memory to setup sort keys");  		return -ENOMEM; @@ -1009,53 +1472,235 @@ int setup_sorting(void)  	return ret;  } -static void sort_entry__setup_elide(struct sort_entry *self, -				    struct strlist *list, -				    const char *list_name, FILE *fp) +void perf_hpp__set_elide(int idx, bool elide) +{ +	struct perf_hpp_fmt *fmt; +	struct hpp_sort_entry *hse; + +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		hse = container_of(fmt, struct hpp_sort_entry, hpp); +		if (hse->se->se_width_idx == idx) { +			fmt->elide = elide; +			break; +		} +	} +} + +static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)  {  	if (list && strlist__nr_entries(list) == 1) {  		if (fp != NULL)  			fprintf(fp, "# %s: %s\n", list_name,  				strlist__entry(list, 0)->s); -		self->elide = true; +		return true;  	} +	return false; +} + +static bool get_elide(int idx, FILE *output) +{ +	switch (idx) { +	case HISTC_SYMBOL: +		return __get_elide(symbol_conf.sym_list, "symbol", output); +	case HISTC_DSO: +		return __get_elide(symbol_conf.dso_list, "dso", output); +	case HISTC_COMM: +		return __get_elide(symbol_conf.comm_list, "comm", output); +	default: +		break; +	} + +	if (sort__mode != SORT_MODE__BRANCH) +		return false; + +	switch (idx) { +	case HISTC_SYMBOL_FROM: +		return __get_elide(symbol_conf.sym_from_list, "sym_from", output); +	case HISTC_SYMBOL_TO: +		return __get_elide(symbol_conf.sym_to_list, "sym_to", output); +	case HISTC_DSO_FROM: +		return __get_elide(symbol_conf.dso_from_list, "dso_from", output); +	case HISTC_DSO_TO: +		return __get_elide(symbol_conf.dso_to_list, "dso_to", output); +	default: +		break; +	} + +	return false;  }  void sort__setup_elide(FILE *output)  { -	sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -				"dso", output); -	sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, -				"comm", output); -	sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, -				"symbol", output); - -	if (sort__mode == SORT_MODE__BRANCH) { -		sort_entry__setup_elide(&sort_dso_from, -					symbol_conf.dso_from_list, -					"dso_from", output); -		sort_entry__setup_elide(&sort_dso_to, -					symbol_conf.dso_to_list, -					"dso_to", output); -		sort_entry__setup_elide(&sort_sym_from, -					symbol_conf.sym_from_list, -					"sym_from", output); -		sort_entry__setup_elide(&sort_sym_to, -					symbol_conf.sym_to_list, -					"sym_to", output); -	} else if (sort__mode == SORT_MODE__MEMORY) { -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"symbol_daddr", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"dso_daddr", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"mem", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"local_weight", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"tlb", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"snoop", output); +	struct perf_hpp_fmt *fmt; +	struct hpp_sort_entry *hse; + +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		hse = container_of(fmt, struct hpp_sort_entry, hpp); +		fmt->elide = get_elide(hse->se->se_width_idx, output); +	} + +	/* +	 * It makes no sense to elide all of sort entries. +	 * Just revert them to show up again. +	 */ +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		if (!fmt->elide) +			return;  	} +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		fmt->elide = false; +	} +} + +static int output_field_add(char *tok) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { +		struct sort_dimension *sd = &common_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { +		struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + +		if (strncasecmp(tok, hd->name, strlen(tok))) +			continue; + +		return __hpp_dimension__add_output(hd); +	} + +	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { +		struct sort_dimension *sd = &bstack_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { +		struct sort_dimension *sd = &memory_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	return -ESRCH; +} + +static void reset_dimensions(void) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) +		common_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) +		hpp_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) +		bstack_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) +		memory_sort_dimensions[i].taken = 0; +} + +static int __setup_output_field(void) +{ +	char *tmp, *tok, *str; +	int ret = 0; + +	if (field_order == NULL) +		return 0; + +	reset_dimensions(); + +	str = strdup(field_order); +	if (str == NULL) { +		error("Not enough memory to setup output fields"); +		return -ENOMEM; +	} + +	for (tok = strtok_r(str, ", ", &tmp); +			tok; tok = strtok_r(NULL, ", ", &tmp)) { +		ret = output_field_add(tok); +		if (ret == -EINVAL) { +			error("Invalid --fields key: `%s'", tok); +			break; +		} else if (ret == -ESRCH) { +			error("Unknown --fields key: `%s'", tok); +			break; +		} +	} + +	free(str); +	return ret; +} + +int setup_sorting(void) +{ +	int err; + +	err = __setup_sorting(); +	if (err < 0) +		return err; + +	if (parent_pattern != default_parent_pattern) { +		err = sort_dimension__add("parent"); +		if (err < 0) +			return err; +	} + +	reset_dimensions(); + +	/* +	 * perf diff doesn't use default hpp output fields. +	 */ +	if (sort__mode != SORT_MODE__DIFF) +		perf_hpp__init(); + +	err = __setup_output_field(); +	if (err < 0) +		return err; + +	/* copy sort keys to output fields */ +	perf_hpp__setup_output_field(); +	/* and then copy output fields to sort keys */ +	perf_hpp__append_sort_keys(); + +	return 0; +} + +void reset_output_field(void) +{ +	sort__need_collapse = 0; +	sort__has_parent = 0; +	sort__has_sym = 0; +	sort__has_dso = 0; + +	field_order = NULL; +	sort_order = NULL; + +	reset_dimensions(); +	perf_hpp__reset_output_field();  } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 4e80dbd271e..041f0c9cea2 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -20,12 +20,12 @@  #include "parse-options.h"  #include "parse-events.h" - +#include "hist.h"  #include "thread.h" -#include "sort.h"  extern regex_t parent_regex;  extern const char *sort_order; +extern const char *field_order;  extern const char default_parent_pattern[];  extern const char *parent_pattern;  extern const char default_sort_order[]; @@ -82,10 +82,14 @@ struct hist_entry {  		struct list_head head;  	} pairs;  	struct he_stat		stat; +	struct he_stat		*stat_acc;  	struct map_symbol	ms;  	struct thread		*thread; +	struct comm		*comm;  	u64			ip; +	u64			transaction;  	s32			cpu; +	u8			cpumode;  	struct hist_entry_diff	diff; @@ -128,10 +132,27 @@ static inline void hist_entry__add_pair(struct hist_entry *pair,  	list_add_tail(&pair->pairs.node, &he->pairs.head);  } +static inline float hist_entry__get_percent_limit(struct hist_entry *he) +{ +	u64 period = he->stat.period; +	u64 total_period = hists__total_period(he->hists); + +	if (unlikely(total_period == 0)) +		return 0; + +	if (symbol_conf.cumulate_callchain) +		period = he->stat_acc->period; + +	return period * 100.0 / total_period; +} + +  enum sort_mode {  	SORT_MODE__NORMAL,  	SORT_MODE__BRANCH,  	SORT_MODE__MEMORY, +	SORT_MODE__TOP, +	SORT_MODE__DIFF,  };  enum sort_type { @@ -145,6 +166,7 @@ enum sort_type {  	SORT_SRCLINE,  	SORT_LOCAL_WEIGHT,  	SORT_GLOBAL_WEIGHT, +	SORT_TRANSACTION,  	/* branch stack specific sort keys */  	__SORT_BRANCH_STACK, @@ -153,6 +175,8 @@ enum sort_type {  	SORT_SYM_FROM,  	SORT_SYM_TO,  	SORT_MISPREDICT, +	SORT_ABORT, +	SORT_IN_TX,  	/* memory mode specific sort keys */  	__SORT_MEMORY_MODE, @@ -162,6 +186,7 @@ enum sort_type {  	SORT_MEM_TLB,  	SORT_MEM_LVL,  	SORT_MEM_SNOOP, +	SORT_MEM_DCACHELINE,  };  /* @@ -175,18 +200,21 @@ struct sort_entry {  	int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);  	int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); -	int	(*se_snprintf)(struct hist_entry *self, char *bf, size_t size, +	int64_t	(*se_sort)(struct hist_entry *, struct hist_entry *); +	int	(*se_snprintf)(struct hist_entry *he, char *bf, size_t size,  			       unsigned int width);  	u8	se_width_idx; -	bool	elide;  };  extern struct sort_entry sort_thread;  extern struct list_head hist_entry__sort_list;  int setup_sorting(void); +int setup_output_field(void); +void reset_output_field(void);  extern int sort_dimension__add(const char *);  void sort__setup_elide(FILE *fp); +void perf_hpp__set_elide(int idx, bool elide);  int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c new file mode 100644 index 00000000000..f3e4bc5fe5d --- /dev/null +++ b/tools/perf/util/srcline.c @@ -0,0 +1,299 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <linux/kernel.h> + +#include "util/dso.h" +#include "util/util.h" +#include "util/debug.h" + +#ifdef HAVE_LIBBFD_SUPPORT + +/* + * Implement addr2line using libbfd. + */ +#define PACKAGE "perf" +#include <bfd.h> + +struct a2l_data { +	const char 	*input; +	unsigned long 	addr; + +	bool 		found; +	const char 	*filename; +	const char 	*funcname; +	unsigned 	line; + +	bfd 		*abfd; +	asymbol 	**syms; +}; + +static int bfd_error(const char *string) +{ +	const char *errmsg; + +	errmsg = bfd_errmsg(bfd_get_error()); +	fflush(stdout); + +	if (string) +		pr_debug("%s: %s\n", string, errmsg); +	else +		pr_debug("%s\n", errmsg); + +	return -1; +} + +static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) +{ +	long storage; +	long symcount; +	asymbol **syms; +	bfd_boolean dynamic = FALSE; + +	if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) +		return bfd_error(bfd_get_filename(abfd)); + +	storage = bfd_get_symtab_upper_bound(abfd); +	if (storage == 0L) { +		storage = bfd_get_dynamic_symtab_upper_bound(abfd); +		dynamic = TRUE; +	} +	if (storage < 0L) +		return bfd_error(bfd_get_filename(abfd)); + +	syms = malloc(storage); +	if (dynamic) +		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); +	else +		symcount = bfd_canonicalize_symtab(abfd, syms); + +	if (symcount < 0) { +		free(syms); +		return bfd_error(bfd_get_filename(abfd)); +	} + +	a2l->syms = syms; +	return 0; +} + +static void find_address_in_section(bfd *abfd, asection *section, void *data) +{ +	bfd_vma pc, vma; +	bfd_size_type size; +	struct a2l_data *a2l = data; + +	if (a2l->found) +		return; + +	if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) +		return; + +	pc = a2l->addr; +	vma = bfd_get_section_vma(abfd, section); +	size = bfd_get_section_size(section); + +	if (pc < vma || pc >= vma + size) +		return; + +	a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, +					   &a2l->filename, &a2l->funcname, +					   &a2l->line); +} + +static struct a2l_data *addr2line_init(const char *path) +{ +	bfd *abfd; +	struct a2l_data *a2l = NULL; + +	abfd = bfd_openr(path, NULL); +	if (abfd == NULL) +		return NULL; + +	if (!bfd_check_format(abfd, bfd_object)) +		goto out; + +	a2l = zalloc(sizeof(*a2l)); +	if (a2l == NULL) +		goto out; + +	a2l->abfd = abfd; +	a2l->input = strdup(path); +	if (a2l->input == NULL) +		goto out; + +	if (slurp_symtab(abfd, a2l)) +		goto out; + +	return a2l; + +out: +	if (a2l) { +		zfree((char **)&a2l->input); +		free(a2l); +	} +	bfd_close(abfd); +	return NULL; +} + +static void addr2line_cleanup(struct a2l_data *a2l) +{ +	if (a2l->abfd) +		bfd_close(a2l->abfd); +	zfree((char **)&a2l->input); +	zfree(&a2l->syms); +	free(a2l); +} + +static int addr2line(const char *dso_name, unsigned long addr, +		     char **file, unsigned int *line, struct dso *dso) +{ +	int ret = 0; +	struct a2l_data *a2l = dso->a2l; + +	if (!a2l) { +		dso->a2l = addr2line_init(dso_name); +		a2l = dso->a2l; +	} + +	if (a2l == NULL) { +		pr_warning("addr2line_init failed for %s\n", dso_name); +		return 0; +	} + +	a2l->addr = addr; +	a2l->found = false; + +	bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); + +	if (a2l->found && a2l->filename) { +		*file = strdup(a2l->filename); +		*line = a2l->line; + +		if (*file) +			ret = 1; +	} + +	return ret; +} + +void dso__free_a2l(struct dso *dso) +{ +	struct a2l_data *a2l = dso->a2l; + +	if (!a2l) +		return; + +	addr2line_cleanup(a2l); + +	dso->a2l = NULL; +} + +#else /* HAVE_LIBBFD_SUPPORT */ + +static int addr2line(const char *dso_name, unsigned long addr, +		     char **file, unsigned int *line_nr, +		     struct dso *dso __maybe_unused) +{ +	FILE *fp; +	char cmd[PATH_MAX]; +	char *filename = NULL; +	size_t len; +	char *sep; +	int ret = 0; + +	scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, +		  dso_name, addr); + +	fp = popen(cmd, "r"); +	if (fp == NULL) { +		pr_warning("popen failed for %s\n", dso_name); +		return 0; +	} + +	if (getline(&filename, &len, fp) < 0 || !len) { +		pr_warning("addr2line has no output for %s\n", dso_name); +		goto out; +	} + +	sep = strchr(filename, '\n'); +	if (sep) +		*sep = '\0'; + +	if (!strcmp(filename, "??:0")) { +		pr_debug("no debugging info in %s\n", dso_name); +		free(filename); +		goto out; +	} + +	sep = strchr(filename, ':'); +	if (sep) { +		*sep++ = '\0'; +		*file = filename; +		*line_nr = strtoul(sep, NULL, 0); +		ret = 1; +	} +out: +	pclose(fp); +	return ret; +} + +void dso__free_a2l(struct dso *dso __maybe_unused) +{ +} + +#endif /* HAVE_LIBBFD_SUPPORT */ + +/* + * Number of addr2line failures (without success) before disabling it for that + * dso. + */ +#define A2L_FAIL_LIMIT 123 + +char *get_srcline(struct dso *dso, unsigned long addr) +{ +	char *file = NULL; +	unsigned line = 0; +	char *srcline; +	const char *dso_name; + +	if (!dso->has_srcline) +		return SRCLINE_UNKNOWN; + +	if (dso->symsrc_filename) +		dso_name = dso->symsrc_filename; +	else +		dso_name = dso->long_name; + +	if (dso_name[0] == '[') +		goto out; + +	if (!strncmp(dso_name, "/tmp/perf-", 10)) +		goto out; + +	if (!addr2line(dso_name, addr, &file, &line, dso)) +		goto out; + +	if (asprintf(&srcline, "%s:%u", file, line) < 0) { +		free(file); +		goto out; +	} + +	dso->a2l_fails = 0; + +	free(file); +	return srcline; + +out: +	if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { +		dso->has_srcline = 0; +		dso__free_a2l(dso); +	} +	return SRCLINE_UNKNOWN; +} + +void free_srcline(char *srcline) +{ +	if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) +		free(srcline); +} diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index ae8ccd7227c..5667fc3e39c 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -1,7 +1,7 @@  #ifndef __PERF_STATS_H  #define __PERF_STATS_H -#include "types.h" +#include <linux/types.h>  struct stats  { diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index cfa906882e2..4abe23550c7 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c @@ -28,7 +28,7 @@ void strbuf_init(struct strbuf *sb, ssize_t hint)  void strbuf_release(struct strbuf *sb)  {  	if (sb->alloc) { -		free(sb->buf); +		zfree(&sb->buf);  		strbuf_init(sb, 0);  	}  } diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 834c8ebfe38..79a757a2a15 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -10,22 +10,22 @@ static const char *OP_not	= "!";	/* Logical NOT */  #define is_operator(c)	((c) == '|' || (c) == '&' || (c) == '!')  #define is_separator(c)	(is_operator(c) || (c) == '(' || (c) == ')') -static void strfilter_node__delete(struct strfilter_node *self) +static void strfilter_node__delete(struct strfilter_node *node)  { -	if (self) { -		if (self->p && !is_operator(*self->p)) -			free((char *)self->p); -		strfilter_node__delete(self->l); -		strfilter_node__delete(self->r); -		free(self); +	if (node) { +		if (node->p && !is_operator(*node->p)) +			zfree((char **)&node->p); +		strfilter_node__delete(node->l); +		strfilter_node__delete(node->r); +		free(node);  	}  } -void strfilter__delete(struct strfilter *self) +void strfilter__delete(struct strfilter *filter)  { -	if (self) { -		strfilter_node__delete(self->root); -		free(self); +	if (filter) { +		strfilter_node__delete(filter->root); +		free(filter);  	}  } @@ -62,15 +62,15 @@ static struct strfilter_node *strfilter_node__alloc(const char *op,  						    struct strfilter_node *l,  						    struct strfilter_node *r)  { -	struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node)); +	struct strfilter_node *node = zalloc(sizeof(*node)); -	if (ret) { -		ret->p = op; -		ret->l = l; -		ret->r = r; +	if (node) { +		node->p = op; +		node->l = l; +		node->r = r;  	} -	return ret; +	return node;  }  static struct strfilter_node *strfilter_node__new(const char *s, @@ -154,46 +154,46 @@ error:   */  struct strfilter *strfilter__new(const char *rules, const char **err)  { -	struct strfilter *ret = zalloc(sizeof(struct strfilter)); +	struct strfilter *filter = zalloc(sizeof(*filter));  	const char *ep = NULL; -	if (ret) -		ret->root = strfilter_node__new(rules, &ep); +	if (filter) +		filter->root = strfilter_node__new(rules, &ep); -	if (!ret || !ret->root || *ep != '\0') { +	if (!filter || !filter->root || *ep != '\0') {  		if (err)  			*err = ep; -		strfilter__delete(ret); -		ret = NULL; +		strfilter__delete(filter); +		filter = NULL;  	} -	return ret; +	return filter;  } -static bool strfilter_node__compare(struct strfilter_node *self, +static bool strfilter_node__compare(struct strfilter_node *node,  				    const char *str)  { -	if (!self || !self->p) +	if (!node || !node->p)  		return false; -	switch (*self->p) { +	switch (*node->p) {  	case '|':	/* OR */ -		return strfilter_node__compare(self->l, str) || -			strfilter_node__compare(self->r, str); +		return strfilter_node__compare(node->l, str) || +			strfilter_node__compare(node->r, str);  	case '&':	/* AND */ -		return strfilter_node__compare(self->l, str) && -			strfilter_node__compare(self->r, str); +		return strfilter_node__compare(node->l, str) && +			strfilter_node__compare(node->r, str);  	case '!':	/* NOT */ -		return !strfilter_node__compare(self->r, str); +		return !strfilter_node__compare(node->r, str);  	default: -		return strglobmatch(str, self->p); +		return strglobmatch(str, node->p);  	}  }  /* Return true if STR matches the filter rules */ -bool strfilter__compare(struct strfilter *self, const char *str) +bool strfilter__compare(struct strfilter *filter, const char *str)  { -	if (!self) +	if (!filter)  		return false; -	return strfilter_node__compare(self->root, str); +	return strfilter_node__compare(filter->root, str);  } diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h index 00f58a7506d..fe611f3c9e3 100644 --- a/tools/perf/util/strfilter.h +++ b/tools/perf/util/strfilter.h @@ -30,19 +30,19 @@ struct strfilter *strfilter__new(const char *rules, const char **err);  /**   * strfilter__compare - compare given string and a string filter - * @self: String filter + * @filter: String filter   * @str: target string   * - * Compare @str and @self. Return true if the str match the rule + * Compare @str and @filter. Return true if the str match the rule   */ -bool strfilter__compare(struct strfilter *self, const char *str); +bool strfilter__compare(struct strfilter *filter, const char *str);  /**   * strfilter__delete - delete a string filter - * @self: String filter to delete + * @filter: String filter to delete   * - * Delete @self. + * Delete @filter.   */ -void strfilter__delete(struct strfilter *self); +void strfilter__delete(struct strfilter *filter);  #endif diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index f0b0c008c50..2553e5b55b8 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -128,7 +128,7 @@ void argv_free(char **argv)  {  	char **p;  	for (p = argv; *p; p++) -		free(*p); +		zfree(p);  	free(argv);  } diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index eabdce0a2da..71f9d102b96 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -5,6 +5,7 @@   */  #include "strlist.h" +#include "util.h"  #include <errno.h>  #include <stdio.h>  #include <stdlib.h> @@ -38,7 +39,7 @@ out_delete:  static void str_node__delete(struct str_node *snode, bool dupstr)  {  	if (dupstr) -		free((void *)snode->s); +		zfree((char **)&snode->s);  	free(snode);  } diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 96c866045d6..6a0a13d07a2 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -17,8 +17,12 @@  #include <stdlib.h>  #include <unistd.h>  #include <string.h> +#include <linux/bitmap.h> +#include "perf.h"  #include "svghelper.h" +#include "util.h" +#include "cpumap.h"  static u64 first_time, last_time;  static u64 turbo_frequency, max_freq; @@ -28,6 +32,8 @@ static u64 turbo_frequency, max_freq;  #define SLOT_HEIGHT 25.0  int svg_page_width = 1000; +u64 svg_highlight; +const char *svg_highlight_name;  #define MIN_TEXT_SIZE 0.01 @@ -39,9 +45,14 @@ static double cpu2slot(int cpu)  	return 2 * cpu + 1;  } +static int *topology_map; +  static double cpu2y(int cpu)  { -	return cpu2slot(cpu) * SLOT_MULT; +	if (topology_map) +		return cpu2slot(topology_map[cpu]) * SLOT_MULT; +	else +		return cpu2slot(cpu) * SLOT_MULT;  }  static double time2pixels(u64 __time) @@ -95,6 +106,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)  	total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT;  	fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n"); +	fprintf(svgfile, "<!DOCTYPE svg SYSTEM \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");  	fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height);  	fprintf(svgfile, "<defs>\n  <style type=\"text/css\">\n    <![CDATA[\n"); @@ -103,6 +115,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)  	fprintf(svgfile, "      rect.process  { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.sample   { fill:rgb(  0,  0,255); fill-opacity:0.8; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n"); +	fprintf(svgfile, "      rect.sample_hi{ fill:rgb(255,128,  0); fill-opacity:0.8; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.blocked  { fill:rgb(255,  0,  0); fill-opacity:0.5; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.waiting  { fill:rgb(224,214,  0); fill-opacity:0.8; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.WAITING  { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n"); @@ -128,14 +141,42 @@ void svg_box(int Yslot, u64 start, u64 end, const char *type)  		time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);  } -void svg_sample(int Yslot, int cpu, u64 start, u64 end) +static char *time_to_string(u64 duration); +void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) +{ +	if (!svgfile) +		return; + +	fprintf(svgfile, "<g>\n"); +	fprintf(svgfile, "<title>#%d blocked %s</title>\n", cpu, +		time_to_string(end - start)); +	if (backtrace) +		fprintf(svgfile, "<desc>Blocked on:\n%s</desc>\n", backtrace); +	svg_box(Yslot, start, end, "blocked"); +	fprintf(svgfile, "</g>\n"); +} + +void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)  {  	double text_size; +	const char *type; +  	if (!svgfile)  		return; -	fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n", -		time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT); +	if (svg_highlight && end - start > svg_highlight) +		type = "sample_hi"; +	else +		type = "sample"; +	fprintf(svgfile, "<g>\n"); + +	fprintf(svgfile, "<title>#%d running %s</title>\n", +		cpu, time_to_string(end - start)); +	if (backtrace) +		fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); +	fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n", +		time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, +		type);  	text_size = (time2pixels(end)-time2pixels(start));  	if (cpu > 9) @@ -148,6 +189,7 @@ void svg_sample(int Yslot, int cpu, u64 start, u64 end)  		fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n",  			time2pixels(start), Yslot *  SLOT_MULT + SLOT_HEIGHT - 1, text_size,  cpu + 1); +	fprintf(svgfile, "</g>\n");  }  static char *time_to_string(u64 duration) @@ -168,7 +210,7 @@ static char *time_to_string(u64 duration)  	return text;  } -void svg_waiting(int Yslot, u64 start, u64 end) +void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)  {  	char *text;  	const char *style; @@ -192,6 +234,9 @@ void svg_waiting(int Yslot, u64 start, u64 end)  	font_size = round_text_size(font_size);  	fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT); +	fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start)); +	if (backtrace) +		fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace);  	fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",  		time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style);  	if (font_size > MIN_TEXT_SIZE) @@ -242,28 +287,42 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)  	max_freq = __max_freq;  	turbo_frequency = __turbo_freq; +	fprintf(svgfile, "<g>\n"); +  	fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n",  		time2pixels(first_time),  		time2pixels(last_time)-time2pixels(first_time),  		cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); -	sprintf(cpu_string, "CPU %i", (int)cpu+1); +	sprintf(cpu_string, "CPU %i", (int)cpu);  	fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n",  		10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string);  	fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n",  		10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model()); + +	fprintf(svgfile, "</g>\n");  } -void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name) +void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace)  {  	double width; +	const char *type;  	if (!svgfile)  		return; +	if (svg_highlight && end - start >= svg_highlight) +		type = "sample_hi"; +	else if (svg_highlight_name && strstr(name, svg_highlight_name)) +		type = "sample_hi"; +	else +		type = "sample";  	fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu)); +	fprintf(svgfile, "<title>%d %s running %s</title>\n", pid, name, time_to_string(end - start)); +	if (backtrace) +		fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace);  	fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",  		time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type);  	width = time2pixels(end)-time2pixels(start); @@ -288,6 +347,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type)  		return; +	fprintf(svgfile, "<g>\n"); +  	if (type > 6)  		type = 6;  	sprintf(style, "c%i", type); @@ -306,6 +367,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type)  	if (width > MIN_TEXT_SIZE)  		fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n",  			time2pixels(start), cpu2y(cpu)+width, width, type); + +	fprintf(svgfile, "</g>\n");  }  static char *HzToHuman(unsigned long hz) @@ -339,6 +402,8 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)  	if (!svgfile)  		return; +	fprintf(svgfile, "<g>\n"); +  	if (max_freq)  		height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT);  	height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height; @@ -347,10 +412,11 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)  	fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n",  		time2pixels(start), height+0.9, HzToHuman(freq)); +	fprintf(svgfile, "</g>\n");  } -void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2) +void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace)  {  	double height; @@ -358,6 +424,15 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc  		return; +	fprintf(svgfile, "<g>\n"); + +	fprintf(svgfile, "<title>%s wakes up %s</title>\n", +		desc1 ? desc1 : "?", +		desc2 ? desc2 : "?"); + +	if (backtrace) +		fprintf(svgfile, "<desc>%s</desc>\n", backtrace); +  	if (row1 < row2) {  		if (row1) {  			fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", @@ -395,9 +470,11 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc  	if (row1)  		fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(32,255,32)\"/>\n",  			time2pixels(start), height); + +	fprintf(svgfile, "</g>\n");  } -void svg_wakeline(u64 start, int row1, int row2) +void svg_wakeline(u64 start, int row1, int row2, const char *backtrace)  {  	double height; @@ -405,6 +482,11 @@ void svg_wakeline(u64 start, int row1, int row2)  		return; +	fprintf(svgfile, "<g>\n"); + +	if (backtrace) +		fprintf(svgfile, "<desc>%s</desc>\n", backtrace); +  	if (row1 < row2)  		fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",  			time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT,  time2pixels(start), row2 * SLOT_MULT); @@ -417,17 +499,28 @@ void svg_wakeline(u64 start, int row1, int row2)  		height += SLOT_HEIGHT;  	fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(32,255,32)\"/>\n",  			time2pixels(start), height); + +	fprintf(svgfile, "</g>\n");  } -void svg_interrupt(u64 start, int row) +void svg_interrupt(u64 start, int row, const char *backtrace)  {  	if (!svgfile)  		return; +	fprintf(svgfile, "<g>\n"); + +	fprintf(svgfile, "<title>Wakeup from interrupt</title>\n"); + +	if (backtrace) +		fprintf(svgfile, "<desc>%s</desc>\n", backtrace); +  	fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(255,128,128)\"/>\n",  			time2pixels(start), row * SLOT_MULT);  	fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(255,128,128)\"/>\n",  			time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT); + +	fprintf(svgfile, "</g>\n");  }  void svg_text(int Yslot, u64 start, const char *text) @@ -455,6 +548,7 @@ void svg_legenda(void)  	if (!svgfile)  		return; +	fprintf(svgfile, "<g>\n");  	svg_legenda_box(0,	"Running", "sample");  	svg_legenda_box(100,	"Idle","c1");  	svg_legenda_box(200,	"Deeper Idle", "c3"); @@ -462,6 +556,7 @@ void svg_legenda(void)  	svg_legenda_box(550,	"Sleeping", "process2");  	svg_legenda_box(650,	"Waiting for cpu", "waiting");  	svg_legenda_box(800,	"Blocked on IO", "blocked"); +	fprintf(svgfile, "</g>\n");  }  void svg_time_grid(void) @@ -499,3 +594,123 @@ void svg_close(void)  		svgfile = NULL;  	}  } + +#define cpumask_bits(maskp) ((maskp)->bits) +typedef struct { DECLARE_BITMAP(bits, MAX_NR_CPUS); } cpumask_t; + +struct topology { +	cpumask_t *sib_core; +	int sib_core_nr; +	cpumask_t *sib_thr; +	int sib_thr_nr; +}; + +static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos) +{ +	int i; +	int thr; + +	for (i = 0; i < t->sib_thr_nr; i++) { +		if (!test_bit(cpu, cpumask_bits(&t->sib_thr[i]))) +			continue; + +		for_each_set_bit(thr, +				 cpumask_bits(&t->sib_thr[i]), +				 MAX_NR_CPUS) +			if (map[thr] == -1) +				map[thr] = (*pos)++; +	} +} + +static void scan_core_topology(int *map, struct topology *t) +{ +	int pos = 0; +	int i; +	int cpu; + +	for (i = 0; i < t->sib_core_nr; i++) +		for_each_set_bit(cpu, +				 cpumask_bits(&t->sib_core[i]), +				 MAX_NR_CPUS) +			scan_thread_topology(map, t, cpu, &pos); +} + +static int str_to_bitmap(char *s, cpumask_t *b) +{ +	int i; +	int ret = 0; +	struct cpu_map *m; +	int c; + +	m = cpu_map__new(s); +	if (!m) +		return -1; + +	for (i = 0; i < m->nr; i++) { +		c = m->map[i]; +		if (c >= MAX_NR_CPUS) { +			ret = -1; +			break; +		} + +		set_bit(c, cpumask_bits(b)); +	} + +	cpu_map__delete(m); + +	return ret; +} + +int svg_build_topology_map(char *sib_core, int sib_core_nr, +			   char *sib_thr, int sib_thr_nr) +{ +	int i; +	struct topology t; + +	t.sib_core_nr = sib_core_nr; +	t.sib_thr_nr = sib_thr_nr; +	t.sib_core = calloc(sib_core_nr, sizeof(cpumask_t)); +	t.sib_thr = calloc(sib_thr_nr, sizeof(cpumask_t)); + +	if (!t.sib_core || !t.sib_thr) { +		fprintf(stderr, "topology: no memory\n"); +		goto exit; +	} + +	for (i = 0; i < sib_core_nr; i++) { +		if (str_to_bitmap(sib_core, &t.sib_core[i])) { +			fprintf(stderr, "topology: can't parse siblings map\n"); +			goto exit; +		} + +		sib_core += strlen(sib_core) + 1; +	} + +	for (i = 0; i < sib_thr_nr; i++) { +		if (str_to_bitmap(sib_thr, &t.sib_thr[i])) { +			fprintf(stderr, "topology: can't parse siblings map\n"); +			goto exit; +		} + +		sib_thr += strlen(sib_thr) + 1; +	} + +	topology_map = malloc(sizeof(int) * MAX_NR_CPUS); +	if (!topology_map) { +		fprintf(stderr, "topology: no memory\n"); +		goto exit; +	} + +	for (i = 0; i < MAX_NR_CPUS; i++) +		topology_map[i] = -1; + +	scan_core_topology(topology_map, &t); + +	return 0; + +exit: +	zfree(&t.sib_core); +	zfree(&t.sib_thr); + +	return -1; +} diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index e0781989cc3..e3aff5332e3 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h @@ -1,28 +1,33 @@  #ifndef __PERF_SVGHELPER_H  #define __PERF_SVGHELPER_H -#include "types.h" +#include <linux/types.h>  extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);  extern void svg_box(int Yslot, u64 start, u64 end, const char *type); -extern void svg_sample(int Yslot, int cpu, u64 start, u64 end); -extern void svg_waiting(int Yslot, u64 start, u64 end); +extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); +extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); +extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);  extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency); -extern void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name); +extern void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace);  extern void svg_cstate(int cpu, u64 start, u64 end, int type);  extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq);  extern void svg_time_grid(void);  extern void svg_legenda(void); -extern void svg_wakeline(u64 start, int row1, int row2); -extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2); -extern void svg_interrupt(u64 start, int row); +extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace); +extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace); +extern void svg_interrupt(u64 start, int row, const char *backtrace);  extern void svg_text(int Yslot, u64 start, const char *text);  extern void svg_close(void); +extern int svg_build_topology_map(char *sib_core, int sib_core_nr, +				  char *sib_thr, int sib_thr_nr);  extern int svg_page_width; +extern u64 svg_highlight; +extern const char *svg_highlight_name;  #endif /* __PERF_SVGHELPER_H */ diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index a7b9ab55738..6864661a79d 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -6,8 +6,26 @@  #include <inttypes.h>  #include "symbol.h" +#include "vdso.h" +#include <symbol/kallsyms.h>  #include "debug.h" +#ifndef HAVE_ELF_GETPHDRNUM_SUPPORT +static int elf_getphdrnum(Elf *elf, size_t *dst) +{ +	GElf_Ehdr gehdr; +	GElf_Ehdr *ehdr; + +	ehdr = gelf_getehdr(elf, &gehdr); +	if (!ehdr) +		return -1; + +	*dst = ehdr->e_phnum; + +	return 0; +} +#endif +  #ifndef NT_GNU_BUILD_ID  #define NT_GNU_BUILD_ID 3  #endif @@ -119,9 +137,8 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)  	return -1;  } -static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, -				    GElf_Shdr *shp, const char *name, -				    size_t *idx) +Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, +			     GElf_Shdr *shp, const char *name, size_t *idx)  {  	Elf_Scn *sec = NULL;  	size_t cnt = 1; @@ -135,15 +152,15 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,  		gelf_getshdr(sec, shp);  		str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); -		if (!strcmp(name, str)) { +		if (str && !strcmp(name, str)) {  			if (idx)  				*idx = cnt; -			break; +			return sec;  		}  		++cnt;  	} -	return sec; +	return NULL;  }  #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ @@ -471,27 +488,29 @@ int filename__read_debuglink(const char *filename, char *debuglink,  	ek = elf_kind(elf);  	if (ek != ELF_K_ELF) -		goto out_close; +		goto out_elf_end;  	if (gelf_getehdr(elf, &ehdr) == NULL) {  		pr_err("%s: cannot get elf header.\n", __func__); -		goto out_close; +		goto out_elf_end;  	}  	sec = elf_section_by_name(elf, &ehdr, &shdr,  				  ".gnu_debuglink", NULL);  	if (sec == NULL) -		goto out_close; +		goto out_elf_end;  	data = elf_getdata(sec, NULL);  	if (data == NULL) -		goto out_close; +		goto out_elf_end;  	/* the start of this section is a zero-terminated string */  	strncpy(debuglink, data->d_buf, size); -	elf_end(elf); +	err = 0; +out_elf_end: +	elf_end(elf);  out_close:  	close(fd);  out: @@ -537,7 +556,7 @@ bool symsrc__has_symtab(struct symsrc *ss)  void symsrc__destroy(struct symsrc *ss)  { -	free(ss->name); +	zfree(&ss->name);  	elf_end(ss->elf);  	close(ss->fd);  } @@ -600,6 +619,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,  		GElf_Shdr shdr;  		ss->adjust_symbols = (ehdr.e_type == ET_EXEC ||  				ehdr.e_type == ET_REL || +				is_vdso_map(dso->short_name) ||  				elf_section_by_name(elf, &ehdr, &shdr,  						     ".gnu.prelink_undo",  						     NULL) != NULL); @@ -735,6 +755,8 @@ int dso__load_sym(struct dso *dso, struct map *map,  			if (strcmp(elf_name, kmap->ref_reloc_sym->name))  				continue;  			kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; +			map->reloc = kmap->ref_reloc_sym->addr - +				     kmap->ref_reloc_sym->unrelocated_addr;  			break;  		}  	} @@ -906,6 +928,7 @@ int dso__load_sym(struct dso *dso, struct map *map,  				  (u64)shdr.sh_offset);  			sym.st_value -= shdr.sh_addr - shdr.sh_offset;  		} +new_symbol:  		/*  		 * 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 @@ -917,7 +940,6 @@ int dso__load_sym(struct dso *dso, struct map *map,  			if (demangled != NULL)  				elf_name = demangled;  		} -new_symbol:  		f = symbol__new(sym.st_value, sym.st_size,  				GELF_ST_BIND(sym.st_info), elf_name);  		free(demangled); @@ -1002,6 +1024,601 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,  	return err;  } +static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len) +{ +	ssize_t r; +	size_t n; +	int err = -1; +	char *buf = malloc(page_size); + +	if (buf == NULL) +		return -1; + +	if (lseek(to, to_offs, SEEK_SET) != to_offs) +		goto out; + +	if (lseek(from, from_offs, SEEK_SET) != from_offs) +		goto out; + +	while (len) { +		n = page_size; +		if (len < n) +			n = len; +		/* Use read because mmap won't work on proc files */ +		r = read(from, buf, n); +		if (r < 0) +			goto out; +		if (!r) +			break; +		n = r; +		r = write(to, buf, n); +		if (r < 0) +			goto out; +		if ((size_t)r != n) +			goto out; +		len -= n; +	} + +	err = 0; +out: +	free(buf); +	return err; +} + +struct kcore { +	int fd; +	int elfclass; +	Elf *elf; +	GElf_Ehdr ehdr; +}; + +static int kcore__open(struct kcore *kcore, const char *filename) +{ +	GElf_Ehdr *ehdr; + +	kcore->fd = open(filename, O_RDONLY); +	if (kcore->fd == -1) +		return -1; + +	kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL); +	if (!kcore->elf) +		goto out_close; + +	kcore->elfclass = gelf_getclass(kcore->elf); +	if (kcore->elfclass == ELFCLASSNONE) +		goto out_end; + +	ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); +	if (!ehdr) +		goto out_end; + +	return 0; + +out_end: +	elf_end(kcore->elf); +out_close: +	close(kcore->fd); +	return -1; +} + +static int kcore__init(struct kcore *kcore, char *filename, int elfclass, +		       bool temp) +{ +	GElf_Ehdr *ehdr; + +	kcore->elfclass = elfclass; + +	if (temp) +		kcore->fd = mkstemp(filename); +	else +		kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); +	if (kcore->fd == -1) +		return -1; + +	kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL); +	if (!kcore->elf) +		goto out_close; + +	if (!gelf_newehdr(kcore->elf, elfclass)) +		goto out_end; + +	ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); +	if (!ehdr) +		goto out_end; + +	return 0; + +out_end: +	elf_end(kcore->elf); +out_close: +	close(kcore->fd); +	unlink(filename); +	return -1; +} + +static void kcore__close(struct kcore *kcore) +{ +	elf_end(kcore->elf); +	close(kcore->fd); +} + +static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count) +{ +	GElf_Ehdr *ehdr = &to->ehdr; +	GElf_Ehdr *kehdr = &from->ehdr; + +	memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT); +	ehdr->e_type      = kehdr->e_type; +	ehdr->e_machine   = kehdr->e_machine; +	ehdr->e_version   = kehdr->e_version; +	ehdr->e_entry     = 0; +	ehdr->e_shoff     = 0; +	ehdr->e_flags     = kehdr->e_flags; +	ehdr->e_phnum     = count; +	ehdr->e_shentsize = 0; +	ehdr->e_shnum     = 0; +	ehdr->e_shstrndx  = 0; + +	if (from->elfclass == ELFCLASS32) { +		ehdr->e_phoff     = sizeof(Elf32_Ehdr); +		ehdr->e_ehsize    = sizeof(Elf32_Ehdr); +		ehdr->e_phentsize = sizeof(Elf32_Phdr); +	} else { +		ehdr->e_phoff     = sizeof(Elf64_Ehdr); +		ehdr->e_ehsize    = sizeof(Elf64_Ehdr); +		ehdr->e_phentsize = sizeof(Elf64_Phdr); +	} + +	if (!gelf_update_ehdr(to->elf, ehdr)) +		return -1; + +	if (!gelf_newphdr(to->elf, count)) +		return -1; + +	return 0; +} + +static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset, +			   u64 addr, u64 len) +{ +	GElf_Phdr gphdr; +	GElf_Phdr *phdr; + +	phdr = gelf_getphdr(kcore->elf, idx, &gphdr); +	if (!phdr) +		return -1; + +	phdr->p_type	= PT_LOAD; +	phdr->p_flags	= PF_R | PF_W | PF_X; +	phdr->p_offset	= offset; +	phdr->p_vaddr	= addr; +	phdr->p_paddr	= 0; +	phdr->p_filesz	= len; +	phdr->p_memsz	= len; +	phdr->p_align	= page_size; + +	if (!gelf_update_phdr(kcore->elf, idx, phdr)) +		return -1; + +	return 0; +} + +static off_t kcore__write(struct kcore *kcore) +{ +	return elf_update(kcore->elf, ELF_C_WRITE); +} + +struct phdr_data { +	off_t offset; +	u64 addr; +	u64 len; +}; + +struct kcore_copy_info { +	u64 stext; +	u64 etext; +	u64 first_symbol; +	u64 last_symbol; +	u64 first_module; +	u64 last_module_symbol; +	struct phdr_data kernel_map; +	struct phdr_data modules_map; +}; + +static int kcore_copy__process_kallsyms(void *arg, const char *name, char type, +					u64 start) +{ +	struct kcore_copy_info *kci = arg; + +	if (!symbol_type__is_a(type, MAP__FUNCTION)) +		return 0; + +	if (strchr(name, '[')) { +		if (start > kci->last_module_symbol) +			kci->last_module_symbol = start; +		return 0; +	} + +	if (!kci->first_symbol || start < kci->first_symbol) +		kci->first_symbol = start; + +	if (!kci->last_symbol || start > kci->last_symbol) +		kci->last_symbol = start; + +	if (!strcmp(name, "_stext")) { +		kci->stext = start; +		return 0; +	} + +	if (!strcmp(name, "_etext")) { +		kci->etext = start; +		return 0; +	} + +	return 0; +} + +static int kcore_copy__parse_kallsyms(struct kcore_copy_info *kci, +				      const char *dir) +{ +	char kallsyms_filename[PATH_MAX]; + +	scnprintf(kallsyms_filename, PATH_MAX, "%s/kallsyms", dir); + +	if (symbol__restricted_filename(kallsyms_filename, "/proc/kallsyms")) +		return -1; + +	if (kallsyms__parse(kallsyms_filename, kci, +			    kcore_copy__process_kallsyms) < 0) +		return -1; + +	return 0; +} + +static int kcore_copy__process_modules(void *arg, +				       const char *name __maybe_unused, +				       u64 start) +{ +	struct kcore_copy_info *kci = arg; + +	if (!kci->first_module || start < kci->first_module) +		kci->first_module = start; + +	return 0; +} + +static int kcore_copy__parse_modules(struct kcore_copy_info *kci, +				     const char *dir) +{ +	char modules_filename[PATH_MAX]; + +	scnprintf(modules_filename, PATH_MAX, "%s/modules", dir); + +	if (symbol__restricted_filename(modules_filename, "/proc/modules")) +		return -1; + +	if (modules__parse(modules_filename, kci, +			   kcore_copy__process_modules) < 0) +		return -1; + +	return 0; +} + +static void kcore_copy__map(struct phdr_data *p, u64 start, u64 end, u64 pgoff, +			    u64 s, u64 e) +{ +	if (p->addr || s < start || s >= end) +		return; + +	p->addr = s; +	p->offset = (s - start) + pgoff; +	p->len = e < end ? e - s : end - s; +} + +static int kcore_copy__read_map(u64 start, u64 len, u64 pgoff, void *data) +{ +	struct kcore_copy_info *kci = data; +	u64 end = start + len; + +	kcore_copy__map(&kci->kernel_map, start, end, pgoff, kci->stext, +			kci->etext); + +	kcore_copy__map(&kci->modules_map, start, end, pgoff, kci->first_module, +			kci->last_module_symbol); + +	return 0; +} + +static int kcore_copy__read_maps(struct kcore_copy_info *kci, Elf *elf) +{ +	if (elf_read_maps(elf, true, kcore_copy__read_map, kci) < 0) +		return -1; + +	return 0; +} + +static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir, +				 Elf *elf) +{ +	if (kcore_copy__parse_kallsyms(kci, dir)) +		return -1; + +	if (kcore_copy__parse_modules(kci, dir)) +		return -1; + +	if (kci->stext) +		kci->stext = round_down(kci->stext, page_size); +	else +		kci->stext = round_down(kci->first_symbol, page_size); + +	if (kci->etext) { +		kci->etext = round_up(kci->etext, page_size); +	} else if (kci->last_symbol) { +		kci->etext = round_up(kci->last_symbol, page_size); +		kci->etext += page_size; +	} + +	kci->first_module = round_down(kci->first_module, page_size); + +	if (kci->last_module_symbol) { +		kci->last_module_symbol = round_up(kci->last_module_symbol, +						   page_size); +		kci->last_module_symbol += page_size; +	} + +	if (!kci->stext || !kci->etext) +		return -1; + +	if (kci->first_module && !kci->last_module_symbol) +		return -1; + +	return kcore_copy__read_maps(kci, elf); +} + +static int kcore_copy__copy_file(const char *from_dir, const char *to_dir, +				 const char *name) +{ +	char from_filename[PATH_MAX]; +	char to_filename[PATH_MAX]; + +	scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); +	scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); + +	return copyfile_mode(from_filename, to_filename, 0400); +} + +static int kcore_copy__unlink(const char *dir, const char *name) +{ +	char filename[PATH_MAX]; + +	scnprintf(filename, PATH_MAX, "%s/%s", dir, name); + +	return unlink(filename); +} + +static int kcore_copy__compare_fds(int from, int to) +{ +	char *buf_from; +	char *buf_to; +	ssize_t ret; +	size_t len; +	int err = -1; + +	buf_from = malloc(page_size); +	buf_to = malloc(page_size); +	if (!buf_from || !buf_to) +		goto out; + +	while (1) { +		/* Use read because mmap won't work on proc files */ +		ret = read(from, buf_from, page_size); +		if (ret < 0) +			goto out; + +		if (!ret) +			break; + +		len = ret; + +		if (readn(to, buf_to, len) != (int)len) +			goto out; + +		if (memcmp(buf_from, buf_to, len)) +			goto out; +	} + +	err = 0; +out: +	free(buf_to); +	free(buf_from); +	return err; +} + +static int kcore_copy__compare_files(const char *from_filename, +				     const char *to_filename) +{ +	int from, to, err = -1; + +	from = open(from_filename, O_RDONLY); +	if (from < 0) +		return -1; + +	to = open(to_filename, O_RDONLY); +	if (to < 0) +		goto out_close_from; + +	err = kcore_copy__compare_fds(from, to); + +	close(to); +out_close_from: +	close(from); +	return err; +} + +static int kcore_copy__compare_file(const char *from_dir, const char *to_dir, +				    const char *name) +{ +	char from_filename[PATH_MAX]; +	char to_filename[PATH_MAX]; + +	scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); +	scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); + +	return kcore_copy__compare_files(from_filename, to_filename); +} + +/** + * kcore_copy - copy kallsyms, modules and kcore from one directory to another. + * @from_dir: from directory + * @to_dir: to directory + * + * This function copies kallsyms, modules and kcore files from one directory to + * another.  kallsyms and modules are copied entirely.  Only code segments are + * copied from kcore.  It is assumed that two segments suffice: one for the + * kernel proper and one for all the modules.  The code segments are determined + * from kallsyms and modules files.  The kernel map starts at _stext or the + * lowest function symbol, and ends at _etext or the highest function symbol. + * The module map starts at the lowest module address and ends at the highest + * module symbol.  Start addresses are rounded down to the nearest page.  End + * addresses are rounded up to the nearest page.  An extra page is added to the + * highest kernel symbol and highest module symbol to, hopefully, encompass that + * symbol too.  Because it contains only code sections, the resulting kcore is + * unusual.  One significant peculiarity is that the mapping (start -> pgoff) + * is not the same for the kernel map and the modules map.  That happens because + * the data is copied adjacently whereas the original kcore has gaps.  Finally, + * kallsyms and modules files are compared with their copies to check that + * modules have not been loaded or unloaded while the copies were taking place. + * + * Return: %0 on success, %-1 on failure. + */ +int kcore_copy(const char *from_dir, const char *to_dir) +{ +	struct kcore kcore; +	struct kcore extract; +	size_t count = 2; +	int idx = 0, err = -1; +	off_t offset = page_size, sz, modules_offset = 0; +	struct kcore_copy_info kci = { .stext = 0, }; +	char kcore_filename[PATH_MAX]; +	char extract_filename[PATH_MAX]; + +	if (kcore_copy__copy_file(from_dir, to_dir, "kallsyms")) +		return -1; + +	if (kcore_copy__copy_file(from_dir, to_dir, "modules")) +		goto out_unlink_kallsyms; + +	scnprintf(kcore_filename, PATH_MAX, "%s/kcore", from_dir); +	scnprintf(extract_filename, PATH_MAX, "%s/kcore", to_dir); + +	if (kcore__open(&kcore, kcore_filename)) +		goto out_unlink_modules; + +	if (kcore_copy__calc_maps(&kci, from_dir, kcore.elf)) +		goto out_kcore_close; + +	if (kcore__init(&extract, extract_filename, kcore.elfclass, false)) +		goto out_kcore_close; + +	if (!kci.modules_map.addr) +		count -= 1; + +	if (kcore__copy_hdr(&kcore, &extract, count)) +		goto out_extract_close; + +	if (kcore__add_phdr(&extract, idx++, offset, kci.kernel_map.addr, +			    kci.kernel_map.len)) +		goto out_extract_close; + +	if (kci.modules_map.addr) { +		modules_offset = offset + kci.kernel_map.len; +		if (kcore__add_phdr(&extract, idx, modules_offset, +				    kci.modules_map.addr, kci.modules_map.len)) +			goto out_extract_close; +	} + +	sz = kcore__write(&extract); +	if (sz < 0 || sz > offset) +		goto out_extract_close; + +	if (copy_bytes(kcore.fd, kci.kernel_map.offset, extract.fd, offset, +		       kci.kernel_map.len)) +		goto out_extract_close; + +	if (modules_offset && copy_bytes(kcore.fd, kci.modules_map.offset, +					 extract.fd, modules_offset, +					 kci.modules_map.len)) +		goto out_extract_close; + +	if (kcore_copy__compare_file(from_dir, to_dir, "modules")) +		goto out_extract_close; + +	if (kcore_copy__compare_file(from_dir, to_dir, "kallsyms")) +		goto out_extract_close; + +	err = 0; + +out_extract_close: +	kcore__close(&extract); +	if (err) +		unlink(extract_filename); +out_kcore_close: +	kcore__close(&kcore); +out_unlink_modules: +	if (err) +		kcore_copy__unlink(to_dir, "modules"); +out_unlink_kallsyms: +	if (err) +		kcore_copy__unlink(to_dir, "kallsyms"); + +	return err; +} + +int kcore_extract__create(struct kcore_extract *kce) +{ +	struct kcore kcore; +	struct kcore extract; +	size_t count = 1; +	int idx = 0, err = -1; +	off_t offset = page_size, sz; + +	if (kcore__open(&kcore, kce->kcore_filename)) +		return -1; + +	strcpy(kce->extract_filename, PERF_KCORE_EXTRACT); +	if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true)) +		goto out_kcore_close; + +	if (kcore__copy_hdr(&kcore, &extract, count)) +		goto out_extract_close; + +	if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len)) +		goto out_extract_close; + +	sz = kcore__write(&extract); +	if (sz < 0 || sz > offset) +		goto out_extract_close; + +	if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len)) +		goto out_extract_close; + +	err = 0; + +out_extract_close: +	kcore__close(&extract); +	if (err) +		unlink(kce->extract_filename); +out_kcore_close: +	kcore__close(&kcore); + +	return err; +} + +void kcore_extract__delete(struct kcore_extract *kce) +{ +	unlink(kce->extract_filename); +} +  void symbol__elf_init(void)  {  	elf_version(EV_CURRENT); diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 3a802c300fc..bd15f490d04 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -1,4 +1,5 @@  #include "symbol.h" +#include "util.h"  #include <stdio.h>  #include <fcntl.h> @@ -253,6 +254,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,  	if (!ss->name)  		goto out_close; +	ss->fd = fd;  	ss->type = type;  	return 0; @@ -274,7 +276,7 @@ bool symsrc__has_symtab(struct symsrc *ss __maybe_unused)  void symsrc__destroy(struct symsrc *ss)  { -	free(ss->name); +	zfree(&ss->name);  	close(ss->fd);  } @@ -308,6 +310,21 @@ int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused,  	return -1;  } +int kcore_extract__create(struct kcore_extract *kce __maybe_unused) +{ +	return -1; +} + +void kcore_extract__delete(struct kcore_extract *kce __maybe_unused) +{ +} + +int kcore_copy(const char *from_dir __maybe_unused, +	       const char *to_dir __maybe_unused) +{ +	return -1; +} +  void symbol__elf_init(void)  {  } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7eb0362f4ff..7b9096f29cd 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -18,12 +18,9 @@  #include <elf.h>  #include <limits.h> +#include <symbol/kallsyms.h>  #include <sys/utsname.h> -#ifndef KSYM_NAME_LEN -#define KSYM_NAME_LEN 256 -#endif -  static int dso__load_kernel_sym(struct dso *dso, struct map *map,  				symbol_filter_t filter);  static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, @@ -32,11 +29,12 @@ int vmlinux_path__nr_entries;  char **vmlinux_path;  struct symbol_conf symbol_conf = { -	.use_modules	  = true, -	.try_vmlinux_path = true, -	.annotate_src	  = true, -	.demangle	  = true, -	.symfs            = "", +	.use_modules		= true, +	.try_vmlinux_path	= true, +	.annotate_src		= true, +	.demangle		= true, +	.cumulate_callchain	= true, +	.symfs			= "",  };  static enum dso_binary_type binary_type_symtab[] = { @@ -51,6 +49,7 @@ static enum dso_binary_type binary_type_symtab[] = {  	DSO_BINARY_TYPE__SYSTEM_PATH_DSO,  	DSO_BINARY_TYPE__GUEST_KMODULE,  	DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, +	DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,  	DSO_BINARY_TYPE__NOT_FOUND,  }; @@ -159,10 +158,12 @@ again:  		if (choose_best_symbol(curr, next) == SYMBOL_A) {  			rb_erase(&next->rb_node, symbols); +			symbol__delete(next);  			goto again;  		} else {  			nd = rb_next(&curr->rb_node);  			rb_erase(&curr->rb_node, symbols); +			symbol__delete(curr);  		}  	}  } @@ -410,7 +411,7 @@ struct symbol *dso__find_symbol(struct dso *dso,  	return symbols__find(&dso->symbols[type], addr);  } -struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) +static struct symbol *dso__first_symbol(struct dso *dso, enum map_type type)  {  	return symbols__first(&dso->symbols[type]);  } @@ -443,60 +444,62 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,  	return ret;  } -int kallsyms__parse(const char *filename, void *arg, -		    int (*process_symbol)(void *arg, const char *name, -					  char type, u64 start)) +int modules__parse(const char *filename, void *arg, +		   int (*process_module)(void *arg, const char *name, +					 u64 start))  {  	char *line = NULL;  	size_t n; -	int err = -1; -	FILE *file = fopen(filename, "r"); +	FILE *file; +	int err = 0; +	file = fopen(filename, "r");  	if (file == NULL) -		goto out_failure; - -	err = 0; +		return -1; -	while (!feof(file)) { +	while (1) { +		char name[PATH_MAX];  		u64 start; -		int line_len, len; -		char symbol_type; -		char *symbol_name; +		char *sep; +		ssize_t line_len;  		line_len = getline(&line, &n, file); -		if (line_len < 0 || !line) -			break; +		if (line_len < 0) { +			if (feof(file)) +				break; +			err = -1; +			goto out; +		} + +		if (!line) { +			err = -1; +			goto out; +		}  		line[--line_len] = '\0'; /* \n */ -		len = hex2u64(line, &start); +		sep = strrchr(line, 'x'); +		if (sep == NULL) +			continue; -		len++; -		if (len + 2 >= line_len) +		hex2u64(sep + 1, &start); + +		sep = strchr(line, ' '); +		if (sep == NULL)  			continue; -		symbol_type = line[len]; -		len += 2; -		symbol_name = line + len; -		len = line_len - len; +		*sep = '\0'; -		if (len >= KSYM_NAME_LEN) { -			err = -1; -			break; -		} +		scnprintf(name, sizeof(name), "[%s]", line); -		err = process_symbol(arg, symbol_name, -				     symbol_type, start); +		err = process_module(arg, name, start);  		if (err)  			break;  	} - +out:  	free(line);  	fclose(file);  	return err; - -out_failure: -	return -1;  }  struct process_kallsyms_args { @@ -504,12 +507,34 @@ struct process_kallsyms_args {  	struct dso *dso;  }; -static u8 kallsyms2elf_type(char type) +bool symbol__is_idle(struct symbol *sym)  { -	if (type == 'W') -		return STB_WEAK; +	const char * const idle_symbols[] = { +		"cpu_idle", +		"intel_idle", +		"default_idle", +		"native_safe_halt", +		"enter_idle", +		"exit_idle", +		"mwait_idle", +		"mwait_idle_with_hints", +		"poll_idle", +		"ppc64_runlatch_off", +		"pseries_dedicated_idle_sleep", +		NULL +	}; + +	int i; + +	if (!sym) +		return false; -	return isupper(type) ? STB_GLOBAL : STB_LOCAL; +	for (i = 0; idle_symbols[i]; i++) { +		if (!strcmp(idle_symbols[i], sym->name)) +			return true; +	} + +	return false;  }  static int map__process_kallsym_symbol(void *arg, const char *name, @@ -603,7 +628,7 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map,   * 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 *dso, struct map *map, +static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta,  			       symbol_filter_t filter)  {  	struct map_groups *kmaps = map__kmap(map)->kmaps; @@ -668,6 +693,12 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map,  			char dso_name[PATH_MAX];  			struct dso *ndso; +			if (delta) { +				/* Kernel was relocated at boot time */ +				pos->start -= delta; +				pos->end -= delta; +			} +  			if (count == 0) {  				curr_map = map;  				goto filter_symbol; @@ -697,6 +728,10 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map,  			curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;  			map_groups__insert(kmaps, curr_map);  			++kernel_range; +		} else if (delta) { +			/* Kernel was relocated at boot time */ +			pos->start -= delta; +			pos->end -= delta;  		}  filter_symbol:  		if (filter && filter(curr_map, pos)) { @@ -739,51 +774,259 @@ bool symbol__restricted_filename(const char *filename,  	return restricted;  } -struct kcore_mapfn_data { -	struct dso *dso; -	enum map_type type; -	struct list_head maps; +struct module_info { +	struct rb_node rb_node; +	char *name; +	u64 start;  }; -static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) +static void add_module(struct module_info *mi, struct rb_root *modules)  { -	struct kcore_mapfn_data *md = data; -	struct map *map; +	struct rb_node **p = &modules->rb_node; +	struct rb_node *parent = NULL; +	struct module_info *m; -	map = map__new2(start, md->dso, md->type); -	if (map == NULL) +	while (*p != NULL) { +		parent = *p; +		m = rb_entry(parent, struct module_info, rb_node); +		if (strcmp(mi->name, m->name) < 0) +			p = &(*p)->rb_left; +		else +			p = &(*p)->rb_right; +	} +	rb_link_node(&mi->rb_node, parent, p); +	rb_insert_color(&mi->rb_node, modules); +} + +static void delete_modules(struct rb_root *modules) +{ +	struct module_info *mi; +	struct rb_node *next = rb_first(modules); + +	while (next) { +		mi = rb_entry(next, struct module_info, rb_node); +		next = rb_next(&mi->rb_node); +		rb_erase(&mi->rb_node, modules); +		zfree(&mi->name); +		free(mi); +	} +} + +static struct module_info *find_module(const char *name, +				       struct rb_root *modules) +{ +	struct rb_node *n = modules->rb_node; + +	while (n) { +		struct module_info *m; +		int cmp; + +		m = rb_entry(n, struct module_info, rb_node); +		cmp = strcmp(name, m->name); +		if (cmp < 0) +			n = n->rb_left; +		else if (cmp > 0) +			n = n->rb_right; +		else +			return m; +	} + +	return NULL; +} + +static int __read_proc_modules(void *arg, const char *name, u64 start) +{ +	struct rb_root *modules = arg; +	struct module_info *mi; + +	mi = zalloc(sizeof(struct module_info)); +	if (!mi)  		return -ENOMEM; -	map->end = map->start + len; -	map->pgoff = pgoff; +	mi->name = strdup(name); +	mi->start = start; -	list_add(&map->node, &md->maps); +	if (!mi->name) { +		free(mi); +		return -ENOMEM; +	} + +	add_module(mi, modules); + +	return 0; +} + +static int read_proc_modules(const char *filename, struct rb_root *modules) +{ +	if (symbol__restricted_filename(filename, "/proc/modules")) +		return -1; + +	if (modules__parse(filename, modules, __read_proc_modules)) { +		delete_modules(modules); +		return -1; +	}  	return 0;  } +int compare_proc_modules(const char *from, const char *to) +{ +	struct rb_root from_modules = RB_ROOT; +	struct rb_root to_modules = RB_ROOT; +	struct rb_node *from_node, *to_node; +	struct module_info *from_m, *to_m; +	int ret = -1; + +	if (read_proc_modules(from, &from_modules)) +		return -1; + +	if (read_proc_modules(to, &to_modules)) +		goto out_delete_from; + +	from_node = rb_first(&from_modules); +	to_node = rb_first(&to_modules); +	while (from_node) { +		if (!to_node) +			break; + +		from_m = rb_entry(from_node, struct module_info, rb_node); +		to_m = rb_entry(to_node, struct module_info, rb_node); + +		if (from_m->start != to_m->start || +		    strcmp(from_m->name, to_m->name)) +			break; + +		from_node = rb_next(from_node); +		to_node = rb_next(to_node); +	} + +	if (!from_node && !to_node) +		ret = 0; + +	delete_modules(&to_modules); +out_delete_from: +	delete_modules(&from_modules); + +	return ret; +} + +static int do_validate_kcore_modules(const char *filename, struct map *map, +				  struct map_groups *kmaps) +{ +	struct rb_root modules = RB_ROOT; +	struct map *old_map; +	int err; + +	err = read_proc_modules(filename, &modules); +	if (err) +		return err; + +	old_map = map_groups__first(kmaps, map->type); +	while (old_map) { +		struct map *next = map_groups__next(old_map); +		struct module_info *mi; + +		if (old_map == map || old_map->start == map->start) { +			/* The kernel map */ +			old_map = next; +			continue; +		} + +		/* Module must be in memory at the same address */ +		mi = find_module(old_map->dso->short_name, &modules); +		if (!mi || mi->start != old_map->start) { +			err = -EINVAL; +			goto out; +		} + +		old_map = next; +	} +out: +	delete_modules(&modules); +	return err; +} +  /* - * If kallsyms is referenced by name then we look for kcore in the same + * If kallsyms is referenced by name then we look for filename in the same   * directory.   */ -static bool kcore_filename_from_kallsyms_filename(char *kcore_filename, -						  const char *kallsyms_filename) +static bool filename_from_kallsyms_filename(char *filename, +					    const char *base_name, +					    const char *kallsyms_filename)  {  	char *name; -	strcpy(kcore_filename, kallsyms_filename); -	name = strrchr(kcore_filename, '/'); +	strcpy(filename, kallsyms_filename); +	name = strrchr(filename, '/');  	if (!name)  		return false; -	if (!strcmp(name, "/kallsyms")) { -		strcpy(name, "/kcore"); +	name += 1; + +	if (!strcmp(name, "kallsyms")) { +		strcpy(name, base_name);  		return true;  	}  	return false;  } +static int validate_kcore_modules(const char *kallsyms_filename, +				  struct map *map) +{ +	struct map_groups *kmaps = map__kmap(map)->kmaps; +	char modules_filename[PATH_MAX]; + +	if (!filename_from_kallsyms_filename(modules_filename, "modules", +					     kallsyms_filename)) +		return -EINVAL; + +	if (do_validate_kcore_modules(modules_filename, map, kmaps)) +		return -EINVAL; + +	return 0; +} + +static int validate_kcore_addresses(const char *kallsyms_filename, +				    struct map *map) +{ +	struct kmap *kmap = map__kmap(map); + +	if (kmap->ref_reloc_sym && kmap->ref_reloc_sym->name) { +		u64 start; + +		start = kallsyms__get_function_start(kallsyms_filename, +						     kmap->ref_reloc_sym->name); +		if (start != kmap->ref_reloc_sym->addr) +			return -EINVAL; +	} + +	return validate_kcore_modules(kallsyms_filename, map); +} + +struct kcore_mapfn_data { +	struct dso *dso; +	enum map_type type; +	struct list_head maps; +}; + +static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) +{ +	struct kcore_mapfn_data *md = data; +	struct map *map; + +	map = map__new2(start, md->dso, md->type); +	if (map == NULL) +		return -ENOMEM; + +	map->end = map->start + len; +	map->pgoff = pgoff; + +	list_add(&map->node, &md->maps); + +	return 0; +} +  static int dso__load_kcore(struct dso *dso, struct map *map,  			   const char *kallsyms_filename)  { @@ -800,8 +1043,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map,  	if (map != machine->vmlinux_maps[map->type])  		return -EINVAL; -	if (!kcore_filename_from_kallsyms_filename(kcore_filename, -						   kallsyms_filename)) +	if (!filename_from_kallsyms_filename(kcore_filename, "kcore", +					     kallsyms_filename)) +		return -EINVAL; + +	/* Modules and kernel must be present at their original addresses */ +	if (validate_kcore_addresses(kallsyms_filename, map))  		return -EINVAL;  	md.dso = dso; @@ -870,10 +1117,10 @@ static int dso__load_kcore(struct dso *dso, struct map *map,  	 * dso__data_read_addr().  	 */  	if (dso->kernel == DSO_TYPE_GUEST_KERNEL) -		dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE; +		dso->binary_type = DSO_BINARY_TYPE__GUEST_KCORE;  	else -		dso->data_type = DSO_BINARY_TYPE__KCORE; -	dso__set_long_name(dso, strdup(kcore_filename)); +		dso->binary_type = DSO_BINARY_TYPE__KCORE; +	dso__set_long_name(dso, strdup(kcore_filename), true);  	close(fd); @@ -894,15 +1141,41 @@ out_err:  	return -EINVAL;  } +/* + * If the kernel is relocated at boot time, kallsyms won't match.  Compute the + * delta based on the relocation reference symbol. + */ +static int kallsyms__delta(struct map *map, const char *filename, u64 *delta) +{ +	struct kmap *kmap = map__kmap(map); +	u64 addr; + +	if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->name) +		return 0; + +	addr = kallsyms__get_function_start(filename, +					    kmap->ref_reloc_sym->name); +	if (!addr) +		return -1; + +	*delta = addr - kmap->ref_reloc_sym->addr; +	return 0; +} +  int dso__load_kallsyms(struct dso *dso, const char *filename,  		       struct map *map, symbol_filter_t filter)  { +	u64 delta = 0; +  	if (symbol__restricted_filename(filename, "/proc/kallsyms"))  		return -1;  	if (dso__load_all_kallsyms(dso, filename, map) < 0)  		return -1; +	if (kallsyms__delta(map, filename, &delta)) +		return -1; +  	symbols__fixup_duplicate(&dso->symbols[map->type]);  	symbols__fixup_end(&dso->symbols[map->type]); @@ -914,7 +1187,7 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,  	if (!dso__load_kcore(dso, map, filename))  		return dso__split_kallsyms_for_kcore(dso, map, filter);  	else -		return dso__split_kallsyms(dso, map, filter); +		return dso__split_kallsyms(dso, map, delta, filter);  }  static int dso__load_perf_map(struct dso *dso, struct map *map, @@ -979,6 +1252,46 @@ out_failure:  	return -1;  } +static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, +					   enum dso_binary_type type) +{ +	switch (type) { +	case DSO_BINARY_TYPE__JAVA_JIT: +	case DSO_BINARY_TYPE__DEBUGLINK: +	case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: +	case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: +	case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: +	case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: +	case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: +		return !kmod && dso->kernel == DSO_TYPE_USER; + +	case DSO_BINARY_TYPE__KALLSYMS: +	case DSO_BINARY_TYPE__VMLINUX: +	case DSO_BINARY_TYPE__KCORE: +		return dso->kernel == DSO_TYPE_KERNEL; + +	case DSO_BINARY_TYPE__GUEST_KALLSYMS: +	case DSO_BINARY_TYPE__GUEST_VMLINUX: +	case DSO_BINARY_TYPE__GUEST_KCORE: +		return dso->kernel == DSO_TYPE_GUEST_KERNEL; + +	case DSO_BINARY_TYPE__GUEST_KMODULE: +	case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: +		/* +		 * kernel modules know their symtab type - it's set when +		 * creating a module dso in machine__new_module(). +		 */ +		return kmod && dso->symtab_type == type; + +	case DSO_BINARY_TYPE__BUILD_ID_CACHE: +		return true; + +	case DSO_BINARY_TYPE__NOT_FOUND: +	default: +		return false; +	} +} +  int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  {  	char *name; @@ -989,6 +1302,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  	int ss_pos = 0;  	struct symsrc ss_[2];  	struct symsrc *syms_ss = NULL, *runtime_ss = NULL; +	bool kmod;  	dso__set_loaded(dso, map->type); @@ -1029,7 +1343,11 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  	if (!name)  		return -1; -	/* Iterate over candidate debug images. +	kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || +		dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; + +	/* +	 * Iterate over candidate debug images.  	 * Keep track of "interesting" ones (those which have a symtab, dynsym,  	 * and/or opd section) for processing.  	 */ @@ -1039,8 +1357,11 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  		enum dso_binary_type symtab_type = binary_type_symtab[i]; -		if (dso__binary_type_file(dso, symtab_type, -					  root_dir, name, PATH_MAX)) +		if (!dso__is_compatible_symtab_type(dso, kmod, symtab_type)) +			continue; + +		if (dso__read_binary_type_filename(dso, symtab_type, +						   root_dir, name, PATH_MAX))  			continue;  		/* Name is now the name of the next image to try */ @@ -1050,6 +1371,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  		if (!syms_ss && symsrc__has_symtab(ss)) {  			syms_ss = ss;  			next_slot = true; +			if (!dso->symsrc_filename) +				dso->symsrc_filename = strdup(name);  		}  		if (!runtime_ss && symsrc__possibly_runtime(ss)) { @@ -1062,6 +1385,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  			if (syms_ss && runtime_ss)  				break; +		} else { +			symsrc__destroy(ss);  		}  	} @@ -1077,15 +1402,10 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  	if (!runtime_ss && syms_ss)  		runtime_ss = syms_ss; -	if (syms_ss) { -		int km; - -		km = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || -		     dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; -		ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, km); -	} else { +	if (syms_ss) +		ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, kmod); +	else  		ret = -1; -	}  	if (ret > 0) {  		int nr_plt; @@ -1120,7 +1440,8 @@ struct map *map_groups__find_by_name(struct map_groups *mg,  }  int dso__load_vmlinux(struct dso *dso, struct map *map, -		      const char *vmlinux, symbol_filter_t filter) +		      const char *vmlinux, bool vmlinux_allocated, +		      symbol_filter_t filter)  {  	int err = -1;  	struct symsrc ss; @@ -1146,10 +1467,10 @@ int dso__load_vmlinux(struct dso *dso, struct map *map,  	if (err > 0) {  		if (dso->kernel == DSO_TYPE_GUEST_KERNEL) -			dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX; +			dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX;  		else -			dso->data_type = DSO_BINARY_TYPE__VMLINUX; -		dso__set_long_name(dso, (char *)vmlinux); +			dso->binary_type = DSO_BINARY_TYPE__VMLINUX; +		dso__set_long_name(dso, vmlinux, vmlinux_allocated);  		dso__set_loaded(dso, map->type);  		pr_debug("Using %s for symbols\n", symfs_vmlinux);  	} @@ -1168,26 +1489,125 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,  	filename = dso__build_id_filename(dso, NULL, 0);  	if (filename != NULL) { -		err = dso__load_vmlinux(dso, map, filename, filter); -		if (err > 0) { -			dso->lname_alloc = 1; +		err = dso__load_vmlinux(dso, map, filename, true, filter); +		if (err > 0)  			goto out; -		}  		free(filename);  	}  	for (i = 0; i < vmlinux_path__nr_entries; ++i) { -		err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); -		if (err > 0) { -			dso__set_long_name(dso, strdup(vmlinux_path[i])); -			dso->lname_alloc = 1; +		err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); +		if (err > 0)  			break; -		}  	}  out:  	return err;  } +static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) +{ +	char kallsyms_filename[PATH_MAX]; +	struct dirent *dent; +	int ret = -1; +	DIR *d; + +	d = opendir(dir); +	if (!d) +		return -1; + +	while (1) { +		dent = readdir(d); +		if (!dent) +			break; +		if (dent->d_type != DT_DIR) +			continue; +		scnprintf(kallsyms_filename, sizeof(kallsyms_filename), +			  "%s/%s/kallsyms", dir, dent->d_name); +		if (!validate_kcore_addresses(kallsyms_filename, map)) { +			strlcpy(dir, kallsyms_filename, dir_sz); +			ret = 0; +			break; +		} +	} + +	closedir(d); + +	return ret; +} + +static char *dso__find_kallsyms(struct dso *dso, struct map *map) +{ +	u8 host_build_id[BUILD_ID_SIZE]; +	char sbuild_id[BUILD_ID_SIZE * 2 + 1]; +	bool is_host = false; +	char path[PATH_MAX]; + +	if (!dso->has_build_id) { +		/* +		 * Last resort, if we don't have a build-id and couldn't find +		 * any vmlinux file, try the running kernel kallsyms table. +		 */ +		goto proc_kallsyms; +	} + +	if (sysfs__read_build_id("/sys/kernel/notes", host_build_id, +				 sizeof(host_build_id)) == 0) +		is_host = dso__build_id_equal(dso, host_build_id); + +	build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + +	scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir, +		  sbuild_id); + +	/* Use /proc/kallsyms if possible */ +	if (is_host) { +		DIR *d; +		int fd; + +		/* If no cached kcore go with /proc/kallsyms */ +		d = opendir(path); +		if (!d) +			goto proc_kallsyms; +		closedir(d); + +		/* +		 * Do not check the build-id cache, until we know we cannot use +		 * /proc/kcore. +		 */ +		fd = open("/proc/kcore", O_RDONLY); +		if (fd != -1) { +			close(fd); +			/* If module maps match go with /proc/kallsyms */ +			if (!validate_kcore_addresses("/proc/kallsyms", map)) +				goto proc_kallsyms; +		} + +		/* Find kallsyms in build-id cache with kcore */ +		if (!find_matching_kcore(map, path, sizeof(path))) +			return strdup(path); + +		goto proc_kallsyms; +	} + +	/* Find kallsyms in build-id cache with kcore */ +	if (!find_matching_kcore(map, path, sizeof(path))) +		return strdup(path); + +	scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", +		  buildid_dir, sbuild_id); + +	if (access(path, F_OK)) { +		pr_err("No kallsyms or vmlinux with build-id %s was found\n", +		       sbuild_id); +		return NULL; +	} + +	return strdup(path); + +proc_kallsyms: +	return strdup("/proc/kallsyms"); +} +  static int dso__load_kernel_sym(struct dso *dso, struct map *map,  				symbol_filter_t filter)  { @@ -1214,19 +1634,12 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,  		goto do_kallsyms;  	} -	if (symbol_conf.vmlinux_name != NULL) { -		err = dso__load_vmlinux(dso, map, -					symbol_conf.vmlinux_name, filter); -		if (err > 0) { -			dso__set_long_name(dso, -					   strdup(symbol_conf.vmlinux_name)); -			dso->lname_alloc = 1; -			return err; -		} -		return err; +	if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { +		return dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name, +					 false, filter);  	} -	if (vmlinux_path != NULL) { +	if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) {  		err = dso__load_vmlinux_path(dso, map, filter);  		if (err > 0)  			return err; @@ -1236,51 +1649,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,  	if (symbol_conf.symfs[0] != 0)  		return -1; -	/* -	 * 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 (dso->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(dso, 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(dso->build_id, sizeof(dso->build_id), -				  sbuild_id); - -		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; -		} +	kallsyms_allocated_filename = dso__find_kallsyms(dso, map); +	if (!kallsyms_allocated_filename) +		return -1; -		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"; -	} +	kallsyms_filename = kallsyms_allocated_filename;  do_kallsyms:  	err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); @@ -1289,7 +1662,7 @@ do_kallsyms:  	free(kallsyms_allocated_filename);  	if (err > 0 && !dso__is_kcore(dso)) { -		dso__set_long_name(dso, strdup("[kernel.kallsyms]")); +		dso__set_long_name(dso, "[kernel.kallsyms]", false);  		map__fixup_start(map);  		map__fixup_end(map);  	} @@ -1319,7 +1692,8 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,  		 */  		if (symbol_conf.default_guest_vmlinux_name != NULL) {  			err = dso__load_vmlinux(dso, map, -				symbol_conf.default_guest_vmlinux_name, filter); +						symbol_conf.default_guest_vmlinux_name, +						false, filter);  			return err;  		} @@ -1336,7 +1710,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,  		pr_debug("Using %s for symbols\n", kallsyms_filename);  	if (err > 0 && !dso__is_kcore(dso)) {  		machine__mmap_name(machine, path, sizeof(path)); -		dso__set_long_name(dso, strdup(path)); +		dso__set_long_name(dso, strdup(path), true);  		map__fixup_start(map);  		map__fixup_end(map);  	} @@ -1346,13 +1720,10 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,  static void vmlinux_path__exit(void)  { -	while (--vmlinux_path__nr_entries >= 0) { -		free(vmlinux_path[vmlinux_path__nr_entries]); -		vmlinux_path[vmlinux_path__nr_entries] = NULL; -	} +	while (--vmlinux_path__nr_entries >= 0) +		zfree(&vmlinux_path[vmlinux_path__nr_entries]); -	free(vmlinux_path); -	vmlinux_path = NULL; +	zfree(&vmlinux_path);  }  static int vmlinux_path__init(void) @@ -1404,7 +1775,7 @@ out_fail:  	return -1;  } -static int setup_list(struct strlist **list, const char *list_str, +int setup_list(struct strlist **list, const char *list_str,  		      const char *list_name)  {  	if (list_str == NULL) diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index fd5b70ea298..615c752dd76 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -12,8 +12,9 @@  #include <byteswap.h>  #include <libgen.h>  #include "build-id.h" +#include "event.h" -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT  #include <libelf.h>  #include <gelf.h>  #endif @@ -21,7 +22,7 @@  #include "dso.h" -#ifdef HAVE_CPLUS_DEMANGLE +#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT  extern char *cplus_demangle(const char *, int);  static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) @@ -46,12 +47,17 @@ static inline char *bfd_demangle(void __maybe_unused *v,   * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;   * for newer versions we can use mmap to reduce memory usage:   */ -#ifdef LIBELF_MMAP +#ifdef HAVE_LIBELF_MMAP_SUPPORT  # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP  #else  # define PERF_ELF_C_READ_MMAP ELF_C_READ  #endif +#ifdef HAVE_LIBELF_SUPPORT +extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, +				GElf_Shdr *shp, const char *name, size_t *idx); +#endif +  #ifndef DMGL_PARAMS  #define DMGL_PARAMS      (1 << 0)       /* Include function args */  #define DMGL_ANSI        (1 << 1)       /* Include const, volatile, etc */ @@ -74,6 +80,17 @@ struct symbol {  void symbol__delete(struct symbol *sym);  void symbols__delete(struct rb_root *symbols); +/* symbols__for_each_entry - iterate over symbols (rb_root) + * + * @symbols: the rb_root of symbols + * @pos: the 'struct symbol *' to use as a loop cursor + * @nd: the 'struct rb_node *' to use as a temporary storage + */ +#define symbols__for_each_entry(symbols, pos, nd)			\ +	for (nd = rb_first(symbols);					\ +	     nd && (pos = rb_entry(nd, struct symbol, rb_node));	\ +	     nd = rb_next(nd)) +  static inline size_t symbol__size(const struct symbol *sym)  {  	return sym->end - sym->start + 1; @@ -85,12 +102,14 @@ struct symbol_conf {  	unsigned short	priv_size;  	unsigned short	nr_events;  	bool		try_vmlinux_path, +			ignore_vmlinux,  			show_kernel_path,  			use_modules,  			sort_by_name,  			show_nr_samples,  			show_total_period,  			use_callchain, +			cumulate_callchain,  			exclude_other,  			show_cpu_utilization,  			initialized, @@ -98,7 +117,8 @@ struct symbol_conf {  			annotate_asm_raw,  			annotate_src,  			event_group, -			demangle; +			demangle, +			filter_relative;  	const char	*vmlinux_name,  			*kallsyms_name,  			*source_prefix, @@ -163,12 +183,13 @@ struct mem_info {  };  struct addr_location { +	struct machine *machine;  	struct thread *thread;  	struct map    *map;  	struct symbol *sym;  	u64	      addr;  	char	      level; -	bool	      filtered; +	u8	      filtered;  	u8	      cpumode;  	s32	      cpu;  }; @@ -178,7 +199,7 @@ struct symsrc {  	int fd;  	enum dso_binary_type type; -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT  	Elf *elf;  	GElf_Ehdr ehdr; @@ -205,7 +226,8 @@ bool symsrc__possibly_runtime(struct symsrc *ss);  int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter);  int dso__load_vmlinux(struct dso *dso, struct map *map, -		      const char *vmlinux, symbol_filter_t filter); +		      const char *vmlinux, bool vmlinux_allocated, +		      symbol_filter_t filter);  int dso__load_vmlinux_path(struct dso *dso, struct map *map,  			   symbol_filter_t filter);  int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, @@ -215,13 +237,12 @@ struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,  				u64 addr);  struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,  					const char *name); -struct symbol *dso__first_symbol(struct dso *dso, enum map_type type);  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 kallsyms__parse(const char *filename, void *arg, -		    int (*process_symbol)(void *arg, const char *name, -					  char type, u64 start)); +int modules__parse(const char *filename, void *arg, +		   int (*process_module)(void *arg, const char *name, +					 u64 start));  int filename__read_debuglink(const char *filename, char *debuglink,  			     size_t size); @@ -236,6 +257,7 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp);  bool symbol_type__is_a(char symbol_type, enum map_type map_type);  bool symbol__restricted_filename(const char *filename,  				 const char *restricted_filename); +bool symbol__is_idle(struct symbol *sym);  int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,  		  struct symsrc *runtime_ss, symbol_filter_t filter, @@ -252,4 +274,24 @@ typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data);  int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,  		    bool *is_64_bit); +#define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX" + +struct kcore_extract { +	char *kcore_filename; +	u64 addr; +	u64 offs; +	u64 len; +	char extract_filename[sizeof(PERF_KCORE_EXTRACT)]; +	int fd; +}; + +int kcore_extract__create(struct kcore_extract *kce); +void kcore_extract__delete(struct kcore_extract *kce); + +int kcore_copy(const char *from_dir, const char *to_dir); +int compare_proc_modules(const char *from, const char *to); + +int setup_list(struct strlist **list, const char *list_str, +	       const char *list_name); +  #endif /* __PERF_SYMBOL */ diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c deleted file mode 100644 index f71e9eafe15..00000000000 --- a/tools/perf/util/sysfs.c +++ /dev/null @@ -1,60 +0,0 @@ - -#include "util.h" -#include "sysfs.h" - -static const char * const sysfs_known_mountpoints[] = { -	"/sys", -	0, -}; - -static int sysfs_found; -char sysfs_mountpoint[PATH_MAX + 1]; - -static int sysfs_valid_mountpoint(const char *sysfs) -{ -	struct statfs st_fs; - -	if (statfs(sysfs, &st_fs) < 0) -		return -ENOENT; -	else if (st_fs.f_type != (long) SYSFS_MAGIC) -		return -ENOENT; - -	return 0; -} - -const char *sysfs_find_mountpoint(void) -{ -	const char * const *ptr; -	char type[100]; -	FILE *fp; - -	if (sysfs_found) -		return (const char *) sysfs_mountpoint; - -	ptr = sysfs_known_mountpoints; -	while (*ptr) { -		if (sysfs_valid_mountpoint(*ptr) == 0) { -			sysfs_found = 1; -			strcpy(sysfs_mountpoint, *ptr); -			return sysfs_mountpoint; -		} -		ptr++; -	} - -	/* give up and parse /proc/mounts */ -	fp = fopen("/proc/mounts", "r"); -	if (fp == NULL) -		return NULL; - -	while (!sysfs_found && -	       fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", -		      sysfs_mountpoint, type) == 2) { - -		if (strcmp(type, "sysfs") == 0) -			sysfs_found = 1; -	} - -	fclose(fp); - -	return sysfs_found ? sysfs_mountpoint : NULL; -} diff --git a/tools/perf/util/sysfs.h b/tools/perf/util/sysfs.h deleted file mode 100644 index a813b720393..00000000000 --- a/tools/perf/util/sysfs.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __SYSFS_H__ -#define __SYSFS_H__ - -const char *sysfs_find_mountpoint(void); - -#endif /* __DEBUGFS_H__ */ diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 065528b7563..e74c5963dc7 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c @@ -13,9 +13,9 @@  #include <string.h> -enum perf_target_errno perf_target__validate(struct perf_target *target) +enum target_errno target__validate(struct target *target)  { -	enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS; +	enum target_errno ret = TARGET_ERRNO__SUCCESS;  	if (target->pid)  		target->tid = target->pid; @@ -23,42 +23,49 @@ enum perf_target_errno perf_target__validate(struct perf_target *target)  	/* CPU and PID are mutually exclusive */  	if (target->tid && target->cpu_list) {  		target->cpu_list = NULL; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__PID_OVERRIDE_CPU;  	}  	/* UID and PID are mutually exclusive */  	if (target->tid && target->uid_str) {  		target->uid_str = NULL; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__PID_OVERRIDE_UID;  	}  	/* UID and CPU are mutually exclusive */  	if (target->uid_str && target->cpu_list) {  		target->cpu_list = NULL; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__UID_OVERRIDE_CPU;  	}  	/* PID and SYSTEM are mutually exclusive */  	if (target->tid && target->system_wide) {  		target->system_wide = false; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__PID_OVERRIDE_SYSTEM;  	}  	/* UID and SYSTEM are mutually exclusive */  	if (target->uid_str && target->system_wide) {  		target->system_wide = false; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM; +	} + +	/* THREAD and SYSTEM/CPU are mutually exclusive */ +	if (target->per_thread && (target->system_wide || target->cpu_list)) { +		target->per_thread = false; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD;  	}  	return ret;  } -enum perf_target_errno perf_target__parse_uid(struct perf_target *target) +enum target_errno target__parse_uid(struct target *target)  {  	struct passwd pwd, *result;  	char buf[1024]; @@ -66,7 +73,7 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target)  	target->uid = UINT_MAX;  	if (str == NULL) -		return PERF_ERRNO_TARGET__SUCCESS; +		return TARGET_ERRNO__SUCCESS;  	/* Try user name first */  	getpwnam_r(str, &pwd, buf, sizeof(buf), &result); @@ -79,32 +86,33 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target)  		int uid = strtol(str, &endptr, 10);  		if (*endptr != '\0') -			return PERF_ERRNO_TARGET__INVALID_UID; +			return TARGET_ERRNO__INVALID_UID;  		getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);  		if (result == NULL) -			return PERF_ERRNO_TARGET__USER_NOT_FOUND; +			return TARGET_ERRNO__USER_NOT_FOUND;  	}  	target->uid = result->pw_uid; -	return PERF_ERRNO_TARGET__SUCCESS; +	return TARGET_ERRNO__SUCCESS;  }  /* - * This must have a same ordering as the enum perf_target_errno. + * This must have a same ordering as the enum target_errno.   */ -static const char *perf_target__error_str[] = { +static const char *target__error_str[] = {  	"PID/TID switch overriding CPU",  	"PID/TID switch overriding UID",  	"UID switch overriding CPU",  	"PID/TID switch overriding SYSTEM",  	"UID switch overriding SYSTEM", +	"SYSTEM/CPU switch overriding PER-THREAD",  	"Invalid User: %s",  	"Problems obtaining information for user %s",  }; -int perf_target__strerror(struct perf_target *target, int errnum, +int target__strerror(struct target *target, int errnum,  			  char *buf, size_t buflen)  {  	int idx; @@ -124,21 +132,20 @@ int perf_target__strerror(struct perf_target *target, int errnum,  		return 0;  	} -	if (errnum <  __PERF_ERRNO_TARGET__START || -	    errnum >= __PERF_ERRNO_TARGET__END) +	if (errnum <  __TARGET_ERRNO__START || errnum >= __TARGET_ERRNO__END)  		return -1; -	idx = errnum - __PERF_ERRNO_TARGET__START; -	msg = perf_target__error_str[idx]; +	idx = errnum - __TARGET_ERRNO__START; +	msg = target__error_str[idx];  	switch (errnum) { -	case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU -	 ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM: +	case TARGET_ERRNO__PID_OVERRIDE_CPU ... +	     TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD:  		snprintf(buf, buflen, "%s", msg);  		break; -	case PERF_ERRNO_TARGET__INVALID_UID: -	case PERF_ERRNO_TARGET__USER_NOT_FOUND: +	case TARGET_ERRNO__INVALID_UID: +	case TARGET_ERRNO__USER_NOT_FOUND:  		snprintf(buf, buflen, msg, target->uid_str);  		break; diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index a4be8575fda..7381b1ca404 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h @@ -4,7 +4,7 @@  #include <stdbool.h>  #include <sys/types.h> -struct perf_target { +struct target {  	const char   *pid;  	const char   *tid;  	const char   *cpu_list; @@ -12,10 +12,12 @@ struct perf_target {  	uid_t	     uid;  	bool	     system_wide;  	bool	     uses_mmap; +	bool	     default_per_cpu; +	bool	     per_thread;  }; -enum perf_target_errno { -	PERF_ERRNO_TARGET__SUCCESS		= 0, +enum target_errno { +	TARGET_ERRNO__SUCCESS		= 0,  	/*  	 * Choose an arbitrary negative big number not to clash with standard @@ -24,42 +26,54 @@ enum perf_target_errno {  	 *  	 * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html  	 */ -	__PERF_ERRNO_TARGET__START		= -10000, +	__TARGET_ERRNO__START		= -10000, +	/* for target__validate() */ +	TARGET_ERRNO__PID_OVERRIDE_CPU	= __TARGET_ERRNO__START, +	TARGET_ERRNO__PID_OVERRIDE_UID, +	TARGET_ERRNO__UID_OVERRIDE_CPU, +	TARGET_ERRNO__PID_OVERRIDE_SYSTEM, +	TARGET_ERRNO__UID_OVERRIDE_SYSTEM, +	TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD, -	/* for perf_target__validate() */ -	PERF_ERRNO_TARGET__PID_OVERRIDE_CPU	= __PERF_ERRNO_TARGET__START, -	PERF_ERRNO_TARGET__PID_OVERRIDE_UID, -	PERF_ERRNO_TARGET__UID_OVERRIDE_CPU, -	PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM, -	PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM, +	/* for target__parse_uid() */ +	TARGET_ERRNO__INVALID_UID, +	TARGET_ERRNO__USER_NOT_FOUND, -	/* for perf_target__parse_uid() */ -	PERF_ERRNO_TARGET__INVALID_UID, -	PERF_ERRNO_TARGET__USER_NOT_FOUND, - -	__PERF_ERRNO_TARGET__END, +	__TARGET_ERRNO__END,  }; -enum perf_target_errno perf_target__validate(struct perf_target *target); -enum perf_target_errno perf_target__parse_uid(struct perf_target *target); +enum target_errno target__validate(struct target *target); +enum target_errno target__parse_uid(struct target *target); -int perf_target__strerror(struct perf_target *target, int errnum, char *buf, -			  size_t buflen); +int target__strerror(struct target *target, int errnum, char *buf, size_t buflen); -static inline bool perf_target__has_task(struct perf_target *target) +static inline bool target__has_task(struct target *target)  {  	return target->tid || target->pid || target->uid_str;  } -static inline bool perf_target__has_cpu(struct perf_target *target) +static inline bool target__has_cpu(struct target *target)  {  	return target->system_wide || target->cpu_list;  } -static inline bool perf_target__none(struct perf_target *target) +static inline bool target__none(struct target *target) +{ +	return !target__has_task(target) && !target__has_cpu(target); +} + +static inline bool target__uses_dummy_map(struct target *target)  { -	return !perf_target__has_task(target) && !perf_target__has_cpu(target); +	bool use_dummy = false; + +	if (target->default_per_cpu) +		use_dummy = target->per_thread ? true : false; +	else if (target__has_task(target) || +	         (!target__has_cpu(target) && !target->uses_mmap)) +		use_dummy = true; + +	return use_dummy;  }  #endif /* _PERF_TARGET_H */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index e3d4a550a70..2fde0d5e40b 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -6,86 +6,188 @@  #include "thread.h"  #include "util.h"  #include "debug.h" +#include "comm.h" + +int thread__init_map_groups(struct thread *thread, struct machine *machine) +{ +	struct thread *leader; +	pid_t pid = thread->pid_; + +	if (pid == thread->tid) { +		thread->mg = map_groups__new(); +	} else { +		leader = machine__findnew_thread(machine, pid, pid); +		if (leader) +			thread->mg = map_groups__get(leader->mg); +	} + +	return thread->mg ? 0 : -1; +}  struct thread *thread__new(pid_t pid, pid_t tid)  { -	struct thread *self = zalloc(sizeof(*self)); - -	if (self != NULL) { -		map_groups__init(&self->mg); -		self->pid_ = pid; -		self->tid = tid; -		self->ppid = -1; -		self->comm = malloc(32); -		if (self->comm) -			snprintf(self->comm, 32, ":%d", self->tid); +	char *comm_str; +	struct comm *comm; +	struct thread *thread = zalloc(sizeof(*thread)); + +	if (thread != NULL) { +		thread->pid_ = pid; +		thread->tid = tid; +		thread->ppid = -1; +		INIT_LIST_HEAD(&thread->comm_list); + +		comm_str = malloc(32); +		if (!comm_str) +			goto err_thread; + +		snprintf(comm_str, 32, ":%d", tid); +		comm = comm__new(comm_str, 0); +		free(comm_str); +		if (!comm) +			goto err_thread; + +		list_add(&comm->list, &thread->comm_list); +	} + +	return thread; + +err_thread: +	free(thread); +	return NULL; +} + +void thread__delete(struct thread *thread) +{ +	struct comm *comm, *tmp; + +	map_groups__put(thread->mg); +	thread->mg = NULL; +	list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { +		list_del(&comm->list); +		comm__free(comm);  	} -	return self; +	free(thread);  } -void thread__delete(struct thread *self) +struct comm *thread__comm(const struct thread *thread)  { -	map_groups__exit(&self->mg); -	free(self->comm); -	free(self); +	if (list_empty(&thread->comm_list)) +		return NULL; + +	return list_first_entry(&thread->comm_list, struct comm, list);  } -int thread__set_comm(struct thread *self, const char *comm) +/* CHECKME: time should always be 0 if event aren't ordered */ +int thread__set_comm(struct thread *thread, const char *str, u64 timestamp)  { +	struct comm *new, *curr = thread__comm(thread);  	int err; -	if (self->comm) -		free(self->comm); -	self->comm = strdup(comm); -	err = self->comm == NULL ? -ENOMEM : 0; -	if (!err) { -		self->comm_set = true; +	/* Override latest entry if it had no specific time coverage */ +	if (!curr->start) { +		err = comm__override(curr, str, timestamp); +		if (err) +			return err; +	} else { +		new = comm__new(str, timestamp); +		if (!new) +			return -ENOMEM; +		list_add(&new->list, &thread->comm_list);  	} -	return err; + +	thread->comm_set = true; + +	return 0;  } -int thread__comm_len(struct thread *self) +const char *thread__comm_str(const struct thread *thread)  { -	if (!self->comm_len) { -		if (!self->comm) +	const struct comm *comm = thread__comm(thread); + +	if (!comm) +		return NULL; + +	return comm__str(comm); +} + +/* CHECKME: it should probably better return the max comm len from its comm list */ +int thread__comm_len(struct thread *thread) +{ +	if (!thread->comm_len) { +		const char *comm = thread__comm_str(thread); +		if (!comm)  			return 0; -		self->comm_len = strlen(self->comm); +		thread->comm_len = strlen(comm);  	} -	return self->comm_len; +	return thread->comm_len;  }  size_t thread__fprintf(struct thread *thread, FILE *fp)  { -	return fprintf(fp, "Thread %d %s\n", thread->tid, thread->comm) + -	       map_groups__fprintf(&thread->mg, verbose, fp); +	return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + +	       map_groups__fprintf(thread->mg, verbose, fp);  } -void thread__insert_map(struct thread *self, struct map *map) +void thread__insert_map(struct thread *thread, struct map *map)  { -	map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); -	map_groups__insert(&self->mg, map); +	map_groups__fixup_overlappings(thread->mg, map, verbose, stderr); +	map_groups__insert(thread->mg, map);  } -int thread__fork(struct thread *self, struct thread *parent) +static int thread__clone_map_groups(struct thread *thread, +				    struct thread *parent)  {  	int i; +	/* This is new thread, we share map groups for process. */ +	if (thread->pid_ == parent->pid_) +		return 0; + +	/* But this one is new process, copy maps. */ +	for (i = 0; i < MAP__NR_TYPES; ++i) +		if (map_groups__clone(thread->mg, parent->mg, i) < 0) +			return -ENOMEM; + +	return 0; +} + +int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) +{ +	int err; +  	if (parent->comm_set) { -		if (self->comm) -			free(self->comm); -		self->comm = strdup(parent->comm); -		if (!self->comm) +		const char *comm = thread__comm_str(parent); +		if (!comm)  			return -ENOMEM; -		self->comm_set = true; +		err = thread__set_comm(thread, comm, timestamp); +		if (err) +			return err; +		thread->comm_set = true;  	} -	for (i = 0; i < MAP__NR_TYPES; ++i) -		if (map_groups__clone(&self->mg, &parent->mg, i) < 0) -			return -ENOMEM; +	thread->ppid = parent->tid; +	return thread__clone_map_groups(thread, parent); +} -	self->ppid = parent->tid; +void thread__find_cpumode_addr_location(struct thread *thread, +					struct machine *machine, +					enum map_type type, u64 addr, +					struct addr_location *al) +{ +	size_t i; +	const u8 const cpumodes[] = { +		PERF_RECORD_MISC_USER, +		PERF_RECORD_MISC_KERNEL, +		PERF_RECORD_MISC_GUEST_USER, +		PERF_RECORD_MISC_GUEST_KERNEL +	}; -	return 0; +	for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { +		thread__find_addr_location(thread, machine, cpumodes[i], type, +					   addr, al); +		if (al->map) +			break; +	}  } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 4ebbb40d46d..3c0c2724f82 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -2,49 +2,49 @@  #define __PERF_THREAD_H  #include <linux/rbtree.h> +#include <linux/list.h>  #include <unistd.h>  #include <sys/types.h>  #include "symbol.h" +#include <strlist.h>  struct thread {  	union {  		struct rb_node	 rb_node;  		struct list_head node;  	}; -	struct map_groups	mg; +	struct map_groups	*mg;  	pid_t			pid_; /* Not all tools update this */  	pid_t			tid;  	pid_t			ppid;  	char			shortname[3];  	bool			comm_set;  	bool			dead; /* if set thread has exited */ -	char			*comm; +	struct list_head	comm_list;  	int			comm_len;  	void			*priv;  };  struct machine; +struct comm;  struct thread *thread__new(pid_t pid, pid_t tid); -void thread__delete(struct thread *self); +int thread__init_map_groups(struct thread *thread, struct machine *machine); +void thread__delete(struct thread *thread);  static inline void thread__exited(struct thread *thread)  {  	thread->dead = true;  } -int thread__set_comm(struct thread *self, const char *comm); -int thread__comm_len(struct thread *self); -void thread__insert_map(struct thread *self, struct map *map); -int thread__fork(struct thread *self, struct thread *parent); +int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); +int thread__comm_len(struct thread *thread); +struct comm *thread__comm(const struct thread *thread); +const char *thread__comm_str(const struct thread *thread); +void thread__insert_map(struct thread *thread, struct map *map); +int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp);  size_t thread__fprintf(struct thread *thread, FILE *fp); -static inline struct map *thread__find_map(struct thread *self, -					   enum map_type type, u64 addr) -{ -	return self ? map_groups__find(&self->mg, type, addr) : NULL; -} -  void thread__find_addr_map(struct thread *thread, struct machine *machine,  			   u8 cpumode, enum map_type type, u64 addr,  			   struct addr_location *al); @@ -53,6 +53,11 @@ void thread__find_addr_location(struct thread *thread, struct machine *machine,  				u8 cpumode, enum map_type type, u64 addr,  				struct addr_location *al); +void thread__find_cpumode_addr_location(struct thread *thread, +					struct machine *machine, +					enum map_type type, u64 addr, +					struct addr_location *al); +  static inline void *thread__priv(struct thread *thread)  {  	return thread->priv; @@ -62,4 +67,15 @@ static inline void thread__set_priv(struct thread *thread, void *p)  {  	thread->priv = p;  } + +static inline bool thread__is_filtered(struct thread *thread) +{ +	if (symbol_conf.comm_list && +	    !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) { +		return true; +	} + +	return false; +} +  #endif	/* __PERF_THREAD_H */ diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 9b5f856cc28..5d321591210 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -9,6 +9,7 @@  #include "strlist.h"  #include <string.h>  #include "thread_map.h" +#include "util.h"  /* Skip "." and ".." directories */  static int filter(const struct dirent *dir) @@ -40,7 +41,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)  	}  	for (i=0; i<items; i++) -		free(namelist[i]); +		zfree(&namelist[i]);  	free(namelist);  	return threads; @@ -117,7 +118,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)  			threads->map[threads->nr + i] = atoi(namelist[i]->d_name);  		for (i = 0; i < items; i++) -			free(namelist[i]); +			zfree(&namelist[i]);  		free(namelist);  		threads->nr += items; @@ -134,12 +135,11 @@ out_free_threads:  out_free_namelist:  	for (i = 0; i < items; i++) -		free(namelist[i]); +		zfree(&namelist[i]);  	free(namelist);  out_free_closedir: -	free(threads); -	threads = NULL; +	zfree(&threads);  	goto out_closedir;  } @@ -194,7 +194,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)  		for (i = 0; i < items; i++) {  			threads->map[j++] = atoi(namelist[i]->d_name); -			free(namelist[i]); +			zfree(&namelist[i]);  		}  		threads->nr = total_tasks;  		free(namelist); @@ -206,12 +206,11 @@ out:  out_free_namelist:  	for (i = 0; i < items; i++) -		free(namelist[i]); +		zfree(&namelist[i]);  	free(namelist);  out_free_threads: -	free(threads); -	threads = NULL; +	zfree(&threads);  	goto out;  } @@ -262,8 +261,7 @@ out:  	return threads;  out_free_threads: -	free(threads); -	threads = NULL; +	zfree(&threads);  	goto out;  } diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index f857b51b6bd..8e517def925 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -26,8 +26,8 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)  	float samples_per_sec;  	float ksamples_per_sec;  	float esamples_percent; -	struct perf_record_opts *opts = &top->record_opts; -	struct perf_target *target = &opts->target; +	struct record_opts *opts = &top->record_opts; +	struct target *target = &opts->target;  	size_t ret = 0;  	if (top->samples) { diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index b554ffc462b..f92c37abb0a 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -2,7 +2,7 @@  #define __PERF_TOP_H 1  #include "tool.h" -#include "types.h" +#include <linux/types.h>  #include <stddef.h>  #include <stdbool.h>  #include <termios.h> @@ -14,7 +14,7 @@ struct perf_session;  struct perf_top {  	struct perf_tool   tool;  	struct perf_evlist *evlist; -	struct perf_record_opts record_opts; +	struct record_opts record_opts;  	/*  	 * Symbols will be added here in perf_event__process_sample and will  	 * get out after decayed. @@ -24,6 +24,7 @@ struct perf_top {  	u64		   exact_samples;  	u64		   guest_us_samples, guest_kernel_samples;  	int		   print_entries, count_filter, delay_secs; +	int		   max_stack;  	bool		   hide_kernel_symbols, hide_user_symbols, zero;  	bool		   use_tui, use_stdio;  	bool		   kptr_restrict_warned; diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index f3c9e551bd3..7e6fcfe8b43 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -38,7 +38,7 @@  #include "../perf.h"  #include "trace-event.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include "evsel.h"  #define VERSION "0.5" @@ -397,8 +397,8 @@ put_tracepoints_path(struct tracepoint_path *tps)  		struct tracepoint_path *t = tps;  		tps = tps->next; -		free(t->name); -		free(t->system); +		zfree(&t->name); +		zfree(&t->system);  		free(t);  	}  } @@ -562,10 +562,8 @@ out:  		output_fd = fd;  	} -	if (err) { -		free(tdata); -		tdata = NULL; -	} +	if (err) +		zfree(&tdata);  	put_tracepoints_path(tps);  	return tdata; diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index fe7a27d67d2..c36636fd825 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -28,19 +28,6 @@  #include "util.h"  #include "trace-event.h" -struct pevent *read_trace_init(int file_bigendian, int host_bigendian) -{ -	struct pevent *pevent = pevent_alloc(); - -	if (pevent != NULL) { -		pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); -		pevent_set_file_bigendian(pevent, file_bigendian); -		pevent_set_host_bigendian(pevent, host_bigendian); -	} - -	return pevent; -} -  static int get_common_field(struct scripting_context *context,  			    int *offset, int *size, const char *type)  { @@ -120,42 +107,6 @@ raw_field_value(struct event_format *event, const char *name, void *data)  	return val;  } -void *raw_field_ptr(struct event_format *event, const char *name, void *data) -{ -	struct format_field *field; - -	field = pevent_find_any_field(event, name); -	if (!field) -		return NULL; - -	if (field->flags & FIELD_IS_DYNAMIC) { -		int offset; - -		offset = *(int *)(data + field->offset); -		offset &= 0xffff; - -		return data + offset; -	} - -	return data + field->offset; -} - -int trace_parse_common_type(struct pevent *pevent, void *data) -{ -	struct pevent_record record; - -	record.data = data; -	return pevent_data_type(pevent, &record); -} - -int trace_parse_common_pid(struct pevent *pevent, void *data) -{ -	struct pevent_record record; - -	record.data = data; -	return pevent_data_pid(pevent, &record); -} -  unsigned long long read_size(struct event_format *event, void *ptr, int size)  {  	return pevent_read_number(event->pevent, ptr, size); @@ -175,6 +126,7 @@ void event_format__print(struct event_format *event,  	trace_seq_init(&s);  	pevent_event_info(&s, event, &record);  	trace_seq_do_printf(&s); +	trace_seq_destroy(&s);  }  void parse_proc_kallsyms(struct pevent *pevent, @@ -186,7 +138,7 @@ void parse_proc_kallsyms(struct pevent *pevent,  	char *next = NULL;  	char *addr_str;  	char *mod; -	char *fmt; +	char *fmt = NULL;  	line = strtok_r(file, "\n", &next);  	while (line) { diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index f2112270c66..e113e180c48 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -343,7 +343,7 @@ static int read_event_files(struct pevent *pevent)  	return 0;  } -ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) +ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)  {  	char buf[BUFSIZ];  	char test[] = { 23, 8, 68 }; @@ -356,11 +356,9 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)  	int host_bigendian;  	int file_long_size;  	int file_page_size; -	struct pevent *pevent; +	struct pevent *pevent = NULL;  	int err; -	*ppevent = NULL; -  	repipe = __repipe;  	input_fd = fd; @@ -390,12 +388,17 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)  	file_bigendian = buf[0];  	host_bigendian = bigendian(); -	pevent = read_trace_init(file_bigendian, host_bigendian); -	if (pevent == NULL) { -		pr_debug("read_trace_init failed"); +	if (trace_event__init(tevent)) { +		pr_debug("trace_event__init failed");  		goto out;  	} +	pevent = tevent->pevent; + +	pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); +	pevent_set_file_bigendian(pevent, file_bigendian); +	pevent_set_host_bigendian(pevent, host_bigendian); +  	if (do_read(buf, 1) < 0)  		goto out;  	file_long_size = buf[0]; @@ -432,11 +435,10 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)  		pevent_print_printk(pevent);  	} -	*ppevent = pevent;  	pevent = NULL;  out:  	if (pevent) -		pevent_free(pevent); +		trace_event__cleanup(tevent);  	return size;  } diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 95199e4eea9..57aaccc1692 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -38,9 +38,8 @@ static int stop_script_unsupported(void)  static void process_event_unsupported(union perf_event *event __maybe_unused,  				      struct perf_sample *sample __maybe_unused,  				      struct perf_evsel *evsel __maybe_unused, -				      struct machine *machine __maybe_unused,  				      struct thread *thread __maybe_unused, -					  struct addr_location *al __maybe_unused) +				      struct addr_location *al __maybe_unused)  {  } diff --git a/tools/perf/util/trace-event.c b/tools/perf/util/trace-event.c new file mode 100644 index 00000000000..6322d37164c --- /dev/null +++ b/tools/perf/util/trace-event.c @@ -0,0 +1,82 @@ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <linux/kernel.h> +#include <traceevent/event-parse.h> +#include "trace-event.h" +#include "util.h" + +/* + * global trace_event object used by trace_event__tp_format + * + * TODO There's no cleanup call for this. Add some sort of + * __exit function support and call trace_event__cleanup + * there. + */ +static struct trace_event tevent; + +int trace_event__init(struct trace_event *t) +{ +	struct pevent *pevent = pevent_alloc(); + +	if (pevent) { +		t->plugin_list = traceevent_load_plugins(pevent); +		t->pevent  = pevent; +	} + +	return pevent ? 0 : -1; +} + +void trace_event__cleanup(struct trace_event *t) +{ +	traceevent_unload_plugins(t->plugin_list, t->pevent); +	pevent_free(t->pevent); +} + +static struct event_format* +tp_format(const char *sys, const char *name) +{ +	struct pevent *pevent = tevent.pevent; +	struct event_format *event = NULL; +	char path[PATH_MAX]; +	size_t size; +	char *data; + +	scnprintf(path, PATH_MAX, "%s/%s/%s/format", +		  tracing_events_path, sys, name); + +	if (filename__read_str(path, &data, &size)) +		return NULL; + +	pevent_parse_format(pevent, &event, data, size, sys); + +	free(data); +	return event; +} + +struct event_format* +trace_event__tp_format(const char *sys, const char *name) +{ +	static bool initialized; + +	if (!initialized) { +		int be = traceevent_host_bigendian(); +		struct pevent *pevent; + +		if (trace_event__init(&tevent)) +			return NULL; + +		pevent = tevent.pevent; +		pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); +		pevent_set_file_bigendian(pevent, be); +		pevent_set_host_bigendian(pevent, be); +		initialized = true; +	} + +	return tp_format(sys, name); +} diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index fafe1a40444..7b6d6868832 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -3,19 +3,26 @@  #include <traceevent/event-parse.h>  #include "parse-events.h" -#include "session.h"  struct machine;  struct perf_sample;  union perf_event;  struct perf_tool;  struct thread; +struct plugin_list; -extern struct pevent *perf_pevent; +struct trace_event { +	struct pevent		*pevent; +	struct plugin_list	*plugin_list; +}; + +int trace_event__init(struct trace_event *t); +void trace_event__cleanup(struct trace_event *t); +struct event_format* +trace_event__tp_format(const char *sys, const char *name);  int bigendian(void); -struct pevent *read_trace_init(int file_bigendian, int host_bigendian);  void event_format__print(struct event_format *event,  			 int cpu, void *data, int size); @@ -23,26 +30,19 @@ int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size);  int parse_event_file(struct pevent *pevent,  		     char *buf, unsigned long size, char *sys); -struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu); -  unsigned long long  raw_field_value(struct event_format *event, const char *name, void *data); -void *raw_field_ptr(struct event_format *event, const char *name, void *data);  void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size);  void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); -ssize_t trace_report(int fd, struct pevent **pevent, bool repipe); - -int trace_parse_common_type(struct pevent *pevent, void *data); -int trace_parse_common_pid(struct pevent *pevent, void *data); +ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe);  struct event_format *trace_find_next_event(struct pevent *pevent,  					   struct event_format *event);  unsigned long long read_size(struct event_format *event, void *ptr, int size);  unsigned long long eval_flag(const char *flag); -struct pevent_record *trace_read_data(struct pevent *pevent, int cpu);  int read_tracing_data(int fd, struct list_head *pattrs);  struct tracing_data { @@ -68,7 +68,6 @@ struct scripting_ops {  	void (*process_event) (union perf_event *event,  			       struct perf_sample *sample,  			       struct perf_evsel *evsel, -			       struct machine *machine,  			       struct thread *thread,  				   struct addr_location *al);  	int (*generate_script) (struct pevent *pevent, const char *outfile); diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h deleted file mode 100644 index c51fa6b70a2..00000000000 --- a/tools/perf/util/types.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __PERF_TYPES_H -#define __PERF_TYPES_H - -#include <stdint.h> - -/* - * We define u64 as uint64_t for every architecture - * so that we can print it with "%"PRIx64 without getting warnings. - */ -typedef uint64_t	   u64; -typedef int64_t		   s64; -typedef unsigned int	   u32; -typedef signed int	   s32; -typedef unsigned short	   u16; -typedef signed short	   s16; -typedef unsigned char	   u8; -typedef signed char	   s8; - -union u64_swap { -	u64 val64; -	u32 val32[2]; -}; - -#endif /* __PERF_TYPES_H */ diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c new file mode 100644 index 00000000000..5ec80a575b5 --- /dev/null +++ b/tools/perf/util/unwind-libdw.c @@ -0,0 +1,210 @@ +#include <linux/compiler.h> +#include <elfutils/libdw.h> +#include <elfutils/libdwfl.h> +#include <inttypes.h> +#include <errno.h> +#include "unwind.h" +#include "unwind-libdw.h" +#include "machine.h" +#include "thread.h" +#include <linux/types.h> +#include "event.h" +#include "perf_regs.h" + +static char *debuginfo_path; + +static const Dwfl_Callbacks offline_callbacks = { +	.find_debuginfo		= dwfl_standard_find_debuginfo, +	.debuginfo_path		= &debuginfo_path, +	.section_address	= dwfl_offline_section_address, +}; + +static int __report_module(struct addr_location *al, u64 ip, +			    struct unwind_info *ui) +{ +	Dwfl_Module *mod; +	struct dso *dso = NULL; + +	thread__find_addr_location(ui->thread, ui->machine, +				   PERF_RECORD_MISC_USER, +				   MAP__FUNCTION, ip, al); + +	if (al->map) +		dso = al->map->dso; + +	if (!dso) +		return 0; + +	mod = dwfl_addrmodule(ui->dwfl, ip); +	if (!mod) +		mod = dwfl_report_elf(ui->dwfl, dso->short_name, +				      dso->long_name, -1, al->map->start, +				      false); + +	return mod && dwfl_addrmodule(ui->dwfl, ip) == mod ? 0 : -1; +} + +static int report_module(u64 ip, struct unwind_info *ui) +{ +	struct addr_location al; + +	return __report_module(&al, ip, ui); +} + +static int entry(u64 ip, struct unwind_info *ui) + +{ +	struct unwind_entry e; +	struct addr_location al; + +	if (__report_module(&al, ip, ui)) +		return -1; + +	e.ip  = ip; +	e.map = al.map; +	e.sym = al.sym; + +	pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", +		 al.sym ? al.sym->name : "''", +		 ip, +		 al.map ? al.map->map_ip(al.map, ip) : (u64) 0); + +	return ui->cb(&e, ui->arg); +} + +static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) +{ +	/* We want only single thread to be processed. */ +	if (*thread_argp != NULL) +		return 0; + +	*thread_argp = arg; +	return dwfl_pid(dwfl); +} + +static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, +			  Dwarf_Word *data) +{ +	struct addr_location al; +	ssize_t size; + +	thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, +			      MAP__FUNCTION, addr, &al); +	if (!al.map) { +		pr_debug("unwind: no map for %lx\n", (unsigned long)addr); +		return -1; +	} + +	if (!al.map->dso) +		return -1; + +	size = dso__data_read_addr(al.map->dso, al.map, ui->machine, +				   addr, (u8 *) data, sizeof(*data)); + +	return !(size == sizeof(*data)); +} + +static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result, +			void *arg) +{ +	struct unwind_info *ui = arg; +	struct stack_dump *stack = &ui->sample->user_stack; +	u64 start, end; +	int offset; +	int ret; + +	ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP); +	if (ret) +		return false; + +	end = start + stack->size; + +	/* Check overflow. */ +	if (addr + sizeof(Dwarf_Word) < addr) +		return false; + +	if (addr < start || addr + sizeof(Dwarf_Word) > end) { +		ret = access_dso_mem(ui, addr, result); +		if (ret) { +			pr_debug("unwind: access_mem 0x%" PRIx64 " not inside range" +				 " 0x%" PRIx64 "-0x%" PRIx64 "\n", +				addr, start, end); +			return false; +		} +		return true; +	} + +	offset  = addr - start; +	*result = *(Dwarf_Word *)&stack->data[offset]; +	pr_debug("unwind: access_mem addr 0x%" PRIx64 ", val %lx, offset %d\n", +		 addr, (unsigned long)*result, offset); +	return true; +} + +static const Dwfl_Thread_Callbacks callbacks = { +	.next_thread		= next_thread, +	.memory_read		= memory_read, +	.set_initial_registers	= libdw__arch_set_initial_registers, +}; + +static int +frame_callback(Dwfl_Frame *state, void *arg) +{ +	struct unwind_info *ui = arg; +	Dwarf_Addr pc; + +	if (!dwfl_frame_pc(state, &pc, NULL)) { +		pr_err("%s", dwfl_errmsg(-1)); +		return DWARF_CB_ABORT; +	} + +	return entry(pc, ui) || !(--ui->max_stack) ? +	       DWARF_CB_ABORT : DWARF_CB_OK; +} + +int unwind__get_entries(unwind_entry_cb_t cb, void *arg, +			struct machine *machine, struct thread *thread, +			struct perf_sample *data, +			int max_stack) +{ +	struct unwind_info ui = { +		.sample		= data, +		.thread		= thread, +		.machine	= machine, +		.cb		= cb, +		.arg		= arg, +		.max_stack	= max_stack, +	}; +	Dwarf_Word ip; +	int err = -EINVAL; + +	if (!data->user_regs.regs) +		return -EINVAL; + +	ui.dwfl = dwfl_begin(&offline_callbacks); +	if (!ui.dwfl) +		goto out; + +	err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); +	if (err) +		goto out; + +	err = report_module(ip, &ui); +	if (err) +		goto out; + +	if (!dwfl_attach_state(ui.dwfl, EM_NONE, thread->tid, &callbacks, &ui)) +		goto out; + +	err = dwfl_getthread_frames(ui.dwfl, thread->tid, frame_callback, &ui); + +	if (err && !ui.max_stack) +		err = 0; + + out: +	if (err) +		pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1)); + +	dwfl_end(ui.dwfl); +	return 0; +} diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h new file mode 100644 index 00000000000..417a1426f3a --- /dev/null +++ b/tools/perf/util/unwind-libdw.h @@ -0,0 +1,21 @@ +#ifndef __PERF_UNWIND_LIBDW_H +#define __PERF_UNWIND_LIBDW_H + +#include <elfutils/libdwfl.h> +#include "event.h" +#include "thread.h" +#include "unwind.h" + +bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg); + +struct unwind_info { +	Dwfl			*dwfl; +	struct perf_sample      *sample; +	struct machine          *machine; +	struct thread           *thread; +	unwind_entry_cb_t	cb; +	void			*arg; +	int			max_stack; +}; + +#endif /* __PERF_UNWIND_LIBDW_H */ diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind-libunwind.c index 2f891f7e70b..25578b98f5c 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -28,6 +28,7 @@  #include "session.h"  #include "perf_regs.h"  #include "unwind.h" +#include "symbol.h"  #include "util.h"  extern int @@ -39,6 +40,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,  #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +extern int +UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, +				 unw_word_t ip, +				 unw_word_t segbase, +				 const char *obj_name, unw_word_t start, +				 unw_word_t end); + +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +  #define DW_EH_PE_FORMAT_MASK	0x0f	/* format of the encoded value */  #define DW_EH_PE_APPL_MASK	0x70	/* how the value is to be applied */ @@ -76,7 +86,6 @@ struct unwind_info {  	struct perf_sample	*sample;  	struct machine		*machine;  	struct thread		*thread; -	u64			sample_uregs;  };  #define dw_read(ptr, type, end) ({	\ @@ -149,23 +158,6 @@ static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,  	__v;                                                    \  	}) -static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, -				    GElf_Shdr *shp, const char *name) -{ -	Elf_Scn *sec = NULL; - -	while ((sec = elf_nextscn(elf, sec)) != NULL) { -		char *str; - -		gelf_getshdr(sec, shp); -		str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); -		if (!strcmp(name, str)) -			break; -	} - -	return sec; -} -  static u64 elf_section_offset(int fd, const char *name)  {  	Elf *elf; @@ -181,7 +173,7 @@ static u64 elf_section_offset(int fd, const char *name)  		if (gelf_getehdr(elf, &ehdr) == NULL)  			break; -		if (!elf_section_by_name(elf, &ehdr, &shdr, name)) +		if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))  			break;  		offset = shdr.sh_offset; @@ -245,8 +237,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,  	return 0;  } -static int read_unwind_spec(struct dso *dso, struct machine *machine, -			    u64 *table_data, u64 *segbase, u64 *fde_count) +static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, +				     u64 *table_data, u64 *segbase, +				     u64 *fde_count)  {  	int ret = -EINVAL, fd;  	u64 offset; @@ -255,18 +248,36 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine,  	if (fd < 0)  		return -EINVAL; +	/* Check the .eh_frame section for unwinding info */  	offset = elf_section_offset(fd, ".eh_frame_hdr"); -	close(fd);  	if (offset)  		ret = unwind_spec_ehframe(dso, machine, offset,  					  table_data, segbase,  					  fde_count); -	/* TODO .debug_frame check if eh_frame_hdr fails */  	return ret;  } +#ifndef NO_LIBUNWIND_DEBUG_FRAME +static int read_unwind_spec_debug_frame(struct dso *dso, +					struct machine *machine, u64 *offset) +{ +	int fd = dso__data_fd(dso, machine); + +	if (fd < 0) +		return -EINVAL; + +	/* Check the .debug_frame section for unwinding info */ +	*offset = elf_section_offset(fd, ".debug_frame"); + +	if (*offset) +		return 0; + +	return -EINVAL; +} +#endif +  static struct map *find_map(unw_word_t ip, struct unwind_info *ui)  {  	struct addr_location al; @@ -291,20 +302,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,  	pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); -	if (read_unwind_spec(map->dso, ui->machine, -			     &table_data, &segbase, &fde_count)) -		return -EINVAL; +	/* Check the .eh_frame section for unwinding info */ +	if (!read_unwind_spec_eh_frame(map->dso, ui->machine, +				       &table_data, &segbase, &fde_count)) { +		memset(&di, 0, sizeof(di)); +		di.format   = UNW_INFO_FORMAT_REMOTE_TABLE; +		di.start_ip = map->start; +		di.end_ip   = map->end; +		di.u.rti.segbase    = map->start + segbase; +		di.u.rti.table_data = map->start + table_data; +		di.u.rti.table_len  = fde_count * sizeof(struct table_entry) +				      / sizeof(unw_word_t); +		return dwarf_search_unwind_table(as, ip, &di, pi, +						 need_unwind_info, arg); +	} + +#ifndef NO_LIBUNWIND_DEBUG_FRAME +	/* Check the .debug_frame section for unwinding info */ +	if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { +		memset(&di, 0, sizeof(di)); +		if (dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name, +					   map->start, map->end)) +			return dwarf_search_unwind_table(as, ip, &di, pi, +							 need_unwind_info, arg); +	} +#endif -	memset(&di, 0, sizeof(di)); -	di.format   = UNW_INFO_FORMAT_REMOTE_TABLE; -	di.start_ip = map->start; -	di.end_ip   = map->end; -	di.u.rti.segbase    = map->start + segbase; -	di.u.rti.table_data = map->start + table_data; -	di.u.rti.table_len  = fde_count * sizeof(struct table_entry) -			      / sizeof(unw_word_t); -	return dwarf_search_unwind_table(as, ip, &di, pi, -					 need_unwind_info, arg); +	return -EINVAL;  }  static int access_fpreg(unw_addr_space_t __maybe_unused as, @@ -364,30 +388,13 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,  	return !(size == sizeof(*data));  } -static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id, -		     u64 sample_regs) -{ -	int i, idx = 0; - -	if (!(sample_regs & (1 << id))) -		return -EINVAL; - -	for (i = 0; i < id; i++) { -		if (sample_regs & (1 << i)) -			idx++; -	} - -	*valp = regs->regs[idx]; -	return 0; -} -  static int access_mem(unw_addr_space_t __maybe_unused as,  		      unw_word_t addr, unw_word_t *valp,  		      int __write, void *arg)  {  	struct unwind_info *ui = arg;  	struct stack_dump *stack = &ui->sample->user_stack; -	unw_word_t start, end; +	u64 start, end;  	int offset;  	int ret; @@ -397,8 +404,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,  		return 0;  	} -	ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP, -			ui->sample_uregs); +	ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);  	if (ret)  		return ret; @@ -411,8 +417,9 @@ static int access_mem(unw_addr_space_t __maybe_unused as,  	if (addr < start || addr + sizeof(unw_word_t) >= end) {  		ret = access_dso_mem(ui, addr, valp);  		if (ret) { -			pr_debug("unwind: access_mem %p not inside range %p-%p\n", -				(void *)addr, (void *)start, (void *)end); +			pr_debug("unwind: access_mem %p not inside range" +				 " 0x%" PRIx64 "-0x%" PRIx64 "\n", +				 (void *) addr, start, end);  			*valp = 0;  			return ret;  		} @@ -421,8 +428,8 @@ static int access_mem(unw_addr_space_t __maybe_unused as,  	offset = addr - start;  	*valp  = *(unw_word_t *)&stack->data[offset]; -	pr_debug("unwind: access_mem addr %p, val %lx, offset %d\n", -		 (void *)addr, (unsigned long)*valp, offset); +	pr_debug("unwind: access_mem addr %p val %lx, offset %d\n", +		 (void *) addr, (unsigned long)*valp, offset);  	return 0;  } @@ -432,6 +439,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as,  {  	struct unwind_info *ui = arg;  	int id, ret; +	u64 val;  	/* Don't support write, I suspect we don't need it. */  	if (__write) { @@ -444,16 +452,17 @@ static int access_reg(unw_addr_space_t __maybe_unused as,  		return 0;  	} -	id = unwind__arch_reg_id(regnum); +	id = libunwind__arch_reg_id(regnum);  	if (id < 0)  		return -EINVAL; -	ret = reg_value(valp, &ui->sample->user_regs, id, ui->sample_uregs); +	ret = perf_reg_value(&val, &ui->sample->user_regs, id);  	if (ret) {  		pr_err("unwind: can't read reg %d\n", regnum);  		return ret;  	} +	*valp = (unw_word_t) val;  	pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);  	return 0;  } @@ -516,7 +525,7 @@ static unw_accessors_t accessors = {  };  static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, -		       void *arg) +		       void *arg, int max_stack)  {  	unw_addr_space_t addr_space;  	unw_cursor_t c; @@ -532,11 +541,11 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,  	if (ret)  		display_error(ret); -	while (!ret && (unw_step(&c) > 0)) { +	while (!ret && (unw_step(&c) > 0) && max_stack--) {  		unw_word_t ip;  		unw_get_reg(&c, UNW_REG_IP, &ip); -		ret = entry(ip, ui->thread, ui->machine, cb, arg); +		ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0;  	}  	unw_destroy_addr_space(addr_space); @@ -545,12 +554,11 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,  int unwind__get_entries(unwind_entry_cb_t cb, void *arg,  			struct machine *machine, struct thread *thread, -			u64 sample_uregs, struct perf_sample *data) +			struct perf_sample *data, int max_stack)  { -	unw_word_t ip; +	u64 ip;  	struct unwind_info ui = {  		.sample       = data, -		.sample_uregs = sample_uregs,  		.thread       = thread,  		.machine      = machine,  	}; @@ -559,7 +567,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,  	if (!data->user_regs.regs)  		return -EINVAL; -	ret = reg_value(&ip, &data->user_regs, PERF_REG_IP, sample_uregs); +	ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);  	if (ret)  		return ret; @@ -567,5 +575,5 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,  	if (ret)  		return -ENOMEM; -	return get_entries(&ui, cb, arg); +	return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0;  } diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index cb6bc503a79..f03061260b4 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -1,7 +1,7 @@  #ifndef __UNWIND_H  #define __UNWIND_H -#include "types.h" +#include <linux/types.h>  #include "event.h"  #include "symbol.h" @@ -13,23 +13,25 @@ struct unwind_entry {  typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_DWARF_UNWIND_SUPPORT  int unwind__get_entries(unwind_entry_cb_t cb, void *arg,  			struct machine *machine,  			struct thread *thread, -			u64 sample_uregs, -			struct perf_sample *data); -int unwind__arch_reg_id(int regnum); +			struct perf_sample *data, int max_stack); +/* libunwind specific */ +#ifdef HAVE_LIBUNWIND_SUPPORT +int libunwind__arch_reg_id(int regnum); +#endif  #else  static inline int  unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,  		    void *arg __maybe_unused,  		    struct machine *machine __maybe_unused,  		    struct thread *thread __maybe_unused, -		    u64 sample_uregs __maybe_unused, -		    struct perf_sample *data __maybe_unused) +		    struct perf_sample *data __maybe_unused, +		    int max_stack __maybe_unused)  {  	return 0;  } -#endif /* LIBUNWIND_SUPPORT */ +#endif /* HAVE_DWARF_UNWIND_SUPPORT */  #endif /* __UNWIND_H */ diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 6d17b18e915..95aefa78bb0 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -1,16 +1,23 @@  #include "../perf.h"  #include "util.h" +#include <api/fs/fs.h>  #include <sys/mman.h> -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT  #include <execinfo.h>  #endif  #include <stdio.h>  #include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <byteswap.h> +#include <linux/kernel.h>  /*   * XXX We need to find a better place for these things...   */  unsigned int page_size; +int cacheline_size;  bool test_attr__enabled; @@ -55,17 +62,20 @@ int mkdir_p(char *path, mode_t mode)  	return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;  } -static int slow_copyfile(const char *from, const char *to) +static int slow_copyfile(const char *from, const char *to, mode_t mode)  { -	int err = 0; +	int err = -1;  	char *line = NULL;  	size_t n;  	FILE *from_fp = fopen(from, "r"), *to_fp; +	mode_t old_umask;  	if (from_fp == NULL)  		goto out; +	old_umask = umask(mode ^ 0777);  	to_fp = fopen(to, "w"); +	umask(old_umask);  	if (to_fp == NULL)  		goto out_fclose_from; @@ -82,7 +92,7 @@ out:  	return err;  } -int copyfile(const char *from, const char *to) +int copyfile_mode(const char *from, const char *to, mode_t mode)  {  	int fromfd, tofd;  	struct stat st; @@ -93,13 +103,13 @@ int copyfile(const char *from, const char *to)  		goto out;  	if (st.st_size == 0) /* /proc? do it slowly... */ -		return slow_copyfile(from, to); +		return slow_copyfile(from, to, mode);  	fromfd = open(from, O_RDONLY);  	if (fromfd < 0)  		goto out; -	tofd = creat(to, 0755); +	tofd = creat(to, mode);  	if (tofd < 0)  		goto out_close_from; @@ -121,6 +131,11 @@ out:  	return err;  } +int copyfile(const char *from, const char *to) +{ +	return copyfile_mode(from, to, 0755); +} +  unsigned long convert_unit(unsigned long value, char *unit)  {  	*unit = ' '; @@ -143,21 +158,42 @@ unsigned long convert_unit(unsigned long value, char *unit)  	return value;  } -int readn(int fd, void *buf, size_t n) +static ssize_t ion(bool is_read, int fd, void *buf, size_t n)  {  	void *buf_start = buf; +	size_t left = n; -	while (n) { -		int ret = read(fd, buf, n); +	while (left) { +		ssize_t ret = is_read ? read(fd, buf, left) : +					write(fd, buf, left); +		if (ret < 0 && errno == EINTR) +			continue;  		if (ret <= 0)  			return ret; -		n -= ret; -		buf += ret; +		left -= ret; +		buf  += ret;  	} -	return buf - buf_start; +	BUG_ON((size_t)(buf - buf_start) != n); +	return n; +} + +/* + * Read exactly 'n' bytes or return an error. + */ +ssize_t readn(int fd, void *buf, size_t n) +{ +	return ion(true, fd, buf, n); +} + +/* + * Write exactly 'n' bytes or return an error. + */ +ssize_t writen(int fd, void *buf, size_t n) +{ +	return ion(false, fd, buf, n);  }  size_t hex_width(u64 v) @@ -204,7 +240,7 @@ int hex2u64(const char *ptr, u64 *long_val)  }  /* Obtain a backtrace and print it to stdout. */ -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT  void dump_stack(void)  {  	void *array[16]; @@ -361,3 +397,146 @@ int parse_nsec_time(const char *str, u64 *ptime)  	*ptime = time_sec * NSEC_PER_SEC + time_nsec;  	return 0;  } + +unsigned long parse_tag_value(const char *str, struct parse_tag *tags) +{ +	struct parse_tag *i = tags; + +	while (i->tag) { +		char *s; + +		s = strchr(str, i->tag); +		if (s) { +			unsigned long int value; +			char *endptr; + +			value = strtoul(str, &endptr, 10); +			if (s != endptr) +				break; + +			if (value > ULONG_MAX / i->mult) +				break; +			value *= i->mult; +			return value; +		} +		i++; +	} + +	return (unsigned long) -1; +} + +int filename__read_int(const char *filename, int *value) +{ +	char line[64]; +	int fd = open(filename, O_RDONLY), err = -1; + +	if (fd < 0) +		return -1; + +	if (read(fd, line, sizeof(line)) > 0) { +		*value = atoi(line); +		err = 0; +	} + +	close(fd); +	return err; +} + +int filename__read_str(const char *filename, char **buf, size_t *sizep) +{ +	size_t size = 0, alloc_size = 0; +	void *bf = NULL, *nbf; +	int fd, n, err = 0; + +	fd = open(filename, O_RDONLY); +	if (fd < 0) +		return -errno; + +	do { +		if (size == alloc_size) { +			alloc_size += BUFSIZ; +			nbf = realloc(bf, alloc_size); +			if (!nbf) { +				err = -ENOMEM; +				break; +			} + +			bf = nbf; +		} + +		n = read(fd, bf + size, alloc_size - size); +		if (n < 0) { +			if (size) { +				pr_warning("read failed %d: %s\n", +					   errno, strerror(errno)); +				err = 0; +			} else +				err = -errno; + +			break; +		} + +		size += n; +	} while (n > 0); + +	if (!err) { +		*sizep = size; +		*buf   = bf; +	} else +		free(bf); + +	close(fd); +	return err; +} + +const char *get_filename_for_perf_kvm(void) +{ +	const char *filename; + +	if (perf_host && !perf_guest) +		filename = strdup("perf.data.host"); +	else if (!perf_host && perf_guest) +		filename = strdup("perf.data.guest"); +	else +		filename = strdup("perf.data.kvm"); + +	return filename; +} + +int perf_event_paranoid(void) +{ +	char path[PATH_MAX]; +	const char *procfs = procfs__mountpoint(); +	int value; + +	if (!procfs) +		return INT_MAX; + +	scnprintf(path, PATH_MAX, "%s/sys/kernel/perf_event_paranoid", procfs); + +	if (filename__read_int(path, &value)) +		return INT_MAX; + +	return value; +} + +void mem_bswap_32(void *src, int byte_size) +{ +	u32 *m = src; +	while (byte_size > 0) { +		*m = bswap_32(*m); +		byte_size -= sizeof(u32); +		++m; +	} +} + +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; +	} +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index a5353594904..66864364ccb 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -69,10 +69,11 @@  #include <sys/ioctl.h>  #include <inttypes.h>  #include <linux/magic.h> -#include "types.h" +#include <linux/types.h>  #include <sys/ttydefaults.h> -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include <termios.h> +#include <linux/bitops.h>  extern const char *graph_line;  extern const char *graph_dotted_line; @@ -128,6 +129,8 @@ void put_tracing_file(char *file);  #endif  #endif +#define PERF_GTK_DSO  "libperf-gtk.so" +  /* General helper functions */  extern void usage(const char *err) NORETURN;  extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); @@ -183,6 +186,8 @@ static inline void *zalloc(size_t size)  	return calloc(1, size);  } +#define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) +  static inline int has_extension(const char *filename, const char *ext)  {  	size_t len = strlen(filename); @@ -241,6 +246,7 @@ static inline int sane_case(int x, int high)  int mkdir_p(char *path, mode_t mode);  int copyfile(const char *from, const char *to); +int copyfile_mode(const char *from, const char *to, mode_t mode);  s64 perf_atoll(const char *str);  char **argv_split(const char *str, int *argcp); @@ -250,7 +256,8 @@ bool strlazymatch(const char *str, const char *pat);  int strtailcmp(const char *s1, const char *s2);  char *strxfrchar(char *s, char from, char to);  unsigned long convert_unit(unsigned long value, char *unit); -int readn(int fd, void *buf, size_t size); +ssize_t readn(int fd, void *buf, size_t n); +ssize_t writen(int fd, void *buf, size_t n);  struct perf_event_attr; @@ -270,6 +277,24 @@ bool is_power_of_2(unsigned long n)  	return (n != 0 && ((n & (n - 1)) == 0));  } +static inline unsigned next_pow2(unsigned x) +{ +	if (!x) +		return 1; +	return 1ULL << (32 - __builtin_clz(x - 1)); +} + +static inline unsigned long next_pow2_l(unsigned long x) +{ +#if BITS_PER_LONG == 64 +	if (x <= (1UL << 31)) +		return next_pow2(x); +	return (unsigned long)next_pow2(x >> 32) << 32; +#else +	return next_pow2(x); +#endif +} +  size_t hex_width(u64 v);  int hex2u64(const char *ptr, u64 *val); @@ -279,6 +304,30 @@ char *rtrim(char *s);  void dump_stack(void);  extern unsigned int page_size; +extern int cacheline_size;  void get_term_dimensions(struct winsize *ws); + +struct parse_tag { +	char tag; +	int mult; +}; + +unsigned long parse_tag_value(const char *str, struct parse_tag *tags); + +#define SRCLINE_UNKNOWN  ((char *) "??:0") + +struct dso; + +char *get_srcline(struct dso *dso, unsigned long addr); +void free_srcline(char *srcline); + +int filename__read_int(const char *filename, int *value); +int filename__read_str(const char *filename, char **buf, size_t *sizep); +int perf_event_paranoid(void); + +void mem_bswap_64(void *src, int byte_size); +void mem_bswap_32(void *src, int byte_size); + +const char *get_filename_for_perf_kvm(void);  #endif /* GIT_COMPAT_UTIL_H */ diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 697c8b4e59c..0fb3c1fcd3e 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c @@ -31,14 +31,14 @@ void perf_read_values_destroy(struct perf_read_values *values)  		return;  	for (i = 0; i < values->threads; i++) -		free(values->value[i]); -	free(values->value); -	free(values->pid); -	free(values->tid); -	free(values->counterrawid); +		zfree(&values->value[i]); +	zfree(&values->value); +	zfree(&values->pid); +	zfree(&values->tid); +	zfree(&values->counterrawid);  	for (i = 0; i < values->counters; i++) -		free(values->countername[i]); -	free(values->countername); +		zfree(&values->countername[i]); +	zfree(&values->countername);  }  static void perf_read_values__enlarge_threads(struct perf_read_values *values) diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index 2fa967e1a88..b21a80c6cf8 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h @@ -1,7 +1,7 @@  #ifndef __PERF_VALUES_H  #define __PERF_VALUES_H -#include "types.h" +#include <linux/types.h>  struct perf_read_values {  	int threads; diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index 39159822d58..0ddb3b8a89e 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c @@ -103,7 +103,7 @@ struct dso *vdso__dso_findnew(struct list_head *head)  		dso = dso__new(VDSO__MAP_NAME);  		if (dso != NULL) {  			dsos__add(head, dso); -			dso__set_long_name(dso, file); +			dso__set_long_name(dso, file, false);  		}  	}  | 
