359 lines · 7.4 KB · C
RawDownload
/* * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License (not later!) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h> #include "../perf.h"#include "util.h"#include "trace-event.h" int header_page_size_size;int header_page_ts_size;int header_page_data_offset; struct pevent *perf_pevent;static struct pevent *pevent; bool latency_format; int read_trace_init(int file_bigendian, int host_bigendian){	if (pevent)		return 0; 	perf_pevent = pevent_alloc();	pevent = perf_pevent; 	pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT);	pevent_set_file_bigendian(pevent, file_bigendian);	pevent_set_host_bigendian(pevent, host_bigendian); 	return 0;} static int get_common_field(struct scripting_context *context,			    int *offset, int *size, const char *type){	struct event_format *event;	struct format_field *field; 	if (!*size) {		if (!pevent->events)			return 0; 		event = pevent->events[0];		field = pevent_find_common_field(event, type);		if (!field)			return 0;		*offset = field->offset;		*size = field->size;	} 	return pevent_read_number(pevent, context->event_data + *offset, *size);} int common_lock_depth(struct scripting_context *context){	static int offset;	static int size;	int ret; 	ret = get_common_field(context, &size, &offset,			       "common_lock_depth");	if (ret < 0)		return -1; 	return ret;} int common_flags(struct scripting_context *context){	static int offset;	static int size;	int ret; 	ret = get_common_field(context, &size, &offset,			       "common_flags");	if (ret < 0)		return -1; 	return ret;} int common_pc(struct scripting_context *context){	static int offset;	static int size;	int ret; 	ret = get_common_field(context, &size, &offset,			       "common_preempt_count");	if (ret < 0)		return -1; 	return ret;} unsigned long longraw_field_value(struct event_format *event, const char *name, void *data){	struct format_field *field;	unsigned long long val; 	field = pevent_find_any_field(event, name);	if (!field)		return 0ULL; 	pevent_read_number_field(field, data, &val); 	return val;} void *raw_field_ptr(struct event_format *event, const char *name, void *data){	struct format_field *field; 	field = pevent_find_any_field(event, name);	if (!field)		return NULL; 	if (field->flags & FIELD_IS_DYNAMIC) {		int offset; 		offset = *(int *)(data + field->offset);		offset &= 0xffff; 		return data + offset;	} 	return data + field->offset;} int trace_parse_common_type(void *data){	struct pevent_record record; 	record.data = data;	return pevent_data_type(pevent, &record);} int trace_parse_common_pid(void *data){	struct pevent_record record; 	record.data = data;	return pevent_data_pid(pevent, &record);} unsigned long long read_size(void *ptr, int size){	return pevent_read_number(pevent, ptr, size);} struct event_format *trace_find_event(int type){	return pevent_find_event(pevent, type);}  void print_trace_event(int cpu, void *data, int size){	struct event_format *event;	struct pevent_record record;	struct trace_seq s;	int type; 	type = trace_parse_common_type(data); 	event = trace_find_event(type);	if (!event) {		warning("ug! no event found for type %d", type);		return;	} 	memset(&record, 0, sizeof(record));	record.cpu = cpu;	record.size = size;	record.data = data; 	trace_seq_init(&s);	pevent_print_event(pevent, &s, &record);	trace_seq_do_printf(&s);	printf("\n");} void print_event(int cpu, void *data, int size, unsigned long long nsecs,		  char *comm){	struct pevent_record record;	struct trace_seq s;	int pid; 	pevent->latency_format = latency_format; 	record.ts = nsecs;	record.cpu = cpu;	record.size = size;	record.data = data;	pid = pevent_data_pid(pevent, &record); 	if (!pevent_pid_is_registered(pevent, pid))		pevent_register_comm(pevent, comm, pid); 	trace_seq_init(&s);	pevent_print_event(pevent, &s, &record);	trace_seq_do_printf(&s);	printf("\n");} void parse_proc_kallsyms(char *file, unsigned int size __unused){	unsigned long long addr;	char *func;	char *line;	char *next = NULL;	char *addr_str;	char *mod;	char ch; 	line = strtok_r(file, "\n", &next);	while (line) {		mod = NULL;		sscanf(line, "%as %c %as\t[%as",		       (float *)(void *)&addr_str, /* workaround gcc warning */		       &ch, (float *)(void *)&func, (float *)(void *)&mod);		addr = strtoull(addr_str, NULL, 16);		free(addr_str); 		/* truncate the extra ']' */		if (mod)			mod[strlen(mod) - 1] = 0; 		pevent_register_function(pevent, func, addr, mod);		free(func);		free(mod); 		line = strtok_r(NULL, "\n", &next);	}} void parse_ftrace_printk(char *file, unsigned int size __unused){	unsigned long long addr;	char *printk;	char *line;	char *next = NULL;	char *addr_str;	char *fmt; 	line = strtok_r(file, "\n", &next);	while (line) {		addr_str = strtok_r(line, ":", &fmt);		if (!addr_str) {			warning("printk format with empty entry");			break;		}		addr = strtoull(addr_str, NULL, 16);		/* fmt still has a space, skip it */		printk = strdup(fmt+1);		line = strtok_r(NULL, "\n", &next);		pevent_register_print_string(pevent, printk, addr);	}} int parse_ftrace_file(char *buf, unsigned long size){	return pevent_parse_event(pevent, buf, size, "ftrace");} int parse_event_file(char *buf, unsigned long size, char *sys){	return pevent_parse_event(pevent, buf, size, sys);} struct event_format *trace_find_next_event(struct event_format *event){	static int idx; 	if (!pevent->events)		return NULL; 	if (!event) {		idx = 0;		return pevent->events[0];	} 	if (idx < pevent->nr_events && event == pevent->events[idx]) {		idx++;		if (idx == pevent->nr_events)			return NULL;		return pevent->events[idx];	} 	for (idx = 1; idx < pevent->nr_events; idx++) {		if (event == pevent->events[idx - 1])			return pevent->events[idx];	}	return NULL;} struct flag {	const char *name;	unsigned long long value;}; static const struct flag flags[] = {	{ "HI_SOFTIRQ", 0 },	{ "TIMER_SOFTIRQ", 1 },	{ "NET_TX_SOFTIRQ", 2 },	{ "NET_RX_SOFTIRQ", 3 },	{ "BLOCK_SOFTIRQ", 4 },	{ "BLOCK_IOPOLL_SOFTIRQ", 5 },	{ "TASKLET_SOFTIRQ", 6 },	{ "SCHED_SOFTIRQ", 7 },	{ "HRTIMER_SOFTIRQ", 8 },	{ "RCU_SOFTIRQ", 9 }, 	{ "HRTIMER_NORESTART", 0 },	{ "HRTIMER_RESTART", 1 },}; unsigned long long eval_flag(const char *flag){	int i; 	/*	 * Some flags in the format files do not get converted.	 * If the flag is not numeric, see if it is something that	 * we already know about.	 */	if (isdigit(flag[0]))		return strtoull(flag, NULL, 0); 	for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++)		if (strcmp(flags[i].name, flag) == 0)			return flags[i].value; 	return 0;}