aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2009-02-25 06:50:07 -0500
committerIngo Molnar <mingo@elte.hu>2009-02-25 06:50:07 -0500
commit2b1b858f690d6369a59ad241335eeedec6eb0c8c (patch)
tree91838f66d8ef6bb42fe66849c5cbd05650d272a3
parent886b5b73d71e4027d7dc6c14f5f7ab102201ea6b (diff)
parent1473e4417c79f12d91ef91a469699bfa911f510f (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.h11
-rw-r--r--include/linux/tracepoint.h3
-rw-r--r--include/trace/sched.h49
-rw-r--r--include/trace/sched_event_types.h72
-rw-r--r--kernel/trace/Kconfig9
-rw-r--r--kernel/trace/Makefile2
-rw-r--r--kernel/trace/events.c13
-rw-r--r--kernel/trace/trace_events.c407
-rw-r--r--kernel/trace/trace_events.h55
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
7DECLARE_TRACE(sched_kthread_stop, 7#include <trace/sched_event_types.h>
8 TPPROTO(struct task_struct *t),
9 TPARGS(t));
10
11DECLARE_TRACE(sched_kthread_stop_ret,
12 TPPROTO(int ret),
13 TPARGS(ret));
14
15DECLARE_TRACE(sched_wait_task,
16 TPPROTO(struct rq *rq, struct task_struct *p),
17 TPARGS(rq, p));
18
19DECLARE_TRACE(sched_wakeup,
20 TPPROTO(struct rq *rq, struct task_struct *p, int success),
21 TPARGS(rq, p, success));
22
23DECLARE_TRACE(sched_wakeup_new,
24 TPPROTO(struct rq *rq, struct task_struct *p, int success),
25 TPARGS(rq, p, success));
26
27DECLARE_TRACE(sched_switch,
28 TPPROTO(struct rq *rq, struct task_struct *prev,
29 struct task_struct *next),
30 TPARGS(rq, prev, next));
31
32DECLARE_TRACE(sched_migrate_task,
33 TPPROTO(struct task_struct *p, int orig_cpu, int dest_cpu),
34 TPARGS(p, orig_cpu, dest_cpu));
35
36DECLARE_TRACE(sched_process_free,
37 TPPROTO(struct task_struct *p),
38 TPARGS(p));
39
40DECLARE_TRACE(sched_process_exit,
41 TPPROTO(struct task_struct *p),
42 TPARGS(p));
43
44DECLARE_TRACE(sched_process_wait,
45 TPPROTO(struct pid *pid),
46 TPARGS(pid));
47
48DECLARE_TRACE(sched_process_fork,
49 TPPROTO(struct task_struct *parent, struct task_struct *child),
50 TPARGS(parent, child));
51
52DECLARE_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
8DEFINE_TRACE_FMT(sched_kthread_stop,
9 TPPROTO(struct task_struct *t),
10 TPARGS(t),
11 TPFMT("task %s:%d", t->comm, t->pid));
12
13DEFINE_TRACE_FMT(sched_kthread_stop_ret,
14 TPPROTO(int ret),
15 TPARGS(ret),
16 TPFMT("ret=%d", ret));
17
18DEFINE_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
23DEFINE_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
29DEFINE_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
35DEFINE_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
42DEFINE_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
48DEFINE_TRACE_FMT(sched_process_free,
49 TPPROTO(struct task_struct *p),
50 TPARGS(p),
51 TPFMT("task %s:%d", p->comm, p->pid));
52
53DEFINE_TRACE_FMT(sched_process_exit,
54 TPPROTO(struct task_struct *p),
55 TPARGS(p),
56 TPFMT("task %s:%d", p->comm, p->pid));
57
58DEFINE_TRACE_FMT(sched_process_wait,
59 TPPROTO(struct pid *pid),
60 TPARGS(pid),
61 TPFMT("pid %d", pid));
62
63DEFINE_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
69DEFINE_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
162config 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
162config BOOT_TRACER 171config 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
38obj-$(CONFIG_KMEMTRACE) += kmemtrace.o 38obj-$(CONFIG_KMEMTRACE) += kmemtrace.o
39obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o 39obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o
40obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o 40obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o
41obj-$(CONFIG_EVENT_TRACER) += trace_events.o
42obj-$(CONFIG_EVENT_TRACER) += events.o
41 43
42libftrace-y := ftrace.o 44libftrace-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
20void 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
30static 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
45static 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
79static ssize_t
80ftrace_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
150static void *
151t_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
166static void *t_start(struct seq_file *m, loff_t *pos)
167{
168 return t_next(m, NULL, pos);
169}
170
171static void *
172s_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
194static void *s_start(struct seq_file *m, loff_t *pos)
195{
196 return s_next(m, NULL, pos);
197}
198
199static 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
208static void t_stop(struct seq_file *m, void *p)
209{
210}
211
212static int
213ftrace_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
232static ssize_t
233event_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
247static ssize_t
248event_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
293static 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
300static 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
307static 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
314static 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
322static const struct file_operations ftrace_enable_fops = {
323 .open = tracing_open_generic,
324 .read = event_enable_read,
325 .write = event_enable_write,
326};
327
328static 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
348static int
349event_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
369static __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}
407fs_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
8struct 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) \
22static void ftrace_event_##call(proto) \
23{ \
24 event_trace_printk(_RET_IP_, "(" #call ") " fmt); \
25} \
26 \
27static 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 \
38static void ftrace_unreg_event_##call(void) \
39{ \
40 unregister_trace_##call(ftrace_event_##call); \
41} \
42 \
43static 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
51void event_trace_printk(unsigned long ip, const char *fmt, ...);
52extern struct ftrace_event_call __start_ftrace_events[];
53extern struct ftrace_event_call __stop_ftrace_events[];
54
55#endif /* _LINUX_KERNEL_TRACE_EVENTS_H */