Commit acdb2c28 authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'tip/tracing/ftrace' of...

Merge branch 'tip/tracing/ftrace' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into tracing/ftrace
parents e9abf4c5 f2034f1e
...@@ -157,4 +157,7 @@ static inline void tracepoint_synchronize_unregister(void) ...@@ -157,4 +157,7 @@ static inline void tracepoint_synchronize_unregister(void)
#define TRACE_FORMAT(name, proto, args, fmt) \ #define TRACE_FORMAT(name, proto, args, fmt) \
DECLARE_TRACE(name, PARAMS(proto), PARAMS(args)) DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))
#define TRACE_EVENT_FORMAT(name, proto, args, fmt, struct, tpfmt) \
TRACE_FORMAT(name, PARAMS(proto), PARAMS(args), PARAMS(fmt))
#endif #endif
...@@ -5,13 +5,29 @@ ...@@ -5,13 +5,29 @@
# error Unless you know what you are doing. # error Unless you know what you are doing.
#endif #endif
TRACE_FORMAT(irq_handler_entry, #undef TRACE_SYSTEM
#define TRACE_SYSTEM irq
TRACE_EVENT_FORMAT(irq_handler_entry,
TPPROTO(int irq, struct irqaction *action), TPPROTO(int irq, struct irqaction *action),
TPARGS(irq, action), TPARGS(irq, action),
TPFMT("irq=%d handler=%s", irq, action->name)); TPFMT("irq=%d handler=%s", irq, action->name),
TRACE_STRUCT(
TRACE_FIELD(int, irq, irq)
),
TPRAWFMT("irq %d")
);
TRACE_FORMAT(irq_handler_exit, TRACE_EVENT_FORMAT(irq_handler_exit,
TPPROTO(int irq, struct irqaction *action, int ret), TPPROTO(int irq, struct irqaction *action, int ret),
TPARGS(irq, action, ret), TPARGS(irq, action, ret),
TPFMT("irq=%d handler=%s return=%s", TPFMT("irq=%d handler=%s return=%s",
irq, action->name, ret ? "handled" : "unhandled")); irq, action->name, ret ? "handled" : "unhandled"),
TRACE_STRUCT(
TRACE_FIELD(int, irq, irq)
TRACE_FIELD(int, ret, ret)
),
TPRAWFMT("irq %d ret %d")
);
#undef TRACE_SYSTEM
/* use <trace/sched.h> instead */ /* use <trace/sched.h> instead */
#ifndef TRACE_FORMAT #ifndef TRACE_EVENT_FORMAT
# error Do not include this file directly. # error Do not include this file directly.
# error Unless you know what you are doing. # error Unless you know what you are doing.
#endif #endif
TRACE_FORMAT(sched_kthread_stop, #undef TRACE_SYSTEM
#define TRACE_SYSTEM sched
TRACE_EVENT_FORMAT(sched_kthread_stop,
TPPROTO(struct task_struct *t), TPPROTO(struct task_struct *t),
TPARGS(t), TPARGS(t),
TPFMT("task %s:%d", t->comm, t->pid)); TPFMT("task %s:%d", t->comm, t->pid),
TRACE_STRUCT(
TRACE_FIELD(pid_t, pid, t->pid)
),
TPRAWFMT("task %d")
);
TRACE_FORMAT(sched_kthread_stop_ret, TRACE_EVENT_FORMAT(sched_kthread_stop_ret,
TPPROTO(int ret), TPPROTO(int ret),
TPARGS(ret), TPARGS(ret),
TPFMT("ret=%d", ret)); TPFMT("ret=%d", ret),
TRACE_STRUCT(
TRACE_FIELD(int, ret, ret)
),
TPRAWFMT("ret=%d")
);
TRACE_FORMAT(sched_wait_task, TRACE_EVENT_FORMAT(sched_wait_task,
TPPROTO(struct rq *rq, struct task_struct *p), TPPROTO(struct rq *rq, struct task_struct *p),
TPARGS(rq, p), TPARGS(rq, p),
TPFMT("task %s:%d", p->comm, p->pid)); TPFMT("task %s:%d", p->comm, p->pid),
TRACE_STRUCT(
TRACE_FIELD(pid_t, pid, p->pid)
),
TPRAWFMT("task %d")
);
TRACE_FORMAT(sched_wakeup, TRACE_EVENT_FORMAT(sched_wakeup,
TPPROTO(struct rq *rq, struct task_struct *p, int success), TPPROTO(struct rq *rq, struct task_struct *p, int success),
TPARGS(rq, p, success), TPARGS(rq, p, success),
TPFMT("task %s:%d %s", TPFMT("task %s:%d %s",
p->comm, p->pid, success?"succeeded":"failed")); p->comm, p->pid, success ? "succeeded" : "failed"),
TRACE_STRUCT(
TRACE_FIELD(pid_t, pid, p->pid)
TRACE_FIELD(int, success, success)
),
TPRAWFMT("task %d success=%d")
);
TRACE_FORMAT(sched_wakeup_new, TRACE_EVENT_FORMAT(sched_wakeup_new,
TPPROTO(struct rq *rq, struct task_struct *p, int success), TPPROTO(struct rq *rq, struct task_struct *p, int success),
TPARGS(rq, p, success), TPARGS(rq, p, success),
TPFMT("task %s:%d", TPFMT("task %s:%d",
p->comm, p->pid, success?"succeeded":"failed")); p->comm, p->pid, success ? "succeeded" : "failed"),
TRACE_STRUCT(
TRACE_FIELD(pid_t, pid, p->pid)
TRACE_FIELD(int, success, success)
),
TPRAWFMT("task %d success=%d")
);
TRACE_FORMAT(sched_switch, TRACE_EVENT_FORMAT(sched_switch,
TPPROTO(struct rq *rq, struct task_struct *prev, TPPROTO(struct rq *rq, struct task_struct *prev,
struct task_struct *next), struct task_struct *next),
TPARGS(rq, prev, next), TPARGS(rq, prev, next),
TPFMT("task %s:%d ==> %s:%d", TPFMT("task %s:%d ==> %s:%d",
prev->comm, prev->pid, next->comm, next->pid)); prev->comm, prev->pid, next->comm, next->pid),
TRACE_STRUCT(
TRACE_FIELD(pid_t, prev_pid, prev->pid)
TRACE_FIELD(int, prev_prio, prev->prio)
TRACE_FIELD(pid_t, next_pid, next->pid)
TRACE_FIELD(int, next_prio, next->prio)
),
TPRAWFMT("prev %d:%d ==> next %d:%d")
);
TRACE_FORMAT(sched_migrate_task, TRACE_EVENT_FORMAT(sched_migrate_task,
TPPROTO(struct task_struct *p, int orig_cpu, int dest_cpu), TPPROTO(struct task_struct *p, int orig_cpu, int dest_cpu),
TPARGS(p, orig_cpu, dest_cpu), TPARGS(p, orig_cpu, dest_cpu),
TPFMT("task %s:%d from: %d to: %d", TPFMT("task %s:%d from: %d to: %d",
p->comm, p->pid, orig_cpu, dest_cpu)); p->comm, p->pid, orig_cpu, dest_cpu),
TRACE_STRUCT(
TRACE_FIELD(pid_t, pid, p->pid)
TRACE_FIELD(int, orig_cpu, orig_cpu)
TRACE_FIELD(int, dest_cpu, dest_cpu)
),
TPRAWFMT("task %d from: %d to: %d")
);
TRACE_FORMAT(sched_process_free, TRACE_EVENT_FORMAT(sched_process_free,
TPPROTO(struct task_struct *p), TPPROTO(struct task_struct *p),
TPARGS(p), TPARGS(p),
TPFMT("task %s:%d", p->comm, p->pid)); TPFMT("task %s:%d", p->comm, p->pid),
TRACE_STRUCT(
TRACE_FIELD(pid_t, pid, p->pid)
),
TPRAWFMT("task %d")
);
TRACE_FORMAT(sched_process_exit, TRACE_EVENT_FORMAT(sched_process_exit,
TPPROTO(struct task_struct *p), TPPROTO(struct task_struct *p),
TPARGS(p), TPARGS(p),
TPFMT("task %s:%d", p->comm, p->pid)); TPFMT("task %s:%d", p->comm, p->pid),
TRACE_STRUCT(
TRACE_FIELD(pid_t, pid, p->pid)
),
TPRAWFMT("task %d")
);
TRACE_FORMAT(sched_process_wait, TRACE_EVENT_FORMAT(sched_process_wait,
TPPROTO(struct pid *pid), TPPROTO(struct pid *pid),
TPARGS(pid), TPARGS(pid),
TPFMT("pid %d", pid)); TPFMT("pid %d", pid_nr(pid)),
TRACE_STRUCT(
TRACE_FIELD(pid_t, pid, pid_nr(pid))
),
TPRAWFMT("task %d")
);
TRACE_FORMAT(sched_process_fork, TRACE_EVENT_FORMAT(sched_process_fork,
TPPROTO(struct task_struct *parent, struct task_struct *child), TPPROTO(struct task_struct *parent, struct task_struct *child),
TPARGS(parent, child), TPARGS(parent, child),
TPFMT("parent %s:%d child %s:%d", TPFMT("parent %s:%d child %s:%d",
parent->comm, parent->pid, child->comm, child->pid)); parent->comm, parent->pid, child->comm, child->pid),
TRACE_STRUCT(
TRACE_FIELD(pid_t, parent, parent->pid)
TRACE_FIELD(pid_t, child, child->pid)
),
TPRAWFMT("parent %d child %d")
);
TRACE_FORMAT(sched_signal_send, TRACE_EVENT_FORMAT(sched_signal_send,
TPPROTO(int sig, struct task_struct *p), TPPROTO(int sig, struct task_struct *p),
TPARGS(sig, p), TPARGS(sig, p),
TPFMT("sig: %d task %s:%d", sig, p->comm, p->pid)); TPFMT("sig: %d task %s:%d", sig, p->comm, p->pid),
TRACE_STRUCT(
TRACE_FIELD(int, sig, sig)
TRACE_FIELD(pid_t, pid, p->pid)
),
TPRAWFMT("sig: %d task %d")
);
#undef TRACE_SYSTEM
/* trace/<type>_event_types.h here */
#include <trace/sched_event_types.h>
#include <trace/irq_event_types.h>
/* trace/<type>.h here */
#include <trace/sched.h>
#include <trace/irq.h>
/* /*
* This is the place to register all trace points as events. * This is the place to register all trace points as events.
* Include the trace/<type>.h at the top.
* Include the trace/<type>_event_types.h at the bottom.
*/ */
/* trace/<type>.h here */ /* someday this needs to go in a generic header */
#include <trace/sched.h> #define __STR(x) #x
#include <trace/irq.h> #define STR(x) __STR(x)
#include "trace_events.h" #include <trace/trace_events.h>
/* trace/<type>_event_types.h here */ #include "trace_output.h"
#include <trace/sched_event_types.h>
#include <trace/irq_event_types.h> #include "trace_events_stage_1.h"
#include "trace_events_stage_2.h"
#include "trace_events_stage_3.h"
#include <trace/trace_event_types.h>
...@@ -846,6 +846,20 @@ void trace_buffer_unlock_commit(struct trace_array *tr, ...@@ -846,6 +846,20 @@ void trace_buffer_unlock_commit(struct trace_array *tr,
trace_wake_up(); trace_wake_up();
} }
struct ring_buffer_event *
trace_current_buffer_lock_reserve(unsigned char type, unsigned long len,
unsigned long flags, int pc)
{
return trace_buffer_lock_reserve(&global_trace,
type, len, flags, pc);
}
void trace_current_buffer_unlock_commit(struct ring_buffer_event *event,
unsigned long flags, int pc)
{
return trace_buffer_unlock_commit(&global_trace, event, flags, pc);
}
void void
trace_function(struct trace_array *tr, trace_function(struct trace_array *tr,
unsigned long ip, unsigned long parent_ip, unsigned long flags, unsigned long ip, unsigned long parent_ip, unsigned long flags,
......
...@@ -442,6 +442,12 @@ void trace_buffer_unlock_commit(struct trace_array *tr, ...@@ -442,6 +442,12 @@ void trace_buffer_unlock_commit(struct trace_array *tr,
struct ring_buffer_event *event, struct ring_buffer_event *event,
unsigned long flags, int pc); unsigned long flags, int pc);
struct ring_buffer_event *
trace_current_buffer_lock_reserve(unsigned char type, unsigned long len,
unsigned long flags, int pc);
void trace_current_buffer_unlock_commit(struct ring_buffer_event *event,
unsigned long flags, int pc);
struct trace_entry *tracing_get_trace_entry(struct trace_array *tr, struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
struct trace_array_cpu *data); struct trace_array_cpu *data);
...@@ -720,4 +726,30 @@ static inline void trace_branch_disable(void) ...@@ -720,4 +726,30 @@ static inline void trace_branch_disable(void)
} }
#endif /* CONFIG_BRANCH_TRACER */ #endif /* CONFIG_BRANCH_TRACER */
/* trace event type bit fields, not numeric */
enum {
TRACE_EVENT_TYPE_PRINTF = 1,
TRACE_EVENT_TYPE_RAW = 2,
};
struct ftrace_event_call {
char *name;
char *system;
struct dentry *dir;
int enabled;
int (*regfunc)(void);
void (*unregfunc)(void);
int id;
struct dentry *raw_dir;
int raw_enabled;
int type;
int (*raw_init)(void);
int (*raw_reg)(void);
void (*raw_unreg)(void);
};
void event_trace_printk(unsigned long ip, const char *fmt, ...);
extern struct ftrace_event_call __start_ftrace_events[];
extern struct ftrace_event_call __stop_ftrace_events[];
#endif /* _LINUX_KERNEL_TRACE_H */ #endif /* _LINUX_KERNEL_TRACE_H */
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include "trace_events.h" #include "trace.h"
#define TRACE_SYSTEM "TRACE_SYSTEM"
#define events_for_each(event) \ #define events_for_each(event) \
for (event = __start_ftrace_events; \ for (event = __start_ftrace_events; \
...@@ -42,35 +44,87 @@ static void ftrace_clear_events(void) ...@@ -42,35 +44,87 @@ static void ftrace_clear_events(void)
} }
} }
static void ftrace_event_enable_disable(struct ftrace_event_call *call,
int enable)
{
switch (enable) {
case 0:
if (call->enabled) {
call->enabled = 0;
call->unregfunc();
}
if (call->raw_enabled) {
call->raw_enabled = 0;
call->raw_unreg();
}
break;
case 1:
if (!call->enabled &&
(call->type & TRACE_EVENT_TYPE_PRINTF)) {
call->enabled = 1;
call->regfunc();
}
if (!call->raw_enabled &&
(call->type & TRACE_EVENT_TYPE_RAW)) {
call->raw_enabled = 1;
call->raw_reg();
}
break;
}
}
static int ftrace_set_clr_event(char *buf, int set) static int ftrace_set_clr_event(char *buf, int set)
{ {
struct ftrace_event_call *call = __start_ftrace_events; struct ftrace_event_call *call = __start_ftrace_events;
char *event = NULL, *sub = NULL, *match;
int ret = -EINVAL;
/*
* 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;
}
events_for_each(call) { events_for_each(call) {
if (!call->name) if (!call->name)
continue; continue;
if (strcmp(buf, call->name) != 0) if (match &&
strcmp(match, call->name) != 0 &&
strcmp(match, call->system) != 0)
continue; continue;
if (set) { if (sub && strcmp(sub, call->system) != 0)
/* Already set? */ continue;
if (call->enabled)
return 0; if (event && strcmp(event, call->name) != 0)
call->enabled = 1; continue;
call->regfunc();
} else { ftrace_event_enable_disable(call, set);
/* Already cleared? */
if (!call->enabled) ret = 0;
return 0;
call->enabled = 0;
call->unregfunc();
}
return 0;
} }
return -EINVAL; return ret;
} }
/* 128 should be much more than enough */ /* 128 should be much more than enough */
...@@ -200,6 +254,8 @@ static int t_show(struct seq_file *m, void *v) ...@@ -200,6 +254,8 @@ static int t_show(struct seq_file *m, void *v)
{ {
struct ftrace_event_call *call = v; struct ftrace_event_call *call = v;
if (strcmp(call->system, TRACE_SYSTEM) != 0)
seq_printf(m, "%s:", call->system);
seq_printf(m, "%s\n", call->name); seq_printf(m, "%s\n", call->name);
return 0; return 0;
...@@ -236,7 +292,7 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, ...@@ -236,7 +292,7 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
struct ftrace_event_call *call = filp->private_data; struct ftrace_event_call *call = filp->private_data;
char *buf; char *buf;
if (call->enabled) if (call->enabled || call->raw_enabled)
buf = "1\n"; buf = "1\n";
else else
buf = "0\n"; buf = "0\n";
...@@ -267,29 +323,120 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, ...@@ -267,29 +323,120 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
switch (val) { switch (val) {
case 0: case 0:
if (!call->enabled)
break;
call->enabled = 0;
call->unregfunc();
break;
case 1: case 1:
if (call->enabled) ftrace_event_enable_disable(call, val);
break; break;
default:
return -EINVAL;
}
*ppos += cnt;
return cnt;
}
static ssize_t
event_type_read(struct file *filp, char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct ftrace_event_call *call = filp->private_data;
char buf[16];
int r = 0;
if (call->type & TRACE_EVENT_TYPE_PRINTF)
r += sprintf(buf, "printf\n");
if (call->type & TRACE_EVENT_TYPE_RAW)
r += sprintf(buf+r, "raw\n");
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
static ssize_t
event_type_write(struct file *filp, const char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct ftrace_event_call *call = filp->private_data;
char buf[64];
/*
* If there's only one type, we can't change it.
* And currently we always have printf type, and we
* may or may not have raw type.
*
* This is a redundant check, the file should be read
* only if this is the case anyway.
*/
if (!call->raw_init)
return -EPERM;
if (cnt >= sizeof(buf))
return -EINVAL;
if (copy_from_user(&buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = 0;
if (!strncmp(buf, "printf", 6) &&
(!buf[6] || isspace(buf[6]))) {
call->type = TRACE_EVENT_TYPE_PRINTF;
/*
* If raw enabled, the disable it and enable
* printf type.
*/
if (call->raw_enabled) {
call->raw_enabled = 0;
call->raw_unreg();
call->enabled = 1; call->enabled = 1;
call->regfunc(); call->regfunc();
break; }
default: } else if (!strncmp(buf, "raw", 3) &&
return -EINVAL; (!buf[3] || isspace(buf[3]))) {
call->type = TRACE_EVENT_TYPE_RAW;
/*
* If printf enabled, the disable it and enable
* raw type.
*/
if (call->enabled) {
call->enabled = 0;
call->unregfunc();
call->raw_enabled = 1;
call->raw_reg();
} }
} else
return -EINVAL;
*ppos += cnt; *ppos += cnt;
return cnt; return cnt;
} }
static ssize_t
event_available_types_read(struct file *filp, char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct ftrace_event_call *call = filp->private_data;
char buf[16];
int r = 0;
r += sprintf(buf, "printf\n");
if (call->raw_init)
r += sprintf(buf+r, "raw\n");
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
static const struct seq_operations show_event_seq_ops = { static const struct seq_operations show_event_seq_ops = {
.start = t_start, .start = t_start,
.next = t_next, .next = t_next,
...@@ -325,6 +472,17 @@ static const struct file_operations ftrace_enable_fops = { ...@@ -325,6 +472,17 @@ static const struct file_operations ftrace_enable_fops = {
.write = event_enable_write, .write = event_enable_write,
}; };
static const struct file_operations ftrace_type_fops = {
.open = tracing_open_generic,
.read = event_type_read,
.write = event_type_write,
};
static const struct file_operations ftrace_available_types_fops = {
.open = tracing_open_generic,
.read = event_available_types_read,
};
static struct dentry *event_trace_events_dir(void) static struct dentry *event_trace_events_dir(void)
{ {
static struct dentry *d_tracer; static struct dentry *d_tracer;
...@@ -345,10 +503,71 @@ static struct dentry *event_trace_events_dir(void) ...@@ -345,10 +503,71 @@ static struct dentry *event_trace_events_dir(void)
return d_events; return d_events;
} }
struct event_subsystem {
struct list_head list;
const char *name;
struct dentry *entry;
};
static LIST_HEAD(event_subsystems);
static struct dentry *
event_subsystem_dir(const char *name, struct dentry *d_events)
{
struct event_subsystem *system;
/* First see if we did not already create this dir */
list_for_each_entry(system, &event_subsystems, list) {
if (strcmp(system->name, name) == 0)
return system->entry;
}
/* need to create new entry */
system = kmalloc(sizeof(*system), GFP_KERNEL);
if (!system) {
pr_warning("No memory to create event subsystem %s\n",
name);
return d_events;
}
system->entry = debugfs_create_dir(name, d_events);
if (!system->entry) {
pr_warning("Could not create event subsystem %s\n",
name);
kfree(system);
return d_events;
}
system->name = name;
list_add(&system->list, &event_subsystems);
return system->entry;
}
static int static int
event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
{ {
struct dentry *entry; struct dentry *entry;
int ret;
/*
* If the trace point header did not define TRACE_SYSTEM
* then the system would be called "TRACE_SYSTEM".
*/
if (strcmp(call->system, "TRACE_SYSTEM") != 0)
d_events = event_subsystem_dir(call->system, d_events);
if (call->raw_init) {
ret = call->raw_init();
if (ret < 0) {
pr_warning("Could not initialize trace point"
" events/%s\n", call->name);
return ret;
}
}
/* default the output to printf */
call->type = TRACE_EVENT_TYPE_PRINTF;
call->dir = debugfs_create_dir(call->name, d_events); call->dir = debugfs_create_dir(call->name, d_events);
if (!call->dir) { if (!call->dir) {
...@@ -363,6 +582,21 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) ...@@ -363,6 +582,21 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
pr_warning("Could not create debugfs " pr_warning("Could not create debugfs "
"'%s/enable' entry\n", call->name); "'%s/enable' entry\n", call->name);
/* Only let type be writable, if we can change it */
entry = debugfs_create_file("type",
call->raw_init ? 0644 : 0444,
call->dir, call,
&ftrace_type_fops);
if (!entry)
pr_warning("Could not create debugfs "
"'%s/type' entry\n", call->name);
entry = debugfs_create_file("available_types", 0444, call->dir, call,
&ftrace_available_types_fops);
if (!entry)
pr_warning("Could not create debugfs "
"'%s/type' available_types\n", call->name);
return 0; return 0;
} }
......
#ifndef _LINUX_KERNEL_TRACE_EVENTS_H
#define _LINUX_KERNEL_TRACE_EVENTS_H
#include <linux/debugfs.h>
#include <linux/ftrace.h>
#include "trace.h"
struct ftrace_event_call {
char *name;
struct dentry *dir;
int enabled;
int (*regfunc)(void);
void (*unregfunc)(void);
};
#undef TPFMT
#define TPFMT(fmt, args...) fmt "\n", ##args
#undef TRACE_FORMAT
#define TRACE_FORMAT(call, proto, args, fmt) \
static void ftrace_event_##call(proto) \
{ \
event_trace_printk(_RET_IP_, "(" #call ") " fmt); \
} \
\
static int ftrace_reg_event_##call(void) \
{ \
int ret; \
\
ret = register_trace_##call(ftrace_event_##call); \
if (!ret) \
pr_info("event trace: Could not activate trace point " \
"probe to " #call); \
return ret; \
} \
\
static void ftrace_unreg_event_##call(void) \
{ \
unregister_trace_##call(ftrace_event_##call); \
} \
\
static struct ftrace_event_call __used \
__attribute__((__aligned__(4))) \
__attribute__((section("_ftrace_events"))) event_##call = { \
.name = #call, \
.regfunc = ftrace_reg_event_##call, \
.unregfunc = ftrace_unreg_event_##call, \
}
void event_trace_printk(unsigned long ip, const char *fmt, ...);
extern struct ftrace_event_call __start_ftrace_events[];
extern struct ftrace_event_call __stop_ftrace_events[];
#endif /* _LINUX_KERNEL_TRACE_EVENTS_H */
/*
* Stage 1 of the trace events.
*
* Override the macros in <trace/trace_event_types.h> to include the following:
*
* struct ftrace_raw_<call> {
* struct trace_entry ent;
* <type> <item>;
* [...]
* };
*
* The <type> <item> is created by the TRACE_FIELD(type, item, assign)
* macro. We simply do "type item;", and that will create the fields
* in the structure.
*/
#undef TRACE_FORMAT
#define TRACE_FORMAT(call, proto, args, fmt)
#undef TRACE_EVENT_FORMAT
#define TRACE_EVENT_FORMAT(name, proto, args, fmt, tstruct, tpfmt) \
struct ftrace_raw_##name { \
struct trace_entry ent; \
tstruct \
}; \
static struct ftrace_event_call event_##name
#undef TRACE_STRUCT
#define TRACE_STRUCT(args...) args
#define TRACE_FIELD(type, item, assign) \
type item;
#include <trace/trace_event_types.h>
/*
* Stage 2 of the trace events.
*
* Override the macros in <trace/trace_event_types.h> to include the following:
*
* enum print_line_t
* ftrace_raw_output_<call>(struct trace_iterator *iter, int flags)
* {
* struct trace_seq *s = &iter->seq;
* struct ftrace_raw_<call> *field; <-- defined in stage 1
* struct trace_entry *entry;
* int ret;
*
* entry = iter->ent;
*
* if (entry->type != event_<call>.id) {
* WARN_ON_ONCE(1);
* return TRACE_TYPE_UNHANDLED;
* }
*
* field = (typeof(field))entry;
*
* ret = trace_seq_printf(s, <TPRAWFMT> "%s", <ARGS> "\n");
* if (!ret)
* return TRACE_TYPE_PARTIAL_LINE;
*
* return TRACE_TYPE_HANDLED;
* }
*
* This is the method used to print the raw event to the trace
* output format. Note, this is not needed if the data is read
* in binary.
*/
#undef TRACE_STRUCT
#define TRACE_STRUCT(args...) args
#undef TRACE_FIELD
#define TRACE_FIELD(type, item, assign) \
field->item,
#undef TPRAWFMT
#define TPRAWFMT(args...) args
#undef TRACE_EVENT_FORMAT
#define TRACE_EVENT_FORMAT(call, proto, args, fmt, tstruct, tpfmt) \
enum print_line_t \
ftrace_raw_output_##call(struct trace_iterator *iter, int flags) \
{ \
struct trace_seq *s = &iter->seq; \
struct ftrace_raw_##call *field; \
struct trace_entry *entry; \
int ret; \
\
entry = iter->ent; \
\
if (entry->type != event_##call.id) { \
WARN_ON_ONCE(1); \
return TRACE_TYPE_UNHANDLED; \
} \
\
field = (typeof(field))entry; \
\
ret = trace_seq_printf(s, tpfmt "%s", tstruct "\n"); \
if (!ret) \
return TRACE_TYPE_PARTIAL_LINE; \
\
return TRACE_TYPE_HANDLED; \
}
#include <trace/trace_event_types.h>
/*
* Stage 3 of the trace events.
*
* Override the macros in <trace/trace_event_types.h> to include the following:
*
* static void ftrace_event_<call>(proto)
* {
* event_trace_printk(_RET_IP_, "(<call>) " <fmt>);
* }
*
* static int ftrace_reg_event_<call>(void)
* {
* int ret;
*
* ret = register_trace_<call>(ftrace_event_<call>);
* if (!ret)
* pr_info("event trace: Could not activate trace point "
* "probe to <call>");
* return ret;
* }
*
* static void ftrace_unreg_event_<call>(void)
* {
* unregister_trace_<call>(ftrace_event_<call>);
* }
*
* For those macros defined with TRACE_FORMAT:
*
* static struct ftrace_event_call __used
* __attribute__((__aligned__(4)))
* __attribute__((section("_ftrace_events"))) event_<call> = {
* .name = "<call>",
* .regfunc = ftrace_reg_event_<call>,
* .unregfunc = ftrace_unreg_event_<call>,
* }
*
*
* For those macros defined with TRACE_EVENT_FORMAT:
*
* static struct ftrace_event_call event_<call>;
*
* static void ftrace_raw_event_<call>(proto)
* {
* struct ring_buffer_event *event;
* struct ftrace_raw_<call> *entry; <-- defined in stage 1
* unsigned long irq_flags;
* int pc;
*
* local_save_flags(irq_flags);
* pc = preempt_count();
*
* event = trace_current_buffer_lock_reserve(event_<call>.id,
* sizeof(struct ftrace_raw_<call>),
* irq_flags, pc);
* if (!event)
* return;
* entry = ring_buffer_event_data(event);
*
* <tstruct>; <-- Here we assign the entries by the TRACE_FIELD.
*
* trace_current_buffer_unlock_commit(event, irq_flags, pc);
* }
*
* static int ftrace_raw_reg_event_<call>(void)
* {
* int ret;
*
* ret = register_trace_<call>(ftrace_raw_event_<call>);
* if (!ret)
* pr_info("event trace: Could not activate trace point "
* "probe to <call>");
* return ret;
* }
*
* static void ftrace_unreg_event_<call>(void)
* {
* unregister_trace_<call>(ftrace_raw_event_<call>);
* }
*
* static struct trace_event ftrace_event_type_<call> = {
* .trace = ftrace_raw_output_<call>, <-- stage 2
* };
*
* static int ftrace_raw_init_event_<call>(void)
* {
* int id;
*
* id = register_ftrace_event(&ftrace_event_type_<call>);
* if (!id)
* return -ENODEV;
* event_<call>.id = id;
* return 0;
* }
*
* static struct ftrace_event_call __used
* __attribute__((__aligned__(4)))
* __attribute__((section("_ftrace_events"))) event_<call> = {
* .name = "<call>",
* .regfunc = ftrace_reg_event_<call>,
* .unregfunc = ftrace_unreg_event_<call>,
* .raw_init = ftrace_raw_init_event_<call>,
* .raw_reg = ftrace_raw_reg_event_<call>,
* .raw_unreg = ftrace_raw_unreg_event_<call>,
* }
*
*/
#undef TPFMT
#define TPFMT(fmt, args...) fmt "\n", ##args
#define _TRACE_FORMAT(call, proto, args, fmt) \
static void ftrace_event_##call(proto) \
{ \
event_trace_printk(_RET_IP_, "(" #call ") " fmt); \
} \
\
static int ftrace_reg_event_##call(void) \
{ \
int ret; \
\
ret = register_trace_##call(ftrace_event_##call); \
if (!ret) \
pr_info("event trace: Could not activate trace point " \
"probe to " #call); \
return ret; \
} \
\
static void ftrace_unreg_event_##call(void) \
{ \
unregister_trace_##call(ftrace_event_##call); \
} \
#undef TRACE_FORMAT
#define TRACE_FORMAT(call, proto, args, fmt) \
_TRACE_FORMAT(call, PARAMS(proto), PARAMS(args), PARAMS(fmt)) \
static struct ftrace_event_call __used \
__attribute__((__aligned__(4))) \
__attribute__((section("_ftrace_events"))) event_##call = { \
.name = #call, \
.system = STR(TRACE_SYSTEM), \
.regfunc = ftrace_reg_event_##call, \
.unregfunc = ftrace_unreg_event_##call, \
}
#undef TRACE_FIELD
#define TRACE_FIELD(type, item, assign)\
entry->item = assign;
#undef TRACE_EVENT_FORMAT
#define TRACE_EVENT_FORMAT(call, proto, args, fmt, tstruct, tpfmt) \
_TRACE_FORMAT(call, PARAMS(proto), PARAMS(args), PARAMS(fmt)) \
\
static struct ftrace_event_call event_##call; \
\
static void ftrace_raw_event_##call(proto) \
{ \
struct ring_buffer_event *event; \
struct ftrace_raw_##call *entry; \
unsigned long irq_flags; \
int pc; \
\
local_save_flags(irq_flags); \
pc = preempt_count(); \
\
event = trace_current_buffer_lock_reserve(event_##call.id, \
sizeof(struct ftrace_raw_##call), \
irq_flags, pc); \
if (!event) \
return; \
entry = ring_buffer_event_data(event); \
\
tstruct; \
\
trace_current_buffer_unlock_commit(event, irq_flags, pc); \
} \
\
static int ftrace_raw_reg_event_##call(void) \
{ \
int ret; \
\
ret = register_trace_##call(ftrace_raw_event_##call); \
if (!ret) \
pr_info("event trace: Could not activate trace point " \
"probe to " #call); \
return ret; \
} \
\
static void ftrace_raw_unreg_event_##call(void) \
{ \
unregister_trace_##call(ftrace_raw_event_##call); \
} \
\
static struct trace_event ftrace_event_type_##call = { \
.trace = ftrace_raw_output_##call, \
}; \
\
static int ftrace_raw_init_event_##call(void) \
{ \
int id; \
\
id = register_ftrace_event(&ftrace_event_type_##call); \
if (!id) \
return -ENODEV; \
event_##call.id = id; \
return 0; \
} \
\
static struct ftrace_event_call __used \
__attribute__((__aligned__(4))) \
__attribute__((section("_ftrace_events"))) event_##call = { \
.name = #call, \
.system = STR(TRACE_SYSTEM), \
.regfunc = ftrace_reg_event_##call, \
.unregfunc = ftrace_unreg_event_##call, \
.raw_init = ftrace_raw_init_event_##call, \
.raw_reg = ftrace_raw_reg_event_##call, \
.raw_unreg = ftrace_raw_unreg_event_##call, \
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment