tracing: add event trace infrastructure
[platform/adaptation/renesas_rcar/renesas_kernel.git] / kernel / trace / trace_events.c
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 void 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
25 static 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
40 static 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
73 static ssize_t
74 ftrace_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
144 static void *
145 t_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
160 static void *t_start(struct seq_file *m, loff_t *pos)
161 {
162         return t_next(m, NULL, pos);
163 }
164
165 static void *
166 s_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
188 static void *s_start(struct seq_file *m, loff_t *pos)
189 {
190         return s_next(m, NULL, pos);
191 }
192
193 static 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
202 static void t_stop(struct seq_file *m, void *p)
203 {
204 }
205
206 static int
207 ftrace_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
226 static 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
233 static 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
240 static 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
247 static 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
255 static __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 }
280 fs_initcall(event_trace_init);