2660 lines · 58.6 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);static LIST_HEAD(ftrace_common_fields); #define GFP_TRACE (GFP_KERNEL | __GFP_ZERO) static struct kmem_cache *field_cachep;static struct kmem_cache *file_cachep; #define SYSTEM_FL_FREE_NAME		(1 << 31) static inline int system_refcount(struct event_subsystem *system){	return system->ref_count & ~SYSTEM_FL_FREE_NAME;} static int system_refcount_inc(struct event_subsystem *system){	return (system->ref_count++) & ~SYSTEM_FL_FREE_NAME;} static int system_refcount_dec(struct event_subsystem *system){	return (--system->ref_count) & ~SYSTEM_FL_FREE_NAME;} /* Double loops, do not use break, only goto's work */#define do_for_each_event_file(tr, file)			\	list_for_each_entry(tr, &ftrace_trace_arrays, list) {	\		list_for_each_entry(file, &tr->events, list) #define do_for_each_event_file_safe(tr, file)			\	list_for_each_entry(tr, &ftrace_trace_arrays, list) {	\		struct ftrace_event_file *___n;				\		list_for_each_entry_safe(file, ___n, &tr->events, list) #define while_for_each_event_file()		\	} static 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 struct ftrace_event_field *__find_event_field(struct list_head *head, char *name){	struct ftrace_event_field *field; 	list_for_each_entry(field, head, link) {		if (!strcmp(field->name, name))			return field;	} 	return NULL;} struct ftrace_event_field *trace_find_event_field(struct ftrace_event_call *call, char *name){	struct ftrace_event_field *field;	struct list_head *head; 	field = __find_event_field(&ftrace_common_fields, name);	if (field)		return field; 	head = trace_get_fields(call);	return __find_event_field(head, name);} 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 = kmem_cache_alloc(field_cachep, GFP_TRACE);	if (!field)		return -ENOMEM; 	field->name = name;	field->type = type; 	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;} 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); 	return ret;} static 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);		kmem_cache_free(field_cachep, 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){	struct ftrace_event_file *file = data; 	switch (type) {	case TRACE_REG_REGISTER:		return tracepoint_probe_register(call->name,						 call->class->probe,						 file);	case TRACE_REG_UNREGISTER:		tracepoint_probe_unregister(call->name,					    call->class->probe,					    file);		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_file *file;	struct trace_array *tr; 	mutex_lock(&event_mutex);	do_for_each_event_file(tr, file) { 		if (!(file->flags & FTRACE_EVENT_FL_ENABLED))			continue; 		if (enable) {			tracing_start_cmdline_record();			set_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags);		} else {			tracing_stop_cmdline_record();			clear_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags);		}	} while_for_each_event_file();	mutex_unlock(&event_mutex);} static int __ftrace_event_enable_disable(struct ftrace_event_file *file,					 int enable, int soft_disable){	struct ftrace_event_call *call = file->event_call;	int ret = 0;	int disable; 	switch (enable) {	case 0:		/*		 * When soft_disable is set and enable is cleared, the sm_ref		 * reference counter is decremented. If it reaches 0, we want		 * to clear the SOFT_DISABLED flag but leave the event in the		 * state that it was. That is, if the event was enabled and		 * SOFT_DISABLED isn't set, then do nothing. But if SOFT_DISABLED		 * is set we do not want the event to be enabled before we		 * clear the bit.		 *		 * When soft_disable is not set but the SOFT_MODE flag is,		 * we do nothing. Do not disable the tracepoint, otherwise		 * "soft enable"s (clearing the SOFT_DISABLED bit) wont work.		 */		if (soft_disable) {			if (atomic_dec_return(&file->sm_ref) > 0)				break;			disable = file->flags & FTRACE_EVENT_FL_SOFT_DISABLED;			clear_bit(FTRACE_EVENT_FL_SOFT_MODE_BIT, &file->flags);		} else			disable = !(file->flags & FTRACE_EVENT_FL_SOFT_MODE); 		if (disable && (file->flags & FTRACE_EVENT_FL_ENABLED)) {			clear_bit(FTRACE_EVENT_FL_ENABLED_BIT, &file->flags);			if (file->flags & FTRACE_EVENT_FL_RECORDED_CMD) {				tracing_stop_cmdline_record();				clear_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags);			}			call->class->reg(call, TRACE_REG_UNREGISTER, file);		}		/* If in SOFT_MODE, just set the SOFT_DISABLE_BIT, else clear it */		if (file->flags & FTRACE_EVENT_FL_SOFT_MODE)			set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags);		else			clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags);		break;	case 1:		/*		 * When soft_disable is set and enable is set, we want to		 * register the tracepoint for the event, but leave the event		 * as is. That means, if the event was already enabled, we do		 * nothing (but set SOFT_MODE). If the event is disabled, we		 * set SOFT_DISABLED before enabling the event tracepoint, so		 * it still seems to be disabled.		 */		if (!soft_disable)			clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags);		else {			if (atomic_inc_return(&file->sm_ref) > 1)				break;			set_bit(FTRACE_EVENT_FL_SOFT_MODE_BIT, &file->flags);		} 		if (!(file->flags & FTRACE_EVENT_FL_ENABLED)) { 			/* Keep the event disabled, when going to SOFT_MODE. */			if (soft_disable)				set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); 			if (trace_flags & TRACE_ITER_RECORD_CMD) {				tracing_start_cmdline_record();				set_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags);			}			ret = call->class->reg(call, TRACE_REG_REGISTER, file);			if (ret) {				tracing_stop_cmdline_record();				pr_info("event trace: Could not enable event "					"%s\n", call->name);				break;			}			set_bit(FTRACE_EVENT_FL_ENABLED_BIT, &file->flags); 			/* WAS_ENABLED gets set but never cleared. */			call->flags |= TRACE_EVENT_FL_WAS_ENABLED;		}		break;	} 	return ret;} static int ftrace_event_enable_disable(struct ftrace_event_file *file,				       int enable){	return __ftrace_event_enable_disable(file, enable, 0);} static void ftrace_clear_events(struct trace_array *tr){	struct ftrace_event_file *file; 	mutex_lock(&event_mutex);	list_for_each_entry(file, &tr->events, list) {		ftrace_event_enable_disable(file, 0);	}	mutex_unlock(&event_mutex);} static void __put_system(struct event_subsystem *system){	struct event_filter *filter = system->filter; 	WARN_ON_ONCE(system_refcount(system) == 0);	if (system_refcount_dec(system))		return; 	list_del(&system->list); 	if (filter) {		kfree(filter->filter_string);		kfree(filter);	}	if (system->ref_count & SYSTEM_FL_FREE_NAME)		kfree(system->name);	kfree(system);} static void __get_system(struct event_subsystem *system){	WARN_ON_ONCE(system_refcount(system) == 0);	system_refcount_inc(system);} static void __get_system_dir(struct ftrace_subsystem_dir *dir){	WARN_ON_ONCE(dir->ref_count == 0);	dir->ref_count++;	__get_system(dir->subsystem);} static void __put_system_dir(struct ftrace_subsystem_dir *dir){	WARN_ON_ONCE(dir->ref_count == 0);	/* If the subsystem is about to be freed, the dir must be too */	WARN_ON_ONCE(system_refcount(dir->subsystem) == 1 && dir->ref_count != 1); 	__put_system(dir->subsystem);	if (!--dir->ref_count)		kfree(dir);} static void put_system(struct ftrace_subsystem_dir *dir){	mutex_lock(&event_mutex);	__put_system_dir(dir);	mutex_unlock(&event_mutex);} static void remove_subsystem(struct ftrace_subsystem_dir *dir){	if (!dir)		return; 	if (!--dir->nr_events) {		debugfs_remove_recursive(dir->entry);		list_del(&dir->list);		__put_system_dir(dir);	}} static void *event_file_data(struct file *filp){	return ACCESS_ONCE(file_inode(filp)->i_private);} static void remove_event_file_dir(struct ftrace_event_file *file){	struct dentry *dir = file->dir;	struct dentry *child; 	if (dir) {		spin_lock(&dir->d_lock);	/* probably unneeded */		list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) {			if (child->d_inode)	/* probably unneeded */				child->d_inode->i_private = NULL;		}		spin_unlock(&dir->d_lock); 		debugfs_remove_recursive(dir);	} 	list_del(&file->list);	remove_subsystem(file->system);	kmem_cache_free(file_cachep, file);} /* * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. */static int__ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,			      const char *sub, const char *event, int set){	struct ftrace_event_file *file;	struct ftrace_event_call *call;	int ret = -EINVAL; 	list_for_each_entry(file, &tr->events, list) { 		call = file->event_call; 		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(file, set); 		ret = 0;	} 	return ret;} static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,				  const char *sub, const char *event, int set){	int ret; 	mutex_lock(&event_mutex);	ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set);	mutex_unlock(&event_mutex); 	return ret;}

Lines 1–500 of 2660.