08aed439feaf936733650a646f262a7cb29da039
[pandora-kernel.git] / kernel / trace / trace_syscalls.c
1 #include <trace/syscall.h>
2 #include <linux/kernel.h>
3 #include <asm/syscall.h>
4
5 #include "trace_output.h"
6 #include "trace.h"
7
8 /* Keep a counter of the syscall tracing users */
9 static int refcount;
10
11 /* Prevent from races on thread flags toggling */
12 static DEFINE_MUTEX(syscall_trace_lock);
13
14 /* Option to display the parameters types */
15 enum {
16         TRACE_SYSCALLS_OPT_TYPES = 0x1,
17 };
18
19 static struct tracer_opt syscalls_opts[] = {
20         { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) },
21         { }
22 };
23
24 static struct tracer_flags syscalls_flags = {
25         .val = 0, /* By default: no parameters types */
26         .opts = syscalls_opts
27 };
28
29 enum print_line_t
30 print_syscall_enter(struct trace_iterator *iter, int flags)
31 {
32         struct trace_seq *s = &iter->seq;
33         struct trace_entry *ent = iter->ent;
34         struct syscall_trace_enter *trace;
35         struct syscall_metadata *entry;
36         int i, ret, syscall;
37
38         trace_assign_type(trace, ent);
39
40         syscall = trace->nr;
41
42         entry = syscall_nr_to_meta(syscall);
43         if (!entry)
44                 goto end;
45
46         ret = trace_seq_printf(s, "%s(", entry->name);
47         if (!ret)
48                 return TRACE_TYPE_PARTIAL_LINE;
49
50         for (i = 0; i < entry->nb_args; i++) {
51                 /* parameter types */
52                 if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) {
53                         ret = trace_seq_printf(s, "%s ", entry->types[i]);
54                         if (!ret)
55                                 return TRACE_TYPE_PARTIAL_LINE;
56                 }
57                 /* parameter values */
58                 ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i],
59                                        trace->args[i],
60                                        i == entry->nb_args - 1 ? ")" : ",");
61                 if (!ret)
62                         return TRACE_TYPE_PARTIAL_LINE;
63         }
64
65 end:
66         trace_seq_printf(s, "\n");
67         return TRACE_TYPE_HANDLED;
68 }
69
70 enum print_line_t
71 print_syscall_exit(struct trace_iterator *iter, int flags)
72 {
73         struct trace_seq *s = &iter->seq;
74         struct trace_entry *ent = iter->ent;
75         struct syscall_trace_exit *trace;
76         int syscall;
77         struct syscall_metadata *entry;
78         int ret;
79
80         trace_assign_type(trace, ent);
81
82         syscall = trace->nr;
83
84         entry = syscall_nr_to_meta(syscall);
85         if (!entry) {
86                 trace_seq_printf(s, "\n");
87                 return TRACE_TYPE_HANDLED;
88         }
89
90         ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
91                                 trace->ret);
92         if (!ret)
93                 return TRACE_TYPE_PARTIAL_LINE;
94
95         return TRACE_TYPE_HANDLED;
96 }
97
98 void start_ftrace_syscalls(void)
99 {
100         unsigned long flags;
101         struct task_struct *g, *t;
102
103         mutex_lock(&syscall_trace_lock);
104
105         /* Don't enable the flag on the tasks twice */
106         if (++refcount != 1)
107                 goto unlock;
108
109         read_lock_irqsave(&tasklist_lock, flags);
110
111         do_each_thread(g, t) {
112                 set_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
113         } while_each_thread(g, t);
114
115         read_unlock_irqrestore(&tasklist_lock, flags);
116
117 unlock:
118         mutex_unlock(&syscall_trace_lock);
119 }
120
121 void stop_ftrace_syscalls(void)
122 {
123         unsigned long flags;
124         struct task_struct *g, *t;
125
126         mutex_lock(&syscall_trace_lock);
127
128         /* There are perhaps still some users */
129         if (--refcount)
130                 goto unlock;
131
132         read_lock_irqsave(&tasklist_lock, flags);
133
134         do_each_thread(g, t) {
135                 clear_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
136         } while_each_thread(g, t);
137
138         read_unlock_irqrestore(&tasklist_lock, flags);
139
140 unlock:
141         mutex_unlock(&syscall_trace_lock);
142 }
143
144 void ftrace_syscall_enter(struct pt_regs *regs)
145 {
146         struct syscall_trace_enter *entry;
147         struct syscall_metadata *sys_data;
148         struct ring_buffer_event *event;
149         int size;
150         int syscall_nr;
151
152         syscall_nr = syscall_get_nr(current, regs);
153
154         sys_data = syscall_nr_to_meta(syscall_nr);
155         if (!sys_data)
156                 return;
157
158         size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
159
160         event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size,
161                                                         0, 0);
162         if (!event)
163                 return;
164
165         entry = ring_buffer_event_data(event);
166         entry->nr = syscall_nr;
167         syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
168
169         trace_current_buffer_unlock_commit(event, 0, 0);
170         trace_wake_up();
171 }
172
173 void ftrace_syscall_exit(struct pt_regs *regs)
174 {
175         struct syscall_trace_exit *entry;
176         struct syscall_metadata *sys_data;
177         struct ring_buffer_event *event;
178         int syscall_nr;
179
180         syscall_nr = syscall_get_nr(current, regs);
181
182         sys_data = syscall_nr_to_meta(syscall_nr);
183         if (!sys_data)
184                 return;
185
186         event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT,
187                                 sizeof(*entry), 0, 0);
188         if (!event)
189                 return;
190
191         entry = ring_buffer_event_data(event);
192         entry->nr = syscall_nr;
193         entry->ret = syscall_get_return_value(current, regs);
194
195         trace_current_buffer_unlock_commit(event, 0, 0);
196         trace_wake_up();
197 }
198
199 static int init_syscall_tracer(struct trace_array *tr)
200 {
201         start_ftrace_syscalls();
202
203         return 0;
204 }
205
206 static void reset_syscall_tracer(struct trace_array *tr)
207 {
208         stop_ftrace_syscalls();
209         tracing_reset_online_cpus(tr);
210 }
211
212 static struct trace_event syscall_enter_event = {
213         .type           = TRACE_SYSCALL_ENTER,
214         .trace          = print_syscall_enter,
215 };
216
217 static struct trace_event syscall_exit_event = {
218         .type           = TRACE_SYSCALL_EXIT,
219         .trace          = print_syscall_exit,
220 };
221
222 static struct tracer syscall_tracer __read_mostly = {
223         .name           = "syscall",
224         .init           = init_syscall_tracer,
225         .reset          = reset_syscall_tracer,
226         .flags          = &syscalls_flags,
227 };
228
229 __init int register_ftrace_syscalls(void)
230 {
231         int ret;
232
233         ret = register_ftrace_event(&syscall_enter_event);
234         if (!ret) {
235                 printk(KERN_WARNING "event %d failed to register\n",
236                        syscall_enter_event.type);
237                 WARN_ON_ONCE(1);
238         }
239
240         ret = register_ftrace_event(&syscall_exit_event);
241         if (!ret) {
242                 printk(KERN_WARNING "event %d failed to register\n",
243                        syscall_exit_event.type);
244                 WARN_ON_ONCE(1);
245         }
246
247         return register_tracer(&syscall_tracer);
248 }
249 device_initcall(register_ftrace_syscalls);