diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-02-25 06:50:07 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-25 06:50:07 -0500 |
commit | 2b1b858f690d6369a59ad241335eeedec6eb0c8c (patch) | |
tree | 91838f66d8ef6bb42fe66849c5cbd05650d272a3 | |
parent | 886b5b73d71e4027d7dc6c14f5f7ab102201ea6b (diff) | |
parent | 1473e4417c79f12d91ef91a469699bfa911f510f (diff) |
Merge branch 'tip/tracing/ftrace' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into tracing/ftrace
-rw-r--r-- | include/asm-generic/vmlinux.lds.h | 11 | ||||
-rw-r--r-- | include/linux/tracepoint.h | 3 | ||||
-rw-r--r-- | include/trace/sched.h | 49 | ||||
-rw-r--r-- | include/trace/sched_event_types.h | 72 | ||||
-rw-r--r-- | kernel/trace/Kconfig | 9 | ||||
-rw-r--r-- | kernel/trace/Makefile | 2 | ||||
-rw-r--r-- | kernel/trace/events.c | 13 | ||||
-rw-r--r-- | kernel/trace/trace_events.c | 407 | ||||
-rw-r--r-- | kernel/trace/trace_events.h | 55 |
9 files changed, 572 insertions, 49 deletions
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index c61fab1dd2f8..0add6b28c366 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
@@ -61,6 +61,14 @@ | |||
61 | #define BRANCH_PROFILE() | 61 | #define BRANCH_PROFILE() |
62 | #endif | 62 | #endif |
63 | 63 | ||
64 | #ifdef CONFIG_EVENT_TRACER | ||
65 | #define FTRACE_EVENTS() VMLINUX_SYMBOL(__start_ftrace_events) = .; \ | ||
66 | *(_ftrace_events) \ | ||
67 | VMLINUX_SYMBOL(__stop_ftrace_events) = .; | ||
68 | #else | ||
69 | #define FTRACE_EVENTS() | ||
70 | #endif | ||
71 | |||
64 | /* .data section */ | 72 | /* .data section */ |
65 | #define DATA_DATA \ | 73 | #define DATA_DATA \ |
66 | *(.data) \ | 74 | *(.data) \ |
@@ -81,7 +89,8 @@ | |||
81 | *(__tracepoints) \ | 89 | *(__tracepoints) \ |
82 | VMLINUX_SYMBOL(__stop___tracepoints) = .; \ | 90 | VMLINUX_SYMBOL(__stop___tracepoints) = .; \ |
83 | LIKELY_PROFILE() \ | 91 | LIKELY_PROFILE() \ |
84 | BRANCH_PROFILE() | 92 | BRANCH_PROFILE() \ |
93 | FTRACE_EVENTS() | ||
85 | 94 | ||
86 | #define RO_DATA(align) \ | 95 | #define RO_DATA(align) \ |
87 | . = ALIGN((align)); \ | 96 | . = ALIGN((align)); \ |
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 757005458366..34ae464effff 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h | |||
@@ -153,4 +153,7 @@ static inline void tracepoint_synchronize_unregister(void) | |||
153 | synchronize_sched(); | 153 | synchronize_sched(); |
154 | } | 154 | } |
155 | 155 | ||
156 | #define DEFINE_TRACE_FMT(name, proto, args, fmt) \ | ||
157 | DECLARE_TRACE(name, TPPROTO(proto), TPARGS(args)) | ||
158 | |||
156 | #endif | 159 | #endif |
diff --git a/include/trace/sched.h b/include/trace/sched.h index 0d81098ee9fc..4e372a1a29bf 100644 --- a/include/trace/sched.h +++ b/include/trace/sched.h | |||
@@ -4,53 +4,6 @@ | |||
4 | #include <linux/sched.h> | 4 | #include <linux/sched.h> |
5 | #include <linux/tracepoint.h> | 5 | #include <linux/tracepoint.h> |
6 | 6 | ||
7 | DECLARE_TRACE(sched_kthread_stop, | 7 | #include <trace/sched_event_types.h> |
8 | TPPROTO(struct task_struct *t), | ||
9 | TPARGS(t)); | ||
10 | |||
11 | DECLARE_TRACE(sched_kthread_stop_ret, | ||
12 | TPPROTO(int ret), | ||
13 | TPARGS(ret)); | ||
14 | |||
15 | DECLARE_TRACE(sched_wait_task, | ||
16 | TPPROTO(struct rq *rq, struct task_struct *p), | ||
17 | TPARGS(rq, p)); | ||
18 | |||
19 | DECLARE_TRACE(sched_wakeup, | ||
20 | TPPROTO(struct rq *rq, struct task_struct *p, int success), | ||
21 | TPARGS(rq, p, success)); | ||
22 | |||
23 | DECLARE_TRACE(sched_wakeup_new, | ||
24 | TPPROTO(struct rq *rq, struct task_struct *p, int success), | ||
25 | TPARGS(rq, p, success)); | ||
26 | |||
27 | DECLARE_TRACE(sched_switch, | ||
28 | TPPROTO(struct rq *rq, struct task_struct *prev, | ||
29 | struct task_struct *next), | ||
30 | TPARGS(rq, prev, next)); | ||
31 | |||
32 | DECLARE_TRACE(sched_migrate_task, | ||
33 | TPPROTO(struct task_struct *p, int orig_cpu, int dest_cpu), | ||
34 | TPARGS(p, orig_cpu, dest_cpu)); | ||
35 | |||
36 | DECLARE_TRACE(sched_process_free, | ||
37 | TPPROTO(struct task_struct *p), | ||
38 | TPARGS(p)); | ||
39 | |||
40 | DECLARE_TRACE(sched_process_exit, | ||
41 | TPPROTO(struct task_struct *p), | ||
42 | TPARGS(p)); | ||
43 | |||
44 | DECLARE_TRACE(sched_process_wait, | ||
45 | TPPROTO(struct pid *pid), | ||
46 | TPARGS(pid)); | ||
47 | |||
48 | DECLARE_TRACE(sched_process_fork, | ||
49 | TPPROTO(struct task_struct *parent, struct task_struct *child), | ||
50 | TPARGS(parent, child)); | ||
51 | |||
52 | DECLARE_TRACE(sched_signal_send, | ||
53 | TPPROTO(int sig, struct task_struct *p), | ||
54 | TPARGS(sig, p)); | ||
55 | 8 | ||
56 | #endif | 9 | #endif |
diff --git a/include/trace/sched_event_types.h b/include/trace/sched_event_types.h new file mode 100644 index 000000000000..a4f662940f4e --- /dev/null +++ b/include/trace/sched_event_types.h | |||
@@ -0,0 +1,72 @@ | |||
1 | |||
2 | /* use <trace/sched.h> instead */ | ||
3 | #ifndef DEFINE_TRACE_FMT | ||
4 | # error Do not include this file directly. | ||
5 | # error Unless you know what you are doing. | ||
6 | #endif | ||
7 | |||
8 | DEFINE_TRACE_FMT(sched_kthread_stop, | ||
9 | TPPROTO(struct task_struct *t), | ||
10 | TPARGS(t), | ||
11 | TPFMT("task %s:%d", t->comm, t->pid)); | ||
12 | |||
13 | DEFINE_TRACE_FMT(sched_kthread_stop_ret, | ||
14 | TPPROTO(int ret), | ||
15 | TPARGS(ret), | ||
16 | TPFMT("ret=%d", ret)); | ||
17 | |||
18 | DEFINE_TRACE_FMT(sched_wait_task, | ||
19 | TPPROTO(struct rq *rq, struct task_struct *p), | ||
20 | TPARGS(rq, p), | ||
21 | TPFMT("task %s:%d", p->comm, p->pid)); | ||
22 | |||
23 | DEFINE_TRACE_FMT(sched_wakeup, | ||
24 | TPPROTO(struct rq *rq, struct task_struct *p, int success), | ||
25 | TPARGS(rq, p, success), | ||
26 | TPFMT("task %s:%d %s", | ||
27 | p->comm, p->pid, success?"succeeded":"failed")); | ||
28 | |||
29 | DEFINE_TRACE_FMT(sched_wakeup_new, | ||
30 | TPPROTO(struct rq *rq, struct task_struct *p, int success), | ||
31 | TPARGS(rq, p, success), | ||
32 | TPFMT("task %s:%d", | ||
33 | p->comm, p->pid, success?"succeeded":"failed")); | ||
34 | |||
35 | DEFINE_TRACE_FMT(sched_switch, | ||
36 | TPPROTO(struct rq *rq, struct task_struct *prev, | ||
37 | struct task_struct *next), | ||
38 | TPARGS(rq, prev, next), | ||
39 | TPFMT("task %s:%d ==> %s:%d", | ||
40 | prev->comm, prev->pid, next->comm, next->pid)); | ||
41 | |||
42 | DEFINE_TRACE_FMT(sched_migrate_task, | ||
43 | TPPROTO(struct task_struct *p, int orig_cpu, int dest_cpu), | ||
44 | TPARGS(p, orig_cpu, dest_cpu), | ||
45 | TPFMT("task %s:%d from: %d to: %d", | ||
46 | p->comm, p->pid, orig_cpu, dest_cpu)); | ||
47 | |||
48 | DEFINE_TRACE_FMT(sched_process_free, | ||
49 | TPPROTO(struct task_struct *p), | ||
50 | TPARGS(p), | ||
51 | TPFMT("task %s:%d", p->comm, p->pid)); | ||
52 | |||
53 | DEFINE_TRACE_FMT(sched_process_exit, | ||
54 | TPPROTO(struct task_struct *p), | ||
55 | TPARGS(p), | ||
56 | TPFMT("task %s:%d", p->comm, p->pid)); | ||
57 | |||
58 | DEFINE_TRACE_FMT(sched_process_wait, | ||
59 | TPPROTO(struct pid *pid), | ||
60 | TPARGS(pid), | ||
61 | TPFMT("pid %d", pid)); | ||
62 | |||
63 | DEFINE_TRACE_FMT(sched_process_fork, | ||
64 | TPPROTO(struct task_struct *parent, struct task_struct *child), | ||
65 | TPARGS(parent, child), | ||
66 | TPFMT("parent %s:%d child %s:%d", | ||
67 | parent->comm, parent->pid, child->comm, child->pid)); | ||
68 | |||
69 | DEFINE_TRACE_FMT(sched_signal_send, | ||
70 | TPPROTO(int sig, struct task_struct *p), | ||
71 | TPARGS(sig, p), | ||
72 | TPFMT("sig: %d task %s:%d", sig, p->comm, p->pid)); | ||
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 07877f4b5233..999c6a2485df 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
@@ -159,6 +159,15 @@ config CONTEXT_SWITCH_TRACER | |||
159 | This tracer gets called from the context switch and records | 159 | This tracer gets called from the context switch and records |
160 | all switching of tasks. | 160 | all switching of tasks. |
161 | 161 | ||
162 | config EVENT_TRACER | ||
163 | bool "Trace various events in the kernel" | ||
164 | depends on DEBUG_KERNEL | ||
165 | select TRACING | ||
166 | help | ||
167 | This tracer hooks to various trace points in the kernel | ||
168 | allowing the user to pick and choose which trace point they | ||
169 | want to trace. | ||
170 | |||
162 | config BOOT_TRACER | 171 | config BOOT_TRACER |
163 | bool "Trace boot initcalls" | 172 | bool "Trace boot initcalls" |
164 | depends on DEBUG_KERNEL | 173 | depends on DEBUG_KERNEL |
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 627090bc262d..664b6c0dc75a 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
@@ -38,5 +38,7 @@ obj-$(CONFIG_POWER_TRACER) += trace_power.o | |||
38 | obj-$(CONFIG_KMEMTRACE) += kmemtrace.o | 38 | obj-$(CONFIG_KMEMTRACE) += kmemtrace.o |
39 | obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o | 39 | obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o |
40 | obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o | 40 | obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o |
41 | obj-$(CONFIG_EVENT_TRACER) += trace_events.o | ||
42 | obj-$(CONFIG_EVENT_TRACER) += events.o | ||
41 | 43 | ||
42 | libftrace-y := ftrace.o | 44 | libftrace-y := ftrace.o |
diff --git a/kernel/trace/events.c b/kernel/trace/events.c new file mode 100644 index 000000000000..38c89eef99ee --- /dev/null +++ b/kernel/trace/events.c | |||
@@ -0,0 +1,13 @@ | |||
1 | /* | ||
2 | * This is the place to register all trace points as events. | ||
3 | * Include the trace/<type>.h at the top. | ||
4 | * Include the trace/<type>_event_types.h at the bottom. | ||
5 | */ | ||
6 | |||
7 | /* trace/<type>.h here */ | ||
8 | #include <trace/sched.h> | ||
9 | |||
10 | #include "trace_events.h" | ||
11 | |||
12 | /* trace/<type>_event_types.h here */ | ||
13 | #include <trace/sched_event_types.h> | ||
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c new file mode 100644 index 000000000000..3bcb9df93342 --- /dev/null +++ b/kernel/trace/trace_events.c | |||
@@ -0,0 +1,407 @@ | |||
1 | /* | ||
2 | * event tracer | ||
3 | * | ||
4 | * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/debugfs.h> | ||
9 | #include <linux/uaccess.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/ctype.h> | ||
12 | |||
13 | #include "trace_events.h" | ||
14 | |||
15 | #define events_for_each(event) \ | ||
16 | for (event = __start_ftrace_events; \ | ||
17 | (unsigned long)event < (unsigned long)__stop_ftrace_events; \ | ||
18 | event++) | ||
19 | |||
20 | void event_trace_printk(unsigned long ip, const char *fmt, ...) | ||
21 | { | ||
22 | va_list ap; | ||
23 | |||
24 | va_start(ap, fmt); | ||
25 | tracing_record_cmdline(current); | ||
26 | trace_vprintk(ip, task_curr_ret_stack(current), fmt, ap); | ||
27 | va_end(ap); | ||
28 | } | ||
29 | |||
30 | static void ftrace_clear_events(void) | ||
31 | { | ||
32 | struct ftrace_event_call *call = (void *)__start_ftrace_events; | ||
33 | |||
34 | |||
35 | while ((unsigned long)call < (unsigned long)__stop_ftrace_events) { | ||
36 | |||
37 | if (call->enabled) { | ||
38 | call->enabled = 0; | ||
39 | call->unregfunc(); | ||
40 | } | ||
41 | call++; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | static int ftrace_set_clr_event(char *buf, int set) | ||
46 | { | ||
47 | struct ftrace_event_call *call = __start_ftrace_events; | ||
48 | |||
49 | |||
50 | events_for_each(call) { | ||
51 | |||
52 | if (!call->name) | ||
53 | continue; | ||
54 | |||
55 | if (strcmp(buf, call->name) != 0) | ||
56 | continue; | ||
57 | |||
58 | if (set) { | ||
59 | /* Already set? */ | ||
60 | if (call->enabled) | ||
61 | return 0; | ||
62 | call->enabled = 1; | ||
63 | call->regfunc(); | ||
64 | } else { | ||
65 | /* Already cleared? */ | ||
66 | if (!call->enabled) | ||
67 | return 0; | ||
68 | call->enabled = 0; | ||
69 | call->unregfunc(); | ||
70 | } | ||
71 | return 0; | ||
72 | } | ||
73 | return -EINVAL; | ||
74 | } | ||
75 | |||
76 | /* 128 should be much more than enough */ | ||
77 | #define EVENT_BUF_SIZE 127 | ||
78 | |||
79 | static ssize_t | ||
80 | ftrace_event_write(struct file *file, const char __user *ubuf, | ||
81 | size_t cnt, loff_t *ppos) | ||
82 | { | ||
83 | size_t read = 0; | ||
84 | int i, set = 1; | ||
85 | ssize_t ret; | ||
86 | char *buf; | ||
87 | char ch; | ||
88 | |||
89 | if (!cnt || cnt < 0) | ||
90 | return 0; | ||
91 | |||
92 | ret = get_user(ch, ubuf++); | ||
93 | if (ret) | ||
94 | return ret; | ||
95 | read++; | ||
96 | cnt--; | ||
97 | |||
98 | /* skip white space */ | ||
99 | while (cnt && isspace(ch)) { | ||
100 | ret = get_user(ch, ubuf++); | ||
101 | if (ret) | ||
102 | return ret; | ||
103 | read++; | ||
104 | cnt--; | ||
105 | } | ||
106 | |||
107 | /* Only white space found? */ | ||
108 | if (isspace(ch)) { | ||
109 | file->f_pos += read; | ||
110 | ret = read; | ||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | buf = kmalloc(EVENT_BUF_SIZE+1, GFP_KERNEL); | ||
115 | if (!buf) | ||
116 | return -ENOMEM; | ||
117 | |||
118 | if (cnt > EVENT_BUF_SIZE) | ||
119 | cnt = EVENT_BUF_SIZE; | ||
120 | |||
121 | i = 0; | ||
122 | while (cnt && !isspace(ch)) { | ||
123 | if (!i && ch == '!') | ||
124 | set = 0; | ||
125 | else | ||
126 | buf[i++] = ch; | ||
127 | |||
128 | ret = get_user(ch, ubuf++); | ||
129 | if (ret) | ||
130 | goto out_free; | ||
131 | read++; | ||
132 | cnt--; | ||
133 | } | ||
134 | buf[i] = 0; | ||
135 | |||
136 | file->f_pos += read; | ||
137 | |||
138 | ret = ftrace_set_clr_event(buf, set); | ||
139 | if (ret) | ||
140 | goto out_free; | ||
141 | |||
142 | ret = read; | ||
143 | |||
144 | out_free: | ||
145 | kfree(buf); | ||
146 | |||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | static void * | ||
151 | t_next(struct seq_file *m, void *v, loff_t *pos) | ||
152 | { | ||
153 | struct ftrace_event_call *call = m->private; | ||
154 | struct ftrace_event_call *next = call; | ||
155 | |||
156 | (*pos)++; | ||
157 | |||
158 | if ((unsigned long)call >= (unsigned long)__stop_ftrace_events) | ||
159 | return NULL; | ||
160 | |||
161 | m->private = ++next; | ||
162 | |||
163 | return call; | ||
164 | } | ||
165 | |||
166 | static void *t_start(struct seq_file *m, loff_t *pos) | ||
167 | { | ||
168 | return t_next(m, NULL, pos); | ||
169 | } | ||
170 | |||
171 | static void * | ||
172 | s_next(struct seq_file *m, void *v, loff_t *pos) | ||
173 | { | ||
174 | struct ftrace_event_call *call = m->private; | ||
175 | struct ftrace_event_call *next; | ||
176 | |||
177 | (*pos)++; | ||
178 | |||
179 | retry: | ||
180 | if ((unsigned long)call >= (unsigned long)__stop_ftrace_events) | ||
181 | return NULL; | ||
182 | |||
183 | if (!call->enabled) { | ||
184 | call++; | ||
185 | goto retry; | ||
186 | } | ||
187 | |||
188 | next = call; | ||
189 | m->private = ++next; | ||
190 | |||
191 | return call; | ||
192 | } | ||
193 | |||
194 | static void *s_start(struct seq_file *m, loff_t *pos) | ||
195 | { | ||
196 | return s_next(m, NULL, pos); | ||
197 | } | ||
198 | |||
199 | static int t_show(struct seq_file *m, void *v) | ||
200 | { | ||
201 | struct ftrace_event_call *call = v; | ||
202 | |||
203 | seq_printf(m, "%s\n", call->name); | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static void t_stop(struct seq_file *m, void *p) | ||
209 | { | ||
210 | } | ||
211 | |||
212 | static int | ||
213 | ftrace_event_seq_open(struct inode *inode, struct file *file) | ||
214 | { | ||
215 | int ret; | ||
216 | const struct seq_operations *seq_ops; | ||
217 | |||
218 | if ((file->f_mode & FMODE_WRITE) && | ||
219 | !(file->f_flags & O_APPEND)) | ||
220 | ftrace_clear_events(); | ||
221 | |||
222 | seq_ops = inode->i_private; | ||
223 | ret = seq_open(file, seq_ops); | ||
224 | if (!ret) { | ||
225 | struct seq_file *m = file->private_data; | ||
226 | |||
227 | m->private = __start_ftrace_events; | ||
228 | } | ||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | static ssize_t | ||
233 | event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
234 | loff_t *ppos) | ||
235 | { | ||
236 | struct ftrace_event_call *call = filp->private_data; | ||
237 | char *buf; | ||
238 | |||
239 | if (call->enabled) | ||
240 | buf = "1\n"; | ||
241 | else | ||
242 | buf = "0\n"; | ||
243 | |||
244 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, 2); | ||
245 | } | ||
246 | |||
247 | static ssize_t | ||
248 | event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | ||
249 | loff_t *ppos) | ||
250 | { | ||
251 | struct ftrace_event_call *call = filp->private_data; | ||
252 | char buf[64]; | ||
253 | unsigned long val; | ||
254 | int ret; | ||
255 | |||
256 | if (cnt >= sizeof(buf)) | ||
257 | return -EINVAL; | ||
258 | |||
259 | if (copy_from_user(&buf, ubuf, cnt)) | ||
260 | return -EFAULT; | ||
261 | |||
262 | buf[cnt] = 0; | ||
263 | |||
264 | ret = strict_strtoul(buf, 10, &val); | ||
265 | if (ret < 0) | ||
266 | return ret; | ||
267 | |||
268 | switch (val) { | ||
269 | case 0: | ||
270 | if (!call->enabled) | ||
271 | break; | ||
272 | |||
273 | call->enabled = 0; | ||
274 | call->unregfunc(); | ||
275 | break; | ||
276 | case 1: | ||
277 | if (call->enabled) | ||
278 | break; | ||
279 | |||
280 | call->enabled = 1; | ||
281 | call->regfunc(); | ||
282 | break; | ||
283 | |||
284 | default: | ||
285 | return -EINVAL; | ||
286 | } | ||
287 | |||
288 | *ppos += cnt; | ||
289 | |||
290 | return cnt; | ||
291 | } | ||
292 | |||
293 | static const struct seq_operations show_event_seq_ops = { | ||
294 | .start = t_start, | ||
295 | .next = t_next, | ||
296 | .show = t_show, | ||
297 | .stop = t_stop, | ||
298 | }; | ||
299 | |||
300 | static const struct seq_operations show_set_event_seq_ops = { | ||
301 | .start = s_start, | ||
302 | .next = s_next, | ||
303 | .show = t_show, | ||
304 | .stop = t_stop, | ||
305 | }; | ||
306 | |||
307 | static const struct file_operations ftrace_avail_fops = { | ||
308 | .open = ftrace_event_seq_open, | ||
309 | .read = seq_read, | ||
310 | .llseek = seq_lseek, | ||
311 | .release = seq_release, | ||
312 | }; | ||
313 | |||
314 | static const struct file_operations ftrace_set_event_fops = { | ||
315 | .open = ftrace_event_seq_open, | ||
316 | .read = seq_read, | ||
317 | .write = ftrace_event_write, | ||
318 | .llseek = seq_lseek, | ||
319 | .release = seq_release, | ||
320 | }; | ||
321 | |||
322 | static const struct file_operations ftrace_enable_fops = { | ||
323 | .open = tracing_open_generic, | ||
324 | .read = event_enable_read, | ||
325 | .write = event_enable_write, | ||
326 | }; | ||
327 | |||
328 | static struct dentry *event_trace_events_dir(void) | ||
329 | { | ||
330 | static struct dentry *d_tracer; | ||
331 | static struct dentry *d_events; | ||
332 | |||
333 | if (d_events) | ||
334 | return d_events; | ||
335 | |||
336 | d_tracer = tracing_init_dentry(); | ||
337 | if (!d_tracer) | ||
338 | return NULL; | ||
339 | |||
340 | d_events = debugfs_create_dir("events", d_tracer); | ||
341 | if (!d_events) | ||
342 | pr_warning("Could not create debugfs " | ||
343 | "'events' directory\n"); | ||
344 | |||
345 | return d_events; | ||
346 | } | ||
347 | |||
348 | static int | ||
349 | event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | ||
350 | { | ||
351 | struct dentry *entry; | ||
352 | |||
353 | call->dir = debugfs_create_dir(call->name, d_events); | ||
354 | if (!call->dir) { | ||
355 | pr_warning("Could not create debugfs " | ||
356 | "'%s' directory\n", call->name); | ||
357 | return -1; | ||
358 | } | ||
359 | |||
360 | entry = debugfs_create_file("enable", 0644, call->dir, call, | ||
361 | &ftrace_enable_fops); | ||
362 | if (!entry) | ||
363 | pr_warning("Could not create debugfs " | ||
364 | "'%s/enable' entry\n", call->name); | ||
365 | |||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static __init int event_trace_init(void) | ||
370 | { | ||
371 | struct ftrace_event_call *call = __start_ftrace_events; | ||
372 | struct dentry *d_tracer; | ||
373 | struct dentry *entry; | ||
374 | struct dentry *d_events; | ||
375 | |||
376 | d_tracer = tracing_init_dentry(); | ||
377 | if (!d_tracer) | ||
378 | return 0; | ||
379 | |||
380 | entry = debugfs_create_file("available_events", 0444, d_tracer, | ||
381 | (void *)&show_event_seq_ops, | ||
382 | &ftrace_avail_fops); | ||
383 | if (!entry) | ||
384 | pr_warning("Could not create debugfs " | ||
385 | "'available_events' entry\n"); | ||
386 | |||
387 | entry = debugfs_create_file("set_event", 0644, d_tracer, | ||
388 | (void *)&show_set_event_seq_ops, | ||
389 | &ftrace_set_event_fops); | ||
390 | if (!entry) | ||
391 | pr_warning("Could not create debugfs " | ||
392 | "'set_event' entry\n"); | ||
393 | |||
394 | d_events = event_trace_events_dir(); | ||
395 | if (!d_events) | ||
396 | return 0; | ||
397 | |||
398 | events_for_each(call) { | ||
399 | /* The linker may leave blanks */ | ||
400 | if (!call->name) | ||
401 | continue; | ||
402 | event_create_dir(call, d_events); | ||
403 | } | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | fs_initcall(event_trace_init); | ||
diff --git a/kernel/trace/trace_events.h b/kernel/trace/trace_events.h new file mode 100644 index 000000000000..cb8455b3ac9a --- /dev/null +++ b/kernel/trace/trace_events.h | |||
@@ -0,0 +1,55 @@ | |||
1 | #ifndef _LINUX_KERNEL_TRACE_EVENTS_H | ||
2 | #define _LINUX_KERNEL_TRACE_EVENTS_H | ||
3 | |||
4 | #include <linux/debugfs.h> | ||
5 | #include <linux/ftrace.h> | ||
6 | #include "trace.h" | ||
7 | |||
8 | struct ftrace_event_call { | ||
9 | char *name; | ||
10 | struct dentry *dir; | ||
11 | int enabled; | ||
12 | int (*regfunc)(void); | ||
13 | void (*unregfunc)(void); | ||
14 | }; | ||
15 | |||
16 | |||
17 | #undef TPFMT | ||
18 | #define TPFMT(fmt, args...) fmt "\n", ##args | ||
19 | |||
20 | #undef DEFINE_TRACE_FMT | ||
21 | #define DEFINE_TRACE_FMT(call, proto, args, fmt) \ | ||
22 | static void ftrace_event_##call(proto) \ | ||
23 | { \ | ||
24 | event_trace_printk(_RET_IP_, "(" #call ") " fmt); \ | ||
25 | } \ | ||
26 | \ | ||
27 | static int ftrace_reg_event_##call(void) \ | ||
28 | { \ | ||
29 | int ret; \ | ||
30 | \ | ||
31 | ret = register_trace_##call(ftrace_event_##call); \ | ||
32 | if (!ret) \ | ||
33 | pr_info("event trace: Could not activate trace point " \ | ||
34 | "probe to " #call); \ | ||
35 | return ret; \ | ||
36 | } \ | ||
37 | \ | ||
38 | static void ftrace_unreg_event_##call(void) \ | ||
39 | { \ | ||
40 | unregister_trace_##call(ftrace_event_##call); \ | ||
41 | } \ | ||
42 | \ | ||
43 | static struct ftrace_event_call __used \ | ||
44 | __attribute__((__aligned__(4))) \ | ||
45 | __attribute__((section("_ftrace_events"))) event_##call = { \ | ||
46 | .name = #call, \ | ||
47 | .regfunc = ftrace_reg_event_##call, \ | ||
48 | .unregfunc = ftrace_unreg_event_##call, \ | ||
49 | } | ||
50 | |||
51 | void event_trace_printk(unsigned long ip, const char *fmt, ...); | ||
52 | extern struct ftrace_event_call __start_ftrace_events[]; | ||
53 | extern struct ftrace_event_call __stop_ftrace_events[]; | ||
54 | |||
55 | #endif /* _LINUX_KERNEL_TRACE_EVENTS_H */ | ||