diff options
Diffstat (limited to 'tools/perf/util')
83 files changed, 2761 insertions, 1124 deletions
| diff --git a/tools/perf/util/Build b/tools/perf/util/Build index da48fd843438..8c6c8a0ca642 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -8,6 +8,7 @@ libperf-y += env.o  libperf-y += event.o  libperf-y += evlist.o  libperf-y += evsel.o +libperf-y += evsel_fprintf.o  libperf-y += find_bit.o  libperf-y += kallsyms.o  libperf-y += levenshtein.o @@ -26,9 +27,9 @@ libperf-y += strlist.o  libperf-y += strfilter.o  libperf-y += top.o  libperf-y += usage.o -libperf-y += wrapper.o  libperf-y += dso.o  libperf-y += symbol.o +libperf-y += symbol_fprintf.o  libperf-y += color.o  libperf-y += header.o  libperf-y += callchain.o @@ -38,6 +39,7 @@ libperf-y += machine.o  libperf-y += map.o  libperf-y += pstack.o  libperf-y += session.o +libperf-$(CONFIG_AUDIT) += syscalltbl.o  libperf-y += ordered-events.o  libperf-y += comm.o  libperf-y += thread.o @@ -69,9 +71,9 @@ libperf-y += stat-shadow.o  libperf-y += record.o  libperf-y += srcline.o  libperf-y += data.o -libperf-$(CONFIG_X86) += tsc.o -libperf-$(CONFIG_AUXTRACE) += tsc.o +libperf-y += tsc.o  libperf-y += cloexec.o +libperf-y += call-path.o  libperf-y += thread-stack.o  libperf-$(CONFIG_AUXTRACE) += auxtrace.o  libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/ diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index b795b6994144..4db73d5a0dbc 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1138,7 +1138,7 @@ fallback:  	if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&  	    !dso__is_kcore(dso)) { -		char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; +		char bf[SBUILD_ID_SIZE + 15] = " with build id ";  		char *build_id_msg = NULL;  		if (dso->annotate_warned) @@ -1665,5 +1665,5 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize)  bool ui__has_annotation(void)  { -	return use_browser == 1 && sort__has_sym; +	return use_browser == 1 && perf_hpp_list.sym;  } diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index ec164fe70718..c9169011e55e 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -940,6 +940,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts)  	synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;  	synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;  	synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ; +	synth_opts->initial_skip = 0;  }  /* @@ -1064,6 +1065,12 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,  				synth_opts->last_branch_sz = val;  			}  			break; +		case 's': +			synth_opts->initial_skip = strtoul(p, &endptr, 10); +			if (p == endptr) +				goto out_err; +			p = endptr; +			break;  		case ' ':  		case ',':  			break; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 57ff31ecb8e4..767989e0e312 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -68,6 +68,7 @@ enum itrace_period_type {   * @last_branch_sz: branch context size   * @period: 'instructions' events period   * @period_type: 'instructions' events period type + * @initial_skip: skip N events at the beginning.   */  struct itrace_synth_opts {  	bool			set; @@ -86,6 +87,7 @@ struct itrace_synth_opts {  	unsigned int		last_branch_sz;  	unsigned long long	period;  	enum itrace_period_type	period_type; +	unsigned long		initial_skip;  };  /** diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 0967ce601931..493307d1414c 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -842,6 +842,58 @@ bpf_map_op__new(struct parse_events_term *term)  	return op;  } +static struct bpf_map_op * +bpf_map_op__clone(struct bpf_map_op *op) +{ +	struct bpf_map_op *newop; + +	newop = memdup(op, sizeof(*op)); +	if (!newop) { +		pr_debug("Failed to alloc bpf_map_op\n"); +		return NULL; +	} + +	INIT_LIST_HEAD(&newop->list); +	if (op->key_type == BPF_MAP_KEY_RANGES) { +		size_t memsz = op->k.array.nr_ranges * +			       sizeof(op->k.array.ranges[0]); + +		newop->k.array.ranges = memdup(op->k.array.ranges, memsz); +		if (!newop->k.array.ranges) { +			pr_debug("Failed to alloc indices for map\n"); +			free(newop); +			return NULL; +		} +	} + +	return newop; +} + +static struct bpf_map_priv * +bpf_map_priv__clone(struct bpf_map_priv *priv) +{ +	struct bpf_map_priv *newpriv; +	struct bpf_map_op *pos, *newop; + +	newpriv = zalloc(sizeof(*newpriv)); +	if (!newpriv) { +		pr_debug("No enough memory to alloc map private\n"); +		return NULL; +	} +	INIT_LIST_HEAD(&newpriv->ops_list); + +	list_for_each_entry(pos, &priv->ops_list, list) { +		newop = bpf_map_op__clone(pos); +		if (!newop) { +			bpf_map_priv__purge(newpriv); +			return NULL; +		} +		list_add_tail(&newop->list, &newpriv->ops_list); +	} + +	return newpriv; +} +  static int  bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op)  { @@ -1417,6 +1469,89 @@ int bpf__apply_obj_config(void)  	return 0;  } +#define bpf__for_each_map(pos, obj, objtmp)	\ +	bpf_object__for_each_safe(obj, objtmp)	\ +		bpf_map__for_each(pos, obj) + +#define bpf__for_each_stdout_map(pos, obj, objtmp)	\ +	bpf__for_each_map(pos, obj, objtmp) 		\ +		if (bpf_map__get_name(pos) && 		\ +			(strcmp("__bpf_stdout__", 	\ +				bpf_map__get_name(pos)) == 0)) + +int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused) +{ +	struct bpf_map_priv *tmpl_priv = NULL; +	struct bpf_object *obj, *tmp; +	struct perf_evsel *evsel = NULL; +	struct bpf_map *map; +	int err; +	bool need_init = false; + +	bpf__for_each_stdout_map(map, obj, tmp) { +		struct bpf_map_priv *priv; + +		err = bpf_map__get_private(map, (void **)&priv); +		if (err) +			return -BPF_LOADER_ERRNO__INTERNAL; + +		/* +		 * No need to check map type: type should have been +		 * verified by kernel. +		 */ +		if (!need_init && !priv) +			need_init = !priv; +		if (!tmpl_priv && priv) +			tmpl_priv = priv; +	} + +	if (!need_init) +		return 0; + +	if (!tmpl_priv) { +		err = parse_events(evlist, "bpf-output/no-inherit=1,name=__bpf_stdout__/", +				   NULL); +		if (err) { +			pr_debug("ERROR: failed to create bpf-output event\n"); +			return -err; +		} + +		evsel = perf_evlist__last(evlist); +	} + +	bpf__for_each_stdout_map(map, obj, tmp) { +		struct bpf_map_priv *priv; + +		err = bpf_map__get_private(map, (void **)&priv); +		if (err) +			return -BPF_LOADER_ERRNO__INTERNAL; +		if (priv) +			continue; + +		if (tmpl_priv) { +			priv = bpf_map_priv__clone(tmpl_priv); +			if (!priv) +				return -ENOMEM; + +			err = bpf_map__set_private(map, priv, bpf_map_priv__clear); +			if (err) { +				bpf_map_priv__clear(map, priv); +				return err; +			} +		} else if (evsel) { +			struct bpf_map_op *op; + +			op = bpf_map__add_newop(map, NULL); +			if (IS_ERR(op)) +				return PTR_ERR(op); +			op->op_type = BPF_MAP_OP_SET_EVSEL; +			op->v.evsel = evsel; +		} +	} + +	return 0; +} +  #define ERRNO_OFFSET(e)		((e) - __BPF_LOADER_ERRNO__START)  #define ERRCODE_OFFSET(c)	ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)  #define NR_ERRNO	(__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START) @@ -1590,3 +1725,11 @@ int bpf__strerror_apply_obj_config(int err, char *buf, size_t size)  	bpf__strerror_end(buf, size);  	return 0;  } + +int bpf__strerror_setup_stdout(struct perf_evlist *evlist __maybe_unused, +			       int err, char *buf, size_t size) +{ +	bpf__strerror_head(err, buf, size); +	bpf__strerror_end(buf, size); +	return 0; +} diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index be4311944e3d..941e17275aa7 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -79,6 +79,11 @@ int bpf__strerror_config_obj(struct bpf_object *obj,  			     size_t size);  int bpf__apply_obj_config(void);  int bpf__strerror_apply_obj_config(int err, char *buf, size_t size); + +int bpf__setup_stdout(struct perf_evlist *evlist); +int bpf__strerror_setup_stdout(struct perf_evlist *evlist, int err, +			       char *buf, size_t size); +  #else  static inline struct bpf_object *  bpf__prepare_load(const char *filename __maybe_unused, @@ -125,6 +130,12 @@ bpf__apply_obj_config(void)  }  static inline int +bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused) +{ +	return 0; +} + +static inline int  __bpf_strerror(char *buf, size_t size)  {  	if (!size) @@ -177,5 +188,13 @@ bpf__strerror_apply_obj_config(int err __maybe_unused,  {  	return __bpf_strerror(buf, size);  } + +static inline int +bpf__strerror_setup_stdout(struct perf_evlist *evlist __maybe_unused, +			   int err __maybe_unused, char *buf, +			   size_t size) +{ +	return __bpf_strerror(buf, size); +}  #endif  #endif diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 0573c2ec861d..bff425e1232c 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -261,14 +261,14 @@ static int machine__write_buildid_table(struct machine *machine, int fd)  		if (dso__is_vdso(pos)) {  			name = pos->short_name; -			name_len = pos->short_name_len + 1; +			name_len = pos->short_name_len;  		} else if (dso__is_kcore(pos)) {  			machine__mmap_name(machine, nm, sizeof(nm));  			name = nm; -			name_len = strlen(nm) + 1; +			name_len = strlen(nm);  		} else {  			name = pos->long_name; -			name_len = pos->long_name_len + 1; +			name_len = pos->long_name_len;  		}  		in_kernel = pos->kernel || @@ -365,39 +365,17 @@ static char *build_id_cache__dirname_from_path(const char *name,  int build_id_cache__list_build_ids(const char *pathname,  				   struct strlist **result)  { -	struct strlist *list;  	char *dir_name; -	DIR *dir; -	struct dirent *d;  	int ret = 0; -	list = strlist__new(NULL, NULL);  	dir_name = build_id_cache__dirname_from_path(pathname, false, false); -	if (!list || !dir_name) { -		ret = -ENOMEM; -		goto out; -	} +	if (!dir_name) +		return -ENOMEM; -	/* List up all dirents */ -	dir = opendir(dir_name); -	if (!dir) { +	*result = lsdir(dir_name, lsdir_no_dot_filter); +	if (!*result)  		ret = -errno; -		goto out; -	} - -	while ((d = readdir(dir)) != NULL) { -		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) -			continue; -		strlist__add(list, d->d_name); -	} -	closedir(dir); - -out:  	free(dir_name); -	if (ret) -		strlist__delete(list); -	else -		*result = list;  	return ret;  } diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 1f5a93c2c9a2..0d814bb74661 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -40,25 +40,6 @@ int split_cmdline(char *cmdline, const char ***argv);  #define alloc_nr(x) (((x)+16)*3/2) -/* - * Realloc the buffer pointed at by variable 'x' so that it can hold - * at least 'nr' entries; the number of entries currently allocated - * is 'alloc', using the standard growing factor alloc_nr() macro. - * - * DO NOT USE any expression with side-effect for 'x' or 'alloc'. - */ -#define ALLOC_GROW(x, nr, alloc) \ -	do { \ -		if ((nr) > alloc) { \ -			if (alloc_nr(alloc) < (nr)) \ -				alloc = (nr); \ -			else \ -				alloc = alloc_nr(alloc); \ -			x = xrealloc((x), alloc * sizeof(*(x))); \ -		} \ -	} while(0) - -  static inline int is_absolute_path(const char *path)  {  	return path[0] == '/'; diff --git a/tools/perf/util/call-path.c b/tools/perf/util/call-path.c new file mode 100644 index 000000000000..904a17052e38 --- /dev/null +++ b/tools/perf/util/call-path.c @@ -0,0 +1,122 @@ +/* + * call-path.h: Manipulate a tree data structure containing function call paths + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + +#include <linux/rbtree.h> +#include <linux/list.h> + +#include "util.h" +#include "call-path.h" + +static void call_path__init(struct call_path *cp, struct call_path *parent, +			    struct symbol *sym, u64 ip, bool in_kernel) +{ +	cp->parent = parent; +	cp->sym = sym; +	cp->ip = sym ? 0 : ip; +	cp->db_id = 0; +	cp->in_kernel = in_kernel; +	RB_CLEAR_NODE(&cp->rb_node); +	cp->children = RB_ROOT; +} + +struct call_path_root *call_path_root__new(void) +{ +	struct call_path_root *cpr; + +	cpr = zalloc(sizeof(struct call_path_root)); +	if (!cpr) +		return NULL; +	call_path__init(&cpr->call_path, NULL, NULL, 0, false); +	INIT_LIST_HEAD(&cpr->blocks); +	return cpr; +} + +void call_path_root__free(struct call_path_root *cpr) +{ +	struct call_path_block *pos, *n; + +	list_for_each_entry_safe(pos, n, &cpr->blocks, node) { +		list_del(&pos->node); +		free(pos); +	} +	free(cpr); +} + +static struct call_path *call_path__new(struct call_path_root *cpr, +					struct call_path *parent, +					struct symbol *sym, u64 ip, +					bool in_kernel) +{ +	struct call_path_block *cpb; +	struct call_path *cp; +	size_t n; + +	if (cpr->next < cpr->sz) { +		cpb = list_last_entry(&cpr->blocks, struct call_path_block, +				      node); +	} else { +		cpb = zalloc(sizeof(struct call_path_block)); +		if (!cpb) +			return NULL; +		list_add_tail(&cpb->node, &cpr->blocks); +		cpr->sz += CALL_PATH_BLOCK_SIZE; +	} + +	n = cpr->next++ & CALL_PATH_BLOCK_MASK; +	cp = &cpb->cp[n]; + +	call_path__init(cp, parent, sym, ip, in_kernel); + +	return cp; +} + +struct call_path *call_path__findnew(struct call_path_root *cpr, +				     struct call_path *parent, +				     struct symbol *sym, u64 ip, u64 ks) +{ +	struct rb_node **p; +	struct rb_node *node_parent = NULL; +	struct call_path *cp; +	bool in_kernel = ip >= ks; + +	if (sym) +		ip = 0; + +	if (!parent) +		return call_path__new(cpr, parent, sym, ip, in_kernel); + +	p = &parent->children.rb_node; +	while (*p != NULL) { +		node_parent = *p; +		cp = rb_entry(node_parent, struct call_path, rb_node); + +		if (cp->sym == sym && cp->ip == ip) +			return cp; + +		if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) +			p = &(*p)->rb_left; +		else +			p = &(*p)->rb_right; +	} + +	cp = call_path__new(cpr, parent, sym, ip, in_kernel); +	if (!cp) +		return NULL; + +	rb_link_node(&cp->rb_node, node_parent, p); +	rb_insert_color(&cp->rb_node, &parent->children); + +	return cp; +} diff --git a/tools/perf/util/call-path.h b/tools/perf/util/call-path.h new file mode 100644 index 000000000000..477f6d03b659 --- /dev/null +++ b/tools/perf/util/call-path.h @@ -0,0 +1,77 @@ +/* + * call-path.h: Manipulate a tree data structure containing function call paths + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + +#ifndef __PERF_CALL_PATH_H +#define __PERF_CALL_PATH_H + +#include <sys/types.h> + +#include <linux/types.h> +#include <linux/rbtree.h> + +/** + * struct call_path - node in list of calls leading to a function call. + * @parent: call path to the parent function call + * @sym: symbol of function called + * @ip: only if sym is null, the ip of the function + * @db_id: id used for db-export + * @in_kernel: whether function is a in the kernel + * @rb_node: node in parent's tree of called functions + * @children: tree of call paths of functions called + * + * In combination with the call_return structure, the call_path structure + * defines a context-sensitve call-graph. + */ +struct call_path { +	struct call_path *parent; +	struct symbol *sym; +	u64 ip; +	u64 db_id; +	bool in_kernel; +	struct rb_node rb_node; +	struct rb_root children; +}; + +#define CALL_PATH_BLOCK_SHIFT 8 +#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT) +#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1) + +struct call_path_block { +	struct call_path cp[CALL_PATH_BLOCK_SIZE]; +	struct list_head node; +}; + +/** + * struct call_path_root - root of all call paths. + * @call_path: root call path + * @blocks: list of blocks to store call paths + * @next: next free space + * @sz: number of spaces + */ +struct call_path_root { +	struct call_path call_path; +	struct list_head blocks; +	size_t next; +	size_t sz; +}; + +struct call_path_root *call_path_root__new(void); +void call_path_root__free(struct call_path_root *cpr); + +struct call_path *call_path__findnew(struct call_path_root *cpr, +				     struct call_path *parent, +				     struct symbol *sym, u64 ip, u64 ks); + +#endif diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 24b4bd0d7754..07fd30bc2f81 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -109,6 +109,7 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt)  	bool record_opt_set = false;  	bool try_stack_size = false; +	callchain_param.enabled = true;  	symbol_conf.use_callchain = true;  	if (!arg) @@ -117,6 +118,7 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt)  	while ((tok = strtok((char *)arg, ",")) != NULL) {  		if (!strncmp(tok, "none", strlen(tok))) {  			callchain_param.mode = CHAIN_NONE; +			callchain_param.enabled = false;  			symbol_conf.use_callchain = false;  			return 0;  		} @@ -788,7 +790,8 @@ int callchain_cursor_append(struct callchain_cursor *cursor,  	return 0;  } -int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, +int sample__resolve_callchain(struct perf_sample *sample, +			      struct callchain_cursor *cursor, struct symbol **parent,  			      struct perf_evsel *evsel, struct addr_location *al,  			      int max_stack)  { @@ -796,8 +799,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent  		return 0;  	if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || -	    sort__has_parent) { -		return thread__resolve_callchain(al->thread, evsel, sample, +	    perf_hpp_list.parent) { +		return thread__resolve_callchain(al->thread, cursor, evsel, sample,  						 parent, al, max_stack);  	}  	return 0; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index d2a9e694810c..65e2a4f7cb4e 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -212,7 +212,14 @@ struct hist_entry;  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 record_opts; + +int record_opts__parse_callchain(struct record_opts *record, +				 struct callchain_param *callchain, +				 const char *arg, bool unset); + +int sample__resolve_callchain(struct perf_sample *sample, +			      struct callchain_cursor *cursor, 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); diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 4e727635476e..dad7d8272168 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -13,6 +13,7 @@  #include <subcmd/exec-cmd.h>  #include "util/hist.h"  /* perf_hist_config */  #include "util/llvm-utils.h"   /* perf_llvm_config */ +#include "config.h"  #define MAXNAME (256) @@ -377,6 +378,21 @@ const char *perf_config_dirname(const char *name, const char *value)  	return value;  } +static int perf_buildid_config(const char *var, const char *value) +{ +	/* same dir for all commands */ +	if (!strcmp(var, "buildid.dir")) { +		const char *dir = perf_config_dirname(var, value); + +		if (!dir) +			return -1; +		strncpy(buildid_dir, dir, MAXPATHLEN-1); +		buildid_dir[MAXPATHLEN-1] = '\0'; +	} + +	return 0; +} +  static int perf_default_core_config(const char *var __maybe_unused,  				    const char *value __maybe_unused)  { @@ -412,6 +428,9 @@ int perf_default_config(const char *var, const char *value,  	if (!prefixcmp(var, "llvm."))  		return perf_llvm_config(var, value); +	if (!prefixcmp(var, "buildid.")) +		return perf_buildid_config(var, value); +  	/* Add other config variables here. */  	return 0;  } @@ -506,41 +525,185 @@ out:  	return ret;  } -/* - * Call this to report error for your variable that should not - * get a boolean value (i.e. "[my] var" means "true"). - */ -int config_error_nonbool(const char *var) +static struct perf_config_section *find_section(struct list_head *sections, +						const char *section_name)  { -	return error("Missing value for '%s'", var); +	struct perf_config_section *section; + +	list_for_each_entry(section, sections, node) +		if (!strcmp(section->name, section_name)) +			return section; + +	return NULL; +} + +static struct perf_config_item *find_config_item(const char *name, +						 struct perf_config_section *section) +{ +	struct perf_config_item *item; + +	list_for_each_entry(item, §ion->items, node) +		if (!strcmp(item->name, name)) +			return item; + +	return NULL;  } -struct buildid_dir_config { -	char *dir; -}; +static struct perf_config_section *add_section(struct list_head *sections, +					       const char *section_name) +{ +	struct perf_config_section *section = zalloc(sizeof(*section)); + +	if (!section) +		return NULL; + +	INIT_LIST_HEAD(§ion->items); +	section->name = strdup(section_name); +	if (!section->name) { +		pr_debug("%s: strdup failed\n", __func__); +		free(section); +		return NULL; +	} + +	list_add_tail(§ion->node, sections); +	return section; +} -static int buildid_dir_command_config(const char *var, const char *value, -				      void *data) +static struct perf_config_item *add_config_item(struct perf_config_section *section, +						const char *name)  { -	struct buildid_dir_config *c = data; -	const char *v; +	struct perf_config_item *item = zalloc(sizeof(*item)); -	/* same dir for all commands */ -	if (!strcmp(var, "buildid.dir")) { -		v = perf_config_dirname(var, value); -		if (!v) -			return -1; -		strncpy(c->dir, v, MAXPATHLEN-1); -		c->dir[MAXPATHLEN-1] = '\0'; +	if (!item) +		return NULL; + +	item->name = strdup(name); +	if (!item->name) { +		pr_debug("%s: strdup failed\n", __func__); +		free(item); +		return NULL;  	} + +	list_add_tail(&item->node, §ion->items); +	return item; +} + +static int set_value(struct perf_config_item *item, const char *value) +{ +	char *val = strdup(value); + +	if (!val) +		return -1; + +	zfree(&item->value); +	item->value = val;  	return 0;  } -static void check_buildid_dir_config(void) +static int collect_config(const char *var, const char *value, +			  void *perf_config_set)  { -	struct buildid_dir_config c; -	c.dir = buildid_dir; -	perf_config(buildid_dir_command_config, &c); +	int ret = -1; +	char *ptr, *key; +	char *section_name, *name; +	struct perf_config_section *section = NULL; +	struct perf_config_item *item = NULL; +	struct perf_config_set *set = perf_config_set; +	struct list_head *sections = &set->sections; + +	key = ptr = strdup(var); +	if (!key) { +		pr_debug("%s: strdup failed\n", __func__); +		return -1; +	} + +	section_name = strsep(&ptr, "."); +	name = ptr; +	if (name == NULL || value == NULL) +		goto out_free; + +	section = find_section(sections, section_name); +	if (!section) { +		section = add_section(sections, section_name); +		if (!section) +			goto out_free; +	} + +	item = find_config_item(name, section); +	if (!item) { +		item = add_config_item(section, name); +		if (!item) +			goto out_free; +	} + +	ret = set_value(item, value); +	return ret; + +out_free: +	free(key); +	perf_config_set__delete(set); +	return -1; +} + +struct perf_config_set *perf_config_set__new(void) +{ +	struct perf_config_set *set = zalloc(sizeof(*set)); + +	if (set) { +		INIT_LIST_HEAD(&set->sections); +		perf_config(collect_config, set); +	} + +	return set; +} + +static void perf_config_item__delete(struct perf_config_item *item) +{ +	zfree(&item->name); +	zfree(&item->value); +	free(item); +} + +static void perf_config_section__purge(struct perf_config_section *section) +{ +	struct perf_config_item *item, *tmp; + +	list_for_each_entry_safe(item, tmp, §ion->items, node) { +		list_del_init(&item->node); +		perf_config_item__delete(item); +	} +} + +static void perf_config_section__delete(struct perf_config_section *section) +{ +	perf_config_section__purge(section); +	zfree(§ion->name); +	free(section); +} + +static void perf_config_set__purge(struct perf_config_set *set) +{ +	struct perf_config_section *section, *tmp; + +	list_for_each_entry_safe(section, tmp, &set->sections, node) { +		list_del_init(§ion->node); +		perf_config_section__delete(section); +	} +} + +void perf_config_set__delete(struct perf_config_set *set) +{ +	perf_config_set__purge(set); +	free(set); +} + +/* + * Call this to report error for your variable that should not + * get a boolean value (i.e. "[my] var" means "true"). + */ +int config_error_nonbool(const char *var) +{ +	return error("Missing value for '%s'", var);  }  void set_buildid_dir(const char *dir) @@ -548,16 +711,13 @@ void set_buildid_dir(const char *dir)  	if (dir)  		scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir); -	/* try config file */ -	if (buildid_dir[0] == '\0') -		check_buildid_dir_config(); -  	/* default to $HOME/.debug */  	if (buildid_dir[0] == '\0') { -		char *v = getenv("HOME"); -		if (v) { +		char *home = getenv("HOME"); + +		if (home) {  			snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", -				 v, DEBUG_CACHE_DIR); +				 home, DEBUG_CACHE_DIR);  		} else {  			strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);  		} diff --git a/tools/perf/util/config.h b/tools/perf/util/config.h new file mode 100644 index 000000000000..22ec626ac718 --- /dev/null +++ b/tools/perf/util/config.h @@ -0,0 +1,26 @@ +#ifndef __PERF_CONFIG_H +#define __PERF_CONFIG_H + +#include <stdbool.h> +#include <linux/list.h> + +struct perf_config_item { +	char *name; +	char *value; +	struct list_head node; +}; + +struct perf_config_section { +	char *name; +	struct list_head items; +	struct list_head node; +}; + +struct perf_config_set { +	struct list_head sections; +}; + +struct perf_config_set *perf_config_set__new(void); +void perf_config_set__delete(struct perf_config_set *set); + +#endif /* __PERF_CONFIG_H */ diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 9bcf2bed3a6d..02d801670f30 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -587,3 +587,15 @@ int cpu__setup_cpunode_map(void)  	closedir(dir1);  	return 0;  } + +bool cpu_map__has(struct cpu_map *cpus, int cpu) +{ +	int i; + +	for (i = 0; i < cpus->nr; ++i) { +		if (cpus->map[i] == cpu) +			return true; +	} + +	return false; +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 81a2562aaa2b..1a0a35073ce1 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -66,4 +66,6 @@ int cpu__get_node(int cpu);  int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,  		       int (*f)(struct cpu_map *map, int cpu, void *data),  		       void *data); + +bool cpu_map__has(struct cpu_map *cpus, int cpu);  #endif /* __PERF_CPUMAP_H */ diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 1921942fc2e0..be83516155ee 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -136,3 +136,44 @@ ssize_t perf_data_file__write(struct perf_data_file *file,  {  	return writen(file->fd, buf, size);  } + +int perf_data_file__switch(struct perf_data_file *file, +			   const char *postfix, +			   size_t pos, bool at_exit) +{ +	char *new_filepath; +	int ret; + +	if (check_pipe(file)) +		return -EINVAL; +	if (perf_data_file__is_read(file)) +		return -EINVAL; + +	if (asprintf(&new_filepath, "%s.%s", file->path, postfix) < 0) +		return -ENOMEM; + +	/* +	 * Only fire a warning, don't return error, continue fill +	 * original file. +	 */ +	if (rename(file->path, new_filepath)) +		pr_warning("Failed to rename %s to %s\n", file->path, new_filepath); + +	if (!at_exit) { +		close(file->fd); +		ret = perf_data_file__open(file); +		if (ret < 0) +			goto out; + +		if (lseek(file->fd, pos, SEEK_SET) == (off_t)-1) { +			ret = -errno; +			pr_debug("Failed to lseek to %zu: %s", +				 pos, strerror(errno)); +			goto out; +		} +	} +	ret = file->fd; +out: +	free(new_filepath); +	return ret; +} diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index 2b15d0c95c7f..ae510ce16cb1 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -46,5 +46,14 @@ 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); - +/* + * If at_exit is set, only rename current perf.data to + * perf.data.<postfix>, continue write on original file. + * Set at_exit when flushing the last output. + * + * Return value is fd of new output. + */ +int perf_data_file__switch(struct perf_data_file *file, +			   const char *postfix, +			   size_t pos, bool at_exit);  #endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index 049438d51b9a..8d96c80cc67e 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -23,6 +23,8 @@  #include "event.h"  #include "util.h"  #include "thread-stack.h" +#include "callchain.h" +#include "call-path.h"  #include "db-export.h"  struct deferred_export { @@ -258,8 +260,7 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,  		if (!al->sym) {  			al->sym = symbol__new(al->addr, 0, 0, "unknown");  			if (al->sym) -				symbols__insert(&dso->symbols[al->map->type], -						al->sym); +				dso__insert_symbol(dso, al->map->type, al->sym);  		}  		if (al->sym) { @@ -276,6 +277,80 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,  	return 0;  } +static struct call_path *call_path_from_sample(struct db_export *dbe, +					       struct machine *machine, +					       struct thread *thread, +					       struct perf_sample *sample, +					       struct perf_evsel *evsel) +{ +	u64 kernel_start = machine__kernel_start(machine); +	struct call_path *current = &dbe->cpr->call_path; +	enum chain_order saved_order = callchain_param.order; +	int err; + +	if (!symbol_conf.use_callchain || !sample->callchain) +		return NULL; + +	/* +	 * Since the call path tree must be built starting with the root, we +	 * must use ORDER_CALL for call chain resolution, in order to process +	 * the callchain starting with the root node and ending with the leaf. +	 */ +	callchain_param.order = ORDER_CALLER; +	err = thread__resolve_callchain(thread, &callchain_cursor, evsel, +					sample, NULL, NULL, +					sysctl_perf_event_max_stack); +	if (err) { +		callchain_param.order = saved_order; +		return NULL; +	} +	callchain_cursor_commit(&callchain_cursor); + +	while (1) { +		struct callchain_cursor_node *node; +		struct addr_location al; +		u64 dso_db_id = 0, sym_db_id = 0, offset = 0; + +		memset(&al, 0, sizeof(al)); + +		node = callchain_cursor_current(&callchain_cursor); +		if (!node) +			break; +		/* +		 * Handle export of symbol and dso for this node by +		 * constructing an addr_location struct and then passing it to +		 * db_ids_from_al() to perform the export. +		 */ +		al.sym = node->sym; +		al.map = node->map; +		al.machine = machine; +		al.addr = node->ip; + +		if (al.map && !al.sym) +			al.sym = dso__find_symbol(al.map->dso, MAP__FUNCTION, +						  al.addr); + +		db_ids_from_al(dbe, &al, &dso_db_id, &sym_db_id, &offset); + +		/* add node to the call path tree if it doesn't exist */ +		current = call_path__findnew(dbe->cpr, current, +					     al.sym, node->ip, +					     kernel_start); + +		callchain_cursor_advance(&callchain_cursor); +	} + +	/* Reset the callchain order to its prior value. */ +	callchain_param.order = saved_order; + +	if (current == &dbe->cpr->call_path) { +		/* Bail because the callchain was empty. */ +		return NULL; +	} + +	return current; +} +  int db_export__branch_type(struct db_export *dbe, u32 branch_type,  			   const char *name)  { @@ -329,6 +404,16 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,  	if (err)  		goto out_put; +	if (dbe->cpr) { +		struct call_path *cp = call_path_from_sample(dbe, al->machine, +							     thread, sample, +							     evsel); +		if (cp) { +			db_export__call_path(dbe, cp); +			es.call_path_id = cp->db_id; +		} +	} +  	if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&  	    sample_addr_correlates_sym(&evsel->attr)) {  		struct addr_location addr_al; diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h index 25e22fd76aca..67bc6b8ad2d6 100644 --- a/tools/perf/util/db-export.h +++ b/tools/perf/util/db-export.h @@ -27,6 +27,7 @@ struct dso;  struct perf_sample;  struct addr_location;  struct call_return_processor; +struct call_path_root;  struct call_path;  struct call_return; @@ -43,6 +44,7 @@ struct export_sample {  	u64			addr_dso_db_id;  	u64			addr_sym_db_id;  	u64			addr_offset; /* addr offset from symbol start */ +	u64			call_path_id;  };  struct db_export { @@ -64,6 +66,7 @@ struct db_export {  	int (*export_call_return)(struct db_export *dbe,  				  struct call_return *cr);  	struct call_return_processor *crp; +	struct call_path_root *cpr;  	u64 evsel_last_db_id;  	u64 machine_last_db_id;  	u64 thread_last_db_id; diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 8e6395439ca0..3357479082ca 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -38,7 +38,7 @@ 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]; +	char build_id_hex[SBUILD_ID_SIZE];  	int ret = 0;  	size_t len; @@ -1301,7 +1301,7 @@ size_t __dsos__fprintf(struct list_head *head, FILE *fp)  size_t dso__fprintf_buildid(struct dso *dso, FILE *fp)  { -	char sbuild_id[BUILD_ID_SIZE * 2 + 1]; +	char sbuild_id[SBUILD_ID_SIZE];  	build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);  	return fprintf(fp, "%s", sbuild_id); diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 577e600c8eb1..a347b19c961a 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -915,8 +915,7 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)  		tmp = "*";  	else if (tag == DW_TAG_subroutine_type) {  		/* Function pointer */ -		strbuf_add(buf, "(function_type)", 15); -		return 0; +		return strbuf_add(buf, "(function_type)", 15);  	} else {  		if (!dwarf_diename(&type))  			return -ENOENT; @@ -927,14 +926,10 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)  		else if (tag == DW_TAG_enumeration_type)  			tmp = "enum ";  		/* Write a base name */ -		strbuf_addf(buf, "%s%s", tmp, dwarf_diename(&type)); -		return 0; +		return strbuf_addf(buf, "%s%s", tmp, dwarf_diename(&type));  	}  	ret = die_get_typename(&type, buf); -	if (ret == 0) -		strbuf_addstr(buf, tmp); - -	return ret; +	return ret ? ret : strbuf_addstr(buf, tmp);  }  /** @@ -951,14 +946,13 @@ int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf)  	ret = die_get_typename(vr_die, buf);  	if (ret < 0) {  		pr_debug("Failed to get type, make it unknown.\n"); -		strbuf_add(buf, " (unknown_type)", 14); +		ret = strbuf_add(buf, " (unknown_type)", 14);  	} -	strbuf_addf(buf, "\t%s", dwarf_diename(vr_die)); - -	return 0; +	return ret < 0 ? ret : strbuf_addf(buf, "\t%s", dwarf_diename(vr_die));  } +#ifdef HAVE_DWARF_GETLOCATIONS  /**   * die_get_var_innermost_scope - Get innermost scope range of given variable DIE   * @sp_die: a subprogram DIE @@ -998,22 +992,24 @@ static int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die,  	}  	while ((offset = dwarf_ranges(&scopes[1], offset, &base, -				&start, &end)) > 0) { +					&start, &end)) > 0) {  		start -= entry;  		end -= entry;  		if (first) { -			strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64, -				name, start, end); +			ret = strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64, +					  name, start, end);  			first = false;  		} else { -			strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64, -				start, end); +			ret = strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64, +					  start, end);  		} +		if (ret < 0) +			goto out;  	}  	if (!first) -		strbuf_add(buf, "]>", 2); +		ret = strbuf_add(buf, "]>", 2);  out:  	free(scopes); @@ -1053,30 +1049,39 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)  	if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)  		return -EINVAL; -	while ((offset = dwarf_getlocations( -				&attr, offset, &base, -				&start, &end, &op, &nops)) > 0) { +	while ((offset = dwarf_getlocations(&attr, offset, &base, +					&start, &end, &op, &nops)) > 0) {  		if (start == 0) {  			/* Single Location Descriptions */  			ret = die_get_var_innermost_scope(sp_die, vr_die, buf); -			return ret; +			goto out;  		}  		/* Location Lists */  		start -= entry;  		end -= entry;  		if (first) { -			strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64, -				name, start, end); +			ret = strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64, +					  name, start, end);  			first = false;  		} else { -			strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64, -				start, end); +			ret = strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64, +					  start, end);  		} +		if (ret < 0) +			goto out;  	}  	if (!first) -		strbuf_add(buf, "]>", 2); - +		ret = strbuf_add(buf, "]>", 2); +out:  	return ret;  } +#else +int die_get_var_range(Dwarf_Die *sp_die __maybe_unused, +		      Dwarf_Die *vr_die __maybe_unused, +		      struct strbuf *buf __maybe_unused) +{ +	return -ENOTSUP; +} +#endif diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index dad55d04ffdd..f6fcc6832949 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -45,6 +45,7 @@ static const char *perf_event__names[] = {  	[PERF_RECORD_STAT]			= "STAT",  	[PERF_RECORD_STAT_ROUND]		= "STAT_ROUND",  	[PERF_RECORD_EVENT_UPDATE]		= "EVENT_UPDATE", +	[PERF_RECORD_TIME_CONV]			= "TIME_CONV",  };  const char *perf_event__name(unsigned int id) @@ -433,7 +434,7 @@ static int __event__synthesize_thread(union perf_event *comm_event,  {  	char filename[PATH_MAX];  	DIR *tasks; -	struct dirent dirent, *next; +	struct dirent *dirent;  	pid_t tgid, ppid;  	int rc = 0; @@ -462,11 +463,11 @@ static int __event__synthesize_thread(union perf_event *comm_event,  		return 0;  	} -	while (!readdir_r(tasks, &dirent, &next) && next) { +	while ((dirent = readdir(tasks)) != NULL) {  		char *end;  		pid_t _pid; -		_pid = strtol(dirent.d_name, &end, 10); +		_pid = strtol(dirent->d_name, &end, 10);  		if (*end)  			continue; @@ -575,7 +576,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool,  {  	DIR *proc;  	char proc_path[PATH_MAX]; -	struct dirent dirent, *next; +	struct dirent *dirent;  	union perf_event *comm_event, *mmap_event, *fork_event;  	int err = -1; @@ -600,9 +601,9 @@ int perf_event__synthesize_threads(struct perf_tool *tool,  	if (proc == NULL)  		goto out_free_fork; -	while (!readdir_r(proc, &dirent, &next) && next) { +	while ((dirent = readdir(proc)) != NULL) {  		char *end; -		pid_t pid = strtol(dirent.d_name, &end, 10); +		pid_t pid = strtol(dirent->d_name, &end, 10);  		if (*end) /* only interested in proper numerical dirents */  			continue; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 6bb1c928350d..8d363d5e65a2 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -233,6 +233,7 @@ enum perf_user_event_type { /* above any possible kernel type */  	PERF_RECORD_STAT			= 76,  	PERF_RECORD_STAT_ROUND			= 77,  	PERF_RECORD_EVENT_UPDATE		= 78, +	PERF_RECORD_TIME_CONV			= 79,  	PERF_RECORD_HEADER_MAX  }; @@ -469,6 +470,13 @@ struct stat_round_event {  	u64				time;  }; +struct time_conv_event { +	struct perf_event_header header; +	u64 time_shift; +	u64 time_mult; +	u64 time_zero; +}; +  union perf_event {  	struct perf_event_header	header;  	struct mmap_event		mmap; @@ -497,6 +505,7 @@ union perf_event {  	struct stat_config_event	stat_config;  	struct stat_event		stat;  	struct stat_round_event		stat_round; +	struct time_conv_event		time_conv;  };  void perf_event__print_totals(void); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 86a03836a83f..c4bfe11479a0 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -679,53 +679,52 @@ static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,  	return NULL;  } -union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) +/* When check_messup is true, 'end' must points to a good entry */ +static union perf_event * +perf_mmap__read(struct perf_mmap *md, bool check_messup, u64 start, +		u64 end, u64 *prev)  { -	struct perf_mmap *md = &evlist->mmap[idx]; -	u64 head; -	u64 old = md->prev;  	unsigned char *data = md->base + page_size;  	union perf_event *event = NULL; +	int diff = end - start; -	/* -	 * Check if event was unmapped due to a POLLHUP/POLLERR. -	 */ -	if (!atomic_read(&md->refcnt)) -		return NULL; - -	head = perf_mmap__read_head(md); -	if (evlist->overwrite) { +	if (check_messup) {  		/*  		 * If we're further behind than half the buffer, there's a chance  		 * the writer will bite our tail and mess up the samples under us.  		 * -		 * If we somehow ended up ahead of the head, we got messed up. +		 * If we somehow ended up ahead of the 'end', we got messed up.  		 * -		 * In either case, truncate and restart at head. +		 * In either case, truncate and restart at 'end'.  		 */ -		int diff = head - old;  		if (diff > md->mask / 2 || diff < 0) {  			fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");  			/* -			 * head points to a known good entry, start there. +			 * 'end' points to a known good entry, start there.  			 */ -			old = head; +			start = end; +			diff = 0;  		}  	} -	if (old != head) { +	if (diff >= (int)sizeof(event->header)) {  		size_t size; -		event = (union perf_event *)&data[old & md->mask]; +		event = (union perf_event *)&data[start & md->mask];  		size = event->header.size; +		if (size < sizeof(event->header) || diff < (int)size) { +			event = NULL; +			goto broken_event; +		} +  		/*  		 * Event straddles the mmap boundary -- header should always  		 * be inside due to u64 alignment of output.  		 */ -		if ((old & md->mask) + size != ((old + size) & md->mask)) { -			unsigned int offset = old; +		if ((start & md->mask) + size != ((start + size) & md->mask)) { +			unsigned int offset = start;  			unsigned int len = min(sizeof(*event), size), cpy;  			void *dst = md->event_copy; @@ -740,14 +739,83 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)  			event = (union perf_event *) md->event_copy;  		} -		old += size; +		start += size;  	} -	md->prev = old; +broken_event: +	if (prev) +		*prev = start;  	return event;  } +union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) +{ +	struct perf_mmap *md = &evlist->mmap[idx]; +	u64 head; +	u64 old = md->prev; + +	/* +	 * Check if event was unmapped due to a POLLHUP/POLLERR. +	 */ +	if (!atomic_read(&md->refcnt)) +		return NULL; + +	head = perf_mmap__read_head(md); + +	return perf_mmap__read(md, evlist->overwrite, old, head, &md->prev); +} + +union perf_event * +perf_evlist__mmap_read_backward(struct perf_evlist *evlist, int idx) +{ +	struct perf_mmap *md = &evlist->mmap[idx]; +	u64 head, end; +	u64 start = md->prev; + +	/* +	 * Check if event was unmapped due to a POLLHUP/POLLERR. +	 */ +	if (!atomic_read(&md->refcnt)) +		return NULL; + +	head = perf_mmap__read_head(md); +	if (!head) +		return NULL; + +	/* +	 * 'head' pointer starts from 0. Kernel minus sizeof(record) form +	 * it each time when kernel writes to it, so in fact 'head' is +	 * negative. 'end' pointer is made manually by adding the size of +	 * the ring buffer to 'head' pointer, means the validate data can +	 * read is the whole ring buffer. If 'end' is positive, the ring +	 * buffer has not fully filled, so we must adjust 'end' to 0. +	 * +	 * However, since both 'head' and 'end' is unsigned, we can't +	 * simply compare 'end' against 0. Here we compare '-head' and +	 * the size of the ring buffer, where -head is the number of bytes +	 * kernel write to the ring buffer. +	 */ +	if (-head < (u64)(md->mask + 1)) +		end = 0; +	else +		end = head + md->mask + 1; + +	return perf_mmap__read(md, false, start, end, &md->prev); +} + +void perf_evlist__mmap_read_catchup(struct perf_evlist *evlist, int idx) +{ +	struct perf_mmap *md = &evlist->mmap[idx]; +	u64 head; + +	if (!atomic_read(&md->refcnt)) +		return; + +	head = perf_mmap__read_head(md); +	md->prev = head; +} +  static bool perf_mmap__empty(struct perf_mmap *md)  {  	return perf_mmap__read_head(md) == md->prev && !md->auxtrace_mmap.base; @@ -986,26 +1054,34 @@ out_unmap:  	return -1;  } -static size_t perf_evlist__mmap_size(unsigned long pages) +unsigned long perf_event_mlock_kb_in_pages(void)  { -	if (pages == UINT_MAX) { -		int max; +	unsigned long pages; +	int max; -		if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { -			/* -			 * Pick a once upon a time good value, i.e. things look -			 * strange since we can't read a sysctl value, but lets not -			 * die yet... -			 */ -			max = 512; -		} else { -			max -= (page_size / 1024); -		} +	if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { +		/* +		 * Pick a once upon a time good value, i.e. things look +		 * strange since we can't read a sysctl value, but lets not +		 * die yet... +		 */ +		max = 512; +	} else { +		max -= (page_size / 1024); +	} -		pages = (max * 1024) / page_size; -		if (!is_power_of_2(pages)) -			pages = rounddown_pow_of_two(pages); -	} else if (!is_power_of_2(pages)) +	pages = (max * 1024) / page_size; +	if (!is_power_of_2(pages)) +		pages = rounddown_pow_of_two(pages); + +	return pages; +} + +static size_t perf_evlist__mmap_size(unsigned long pages) +{ +	if (pages == UINT_MAX) +		pages = perf_event_mlock_kb_in_pages(); +	else if (!is_power_of_2(pages))  		return 0;  	return (pages + 1) * page_size; @@ -1192,6 +1268,24 @@ void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,  	perf_evlist__propagate_maps(evlist);  } +void __perf_evlist__set_sample_bit(struct perf_evlist *evlist, +				   enum perf_event_sample_format bit) +{ +	struct perf_evsel *evsel; + +	evlist__for_each(evlist, evsel) +		__perf_evsel__set_sample_bit(evsel, bit); +} + +void __perf_evlist__reset_sample_bit(struct perf_evlist *evlist, +				     enum perf_event_sample_format bit) +{ +	struct perf_evsel *evsel; + +	evlist__for_each(evlist, evsel) +		__perf_evsel__reset_sample_bit(evsel, bit); +} +  int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel)  {  	struct perf_evsel *evsel; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index a0d15221db6e..85d1b59802e8 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -87,6 +87,17 @@ int perf_evlist__add_dummy(struct perf_evlist *evlist);  int perf_evlist__add_newtp(struct perf_evlist *evlist,  			   const char *sys, const char *name, void *handler); +void __perf_evlist__set_sample_bit(struct perf_evlist *evlist, +				   enum perf_event_sample_format bit); +void __perf_evlist__reset_sample_bit(struct perf_evlist *evlist, +				     enum perf_event_sample_format bit); + +#define perf_evlist__set_sample_bit(evlist, bit) \ +	__perf_evlist__set_sample_bit(evlist, PERF_SAMPLE_##bit) + +#define perf_evlist__reset_sample_bit(evlist, bit) \ +	__perf_evlist__reset_sample_bit(evlist, PERF_SAMPLE_##bit) +  int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter);  int perf_evlist__set_filter_pid(struct perf_evlist *evlist, pid_t pid);  int perf_evlist__set_filter_pids(struct perf_evlist *evlist, size_t npids, pid_t *pids); @@ -118,16 +129,23 @@ struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id);  union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx); +union perf_event *perf_evlist__mmap_read_backward(struct perf_evlist *evlist, +						  int idx); +void perf_evlist__mmap_read_catchup(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); +struct callchain_param; +  void perf_evlist__set_id_pos(struct perf_evlist *evlist);  bool perf_can_sample_identifier(void);  bool perf_can_record_switch_events(void);  bool perf_can_record_cpu_wide(void); -void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts); +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts, +			 struct callchain_param *callchain);  int record_opts__config(struct record_opts *opts);  int perf_evlist__prepare_workload(struct perf_evlist *evlist, @@ -144,6 +162,8 @@ int perf_evlist__parse_mmap_pages(const struct option *opt,  				  const char *str,  				  int unset); +unsigned long perf_event_mlock_kb_in_pages(void); +  int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,  			 bool overwrite, unsigned int auxtrace_pages,  			 bool auxtrace_overwrite); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 738ce226002b..52c7d8884741 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -226,7 +226,8 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)  		perf_evsel__init(evsel, attr, idx);  	if (perf_evsel__is_bpf_output(evsel)) { -		evsel->attr.sample_type |= PERF_SAMPLE_RAW; +		evsel->attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | +					    PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD),  		evsel->attr.sample_period = 1;  	} @@ -561,10 +562,9 @@ 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, -			     struct callchain_param *param) +void perf_evsel__config_callchain(struct perf_evsel *evsel, +				  struct record_opts *opts, +				  struct callchain_param *param)  {  	bool function = perf_evsel__is_function_event(evsel);  	struct perf_event_attr *attr = &evsel->attr; @@ -704,7 +704,7 @@ static void apply_config_terms(struct perf_evsel *evsel,  		/* set perf-event callgraph */  		if (param.enabled) -			perf_evsel__config_callgraph(evsel, opts, ¶m); +			perf_evsel__config_callchain(evsel, opts, ¶m);  	}  } @@ -736,7 +736,8 @@ static void apply_config_terms(struct perf_evsel *evsel,   *     enable/disable events specifically, as there's no   *     initial traced exec call.   */ -void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) +void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, +			struct callchain_param *callchain)  {  	struct perf_evsel *leader = evsel->leader;  	struct perf_event_attr *attr = &evsel->attr; @@ -811,8 +812,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)  	if (perf_evsel__is_function_event(evsel))  		evsel->attr.exclude_callchain_user = 1; -	if (callchain_param.enabled && !evsel->no_aux_samples) -		perf_evsel__config_callgraph(evsel, opts, &callchain_param); +	if (callchain && callchain->enabled && !evsel->no_aux_samples) +		perf_evsel__config_callchain(evsel, opts, callchain);  	if (opts->sample_intr_regs) {  		attr->sample_regs_intr = opts->sample_intr_regs; @@ -826,7 +827,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)  		perf_evsel__set_sample_bit(evsel, PERIOD);  	/* -	 * When the user explicitely disabled time don't force it here. +	 * When the user explicitly disabled time don't force it here.  	 */  	if (opts->sample_time &&  	    (!perf_missing_features.sample_id_all && @@ -1230,6 +1231,21 @@ static void __p_sample_type(char *buf, size_t size, u64 value)  	__p_bits(buf, size, value, bits);  } +static void __p_branch_sample_type(char *buf, size_t size, u64 value) +{ +#define bit_name(n) { PERF_SAMPLE_BRANCH_##n, #n } +	struct bit_names bits[] = { +		bit_name(USER), bit_name(KERNEL), bit_name(HV), bit_name(ANY), +		bit_name(ANY_CALL), bit_name(ANY_RETURN), bit_name(IND_CALL), +		bit_name(ABORT_TX), bit_name(IN_TX), bit_name(NO_TX), +		bit_name(COND), bit_name(CALL_STACK), bit_name(IND_JUMP), +		bit_name(CALL), bit_name(NO_FLAGS), bit_name(NO_CYCLES), +		{ .name = NULL, } +	}; +#undef bit_name +	__p_bits(buf, size, value, bits); +} +  static void __p_read_format(char *buf, size_t size, u64 value)  {  #define bit_name(n) { PERF_FORMAT_##n, #n } @@ -1248,6 +1264,7 @@ static void __p_read_format(char *buf, size_t size, u64 value)  #define p_unsigned(val)		snprintf(buf, BUF_SIZE, "%"PRIu64, (uint64_t)(val))  #define p_signed(val)		snprintf(buf, BUF_SIZE, "%"PRId64, (int64_t)(val))  #define p_sample_type(val)	__p_sample_type(buf, BUF_SIZE, val) +#define p_branch_sample_type(val) __p_branch_sample_type(buf, BUF_SIZE, val)  #define p_read_format(val)	__p_read_format(buf, BUF_SIZE, val)  #define PRINT_ATTRn(_n, _f, _p)				\ @@ -1299,12 +1316,13 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,  	PRINT_ATTRf(comm_exec, p_unsigned);  	PRINT_ATTRf(use_clockid, p_unsigned);  	PRINT_ATTRf(context_switch, p_unsigned); +	PRINT_ATTRf(write_backward, p_unsigned);  	PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned);  	PRINT_ATTRf(bp_type, p_unsigned);  	PRINT_ATTRn("{ bp_addr, config1 }", bp_addr, p_hex);  	PRINT_ATTRn("{ bp_len, config2 }", bp_len, p_hex); -	PRINT_ATTRf(branch_sample_type, p_unsigned); +	PRINT_ATTRf(branch_sample_type, p_branch_sample_type);  	PRINT_ATTRf(sample_regs_user, p_hex);  	PRINT_ATTRf(sample_stack_user, p_unsigned);  	PRINT_ATTRf(clockid, p_signed); @@ -2253,98 +2271,11 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,  	return 0;  } -static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) -{ -	va_list args; -	int ret = 0; - -	if (!*first) { -		ret += fprintf(fp, ","); -	} else { -		ret += fprintf(fp, ":"); -		*first = false; -	} - -	va_start(args, fmt); -	ret += vfprintf(fp, fmt, args); -	va_end(args); -	return ret; -} - -static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv) -{ -	return comma_fprintf(fp, (bool *)priv, " %s: %s", name, val); -} - -int perf_evsel__fprintf(struct perf_evsel *evsel, -			struct perf_attr_details *details, FILE *fp) -{ -	bool first = true; -	int printed = 0; - -	if (details->event_group) { -		struct perf_evsel *pos; - -		if (!perf_evsel__is_group_leader(evsel)) -			return 0; - -		if (evsel->nr_members > 1) -			printed += fprintf(fp, "%s{", evsel->group_name ?: ""); - -		printed += fprintf(fp, "%s", perf_evsel__name(evsel)); -		for_each_group_member(pos, evsel) -			printed += fprintf(fp, ",%s", perf_evsel__name(pos)); - -		if (evsel->nr_members > 1) -			printed += fprintf(fp, "}"); -		goto out; -	} - -	printed += fprintf(fp, "%s", perf_evsel__name(evsel)); - -	if (details->verbose) { -		printed += perf_event_attr__fprintf(fp, &evsel->attr, -						    __print_attr__fprintf, &first); -	} else if (details->freq) { -		const char *term = "sample_freq"; - -		if (!evsel->attr.freq) -			term = "sample_period"; - -		printed += comma_fprintf(fp, &first, " %s=%" PRIu64, -					 term, (u64)evsel->attr.sample_freq); -	} - -	if (details->trace_fields) { -		struct format_field *field; - -		if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { -			printed += comma_fprintf(fp, &first, " (not a tracepoint)"); -			goto out; -		} - -		field = evsel->tp_format->format.fields; -		if (field == NULL) { -			printed += comma_fprintf(fp, &first, " (no trace field)"); -			goto out; -		} - -		printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name); - -		field = field->next; -		while (field) { -			printed += comma_fprintf(fp, &first, "%s", field->name); -			field = field->next; -		} -	} -out: -	fputc('\n', fp); -	return ++printed; -} -  bool perf_evsel__fallback(struct perf_evsel *evsel, int err,  			  char *msg, size_t msgsize)  { +	int paranoid; +  	if ((err == ENOENT || err == ENXIO || err == ENODEV) &&  	    evsel->attr.type   == PERF_TYPE_HARDWARE &&  	    evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) { @@ -2364,6 +2295,22 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,  		zfree(&evsel->name);  		return true; +	} else if (err == EACCES && !evsel->attr.exclude_kernel && +		   (paranoid = perf_event_paranoid()) > 1) { +		const char *name = perf_evsel__name(evsel); +		char *new_name; + +		if (asprintf(&new_name, "%s%su", name, strchr(name, ':') ? "" : ":") < 0) +			return false; + +		if (evsel->name) +			free(evsel->name); +		evsel->name = new_name; +		scnprintf(msg, msgsize, +"kernel.perf_event_paranoid=%d, trying to fall back to excluding kernel samples", paranoid); +		evsel->attr.exclude_kernel = 1; + +		return true;  	}  	return false; @@ -2382,12 +2329,13 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,  		 "Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n"  		 "which controls use of the performance events system by\n"  		 "unprivileged users (without CAP_SYS_ADMIN).\n\n" -		 "The default value is 1:\n\n" +		 "The current value is %d:\n\n"  		 "  -1: Allow use of (almost) all events by all users\n"  		 ">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n"  		 ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n"  		 ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN", -				 target->system_wide ? "system-wide " : ""); +				 target->system_wide ? "system-wide " : "", +				 perf_event_paranoid());  	case ENOENT:  		return scnprintf(msg, size, "The %s event is not supported.",  				 perf_evsel__name(evsel)); @@ -2397,10 +2345,18 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,  			 "Probably the maximum number of open file descriptors has been reached.\n"  			 "Hint: Try again after reducing the number of events.\n"  			 "Hint: Try increasing the limit with 'ulimit -n <limit>'"); +	case ENOMEM: +		if ((evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) != 0 && +		    access("/proc/sys/kernel/perf_event_max_stack", F_OK) == 0) +			return scnprintf(msg, size, +					 "Not enough memory to setup event with callchain.\n" +					 "Hint: Try tweaking /proc/sys/kernel/perf_event_max_stack\n" +					 "Hint: Current value: %d", sysctl_perf_event_max_stack); +		break;  	case ENODEV:  		if (target->cpu_list)  			return scnprintf(msg, size, "%s", -	 "No such device - did you specify an out-of-range profile CPU?\n"); +	 "No such device - did you specify an out-of-range profile CPU?");  		break;  	case EOPNOTSUPP:  		if (evsel->attr.precise_ip) @@ -2432,7 +2388,7 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,  	return scnprintf(msg, size,  	"The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n"  	"/bin/dmesg may provide additional information.\n" -	"No CONFIG_PERF_EVENTS=y kernel support configured?\n", +	"No CONFIG_PERF_EVENTS=y kernel support configured?",  			 err, strerror_r(err, sbuf, sizeof(sbuf)),  			 perf_evsel__name(evsel));  } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 501ea6e565f1..8a644fef452c 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -178,8 +178,14 @@ void perf_evsel__init(struct perf_evsel *evsel,  void perf_evsel__exit(struct perf_evsel *evsel);  void perf_evsel__delete(struct perf_evsel *evsel); +struct callchain_param; +  void perf_evsel__config(struct perf_evsel *evsel, -			struct record_opts *opts); +			struct record_opts *opts, +			struct callchain_param *callchain); +void perf_evsel__config_callchain(struct perf_evsel *evsel, +				  struct record_opts *opts, +				  struct callchain_param *callchain);  int __perf_evsel__sample_size(u64 sample_type);  void perf_evsel__calc_id_pos(struct perf_evsel *evsel); @@ -381,6 +387,24 @@ struct perf_attr_details {  int perf_evsel__fprintf(struct perf_evsel *evsel,  			struct perf_attr_details *details, FILE *fp); +#define EVSEL__PRINT_IP			(1<<0) +#define EVSEL__PRINT_SYM		(1<<1) +#define EVSEL__PRINT_DSO		(1<<2) +#define EVSEL__PRINT_SYMOFFSET		(1<<3) +#define EVSEL__PRINT_ONELINE		(1<<4) +#define EVSEL__PRINT_SRCLINE		(1<<5) +#define EVSEL__PRINT_UNKNOWN_AS_ADDR	(1<<6) + +struct callchain_cursor; + +int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, +			      unsigned int print_opts, +			      struct callchain_cursor *cursor, FILE *fp); + +int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, +			int left_alignment, unsigned int print_opts, +			struct callchain_cursor *cursor, FILE *fp); +  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 target *target, @@ -396,7 +420,7 @@ for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); 	\       (_evsel) && (_evsel)->leader == (_leader);					\       (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node)) -static inline bool has_branch_callstack(struct perf_evsel *evsel) +static inline bool perf_evsel__has_branch_callstack(const struct perf_evsel *evsel)  {  	return evsel->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK;  } diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c new file mode 100644 index 000000000000..3674e77ad640 --- /dev/null +++ b/tools/perf/util/evsel_fprintf.c @@ -0,0 +1,212 @@ +#include <stdio.h> +#include <stdbool.h> +#include <traceevent/event-parse.h> +#include "evsel.h" +#include "callchain.h" +#include "map.h" +#include "symbol.h" + +static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) +{ +	va_list args; +	int ret = 0; + +	if (!*first) { +		ret += fprintf(fp, ","); +	} else { +		ret += fprintf(fp, ":"); +		*first = false; +	} + +	va_start(args, fmt); +	ret += vfprintf(fp, fmt, args); +	va_end(args); +	return ret; +} + +static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv) +{ +	return comma_fprintf(fp, (bool *)priv, " %s: %s", name, val); +} + +int perf_evsel__fprintf(struct perf_evsel *evsel, +			struct perf_attr_details *details, FILE *fp) +{ +	bool first = true; +	int printed = 0; + +	if (details->event_group) { +		struct perf_evsel *pos; + +		if (!perf_evsel__is_group_leader(evsel)) +			return 0; + +		if (evsel->nr_members > 1) +			printed += fprintf(fp, "%s{", evsel->group_name ?: ""); + +		printed += fprintf(fp, "%s", perf_evsel__name(evsel)); +		for_each_group_member(pos, evsel) +			printed += fprintf(fp, ",%s", perf_evsel__name(pos)); + +		if (evsel->nr_members > 1) +			printed += fprintf(fp, "}"); +		goto out; +	} + +	printed += fprintf(fp, "%s", perf_evsel__name(evsel)); + +	if (details->verbose) { +		printed += perf_event_attr__fprintf(fp, &evsel->attr, +						    __print_attr__fprintf, &first); +	} else if (details->freq) { +		const char *term = "sample_freq"; + +		if (!evsel->attr.freq) +			term = "sample_period"; + +		printed += comma_fprintf(fp, &first, " %s=%" PRIu64, +					 term, (u64)evsel->attr.sample_freq); +	} + +	if (details->trace_fields) { +		struct format_field *field; + +		if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { +			printed += comma_fprintf(fp, &first, " (not a tracepoint)"); +			goto out; +		} + +		field = evsel->tp_format->format.fields; +		if (field == NULL) { +			printed += comma_fprintf(fp, &first, " (no trace field)"); +			goto out; +		} + +		printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name); + +		field = field->next; +		while (field) { +			printed += comma_fprintf(fp, &first, "%s", field->name); +			field = field->next; +		} +	} +out: +	fputc('\n', fp); +	return ++printed; +} + +int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, +			      unsigned int print_opts, struct callchain_cursor *cursor, +			      FILE *fp) +{ +	int printed = 0; +	struct callchain_cursor_node *node; +	int print_ip = print_opts & EVSEL__PRINT_IP; +	int print_sym = print_opts & EVSEL__PRINT_SYM; +	int print_dso = print_opts & EVSEL__PRINT_DSO; +	int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; +	int print_oneline = print_opts & EVSEL__PRINT_ONELINE; +	int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; +	int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; +	char s = print_oneline ? ' ' : '\t'; + +	if (sample->callchain) { +		struct addr_location node_al; + +		callchain_cursor_commit(cursor); + +		while (1) { +			u64 addr = 0; + +			node = callchain_cursor_current(cursor); +			if (!node) +				break; + +			if (node->sym && node->sym->ignore) +				goto next; + +			printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); + +			if (print_ip) +				printed += fprintf(fp, "%c%16" PRIx64, s, node->ip); + +			if (node->map) +				addr = node->map->map_ip(node->map, node->ip); + +			if (print_sym) { +				printed += fprintf(fp, " "); +				node_al.addr = addr; +				node_al.map  = node->map; + +				if (print_symoffset) { +					printed += __symbol__fprintf_symname_offs(node->sym, &node_al, +										  print_unknown_as_addr, fp); +				} else { +					printed += __symbol__fprintf_symname(node->sym, &node_al, +									     print_unknown_as_addr, fp); +				} +			} + +			if (print_dso) { +				printed += fprintf(fp, " ("); +				printed += map__fprintf_dsoname(node->map, fp); +				printed += fprintf(fp, ")"); +			} + +			if (print_srcline) +				printed += map__fprintf_srcline(node->map, addr, "\n  ", fp); + +			if (!print_oneline) +				printed += fprintf(fp, "\n"); +next: +			callchain_cursor_advance(cursor); +		} +	} + +	return printed; +} + +int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, +			int left_alignment, unsigned int print_opts, +			struct callchain_cursor *cursor, FILE *fp) +{ +	int printed = 0; +	int print_ip = print_opts & EVSEL__PRINT_IP; +	int print_sym = print_opts & EVSEL__PRINT_SYM; +	int print_dso = print_opts & EVSEL__PRINT_DSO; +	int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; +	int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; +	int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; + +	if (cursor != NULL) { +		printed += sample__fprintf_callchain(sample, left_alignment, +						     print_opts, cursor, fp); +	} else if (!(al->sym && al->sym->ignore)) { +		printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); + +		if (print_ip) +			printed += fprintf(fp, "%16" PRIx64, sample->ip); + +		if (print_sym) { +			printed += fprintf(fp, " "); +			if (print_symoffset) { +				printed += __symbol__fprintf_symname_offs(al->sym, al, +									  print_unknown_as_addr, fp); +			} else { +				printed += __symbol__fprintf_symname(al->sym, al, +								     print_unknown_as_addr, fp); +			} +		} + +		if (print_dso) { +			printed += fprintf(fp, " ("); +			printed += map__fprintf_dsoname(al->map, fp); +			printed += fprintf(fp, ")"); +		} + +		if (print_srcline) +			printed += map__fprintf_srcline(al->map, al->addr, "\n  ", fp); +	} + +	return printed; +} diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 90680ec9f8b8..08852dde1378 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1474,7 +1474,7 @@ static int __event_process_build_id(struct build_id_event *bev,  	dso = machine__findnew_dso(machine, filename);  	if (dso != NULL) { -		char sbuild_id[BUILD_ID_SIZE * 2 + 1]; +		char sbuild_id[SBUILD_ID_SIZE];  		dso__set_build_id(dso, &bev->build_id); @@ -1819,7 +1819,8 @@ static int process_cpu_topology(struct perf_file_section *section,  	ph->env.nr_sibling_cores = nr;  	size += sizeof(u32); -	strbuf_init(&sb, 128); +	if (strbuf_init(&sb, 128) < 0) +		goto free_cpu;  	for (i = 0; i < nr; i++) {  		str = do_read_string(fd, ph); @@ -1827,7 +1828,8 @@ static int process_cpu_topology(struct perf_file_section *section,  			goto error;  		/* include a NULL character at the end */ -		strbuf_add(&sb, str, strlen(str) + 1); +		if (strbuf_add(&sb, str, strlen(str) + 1) < 0) +			goto error;  		size += string_size(str);  		free(str);  	} @@ -1849,7 +1851,8 @@ static int process_cpu_topology(struct perf_file_section *section,  			goto error;  		/* include a NULL character at the end */ -		strbuf_add(&sb, str, strlen(str) + 1); +		if (strbuf_add(&sb, str, strlen(str) + 1) < 0) +			goto error;  		size += string_size(str);  		free(str);  	} @@ -1912,13 +1915,14 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse  	/* nr nodes */  	ret = readn(fd, &nr, sizeof(nr));  	if (ret != sizeof(nr)) -		goto error; +		return -1;  	if (ph->needs_swap)  		nr = bswap_32(nr);  	ph->env.nr_numa_nodes = nr; -	strbuf_init(&sb, 256); +	if (strbuf_init(&sb, 256) < 0) +		return -1;  	for (i = 0; i < nr; i++) {  		/* node number */ @@ -1940,15 +1944,17 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse  			mem_free = bswap_64(mem_free);  		} -		strbuf_addf(&sb, "%u:%"PRIu64":%"PRIu64":", -			    node, mem_total, mem_free); +		if (strbuf_addf(&sb, "%u:%"PRIu64":%"PRIu64":", +				node, mem_total, mem_free) < 0) +			goto error;  		str = do_read_string(fd, ph);  		if (!str)  			goto error;  		/* include a NULL character at the end */ -		strbuf_add(&sb, str, strlen(str) + 1); +		if (strbuf_add(&sb, str, strlen(str) + 1) < 0) +			goto error;  		free(str);  	}  	ph->env.numa_nodes = strbuf_detach(&sb, NULL); @@ -1982,7 +1988,8 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused  	}  	ph->env.nr_pmu_mappings = pmu_num; -	strbuf_init(&sb, 128); +	if (strbuf_init(&sb, 128) < 0) +		return -1;  	while (pmu_num) {  		if (readn(fd, &type, sizeof(type)) != sizeof(type)) @@ -1994,9 +2001,11 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused  		if (!name)  			goto error; -		strbuf_addf(&sb, "%u:%s", type, name); +		if (strbuf_addf(&sb, "%u:%s", type, name) < 0) +			goto error;  		/* include a NULL character at the end */ -		strbuf_add(&sb, "", 1); +		if (strbuf_add(&sb, "", 1) < 0) +			goto error;  		if (!strcmp(name, "msr"))  			ph->env.msr_pmu_type = type; diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c index 43a98a4dc1e1..d62ccaeeadd6 100644 --- a/tools/perf/util/help-unknown-cmd.c +++ b/tools/perf/util/help-unknown-cmd.c @@ -27,16 +27,27 @@ static int levenshtein_compare(const void *p1, const void *p2)  	return l1 != l2 ? l1 - l2 : strcmp(s1, s2);  } -static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) +static int add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)  { -	unsigned int i; - -	ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc); - +	unsigned int i, nr = cmds->cnt + old->cnt; +	void *tmp; + +	if (nr > cmds->alloc) { +		/* Choose bigger one to alloc */ +		if (alloc_nr(cmds->alloc) < nr) +			cmds->alloc = nr; +		else +			cmds->alloc = alloc_nr(cmds->alloc); +		tmp = realloc(cmds->names, cmds->alloc * sizeof(*cmds->names)); +		if (!tmp) +			return -1; +		cmds->names = tmp; +	}  	for (i = 0; i < old->cnt; i++)  		cmds->names[cmds->cnt++] = old->names[i];  	zfree(&old->names);  	old->cnt = 0; +	return 0;  }  const char *help_unknown_cmd(const char *cmd) @@ -52,8 +63,11 @@ const char *help_unknown_cmd(const char *cmd)  	load_command_list("perf-", &main_cmds, &other_cmds); -	add_cmd_list(&main_cmds, &aliases); -	add_cmd_list(&main_cmds, &other_cmds); +	if (add_cmd_list(&main_cmds, &aliases) < 0 || +	    add_cmd_list(&main_cmds, &other_cmds) < 0) { +		fprintf(stderr, "ERROR: Failed to allocate command list for unknown command.\n"); +		goto end; +	}  	qsort(main_cmds.names, main_cmds.cnt,  	      sizeof(main_cmds.names), cmdname_compare);  	uniq(&main_cmds); @@ -99,6 +113,6 @@ const char *help_unknown_cmd(const char *cmd)  		for (i = 0; i < n; i++)  			fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);  	} - +end:  	exit(1);  } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 31c4641fe5ff..cfab531437c7 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -295,7 +295,7 @@ static void hists__delete_entry(struct hists *hists, struct hist_entry *he)  		root_in  = &he->parent_he->hroot_in;  		root_out = &he->parent_he->hroot_out;  	} else { -		if (sort__need_collapse) +		if (hists__has(hists, need_collapse))  			root_in = &hists->entries_collapsed;  		else  			root_in = hists->entries_in; @@ -953,7 +953,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,  {  	int err, err2; -	err = sample__resolve_callchain(iter->sample, &iter->parent, +	err = sample__resolve_callchain(iter->sample, &callchain_cursor, &iter->parent,  					iter->evsel, al, max_stack_depth);  	if (err)  		return err; @@ -1295,8 +1295,9 @@ static int hists__hierarchy_insert_entry(struct hists *hists,  	return ret;  } -int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root, -				 struct hist_entry *he) +static int hists__collapse_insert_entry(struct hists *hists, +					struct rb_root *root, +					struct hist_entry *he)  {  	struct rb_node **p = &root->rb_node;  	struct rb_node *parent = NULL; @@ -1372,7 +1373,7 @@ int hists__collapse_resort(struct hists *hists, struct ui_progress *prog)  	struct hist_entry *n;  	int ret; -	if (!sort__need_collapse) +	if (!hists__has(hists, need_collapse))  		return 0;  	hists->nr_entries = 0; @@ -1631,7 +1632,7 @@ static void output_resort(struct hists *hists, struct ui_progress *prog,  		return;  	} -	if (sort__need_collapse) +	if (hists__has(hists, need_collapse))  		root = &hists->entries_collapsed;  	else  		root = hists->entries_in; @@ -2035,7 +2036,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,  	struct hist_entry *he;  	int64_t cmp; -	if (sort__need_collapse) +	if (hists__has(hists, need_collapse))  		root = &hists->entries_collapsed;  	else  		root = hists->entries_in; @@ -2061,6 +2062,8 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,  	if (he) {  		memset(&he->stat, 0, sizeof(he->stat));  		he->hists = hists; +		if (symbol_conf.cumulate_callchain) +			memset(he->stat_acc, 0, sizeof(he->stat));  		rb_link_node(&he->rb_node_in, parent, p);  		rb_insert_color(&he->rb_node_in, root);  		hists__inc_stats(hists, he); @@ -2075,7 +2078,7 @@ static struct hist_entry *hists__find_entry(struct hists *hists,  {  	struct rb_node *n; -	if (sort__need_collapse) +	if (hists__has(hists, need_collapse))  		n = hists->entries_collapsed.rb_node;  	else  		n = hists->entries_in->rb_node; @@ -2104,7 +2107,7 @@ void hists__match(struct hists *leader, struct hists *other)  	struct rb_node *nd;  	struct hist_entry *pos, *pair; -	if (sort__need_collapse) +	if (hists__has(leader, need_collapse))  		root = &leader->entries_collapsed;  	else  		root = leader->entries_in; @@ -2129,7 +2132,7 @@ int hists__link(struct hists *leader, struct hists *other)  	struct rb_node *nd;  	struct hist_entry *pos, *pair; -	if (sort__need_collapse) +	if (hists__has(other, need_collapse))  		root = &other->entries_collapsed;  	else  		root = other->entries_in; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index bec0cd660fbd..0f84bfb42bb1 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -82,6 +82,8 @@ struct hists {  	int			nr_hpp_node;  }; +#define hists__has(__h, __f) (__h)->hpp_list->__f +  struct hist_entry_iter;  struct hist_iter_ops { @@ -199,8 +201,6 @@ int hists__init(void);  int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list);  struct rb_root *hists__get_rotate_entries_in(struct hists *hists); -int hists__collapse_insert_entry(struct hists *hists, -				  struct rb_root *root, struct hist_entry *he);  struct perf_hpp {  	char *buf; @@ -240,6 +240,14 @@ struct perf_hpp_fmt {  struct perf_hpp_list {  	struct list_head fields;  	struct list_head sorts; + +	int need_collapse; +	int parent; +	int sym; +	int dso; +	int socket; +	int thread; +	int comm;  };  extern struct perf_hpp_list perf_hpp_list; diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index abf1366e2a24..9df996085563 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -66,6 +66,7 @@ struct intel_bts {  	u64				branches_id;  	size_t				branches_event_size;  	bool				synth_needs_swap; +	unsigned long			num_events;  };  struct intel_bts_queue { @@ -275,6 +276,10 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq,  	union perf_event event;  	struct perf_sample sample = { .ip = 0, }; +	if (bts->synth_opts.initial_skip && +	    bts->num_events++ <= bts->synth_opts.initial_skip) +		return 0; +  	event.sample.header.type = PERF_RECORD_SAMPLE;  	event.sample.header.misc = PERF_RECORD_MISC_USER;  	event.sample.header.size = sizeof(struct perf_event_header); diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 9409d014b46c..9c8f15da86ce 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c @@ -356,7 +356,7 @@ static const char *intel_pt_err_msgs[] = {  int intel_pt__strerror(int code, char *buf, size_t buflen)  { -	if (code < 1 || code > INTEL_PT_ERR_MAX) +	if (code < 1 || code >= INTEL_PT_ERR_MAX)  		code = INTEL_PT_ERR_UNK;  	strlcpy(buf, intel_pt_err_msgs[code], buflen);  	return 0; diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 407f11b97c8d..137196990012 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -100,6 +100,8 @@ struct intel_pt {  	u64 cyc_bit;  	u64 noretcomp_bit;  	unsigned max_non_turbo_ratio; + +	unsigned long num_events;  };  enum switch_state { @@ -972,6 +974,10 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)  	if (pt->branches_filter && !(pt->branches_filter & ptq->flags))  		return 0; +	if (pt->synth_opts.initial_skip && +	    pt->num_events++ < pt->synth_opts.initial_skip) +		return 0; +  	event->sample.header.type = PERF_RECORD_SAMPLE;  	event->sample.header.misc = PERF_RECORD_MISC_USER;  	event->sample.header.size = sizeof(struct perf_event_header); @@ -1029,6 +1035,10 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)  	union perf_event *event = ptq->event_buf;  	struct perf_sample sample = { .ip = 0, }; +	if (pt->synth_opts.initial_skip && +	    pt->num_events++ < pt->synth_opts.initial_skip) +		return 0; +  	event->sample.header.type = PERF_RECORD_SAMPLE;  	event->sample.header.misc = PERF_RECORD_MISC_USER;  	event->sample.header.size = sizeof(struct perf_event_header); @@ -1087,6 +1097,10 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq)  	union perf_event *event = ptq->event_buf;  	struct perf_sample sample = { .ip = 0, }; +	if (pt->synth_opts.initial_skip && +	    pt->num_events++ < pt->synth_opts.initial_skip) +		return 0; +  	event->sample.header.type = PERF_RECORD_SAMPLE;  	event->sample.header.misc = PERF_RECORD_MISC_USER;  	event->sample.header.size = sizeof(struct perf_event_header); @@ -1130,7 +1144,7 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq)  		pr_err("Intel Processor Trace: failed to deliver transaction event, error %d\n",  		       ret); -	if (pt->synth_opts.callchain) +	if (pt->synth_opts.last_branch)  		intel_pt_reset_last_branch_rb(ptq);  	return ret; @@ -1199,14 +1213,18 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)  	ptq->have_sample = false;  	if (pt->sample_instructions && -	    (state->type & INTEL_PT_INSTRUCTION)) { +	    (state->type & INTEL_PT_INSTRUCTION) && +	    (!pt->synth_opts.initial_skip || +	     pt->num_events++ >= pt->synth_opts.initial_skip)) {  		err = intel_pt_synth_instruction_sample(ptq);  		if (err)  			return err;  	}  	if (pt->sample_transactions && -	    (state->type & INTEL_PT_TRANSACTION)) { +	    (state->type & INTEL_PT_TRANSACTION) && +	    (!pt->synth_opts.initial_skip || +	     pt->num_events++ >= pt->synth_opts.initial_skip)) {  		err = intel_pt_synth_transaction_sample(ptq);  		if (err)  			return err; diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index ad0c0bb1fbc7..86afe9618bb0 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -17,6 +17,7 @@  #include "strlist.h"  #include <elf.h> +#include "tsc.h"  #include "session.h"  #include "jit.h"  #include "jitdump.h" @@ -33,6 +34,7 @@ struct jit_buf_desc {  	size_t           bufsize;  	FILE             *in;  	bool		 needs_bswap; /* handles cross-endianess */ +	bool		 use_arch_timestamp;  	void		 *debug_data;  	size_t		 nr_debug_entries;  	uint32_t         code_load_count; @@ -158,13 +160,16 @@ jit_open(struct jit_buf_desc *jd, const char *name)  		header.flags      = bswap_64(header.flags);  	} +	jd->use_arch_timestamp = header.flags & JITDUMP_FLAGS_ARCH_TIMESTAMP; +  	if (verbose > 2) -		pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\n", +		pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\nuse_arch_timestamp=%d\n",  			header.version,  			header.total_size,  			(unsigned long long)header.timestamp,  			header.pid, -			header.elf_mach); +			header.elf_mach, +			jd->use_arch_timestamp);  	if (header.flags & JITDUMP_FLAGS_RESERVED) {  		pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", @@ -172,10 +177,15 @@ jit_open(struct jit_buf_desc *jd, const char *name)  		goto error;  	} +	if (jd->use_arch_timestamp && !jd->session->time_conv.time_mult) { +		pr_err("jitdump file uses arch timestamps but there is no timestamp conversion\n"); +		goto error; +	} +  	/*  	 * validate event is using the correct clockid  	 */ -	if (jit_validate_events(jd->session)) { +	if (!jd->use_arch_timestamp && jit_validate_events(jd->session)) {  		pr_err("error, jitted code must be sampled with perf record -k 1\n");  		goto error;  	} @@ -329,6 +339,23 @@ jit_inject_event(struct jit_buf_desc *jd, union perf_event *event)  	return 0;  } +static uint64_t convert_timestamp(struct jit_buf_desc *jd, uint64_t timestamp) +{ +	struct perf_tsc_conversion tc; + +	if (!jd->use_arch_timestamp) +		return timestamp; + +	tc.time_shift = jd->session->time_conv.time_shift; +	tc.time_mult  = jd->session->time_conv.time_mult; +	tc.time_zero  = jd->session->time_conv.time_zero; + +	if (!tc.time_mult) +		return 0; + +	return tsc_to_perf_time(timestamp, &tc); +} +  static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)  {  	struct perf_sample sample; @@ -385,7 +412,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)  		return -1;  	}  	if (stat(filename, &st)) -		memset(&st, 0, sizeof(stat)); +		memset(&st, 0, sizeof(st));  	event->mmap2.header.type = PERF_RECORD_MMAP2;  	event->mmap2.header.misc = PERF_RECORD_MISC_USER; @@ -410,7 +437,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)  		id->tid  = tid;  	}  	if (jd->sample_type & PERF_SAMPLE_TIME) -		id->time = jr->load.p.timestamp; +		id->time = convert_timestamp(jd, jr->load.p.timestamp);  	/*  	 * create pseudo sample to induce dso hit increment @@ -473,7 +500,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)  	size++; /* for \0 */  	if (stat(filename, &st)) -		memset(&st, 0, sizeof(stat)); +		memset(&st, 0, sizeof(st));  	size = PERF_ALIGN(size, sizeof(u64)); @@ -499,7 +526,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)  		id->tid  = tid;  	}  	if (jd->sample_type & PERF_SAMPLE_TIME) -		id->time = jr->load.p.timestamp; +		id->time = convert_timestamp(jd, jr->load.p.timestamp);  	/*  	 * create pseudo sample to induce dso hit increment diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h index b66c1f503d9e..bcacd20d0c1c 100644 --- a/tools/perf/util/jitdump.h +++ b/tools/perf/util/jitdump.h @@ -23,9 +23,12 @@  #define JITHEADER_VERSION 1  enum jitdump_flags_bits { +	JITDUMP_FLAGS_ARCH_TIMESTAMP_BIT,  	JITDUMP_FLAGS_MAX_BIT,  }; +#define JITDUMP_FLAGS_ARCH_TIMESTAMP	(1ULL << JITDUMP_FLAGS_ARCH_TIMESTAMP_BIT) +  #define JITDUMP_FLAGS_RESERVED (JITDUMP_FLAGS_MAX_BIT < 64 ? \  				(~((1ULL << JITDUMP_FLAGS_MAX_BIT) - 1)) : 0) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 80b9b6a87990..f9644f79686c 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -32,6 +32,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)  	machine->threads = RB_ROOT;  	pthread_rwlock_init(&machine->threads_lock, NULL); +	machine->nr_threads = 0;  	INIT_LIST_HEAD(&machine->dead_threads);  	machine->last_match = NULL; @@ -361,7 +362,7 @@ out_err:  }  /* - * Caller must eventually drop thread->refcnt returned with a successfull + * Caller must eventually drop thread->refcnt returned with a successful   * lookup/new thread inserted.   */  static struct thread *____machine__findnew_thread(struct machine *machine, @@ -430,6 +431,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine,  		 */  		thread__get(th);  		machine->last_match = th; +		++machine->nr_threads;  	}  	return th; @@ -681,11 +683,13 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp)  size_t machine__fprintf(struct machine *machine, FILE *fp)  { -	size_t ret = 0; +	size_t ret;  	struct rb_node *nd;  	pthread_rwlock_rdlock(&machine->threads_lock); +	ret = fprintf(fp, "Threads: %u\n", machine->nr_threads); +  	for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) {  		struct thread *pos = rb_entry(nd, struct thread, rb_node); @@ -908,11 +912,11 @@ int machines__create_kernel_maps(struct machines *machines, pid_t pid)  	return machine__create_kernel_maps(machine);  } -int machine__load_kallsyms(struct machine *machine, const char *filename, -			   enum map_type type, symbol_filter_t filter) +int __machine__load_kallsyms(struct machine *machine, const char *filename, +			     enum map_type type, bool no_kcore, symbol_filter_t filter)  {  	struct map *map = machine__kernel_map(machine); -	int ret = dso__load_kallsyms(map->dso, filename, map, filter); +	int ret = __dso__load_kallsyms(map->dso, filename, map, no_kcore, filter);  	if (ret > 0) {  		dso__set_loaded(map->dso, type); @@ -927,6 +931,12 @@ int machine__load_kallsyms(struct machine *machine, const char *filename,  	return ret;  } +int machine__load_kallsyms(struct machine *machine, const char *filename, +			   enum map_type type, symbol_filter_t filter) +{ +	return __machine__load_kallsyms(machine, filename, type, false, filter); +} +  int machine__load_vmlinux_path(struct machine *machine, enum map_type type,  			       symbol_filter_t filter)  { @@ -1413,6 +1423,7 @@ static void __machine__remove_thread(struct machine *machine, struct thread *th,  		pthread_rwlock_wrlock(&machine->threads_lock);  	rb_erase_init(&th->rb_node, &machine->threads);  	RB_CLEAR_NODE(&th->rb_node); +	--machine->nr_threads;  	/*  	 * Move it first to the dead_threads list, then drop the reference,  	 * if this is the last reference, then the thread__delete destructor @@ -1599,6 +1610,7 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,  }  static int add_callchain_ip(struct thread *thread, +			    struct callchain_cursor *cursor,  			    struct symbol **parent,  			    struct addr_location *root_al,  			    u8 *cpumode, @@ -1630,7 +1642,7 @@ static int add_callchain_ip(struct thread *thread,  				 * It seems the callchain is corrupted.  				 * Discard all.  				 */ -				callchain_cursor_reset(&callchain_cursor); +				callchain_cursor_reset(cursor);  				return 1;  			}  			return 0; @@ -1640,7 +1652,7 @@ static int add_callchain_ip(struct thread *thread,  	}  	if (al.sym != NULL) { -		if (sort__has_parent && !*parent && +		if (perf_hpp_list.parent && !*parent &&  		    symbol__match_regex(al.sym, &parent_regex))  			*parent = al.sym;  		else if (have_ignore_callees && root_al && @@ -1648,13 +1660,13 @@ static int add_callchain_ip(struct thread *thread,  			/* Treat this symbol as the root,  			   forgetting its callees. */  			*root_al = al; -			callchain_cursor_reset(&callchain_cursor); +			callchain_cursor_reset(cursor);  		}  	}  	if (symbol_conf.hide_unresolved && al.sym == NULL)  		return 0; -	return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym); +	return callchain_cursor_append(cursor, al.addr, al.map, al.sym);  }  struct branch_info *sample__resolve_bstack(struct perf_sample *sample, @@ -1724,6 +1736,7 @@ static int remove_loops(struct branch_entry *l, int nr)   * negative error code on other errors.   */  static int resolve_lbr_callchain_sample(struct thread *thread, +					struct callchain_cursor *cursor,  					struct perf_sample *sample,  					struct symbol **parent,  					struct addr_location *root_al, @@ -1756,7 +1769,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,  		 */  		int mix_chain_nr = i + 1 + lbr_nr + 1; -		if (mix_chain_nr > PERF_MAX_STACK_DEPTH + PERF_MAX_BRANCH_DEPTH) { +		if (mix_chain_nr > (int)sysctl_perf_event_max_stack + PERF_MAX_BRANCH_DEPTH) {  			pr_warning("corrupted callchain. skipping...\n");  			return 0;  		} @@ -1778,7 +1791,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,  					ip = lbr_stack->entries[0].to;  			} -			err = add_callchain_ip(thread, parent, root_al, &cpumode, ip); +			err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip);  			if (err)  				return (err < 0) ? err : 0;  		} @@ -1789,6 +1802,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,  }  static int thread__resolve_callchain_sample(struct thread *thread, +					    struct callchain_cursor *cursor,  					    struct perf_evsel *evsel,  					    struct perf_sample *sample,  					    struct symbol **parent, @@ -1803,10 +1817,8 @@ static int thread__resolve_callchain_sample(struct thread *thread,  	int skip_idx = -1;  	int first_call = 0; -	callchain_cursor_reset(&callchain_cursor); - -	if (has_branch_callstack(evsel)) { -		err = resolve_lbr_callchain_sample(thread, sample, parent, +	if (perf_evsel__has_branch_callstack(evsel)) { +		err = resolve_lbr_callchain_sample(thread, cursor, sample, parent,  						   root_al, max_stack);  		if (err)  			return (err < 0) ? err : 0; @@ -1816,7 +1828,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,  	 * Based on DWARF debug information, some architectures skip  	 * a callchain entry saved by the kernel.  	 */ -	if (chain->nr < PERF_MAX_STACK_DEPTH) +	if (chain->nr < sysctl_perf_event_max_stack)  		skip_idx = arch_skip_callchain_idx(thread, chain);  	/* @@ -1863,10 +1875,10 @@ static int thread__resolve_callchain_sample(struct thread *thread,  		nr = remove_loops(be, nr);  		for (i = 0; i < nr; i++) { -			err = add_callchain_ip(thread, parent, root_al, +			err = add_callchain_ip(thread, cursor, parent, root_al,  					       NULL, be[i].to);  			if (!err) -				err = add_callchain_ip(thread, parent, root_al, +				err = add_callchain_ip(thread, cursor, parent, root_al,  						       NULL, be[i].from);  			if (err == -EINVAL)  				break; @@ -1877,7 +1889,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,  	}  check_calls: -	if (chain->nr > PERF_MAX_STACK_DEPTH && (int)chain->nr > max_stack) { +	if (chain->nr > sysctl_perf_event_max_stack && (int)chain->nr > max_stack) {  		pr_warning("corrupted callchain. skipping...\n");  		return 0;  	} @@ -1896,7 +1908,7 @@ check_calls:  #endif  		ip = chain->ips[j]; -		err = add_callchain_ip(thread, parent, root_al, &cpumode, ip); +		err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip);  		if (err)  			return (err < 0) ? err : 0; @@ -1915,19 +1927,12 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)  				       entry->map, entry->sym);  } -int thread__resolve_callchain(struct thread *thread, -			      struct perf_evsel *evsel, -			      struct perf_sample *sample, -			      struct symbol **parent, -			      struct addr_location *root_al, -			      int max_stack) +static int thread__resolve_callchain_unwind(struct thread *thread, +					    struct callchain_cursor *cursor, +					    struct perf_evsel *evsel, +					    struct perf_sample *sample, +					    int max_stack)  { -	int ret = thread__resolve_callchain_sample(thread, evsel, -						   sample, parent, -						   root_al, max_stack); -	if (ret) -		return ret; -  	/* Can we do dwarf post unwind? */  	if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) &&  	      (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER))) @@ -1938,9 +1943,45 @@ int thread__resolve_callchain(struct thread *thread,  	    (!sample->user_stack.size))  		return 0; -	return unwind__get_entries(unwind_entry, &callchain_cursor, +	return unwind__get_entries(unwind_entry, cursor,  				   thread, sample, max_stack); +} + +int thread__resolve_callchain(struct thread *thread, +			      struct callchain_cursor *cursor, +			      struct perf_evsel *evsel, +			      struct perf_sample *sample, +			      struct symbol **parent, +			      struct addr_location *root_al, +			      int max_stack) +{ +	int ret = 0; + +	callchain_cursor_reset(&callchain_cursor); +	if (callchain_param.order == ORDER_CALLEE) { +		ret = thread__resolve_callchain_sample(thread, cursor, +						       evsel, sample, +						       parent, root_al, +						       max_stack); +		if (ret) +			return ret; +		ret = thread__resolve_callchain_unwind(thread, cursor, +						       evsel, sample, +						       max_stack); +	} else { +		ret = thread__resolve_callchain_unwind(thread, cursor, +						       evsel, sample, +						       max_stack); +		if (ret) +			return ret; +		ret = thread__resolve_callchain_sample(thread, cursor, +						       evsel, sample, +						       parent, root_al, +						       max_stack); +	} + +	return ret;  }  int machine__for_each_thread(struct machine *machine, diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 8499db281158..83f46790c52f 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -31,6 +31,7 @@ struct machine {  	char		  *root_dir;  	struct rb_root	  threads;  	pthread_rwlock_t  threads_lock; +	unsigned int	  nr_threads;  	struct list_head  dead_threads;  	struct thread	  *last_match;  	struct vdso_info  *vdso_info; @@ -141,7 +142,11 @@ 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); + +struct callchain_cursor; +  int thread__resolve_callchain(struct thread *thread, +			      struct callchain_cursor *cursor,  			      struct perf_evsel *evsel,  			      struct perf_sample *sample,  			      struct symbol **parent, @@ -211,6 +216,8 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *machine,  struct map *machine__findnew_module_map(struct machine *machine, u64 start,  					const char *filename); +int __machine__load_kallsyms(struct machine *machine, const char *filename, +			     enum map_type type, bool no_kcore, symbol_filter_t filter);  int machine__load_kallsyms(struct machine *machine, const char *filename,  			   enum map_type type, symbol_filter_t filter);  int machine__load_vmlinux_path(struct machine *machine, enum map_type type, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 171b6d10a04b..b19bcd3b7128 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -289,7 +289,7 @@ int map__load(struct map *map, symbol_filter_t filter)  	nr = dso__load(map->dso, map, filter);  	if (nr < 0) {  		if (map->dso->has_build_id) { -			char sbuild_id[BUILD_ID_SIZE * 2 + 1]; +			char sbuild_id[SBUILD_ID_SIZE];  			build_id__sprintf(map->dso->build_id,  					  sizeof(map->dso->build_id), @@ -431,6 +431,13 @@ u64 map__rip_2objdump(struct map *map, u64 rip)  	if (map->dso->rel)  		return rip - map->pgoff; +	/* +	 * kernel modules also have DSO_TYPE_USER in dso->kernel, +	 * but all kernel modules are ET_REL, so won't get here. +	 */ +	if (map->dso->kernel == DSO_TYPE_USER) +		return rip + map->dso->text_offset; +  	return map->unmap_ip(map, rip) - map->reloc;  } @@ -454,6 +461,13 @@ u64 map__objdump_2mem(struct map *map, u64 ip)  	if (map->dso->rel)  		return map->unmap_ip(map, ip + map->pgoff); +	/* +	 * kernel modules also have DSO_TYPE_USER in dso->kernel, +	 * but all kernel modules are ET_REL, so won't get here. +	 */ +	if (map->dso->kernel == DSO_TYPE_USER) +		return map->unmap_ip(map, ip - map->dso->text_offset); +  	return ip + map->reloc;  } diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c index b1b9e2385f4b..fe84df1875aa 100644 --- a/tools/perf/util/ordered-events.c +++ b/tools/perf/util/ordered-events.c @@ -308,3 +308,12 @@ void ordered_events__free(struct ordered_events *oe)  		free(event);  	}  } + +void ordered_events__reinit(struct ordered_events *oe) +{ +	ordered_events__deliver_t old_deliver = oe->deliver; + +	ordered_events__free(oe); +	memset(oe, '\0', sizeof(*oe)); +	ordered_events__init(oe, old_deliver); +} diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h index f403991e3bfd..e11468a9a6e4 100644 --- a/tools/perf/util/ordered-events.h +++ b/tools/perf/util/ordered-events.h @@ -49,6 +49,7 @@ void ordered_events__delete(struct ordered_events *oe, struct ordered_event *eve  int ordered_events__flush(struct ordered_events *oe, enum oe_flush how);  void ordered_events__init(struct ordered_events *oe, ordered_events__deliver_t deliver);  void ordered_events__free(struct ordered_events *oe); +void ordered_events__reinit(struct ordered_events *oe);  static inline  void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4c19d5e79d8c..c6fd0479f4cd 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -138,11 +138,11 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {  #define PERF_EVENT_TYPE(config)		__PERF_EVENT_FIELD(config, TYPE)  #define PERF_EVENT_ID(config)		__PERF_EVENT_FIELD(config, EVENT) -#define for_each_subsystem(sys_dir, sys_dirent, sys_next)	       \ -	while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next)	       \ -	if (sys_dirent.d_type == DT_DIR &&				       \ -	   (strcmp(sys_dirent.d_name, ".")) &&				       \ -	   (strcmp(sys_dirent.d_name, ".."))) +#define for_each_subsystem(sys_dir, sys_dirent)			\ +	while ((sys_dirent = readdir(sys_dir)) != NULL)		\ +		if (sys_dirent->d_type == DT_DIR &&		\ +		    (strcmp(sys_dirent->d_name, ".")) &&	\ +		    (strcmp(sys_dirent->d_name, "..")))  static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)  { @@ -159,12 +159,12 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)  	return 0;  } -#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next)	       \ -	while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next)        \ -	if (evt_dirent.d_type == DT_DIR &&				       \ -	   (strcmp(evt_dirent.d_name, ".")) &&				       \ -	   (strcmp(evt_dirent.d_name, "..")) &&				       \ -	   (!tp_event_has_id(&sys_dirent, &evt_dirent))) +#define for_each_event(sys_dirent, evt_dir, evt_dirent)		\ +	while ((evt_dirent = readdir(evt_dir)) != NULL)		\ +		if (evt_dirent->d_type == DT_DIR &&		\ +		    (strcmp(evt_dirent->d_name, ".")) &&	\ +		    (strcmp(evt_dirent->d_name, "..")) &&	\ +		    (!tp_event_has_id(sys_dirent, evt_dirent)))  #define MAX_EVENT_LENGTH 512 @@ -173,7 +173,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  {  	struct tracepoint_path *path = NULL;  	DIR *sys_dir, *evt_dir; -	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; +	struct dirent *sys_dirent, *evt_dirent;  	char id_buf[24];  	int fd;  	u64 id; @@ -184,18 +184,18 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  	if (!sys_dir)  		return NULL; -	for_each_subsystem(sys_dir, sys_dirent, sys_next) { +	for_each_subsystem(sys_dir, sys_dirent) {  		snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, -			 sys_dirent.d_name); +			 sys_dirent->d_name);  		evt_dir = opendir(dir_path);  		if (!evt_dir)  			continue; -		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { +		for_each_event(sys_dirent, evt_dir, evt_dirent) {  			snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path, -				 evt_dirent.d_name); +				 evt_dirent->d_name);  			fd = open(evt_path, O_RDONLY);  			if (fd < 0)  				continue; @@ -220,9 +220,9 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  					free(path);  					return NULL;  				} -				strncpy(path->system, sys_dirent.d_name, +				strncpy(path->system, sys_dirent->d_name,  					MAX_EVENT_LENGTH); -				strncpy(path->name, evt_dirent.d_name, +				strncpy(path->name, evt_dirent->d_name,  					MAX_EVENT_LENGTH);  				return path;  			} @@ -1649,7 +1649,7 @@ static void parse_events_print_error(struct parse_events_error *err,  		buf = _buf; -		/* We're cutting from the beggining. */ +		/* We're cutting from the beginning. */  		if (err->idx > max_err_idx)  			cut = err->idx - max_err_idx; @@ -1812,7 +1812,7 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,  			     bool name_only)  {  	DIR *sys_dir, *evt_dir; -	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; +	struct dirent *sys_dirent, *evt_dirent;  	char evt_path[MAXPATHLEN];  	char dir_path[MAXPATHLEN];  	char **evt_list = NULL; @@ -1830,20 +1830,20 @@ restart:  			goto out_close_sys_dir;  	} -	for_each_subsystem(sys_dir, sys_dirent, sys_next) { +	for_each_subsystem(sys_dir, sys_dirent) {  		if (subsys_glob != NULL && -		    !strglobmatch(sys_dirent.d_name, subsys_glob)) +		    !strglobmatch(sys_dirent->d_name, subsys_glob))  			continue;  		snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, -			 sys_dirent.d_name); +			 sys_dirent->d_name);  		evt_dir = opendir(dir_path);  		if (!evt_dir)  			continue; -		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { +		for_each_event(sys_dirent, evt_dir, evt_dirent) {  			if (event_glob != NULL && -			    !strglobmatch(evt_dirent.d_name, event_glob)) +			    !strglobmatch(evt_dirent->d_name, event_glob))  				continue;  			if (!evt_num_known) { @@ -1852,7 +1852,7 @@ restart:  			}  			snprintf(evt_path, MAXPATHLEN, "%s:%s", -				 sys_dirent.d_name, evt_dirent.d_name); +				 sys_dirent->d_name, evt_dirent->d_name);  			evt_list[evt_i] = strdup(evt_path);  			if (evt_list[evt_i] == NULL) @@ -1905,7 +1905,7 @@ out_close_sys_dir:  int is_valid_tracepoint(const char *event_string)  {  	DIR *sys_dir, *evt_dir; -	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; +	struct dirent *sys_dirent, *evt_dirent;  	char evt_path[MAXPATHLEN];  	char dir_path[MAXPATHLEN]; @@ -1913,17 +1913,17 @@ int is_valid_tracepoint(const char *event_string)  	if (!sys_dir)  		return 0; -	for_each_subsystem(sys_dir, sys_dirent, sys_next) { +	for_each_subsystem(sys_dir, sys_dirent) {  		snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, -			 sys_dirent.d_name); +			 sys_dirent->d_name);  		evt_dir = opendir(dir_path);  		if (!evt_dir)  			continue; -		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { +		for_each_event(sys_dirent, evt_dir, evt_dirent) {  			snprintf(evt_path, MAXPATHLEN, "%s:%s", -				 sys_dirent.d_name, evt_dirent.d_name); +				 sys_dirent->d_name, evt_dirent->d_name);  			if (!strcmp(evt_path, event_string)) {  				closedir(evt_dir);  				closedir(sys_dir); diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index 6b8eb13e14e4..c4023f22f287 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c @@ -12,18 +12,18 @@ 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)) +	if (regs->cache_mask & (1ULL << id))  		goto out; -	if (!(mask & (1 << id))) +	if (!(mask & (1ULL << id)))  		return -EINVAL;  	for (i = 0; i < id; i++) { -		if (mask & (1 << i)) +		if (mask & (1ULL << i))  			idx++;  	} -	regs->cache_mask |= (1 << id); +	regs->cache_mask |= (1ULL << id);  	regs->cache_regs[id] = regs->regs[idx];  out: diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index adef23b1352e..ddb0261b2577 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -602,14 +602,13 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v,  static __u64 pmu_format_max_value(const unsigned long *format)  { -	int w; +	__u64 w = 0; +	int fbit; -	w = bitmap_weight(format, PERF_PMU_FORMAT_BITS); -	if (!w) -		return 0; -	if (w < 64) -		return (1ULL << w) - 1; -	return -1; +	for_each_set_bit(fbit, format, PERF_PMU_FORMAT_BITS) +		w |= (1ULL << fbit); + +	return w;  }  /* @@ -644,20 +643,20 @@ static int pmu_resolve_param_term(struct parse_events_term *term,  static char *pmu_formats_string(struct list_head *formats)  {  	struct perf_pmu_format *format; -	char *str; -	struct strbuf buf; +	char *str = NULL; +	struct strbuf buf = STRBUF_INIT;  	unsigned i = 0;  	if (!formats)  		return NULL; -	strbuf_init(&buf, 0);  	/* sysfs exported terms */  	list_for_each_entry(format, formats, list) -		strbuf_addf(&buf, i++ ? ",%s" : "%s", -			    format->name); +		if (strbuf_addf(&buf, i++ ? ",%s" : "%s", format->name) < 0) +			goto error;  	str = strbuf_detach(&buf, NULL); +error:  	strbuf_release(&buf);  	return str; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 8319fbb08636..74401a20106d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -265,6 +265,65 @@ static bool kprobe_warn_out_range(const char *symbol, unsigned long address)  	return true;  } +/* + * NOTE: + * '.gnu.linkonce.this_module' section of kernel module elf directly + * maps to 'struct module' from linux/module.h. This section contains + * actual module name which will be used by kernel after loading it. + * But, we cannot use 'struct module' here since linux/module.h is not + * exposed to user-space. Offset of 'name' has remained same from long + * time, so hardcoding it here. + */ +#ifdef __LP64__ +#define MOD_NAME_OFFSET 24 +#else +#define MOD_NAME_OFFSET 12 +#endif + +/* + * @module can be module name of module file path. In case of path, + * inspect elf and find out what is actual module name. + * Caller has to free mod_name after using it. + */ +static char *find_module_name(const char *module) +{ +	int fd; +	Elf *elf; +	GElf_Ehdr ehdr; +	GElf_Shdr shdr; +	Elf_Data *data; +	Elf_Scn *sec; +	char *mod_name = NULL; + +	fd = open(module, O_RDONLY); +	if (fd < 0) +		return NULL; + +	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); +	if (elf == NULL) +		goto elf_err; + +	if (gelf_getehdr(elf, &ehdr) == NULL) +		goto ret_err; + +	sec = elf_section_by_name(elf, &ehdr, &shdr, +			".gnu.linkonce.this_module", NULL); +	if (!sec) +		goto ret_err; + +	data = elf_getdata(sec, NULL); +	if (!data || !data->d_buf) +		goto ret_err; + +	mod_name = strdup((char *)data->d_buf + MOD_NAME_OFFSET); + +ret_err: +	elf_end(elf); +elf_err: +	close(fd); +	return mod_name; +} +  #ifdef HAVE_DWARF_SUPPORT  static int kernel_get_module_dso(const char *module, struct dso **pdso) @@ -486,8 +545,10 @@ static int get_text_start_address(const char *exec, unsigned long *address)  		return -errno;  	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); -	if (elf == NULL) -		return -EINVAL; +	if (elf == NULL) { +		ret = -EINVAL; +		goto out_close; +	}  	if (gelf_getehdr(elf, &ehdr) == NULL)  		goto out; @@ -499,6 +560,9 @@ static int get_text_start_address(const char *exec, unsigned long *address)  	ret = 0;  out:  	elf_end(elf); +out_close: +	close(fd); +  	return ret;  } @@ -583,32 +647,23 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,  					    int ntevs, const char *module)  {  	int i, ret = 0; -	char *tmp; +	char *mod_name = NULL;  	if (!module)  		return 0; -	tmp = strrchr(module, '/'); -	if (tmp) { -		/* This is a module path -- get the module name */ -		module = strdup(tmp + 1); -		if (!module) -			return -ENOMEM; -		tmp = strchr(module, '.'); -		if (tmp) -			*tmp = '\0'; -		tmp = (char *)module;	/* For free() */ -	} +	mod_name = find_module_name(module);  	for (i = 0; i < ntevs; i++) { -		tevs[i].point.module = strdup(module); +		tevs[i].point.module = +			strdup(mod_name ? mod_name : module);  		if (!tevs[i].point.module) {  			ret = -ENOMEM;  			break;  		}  	} -	free(tmp); +	free(mod_name);  	return ret;  } @@ -1618,69 +1673,65 @@ out:  }  /* Compose only probe arg */ -int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) +char *synthesize_perf_probe_arg(struct perf_probe_arg *pa)  {  	struct perf_probe_arg_field *field = pa->field; -	int ret; -	char *tmp = buf; +	struct strbuf buf; +	char *ret = NULL; +	int err; + +	if (strbuf_init(&buf, 64) < 0) +		return NULL;  	if (pa->name && pa->var) -		ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var); +		err = strbuf_addf(&buf, "%s=%s", pa->name, pa->var);  	else -		ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var); -	if (ret <= 0) -		goto error; -	tmp += ret; -	len -= ret; +		err = strbuf_addstr(&buf, pa->name ?: pa->var); +	if (err) +		goto out;  	while (field) {  		if (field->name[0] == '[') -			ret = e_snprintf(tmp, len, "%s", field->name); +			err = strbuf_addstr(&buf, field->name);  		else -			ret = e_snprintf(tmp, len, "%s%s", -					 field->ref ? "->" : ".", field->name); -		if (ret <= 0) -			goto error; -		tmp += ret; -		len -= ret; +			err = strbuf_addf(&buf, "%s%s", field->ref ? "->" : ".", +					  field->name);  		field = field->next; +		if (err) +			goto out;  	} -	if (pa->type) { -		ret = e_snprintf(tmp, len, ":%s", pa->type); -		if (ret <= 0) -			goto error; -		tmp += ret; -		len -= ret; -	} +	if (pa->type) +		if (strbuf_addf(&buf, ":%s", pa->type) < 0) +			goto out; -	return tmp - buf; -error: -	pr_debug("Failed to synthesize perf probe argument: %d\n", ret); +	ret = strbuf_detach(&buf, NULL); +out: +	strbuf_release(&buf);  	return ret;  }  /* Compose only probe point (not argument) */  static char *synthesize_perf_probe_point(struct perf_probe_point *pp)  { -	char *buf, *tmp; -	char offs[32] = "", line[32] = "", file[32] = ""; -	int ret, len; +	struct strbuf buf; +	char *tmp, *ret = NULL; +	int len, err = 0; -	buf = zalloc(MAX_CMDLEN); -	if (buf == NULL) { -		ret = -ENOMEM; -		goto error; -	} -	if (pp->offset) { -		ret = e_snprintf(offs, 32, "+%lu", pp->offset); -		if (ret <= 0) -			goto error; -	} -	if (pp->line) { -		ret = e_snprintf(line, 32, ":%d", pp->line); -		if (ret <= 0) -			goto error; +	if (strbuf_init(&buf, 64) < 0) +		return NULL; + +	if (pp->function) { +		if (strbuf_addstr(&buf, pp->function) < 0) +			goto out; +		if (pp->offset) +			err = strbuf_addf(&buf, "+%lu", pp->offset); +		else if (pp->line) +			err = strbuf_addf(&buf, ":%d", pp->line); +		else if (pp->retprobe) +			err = strbuf_addstr(&buf, "%return"); +		if (err) +			goto out;  	}  	if (pp->file) {  		tmp = pp->file; @@ -1689,25 +1740,15 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)  			tmp = strchr(pp->file + len - 30, '/');  			tmp = tmp ? tmp + 1 : pp->file + len - 30;  		} -		ret = e_snprintf(file, 32, "@%s", tmp); -		if (ret <= 0) -			goto error; +		err = strbuf_addf(&buf, "@%s", tmp); +		if (!err && !pp->function && pp->line) +			err = strbuf_addf(&buf, ":%d", pp->line);  	} - -	if (pp->function) -		ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, -				 offs, pp->retprobe ? "%return" : "", line, -				 file); -	else -		ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); -	if (ret <= 0) -		goto error; - -	return buf; -error: -	pr_debug("Failed to synthesize perf probe point: %d\n", ret); -	free(buf); -	return NULL; +	if (!err) +		ret = strbuf_detach(&buf, NULL); +out: +	strbuf_release(&buf); +	return ret;  }  #if 0 @@ -1736,45 +1777,32 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev)  #endif  static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref, -					     char **buf, size_t *buflen, -					     int depth) +					    struct strbuf *buf, int depth)  { -	int ret; +	int err;  	if (ref->next) {  		depth = __synthesize_probe_trace_arg_ref(ref->next, buf, -							 buflen, depth + 1); +							 depth + 1);  		if (depth < 0) -			goto out; -	} - -	ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); -	if (ret < 0) -		depth = ret; -	else { -		*buf += ret; -		*buflen -= ret; +			return depth;  	} -out: -	return depth; - +	err = strbuf_addf(buf, "%+ld(", ref->offset); +	return (err < 0) ? err : depth;  }  static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, -				       char *buf, size_t buflen) +				      struct strbuf *buf)  {  	struct probe_trace_arg_ref *ref = arg->ref; -	int ret, depth = 0; -	char *tmp = buf; +	int depth = 0, err;  	/* Argument name or separator */  	if (arg->name) -		ret = e_snprintf(buf, buflen, " %s=", arg->name); +		err = strbuf_addf(buf, " %s=", arg->name);  	else -		ret = e_snprintf(buf, buflen, " "); -	if (ret < 0) -		return ret; -	buf += ret; -	buflen -= ret; +		err = strbuf_addch(buf, ' '); +	if (err) +		return err;  	/* Special case: @XXX */  	if (arg->value[0] == '@' && arg->ref) @@ -1782,59 +1810,44 @@ static int synthesize_probe_trace_arg(struct probe_trace_arg *arg,  	/* Dereferencing arguments */  	if (ref) { -		depth = __synthesize_probe_trace_arg_ref(ref, &buf, -							  &buflen, 1); +		depth = __synthesize_probe_trace_arg_ref(ref, buf, 1);  		if (depth < 0)  			return depth;  	}  	/* Print argument value */  	if (arg->value[0] == '@' && arg->ref) -		ret = e_snprintf(buf, buflen, "%s%+ld", arg->value, -				 arg->ref->offset); +		err = strbuf_addf(buf, "%s%+ld", arg->value, arg->ref->offset);  	else -		ret = e_snprintf(buf, buflen, "%s", arg->value); -	if (ret < 0) -		return ret; -	buf += ret; -	buflen -= ret; +		err = strbuf_addstr(buf, arg->value);  	/* Closing */ -	while (depth--) { -		ret = e_snprintf(buf, buflen, ")"); -		if (ret < 0) -			return ret; -		buf += ret; -		buflen -= ret; -	} +	while (!err && depth--) +		err = strbuf_addch(buf, ')'); +  	/* Print argument type */ -	if (arg->type) { -		ret = e_snprintf(buf, buflen, ":%s", arg->type); -		if (ret <= 0) -			return ret; -		buf += ret; -	} +	if (!err && arg->type) +		err = strbuf_addf(buf, ":%s", arg->type); -	return buf - tmp; +	return err;  }  char *synthesize_probe_trace_command(struct probe_trace_event *tev)  {  	struct probe_trace_point *tp = &tev->point; -	char *buf; -	int i, len, ret; +	struct strbuf buf; +	char *ret = NULL; +	int i, err; -	buf = zalloc(MAX_CMDLEN); -	if (buf == NULL) +	/* Uprobes must have tp->module */ +	if (tev->uprobes && !tp->module)  		return NULL; -	len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p', -			 tev->group, tev->event); -	if (len <= 0) -		goto error; +	if (strbuf_init(&buf, 32) < 0) +		return NULL; -	/* Uprobes must have tp->module */ -	if (tev->uprobes && !tp->module) +	if (strbuf_addf(&buf, "%c:%s/%s ", tp->retprobe ? 'r' : 'p', +			tev->group, tev->event) < 0)  		goto error;  	/*  	 * If tp->address == 0, then this point must be a @@ -1849,34 +1862,25 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)  	/* Use the tp->address for uprobes */  	if (tev->uprobes) -		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", -				 tp->module, tp->address); +		err = strbuf_addf(&buf, "%s:0x%lx", tp->module, tp->address);  	else if (!strncmp(tp->symbol, "0x", 2))  		/* Absolute address. See try_to_find_absolute_address() */ -		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s0x%lx", -				 tp->module ?: "", tp->module ? ":" : "", -				 tp->address); +		err = strbuf_addf(&buf, "%s%s0x%lx", tp->module ?: "", +				  tp->module ? ":" : "", tp->address);  	else -		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu", -				 tp->module ?: "", tp->module ? ":" : "", -				 tp->symbol, tp->offset); - -	if (ret <= 0) +		err = strbuf_addf(&buf, "%s%s%s+%lu", tp->module ?: "", +				tp->module ? ":" : "", tp->symbol, tp->offset); +	if (err)  		goto error; -	len += ret; -	for (i = 0; i < tev->nargs; i++) { -		ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, -						  MAX_CMDLEN - len); -		if (ret <= 0) +	for (i = 0; i < tev->nargs; i++) +		if (synthesize_probe_trace_arg(&tev->args[i], &buf) < 0)  			goto error; -		len += ret; -	} -	return buf; +	ret = strbuf_detach(&buf, NULL);  error: -	free(buf); -	return NULL; +	strbuf_release(&buf); +	return ret;  }  static int find_perf_probe_point_from_map(struct probe_trace_point *tp, @@ -1958,7 +1962,7 @@ static int convert_to_perf_probe_point(struct probe_trace_point *tp,  static int convert_to_perf_probe_event(struct probe_trace_event *tev,  			       struct perf_probe_event *pev, bool is_kprobe)  { -	char buf[64] = ""; +	struct strbuf buf = STRBUF_INIT;  	int i, ret;  	/* Convert event/group name */ @@ -1981,14 +1985,15 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev,  		if (tev->args[i].name)  			pev->args[i].name = strdup(tev->args[i].name);  		else { -			ret = synthesize_probe_trace_arg(&tev->args[i], -							  buf, 64); -			pev->args[i].name = strdup(buf); +			if ((ret = strbuf_init(&buf, 32)) < 0) +				goto error; +			ret = synthesize_probe_trace_arg(&tev->args[i], &buf); +			pev->args[i].name = strbuf_detach(&buf, NULL);  		}  		if (pev->args[i].name == NULL && ret >= 0)  			ret = -ENOMEM;  	} - +error:  	if (ret < 0)  		clear_perf_probe_event(pev); @@ -2162,35 +2167,38 @@ static int perf_probe_event__sprintf(const char *group, const char *event,  				     struct strbuf *result)  {  	int i, ret; -	char buf[128]; -	char *place; +	char *buf; -	/* Synthesize only event probe point */ -	place = synthesize_perf_probe_point(&pev->point); -	if (!place) -		return -EINVAL; +	if (asprintf(&buf, "%s:%s", group, event) < 0) +		return -errno; +	ret = strbuf_addf(result, "  %-20s (on ", buf); +	free(buf); +	if (ret) +		return ret; -	ret = e_snprintf(buf, 128, "%s:%s", group, event); -	if (ret < 0) -		goto out; +	/* Synthesize only event probe point */ +	buf = synthesize_perf_probe_point(&pev->point); +	if (!buf) +		return -ENOMEM; +	ret = strbuf_addstr(result, buf); +	free(buf); -	strbuf_addf(result, "  %-20s (on %s", buf, place); -	if (module) -		strbuf_addf(result, " in %s", module); +	if (!ret && module) +		ret = strbuf_addf(result, " in %s", module); -	if (pev->nargs > 0) { -		strbuf_add(result, " with", 5); -		for (i = 0; i < pev->nargs; i++) { -			ret = synthesize_perf_probe_arg(&pev->args[i], -							buf, 128); -			if (ret < 0) -				goto out; -			strbuf_addf(result, " %s", buf); +	if (!ret && pev->nargs > 0) { +		ret = strbuf_add(result, " with", 5); +		for (i = 0; !ret && i < pev->nargs; i++) { +			buf = synthesize_perf_probe_arg(&pev->args[i]); +			if (!buf) +				return -ENOMEM; +			ret = strbuf_addf(result, " %s", buf); +			free(buf);  		}  	} -	strbuf_addch(result, ')'); -out: -	free(place); +	if (!ret) +		ret = strbuf_addch(result, ')'); +  	return ret;  } @@ -2498,7 +2506,8 @@ static int find_probe_functions(struct map *map, char *name,  void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,  				struct probe_trace_event *tev __maybe_unused, -				struct map *map __maybe_unused) { } +				struct map *map __maybe_unused, +				struct symbol *sym __maybe_unused) { }  /*   * Find probe function addresses from map. @@ -2516,6 +2525,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,  	struct probe_trace_point *tp;  	int num_matched_functions;  	int ret, i, j, skipped = 0; +	char *mod_name;  	map = get_target_map(pev->target, pev->uprobes);  	if (!map) { @@ -2600,9 +2610,19 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,  		tp->realname = strdup_or_goto(sym->name, nomem_out);  		tp->retprobe = pp->retprobe; -		if (pev->target) -			tev->point.module = strdup_or_goto(pev->target, -							   nomem_out); +		if (pev->target) { +			if (pev->uprobes) { +				tev->point.module = strdup_or_goto(pev->target, +								   nomem_out); +			} else { +				mod_name = find_module_name(pev->target); +				tev->point.module = +					strdup(mod_name ? mod_name : pev->target); +				free(mod_name); +				if (!tev->point.module) +					goto nomem_out; +			} +		}  		tev->uprobes = pev->uprobes;  		tev->nargs = pev->nargs;  		if (tev->nargs) { @@ -2624,7 +2644,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,  					strdup_or_goto(pev->args[i].type,  							nomem_out);  		} -		arch__fix_tev_from_maps(pev, tev, map); +		arch__fix_tev_from_maps(pev, tev, map, sym);  	}  	if (ret == skipped) {  		ret = -ENOENT; @@ -2743,9 +2763,13 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,  {  	int ret; -	if (pev->uprobes && !pev->group) { -		/* Replace group name if not given */ -		ret = convert_exec_to_group(pev->target, &pev->group); +	if (!pev->group) { +		/* Set group name if not given */ +		if (!pev->uprobes) { +			pev->group = strdup(PERFPROBE_GROUP); +			ret = pev->group ? 0 : -ENOMEM; +		} else +			ret = convert_exec_to_group(pev->target, &pev->group);  		if (ret != 0) {  			pr_warning("Failed to make a group name.\n");  			return ret; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e54e7b011577..5a27eb4fad05 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -120,7 +120,7 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev);  /* Events to command string */  char *synthesize_perf_probe_command(struct perf_probe_event *pev);  char *synthesize_probe_trace_command(struct probe_trace_event *tev); -int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len); +char *synthesize_perf_probe_arg(struct perf_probe_arg *pa);  /* Check the perf_probe_event needs debuginfo */  bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); @@ -154,7 +154,8 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,  int show_available_funcs(const char *module, struct strfilter *filter, bool user);  bool arch__prefers_symtab(void);  void arch__fix_tev_from_maps(struct perf_probe_event *pev, -			     struct probe_trace_event *tev, struct map *map); +			     struct probe_trace_event *tev, struct map *map, +			     struct symbol *sym);  /* If there is no space to write, returns -E2BIG. */  int e_snprintf(char *str, size_t size, const char *format, ...) diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index e3b3b92e4458..3fe6214970e6 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -220,8 +220,7 @@ int probe_file__add_event(int fd, struct probe_trace_event *tev)  	pr_debug("Writing event: %s\n", buf);  	if (!probe_event_dry_run) { -		ret = write(fd, buf, strlen(buf)); -		if (ret <= 0) { +		if (write(fd, buf, strlen(buf)) < (int)strlen(buf)) {  			ret = -errno;  			pr_warning("Failed to write event: %s\n",  				   strerror_r(errno, sbuf, sizeof(sbuf))); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index b3bd0fba0237..1259839dbf6d 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -553,7 +553,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)  static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)  {  	Dwarf_Die vr_die; -	char buf[32], *ptr; +	char *buf, *ptr;  	int ret = 0;  	/* Copy raw parameters */ @@ -563,13 +563,13 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)  	if (pf->pvar->name)  		pf->tvar->name = strdup(pf->pvar->name);  	else { -		ret = synthesize_perf_probe_arg(pf->pvar, buf, 32); -		if (ret < 0) -			return ret; +		buf = synthesize_perf_probe_arg(pf->pvar); +		if (!buf) +			return -ENOMEM;  		ptr = strchr(buf, ':');	/* Change type separator to _ */  		if (ptr)  			*ptr = '_'; -		pf->tvar->name = strdup(buf); +		pf->tvar->name = buf;  	}  	if (pf->tvar->name == NULL)  		return -ENOMEM; @@ -1294,6 +1294,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)  {  	struct available_var_finder *af = data;  	struct variable_list *vl; +	struct strbuf buf = STRBUF_INIT;  	int tag, ret;  	vl = &af->vls[af->nvls - 1]; @@ -1307,25 +1308,26 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)  		if (ret == 0 || ret == -ERANGE) {  			int ret2;  			bool externs = !af->child; -			struct strbuf buf; -			strbuf_init(&buf, 64); +			if (strbuf_init(&buf, 64) < 0) +				goto error;  			if (probe_conf.show_location_range) { -				if (!externs) { -					if (ret) -						strbuf_add(&buf, "[INV]\t", 6); -					else -						strbuf_add(&buf, "[VAL]\t", 6); -				} else -					strbuf_add(&buf, "[EXT]\t", 6); +				if (!externs) +					ret2 = strbuf_add(&buf, +						ret ? "[INV]\t" : "[VAL]\t", 6); +				else +					ret2 = strbuf_add(&buf, "[EXT]\t", 6); +				if (ret2) +					goto error;  			}  			ret2 = die_get_varname(die_mem, &buf);  			if (!ret2 && probe_conf.show_location_range &&  				!externs) { -				strbuf_addch(&buf, '\t'); +				if (strbuf_addch(&buf, '\t') < 0) +					goto error;  				ret2 = die_get_var_range(&af->pf.sp_die,  							die_mem, &buf);  			} @@ -1343,6 +1345,10 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)  		return DIE_FIND_CB_CONTINUE;  	else  		return DIE_FIND_CB_SIBLING; +error: +	strbuf_release(&buf); +	pr_debug("Error in strbuf\n"); +	return DIE_FIND_CB_END;  }  /* Add a found vars into available variables list */ diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 8162ba0e2e57..36c6862119e3 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -23,3 +23,4 @@ util/strlist.c  util/trace-event.c  ../lib/rbtree.c  util/string.c +util/symbol_fprintf.c diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c index 01f03242b86a..c6d4ee2de752 100644 --- a/tools/perf/util/quote.c +++ b/tools/perf/util/quote.c @@ -17,38 +17,42 @@ static inline int need_bs_quote(char c)  	return (c == '\'' || c == '!');  } -static void sq_quote_buf(struct strbuf *dst, const char *src) +static int sq_quote_buf(struct strbuf *dst, const char *src)  {  	char *to_free = NULL; +	int ret;  	if (dst->buf == src)  		to_free = strbuf_detach(dst, NULL); -	strbuf_addch(dst, '\''); -	while (*src) { +	ret = strbuf_addch(dst, '\''); +	while (!ret && *src) {  		size_t len = strcspn(src, "'!"); -		strbuf_add(dst, src, len); +		ret = strbuf_add(dst, src, len);  		src += len; -		while (need_bs_quote(*src)) { -			strbuf_addstr(dst, "'\\"); -			strbuf_addch(dst, *src++); -			strbuf_addch(dst, '\''); -		} +		while (!ret && need_bs_quote(*src)) +			ret = strbuf_addf(dst, "'\\%c\'", *src++);  	} -	strbuf_addch(dst, '\''); +	if (!ret) +		ret = strbuf_addch(dst, '\'');  	free(to_free); + +	return ret;  } -void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) +int sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)  { -	int i; +	int i, ret;  	/* Copy into destination buffer. */ -	strbuf_grow(dst, 255); -	for (i = 0; argv[i]; ++i) { -		strbuf_addch(dst, ' '); -		sq_quote_buf(dst, argv[i]); +	ret = strbuf_grow(dst, 255); +	for (i = 0; !ret && argv[i]; ++i) { +		ret = strbuf_addch(dst, ' '); +		if (ret) +			break; +		ret = sq_quote_buf(dst, argv[i]);  		if (maxlen && dst->len > maxlen)  			die("Too many or long arguments");  	} +	return ret;  } diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h index 3340c9c4a6ca..e1ec19146fb0 100644 --- a/tools/perf/util/quote.h +++ b/tools/perf/util/quote.h @@ -24,6 +24,6 @@   * sq_quote() in a real application.   */ -void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen); +int sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);  #endif /* __PERF_QUOTE_H */ diff --git a/tools/perf/util/rb_resort.h b/tools/perf/util/rb_resort.h new file mode 100644 index 000000000000..abc76e3d3098 --- /dev/null +++ b/tools/perf/util/rb_resort.h @@ -0,0 +1,149 @@ +#ifndef _PERF_RESORT_RB_H_ +#define _PERF_RESORT_RB_H_ +/* + * Template for creating a class to resort an existing rb_tree according to + * a new sort criteria, that must be present in the entries of the source + * rb_tree. + * + * (c) 2016 Arnaldo Carvalho de Melo <[email protected]> + * + * Quick example, resorting threads by its shortname: + * + * First define the prefix (threads) to be used for the functions and data + * structures created, and provide an expression for the sorting, then the + * fields to be present in each of the entries in the new, sorted, rb_tree. + * + * The body of the init function should collect the fields, maybe + * pre-calculating them from multiple entries in the original 'entry' from + * the rb_tree used as a source for the entries to be sorted: + +DEFINE_RB_RESORT_RB(threads, strcmp(a->thread->shortname, +				    b->thread->shortname) < 0, +	struct thread *thread; +) +{ +	entry->thread = rb_entry(nd, struct thread, rb_node); +} + + * After this it is just a matter of instantiating it and iterating it, + * for a few data structures with existing rb_trees, such as 'struct machine', + * helpers are available to get the rb_root and the nr_entries: + +	DECLARE_RESORT_RB_MACHINE_THREADS(threads, machine_ptr); + + * This will instantiate the new rb_tree and a cursor for it, that can be used as: + +	struct rb_node *nd; + +	resort_rb__for_each(nd, threads) { +		struct thread *t = threads_entry; +		printf("%s: %d\n", t->shortname, t->tid); +	} + + * Then delete it: + +	resort_rb__delete(threads); + + * The name of the data structures and functions will have a _sorted suffix + * right before the method names, i.e. will look like: + * + * 	struct threads_sorted_entry {} + * 	threads_sorted__insert() + */ + +#define DEFINE_RESORT_RB(__name, __comp, ...)					\ +struct __name##_sorted_entry {							\ +	struct rb_node	rb_node;						\ +	__VA_ARGS__								\ +};										\ +static void __name##_sorted__init_entry(struct rb_node *nd,			\ +					struct __name##_sorted_entry *entry);	\ +										\ +static int __name##_sorted__cmp(struct rb_node *nda, struct rb_node *ndb)	\ +{										\ +	struct __name##_sorted_entry *a, *b;					\ +	a = rb_entry(nda, struct __name##_sorted_entry, rb_node);		\ +	b = rb_entry(ndb, struct __name##_sorted_entry, rb_node);		\ +	return __comp;								\ +}										\ +										\ +struct __name##_sorted {							\ +       struct rb_root		    entries;					\ +       struct __name##_sorted_entry nd[0];					\ +};										\ +										\ +static void __name##_sorted__insert(struct __name##_sorted *sorted,		\ +				      struct rb_node *sorted_nd)		\ +{										\ +	struct rb_node **p = &sorted->entries.rb_node, *parent = NULL;		\ +	while (*p != NULL) {							\ +		parent = *p;							\ +		if (__name##_sorted__cmp(sorted_nd, parent))			\ +			p = &(*p)->rb_left;					\ +		else								\ +			p = &(*p)->rb_right;					\ +	}									\ +	rb_link_node(sorted_nd, parent, p);					\ +	rb_insert_color(sorted_nd, &sorted->entries);				\ +}										\ +										\ +static void __name##_sorted__sort(struct __name##_sorted *sorted,		\ +				    struct rb_root *entries)			\ +{										\ +	struct rb_node *nd;							\ +	unsigned int i = 0;							\ +	for (nd = rb_first(entries); nd; nd = rb_next(nd)) {			\ +		struct __name##_sorted_entry *snd = &sorted->nd[i++];		\ +		__name##_sorted__init_entry(nd, snd);				\ +		__name##_sorted__insert(sorted, &snd->rb_node);			\ +	}									\ +}										\ +										\ +static struct __name##_sorted *__name##_sorted__new(struct rb_root *entries,	\ +						    int nr_entries)		\ +{										\ +	struct __name##_sorted *sorted;						\ +	sorted = malloc(sizeof(*sorted) + sizeof(sorted->nd[0]) * nr_entries);	\ +	if (sorted) {								\ +		sorted->entries = RB_ROOT;					\ +		__name##_sorted__sort(sorted, entries);				\ +	}									\ +	return sorted;								\ +}										\ +										\ +static void __name##_sorted__delete(struct __name##_sorted *sorted)		\ +{										\ +	free(sorted);								\ +}										\ +										\ +static void __name##_sorted__init_entry(struct rb_node *nd,			\ +					struct __name##_sorted_entry *entry) + +#define DECLARE_RESORT_RB(__name)						\ +struct __name##_sorted_entry *__name##_entry;					\ +struct __name##_sorted *__name = __name##_sorted__new + +#define resort_rb__for_each(__nd, __name)					\ +	for (__nd = rb_first(&__name->entries);					\ +	     __name##_entry = rb_entry(__nd, struct __name##_sorted_entry,	\ +				       rb_node), __nd;				\ +	     __nd = rb_next(__nd)) + +#define resort_rb__delete(__name)						\ +	__name##_sorted__delete(__name), __name = NULL + +/* + * Helpers for other classes that contains both an rbtree and the + * number of entries in it: + */ + +/* For 'struct intlist' */ +#define DECLARE_RESORT_RB_INTLIST(__name, __ilist)				\ +	DECLARE_RESORT_RB(__name)(&__ilist->rblist.entries,			\ +				  __ilist->rblist.nr_entries) + +/* For 'struct machine->threads' */ +#define DECLARE_RESORT_RB_MACHINE_THREADS(__name, __machine)			\ +	DECLARE_RESORT_RB(__name)(&__machine->threads, __machine->nr_threads) + +#endif /* _PERF_RESORT_RB_H_ */ diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 0467367dc315..481792c7484b 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -129,7 +129,8 @@ bool perf_can_record_cpu_wide(void)  	return true;  } -void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts, +			 struct callchain_param *callchain)  {  	struct perf_evsel *evsel;  	bool use_sample_identifier = false; @@ -148,7 +149,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts)  	use_comm_exec = perf_can_comm_exec();  	evlist__for_each(evlist, evsel) { -		perf_evsel__config(evsel, opts); +		perf_evsel__config(evsel, opts, callchain);  		if (evsel->tracking && use_comm_exec)  			evsel->attr.comm_exec = 1;  	} diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index b3aabc0d4eb0..62c7f6988e0e 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -31,6 +31,8 @@  #include <perl.h>  #include "../../perf.h" +#include "../callchain.h" +#include "../machine.h"  #include "../thread.h"  #include "../event.h"  #include "../trace-event.h" @@ -248,10 +250,90 @@ static void define_event_symbols(struct event_format *event,  		define_event_symbols(event, ev_name, args->next);  } +static SV *perl_process_callchain(struct perf_sample *sample, +				  struct perf_evsel *evsel, +				  struct addr_location *al) +{ +	AV *list; + +	list = newAV(); +	if (!list) +		goto exit; + +	if (!symbol_conf.use_callchain || !sample->callchain) +		goto exit; + +	if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel, +				      sample, NULL, NULL, +				      sysctl_perf_event_max_stack) != 0) { +		pr_err("Failed to resolve callchain. Skipping\n"); +		goto exit; +	} +	callchain_cursor_commit(&callchain_cursor); + + +	while (1) { +		HV *elem; +		struct callchain_cursor_node *node; +		node = callchain_cursor_current(&callchain_cursor); +		if (!node) +			break; + +		elem = newHV(); +		if (!elem) +			goto exit; + +		if (!hv_stores(elem, "ip", newSVuv(node->ip))) { +			hv_undef(elem); +			goto exit; +		} + +		if (node->sym) { +			HV *sym = newHV(); +			if (!sym) { +				hv_undef(elem); +				goto exit; +			} +			if (!hv_stores(sym, "start",   newSVuv(node->sym->start)) || +			    !hv_stores(sym, "end",     newSVuv(node->sym->end)) || +			    !hv_stores(sym, "binding", newSVuv(node->sym->binding)) || +			    !hv_stores(sym, "name",    newSVpvn(node->sym->name, +								node->sym->namelen)) || +			    !hv_stores(elem, "sym",    newRV_noinc((SV*)sym))) { +				hv_undef(sym); +				hv_undef(elem); +				goto exit; +			} +		} + +		if (node->map) { +			struct map *map = node->map; +			const char *dsoname = "[unknown]"; +			if (map && map->dso && (map->dso->name || map->dso->long_name)) { +				if (symbol_conf.show_kernel_path && map->dso->long_name) +					dsoname = map->dso->long_name; +				else if (map->dso->name) +					dsoname = map->dso->name; +			} +			if (!hv_stores(elem, "dso", newSVpv(dsoname,0))) { +				hv_undef(elem); +				goto exit; +			} +		} + +		callchain_cursor_advance(&callchain_cursor); +		av_push(list, newRV_noinc((SV*)elem)); +	} + +exit: +	return newRV_noinc((SV*)list); +} +  static void perl_process_tracepoint(struct perf_sample *sample,  				    struct perf_evsel *evsel, -				    struct thread *thread) +				    struct addr_location *al)  { +	struct thread *thread = al->thread;  	struct event_format *event = evsel->tp_format;  	struct format_field *field;  	static char handler[256]; @@ -295,6 +377,7 @@ static void perl_process_tracepoint(struct perf_sample *sample,  	XPUSHs(sv_2mortal(newSVuv(ns)));  	XPUSHs(sv_2mortal(newSViv(pid)));  	XPUSHs(sv_2mortal(newSVpv(comm, 0))); +	XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al)));  	/* common fields other than pid can be accessed via xsub fns */ @@ -329,6 +412,7 @@ static void perl_process_tracepoint(struct perf_sample *sample,  		XPUSHs(sv_2mortal(newSVuv(nsecs)));  		XPUSHs(sv_2mortal(newSViv(pid)));  		XPUSHs(sv_2mortal(newSVpv(comm, 0))); +		XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al)));  		call_pv("main::trace_unhandled", G_SCALAR);  	}  	SPAGAIN; @@ -366,7 +450,7 @@ static void perl_process_event(union perf_event *event,  			       struct perf_evsel *evsel,  			       struct addr_location *al)  { -	perl_process_tracepoint(sample, evsel, al->thread); +	perl_process_tracepoint(sample, evsel, al);  	perl_process_event_generic(event, sample, evsel);  } @@ -490,7 +574,27 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)  	fprintf(ofp, "use Perf::Trace::Util;\n\n");  	fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n"); -	fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n"); +	fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n"); + + +	fprintf(ofp, "\n\ +sub print_backtrace\n\ +{\n\ +	my $callchain = shift;\n\ +	for my $node (@$callchain)\n\ +	{\n\ +		if(exists $node->{sym})\n\ +		{\n\ +			printf( \"\\t[\\%%x] \\%%s\\n\", $node->{ip}, $node->{sym}{name});\n\ +		}\n\ +		else\n\ +		{\n\ +			printf( \"\\t[\\%%x]\\n\", $node{ip});\n\ +		}\n\ +	}\n\ +}\n\n\ +"); +  	while ((event = trace_find_next_event(pevent, event))) {  		fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); @@ -502,7 +606,8 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)  		fprintf(ofp, "$common_secs, ");  		fprintf(ofp, "$common_nsecs,\n");  		fprintf(ofp, "\t    $common_pid, "); -		fprintf(ofp, "$common_comm,\n\t    "); +		fprintf(ofp, "$common_comm, "); +		fprintf(ofp, "$common_callchain,\n\t    ");  		not_first = 0;  		count = 0; @@ -519,7 +624,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)  		fprintf(ofp, "\tprint_header($event_name, $common_cpu, "  			"$common_secs, $common_nsecs,\n\t             " -			"$common_pid, $common_comm);\n\n"); +			"$common_pid, $common_comm, $common_callchain);\n\n");  		fprintf(ofp, "\tprintf(\""); @@ -581,17 +686,22 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)  				fprintf(ofp, "$%s", f->name);  		} -		fprintf(ofp, ");\n"); +		fprintf(ofp, ");\n\n"); + +		fprintf(ofp, "\tprint_backtrace($common_callchain);\n"); +  		fprintf(ofp, "}\n\n");  	}  	fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, "  		"$common_cpu, $common_secs, $common_nsecs,\n\t    " -		"$common_pid, $common_comm) = @_;\n\n"); +		"$common_pid, $common_comm, $common_callchain) = @_;\n\n");  	fprintf(ofp, "\tprint_header($event_name, $common_cpu, "  		"$common_secs, $common_nsecs,\n\t             $common_pid, " -		"$common_comm);\n}\n\n"); +		"$common_comm, $common_callchain);\n"); +	fprintf(ofp, "\tprint_backtrace($common_callchain);\n"); +	fprintf(ofp, "}\n\n");  	fprintf(ofp, "sub print_header\n{\n"  		"\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n" diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index fbd05242b4e5..ff134700bf30 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -41,6 +41,7 @@  #include "../thread-stack.h"  #include "../trace-event.h"  #include "../machine.h" +#include "../call-path.h"  #include "thread_map.h"  #include "cpumap.h"  #include "stat.h" @@ -323,7 +324,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample,  	if (!symbol_conf.use_callchain || !sample->callchain)  		goto exit; -	if (thread__resolve_callchain(al->thread, evsel, +	if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel,  				      sample, NULL, NULL,  				      scripting_max_stack) != 0) {  		pr_err("Failed to resolve callchain. Skipping\n"); @@ -407,8 +408,11 @@ static void python_process_tracepoint(struct perf_sample *sample,  	if (!t)  		Py_FatalError("couldn't create Python tuple"); -	if (!event) -		die("ug! no event found for type %d", (int)evsel->attr.config); +	if (!event) { +		snprintf(handler_name, sizeof(handler_name), +			 "ug! no event found for type %" PRIu64, (u64)evsel->attr.config); +		Py_FatalError(handler_name); +	}  	pid = raw_field_value(event, "common_pid", data); @@ -614,7 +618,7 @@ static int python_export_dso(struct db_export *dbe, struct dso *dso,  			     struct machine *machine)  {  	struct tables *tables = container_of(dbe, struct tables, dbe); -	char sbuild_id[BUILD_ID_SIZE * 2 + 1]; +	char sbuild_id[SBUILD_ID_SIZE];  	PyObject *t;  	build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); @@ -681,7 +685,7 @@ static int python_export_sample(struct db_export *dbe,  	struct tables *tables = container_of(dbe, struct tables, dbe);  	PyObject *t; -	t = tuple_new(21); +	t = tuple_new(22);  	tuple_set_u64(t, 0, es->db_id);  	tuple_set_u64(t, 1, es->evsel->db_id); @@ -704,6 +708,7 @@ static int python_export_sample(struct db_export *dbe,  	tuple_set_u64(t, 18, es->sample->data_src);  	tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK);  	tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX)); +	tuple_set_u64(t, 21, es->call_path_id);  	call_object(tables->sample_handler, t, "sample_table"); @@ -998,8 +1003,10 @@ static void set_table_handlers(struct tables *tables)  {  	const char *perf_db_export_mode = "perf_db_export_mode";  	const char *perf_db_export_calls = "perf_db_export_calls"; -	PyObject *db_export_mode, *db_export_calls; +	const char *perf_db_export_callchains = "perf_db_export_callchains"; +	PyObject *db_export_mode, *db_export_calls, *db_export_callchains;  	bool export_calls = false; +	bool export_callchains = false;  	int ret;  	memset(tables, 0, sizeof(struct tables)); @@ -1016,6 +1023,7 @@ static void set_table_handlers(struct tables *tables)  	if (!ret)  		return; +	/* handle export calls */  	tables->dbe.crp = NULL;  	db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls);  	if (db_export_calls) { @@ -1033,6 +1041,33 @@ static void set_table_handlers(struct tables *tables)  			Py_FatalError("failed to create calls processor");  	} +	/* handle export callchains */ +	tables->dbe.cpr = NULL; +	db_export_callchains = PyDict_GetItemString(main_dict, +						    perf_db_export_callchains); +	if (db_export_callchains) { +		ret = PyObject_IsTrue(db_export_callchains); +		if (ret == -1) +			handler_call_die(perf_db_export_callchains); +		export_callchains = !!ret; +	} + +	if (export_callchains) { +		/* +		 * Attempt to use the call path root from the call return +		 * processor, if the call return processor is in use. Otherwise, +		 * we allocate a new call path root. This prevents exporting +		 * duplicate call path ids when both are in use simultaniously. +		 */ +		if (tables->dbe.crp) +			tables->dbe.cpr = tables->dbe.crp->cpr; +		else +			tables->dbe.cpr = call_path_root__new(); + +		if (!tables->dbe.cpr) +			Py_FatalError("failed to create call path root"); +	} +  	tables->db_export_mode = true;  	/*  	 * Reserve per symbol space for symbol->db_id via symbol__priv() diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4abd85c6346d..5214974e841a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -409,6 +409,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)  		tool->stat = process_stat_stub;  	if (tool->stat_round == NULL)  		tool->stat_round = process_stat_round_stub; +	if (tool->time_conv == NULL) +		tool->time_conv = process_event_op2_stub;  }  static void swap_sample_id_all(union perf_event *event, void *data) @@ -555,7 +557,7 @@ static u8 revbyte(u8 b)  /*   * XXX this is hack in attempt to carry flags bitfield - * throught endian village. ABI says: + * through endian village. ABI says:   *   * Bit-fields are allocated from right to left (least to most significant)   * on little-endian implementations and from left to right (most to least @@ -794,6 +796,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {  	[PERF_RECORD_STAT]		  = perf_event__stat_swap,  	[PERF_RECORD_STAT_ROUND]	  = perf_event__stat_round_swap,  	[PERF_RECORD_EVENT_UPDATE]	  = perf_event__event_update_swap, +	[PERF_RECORD_TIME_CONV]		  = perf_event__all64_swap,  	[PERF_RECORD_HEADER_MAX]	  = NULL,  }; @@ -904,7 +907,7 @@ static void callchain__printf(struct perf_evsel *evsel,  	unsigned int i;  	struct ip_callchain *callchain = sample->callchain; -	if (has_branch_callstack(evsel)) +	if (perf_evsel__has_branch_callstack(evsel))  		callchain__lbr_callstack_printf(sample);  	printf("... FP chain: nr:%" PRIu64 "\n", callchain->nr); @@ -1078,7 +1081,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,  	if (sample_type & PERF_SAMPLE_CALLCHAIN)  		callchain__printf(evsel, sample); -	if ((sample_type & PERF_SAMPLE_BRANCH_STACK) && !has_branch_callstack(evsel)) +	if ((sample_type & PERF_SAMPLE_BRANCH_STACK) && !perf_evsel__has_branch_callstack(evsel))  		branch_stack__printf(sample);  	if (sample_type & PERF_SAMPLE_REGS_USER) @@ -1341,6 +1344,9 @@ static s64 perf_session__process_user_event(struct perf_session *session,  		return tool->stat(tool, event, session);  	case PERF_RECORD_STAT_ROUND:  		return tool->stat_round(tool, event, session); +	case PERF_RECORD_TIME_CONV: +		session->time_conv = event->time_conv; +		return tool->time_conv(tool, event, session);  	default:  		return -EINVAL;  	} @@ -1830,7 +1836,11 @@ out:  out_err:  	ui_progress__finish();  	perf_session__warn_about_errors(session); -	ordered_events__free(&session->ordered_events); +	/* +	 * We may switching perf.data output, make ordered_events +	 * reusable. +	 */ +	ordered_events__reinit(&session->ordered_events);  	auxtrace__free_events(session);  	session->one_mmap = false;  	return err; @@ -1947,105 +1957,6 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,  	return NULL;  } -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 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 (symbol_conf.use_callchain && sample->callchain) { -		struct addr_location node_al; - -		if (thread__resolve_callchain(al->thread, evsel, -					      sample, NULL, NULL, -					      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) { -					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); -			} - -			if (print_dso) { -				printf(" ("); -				map__fprintf_dsoname(node->map, stdout); -				printf(")"); -			} - -			if (print_srcline) -				map__fprintf_srcline(node->map, addr, "\n  ", -						     stdout); - -			if (!print_oneline) -				printf("\n"); - -			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, -							     stdout); -			else -				symbol__fprintf_symname(al->sym, stdout); -		} - -		if (print_dso) { -			printf(" ("); -			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)  { diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 5f792e35d4c1..4bd758553450 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -26,6 +26,7 @@ struct perf_session {  	struct itrace_synth_opts *itrace_synth_opts;  	struct list_head	auxtrace_index;  	struct trace_event	tevent; +	struct time_conv_event	time_conv;  	bool			repipe;  	bool			one_mmap;  	void			*one_mmap_addr; @@ -35,13 +36,6 @@ struct perf_session {  	struct perf_tool	*tool;  }; -#define PRINT_IP_OPT_IP		(1<<0) -#define PRINT_IP_OPT_SYM		(1<<1) -#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(struct perf_data_file *file, @@ -103,10 +97,6 @@ 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, 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,  			     const char *cpu_list, unsigned long *cpu_bitmap); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 47966a1618c7..20e69edd5006 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -21,13 +21,6 @@ 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; -int		sort__has_socket = 0; -int		sort__has_thread = 0; -int		sort__has_comm = 0;  enum sort_mode	sort__mode = SORT_MODE__NORMAL;  /* @@ -244,7 +237,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)  	 * comparing symbol address alone is not enough since it's a  	 * relative address within a dso.  	 */ -	if (!sort__has_dso) { +	if (!hists__has(left->hists, dso) || hists__has(right->hists, dso)) {  		ret = sort__dso_cmp(left, right);  		if (ret != 0)  			return ret; @@ -2163,7 +2156,7 @@ static int __sort_dimension__add(struct sort_dimension *sd,  		return -1;  	if (sd->entry->se_collapse) -		sort__need_collapse = 1; +		list->need_collapse = 1;  	sd->taken = 1; @@ -2245,9 +2238,9 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,  				pr_err("Invalid regex: %s\n%s", parent_pattern, err);  				return -EINVAL;  			} -			sort__has_parent = 1; +			list->parent = 1;  		} else if (sd->entry == &sort_sym) { -			sort__has_sym = 1; +			list->sym = 1;  			/*  			 * perf diff displays the performance difference amongst  			 * two or more perf.data files. Those files could come @@ -2258,13 +2251,13 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,  				sd->entry->se_collapse = sort__sym_sort;  		} else if (sd->entry == &sort_dso) { -			sort__has_dso = 1; +			list->dso = 1;  		} else if (sd->entry == &sort_socket) { -			sort__has_socket = 1; +			list->socket = 1;  		} else if (sd->entry == &sort_thread) { -			sort__has_thread = 1; +			list->thread = 1;  		} else if (sd->entry == &sort_comm) { -			sort__has_comm = 1; +			list->comm = 1;  		}  		return __sort_dimension__add(sd, list, level); @@ -2289,7 +2282,7 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,  			return -EINVAL;  		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) -			sort__has_sym = 1; +			list->sym = 1;  		__sort_dimension__add(sd, list, level);  		return 0; @@ -2305,7 +2298,7 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,  			return -EINVAL;  		if (sd->entry == &sort_mem_daddr_sym) -			sort__has_sym = 1; +			list->sym = 1;  		__sort_dimension__add(sd, list, level);  		return 0; @@ -2445,6 +2438,9 @@ static char *prefix_if_not_in(const char *pre, char *str)  static char *setup_overhead(char *keys)  { +	if (sort__mode == SORT_MODE__DIFF) +		return keys; +  	keys = prefix_if_not_in("overhead", keys);  	if (symbol_conf.cumulate_callchain) @@ -2746,10 +2742,10 @@ int setup_sorting(struct perf_evlist *evlist)  void reset_output_field(void)  { -	sort__need_collapse = 0; -	sort__has_parent = 0; -	sort__has_sym = 0; -	sort__has_dso = 0; +	perf_hpp_list.need_collapse = 0; +	perf_hpp_list.parent = 0; +	perf_hpp_list.sym = 0; +	perf_hpp_list.dso = 0;  	field_order = NULL;  	sort_order = NULL; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 3f4e35998119..42927f448bcb 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -31,13 +31,6 @@ extern const char *parent_pattern;  extern const char default_sort_order[];  extern regex_t ignore_callees_regex;  extern int have_ignore_callees; -extern int sort__need_collapse; -extern int sort__has_dso; -extern int sort__has_parent; -extern int sort__has_sym; -extern int sort__has_socket; -extern int sort__has_thread; -extern int sort__has_comm;  extern enum sort_mode sort__mode;  extern struct sort_entry sort_comm;  extern struct sort_entry sort_dso; diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 4d9b481cf3b6..ffa1d0653861 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -307,6 +307,7 @@ int perf_stat_process_counter(struct perf_stat_config *config,  	struct perf_counts_values *aggr = &counter->counts->aggr;  	struct perf_stat_evsel *ps = counter->priv;  	u64 *count = counter->counts->aggr.values; +	u64 val;  	int i, ret;  	aggr->val = aggr->ena = aggr->run = 0; @@ -346,7 +347,8 @@ int perf_stat_process_counter(struct perf_stat_config *config,  	/*  	 * Save the full runtime - to allow normalization during printout:  	 */ -	perf_stat__update_shadow_stats(counter, count, 0); +	val = counter->scale * *count; +	perf_stat__update_shadow_stats(counter, &val, 0);  	return 0;  } diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index 8fb73295ec34..f95f682aa2b2 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c @@ -1,3 +1,4 @@ +#include "debug.h"  #include "cache.h"  #include <linux/kernel.h> @@ -17,12 +18,13 @@ int prefixcmp(const char *str, const char *prefix)   */  char strbuf_slopbuf[1]; -void strbuf_init(struct strbuf *sb, ssize_t hint) +int strbuf_init(struct strbuf *sb, ssize_t hint)  {  	sb->alloc = sb->len = 0;  	sb->buf = strbuf_slopbuf;  	if (hint) -		strbuf_grow(sb, hint); +		return strbuf_grow(sb, hint); +	return 0;  }  void strbuf_release(struct strbuf *sb) @@ -42,67 +44,104 @@ char *strbuf_detach(struct strbuf *sb, size_t *sz)  	return res;  } -void strbuf_grow(struct strbuf *sb, size_t extra) +int strbuf_grow(struct strbuf *sb, size_t extra)  { -	if (sb->len + extra + 1 <= sb->len) -		die("you want to use way too much memory"); -	if (!sb->alloc) -		sb->buf = NULL; -	ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); +	char *buf; +	size_t nr = sb->len + extra + 1; + +	if (nr < sb->alloc) +		return 0; + +	if (nr <= sb->len) +		return -E2BIG; + +	if (alloc_nr(sb->alloc) > nr) +		nr = alloc_nr(sb->alloc); + +	/* +	 * Note that sb->buf == strbuf_slopbuf if sb->alloc == 0, and it is +	 * a static variable. Thus we have to avoid passing it to realloc. +	 */ +	buf = realloc(sb->alloc ? sb->buf : NULL, nr * sizeof(*buf)); +	if (!buf) +		return -ENOMEM; + +	sb->buf = buf; +	sb->alloc = nr; +	return 0;  } -void strbuf_addch(struct strbuf *sb, int c) +int strbuf_addch(struct strbuf *sb, int c)  { -	strbuf_grow(sb, 1); +	int ret = strbuf_grow(sb, 1); +	if (ret) +		return ret; +  	sb->buf[sb->len++] = c;  	sb->buf[sb->len] = '\0'; +	return 0;  } -void strbuf_add(struct strbuf *sb, const void *data, size_t len) +int strbuf_add(struct strbuf *sb, const void *data, size_t len)  { -	strbuf_grow(sb, len); +	int ret = strbuf_grow(sb, len); +	if (ret) +		return ret; +  	memcpy(sb->buf + sb->len, data, len); -	strbuf_setlen(sb, sb->len + len); +	return strbuf_setlen(sb, sb->len + len);  } -static void strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap) +static int strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap)  { -	int len; +	int len, ret;  	va_list ap_saved; -	if (!strbuf_avail(sb)) -		strbuf_grow(sb, 64); +	if (!strbuf_avail(sb)) { +		ret = strbuf_grow(sb, 64); +		if (ret) +			return ret; +	}  	va_copy(ap_saved, ap);  	len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);  	if (len < 0) -		die("your vsnprintf is broken"); +		return len;  	if (len > strbuf_avail(sb)) { -		strbuf_grow(sb, len); +		ret = strbuf_grow(sb, len); +		if (ret) +			return ret;  		len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved);  		va_end(ap_saved);  		if (len > strbuf_avail(sb)) { -			die("this should not happen, your vsnprintf is broken"); +			pr_debug("this should not happen, your vsnprintf is broken"); +			return -EINVAL;  		}  	} -	strbuf_setlen(sb, sb->len + len); +	return strbuf_setlen(sb, sb->len + len);  } -void strbuf_addf(struct strbuf *sb, const char *fmt, ...) +int strbuf_addf(struct strbuf *sb, const char *fmt, ...)  {  	va_list ap; +	int ret;  	va_start(ap, fmt); -	strbuf_addv(sb, fmt, ap); +	ret = strbuf_addv(sb, fmt, ap);  	va_end(ap); +	return ret;  }  ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)  {  	size_t oldlen = sb->len;  	size_t oldalloc = sb->alloc; +	int ret; + +	ret = strbuf_grow(sb, hint ? hint : 8192); +	if (ret) +		return ret; -	strbuf_grow(sb, hint ? hint : 8192);  	for (;;) {  		ssize_t cnt; @@ -112,12 +151,14 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)  				strbuf_release(sb);  			else  				strbuf_setlen(sb, oldlen); -			return -1; +			return cnt;  		}  		if (!cnt)  			break;  		sb->len += cnt; -		strbuf_grow(sb, 8192); +		ret = strbuf_grow(sb, 8192); +		if (ret) +			return ret;  	}  	sb->buf[sb->len] = '\0'; diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h index ab9be0fbbd40..54b409297d4a 100644 --- a/tools/perf/util/strbuf.h +++ b/tools/perf/util/strbuf.h @@ -51,7 +51,7 @@ struct strbuf {  #define STRBUF_INIT  { 0, 0, strbuf_slopbuf }  /*----- strbuf life cycle -----*/ -void strbuf_init(struct strbuf *buf, ssize_t hint); +int strbuf_init(struct strbuf *buf, ssize_t hint);  void strbuf_release(struct strbuf *buf);  char *strbuf_detach(struct strbuf *buf, size_t *); @@ -60,26 +60,31 @@ static inline ssize_t strbuf_avail(const struct strbuf *sb) {  	return sb->alloc ? sb->alloc - sb->len - 1 : 0;  } -void strbuf_grow(struct strbuf *buf, size_t); +int strbuf_grow(struct strbuf *buf, size_t); -static inline void strbuf_setlen(struct strbuf *sb, size_t len) { -	if (!sb->alloc) -		strbuf_grow(sb, 0); +static inline int strbuf_setlen(struct strbuf *sb, size_t len) { +	int ret; +	if (!sb->alloc) { +		ret = strbuf_grow(sb, 0); +		if (ret) +			return ret; +	}  	assert(len < sb->alloc);  	sb->len = len;  	sb->buf[len] = '\0'; +	return 0;  }  /*----- add data in your buffer -----*/ -void strbuf_addch(struct strbuf *sb, int c); +int strbuf_addch(struct strbuf *sb, int c); -void strbuf_add(struct strbuf *buf, const void *, size_t); -static inline void strbuf_addstr(struct strbuf *sb, const char *s) { -	strbuf_add(sb, s, strlen(s)); +int strbuf_add(struct strbuf *buf, const void *, size_t); +static inline int strbuf_addstr(struct strbuf *sb, const char *s) { +	return strbuf_add(sb, s, strlen(s));  }  __attribute__((format(printf,2,3))) -void strbuf_addf(struct strbuf *sb, const char *fmt, ...); +int strbuf_addf(struct strbuf *sb, const char *fmt, ...);  /* XXX: if read fails, any partial read is undone */  ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint); diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index bc229a74c6a9..87a297dd8901 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -709,17 +709,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,  	if (ss->opdshdr.sh_type != SHT_PROGBITS)  		ss->opdsec = NULL; -	if (dso->kernel == DSO_TYPE_USER) { -		GElf_Shdr shdr; -		ss->adjust_symbols = (ehdr.e_type == ET_EXEC || -				ehdr.e_type == ET_REL || -				dso__is_vdso(dso) || -				elf_section_by_name(elf, &ehdr, &shdr, -						     ".gnu.prelink_undo", -						     NULL) != NULL); -	} else { +	if (dso->kernel == DSO_TYPE_USER) +		ss->adjust_symbols = true; +	else  		ss->adjust_symbols = elf__needs_adjust_symbols(ehdr); -	}  	ss->name   = strdup(name);  	if (!ss->name) { @@ -777,7 +770,8 @@ static bool want_demangle(bool is_kernel_sym)  	return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle;  } -void __weak arch__elf_sym_adjust(GElf_Sym *sym __maybe_unused) { } +void __weak arch__sym_update(struct symbol *s __maybe_unused, +		GElf_Sym *sym __maybe_unused) { }  int dso__load_sym(struct dso *dso, struct map *map,  		  struct symsrc *syms_ss, struct symsrc *runtime_ss, @@ -954,8 +948,6 @@ int dso__load_sym(struct dso *dso, struct map *map,  		    (sym.st_value & 1))  			--sym.st_value; -		arch__elf_sym_adjust(&sym); -  		if (dso->kernel || kmodule) {  			char dso_name[PATH_MAX]; @@ -1089,6 +1081,8 @@ new_symbol:  		if (!f)  			goto out_elf_end; +		arch__sym_update(f, &sym); +  		if (filter && filter(curr_map, f))  			symbol__delete(f);  		else { diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e7588dc91518..7fb33304fb4e 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -255,40 +255,6 @@ void symbol__delete(struct symbol *sym)  	free(((void *)sym) - symbol_conf.priv_size);  } -size_t symbol__fprintf(struct symbol *sym, FILE *fp) -{ -	return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", -		       sym->start, sym->end, -		       sym->binding == STB_GLOBAL ? 'g' : -		       sym->binding == STB_LOCAL  ? 'l' : 'w', -		       sym->name); -} - -size_t symbol__fprintf_symname_offs(const struct symbol *sym, -				    const struct addr_location *al, FILE *fp) -{ -	unsigned long offset; -	size_t length; - -	if (sym && sym->name) { -		length = fprintf(fp, "%s", sym->name); -		if (al) { -			if (al->addr < sym->end) -				offset = al->addr - sym->start; -			else -				offset = al->addr - al->map->start - sym->start; -			length += fprintf(fp, "+0x%lx", offset); -		} -		return length; -	} else -		return fprintf(fp, "[unknown]"); -} - -size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) -{ -	return symbol__fprintf_symname_offs(sym, NULL, fp); -} -  void symbols__delete(struct rb_root *symbols)  {  	struct symbol *pos; @@ -335,7 +301,7 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip)  		if (ip < s->start)  			n = n->rb_left; -		else if (ip >= s->end) +		else if (ip > s->end || (ip == s->end && ip != s->start))  			n = n->rb_right;  		else  			return s; @@ -364,11 +330,6 @@ static struct symbol *symbols__next(struct symbol *sym)  	return NULL;  } -struct symbol_name_rb_node { -	struct rb_node	rb_node; -	struct symbol	sym; -}; -  static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym)  {  	struct rb_node **p = &symbols->rb_node; @@ -452,6 +413,18 @@ void dso__reset_find_symbol_cache(struct dso *dso)  	}  } +void dso__insert_symbol(struct dso *dso, enum map_type type, struct symbol *sym) +{ +	symbols__insert(&dso->symbols[type], sym); + +	/* update the symbol cache if necessary */ +	if (dso->last_find_result[type].addr >= sym->start && +	    (dso->last_find_result[type].addr < sym->end || +	    sym->start == sym->end)) { +		dso->last_find_result[type].symbol = sym; +	} +} +  struct symbol *dso__find_symbol(struct dso *dso,  				enum map_type type, u64 addr)  { @@ -497,21 +470,6 @@ void dso__sort_by_name(struct dso *dso, enum map_type type)  				     &dso->symbols[type]);  } -size_t dso__fprintf_symbols_by_name(struct dso *dso, -				    enum map_type type, FILE *fp) -{ -	size_t ret = 0; -	struct rb_node *nd; -	struct symbol_name_rb_node *pos; - -	for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { -		pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); -		fprintf(fp, "%s\n", pos->sym.name); -	} - -	return ret; -} -  int modules__parse(const char *filename, void *arg,  		   int (*process_module)(void *arg, const char *name,  					 u64 start)) @@ -1262,8 +1220,8 @@ static int kallsyms__delta(struct map *map, const char *filename, u64 *delta)  	return 0;  } -int dso__load_kallsyms(struct dso *dso, const char *filename, -		       struct map *map, symbol_filter_t filter) +int __dso__load_kallsyms(struct dso *dso, const char *filename, +			 struct map *map, bool no_kcore, symbol_filter_t filter)  {  	u64 delta = 0; @@ -1284,12 +1242,18 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,  	else  		dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; -	if (!dso__load_kcore(dso, map, filename)) +	if (!no_kcore && !dso__load_kcore(dso, map, filename))  		return dso__split_kallsyms_for_kcore(dso, map, filter);  	else  		return dso__split_kallsyms(dso, map, delta, filter);  } +int dso__load_kallsyms(struct dso *dso, const char *filename, +		       struct map *map, symbol_filter_t filter) +{ +	return __dso__load_kallsyms(dso, filename, map, false, filter); +} +  static int dso__load_perf_map(struct dso *dso, struct map *map,  			      symbol_filter_t filter)  { @@ -1644,25 +1608,27 @@ out:  	return err;  } +static bool visible_dir_filter(const char *name, struct dirent *d) +{ +	if (d->d_type != DT_DIR) +		return false; +	return lsdir_no_dot_filter(name, d); +} +  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; +	struct strlist *dirs; +	struct str_node *nd; -	d = opendir(dir); -	if (!d) +	dirs = lsdir(dir, visible_dir_filter); +	if (!dirs)  		return -1; -	while (1) { -		dent = readdir(d); -		if (!dent) -			break; -		if (dent->d_type != DT_DIR) -			continue; +	strlist__for_each(nd, dirs) {  		scnprintf(kallsyms_filename, sizeof(kallsyms_filename), -			  "%s/%s/kallsyms", dir, dent->d_name); +			  "%s/%s/kallsyms", dir, nd->s);  		if (!validate_kcore_addresses(kallsyms_filename, map)) {  			strlcpy(dir, kallsyms_filename, dir_sz);  			ret = 0; @@ -1670,7 +1636,7 @@ static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz)  		}  	} -	closedir(d); +	strlist__delete(dirs);  	return ret;  } @@ -1678,7 +1644,7 @@ static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz)  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]; +	char sbuild_id[SBUILD_ID_SIZE];  	bool is_host = false;  	char path[PATH_MAX]; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index c8b7544d9267..2b5e4ed76fcb 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -55,6 +55,7 @@ struct symbol {  	u16		namelen;  	u8		binding;  	bool		ignore; +	u8		arch_sym;  	char		name[0];  }; @@ -140,6 +141,11 @@ struct symbol_conf {  extern struct symbol_conf symbol_conf; +struct symbol_name_rb_node { +	struct rb_node	rb_node; +	struct symbol	sym; +}; +  static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)  {  	return path__join(bf, size, symbol_conf.symfs, path); @@ -235,9 +241,14 @@ int dso__load_vmlinux(struct dso *dso, struct map *map,  		      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, +			 bool no_kcore, symbol_filter_t filter);  int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map,  		       symbol_filter_t filter); +void dso__insert_symbol(struct dso *dso, enum map_type type, +			struct symbol *sym); +  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, @@ -262,8 +273,14 @@ int symbol__init(struct perf_env *env);  void symbol__exit(void);  void symbol__elf_init(void);  struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); +size_t __symbol__fprintf_symname_offs(const struct symbol *sym, +				      const struct addr_location *al, +				      bool unknown_as_addr, FILE *fp);  size_t symbol__fprintf_symname_offs(const struct symbol *sym,  				    const struct addr_location *al, FILE *fp); +size_t __symbol__fprintf_symname(const struct symbol *sym, +				 const struct addr_location *al, +				 bool unknown_as_addr, FILE *fp);  size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);  size_t symbol__fprintf(struct symbol *sym, FILE *fp);  bool symbol_type__is_a(char symbol_type, enum map_type map_type); @@ -310,7 +327,7 @@ int setup_intlist(struct intlist **list, const char *list_str,  #ifdef HAVE_LIBELF_SUPPORT  bool elf__needs_adjust_symbols(GElf_Ehdr ehdr); -void arch__elf_sym_adjust(GElf_Sym *sym); +void arch__sym_update(struct symbol *s, GElf_Sym *sym);  #endif  #define SYMBOL_A 0 diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c new file mode 100644 index 000000000000..a680bdaa65dc --- /dev/null +++ b/tools/perf/util/symbol_fprintf.c @@ -0,0 +1,71 @@ +#include <elf.h> +#include <inttypes.h> +#include <stdio.h> + +#include "symbol.h" + +size_t symbol__fprintf(struct symbol *sym, FILE *fp) +{ +	return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", +		       sym->start, sym->end, +		       sym->binding == STB_GLOBAL ? 'g' : +		       sym->binding == STB_LOCAL  ? 'l' : 'w', +		       sym->name); +} + +size_t __symbol__fprintf_symname_offs(const struct symbol *sym, +				      const struct addr_location *al, +				      bool unknown_as_addr, FILE *fp) +{ +	unsigned long offset; +	size_t length; + +	if (sym && sym->name) { +		length = fprintf(fp, "%s", sym->name); +		if (al) { +			if (al->addr < sym->end) +				offset = al->addr - sym->start; +			else +				offset = al->addr - al->map->start - sym->start; +			length += fprintf(fp, "+0x%lx", offset); +		} +		return length; +	} else if (al && unknown_as_addr) +		return fprintf(fp, "[%#" PRIx64 "]", al->addr); +	else +		return fprintf(fp, "[unknown]"); +} + +size_t symbol__fprintf_symname_offs(const struct symbol *sym, +				    const struct addr_location *al, +				    FILE *fp) +{ +	return __symbol__fprintf_symname_offs(sym, al, false, fp); +} + +size_t __symbol__fprintf_symname(const struct symbol *sym, +				 const struct addr_location *al, +				 bool unknown_as_addr, FILE *fp) +{ +	return __symbol__fprintf_symname_offs(sym, al, unknown_as_addr, fp); +} + +size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) +{ +	return __symbol__fprintf_symname_offs(sym, NULL, false, fp); +} + +size_t dso__fprintf_symbols_by_name(struct dso *dso, +				    enum map_type type, FILE *fp) +{ +	size_t ret = 0; +	struct rb_node *nd; +	struct symbol_name_rb_node *pos; + +	for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { +		pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); +		fprintf(fp, "%s\n", pos->sym.name); +	} + +	return ret; +} diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/util/syscalltbl.c new file mode 100644 index 000000000000..bbb4c1957578 --- /dev/null +++ b/tools/perf/util/syscalltbl.c @@ -0,0 +1,134 @@ +/* + * System call table mapper + * + * (C) 2016 Arnaldo Carvalho de Melo <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include "syscalltbl.h" +#include <stdlib.h> + +#ifdef HAVE_SYSCALL_TABLE +#include <linux/compiler.h> +#include <string.h> +#include "util.h" + +#if defined(__x86_64__) +#include <asm/syscalls_64.c> +const int syscalltbl_native_max_id = SYSCALLTBL_x86_64_MAX_ID; +static const char **syscalltbl_native = syscalltbl_x86_64; +#endif + +struct syscall { +	int id; +	const char *name; +}; + +static int syscallcmpname(const void *vkey, const void *ventry) +{ +	const char *key = vkey; +	const struct syscall *entry = ventry; + +	return strcmp(key, entry->name); +} + +static int syscallcmp(const void *va, const void *vb) +{ +	const struct syscall *a = va, *b = vb; + +	return strcmp(a->name, b->name); +} + +static int syscalltbl__init_native(struct syscalltbl *tbl) +{ +	int nr_entries = 0, i, j; +	struct syscall *entries; + +	for (i = 0; i <= syscalltbl_native_max_id; ++i) +		if (syscalltbl_native[i]) +			++nr_entries; + +	entries = tbl->syscalls.entries = malloc(sizeof(struct syscall) * nr_entries); +	if (tbl->syscalls.entries == NULL) +		return -1; + +	for (i = 0, j = 0; i <= syscalltbl_native_max_id; ++i) { +		if (syscalltbl_native[i]) { +			entries[j].name = syscalltbl_native[i]; +			entries[j].id = i; +			++j; +		} +	} + +	qsort(tbl->syscalls.entries, nr_entries, sizeof(struct syscall), syscallcmp); +	tbl->syscalls.nr_entries = nr_entries; +	return 0; +} + +struct syscalltbl *syscalltbl__new(void) +{ +	struct syscalltbl *tbl = malloc(sizeof(*tbl)); +	if (tbl) { +		if (syscalltbl__init_native(tbl)) { +			free(tbl); +			return NULL; +		} +	} +	return tbl; +} + +void syscalltbl__delete(struct syscalltbl *tbl) +{ +	zfree(&tbl->syscalls.entries); +	free(tbl); +} + +const char *syscalltbl__name(const struct syscalltbl *tbl __maybe_unused, int id) +{ +	return id <= syscalltbl_native_max_id ? syscalltbl_native[id]: NULL; +} + +int syscalltbl__id(struct syscalltbl *tbl, const char *name) +{ +	struct syscall *sc = bsearch(name, tbl->syscalls.entries, +				     tbl->syscalls.nr_entries, sizeof(*sc), +				     syscallcmpname); + +	return sc ? sc->id : -1; +} + +#else /* HAVE_SYSCALL_TABLE */ + +#include <libaudit.h> + +struct syscalltbl *syscalltbl__new(void) +{ +	struct syscalltbl *tbl = malloc(sizeof(*tbl)); +	if (tbl) +		tbl->audit_machine = audit_detect_machine(); +	return tbl; +} + +void syscalltbl__delete(struct syscalltbl *tbl) +{ +	free(tbl); +} + +const char *syscalltbl__name(const struct syscalltbl *tbl, int id) +{ +	return audit_syscall_to_name(id, tbl->audit_machine); +} + +int syscalltbl__id(struct syscalltbl *tbl, const char *name) +{ +	return audit_name_to_syscall(name, tbl->audit_machine); +} +#endif /* HAVE_SYSCALL_TABLE */ diff --git a/tools/perf/util/syscalltbl.h b/tools/perf/util/syscalltbl.h new file mode 100644 index 000000000000..e2951510484f --- /dev/null +++ b/tools/perf/util/syscalltbl.h @@ -0,0 +1,20 @@ +#ifndef __PERF_SYSCALLTBL_H +#define __PERF_SYSCALLTBL_H + +struct syscalltbl { +	union { +		int audit_machine; +		struct { +			int nr_entries; +			void *entries; +		} syscalls; +	}; +}; + +struct syscalltbl *syscalltbl__new(void); +void syscalltbl__delete(struct syscalltbl *tbl); + +const char *syscalltbl__name(const struct syscalltbl *tbl, int id); +int syscalltbl__id(struct syscalltbl *tbl, const char *name); + +#endif /* __PERF_SYSCALLTBL_H */ diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c index 679688e70ae7..825086aa9a08 100644 --- a/tools/perf/util/thread-stack.c +++ b/tools/perf/util/thread-stack.c @@ -22,44 +22,9 @@  #include "debug.h"  #include "symbol.h"  #include "comm.h" +#include "call-path.h"  #include "thread-stack.h" -#define CALL_PATH_BLOCK_SHIFT 8 -#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT) -#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1) - -struct call_path_block { -	struct call_path cp[CALL_PATH_BLOCK_SIZE]; -	struct list_head node; -}; - -/** - * struct call_path_root - root of all call paths. - * @call_path: root call path - * @blocks: list of blocks to store call paths - * @next: next free space - * @sz: number of spaces - */ -struct call_path_root { -	struct call_path call_path; -	struct list_head blocks; -	size_t next; -	size_t sz; -}; - -/** - * struct call_return_processor - provides a call-back to consume call-return - *                                information. - * @cpr: call path root - * @process: call-back that accepts call/return information - * @data: anonymous data for call-back - */ -struct call_return_processor { -	struct call_path_root *cpr; -	int (*process)(struct call_return *cr, void *data); -	void *data; -}; -  #define STACK_GROWTH 2048  /** @@ -335,108 +300,6 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,  		chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;  } -static void call_path__init(struct call_path *cp, struct call_path *parent, -			    struct symbol *sym, u64 ip, bool in_kernel) -{ -	cp->parent = parent; -	cp->sym = sym; -	cp->ip = sym ? 0 : ip; -	cp->db_id = 0; -	cp->in_kernel = in_kernel; -	RB_CLEAR_NODE(&cp->rb_node); -	cp->children = RB_ROOT; -} - -static struct call_path_root *call_path_root__new(void) -{ -	struct call_path_root *cpr; - -	cpr = zalloc(sizeof(struct call_path_root)); -	if (!cpr) -		return NULL; -	call_path__init(&cpr->call_path, NULL, NULL, 0, false); -	INIT_LIST_HEAD(&cpr->blocks); -	return cpr; -} - -static void call_path_root__free(struct call_path_root *cpr) -{ -	struct call_path_block *pos, *n; - -	list_for_each_entry_safe(pos, n, &cpr->blocks, node) { -		list_del(&pos->node); -		free(pos); -	} -	free(cpr); -} - -static struct call_path *call_path__new(struct call_path_root *cpr, -					struct call_path *parent, -					struct symbol *sym, u64 ip, -					bool in_kernel) -{ -	struct call_path_block *cpb; -	struct call_path *cp; -	size_t n; - -	if (cpr->next < cpr->sz) { -		cpb = list_last_entry(&cpr->blocks, struct call_path_block, -				      node); -	} else { -		cpb = zalloc(sizeof(struct call_path_block)); -		if (!cpb) -			return NULL; -		list_add_tail(&cpb->node, &cpr->blocks); -		cpr->sz += CALL_PATH_BLOCK_SIZE; -	} - -	n = cpr->next++ & CALL_PATH_BLOCK_MASK; -	cp = &cpb->cp[n]; - -	call_path__init(cp, parent, sym, ip, in_kernel); - -	return cp; -} - -static struct call_path *call_path__findnew(struct call_path_root *cpr, -					    struct call_path *parent, -					    struct symbol *sym, u64 ip, u64 ks) -{ -	struct rb_node **p; -	struct rb_node *node_parent = NULL; -	struct call_path *cp; -	bool in_kernel = ip >= ks; - -	if (sym) -		ip = 0; - -	if (!parent) -		return call_path__new(cpr, parent, sym, ip, in_kernel); - -	p = &parent->children.rb_node; -	while (*p != NULL) { -		node_parent = *p; -		cp = rb_entry(node_parent, struct call_path, rb_node); - -		if (cp->sym == sym && cp->ip == ip) -			return cp; - -		if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) -			p = &(*p)->rb_left; -		else -			p = &(*p)->rb_right; -	} - -	cp = call_path__new(cpr, parent, sym, ip, in_kernel); -	if (!cp) -		return NULL; - -	rb_link_node(&cp->rb_node, node_parent, p); -	rb_insert_color(&cp->rb_node, &parent->children); - -	return cp; -} -  struct call_return_processor *  call_return_processor__new(int (*process)(struct call_return *cr, void *data),  			   void *data) diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h index e1528f1374c3..ad44c7944b8e 100644 --- a/tools/perf/util/thread-stack.h +++ b/tools/perf/util/thread-stack.h @@ -19,17 +19,16 @@  #include <sys/types.h>  #include <linux/types.h> -#include <linux/rbtree.h>  struct thread;  struct comm;  struct ip_callchain;  struct symbol;  struct dso; -struct call_return_processor;  struct comm;  struct perf_sample;  struct addr_location; +struct call_path;  /*   * Call/Return flags. @@ -69,26 +68,16 @@ struct call_return {  };  /** - * struct call_path - node in list of calls leading to a function call. - * @parent: call path to the parent function call - * @sym: symbol of function called - * @ip: only if sym is null, the ip of the function - * @db_id: id used for db-export - * @in_kernel: whether function is a in the kernel - * @rb_node: node in parent's tree of called functions - * @children: tree of call paths of functions called - * - * In combination with the call_return structure, the call_path structure - * defines a context-sensitve call-graph. + * struct call_return_processor - provides a call-back to consume call-return + *                                information. + * @cpr: call path root + * @process: call-back that accepts call/return information + * @data: anonymous data for call-back   */ -struct call_path { -	struct call_path *parent; -	struct symbol *sym; -	u64 ip; -	u64 db_id; -	bool in_kernel; -	struct rb_node rb_node; -	struct rb_root children; +struct call_return_processor { +	struct call_path_root *cpr; +	int (*process)(struct call_return *cr, void *data); +	void *data;  };  int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index dfd00c6dad6e..45fcb715a36b 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -10,6 +10,8 @@  #include "comm.h"  #include "unwind.h" +#include <api/fs/fs.h> +  int thread__init_map_groups(struct thread *thread, struct machine *machine)  {  	struct thread *leader; @@ -153,6 +155,23 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,  	return 0;  } +int thread__set_comm_from_proc(struct thread *thread) +{ +	char path[64]; +	char *comm = NULL; +	size_t sz; +	int err = -1; + +	if (!(snprintf(path, sizeof(path), "%d/task/%d/comm", +		       thread->pid_, thread->tid) >= (int)sizeof(path)) && +	    procfs__read_str(path, &comm, &sz) == 0) { +		comm[sz - 1] = '\0'; +		err = thread__set_comm(thread, comm, 0); +	} + +	return err; +} +  const char *thread__comm_str(const struct thread *thread)  {  	const struct comm *comm = thread__comm(thread); @@ -233,7 +252,7 @@ void thread__find_cpumode_addr_location(struct thread *thread,  					struct addr_location *al)  {  	size_t i; -	const u8 const cpumodes[] = { +	const u8 cpumodes[] = {  		PERF_RECORD_MISC_USER,  		PERF_RECORD_MISC_KERNEL,  		PERF_RECORD_MISC_GUEST_USER, diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index a0ac0317affb..45fba13c800b 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -9,6 +9,9 @@  #include "symbol.h"  #include <strlist.h>  #include <intlist.h> +#ifdef HAVE_LIBUNWIND_SUPPORT +#include <libunwind.h> +#endif  struct thread_stack; @@ -32,6 +35,9 @@ struct thread {  	void			*priv;  	struct thread_stack	*ts; +#ifdef HAVE_LIBUNWIND_SUPPORT +	unw_addr_space_t	addr_space; +#endif  };  struct machine; @@ -65,6 +71,8 @@ static inline int thread__set_comm(struct thread *thread, const char *comm,  	return __thread__set_comm(thread, comm, timestamp, false);  } +int thread__set_comm_from_proc(struct thread *thread); +  int thread__comm_len(struct thread *thread);  struct comm *thread__comm(const struct thread *thread);  struct comm *thread__exec_comm(const struct thread *thread); diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 08afc6909953..5654fe15e036 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -94,7 +94,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)  	DIR *proc;  	int max_threads = 32, items, i;  	char path[256]; -	struct dirent dirent, *next, **namelist = NULL; +	struct dirent *dirent, **namelist = NULL;  	struct thread_map *threads = thread_map__alloc(max_threads);  	if (threads == NULL) @@ -107,16 +107,16 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)  	threads->nr = 0;  	atomic_set(&threads->refcnt, 1); -	while (!readdir_r(proc, &dirent, &next) && next) { +	while ((dirent = readdir(proc)) != NULL) {  		char *end;  		bool grow = false;  		struct stat st; -		pid_t pid = strtol(dirent.d_name, &end, 10); +		pid_t pid = strtol(dirent->d_name, &end, 10);  		if (*end) /* only interested in proper numerical dirents */  			continue; -		snprintf(path, sizeof(path), "/proc/%s", dirent.d_name); +		snprintf(path, sizeof(path), "/proc/%s", dirent->d_name);  		if (stat(path, &st) != 0)  			continue; @@ -260,7 +260,7 @@ struct thread_map *thread_map__new_dummy(void)  	return threads;  } -static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) +struct thread_map *thread_map__new_by_tid_str(const char *tid_str)  {  	struct thread_map *threads = NULL, *nt;  	int ntasks = 0; @@ -436,3 +436,15 @@ struct thread_map *thread_map__new_event(struct thread_map_event *event)  	return threads;  } + +bool thread_map__has(struct thread_map *threads, pid_t pid) +{ +	int i; + +	for (i = 0; i < threads->nr; ++i) { +		if (threads->map[i].pid == pid) +			return true; +	} + +	return false; +} diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 85e4c7c4fbde..bd3b971588da 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -31,6 +31,8 @@ void thread_map__put(struct thread_map *map);  struct thread_map *thread_map__new_str(const char *pid,  		const char *tid, uid_t uid); +struct thread_map *thread_map__new_by_tid_str(const char *tid_str); +  size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);  static inline int thread_map__nr(struct thread_map *threads) @@ -55,4 +57,5 @@ static inline char *thread_map__comm(struct thread_map *map, int thread)  }  void thread_map__read_comms(struct thread_map *threads); +bool thread_map__has(struct thread_map *threads, pid_t pid);  #endif	/* __PERF_THREAD_MAP_H */ diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 55de4cffcd4e..ac2590a3de2d 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -57,6 +57,7 @@ struct perf_tool {  			id_index,  			auxtrace_info,  			auxtrace_error, +			time_conv,  			thread_map,  			cpu_map,  			stat_config, diff --git a/tools/perf/util/trigger.h b/tools/perf/util/trigger.h new file mode 100644 index 000000000000..e97d7016d771 --- /dev/null +++ b/tools/perf/util/trigger.h @@ -0,0 +1,94 @@ +#ifndef __TRIGGER_H_ +#define __TRIGGER_H_ 1 + +#include "util/debug.h" +#include "asm/bug.h" + +/* + * Use trigger to model operations which need to be executed when + * an event (a signal, for example) is observed. + * + * States and transits: + * + * + *  OFF--(on)--> READY --(hit)--> HIT + *                 ^               | + *                 |            (ready) + *                 |               | + *                  \_____________/ + * + * is_hit and is_ready are two key functions to query the state of + * a trigger. is_hit means the event already happen; is_ready means the + * trigger is waiting for the event. + */ + +struct trigger { +	volatile enum { +		TRIGGER_ERROR		= -2, +		TRIGGER_OFF		= -1, +		TRIGGER_READY		= 0, +		TRIGGER_HIT		= 1, +	} state; +	const char *name; +}; + +#define TRIGGER_WARN_ONCE(t, exp) \ +	WARN_ONCE(t->state != exp, "trigger '%s' state transist error: %d in %s()\n", \ +		  t->name, t->state, __func__) + +static inline bool trigger_is_available(struct trigger *t) +{ +	return t->state >= 0; +} + +static inline bool trigger_is_error(struct trigger *t) +{ +	return t->state <= TRIGGER_ERROR; +} + +static inline void trigger_on(struct trigger *t) +{ +	TRIGGER_WARN_ONCE(t, TRIGGER_OFF); +	t->state = TRIGGER_READY; +} + +static inline void trigger_ready(struct trigger *t) +{ +	if (!trigger_is_available(t)) +		return; +	t->state = TRIGGER_READY; +} + +static inline void trigger_hit(struct trigger *t) +{ +	if (!trigger_is_available(t)) +		return; +	TRIGGER_WARN_ONCE(t, TRIGGER_READY); +	t->state = TRIGGER_HIT; +} + +static inline void trigger_off(struct trigger *t) +{ +	if (!trigger_is_available(t)) +		return; +	t->state = TRIGGER_OFF; +} + +static inline void trigger_error(struct trigger *t) +{ +	t->state = TRIGGER_ERROR; +} + +static inline bool trigger_is_ready(struct trigger *t) +{ +	return t->state == TRIGGER_READY; +} + +static inline bool trigger_is_hit(struct trigger *t) +{ +	return t->state == TRIGGER_HIT; +} + +#define DEFINE_TRIGGER(n) \ +struct trigger n = {.state = TRIGGER_OFF, .name = #n} +#endif diff --git a/tools/perf/util/tsc.h b/tools/perf/util/tsc.h index a8b78f1b3243..d5b11e2b85e0 100644 --- a/tools/perf/util/tsc.h +++ b/tools/perf/util/tsc.h @@ -3,10 +3,29 @@  #include <linux/types.h> -#include "../arch/x86/util/tsc.h" +#include "event.h" + +struct perf_tsc_conversion { +	u16 time_shift; +	u32 time_mult; +	u64 time_zero; +}; +struct perf_event_mmap_page; + +int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, +			     struct perf_tsc_conversion *tc);  u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);  u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);  u64 rdtsc(void); +struct perf_event_mmap_page; +struct perf_tool; +struct machine; + +int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, +				struct perf_tool *tool, +				perf_event__handler_t process, +				struct machine *machine); +  #endif diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index ee7e372297e5..63687d3a344e 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -32,6 +32,7 @@  #include "symbol.h"  #include "util.h"  #include "debug.h" +#include "asm/bug.h"  extern int  UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, @@ -580,43 +581,33 @@ static unw_accessors_t accessors = {  int unwind__prepare_access(struct thread *thread)  { -	unw_addr_space_t addr_space; -  	if (callchain_param.record_mode != CALLCHAIN_DWARF)  		return 0; -	addr_space = unw_create_addr_space(&accessors, 0); -	if (!addr_space) { +	thread->addr_space = unw_create_addr_space(&accessors, 0); +	if (!thread->addr_space) {  		pr_err("unwind: Can't create unwind address space.\n");  		return -ENOMEM;  	} -	unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); -	thread__set_priv(thread, addr_space); - +	unw_set_caching_policy(thread->addr_space, UNW_CACHE_GLOBAL);  	return 0;  }  void unwind__flush_access(struct thread *thread)  { -	unw_addr_space_t addr_space; -  	if (callchain_param.record_mode != CALLCHAIN_DWARF)  		return; -	addr_space = thread__priv(thread); -	unw_flush_cache(addr_space, 0, 0); +	unw_flush_cache(thread->addr_space, 0, 0);  }  void unwind__finish_access(struct thread *thread)  { -	unw_addr_space_t addr_space; -  	if (callchain_param.record_mode != CALLCHAIN_DWARF)  		return; -	addr_space = thread__priv(thread); -	unw_destroy_addr_space(addr_space); +	unw_destroy_addr_space(thread->addr_space);  }  static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, @@ -639,7 +630,9 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,  	 * unwind itself.  	 */  	if (max_stack - 1 > 0) { -		addr_space = thread__priv(ui->thread); +		WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL"); +		addr_space = ui->thread->addr_space; +  		if (addr_space == NULL)  			return -1; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index b7766c577b01..eab077ad6ca9 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -33,6 +33,8 @@ struct callchain_param	callchain_param = {  unsigned int page_size;  int cacheline_size; +unsigned int sysctl_perf_event_max_stack = PERF_MAX_STACK_DEPTH; +  bool test_attr__enabled;  bool perf_host  = true; @@ -117,6 +119,40 @@ int rm_rf(char *path)  	return rmdir(path);  } +/* A filter which removes dot files */ +bool lsdir_no_dot_filter(const char *name __maybe_unused, struct dirent *d) +{ +	return d->d_name[0] != '.'; +} + +/* lsdir reads a directory and store it in strlist */ +struct strlist *lsdir(const char *name, +		      bool (*filter)(const char *, struct dirent *)) +{ +	struct strlist *list = NULL; +	DIR *dir; +	struct dirent *d; + +	dir = opendir(name); +	if (!dir) +		return NULL; + +	list = strlist__new(NULL, NULL); +	if (!list) { +		errno = ENOMEM; +		goto out; +	} + +	while ((d = readdir(dir)) != NULL) { +		if (!filter || filter(name, d)) +			strlist__add(list, d->d_name); +	} + +out: +	closedir(dir); +	return list; +} +  static int slow_copyfile(const char *from, const char *to)  {  	int err = -1; @@ -471,7 +507,6 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)  				       "needed for --call-graph fp\n");  			break; -#ifdef HAVE_DWARF_UNWIND_SUPPORT  		/* Dwarf style */  		} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {  			const unsigned long default_stack_dump_size = 8192; @@ -487,7 +522,6 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)  				ret = get_stack_size(tok, &size);  				param->dump_size = size;  			} -#endif /* HAVE_DWARF_UNWIND_SUPPORT */  		} else if (!strncmp(name, "lbr", sizeof("lbr"))) {  			if (!strtok_r(NULL, ",", &saveptr)) {  				param->record_mode = CALLCHAIN_LBR; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 8298d607c738..7651633a8dc7 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -79,6 +79,7 @@  #include <termios.h>  #include <linux/bitops.h>  #include <termios.h> +#include "strlist.h"  extern const char *graph_line;  extern const char *graph_dotted_line; @@ -159,12 +160,6 @@ static inline char *gitstrchrnul(const char *s, int c)  }  #endif -/* - * Wrappers: - */ -void *xrealloc(void *ptr, size_t size) __attribute__((weak)); - -  static inline void *zalloc(size_t size)  {  	return calloc(1, size); @@ -222,6 +217,8 @@ static inline int sane_case(int x, int high)  int mkdir_p(char *path, mode_t mode);  int rm_rf(char *path); +struct strlist *lsdir(const char *name, bool (*filter)(const char *, struct dirent *)); +bool lsdir_no_dot_filter(const char *name, struct dirent *d);  int copyfile(const char *from, const char *to);  int copyfile_mode(const char *from, const char *to, mode_t mode);  int copyfile_offset(int fromfd, loff_t from_ofs, int tofd, loff_t to_ofs, u64 size); @@ -254,11 +251,17 @@ int hex2u64(const char *ptr, u64 *val);  char *ltrim(char *s);  char *rtrim(char *s); +static inline char *trim(char *s) +{ +	return ltrim(rtrim(s)); +} +  void dump_stack(void);  void sighandler_dump_stack(int sig);  extern unsigned int page_size;  extern int cacheline_size; +extern unsigned int sysctl_perf_event_max_stack;  struct parse_tag {  	char tag; diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c deleted file mode 100644 index 5f1a07c4b87b..000000000000 --- a/tools/perf/util/wrapper.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Various trivial helper wrappers around standard functions - */ -#include "cache.h" - -/* - * There's no pack memory to release - but stay close to the Git - * version so wrap this away: - */ -static inline void release_pack_memory(size_t size __maybe_unused, -				       int flag __maybe_unused) -{ -} - -void *xrealloc(void *ptr, size_t size) -{ -	void *ret = realloc(ptr, size); -	if (!ret && !size) -		ret = realloc(ptr, 1); -	if (!ret) { -		release_pack_memory(size, -1); -		ret = realloc(ptr, size); -		if (!ret && !size) -			ret = realloc(ptr, 1); -		if (!ret) -			die("Out of memory, realloc failed"); -	} -	return ret; -} |