1755 lines · 37.2 KB · C
RawDownload
/* * event tracer * * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * *  - Added format output of fields of the trace point. *    This was based off of work by Tom Zanussi <tzanussi@gmail.com>. * */ #include <linux/workqueue.h>#include <linux/spinlock.h>#include <linux/kthread.h>#include <linux/debugfs.h>#include <linux/uaccess.h>#include <linux/module.h>#include <linux/ctype.h>#include <linux/slab.h>#include <linux/delay.h> #include <asm/setup.h> #include "trace_output.h" #undef TRACE_SYSTEM#define TRACE_SYSTEM "TRACE_SYSTEM" DEFINE_MUTEX(event_mutex); DEFINE_MUTEX(event_storage_mutex);EXPORT_SYMBOL_GPL(event_storage_mutex); char event_storage[EVENT_STORAGE_SIZE];EXPORT_SYMBOL_GPL(event_storage); LIST_HEAD(ftrace_events);LIST_HEAD(ftrace_common_fields); struct list_head *trace_get_fields(struct ftrace_event_call *event_call){	if (!event_call->class->get_fields)		return &event_call->class->fields;	return event_call->class->get_fields(event_call);} static int __trace_define_field(struct list_head *head, const char *type,				const char *name, int offset, int size,				int is_signed, int filter_type){	struct ftrace_event_field *field; 	field = kzalloc(sizeof(*field), GFP_KERNEL);	if (!field)		goto err; 	field->name = kstrdup(name, GFP_KERNEL);	if (!field->name)		goto err; 	field->type = kstrdup(type, GFP_KERNEL);	if (!field->type)		goto err; 	if (filter_type == FILTER_OTHER)		field->filter_type = filter_assign_type(type);	else		field->filter_type = filter_type; 	field->offset = offset;	field->size = size;	field->is_signed = is_signed; 	list_add(&field->link, head); 	return 0; err:	if (field)		kfree(field->name);	kfree(field); 	return -ENOMEM;} int trace_define_field(struct ftrace_event_call *call, const char *type,		       const char *name, int offset, int size, int is_signed,		       int filter_type){	struct list_head *head; 	if (WARN_ON(!call->class))		return 0; 	head = trace_get_fields(call);	return __trace_define_field(head, type, name, offset, size,				    is_signed, filter_type);}EXPORT_SYMBOL_GPL(trace_define_field); #define __common_field(type, item)					\	ret = __trace_define_field(&ftrace_common_fields, #type,	\				   "common_" #item,			\				   offsetof(typeof(ent), item),		\				   sizeof(ent.item),			\				   is_signed_type(type), FILTER_OTHER);	\	if (ret)							\		return ret; static int trace_define_common_fields(void){	int ret;	struct trace_entry ent; 	__common_field(unsigned short, type);	__common_field(unsigned char, flags);	__common_field(unsigned char, preempt_count);	__common_field(int, pid);	__common_field(int, padding); 	return ret;} void trace_destroy_fields(struct ftrace_event_call *call){	struct ftrace_event_field *field, *next;	struct list_head *head; 	head = trace_get_fields(call);	list_for_each_entry_safe(field, next, head, link) {		list_del(&field->link);		kfree(field->type);		kfree(field->name);		kfree(field);	}} int trace_event_raw_init(struct ftrace_event_call *call){	int id; 	id = register_ftrace_event(&call->event);	if (!id)		return -ENODEV; 	return 0;}EXPORT_SYMBOL_GPL(trace_event_raw_init); int ftrace_event_reg(struct ftrace_event_call *call,		     enum trace_reg type, void *data){	switch (type) {	case TRACE_REG_REGISTER:		return tracepoint_probe_register(call->name,						 call->class->probe,						 call);	case TRACE_REG_UNREGISTER:		tracepoint_probe_unregister(call->name,					    call->class->probe,					    call);		return 0; #ifdef CONFIG_PERF_EVENTS	case TRACE_REG_PERF_REGISTER:		return tracepoint_probe_register(call->name,						 call->class->perf_probe,						 call);	case TRACE_REG_PERF_UNREGISTER:		tracepoint_probe_unregister(call->name,					    call->class->perf_probe,					    call);		return 0;	case TRACE_REG_PERF_OPEN:	case TRACE_REG_PERF_CLOSE:	case TRACE_REG_PERF_ADD:	case TRACE_REG_PERF_DEL:		return 0;#endif	}	return 0;}EXPORT_SYMBOL_GPL(ftrace_event_reg); void trace_event_enable_cmd_record(bool enable){	struct ftrace_event_call *call; 	mutex_lock(&event_mutex);	list_for_each_entry(call, &ftrace_events, list) {		if (!(call->flags & TRACE_EVENT_FL_ENABLED))			continue; 		if (enable) {			tracing_start_cmdline_record();			call->flags |= TRACE_EVENT_FL_RECORDED_CMD;		} else {			tracing_stop_cmdline_record();			call->flags &= ~TRACE_EVENT_FL_RECORDED_CMD;		}	}	mutex_unlock(&event_mutex);} static int ftrace_event_enable_disable(struct ftrace_event_call *call,					int enable){	int ret = 0; 	switch (enable) {	case 0:		if (call->flags & TRACE_EVENT_FL_ENABLED) {			call->flags &= ~TRACE_EVENT_FL_ENABLED;			if (call->flags & TRACE_EVENT_FL_RECORDED_CMD) {				tracing_stop_cmdline_record();				call->flags &= ~TRACE_EVENT_FL_RECORDED_CMD;			}			call->class->reg(call, TRACE_REG_UNREGISTER, NULL);		}		break;	case 1:		if (!(call->flags & TRACE_EVENT_FL_ENABLED)) {			if (trace_flags & TRACE_ITER_RECORD_CMD) {				tracing_start_cmdline_record();				call->flags |= TRACE_EVENT_FL_RECORDED_CMD;			}			ret = call->class->reg(call, TRACE_REG_REGISTER, NULL);			if (ret) {				tracing_stop_cmdline_record();				pr_info("event trace: Could not enable event "					"%s\n", call->name);				break;			}			call->flags |= TRACE_EVENT_FL_ENABLED;		}		break;	} 	return ret;} static void ftrace_clear_events(void){	struct ftrace_event_call *call; 	mutex_lock(&event_mutex);	list_for_each_entry(call, &ftrace_events, list) {		ftrace_event_enable_disable(call, 0);	}	mutex_unlock(&event_mutex);} static void __put_system(struct event_subsystem *system){	struct event_filter *filter = system->filter; 	WARN_ON_ONCE(system->ref_count == 0);	if (--system->ref_count)		return; 	if (filter) {		kfree(filter->filter_string);		kfree(filter);	}	kfree(system->name);	kfree(system);} static void __get_system(struct event_subsystem *system){	WARN_ON_ONCE(system->ref_count == 0);	system->ref_count++;} static void put_system(struct event_subsystem *system){	mutex_lock(&event_mutex);	__put_system(system);	mutex_unlock(&event_mutex);} /* * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. */static int __ftrace_set_clr_event(const char *match, const char *sub,				  const char *event, int set){	struct ftrace_event_call *call;	int ret = -EINVAL; 	mutex_lock(&event_mutex);	list_for_each_entry(call, &ftrace_events, list) { 		if (!call->name || !call->class || !call->class->reg)			continue; 		if (call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)			continue; 		if (match &&		    strcmp(match, call->name) != 0 &&		    strcmp(match, call->class->system) != 0)			continue; 		if (sub && strcmp(sub, call->class->system) != 0)			continue; 		if (event && strcmp(event, call->name) != 0)			continue; 		ftrace_event_enable_disable(call, set); 		ret = 0;	}	mutex_unlock(&event_mutex); 	return ret;} static int ftrace_set_clr_event(char *buf, int set){	char *event = NULL, *sub = NULL, *match; 	/*	 * The buf format can be <subsystem>:<event-name>	 *  *:<event-name> means any event by that name.	 *  :<event-name> is the same.	 *	 *  <subsystem>:* means all events in that subsystem	 *  <subsystem>: means the same.	 *	 *  <name> (no ':') means all events in a subsystem with	 *  the name <name> or any event that matches <name>	 */ 	match = strsep(&buf, ":");	if (buf) {		sub = match;		event = buf;		match = NULL; 		if (!strlen(sub) || strcmp(sub, "*") == 0)			sub = NULL;		if (!strlen(event) || strcmp(event, "*") == 0)			event = NULL;	} 	return __ftrace_set_clr_event(match, sub, event, set);} /** * trace_set_clr_event - enable or disable an event * @system: system name to match (NULL for any system) * @event: event name to match (NULL for all events, within system) * @set: 1 to enable, 0 to disable * * This is a way for other parts of the kernel to enable or disable * event recording. * * Returns 0 on success, -EINVAL if the parameters do not match any * registered events. */int trace_set_clr_event(const char *system, const char *event, int set){	return __ftrace_set_clr_event(NULL, system, event, set);}EXPORT_SYMBOL_GPL(trace_set_clr_event); /* 128 should be much more than enough */#define EVENT_BUF_SIZE		127 static ssize_tftrace_event_write(struct file *file, const char __user *ubuf,		   size_t cnt, loff_t *ppos){	struct trace_parser parser;	ssize_t read, ret; 	if (!cnt)		return 0; 	ret = tracing_update_buffers();	if (ret < 0)		return ret; 	if (trace_parser_get_init(&parser, EVENT_BUF_SIZE + 1))		return -ENOMEM; 	read = trace_get_user(&parser, ubuf, cnt, ppos); 	if (read >= 0 && trace_parser_loaded((&parser))) {		int set = 1; 		if (*parser.buffer == '!')			set = 0; 		parser.buffer[parser.idx] = 0; 		ret = ftrace_set_clr_event(parser.buffer + !set, set);		if (ret)			goto out_put;	} 	ret = read;  out_put:	trace_parser_put(&parser); 	return ret;} static void *t_next(struct seq_file *m, void *v, loff_t *pos){	struct ftrace_event_call *call = v; 	(*pos)++; 	list_for_each_entry_continue(call, &ftrace_events, list) {		/*		 * The ftrace subsystem is for showing formats only.		 * They can not be enabled or disabled via the event files.		 */		if (call->class && call->class->reg)			return call;	} 	return NULL;} static void *t_start(struct seq_file *m, loff_t *pos){	struct ftrace_event_call *call;	loff_t l; 	mutex_lock(&event_mutex); 	call = list_entry(&ftrace_events, struct ftrace_event_call, list);	for (l = 0; l <= *pos; ) {		call = t_next(m, call, &l);		if (!call)			break;	}	return call;} static void *s_next(struct seq_file *m, void *v, loff_t *pos){	struct ftrace_event_call *call = v; 	(*pos)++; 	list_for_each_entry_continue(call, &ftrace_events, list) {		if (call->flags & TRACE_EVENT_FL_ENABLED)			return call;	} 	return NULL;} static void *s_start(struct seq_file *m, loff_t *pos){	struct ftrace_event_call *call;	loff_t l; 	mutex_lock(&event_mutex); 	call = list_entry(&ftrace_events, struct ftrace_event_call, list);	for (l = 0; l <= *pos; ) {		call = s_next(m, call, &l);		if (!call)			break;	}	return call;} static int t_show(struct seq_file *m, void *v){	struct ftrace_event_call *call = v; 	if (strcmp(call->class->system, TRACE_SYSTEM) != 0)		seq_printf(m, "%s:", call->class->system);	seq_printf(m, "%s\n", call->name); 	return 0;} static void t_stop(struct seq_file *m, void *p){	mutex_unlock(&event_mutex);} static intftrace_event_seq_open(struct inode *inode, struct file *file){	const struct seq_operations *seq_ops; 	if ((file->f_mode & FMODE_WRITE) &&	    (file->f_flags & O_TRUNC))

Lines 1–500 of 1755.