diff options
Diffstat (limited to 'tools/perf/builtin-stat.c')
| -rw-r--r-- | tools/perf/builtin-stat.c | 282 | 
1 files changed, 200 insertions, 82 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 0a15253b438c..a098c2ebf4ea 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -65,6 +65,7 @@  #include "util/target.h"  #include "util/time-utils.h"  #include "util/top.h" +#include "util/affinity.h"  #include "asm/bug.h"  #include <linux/time64.h> @@ -265,15 +266,10 @@ static int read_single_counter(struct evsel *counter, int cpu,   * Read out the results of a single counter:   * do not aggregate counts across CPUs in system-wide mode   */ -static int read_counter(struct evsel *counter, struct timespec *rs) +static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu)  {  	int nthreads = perf_thread_map__nr(evsel_list->core.threads); -	int ncpus, cpu, thread; - -	if (target__has_cpu(&target) && !target__has_per_thread(&target)) -		ncpus = perf_evsel__nr_cpus(counter); -	else -		ncpus = 1; +	int thread;  	if (!counter->supported)  		return -ENOENT; @@ -282,39 +278,37 @@ static int read_counter(struct evsel *counter, struct timespec *rs)  		nthreads = 1;  	for (thread = 0; thread < nthreads; thread++) { -		for (cpu = 0; cpu < ncpus; cpu++) { -			struct perf_counts_values *count; +		struct perf_counts_values *count; -			count = perf_counts(counter->counts, cpu, thread); +		count = perf_counts(counter->counts, cpu, thread); -			/* -			 * The leader's group read loads data into its group members -			 * (via perf_evsel__read_counter) and sets threir count->loaded. -			 */ -			if (!perf_counts__is_loaded(counter->counts, cpu, thread) && -			    read_single_counter(counter, cpu, thread, rs)) { -				counter->counts->scaled = -1; -				perf_counts(counter->counts, cpu, thread)->ena = 0; -				perf_counts(counter->counts, cpu, thread)->run = 0; -				return -1; -			} +		/* +		 * The leader's group read loads data into its group members +		 * (via perf_evsel__read_counter()) and sets their count->loaded. +		 */ +		if (!perf_counts__is_loaded(counter->counts, cpu, thread) && +		    read_single_counter(counter, cpu, thread, rs)) { +			counter->counts->scaled = -1; +			perf_counts(counter->counts, cpu, thread)->ena = 0; +			perf_counts(counter->counts, cpu, thread)->run = 0; +			return -1; +		} -			perf_counts__set_loaded(counter->counts, cpu, thread, false); +		perf_counts__set_loaded(counter->counts, cpu, thread, false); -			if (STAT_RECORD) { -				if (perf_evsel__write_stat_event(counter, cpu, thread, count)) { -					pr_err("failed to write stat event\n"); -					return -1; -				} +		if (STAT_RECORD) { +			if (perf_evsel__write_stat_event(counter, cpu, thread, count)) { +				pr_err("failed to write stat event\n"); +				return -1;  			} +		} -			if (verbose > 1) { -				fprintf(stat_config.output, -					"%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", -						perf_evsel__name(counter), -						cpu, -						count->val, count->ena, count->run); -			} +		if (verbose > 1) { +			fprintf(stat_config.output, +				"%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", +					perf_evsel__name(counter), +					cpu, +					count->val, count->ena, count->run);  		}  	} @@ -324,15 +318,37 @@ static int read_counter(struct evsel *counter, struct timespec *rs)  static void read_counters(struct timespec *rs)  {  	struct evsel *counter; -	int ret; +	struct affinity affinity; +	int i, ncpus, cpu; + +	if (affinity__setup(&affinity) < 0) +		return; + +	ncpus = perf_cpu_map__nr(evsel_list->core.all_cpus); +	if (!target__has_cpu(&target) || target__has_per_thread(&target)) +		ncpus = 1; +	evlist__for_each_cpu(evsel_list, i, cpu) { +		if (i >= ncpus) +			break; +		affinity__set(&affinity, cpu); + +		evlist__for_each_entry(evsel_list, counter) { +			if (evsel__cpu_iter_skip(counter, cpu)) +				continue; +			if (!counter->err) { +				counter->err = read_counter_cpu(counter, rs, +								counter->cpu_iter - 1); +			} +		} +	} +	affinity__cleanup(&affinity);  	evlist__for_each_entry(evsel_list, counter) { -		ret = read_counter(counter, rs); -		if (ret) +		if (counter->err)  			pr_debug("failed to read counter %s\n", counter->name); - -		if (ret == 0 && perf_stat_process_counter(&stat_config, counter)) +		if (counter->err == 0 && perf_stat_process_counter(&stat_config, counter))  			pr_warning("failed to process counter %s\n", counter->name); +		counter->err = 0;  	}  } @@ -420,6 +436,62 @@ static bool is_target_alive(struct target *_target,  	return false;  } +enum counter_recovery { +	COUNTER_SKIP, +	COUNTER_RETRY, +	COUNTER_FATAL, +}; + +static enum counter_recovery stat_handle_error(struct evsel *counter) +{ +	char msg[BUFSIZ]; +	/* +	 * PPC returns ENXIO for HW counters until 2.6.37 +	 * (behavior changed with commit b0a873e). +	 */ +	if (errno == EINVAL || errno == ENOSYS || +	    errno == ENOENT || errno == EOPNOTSUPP || +	    errno == ENXIO) { +		if (verbose > 0) +			ui__warning("%s event is not supported by the kernel.\n", +				    perf_evsel__name(counter)); +		counter->supported = false; +		/* +		 * errored is a sticky flag that means one of the counter's +		 * cpu event had a problem and needs to be reexamined. +		 */ +		counter->errored = true; + +		if ((counter->leader != counter) || +		    !(counter->leader->core.nr_members > 1)) +			return COUNTER_SKIP; +	} else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { +		if (verbose > 0) +			ui__warning("%s\n", msg); +		return COUNTER_RETRY; +	} else if (target__has_per_thread(&target) && +		   evsel_list->core.threads && +		   evsel_list->core.threads->err_thread != -1) { +		/* +		 * For global --per-thread case, skip current +		 * error thread. +		 */ +		if (!thread_map__remove(evsel_list->core.threads, +					evsel_list->core.threads->err_thread)) { +			evsel_list->core.threads->err_thread = -1; +			return COUNTER_RETRY; +		} +	} + +	perf_evsel__open_strerror(counter, &target, +				  errno, msg, sizeof(msg)); +	ui__error("%s\n", msg); + +	if (child_pid != -1) +		kill(child_pid, SIGTERM); +	return COUNTER_FATAL; +} +  static int __run_perf_stat(int argc, const char **argv, int run_idx)  {  	int interval = stat_config.interval; @@ -433,6 +505,9 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)  	int status = 0;  	const bool forks = (argc > 0);  	bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false; +	struct affinity affinity; +	int i, cpu; +	bool second_pass = false;  	if (interval) {  		ts.tv_sec  = interval / USEC_PER_MSEC; @@ -457,61 +532,104 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)  	if (group)  		perf_evlist__set_leader(evsel_list); -	evlist__for_each_entry(evsel_list, counter) { -try_again: -		if (create_perf_stat_counter(counter, &stat_config, &target) < 0) { +	if (affinity__setup(&affinity) < 0) +		return -1; -			/* Weak group failed. Reset the group. */ -			if ((errno == EINVAL || errno == EBADF) && -			    counter->leader != counter && -			    counter->weak_group) { -				counter = perf_evlist__reset_weak_group(evsel_list, counter); -				goto try_again; -			} +	evlist__for_each_cpu (evsel_list, i, cpu) { +		affinity__set(&affinity, cpu); -			/* -			 * PPC returns ENXIO for HW counters until 2.6.37 -			 * (behavior changed with commit b0a873e). -			 */ -			if (errno == EINVAL || errno == ENOSYS || -			    errno == ENOENT || errno == EOPNOTSUPP || -			    errno == ENXIO) { -				if (verbose > 0) -					ui__warning("%s event is not supported by the kernel.\n", -						    perf_evsel__name(counter)); -				counter->supported = false; +		evlist__for_each_entry(evsel_list, counter) { +			if (evsel__cpu_iter_skip(counter, cpu)) +				continue; +			if (counter->reset_group || counter->errored) +				continue; +try_again: +			if (create_perf_stat_counter(counter, &stat_config, &target, +						     counter->cpu_iter - 1) < 0) { -				if ((counter->leader != counter) || -				    !(counter->leader->core.nr_members > 1)) -					continue; -			} else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { -                                if (verbose > 0) -                                        ui__warning("%s\n", msg); -                                goto try_again; -			} else if (target__has_per_thread(&target) && -				   evsel_list->core.threads && -				   evsel_list->core.threads->err_thread != -1) {  				/* -				 * For global --per-thread case, skip current -				 * error thread. +				 * Weak group failed. We cannot just undo this here +				 * because earlier CPUs might be in group mode, and the kernel +				 * doesn't support mixing group and non group reads. Defer +				 * it to later. +				 * Don't close here because we're in the wrong affinity.  				 */ -				if (!thread_map__remove(evsel_list->core.threads, -							evsel_list->core.threads->err_thread)) { -					evsel_list->core.threads->err_thread = -1; +				if ((errno == EINVAL || errno == EBADF) && +				    counter->leader != counter && +				    counter->weak_group) { +					perf_evlist__reset_weak_group(evsel_list, counter, false); +					assert(counter->reset_group); +					second_pass = true; +					continue; +				} + +				switch (stat_handle_error(counter)) { +				case COUNTER_FATAL: +					return -1; +				case COUNTER_RETRY:  					goto try_again; +				case COUNTER_SKIP: +					continue; +				default: +					break;  				} +  			} +			counter->supported = true; +		} +	} -			perf_evsel__open_strerror(counter, &target, -						  errno, msg, sizeof(msg)); -			ui__error("%s\n", msg); +	if (second_pass) { +		/* +		 * Now redo all the weak group after closing them, +		 * and also close errored counters. +		 */ -			if (child_pid != -1) -				kill(child_pid, SIGTERM); +		evlist__for_each_cpu(evsel_list, i, cpu) { +			affinity__set(&affinity, cpu); +			/* First close errored or weak retry */ +			evlist__for_each_entry(evsel_list, counter) { +				if (!counter->reset_group && !counter->errored) +					continue; +				if (evsel__cpu_iter_skip_no_inc(counter, cpu)) +					continue; +				perf_evsel__close_cpu(&counter->core, counter->cpu_iter); +			} +			/* Now reopen weak */ +			evlist__for_each_entry(evsel_list, counter) { +				if (!counter->reset_group && !counter->errored) +					continue; +				if (evsel__cpu_iter_skip(counter, cpu)) +					continue; +				if (!counter->reset_group) +					continue; +try_again_reset: +				pr_debug2("reopening weak %s\n", perf_evsel__name(counter)); +				if (create_perf_stat_counter(counter, &stat_config, &target, +							     counter->cpu_iter - 1) < 0) { -			return -1; +					switch (stat_handle_error(counter)) { +					case COUNTER_FATAL: +						return -1; +					case COUNTER_RETRY: +						goto try_again_reset; +					case COUNTER_SKIP: +						continue; +					default: +						break; +					} +				} +				counter->supported = true; +			} +		} +	} +	affinity__cleanup(&affinity); + +	evlist__for_each_entry(evsel_list, counter) { +		if (!counter->supported) { +			perf_evsel__free_fd(&counter->core); +			continue;  		} -		counter->supported = true;  		l = strlen(counter->unit);  		if (l > stat_config.unit_width)  | 
