diff options
Diffstat (limited to 'tools/perf/util/record.c')
| -rw-r--r-- | tools/perf/util/record.c | 215 | 
1 files changed, 215 insertions, 0 deletions
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c new file mode 100644 index 00000000000..049e0a09ccd --- /dev/null +++ b/tools/perf/util/record.c @@ -0,0 +1,215 @@ +#include "evlist.h" +#include "evsel.h" +#include "cpumap.h" +#include "parse-events.h" +#include <api/fs/fs.h> +#include "util.h" + +typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); + +static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) +{ +	struct perf_evlist *evlist; +	struct perf_evsel *evsel; +	int err = -EAGAIN, fd; + +	evlist = perf_evlist__new(); +	if (!evlist) +		return -ENOMEM; + +	if (parse_events(evlist, str)) +		goto out_delete; + +	evsel = perf_evlist__first(evlist); + +	fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); +	if (fd < 0) +		goto out_delete; +	close(fd); + +	fn(evsel); + +	fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); +	if (fd < 0) { +		if (errno == EINVAL) +			err = -EINVAL; +		goto out_delete; +	} +	close(fd); +	err = 0; + +out_delete: +	perf_evlist__delete(evlist); +	return err; +} + +static bool perf_probe_api(setup_probe_fn_t fn) +{ +	const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL}; +	struct cpu_map *cpus; +	int cpu, ret, i = 0; + +	cpus = cpu_map__new(NULL); +	if (!cpus) +		return false; +	cpu = cpus->map[0]; +	cpu_map__delete(cpus); + +	do { +		ret = perf_do_probe_api(fn, cpu, try[i++]); +		if (!ret) +			return true; +	} while (ret == -EAGAIN && try[i]); + +	return false; +} + +static void perf_probe_sample_identifier(struct perf_evsel *evsel) +{ +	evsel->attr.sample_type |= PERF_SAMPLE_IDENTIFIER; +} + +bool perf_can_sample_identifier(void) +{ +	return perf_probe_api(perf_probe_sample_identifier); +} + +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) +{ +	struct perf_evsel *evsel; +	bool use_sample_identifier = false; + +	/* +	 * Set the evsel leader links before we configure attributes, +	 * since some might depend on this info. +	 */ +	if (opts->group) +		perf_evlist__set_leader(evlist); + +	if (evlist->cpus->map[0] < 0) +		opts->no_inherit = true; + +	evlist__for_each(evlist, evsel) +		perf_evsel__config(evsel, opts); + +	if (evlist->nr_entries > 1) { +		struct perf_evsel *first = perf_evlist__first(evlist); + +		evlist__for_each(evlist, evsel) { +			if (evsel->attr.sample_type == first->attr.sample_type) +				continue; +			use_sample_identifier = perf_can_sample_identifier(); +			break; +		} +		evlist__for_each(evlist, evsel) +			perf_evsel__set_sample_id(evsel, use_sample_identifier); +	} + +	perf_evlist__set_id_pos(evlist); +} + +static int get_max_rate(unsigned int *rate) +{ +	char path[PATH_MAX]; +	const char *procfs = procfs__mountpoint(); + +	if (!procfs) +		return -1; + +	snprintf(path, PATH_MAX, +		 "%s/sys/kernel/perf_event_max_sample_rate", procfs); + +	return filename__read_int(path, (int *) rate); +} + +static int record_opts__config_freq(struct record_opts *opts) +{ +	bool user_freq = opts->user_freq != UINT_MAX; +	unsigned int max_rate; + +	if (opts->user_interval != ULLONG_MAX) +		opts->default_interval = opts->user_interval; +	if (user_freq) +		opts->freq = opts->user_freq; + +	/* +	 * User specified count overrides default frequency. +	 */ +	if (opts->default_interval) +		opts->freq = 0; +	else if (opts->freq) { +		opts->default_interval = opts->freq; +	} else { +		pr_err("frequency and count are zero, aborting\n"); +		return -1; +	} + +	if (get_max_rate(&max_rate)) +		return 0; + +	/* +	 * User specified frequency is over current maximum. +	 */ +	if (user_freq && (max_rate < opts->freq)) { +		pr_err("Maximum frequency rate (%u) reached.\n" +		   "Please use -F freq option with lower value or consider\n" +		   "tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n", +		   max_rate); +		return -1; +	} + +	/* +	 * Default frequency is over current maximum. +	 */ +	if (max_rate < opts->freq) { +		pr_warning("Lowering default frequency rate to %u.\n" +			   "Please consider tweaking " +			   "/proc/sys/kernel/perf_event_max_sample_rate.\n", +			   max_rate); +		opts->freq = max_rate; +	} + +	return 0; +} + +int record_opts__config(struct record_opts *opts) +{ +	return record_opts__config_freq(opts); +} + +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) +{ +	struct perf_evlist *temp_evlist; +	struct perf_evsel *evsel; +	int err, fd, cpu; +	bool ret = false; + +	temp_evlist = perf_evlist__new(); +	if (!temp_evlist) +		return false; + +	err = parse_events(temp_evlist, str); +	if (err) +		goto out_delete; + +	evsel = perf_evlist__last(temp_evlist); + +	if (!evlist || cpu_map__empty(evlist->cpus)) { +		struct cpu_map *cpus = cpu_map__new(NULL); + +		cpu =  cpus ? cpus->map[0] : 0; +		cpu_map__delete(cpus); +	} else { +		cpu = evlist->cpus->map[0]; +	} + +	fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); +	if (fd >= 0) { +		close(fd); +		ret = true; +	} + +out_delete: +	perf_evlist__delete(temp_evlist); +	return ret; +}  | 
