perf record: Add clockid parameter
Teach perf-record about the new perf_event_attr::{use_clockid, clockid}
fields. Add a simple parameter to set the clock (if any) to be used for
the events to be recorded into the data file.
Since we store the entire perf_event_attr in the EVENT_DESC section we
also already store the used clockid in the data file.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: David Ahern <dsahern@gmail.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Yunlong Song <yunlong.song@huawei.com>
Link: http://lkml.kernel.org/r/20150407154851.GR23123@twins.programming.kicks-ass.net
[ Conditionally define CLOCK_BOOTTIME, at least rhel6 doesn't have it - dsahern
  Ditto for CLOCK_MONOTONIC_RAW, sles11sp2 doesn't have it - yunlong.song ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									ff5f3bbd40
								
							
						
					
					
						commit
						814c8c38e1
					
				| @ -250,6 +250,13 @@ is off by default. | ||||
| --running-time:: | ||||
| Record running and enabled time for read events (:S) | ||||
| 
 | ||||
| -k:: | ||||
| --clockid:: | ||||
| Sets the clock id to use for the various time fields in the perf_event_type | ||||
| records. See clock_gettime(). In particular CLOCK_MONOTONIC and | ||||
| CLOCK_MONOTONIC_RAW are supported, some events might also allow | ||||
| CLOCK_BOOTTIME, CLOCK_REALTIME and CLOCK_TAI. | ||||
| 
 | ||||
| SEE ALSO | ||||
| -------- | ||||
| linkperf:perf-stat[1], linkperf:perf-list[1] | ||||
|  | ||||
| @ -711,6 +711,90 @@ static int perf_record_config(const char *var, const char *value, void *cb) | ||||
| 	return perf_default_config(var, value, cb); | ||||
| } | ||||
| 
 | ||||
| struct clockid_map { | ||||
| 	const char *name; | ||||
| 	int clockid; | ||||
| }; | ||||
| 
 | ||||
| #define CLOCKID_MAP(n, c)	\ | ||||
| 	{ .name = n, .clockid = (c), } | ||||
| 
 | ||||
| #define CLOCKID_END	{ .name = NULL, } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Add the missing ones, we need to build on many distros... | ||||
|  */ | ||||
| #ifndef CLOCK_MONOTONIC_RAW | ||||
| #define CLOCK_MONOTONIC_RAW 4 | ||||
| #endif | ||||
| #ifndef CLOCK_BOOTTIME | ||||
| #define CLOCK_BOOTTIME 7 | ||||
| #endif | ||||
| #ifndef CLOCK_TAI | ||||
| #define CLOCK_TAI 11 | ||||
| #endif | ||||
| 
 | ||||
| static const struct clockid_map clockids[] = { | ||||
| 	/* available for all events, NMI safe */ | ||||
| 	CLOCKID_MAP("monotonic", CLOCK_MONOTONIC), | ||||
| 	CLOCKID_MAP("monotonic_raw", CLOCK_MONOTONIC_RAW), | ||||
| 
 | ||||
| 	/* available for some events */ | ||||
| 	CLOCKID_MAP("realtime", CLOCK_REALTIME), | ||||
| 	CLOCKID_MAP("boottime", CLOCK_BOOTTIME), | ||||
| 	CLOCKID_MAP("tai", CLOCK_TAI), | ||||
| 
 | ||||
| 	/* available for the lazy */ | ||||
| 	CLOCKID_MAP("mono", CLOCK_MONOTONIC), | ||||
| 	CLOCKID_MAP("raw", CLOCK_MONOTONIC_RAW), | ||||
| 	CLOCKID_MAP("real", CLOCK_REALTIME), | ||||
| 	CLOCKID_MAP("boot", CLOCK_BOOTTIME), | ||||
| 
 | ||||
| 	CLOCKID_END, | ||||
| }; | ||||
| 
 | ||||
