/* * 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 ssize_tevent_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos){ struct ftrace_event_call *call = filp->private_data; char *buf; Lines 1–500 of 1806. Load more
/* * 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_t ftrace_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 ssize_t event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { struct ftrace_event_call *call = filp->private_data; char *buf;