e58a9c11ba856f3353509dd70c741afb62d04ddb
[pandora-kernel.git] / kernel / trace / trace_syscalls.c
1 #include <trace/syscall.h>
2 #include <linux/kernel.h>
3 #include <linux/ftrace.h>
4 #include <asm/syscall.h>
5
6 #include "trace_output.h"
7 #include "trace.h"
8
9 static DEFINE_MUTEX(syscall_trace_lock);
10 static int sys_refcount_enter;
11 static int sys_refcount_exit;
12 static DECLARE_BITMAP(enabled_enter_syscalls, FTRACE_SYSCALL_MAX);
13 static DECLARE_BITMAP(enabled_exit_syscalls, FTRACE_SYSCALL_MAX);
14
15 /* Option to display the parameters types */
16 enum {
17         TRACE_SYSCALLS_OPT_TYPES = 0x1,
18 };
19
20 static struct tracer_opt syscalls_opts[] = {
21         { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) },
22         { }
23 };
24
25 static struct tracer_flags syscalls_flags = {
26         .val = 0, /* By default: no parameters types */
27         .opts = syscalls_opts
28 };
29
30 enum print_line_t
31 print_syscall_enter(struct trace_iterator *iter, int flags)
32 {
33         struct trace_seq *s = &iter->seq;
34         struct trace_entry *ent = iter->ent;
35         struct syscall_trace_enter *trace;
36         struct syscall_metadata *entry;
37         int i, ret, syscall;
38
39         trace = (typeof(trace))ent;
40         syscall = trace->nr;
41         entry = syscall_nr_to_meta(syscall);
42
43         if (!entry)
44                 goto end;
45
46         if (entry->enter_id != ent->type) {
47                 WARN_ON_ONCE(1);
48                 goto end;
49         }
50
51         ret = trace_seq_printf(s, "%s(", entry->name);
52         if (!ret)
53                 return TRACE_TYPE_PARTIAL_LINE;
54
55         for (i = 0; i < entry->nb_args; i++) {
56                 /* parameter types */
57                 if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) {
58                         ret = trace_seq_printf(s, "%s ", entry->types[i]);
59                         if (!ret)
60                                 return TRACE_TYPE_PARTIAL_LINE;
61                 }
62                 /* parameter values */
63                 ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i],
64                                        trace->args[i],
65                                        i == entry->nb_args - 1 ? ")" : ",");
66                 if (!ret)
67                         return TRACE_TYPE_PARTIAL_LINE;
68         }
69
70 end:
71         trace_seq_printf(s, "\n");
72         return TRACE_TYPE_HANDLED;
73 }
74
75 enum print_line_t
76 print_syscall_exit(struct trace_iterator *iter, int flags)
77 {
78         struct trace_seq *s = &iter->seq;
79         struct trace_entry *ent = iter->ent;
80         struct syscall_trace_exit *trace;
81         int syscall;
82         struct syscall_metadata *entry;
83         int ret;
84
85         trace = (typeof(trace))ent;
86         syscall = trace->nr;
87         entry = syscall_nr_to_meta(syscall);
88
89         if (!entry) {
90                 trace_seq_printf(s, "\n");
91                 return TRACE_TYPE_HANDLED;
92         }
93
94         if (entry->exit_id != ent->type) {
95                 WARN_ON_ONCE(1);
96                 return TRACE_TYPE_UNHANDLED;
97         }
98
99         ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
100                                 trace->ret);
101         if (!ret)
102                 return TRACE_TYPE_PARTIAL_LINE;
103
104         return TRACE_TYPE_HANDLED;
105 }
106
107 void ftrace_syscall_enter(struct pt_regs *regs, long id)
108 {
109         struct syscall_trace_enter *entry;
110         struct syscall_metadata *sys_data;
111         struct ring_buffer_event *event;
112         int size;
113         int syscall_nr;
114
115         syscall_nr = syscall_get_nr(current, regs);
116         if (!test_bit(syscall_nr, enabled_enter_syscalls))
117                 return;
118
119         sys_data = syscall_nr_to_meta(syscall_nr);
120         if (!sys_data)
121                 return;
122
123         size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
124
125         event = trace_current_buffer_lock_reserve(sys_data->enter_id, size,
126                                                         0, 0);
127         if (!event)
128                 return;
129
130         entry = ring_buffer_event_data(event);
131         entry->nr = syscall_nr;
132         syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
133
134         trace_current_buffer_unlock_commit(event, 0, 0);
135         trace_wake_up();
136 }
137
138 void ftrace_syscall_exit(struct pt_regs *regs, long ret)
139 {
140         struct syscall_trace_exit *entry;
141         struct syscall_metadata *sys_data;
142         struct ring_buffer_event *event;
143         int syscall_nr;
144
145         syscall_nr = syscall_get_nr(current, regs);
146         if (!test_bit(syscall_nr, enabled_exit_syscalls))
147                 return;
148
149         sys_data = syscall_nr_to_meta(syscall_nr);
150         if (!sys_data)
151                 return;
152
153         event = trace_current_buffer_lock_reserve(sys_data->exit_id,
154                                 sizeof(*entry), 0, 0);
155         if (!event)
156                 return;
157
158         entry = ring_buffer_event_data(event);
159         entry->nr = syscall_nr;
160         entry->ret = syscall_get_return_value(current, regs);
161
162         trace_current_buffer_unlock_commit(event, 0, 0);
163         trace_wake_up();
164 }
165
166 int reg_event_syscall_enter(void *ptr)
167 {
168         int ret = 0;
169         int num;
170         char *name;
171
172         name = (char *)ptr;
173         num = syscall_name_to_nr(name);
174         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
175                 return -ENOSYS;
176         mutex_lock(&syscall_trace_lock);
177         if (!sys_refcount_enter)
178                 ret = register_trace_syscall_enter(ftrace_syscall_enter);
179         if (ret) {
180                 pr_info("event trace: Could not activate"
181                                 "syscall entry trace point");
182         } else {
183                 set_bit(num, enabled_enter_syscalls);
184                 sys_refcount_enter++;
185         }
186         mutex_unlock(&syscall_trace_lock);
187         return ret;
188 }
189
190 void unreg_event_syscall_enter(void *ptr)
191 {
192         int num;
193         char *name;
194
195         name = (char *)ptr;
196         num = syscall_name_to_nr(name);
197         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
198                 return;
199         mutex_lock(&syscall_trace_lock);
200         sys_refcount_enter--;
201         clear_bit(num, enabled_enter_syscalls);
202         if (!sys_refcount_enter)
203                 unregister_trace_syscall_enter(ftrace_syscall_enter);
204         mutex_unlock(&syscall_trace_lock);
205 }
206
207 int reg_event_syscall_exit(void *ptr)
208 {
209         int ret = 0;
210         int num;
211         char *name;
212
213         name = (char *)ptr;
214         num = syscall_name_to_nr(name);
215         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
216                 return -ENOSYS;
217         mutex_lock(&syscall_trace_lock);
218         if (!sys_refcount_exit)
219                 ret = register_trace_syscall_exit(ftrace_syscall_exit);
220         if (ret) {
221                 pr_info("event trace: Could not activate"
222                                 "syscall exit trace point");
223         } else {
224                 set_bit(num, enabled_exit_syscalls);
225                 sys_refcount_exit++;
226         }
227         mutex_unlock(&syscall_trace_lock);
228         return ret;
229 }
230
231 void unreg_event_syscall_exit(void *ptr)
232 {
233         int num;
234         char *name;
235
236         name = (char *)ptr;
237         num = syscall_name_to_nr(name);
238         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
239                 return;
240         mutex_lock(&syscall_trace_lock);
241         sys_refcount_exit--;
242         clear_bit(num, enabled_exit_syscalls);
243         if (!sys_refcount_exit)
244                 unregister_trace_syscall_exit(ftrace_syscall_exit);
245         mutex_unlock(&syscall_trace_lock);
246 }
247
248 struct trace_event event_syscall_enter = {
249         .trace                  = print_syscall_enter,
250 };
251
252 struct trace_event event_syscall_exit = {
253         .trace                  = print_syscall_exit,
254 };