46c1b977a2cba2195c480a9af1a60415a5619412
[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 <linux/perf_counter.h>
5 #include <asm/syscall.h>
6
7 #include "trace_output.h"
8 #include "trace.h"
9
10 static DEFINE_MUTEX(syscall_trace_lock);
11 static int sys_refcount_enter;
12 static int sys_refcount_exit;
13 static DECLARE_BITMAP(enabled_enter_syscalls, FTRACE_SYSCALL_MAX);
14 static DECLARE_BITMAP(enabled_exit_syscalls, FTRACE_SYSCALL_MAX);
15
16 enum print_line_t
17 print_syscall_enter(struct trace_iterator *iter, int flags)
18 {
19         struct trace_seq *s = &iter->seq;
20         struct trace_entry *ent = iter->ent;
21         struct syscall_trace_enter *trace;
22         struct syscall_metadata *entry;
23         int i, ret, syscall;
24
25         trace = (typeof(trace))ent;
26         syscall = trace->nr;
27         entry = syscall_nr_to_meta(syscall);
28
29         if (!entry)
30                 goto end;
31
32         if (entry->enter_id != ent->type) {
33                 WARN_ON_ONCE(1);
34                 goto end;
35         }
36
37         ret = trace_seq_printf(s, "%s(", entry->name);
38         if (!ret)
39                 return TRACE_TYPE_PARTIAL_LINE;
40
41         for (i = 0; i < entry->nb_args; i++) {
42                 /* parameter types */
43                 if (trace_flags & TRACE_ITER_VERBOSE) {
44                         ret = trace_seq_printf(s, "%s ", entry->types[i]);
45                         if (!ret)
46                                 return TRACE_TYPE_PARTIAL_LINE;
47                 }
48                 /* parameter values */
49                 ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i],
50                                        trace->args[i],
51                                        i == entry->nb_args - 1 ? "" : ", ");
52                 if (!ret)
53                         return TRACE_TYPE_PARTIAL_LINE;
54         }
55
56         ret = trace_seq_putc(s, ')');
57         if (!ret)
58                 return TRACE_TYPE_PARTIAL_LINE;
59
60 end:
61         ret =  trace_seq_putc(s, '\n');
62         if (!ret)
63                 return TRACE_TYPE_PARTIAL_LINE;
64
65         return TRACE_TYPE_HANDLED;
66 }
67
68 enum print_line_t
69 print_syscall_exit(struct trace_iterator *iter, int flags)
70 {
71         struct trace_seq *s = &iter->seq;
72         struct trace_entry *ent = iter->ent;
73         struct syscall_trace_exit *trace;
74         int syscall;
75         struct syscall_metadata *entry;
76         int ret;
77
78         trace = (typeof(trace))ent;
79         syscall = trace->nr;
80         entry = syscall_nr_to_meta(syscall);
81
82         if (!entry) {
83                 trace_seq_printf(s, "\n");
84                 return TRACE_TYPE_HANDLED;
85         }
86
87         if (entry->exit_id != ent->type) {
88                 WARN_ON_ONCE(1);
89                 return TRACE_TYPE_UNHANDLED;
90         }
91
92         ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
93                                 trace->ret);
94         if (!ret)
95                 return TRACE_TYPE_PARTIAL_LINE;
96
97         return TRACE_TYPE_HANDLED;
98 }
99
100 extern char *__bad_type_size(void);
101
102 #define SYSCALL_FIELD(type, name)                                       \
103         sizeof(type) != sizeof(trace.name) ?                            \
104                 __bad_type_size() :                                     \
105                 #type, #name, offsetof(typeof(trace), name), sizeof(trace.name)
106
107 int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s)
108 {
109         int i;
110         int nr;
111         int ret;
112         struct syscall_metadata *entry;
113         struct syscall_trace_enter trace;
114         int offset = offsetof(struct syscall_trace_enter, args);
115
116         nr = syscall_name_to_nr(call->data);
117         entry = syscall_nr_to_meta(nr);
118
119         if (!entry)
120                 return 0;
121
122         ret = trace_seq_printf(s, "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n",
123                                SYSCALL_FIELD(int, nr));
124         if (!ret)
125                 return 0;
126
127         for (i = 0; i < entry->nb_args; i++) {
128                 ret = trace_seq_printf(s, "\tfield:%s %s;", entry->types[i],
129                                         entry->args[i]);
130                 if (!ret)
131                         return 0;
132                 ret = trace_seq_printf(s, "\toffset:%d;\tsize:%zu;\n", offset,
133                                        sizeof(unsigned long));
134                 if (!ret)
135                         return 0;
136                 offset += sizeof(unsigned long);
137         }
138
139         trace_seq_puts(s, "\nprint fmt: \"");
140         for (i = 0; i < entry->nb_args; i++) {
141                 ret = trace_seq_printf(s, "%s: 0x%%0%zulx%s", entry->args[i],
142                                         sizeof(unsigned long),
143                                         i == entry->nb_args - 1 ? "" : ", ");
144                 if (!ret)
145                         return 0;
146         }
147         trace_seq_putc(s, '"');
148
149         for (i = 0; i < entry->nb_args; i++) {
150                 ret = trace_seq_printf(s, ", ((unsigned long)(REC->%s))",
151                                        entry->args[i]);
152                 if (!ret)
153                         return 0;
154         }
155
156         return trace_seq_putc(s, '\n');
157 }
158
159 int syscall_exit_format(struct ftrace_event_call *call, struct trace_seq *s)
160 {
161         int ret;
162         struct syscall_trace_exit trace;
163
164         ret = trace_seq_printf(s,
165                                "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
166                                "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n",
167                                SYSCALL_FIELD(int, nr),
168                                SYSCALL_FIELD(unsigned long, ret));
169         if (!ret)
170                 return 0;
171
172         return trace_seq_printf(s, "\nprint fmt: \"0x%%lx\", REC->ret\n");
173 }
174
175 int syscall_enter_define_fields(struct ftrace_event_call *call)
176 {
177         struct syscall_trace_enter trace;
178         struct syscall_metadata *meta;
179         int ret;
180         int nr;
181         int i;
182         int offset = offsetof(typeof(trace), args);
183
184         nr = syscall_name_to_nr(call->data);
185         meta = syscall_nr_to_meta(nr);
186
187         if (!meta)
188                 return 0;
189
190         ret = trace_define_common_fields(call);
191         if (ret)
192                 return ret;
193
194         for (i = 0; i < meta->nb_args; i++) {
195                 ret = trace_define_field(call, meta->types[i],
196                                          meta->args[i], offset,
197                                          sizeof(unsigned long), 0);
198                 offset += sizeof(unsigned long);
199         }
200
201         return ret;
202 }
203
204 int syscall_exit_define_fields(struct ftrace_event_call *call)
205 {
206         struct syscall_trace_exit trace;
207         int ret;
208
209         ret = trace_define_common_fields(call);
210         if (ret)
211                 return ret;
212
213         ret = trace_define_field(call, SYSCALL_FIELD(unsigned long, ret), 0);
214
215         return ret;
216 }
217
218 void ftrace_syscall_enter(struct pt_regs *regs, long id)
219 {
220         struct syscall_trace_enter *entry;
221         struct syscall_metadata *sys_data;
222         struct ring_buffer_event *event;
223         int size;
224         int syscall_nr;
225
226         syscall_nr = syscall_get_nr(current, regs);
227         if (!test_bit(syscall_nr, enabled_enter_syscalls))
228                 return;
229
230         sys_data = syscall_nr_to_meta(syscall_nr);
231         if (!sys_data)
232                 return;
233
234         size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
235
236         event = trace_current_buffer_lock_reserve(sys_data->enter_id, size,
237                                                         0, 0);
238         if (!event)
239                 return;
240
241         entry = ring_buffer_event_data(event);
242         entry->nr = syscall_nr;
243         syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
244
245         if (!filter_current_check_discard(sys_data->enter_event, entry, event))
246                 trace_current_buffer_unlock_commit(event, 0, 0);
247 }
248
249 void ftrace_syscall_exit(struct pt_regs *regs, long ret)
250 {
251         struct syscall_trace_exit *entry;
252         struct syscall_metadata *sys_data;
253         struct ring_buffer_event *event;
254         int syscall_nr;
255
256         syscall_nr = syscall_get_nr(current, regs);
257         if (!test_bit(syscall_nr, enabled_exit_syscalls))
258                 return;
259
260         sys_data = syscall_nr_to_meta(syscall_nr);
261         if (!sys_data)
262                 return;
263
264         event = trace_current_buffer_lock_reserve(sys_data->exit_id,
265                                 sizeof(*entry), 0, 0);
266         if (!event)
267                 return;
268
269         entry = ring_buffer_event_data(event);
270         entry->nr = syscall_nr;
271         entry->ret = syscall_get_return_value(current, regs);
272
273         if (!filter_current_check_discard(sys_data->exit_event, entry, event))
274                 trace_current_buffer_unlock_commit(event, 0, 0);
275 }
276
277 int reg_event_syscall_enter(void *ptr)
278 {
279         int ret = 0;
280         int num;
281         char *name;
282
283         name = (char *)ptr;
284         num = syscall_name_to_nr(name);
285         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
286                 return -ENOSYS;
287         mutex_lock(&syscall_trace_lock);
288         if (!sys_refcount_enter)
289                 ret = register_trace_syscall_enter(ftrace_syscall_enter);
290         if (ret) {
291                 pr_info("event trace: Could not activate"
292                                 "syscall entry trace point");
293         } else {
294                 set_bit(num, enabled_enter_syscalls);
295                 sys_refcount_enter++;
296         }
297         mutex_unlock(&syscall_trace_lock);
298         return ret;
299 }
300
301 void unreg_event_syscall_enter(void *ptr)
302 {
303         int num;
304         char *name;
305
306         name = (char *)ptr;
307         num = syscall_name_to_nr(name);
308         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
309                 return;
310         mutex_lock(&syscall_trace_lock);
311         sys_refcount_enter--;
312         clear_bit(num, enabled_enter_syscalls);
313         if (!sys_refcount_enter)
314                 unregister_trace_syscall_enter(ftrace_syscall_enter);
315         mutex_unlock(&syscall_trace_lock);
316 }
317
318 int reg_event_syscall_exit(void *ptr)
319 {
320         int ret = 0;
321         int num;
322         char *name;
323
324         name = (char *)ptr;
325         num = syscall_name_to_nr(name);
326         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
327                 return -ENOSYS;
328         mutex_lock(&syscall_trace_lock);
329         if (!sys_refcount_exit)
330                 ret = register_trace_syscall_exit(ftrace_syscall_exit);
331         if (ret) {
332                 pr_info("event trace: Could not activate"
333                                 "syscall exit trace point");
334         } else {
335                 set_bit(num, enabled_exit_syscalls);
336                 sys_refcount_exit++;
337         }
338         mutex_unlock(&syscall_trace_lock);
339         return ret;
340 }
341
342 void unreg_event_syscall_exit(void *ptr)
343 {
344         int num;
345         char *name;
346
347         name = (char *)ptr;
348         num = syscall_name_to_nr(name);
349         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
350                 return;
351         mutex_lock(&syscall_trace_lock);
352         sys_refcount_exit--;
353         clear_bit(num, enabled_exit_syscalls);
354         if (!sys_refcount_exit)
355                 unregister_trace_syscall_exit(ftrace_syscall_exit);
356         mutex_unlock(&syscall_trace_lock);
357 }
358
359 struct trace_event event_syscall_enter = {
360         .trace                  = print_syscall_enter,
361 };
362
363 struct trace_event event_syscall_exit = {
364         .trace                  = print_syscall_exit,
365 };
366
367 #ifdef CONFIG_EVENT_PROFILE
368
369 static DECLARE_BITMAP(enabled_prof_enter_syscalls, FTRACE_SYSCALL_MAX);
370 static DECLARE_BITMAP(enabled_prof_exit_syscalls, FTRACE_SYSCALL_MAX);
371 static int sys_prof_refcount_enter;
372 static int sys_prof_refcount_exit;
373
374 static void prof_syscall_enter(struct pt_regs *regs, long id)
375 {
376         struct syscall_trace_enter *rec;
377         struct syscall_metadata *sys_data;
378         int syscall_nr;
379         int size;
380
381         syscall_nr = syscall_get_nr(current, regs);
382         if (!test_bit(syscall_nr, enabled_prof_enter_syscalls))
383                 return;
384
385         sys_data = syscall_nr_to_meta(syscall_nr);
386         if (!sys_data)
387                 return;
388
389         /* get the size after alignment with the u32 buffer size field */
390         size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
391         size = ALIGN(size + sizeof(u32), sizeof(u64));
392         size -= sizeof(u32);
393
394         do {
395                 char raw_data[size];
396
397                 /* zero the dead bytes from align to not leak stack to user */
398                 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
399
400                 rec = (struct syscall_trace_enter *) raw_data;
401                 tracing_generic_entry_update(&rec->ent, 0, 0);
402                 rec->ent.type = sys_data->enter_id;
403                 rec->nr = syscall_nr;
404                 syscall_get_arguments(current, regs, 0, sys_data->nb_args,
405                                        (unsigned long *)&rec->args);
406                 perf_tpcounter_event(sys_data->enter_id, 0, 1, rec, size);
407         } while(0);
408 }
409
410 int reg_prof_syscall_enter(char *name)
411 {
412         int ret = 0;
413         int num;
414
415         num = syscall_name_to_nr(name);
416         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
417                 return -ENOSYS;
418
419         mutex_lock(&syscall_trace_lock);
420         if (!sys_prof_refcount_enter)
421                 ret = register_trace_syscall_enter(prof_syscall_enter);
422         if (ret) {
423                 pr_info("event trace: Could not activate"
424                                 "syscall entry trace point");
425         } else {
426                 set_bit(num, enabled_prof_enter_syscalls);
427                 sys_prof_refcount_enter++;
428         }
429         mutex_unlock(&syscall_trace_lock);
430         return ret;
431 }
432
433 void unreg_prof_syscall_enter(char *name)
434 {
435         int num;
436
437         num = syscall_name_to_nr(name);
438         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
439                 return;
440
441         mutex_lock(&syscall_trace_lock);
442         sys_prof_refcount_enter--;
443         clear_bit(num, enabled_prof_enter_syscalls);
444         if (!sys_prof_refcount_enter)
445                 unregister_trace_syscall_enter(prof_syscall_enter);
446         mutex_unlock(&syscall_trace_lock);
447 }
448
449 static void prof_syscall_exit(struct pt_regs *regs, long ret)
450 {
451         struct syscall_metadata *sys_data;
452         struct syscall_trace_exit rec;
453         int syscall_nr;
454
455         syscall_nr = syscall_get_nr(current, regs);
456         if (!test_bit(syscall_nr, enabled_prof_exit_syscalls))
457                 return;
458
459         sys_data = syscall_nr_to_meta(syscall_nr);
460         if (!sys_data)
461                 return;
462
463         tracing_generic_entry_update(&rec.ent, 0, 0);
464         rec.ent.type = sys_data->exit_id;
465         rec.nr = syscall_nr;
466         rec.ret = syscall_get_return_value(current, regs);
467
468         perf_tpcounter_event(sys_data->exit_id, 0, 1, &rec, sizeof(rec));
469 }
470
471 int reg_prof_syscall_exit(char *name)
472 {
473         int ret = 0;
474         int num;
475
476         num = syscall_name_to_nr(name);
477         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
478                 return -ENOSYS;
479
480         mutex_lock(&syscall_trace_lock);
481         if (!sys_prof_refcount_exit)
482                 ret = register_trace_syscall_exit(prof_syscall_exit);
483         if (ret) {
484                 pr_info("event trace: Could not activate"
485                                 "syscall entry trace point");
486         } else {
487                 set_bit(num, enabled_prof_exit_syscalls);
488                 sys_prof_refcount_exit++;
489         }
490         mutex_unlock(&syscall_trace_lock);
491         return ret;
492 }
493
494 void unreg_prof_syscall_exit(char *name)
495 {
496         int num;
497
498         num = syscall_name_to_nr(name);
499         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
500                 return;
501
502         mutex_lock(&syscall_trace_lock);
503         sys_prof_refcount_exit--;
504         clear_bit(num, enabled_prof_exit_syscalls);
505         if (!sys_prof_refcount_exit)
506                 unregister_trace_syscall_exit(prof_syscall_exit);
507         mutex_unlock(&syscall_trace_lock);
508 }
509
510 #endif
511
512