diff options
Diffstat (limited to 'tools/perf/builtin-stat.c')
| -rw-r--r-- | tools/perf/builtin-stat.c | 121 | 
1 files changed, 96 insertions, 25 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 69523ed55894..59af5a8419e2 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -65,6 +65,7 @@  #include "util/tool.h"  #include "util/group.h"  #include "util/string2.h" +#include "util/metricgroup.h"  #include "asm/bug.h"  #include <linux/time64.h> @@ -133,6 +134,8 @@ static const char *smi_cost_attrs = {  static struct perf_evlist	*evsel_list; +static struct rblist		 metric_events; +  static struct target target = {  	.uid	= UINT_MAX,  }; @@ -172,7 +175,7 @@ static int			print_free_counters_hint;  struct perf_stat {  	bool			 record; -	struct perf_data_file	 file; +	struct perf_data	 data;  	struct perf_session	*session;  	u64			 bytes_written;  	struct perf_tool	 tool; @@ -192,6 +195,11 @@ static struct perf_stat_config stat_config = {  	.scale		= true,  }; +static bool is_duration_time(struct perf_evsel *evsel) +{ +	return !strcmp(evsel->name, "duration_time"); +} +  static inline void diff_timespec(struct timespec *r, struct timespec *a,  				 struct timespec *b)  { @@ -245,7 +253,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)  	 * by attr->sample_type != 0, and we can't run it on  	 * stat sessions.  	 */ -	if (!(STAT_RECORD && perf_stat.file.is_pipe)) +	if (!(STAT_RECORD && perf_stat.data.is_pipe))  		attr->sample_type = PERF_SAMPLE_IDENTIFIER;  	/* @@ -287,7 +295,7 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused,  				     struct perf_sample *sample __maybe_unused,  				     struct machine *machine __maybe_unused)  { -	if (perf_data_file__write(&perf_stat.file, event, event->header.size) < 0) { +	if (perf_data__write(&perf_stat.data, event, event->header.size) < 0) {  		pr_err("failed to write perf data, error: %m\n");  		return -1;  	} @@ -407,6 +415,8 @@ static void process_interval(void)  			pr_err("failed to write stat round event\n");  	} +	init_stats(&walltime_nsecs_stats); +	update_stats(&walltime_nsecs_stats, stat_config.interval * 1000000);  	print_counters(&rs, 0, NULL);  } @@ -582,6 +592,32 @@ static bool perf_evsel__should_store_id(struct perf_evsel *counter)  	return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID;  } +static struct perf_evsel *perf_evsel__reset_weak_group(struct perf_evsel *evsel) +{ +	struct perf_evsel *c2, *leader; +	bool is_open = true; + +	leader = evsel->leader; +	pr_debug("Weak group for %s/%d failed\n", +			leader->name, leader->nr_members); + +	/* +	 * for_each_group_member doesn't work here because it doesn't +	 * include the first entry. +	 */ +	evlist__for_each_entry(evsel_list, c2) { +		if (c2 == evsel) +			is_open = false; +		if (c2->leader == leader) { +			if (is_open) +				perf_evsel__close(c2); +			c2->leader = c2; +			c2->nr_members = 0; +		} +	} +	return leader; +} +  static int __run_perf_stat(int argc, const char **argv)  {  	int interval = stat_config.interval; @@ -592,7 +628,7 @@ static int __run_perf_stat(int argc, const char **argv)  	size_t l;  	int status = 0;  	const bool forks = (argc > 0); -	bool is_pipe = STAT_RECORD ? perf_stat.file.is_pipe : false; +	bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false;  	struct perf_evsel_config_term *err_term;  	if (interval) { @@ -618,6 +654,15 @@ static int __run_perf_stat(int argc, const char **argv)  	evlist__for_each_entry(evsel_list, counter) {  try_again:  		if (create_perf_stat_counter(counter) < 0) { + +			/* Weak group failed. Reset the group. */ +			if ((errno == EINVAL || errno == EBADF) && +			    counter->leader != counter && +			    counter->weak_group) { +				counter = perf_evsel__reset_weak_group(counter); +				goto try_again; +			} +  			/*  			 * PPC returns ENXIO for HW counters until 2.6.37  			 * (behavior changed with commit b0a873e). @@ -674,10 +719,10 @@ try_again:  	}  	if (STAT_RECORD) { -		int err, fd = perf_data_file__fd(&perf_stat.file); +		int err, fd = perf_data__fd(&perf_stat.data);  		if (is_pipe) { -			err = perf_header__write_pipe(perf_data_file__fd(&perf_stat.file)); +			err = perf_header__write_pipe(perf_data__fd(&perf_stat.data));  		} else {  			err = perf_session__write_header(perf_stat.session, evsel_list,  							 fd, false); @@ -800,7 +845,7 @@ static void print_noise(struct perf_evsel *evsel, double avg)  	if (run_count == 1)  		return; -	ps = evsel->priv; +	ps = evsel->stats;  	print_noise_pct(stddev_stats(&ps->res_stats[0]), avg);  } @@ -1199,7 +1244,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,  	perf_stat__print_shadow_stats(counter, uval,  				first_shadow_cpu(counter, id), -				&out); +				&out, &metric_events);  	if (!csv_output && !metric_only) {  		print_noise(counter, noise);  		print_running(run, ena); @@ -1222,8 +1267,7 @@ static void aggr_update_shadow(void)  					continue;  				val += perf_counts(counter->counts, cpu, 0)->val;  			} -			val = val * counter->scale; -			perf_stat__update_shadow_stats(counter, &val, +			perf_stat__update_shadow_stats(counter, val,  						       first_shadow_cpu(counter, id));  		}  	} @@ -1325,6 +1369,9 @@ static void print_aggr(char *prefix)  		ad.id = id = aggr_map->map[s];  		first = true;  		evlist__for_each_entry(evsel_list, counter) { +			if (is_duration_time(counter)) +				continue; +  			ad.val = ad.ena = ad.run = 0;  			ad.nr = 0;  			if (!collect_data(counter, aggr_cb, &ad)) @@ -1384,7 +1431,7 @@ static void counter_aggr_cb(struct perf_evsel *counter, void *data,  			    bool first __maybe_unused)  {  	struct caggr_data *cd = data; -	struct perf_stat_evsel *ps = counter->priv; +	struct perf_stat_evsel *ps = counter->stats;  	cd->avg += avg_stats(&ps->res_stats[0]);  	cd->avg_enabled += avg_stats(&ps->res_stats[1]); @@ -1468,6 +1515,8 @@ static void print_no_aggr_metric(char *prefix)  		if (prefix)  			fputs(prefix, stat_config.output);  		evlist__for_each_entry(evsel_list, counter) { +			if (is_duration_time(counter)) +				continue;  			if (first) {  				aggr_printout(counter, cpu, 0);  				first = false; @@ -1522,6 +1571,8 @@ static void print_metric_headers(const char *prefix, bool no_indent)  	/* Print metrics headers only */  	evlist__for_each_entry(evsel_list, counter) { +		if (is_duration_time(counter)) +			continue;  		os.evsel = counter;  		out.ctx = &os;  		out.print_metric = print_metric_header; @@ -1530,7 +1581,8 @@ static void print_metric_headers(const char *prefix, bool no_indent)  		os.evsel = counter;  		perf_stat__print_shadow_stats(counter, 0,  					      0, -					      &out); +					      &out, +					      &metric_events);  	}  	fputc('\n', stat_config.output);  } @@ -1643,7 +1695,7 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)  	char buf[64], *prefix = NULL;  	/* Do not print anything if we record to the pipe. */ -	if (STAT_RECORD && perf_stat.file.is_pipe) +	if (STAT_RECORD && perf_stat.data.is_pipe)  		return;  	if (interval) @@ -1668,12 +1720,18 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)  		print_aggr(prefix);  		break;  	case AGGR_THREAD: -		evlist__for_each_entry(evsel_list, counter) +		evlist__for_each_entry(evsel_list, counter) { +			if (is_duration_time(counter)) +				continue;  			print_aggr_thread(counter, prefix); +		}  		break;  	case AGGR_GLOBAL: -		evlist__for_each_entry(evsel_list, counter) +		evlist__for_each_entry(evsel_list, counter) { +			if (is_duration_time(counter)) +				continue;  			print_counter_aggr(counter, prefix); +		}  		if (metric_only)  			fputc('\n', stat_config.output);  		break; @@ -1681,8 +1739,11 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)  		if (metric_only)  			print_no_aggr_metric(prefix);  		else { -			evlist__for_each_entry(evsel_list, counter) +			evlist__for_each_entry(evsel_list, counter) { +				if (is_duration_time(counter)) +					continue;  				print_counter(counter, prefix); +			}  		}  		break;  	case AGGR_UNSET: @@ -1754,6 +1815,13 @@ static int enable_metric_only(const struct option *opt __maybe_unused,  	return 0;  } +static int parse_metric_groups(const struct option *opt, +			       const char *str, +			       int unset __maybe_unused) +{ +	return metricgroup__parse_groups(opt, str, &metric_events); +} +  static const struct option stat_options[] = {  	OPT_BOOLEAN('T', "transaction", &transaction_run,  		    "hardware transaction statistics"), @@ -1819,6 +1887,9 @@ static const struct option stat_options[] = {  			"measure topdown level 1 statistics"),  	OPT_BOOLEAN(0, "smi-cost", &smi_cost,  			"measure SMI cost"), +	OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list", +		     "monitor specified metrics or metric groups (separated by ,)", +		     parse_metric_groups),  	OPT_END()  }; @@ -2334,20 +2405,20 @@ static void init_features(struct perf_session *session)  static int __cmd_record(int argc, const char **argv)  {  	struct perf_session *session; -	struct perf_data_file *file = &perf_stat.file; +	struct perf_data *data = &perf_stat.data;  	argc = parse_options(argc, argv, stat_options, stat_record_usage,  			     PARSE_OPT_STOP_AT_NON_OPTION);  	if (output_name) -		file->path = output_name; +		data->file.path = output_name;  	if (run_count != 1 || forever) {  		pr_err("Cannot use -r option with perf stat record.\n");  		return -1;  	} -	session = perf_session__new(file, false, NULL); +	session = perf_session__new(data, false, NULL);  	if (session == NULL) {  		pr_err("Perf session creation failed.\n");  		return -1; @@ -2405,7 +2476,7 @@ int process_stat_config_event(struct perf_tool *tool,  	if (st->aggr_mode != AGGR_UNSET)  		stat_config.aggr_mode = st->aggr_mode; -	if (perf_stat.file.is_pipe) +	if (perf_stat.data.is_pipe)  		perf_stat_init_aggr_mode();  	else  		perf_stat_init_aggr_mode_file(st); @@ -2513,10 +2584,10 @@ static int __cmd_report(int argc, const char **argv)  			input_name = "perf.data";  	} -	perf_stat.file.path = input_name; -	perf_stat.file.mode = PERF_DATA_MODE_READ; +	perf_stat.data.file.path = input_name; +	perf_stat.data.mode      = PERF_DATA_MODE_READ; -	session = perf_session__new(&perf_stat.file, false, &perf_stat.tool); +	session = perf_session__new(&perf_stat.data, false, &perf_stat.tool);  	if (session == NULL)  		return -1; @@ -2787,7 +2858,7 @@ int cmd_stat(int argc, const char **argv)  		 * records, but the need to suppress the kptr_restrict messages in older  		 * tools remain  -acme  		 */ -		int fd = perf_data_file__fd(&perf_stat.file); +		int fd = perf_data__fd(&perf_stat.data);  		int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat,  							     process_synthesized_event,  							     &perf_stat.session->machines.host); @@ -2801,7 +2872,7 @@ int cmd_stat(int argc, const char **argv)  				pr_err("failed to write stat round event\n");  		} -		if (!perf_stat.file.is_pipe) { +		if (!perf_stat.data.is_pipe) {  			perf_stat.session->header.data_size += perf_stat.bytes_written;  			perf_session__write_header(perf_stat.session, evsel_list, fd, true);  		}  | 
