c7ae25ee95d864432f4ecee0a61b92ffc0cc5049
[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_assign_type(trace, ent);
40
41         syscall = trace->nr;
42
43         entry = syscall_nr_to_meta(syscall);
44         if (!entry)
45                 goto end;
46
47         ret = trace_seq_printf(s, "%s(", entry->name);
48         if (!ret)
49                 return TRACE_TYPE_PARTIAL_LINE;
50
51         for (i = 0; i < entry->nb_args; i++) {
52                 /* parameter types */
53                 if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) {
54                         ret = trace_seq_printf(s, "%s ", entry->types[i]);
55                         if (!ret)
56                                 return TRACE_TYPE_PARTIAL_LINE;
57                 }
58                 /* parameter values */
59                 ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i],
60                                        trace->args[i],
61                                        i == entry->nb_args - 1 ? ")" : ",");
62                 if (!ret)
63                         return TRACE_TYPE_PARTIAL_LINE;
64         }
65
66 end:
67         trace_seq_printf(s, "\n");
68         return TRACE_TYPE_HANDLED;
69 }
70
71 enum print_line_t
72 print_syscall_exit(struct trace_iterator *iter, int flags)
73 {
74         struct trace_seq *s = &iter->seq;
75         struct trace_entry *ent = iter->ent;
76         struct syscall_trace_exit *trace;
77         int syscall;
78         struct syscall_metadata *entry;
79         int ret;
80
81         trace_assign_type(trace, ent);
82
83         syscall = trace->nr;
84
85         entry = syscall_nr_to_meta(syscall);
86         if (!entry) {
87                 trace_seq_printf(s, "\n");
88                 return TRACE_TYPE_HANDLED;
89         }
90
91         ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
92                                 trace->ret);
93         if (!ret)
94                 return TRACE_TYPE_PARTIAL_LINE;
95
96         return TRACE_TYPE_HANDLED;
97 }
98
99 void ftrace_syscall_enter(struct pt_regs *regs, long id)
100 {
101         struct syscall_trace_enter *entry;
102         struct syscall_metadata *sys_data;
103         struct ring_buffer_event *event;
104         int size;
105         int syscall_nr;
106
107         syscall_nr = syscall_get_nr(current, regs);
108         if (!test_bit(syscall_nr, enabled_enter_syscalls))
109                 return;
110
111         sys_data = syscall_nr_to_meta(syscall_nr);
112         if (!sys_data)
113                 return;
114
115         size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
116
117         event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size,
118                                                         0, 0);
119         if (!event)
120                 return;
121
122         entry = ring_buffer_event_data(event);
123         entry->nr = syscall_nr;
124         syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
125
126         trace_current_buffer_unlock_commit(event, 0, 0);
127         trace_wake_up();
128 }
129
130 void ftrace_syscall_exit(struct pt_regs *regs, long ret)
131 {
132         struct syscall_trace_exit *entry;
133         struct syscall_metadata *sys_data;
134         struct ring_buffer_event *event;
135         int syscall_nr;
136
137         syscall_nr = syscall_get_nr(current, regs);
138         if (!test_bit(syscall_nr, enabled_exit_syscalls))
139                 return;
140
141         sys_data = syscall_nr_to_meta(syscall_nr);
142         if (!sys_data)
143                 return;
144
145         event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT,
146                                 sizeof(*entry), 0, 0);
147         if (!event)
148                 return;
149
150         entry = ring_buffer_event_data(event);
151         entry->nr = syscall_nr;
152         entry->ret = syscall_get_return_value(current, regs);
153
154         trace_current_buffer_unlock_commit(event, 0, 0);
155         trace_wake_up();
156 }
157
158 int reg_event_syscall_enter(void *ptr)
159 {
160         int ret = 0;
161         int num;
162         char *name;
163
164         name = (char *)ptr;
165         num = syscall_name_to_nr(name);
166         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
167                 return -ENOSYS;
168         mutex_lock(&syscall_trace_lock);
169         if (!sys_refcount_enter)
170                 ret = register_trace_syscall_enter(ftrace_syscall_enter);
171         if (ret) {
172                 pr_info("event trace: Could not activate"
173                                 "syscall entry trace point");
174         } else {
175                 set_bit(num, enabled_enter_syscalls);
176                 sys_refcount_enter++;
177         }
178         mutex_unlock(&syscall_trace_lock);
179         return ret;
180 }
181
182 void unreg_event_syscall_enter(void *ptr)
183 {
184         int num;
185         char *name;
186
187         name = (char *)ptr;
188         num = syscall_name_to_nr(name);
189         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
190                 return;
191         mutex_lock(&syscall_trace_lock);
192         sys_refcount_enter--;
193         clear_bit(num, enabled_enter_syscalls);
194         if (!sys_refcount_enter)
195                 unregister_trace_syscall_enter(ftrace_syscall_enter);
196         mutex_unlock(&syscall_trace_lock);
197 }
198
199 int reg_event_syscall_exit(void *ptr)
200 {
201         int ret = 0;
202         int num;
203         char *name;
204
205         name = (char *)ptr;
206         num = syscall_name_to_nr(name);
207         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
208                 return -ENOSYS;
209         mutex_lock(&syscall_trace_lock);
210         if (!sys_refcount_exit)
211                 ret = register_trace_syscall_exit(ftrace_syscall_exit);
212         if (ret) {
213                 pr_info("event trace: Could not activate"
214                                 "syscall exit trace point");
215         } else {
216                 set_bit(num, enabled_exit_syscalls);
217                 sys_refcount_exit++;
218         }
219         mutex_unlock(&syscall_trace_lock);
220         return ret;
221 }
222
223 void unreg_event_syscall_exit(void *ptr)
224 {
225         int num;
226         char *name;
227
228         name = (char *)ptr;
229         num = syscall_name_to_nr(name);
230         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
231                 return;
232         mutex_lock(&syscall_trace_lock);
233         sys_refcount_exit--;
234         clear_bit(num, enabled_exit_syscalls);
235         if (!sys_refcount_exit)
236                 unregister_trace_syscall_exit(ftrace_syscall_exit);
237         mutex_unlock(&syscall_trace_lock);
238 }
239
240 struct trace_event event_syscall_enter = {
241         .trace                  = print_syscall_enter,
242         .type                   = TRACE_SYSCALL_ENTER
243 };
244
245 struct trace_event event_syscall_exit = {
246         .trace                  = print_syscall_exit,
247         .type                   = TRACE_SYSCALL_EXIT
248 };