| static int parse_clockid(const struct option *opt, const char *str, int unset) | ||||
| { | ||||
| 	struct record_opts *opts = (struct record_opts *)opt->value; | ||||
| 	const struct clockid_map *cm; | ||||
| 	const char *ostr = str; | ||||
| 
 | ||||
| 	if (unset) { | ||||
| 		opts->use_clockid = 0; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* no arg passed */ | ||||
| 	if (!str) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* no setting it twice */ | ||||
| 	if (opts->use_clockid) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	opts->use_clockid = true; | ||||
| 
 | ||||
| 	/* if its a number, we're done */ | ||||
| 	if (sscanf(str, "%d", &opts->clockid) == 1) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* allow a "CLOCK_" prefix to the name */ | ||||
| 	if (!strncasecmp(str, "CLOCK_", 6)) | ||||
| 		str += 6; | ||||
| 
 | ||||
| 	for (cm = clockids; cm->name; cm++) { | ||||
| 		if (!strcasecmp(str, cm->name)) { | ||||
| 			opts->clockid = cm->clockid; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	opts->use_clockid = false; | ||||
| 	ui__warning("unknown clockid %s, check man page\n", ostr); | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static const char * const __record_usage[] = { | ||||
| 	"perf record [<options>] [<command>]", | ||||
| 	"perf record [<options>] -- <command> [<options>]", | ||||
| @ -842,6 +926,9 @@ struct option __record_options[] = { | ||||
| 		    "Sample machine registers on interrupt"), | ||||
| 	OPT_BOOLEAN(0, "running-time", &record.opts.running_time, | ||||
| 		    "Record running/enabled time of read (:S) events"), | ||||
| 	OPT_CALLBACK('k', "clockid", &record.opts, | ||||
| 	"clockid", "clockid to use for events, see clock_gettime()", | ||||
| 	parse_clockid), | ||||
| 	OPT_END() | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -62,6 +62,8 @@ struct record_opts { | ||||
| 	u64	     user_interval; | ||||
| 	bool	     sample_transaction; | ||||
| 	unsigned     initial_delay; | ||||
| 	bool         use_clockid; | ||||
| 	clockid_t    clockid; | ||||
| }; | ||||
| 
 | ||||
| struct option; | ||||
|  | ||||
| @ -32,8 +32,12 @@ static struct { | ||||
| 	bool exclude_guest; | ||||
| 	bool mmap2; | ||||
| 	bool cloexec; | ||||
| 	bool clockid; | ||||
| 	bool clockid_wrong; | ||||
| } perf_missing_features; | ||||
| 
 | ||||
| static clockid_t clockid; | ||||
| 
 | ||||
| static int perf_evsel__no_extra_init(struct perf_evsel *evsel __maybe_unused) | ||||
| { | ||||
| 	return 0; | ||||
| @ -761,6 +765,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | ||||
| 		attr->disabled = 0; | ||||
| 		attr->enable_on_exec = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	clockid = opts->clockid; | ||||
| 	if (opts->use_clockid) { | ||||
| 		attr->use_clockid = 1; | ||||
| 		attr->clockid = opts->clockid; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | ||||
| @ -1036,7 +1046,6 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | ||||
| 	ret += PRINT_ATTR2(exclude_user, exclude_kernel); | ||||
| 	ret += PRINT_ATTR2(exclude_hv, exclude_idle); | ||||
| 	ret += PRINT_ATTR2(mmap, comm); | ||||
| 	ret += PRINT_ATTR2(mmap2, comm_exec); | ||||
| 	ret += PRINT_ATTR2(freq, inherit_stat); | ||||
| 	ret += PRINT_ATTR2(enable_on_exec, task); | ||||
| 	ret += PRINT_ATTR2(watermark, precise_ip); | ||||
| @ -1044,6 +1053,9 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | ||||
| 	ret += PRINT_ATTR2(exclude_host, exclude_guest); | ||||
| 	ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, | ||||
| 			    "excl.callchain_user", exclude_callchain_user); | ||||
| 	ret += PRINT_ATTR2(mmap2, comm_exec); | ||||
| 	ret += __PRINT_ATTR("%u",,use_clockid); | ||||
| 
 | ||||
| 
 | ||||
| 	ret += PRINT_ATTR_U32(wakeup_events); | ||||
| 	ret += PRINT_ATTR_U32(wakeup_watermark); | ||||
| @ -1055,6 +1067,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | ||||
| 	ret += PRINT_ATTR_X64(branch_sample_type); | ||||
| 	ret += PRINT_ATTR_X64(sample_regs_user); | ||||
| 	ret += PRINT_ATTR_U32(sample_stack_user); | ||||
| 	ret += PRINT_ATTR_U32(clockid); | ||||
| 	ret += PRINT_ATTR_X64(sample_regs_intr); | ||||
| 
 | ||||
| 	ret += fprintf(fp, "%.60s\n", graph_dotted_line); | ||||
| @ -1085,6 +1098,12 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||||
| 	} | ||||
| 
 | ||||
| fallback_missing_features: | ||||
| 	if (perf_missing_features.clockid_wrong) | ||||
| 		evsel->attr.clockid = CLOCK_MONOTONIC; /* should always work */ | ||||
| 	if (perf_missing_features.clockid) { | ||||
| 		evsel->attr.use_clockid = 0; | ||||
| 		evsel->attr.clockid = 0; | ||||
| 	} | ||||
| 	if (perf_missing_features.cloexec) | ||||
| 		flags &= ~(unsigned long)PERF_FLAG_FD_CLOEXEC; | ||||
| 	if (perf_missing_features.mmap2) | ||||
| @ -1122,6 +1141,17 @@ retry_open: | ||||
| 				goto try_fallback; | ||||
| 			} | ||||
| 			set_rlimit = NO_CHANGE; | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * If we succeeded but had to kill clockid, fail and | ||||
| 			 * have perf_evsel__open_strerror() print us a nice | ||||
| 			 * error. | ||||
| 			 */ | ||||
| 			if (perf_missing_features.clockid || | ||||
| 			    perf_missing_features.clockid_wrong) { | ||||
| 				err = -EINVAL; | ||||
| 				goto out_close; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -1155,7 +1185,17 @@ try_fallback: | ||||
| 	if (err != -EINVAL || cpu > 0 || thread > 0) | ||||
| 		goto out_close; | ||||
| 
 | ||||
| 	if (!perf_missing_features.cloexec && (flags & PERF_FLAG_FD_CLOEXEC)) { | ||||
| 	/*
 | ||||
| 	 * Must probe features in the order they were added to the | ||||
| 	 * perf_event_attr interface. | ||||
| 	 */ | ||||
| 	if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) { | ||||
| 		perf_missing_features.clockid_wrong = true; | ||||
| 		goto fallback_missing_features; | ||||
| 	} else if (!perf_missing_features.clockid && evsel->attr.use_clockid) { | ||||
| 		perf_missing_features.clockid = true; | ||||
| 		goto fallback_missing_features; | ||||
| 	} else if (!perf_missing_features.cloexec && (flags & PERF_FLAG_FD_CLOEXEC)) { | ||||
| 		perf_missing_features.cloexec = true; | ||||
| 		goto fallback_missing_features; | ||||
| 	} else if (!perf_missing_features.mmap2 && evsel->attr.mmap2) { | ||||
| @ -2063,9 +2103,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | ||||
| 		if_print(exclude_hv); | ||||
| 		if_print(exclude_idle); | ||||
| 		if_print(mmap); | ||||
| 		if_print(mmap2); | ||||
| 		if_print(comm); | ||||
| 		if_print(comm_exec); | ||||
| 		if_print(freq); | ||||
| 		if_print(inherit_stat); | ||||
| 		if_print(enable_on_exec); | ||||
| @ -2076,10 +2114,17 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | ||||
| 		if_print(sample_id_all); | ||||
| 		if_print(exclude_host); | ||||
| 		if_print(exclude_guest); | ||||
| 		if_print(mmap2); | ||||
| 		if_print(comm_exec); | ||||
| 		if_print(use_clockid); | ||||
| 		if_print(__reserved_1); | ||||
| 		if_print(wakeup_events); | ||||
| 		if_print(bp_type); | ||||
| 		if_print(branch_sample_type); | ||||
| 		if_print(sample_regs_user); | ||||
| 		if_print(sample_stack_user); | ||||
| 		if_print(clockid); | ||||
| 		if_print(sample_regs_intr); | ||||
| 	} | ||||
| out: | ||||
| 	fputc('\n', fp); | ||||
| @ -2158,6 +2203,12 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | ||||
| 	"The PMU counters are busy/taken by another profiler.\n" | ||||
| 	"We found oprofile daemon running, please stop it and try again."); | ||||
| 		break; | ||||
| 	case EINVAL: | ||||
| 		if (perf_missing_features.clockid) | ||||
| 			return scnprintf(msg, size, "clockid feature not supported."); | ||||
| 		if (perf_missing_features.clockid_wrong) | ||||
| 			return scnprintf(msg, size, "wrong clockid (%d).", clockid); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| @ -1098,6 +1098,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | ||||
| 			} | ||||
| 			fprintf(fp, " }"); | ||||
| 		} | ||||
| 		if (evsel->attr.use_clockid) | ||||
| 			fprintf(fp, ", clockid = %d", evsel->attr.clockid); | ||||
| 
 | ||||
| 
 | ||||
| 		fputc('\n', fp); | ||||
| 	} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user