1335 lines · 30.9 KB · C
RawDownload
#include "builtin.h" #include "perf.h"#include "util/cache.h"#include "util/debug.h"#include "util/exec_cmd.h"#include "util/header.h"#include "util/parse-options.h"#include "util/session.h"#include "util/symbol.h"#include "util/thread.h"#include "util/trace-event.h"#include "util/util.h"#include "util/evlist.h"#include "util/evsel.h"#include <linux/bitmap.h> static char const		*script_name;static char const		*generate_script_lang;static bool			debug_mode;static u64			last_timestamp;static u64			nr_unordered;extern const struct option	record_options[];static bool			no_callchain;static bool			show_full_info;static const char		*cpu_list;static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); enum perf_output_field {	PERF_OUTPUT_COMM            = 1U << 0,	PERF_OUTPUT_TID             = 1U << 1,	PERF_OUTPUT_PID             = 1U << 2,	PERF_OUTPUT_TIME            = 1U << 3,	PERF_OUTPUT_CPU             = 1U << 4,	PERF_OUTPUT_EVNAME          = 1U << 5,	PERF_OUTPUT_TRACE           = 1U << 6,	PERF_OUTPUT_IP              = 1U << 7,	PERF_OUTPUT_SYM             = 1U << 8,	PERF_OUTPUT_DSO             = 1U << 9,	PERF_OUTPUT_ADDR            = 1U << 10,}; struct output_option {	const char *str;	enum perf_output_field field;} all_output_options[] = {	{.str = "comm",  .field = PERF_OUTPUT_COMM},	{.str = "tid",   .field = PERF_OUTPUT_TID},	{.str = "pid",   .field = PERF_OUTPUT_PID},	{.str = "time",  .field = PERF_OUTPUT_TIME},	{.str = "cpu",   .field = PERF_OUTPUT_CPU},	{.str = "event", .field = PERF_OUTPUT_EVNAME},	{.str = "trace", .field = PERF_OUTPUT_TRACE},	{.str = "ip",    .field = PERF_OUTPUT_IP},	{.str = "sym",   .field = PERF_OUTPUT_SYM},	{.str = "dso",   .field = PERF_OUTPUT_DSO},	{.str = "addr",  .field = PERF_OUTPUT_ADDR},}; /* default set to maintain compatibility with current format */static struct {	bool user_set;	bool wildcard_set;	u64 fields;	u64 invalid_fields;} output[PERF_TYPE_MAX] = { 	[PERF_TYPE_HARDWARE] = {		.user_set = false, 		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |				  PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, 		.invalid_fields = PERF_OUTPUT_TRACE,	}, 	[PERF_TYPE_SOFTWARE] = {		.user_set = false, 		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |				  PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, 		.invalid_fields = PERF_OUTPUT_TRACE,	}, 	[PERF_TYPE_TRACEPOINT] = {		.user_set = false, 		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |				  PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |				  PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE,	}, 	[PERF_TYPE_RAW] = {		.user_set = false, 		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |				  PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, 		.invalid_fields = PERF_OUTPUT_TRACE,	},}; static bool output_set_by_user(void){	int j;	for (j = 0; j < PERF_TYPE_MAX; ++j) {		if (output[j].user_set)			return true;	}	return false;} static const char *output_field2str(enum perf_output_field field){	int i, imax = ARRAY_SIZE(all_output_options);	const char *str = ""; 	for (i = 0; i < imax; ++i) {		if (all_output_options[i].field == field) {			str = all_output_options[i].str;			break;		}	}	return str;} #define PRINT_FIELD(x)  (output[attr->type].fields & PERF_OUTPUT_##x) static int perf_event_attr__check_stype(struct perf_event_attr *attr,				  u64 sample_type, const char *sample_msg,				  enum perf_output_field field){	int type = attr->type;	const char *evname; 	if (attr->sample_type & sample_type)		return 0; 	if (output[type].user_set) {		evname = __event_name(attr->type, attr->config);		pr_err("Samples for '%s' event do not have %s attribute set. "		       "Cannot print '%s' field.\n",		       evname, sample_msg, output_field2str(field));		return -1;	} 	/* user did not ask for it explicitly so remove from the default list */	output[type].fields &= ~field;	evname = __event_name(attr->type, attr->config);	pr_debug("Samples for '%s' event do not have %s attribute set. "		 "Skipping '%s' field.\n",		 evname, sample_msg, output_field2str(field)); 	return 0;} static int perf_evsel__check_attr(struct perf_evsel *evsel,				  struct perf_session *session){	struct perf_event_attr *attr = &evsel->attr; 	if (PRINT_FIELD(TRACE) &&		!perf_session__has_traces(session, "record -R"))		return -EINVAL; 	if (PRINT_FIELD(IP)) {		if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP",					   PERF_OUTPUT_IP))			return -EINVAL; 		if (!no_callchain &&		    !(attr->sample_type & PERF_SAMPLE_CALLCHAIN))			symbol_conf.use_callchain = false;	} 	if (PRINT_FIELD(ADDR) &&		perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR",				       PERF_OUTPUT_ADDR))		return -EINVAL; 	if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {		pr_err("Display of symbols requested but neither sample IP nor "			   "sample address\nis selected. Hence, no addresses to convert "		       "to symbols.\n");		return -EINVAL;	}	if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {		pr_err("Display of DSO requested but neither sample IP nor "			   "sample address\nis selected. Hence, no addresses to convert "		       "to DSO.\n");		return -EINVAL;	} 	if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&		perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID",				       PERF_OUTPUT_TID|PERF_OUTPUT_PID))		return -EINVAL; 	if (PRINT_FIELD(TIME) &&		perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME",				       PERF_OUTPUT_TIME))		return -EINVAL; 	if (PRINT_FIELD(CPU) &&		perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU",				       PERF_OUTPUT_CPU))		return -EINVAL; 	return 0;} /* * verify all user requested events exist and the samples * have the expected data */static int perf_session__check_output_opt(struct perf_session *session){	int j;	struct perf_evsel *evsel; 	for (j = 0; j < PERF_TYPE_MAX; ++j) {		evsel = perf_session__find_first_evtype(session, j); 		/*		 * even if fields is set to 0 (ie., show nothing) event must		 * exist if user explicitly includes it on the command line		 */		if (!evsel && output[j].user_set && !output[j].wildcard_set) {			pr_err("%s events do not exist. "			       "Remove corresponding -f option to proceed.\n",			       event_type(j));			return -1;		} 		if (evsel && output[j].fields &&			perf_evsel__check_attr(evsel, session))			return -1;	} 	return 0;} static void print_sample_start(struct perf_sample *sample,			       struct thread *thread,			       struct perf_event_attr *attr){	int type;	struct event *event;	const char *evname = NULL;	unsigned long secs;	unsigned long usecs;	unsigned long long nsecs; 	if (PRINT_FIELD(COMM)) {		if (latency_format)			printf("%8.8s ", thread->comm);		else if (PRINT_FIELD(IP) && symbol_conf.use_callchain)			printf("%s ", thread->comm);		else			printf("%16s ", thread->comm);	} 	if (PRINT_FIELD(PID) && PRINT_FIELD(TID))		printf("%5d/%-5d ", sample->pid, sample->tid);	else if (PRINT_FIELD(PID))		printf("%5d ", sample->pid);	else if (PRINT_FIELD(TID))		printf("%5d ", sample->tid); 	if (PRINT_FIELD(CPU)) {		if (latency_format)			printf("%3d ", sample->cpu);		else			printf("[%03d] ", sample->cpu);	} 	if (PRINT_FIELD(TIME)) {		nsecs = sample->time;		secs = nsecs / NSECS_PER_SEC;		nsecs -= secs * NSECS_PER_SEC;		usecs = nsecs / NSECS_PER_USEC;		printf("%5lu.%06lu: ", secs, usecs);	} 	if (PRINT_FIELD(EVNAME)) {		if (attr->type == PERF_TYPE_TRACEPOINT) {			type = trace_parse_common_type(sample->raw_data);			event = trace_find_event(type);			if (event)				evname = event->name;		} else			evname = __event_name(attr->type, attr->config); 		printf("%s: ", evname ? evname : "(unknown)");	}} static bool sample_addr_correlates_sym(struct perf_event_attr *attr){	if ((attr->type == PERF_TYPE_SOFTWARE) &&	    ((attr->config == PERF_COUNT_SW_PAGE_FAULTS) ||	     (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN) ||	     (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)))		return true; 	return false;} static void print_sample_addr(union perf_event *event,			  struct perf_sample *sample,			  struct perf_session *session,			  struct thread *thread,			  struct perf_event_attr *attr){	struct addr_location al;	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;	const char *symname, *dsoname; 	printf("%16" PRIx64, sample->addr); 	if (!sample_addr_correlates_sym(attr))		return; 	thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,			      event->ip.pid, sample->addr, &al);	if (!al.map)		thread__find_addr_map(thread, session, cpumode, MAP__VARIABLE,				      event->ip.pid, sample->addr, &al); 	al.cpu = sample->cpu;	al.sym = NULL; 	if (al.map)		al.sym = map__find_symbol(al.map, al.addr, NULL); 	if (PRINT_FIELD(SYM)) {		if (al.sym && al.sym->name)			symname = al.sym->name;		else			symname = ""; 		printf(" %16s", symname);	} 	if (PRINT_FIELD(DSO)) {		if (al.map && al.map->dso && al.map->dso->name)			dsoname = al.map->dso->name;		else			dsoname = ""; 		printf(" (%s)", dsoname);	}} static void process_event(union perf_event *event __unused,			  struct perf_sample *sample,			  struct perf_evsel *evsel,			  struct perf_session *session,			  struct thread *thread){	struct perf_event_attr *attr = &evsel->attr; 	if (output[attr->type].fields == 0)		return; 	print_sample_start(sample, thread, attr); 	if (PRINT_FIELD(TRACE))		print_trace_event(sample->cpu, sample->raw_data,				  sample->raw_size); 	if (PRINT_FIELD(ADDR))		print_sample_addr(event, sample, session, thread, attr); 	if (PRINT_FIELD(IP)) {		if (!symbol_conf.use_callchain)			printf(" ");		else			printf("\n");		perf_session__print_ip(event, sample, session,					      PRINT_FIELD(SYM), PRINT_FIELD(DSO));	} 	printf("\n");} static int default_start_script(const char *script __unused,				int argc __unused,				const char **argv __unused){	return 0;} static int default_stop_script(void){	return 0;} static int default_generate_script(const char *outfile __unused){	return 0;} static struct scripting_ops default_scripting_ops = {	.start_script		= default_start_script,	.stop_script		= default_stop_script,	.process_event		= process_event,	.generate_script	= default_generate_script,}; static struct scripting_ops	*scripting_ops; static void setup_scripting(void){	setup_perl_scripting();	setup_python_scripting(); 	scripting_ops = &default_scripting_ops;} static int cleanup_scripting(void){	pr_debug("\nperf script stopped\n"); 	return scripting_ops->stop_script();} static char const		*input_name = "perf.data"; static int process_sample_event(union perf_event *event,				struct perf_sample *sample,				struct perf_evsel *evsel,				struct perf_session *session){	struct thread *thread = perf_session__findnew(session, event->ip.pid); 	if (thread == NULL) {		pr_debug("problem processing %d event, skipping it.\n",			 event->header.type);		return -1;	} 	if (debug_mode) {		if (sample->time < last_timestamp) {			pr_err("Samples misordered, previous: %" PRIu64				" this: %" PRIu64 "\n", last_timestamp,				sample->time);			nr_unordered++;		}		last_timestamp = sample->time;		return 0;	} 	if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))		return 0; 	scripting_ops->process_event(event, sample, evsel, session, thread); 	session->hists.stats.total_period += sample->period;	return 0;} static struct perf_event_ops event_ops = {	.sample		 = process_sample_event,	.mmap		 = perf_event__process_mmap,	.comm		 = perf_event__process_comm,	.exit		 = perf_event__process_task,	.fork		 = perf_event__process_task,	.attr		 = perf_event__process_attr,	.event_type	 = perf_event__process_event_type,	.tracing_data	 = perf_event__process_tracing_data,	.build_id	 = perf_event__process_build_id,	.ordered_samples = true,	.ordering_requires_timestamps = true,}; extern volatile int session_done; static void sig_handler(int sig __unused){	session_done = 1;} static int __cmd_script(struct perf_session *session){	int ret; 	signal(SIGINT, sig_handler); 	ret = perf_session__process_events(session, &event_ops); 	if (debug_mode)		pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered);

Lines 1–500 of 1335.