]> bbs.cooldavid.org Git - net-next-2.6.git/blame - kernel/trace/trace_events.c
tracing: add event trace infrastructure
[net-next-2.6.git] / kernel / trace / trace_events.c
CommitLineData
b77e38aa
SR
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
15void event_trace_printk(unsigned long ip, const char *fmt, ...)
16{
17 va_list ap;
18
19 va_start(ap, fmt);
20 tracing_record_cmdline(current);
21 trace_vprintk(ip, task_curr_ret_stack(current), fmt, ap);
22 va_end(ap);
23}
24
25static void ftrace_clear_events(void)
26{
27 struct ftrace_event_call *call = (void *)__start_ftrace_events;
28
29
30 while ((unsigned long)call < (unsigned long)__stop_ftrace_events) {
31
32 if (call->enabled) {
33 call->enabled = 0;
34 call->unregfunc();
35 }
36 call++;
37 }
38}
39
40static int ftrace_set_clr_event(char *buf, int set)
41{
42 struct ftrace_event_call *call = (void *)__start_ftrace_events;
43
44
45 while ((unsigned long)call < (unsigned long)__stop_ftrace_events) {
46
47 if (strcmp(buf, call->name) != 0) {
48 call++;
49 continue;
50 }
51
52 if (set) {
53 /* Already set? */
54 if (call->enabled)
55 return 0;
56 call->enabled = 1;
57 call->regfunc();
58 } else {
59 /* Already cleared? */
60 if (!call->enabled)
61 return 0;
62 call->enabled = 0;
63 call->unregfunc();
64 }
65 return 0;
66 }
67 return -EINVAL;
68}
69
70/* 128 should be much more than enough */
71#define EVENT_BUF_SIZE 127
72
73static ssize_t
74ftrace_event_write(struct file *file, const char __user *ubuf,
75 size_t cnt, loff_t *ppos)
76{
77 size_t read = 0;
78 int i, set = 1;
79 ssize_t ret;
80 char *buf;
81 char ch;
82
83 if (!cnt || cnt < 0)
84 return 0;
85
86 ret = get_user(ch, ubuf++);
87 if (ret)
88 return ret;
89 read++;
90 cnt--;
91
92 /* skip white space */
93 while (cnt && isspace(ch)) {
94 ret = get_user(ch, ubuf++);
95 if (ret)
96 return ret;
97 read++;
98 cnt--;
99 }
100
101 /* Only white space found? */
102 if (isspace(ch)) {
103 file->f_pos += read;
104 ret = read;
105 return ret;
106 }
107
108 buf = kmalloc(EVENT_BUF_SIZE+1, GFP_KERNEL);
109 if (!buf)
110 return -ENOMEM;
111
112 if (cnt > EVENT_BUF_SIZE)
113 cnt = EVENT_BUF_SIZE;
114
115 i = 0;
116 while (cnt && !isspace(ch)) {
117 if (!i && ch == '!')
118 set = 0;
119 else
120 buf[i++] = ch;
121
122 ret = get_user(ch, ubuf++);
123 if (ret)
124 goto out_free;
125 read++;
126 cnt--;
127 }
128 buf[i] = 0;
129
130 file->f_pos += read;
131
132 ret = ftrace_set_clr_event(buf, set);
133 if (ret)
134 goto out_free;
135
136 ret = read;
137
138 out_free:
139 kfree(buf);
140
141 return ret;
142}
143
144static void *
145t_next(struct seq_file *m, void *v, loff_t *pos)
146{
147 struct ftrace_event_call *call = m->private;
148 struct ftrace_event_call *next = call;
149
150 (*pos)++;
151
152 if ((unsigned long)call >= (unsigned long)__stop_ftrace_events)
153 return NULL;
154
155 m->private = ++next;
156
157 return call;
158}
159
160static void *t_start(struct seq_file *m, loff_t *pos)
161{
162 return t_next(m, NULL, pos);
163}
164
165static void *
166s_next(struct seq_file *m, void *v, loff_t *pos)
167{
168 struct ftrace_event_call *call = m->private;
169 struct ftrace_event_call *next;
170
171 (*pos)++;
172
173 retry:
174 if ((unsigned long)call >= (unsigned long)__stop_ftrace_events)
175 return NULL;
176
177 if (!call->enabled) {
178 call++;
179 goto retry;
180 }
181
182 next = call;
183 m->private = ++next;
184
185 return call;
186}
187
188static void *s_start(struct seq_file *m, loff_t *pos)
189{
190 return s_next(m, NULL, pos);
191}
192
193static int t_show(struct seq_file *m, void *v)
194{
195 struct ftrace_event_call *call = v;
196
197 seq_printf(m, "%s\n", call->name);
198
199 return 0;
200}
201
202static void t_stop(struct seq_file *m, void *p)
203{
204}
205
206static int
207ftrace_event_seq_open(struct inode *inode, struct file *file)
208{
209 int ret;
210 const struct seq_operations *seq_ops;
211
212 if ((file->f_mode & FMODE_WRITE) &&
213 !(file->f_flags & O_APPEND))
214 ftrace_clear_events();
215
216 seq_ops = inode->i_private;
217 ret = seq_open(file, seq_ops);
218 if (!ret) {
219 struct seq_file *m = file->private_data;
220
221 m->private = __start_ftrace_events;
222 }
223 return ret;
224}
225
226static const struct seq_operations show_event_seq_ops = {
227 .start = t_start,
228 .next = t_next,
229 .show = t_show,
230 .stop = t_stop,
231};
232
233static const struct seq_operations show_set_event_seq_ops = {
234 .start = s_start,
235 .next = s_next,
236 .show = t_show,
237 .stop = t_stop,
238};
239
240static const struct file_operations ftrace_avail_fops = {
241 .open = ftrace_event_seq_open,
242 .read = seq_read,
243 .llseek = seq_lseek,
244 .release = seq_release,
245};
246
247static const struct file_operations ftrace_set_event_fops = {
248 .open = ftrace_event_seq_open,
249 .read = seq_read,
250 .write = ftrace_event_write,
251 .llseek = seq_lseek,
252 .release = seq_release,
253};
254
255static __init int event_trace_init(void)
256{
257 struct dentry *d_tracer;
258 struct dentry *entry;
259
260 d_tracer = tracing_init_dentry();
261 if (!d_tracer)
262 return 0;
263
264 entry = debugfs_create_file("available_events", 0444, d_tracer,
265 (void *)&show_event_seq_ops,
266 &ftrace_avail_fops);
267 if (!entry)
268 pr_warning("Could not create debugfs "
269 "'available_events' entry\n");
270
271 entry = debugfs_create_file("set_event", 0644, d_tracer,
272 (void *)&show_set_event_seq_ops,
273 &ftrace_set_event_fops);
274 if (!entry)
275 pr_warning("Could not create debugfs "
276 "'set_event' entry\n");
277
278 return 0;
279}
280fs_initcall(event_trace_init);