3436 lines · 76.9 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>. * */ #define pr_fmt(fmt) fmt #include <linux/workqueue.h>#include <linux/spinlock.h>#include <linux/kthread.h>#include <linux/tracefs.h>#include <linux/uaccess.h>#include <linux/bsearch.h>#include <linux/module.h>#include <linux/ctype.h>#include <linux/sort.h>#include <linux/slab.h>#include <linux/delay.h> #include <trace/events/sched.h> #include <asm/setup.h> #include "trace_output.h" #undef TRACE_SYSTEM#define TRACE_SYSTEM "TRACE_SYSTEM" DEFINE_MUTEX(event_mutex); LIST_HEAD(ftrace_events);static LIST_HEAD(ftrace_generic_fields);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; static inline int system_refcount(struct event_subsystem *system){	return system->ref_count;} static int system_refcount_inc(struct event_subsystem *system){	return system->ref_count++;} static int system_refcount_dec(struct event_subsystem *system){	return --system->ref_count;} /* 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 trace_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 trace_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 trace_event_call *call, char *name){	struct ftrace_event_field *field;	struct list_head *head; 	field = __find_event_field(&ftrace_generic_fields, name);	if (field)		return field; 	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 trace_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 __generic_field(type, item, filter_type)			\	ret = __trace_define_field(&ftrace_generic_fields, #type,	\				   #item, 0, 0, is_signed_type(type),	\				   filter_type);			\	if (ret)							\		return ret; #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_generic_fields(void){	int ret; 	__generic_field(int, cpu, FILTER_OTHER);	__generic_field(char *, comm, FILTER_PTR_STRING); 	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 trace_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 trace_event_call *call){	int id; 	id = register_trace_event(&call->event);	if (!id)		return -ENODEV; 	return 0;}EXPORT_SYMBOL_GPL(trace_event_raw_init); bool trace_event_ignore_this_pid(struct trace_event_file *trace_file){	struct trace_array *tr = trace_file->tr;	struct trace_array_cpu *data;	struct trace_pid_list *pid_list; 	pid_list = rcu_dereference_sched(tr->filtered_pids);	if (!pid_list)		return false; 	data = this_cpu_ptr(tr->trace_buffer.data); 	return data->ignore_pid;}EXPORT_SYMBOL_GPL(trace_event_ignore_this_pid); void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,				 struct trace_event_file *trace_file,				 unsigned long len){	struct trace_event_call *event_call = trace_file->event_call; 	if ((trace_file->flags & EVENT_FILE_FL_PID_FILTER) &&	    trace_event_ignore_this_pid(trace_file))		return NULL; 	local_save_flags(fbuffer->flags);	fbuffer->pc = preempt_count();	fbuffer->trace_file = trace_file; 	fbuffer->event =		trace_event_buffer_lock_reserve(&fbuffer->buffer, trace_file,						event_call->event.type, len,						fbuffer->flags, fbuffer->pc);	if (!fbuffer->event)		return NULL; 	fbuffer->entry = ring_buffer_event_data(fbuffer->event);	return fbuffer->entry;}EXPORT_SYMBOL_GPL(trace_event_buffer_reserve); static DEFINE_SPINLOCK(tracepoint_iter_lock); static void output_printk(struct trace_event_buffer *fbuffer){	struct trace_event_call *event_call;	struct trace_event *event;	unsigned long flags;	struct trace_iterator *iter = tracepoint_print_iter; 	if (!iter)		return; 	event_call = fbuffer->trace_file->event_call;	if (!event_call || !event_call->event.funcs ||	    !event_call->event.funcs->trace)		return; 	event = &fbuffer->trace_file->event_call->event; 	spin_lock_irqsave(&tracepoint_iter_lock, flags);	trace_seq_init(&iter->seq);	iter->ent = fbuffer->entry;	event_call->event.funcs->trace(iter, 0, event);	trace_seq_putc(&iter->seq, 0);	printk("%s", iter->seq.buffer); 	spin_unlock_irqrestore(&tracepoint_iter_lock, flags);} void trace_event_buffer_commit(struct trace_event_buffer *fbuffer){	if (tracepoint_printk)		output_printk(fbuffer); 	event_trigger_unlock_commit(fbuffer->trace_file, fbuffer->buffer,				    fbuffer->event, fbuffer->entry,				    fbuffer->flags, fbuffer->pc);}EXPORT_SYMBOL_GPL(trace_event_buffer_commit); int trace_event_reg(struct trace_event_call *call,		    enum trace_reg type, void *data){	struct trace_event_file *file = data; 	WARN_ON(!(call->flags & TRACE_EVENT_FL_TRACEPOINT));	switch (type) {	case TRACE_REG_REGISTER:		return tracepoint_probe_register(call->tp,						 call->class->probe,						 file);	case TRACE_REG_UNREGISTER:		tracepoint_probe_unregister(call->tp,					    call->class->probe,					    file);		return 0; #ifdef CONFIG_PERF_EVENTS	case TRACE_REG_PERF_REGISTER:		return tracepoint_probe_register(call->tp,						 call->class->perf_probe,						 call);	case TRACE_REG_PERF_UNREGISTER:		tracepoint_probe_unregister(call->tp,					    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(trace_event_reg); void trace_event_enable_cmd_record(bool enable){	struct trace_event_file *file;	struct trace_array *tr; 	mutex_lock(&event_mutex);	do_for_each_event_file(tr, file) { 		if (!(file->flags & EVENT_FILE_FL_ENABLED))			continue; 		if (enable) {			tracing_start_cmdline_record();			set_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);		} else {			tracing_stop_cmdline_record();			clear_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);		}	} while_for_each_event_file();	mutex_unlock(&event_mutex);} static int __ftrace_event_enable_disable(struct trace_event_file *file,					 int enable, int soft_disable){	struct trace_event_call *call = file->event_call;	struct trace_array *tr = file->tr;	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 & EVENT_FILE_FL_SOFT_DISABLED;			clear_bit(EVENT_FILE_FL_SOFT_MODE_BIT, &file->flags);		} else			disable = !(file->flags & EVENT_FILE_FL_SOFT_MODE); 		if (disable && (file->flags & EVENT_FILE_FL_ENABLED)) {			clear_bit(EVENT_FILE_FL_ENABLED_BIT, &file->flags);			if (file->flags & EVENT_FILE_FL_RECORDED_CMD) {				tracing_stop_cmdline_record();				clear_bit(EVENT_FILE_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 & EVENT_FILE_FL_SOFT_MODE)			set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);		else			clear_bit(EVENT_FILE_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(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);		else {			if (atomic_inc_return(&file->sm_ref) > 1)				break;			set_bit(EVENT_FILE_FL_SOFT_MODE_BIT, &file->flags);		} 		if (!(file->flags & EVENT_FILE_FL_ENABLED)) { 			/* Keep the event disabled, when going to SOFT_MODE. */			if (soft_disable)				set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags); 			if (tr->trace_flags & TRACE_ITER_RECORD_CMD) {				tracing_start_cmdline_record();				set_bit(EVENT_FILE_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", trace_event_name(call));				break;			}			set_bit(EVENT_FILE_FL_ENABLED_BIT, &file->flags); 			/* WAS_ENABLED gets set but never cleared. */			call->flags |= TRACE_EVENT_FL_WAS_ENABLED;		}		break;	} 	return ret;} int trace_event_enable_disable(struct trace_event_file *file,			       int enable, int soft_disable){	return __ftrace_event_enable_disable(file, enable, soft_disable);} static int ftrace_event_enable_disable(struct trace_event_file *file,				       int enable){	return __ftrace_event_enable_disable(file, enable, 0);} static void ftrace_clear_events(struct trace_array *tr){	struct trace_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 int cmp_pid(const void *key, const void *elt){	const pid_t *search_pid = key;	const pid_t *pid = elt; 	if (*search_pid == *pid)		return 0;	if (*search_pid < *pid)		return -1;	return 1;} static boolcheck_ignore_pid(struct trace_pid_list *filtered_pids, struct task_struct *task){	pid_t search_pid;	pid_t *pid; 	/*	 * Return false, because if filtered_pids does not exist,	 * all pids are good to trace.	 */	if (!filtered_pids)		return false; 	search_pid = task->pid; 	pid = bsearch(&search_pid, filtered_pids->pids,		      filtered_pids->nr_pids, sizeof(pid_t),

Lines 1–500 of 3436.