perf tools: Add time conversion event
Intel PT uses the time members from the perf_event_mmap_page to convert between TSC and perf time. Due to a lack of foresight when Intel PT was implemented, those time members were recorded in the (implementation dependent) AUXTRACE_INFO event, the structure of which is generally inaccessible outside of the Intel PT decoder. However now the conversion between TSC and perf time is needed when processing a jitdump file when Intel PT has been used for tracing. So add a user event to record the time members. 'perf record' will synthesize the event if the information is available. And session processing will put a copy of the event on the session so that tools like 'perf inject' can easily access it. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/r/1457426324-30158-1-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
		
							parent
							
								
									39878d492c
								
							
						
					
					
						commit
						46bc29b970
					
				| @ -46,3 +46,34 @@ u64 rdtsc(void) | ||||
| 
 | ||||
| 	return low | ((u64)high) << 32; | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
| 	union perf_event event = { | ||||
| 		.time_conv = { | ||||
| 			.header = { | ||||
| 				.type = PERF_RECORD_TIME_CONV, | ||||
| 				.size = sizeof(struct time_conv_event), | ||||
| 			}, | ||||
| 		}, | ||||
| 	}; | ||||
| 	struct perf_tsc_conversion tc; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = perf_read_tsc_conversion(pc, &tc); | ||||
| 	if (err == -EOPNOTSUPP) | ||||
| 		return 0; | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	pr_debug2("Synthesizing TSC conversion information\n"); | ||||
| 
 | ||||
| 	event.time_conv.time_mult  = tc.time_mult; | ||||
| 	event.time_conv.time_shift = tc.time_shift; | ||||
| 	event.time_conv.time_zero  = tc.time_zero; | ||||
| 
 | ||||
| 	return process(tool, &event, NULL, machine); | ||||
| } | ||||
|  | ||||
| @ -748,6 +748,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | ||||
| 			.auxtrace_info	= perf_event__repipe_op2_synth, | ||||
| 			.auxtrace	= perf_event__repipe_auxtrace, | ||||
| 			.auxtrace_error	= perf_event__repipe_op2_synth, | ||||
| 			.time_conv	= perf_event__repipe_op2_synth, | ||||
| 			.finished_round	= perf_event__repipe_oe_synth, | ||||
| 			.build_id	= perf_event__repipe_op2_synth, | ||||
| 			.id_index	= perf_event__repipe_op2_synth, | ||||
|  | ||||
| @ -29,6 +29,7 @@ | ||||
| #include "util/data.h" | ||||
| #include "util/perf_regs.h" | ||||
| #include "util/auxtrace.h" | ||||
| #include "util/tsc.h" | ||||
| #include "util/parse-branch-options.h" | ||||
| #include "util/parse-regs-options.h" | ||||
| #include "util/llvm-utils.h" | ||||
| @ -512,6 +513,15 @@ static void workload_exec_failed_signal(int signo __maybe_unused, | ||||
| 
 | ||||
| static void snapshot_sig_handler(int sig); | ||||
| 
 | ||||
| int __weak | ||||
| perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused, | ||||
| 			    struct perf_tool *tool __maybe_unused, | ||||
| 			    perf_event__handler_t process __maybe_unused, | ||||
| 			    struct machine *machine __maybe_unused) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int record__synthesize(struct record *rec) | ||||
| { | ||||
| 	struct perf_session *session = rec->session; | ||||
| @ -549,6 +559,11 @@ static int record__synthesize(struct record *rec) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	err = perf_event__synth_time_conv(rec->evlist->mmap[0].base, tool, | ||||
| 					  process_synthesized_event, machine); | ||||
| 	if (err) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (rec->opts.full_auxtrace) { | ||||
| 		err = perf_event__synthesize_auxtrace_info(rec->itr, tool, | ||||
| 					session, process_synthesized_event); | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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) | ||||
| @ -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, | ||||
| }; | ||||
| 
 | ||||
| @ -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; | ||||
| 	} | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -57,6 +57,7 @@ struct perf_tool { | ||||
| 			id_index, | ||||
| 			auxtrace_info, | ||||
| 			auxtrace_error, | ||||
| 			time_conv, | ||||
| 			thread_map, | ||||
| 			cpu_map, | ||||
| 			stat_config, | ||||
|  | ||||
| @ -3,10 +3,20 @@ | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| #include "event.h" | ||||
| #include "../arch/x86/util/tsc.h" | ||||
| 
 | ||||
| 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 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user