tracing/filters: Add __field_ext() to TRACE_EVENT
[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                                          FILTER_OTHER);
199                 offset += sizeof(unsigned long);
200         }
201
202         return ret;
203 }
204
205 int syscall_exit_define_fields(struct ftrace_event_call *call)
206 {
207         struct syscall_trace_exit trace;
208         int ret;
209
210         ret = trace_define_common_fields(call);
211         if (ret)
212                 return ret;
213
214         ret = trace_define_field(call, SYSCALL_FIELD(unsigned long, ret), 0,
215                                  FILTER_OTHER);
216
217         return ret;
218 }
219
220 void ftrace_syscall_enter(struct pt_regs *regs, long id)
221 {
222         struct syscall_trace_enter *entry;
223         struct syscall_metadata *sys_data;
224         struct ring_buffer_event *event;
225         int size;
226         int syscall_nr;
227
228         syscall_nr = syscall_get_nr(current, regs);
229         if (!test_bit(syscall_nr, enabled_enter_syscalls))
230                 return;
231
232         sys_data = syscall_nr_to_meta(syscall_nr);
233         if (!sys_data)
234                 return;
235
236         size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
237
238         event = trace_current_buffer_lock_reserve(sys_data->enter_id, size,
239                                                         0, 0);
240         if (!event)
241                 return;
242
243         entry = ring_buffer_event_data(event);
244         entry->nr = syscall_nr;
245         syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
246
247         if (!filter_current_check_discard(sys_data->enter_event, entry, event))
248                 trace_current_buffer_unlock_commit(event, 0, 0);
249 }
250
251 void ftrace_syscall_exit(struct pt_regs *regs, long ret)
252 {
253         struct syscall_trace_exit *entry;
254         struct syscall_metadata *sys_data;
255         struct ring_buffer_event *event;
256         int syscall_nr;
257
258         syscall_nr = syscall_get_nr(current, regs);
259         if (!test_bit(syscall_nr, enabled_exit_syscalls))
260                 return;
261
262         sys_data = syscall_nr_to_meta(syscall_nr);
263         if (!sys_data)
264                 return;
265
266         event = trace_current_buffer_lock_reserve(sys_data->exit_id,
267                                 sizeof(*entry), 0, 0);
268         if (!event)
269                 return;
270
271         entry = ring_buffer_event_data(event);
272         entry->nr = syscall_nr;
273         entry->ret = syscall_get_return_value(current, regs);
274
275         if (!filter_current_check_discard(sys_data->exit_event, entry, event))
276                 trace_current_buffer_unlock_commit(event, 0, 0);
277 }
278
279 int reg_event_syscall_enter(void *ptr)
280 {
281         int ret = 0;
282         int num;
283         char *name;
284
285         name = (char *)ptr;
286         num = syscall_name_to_nr(name);
287         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
288                 return -ENOSYS;
289         mutex_lock(&syscall_trace_lock);
290         if (!sys_refcount_enter)
291                 ret = register_trace_syscall_enter(ftrace_syscall_enter);
292         if (ret) {
293                 pr_info("event trace: Could not activate"
294                                 "syscall entry trace point");
295         } else {
296                 set_bit(num, enabled_enter_syscalls);
297                 sys_refcount_enter++;
298         }
299         mutex_unlock(&syscall_trace_lock);
300         return ret;
301 }
302
303 void unreg_event_syscall_enter(void *ptr)
304 {
305         int num;
306         char *name;
307
308         name = (char *)ptr;
309         num = syscall_name_to_nr(name);
310         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
311                 return;
312         mutex_lock(&syscall_trace_lock);
313         sys_refcount_enter--;
314         clear_bit(num, enabled_enter_syscalls);
315         if (!sys_refcount_enter)
316                 unregister_trace_syscall_enter(ftrace_syscall_enter);
317         mutex_unlock(&syscall_trace_lock);
318 }
319
320 int reg_event_syscall_exit(void *ptr)
321 {
322         int ret = 0;
323         int num;
324         char *name;
325
326         name = (char *)ptr;
327         num = syscall_name_to_nr(name);
328         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
329                 return -ENOSYS;
330         mutex_lock(&syscall_trace_lock);
331         if (!sys_refcount_exit)
332                 ret = register_trace_syscall_exit(ftrace_syscall_exit);
333         if (ret) {
334                 pr_info("event trace: Could not activate"
335                                 "syscall exit trace point");
336         } else {
337                 set_bit(num, enabled_exit_syscalls);
338                 sys_refcount_exit++;
339         }
340         mutex_unlock(&syscall_trace_lock);
341         return ret;
342 }
343
344 void unreg_event_syscall_exit(void *ptr)
345 {
346         int num;
347         char *name;
348
349         name = (char *)ptr;
350         num = syscall_name_to_nr(name);
351         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
352                 return;
353         mutex_lock(&syscall_trace_lock);
354         sys_refcount_exit--;
355         clear_bit(num, enabled_exit_syscalls);
356         if (!sys_refcount_exit)
357                 unregister_trace_syscall_exit(ftrace_syscall_exit);
358         mutex_unlock(&syscall_trace_lock);
359 }
360
361 struct trace_event event_syscall_enter = {
362         .trace                  = print_syscall_enter,
363 };
364
365 struct trace_event event_syscall_exit = {
366         .trace                  = print_syscall_exit,
367 };
368
369 #ifdef CONFIG_EVENT_PROFILE
370
371 static DECLARE_BITMAP(enabled_prof_enter_syscalls, FTRACE_SYSCALL_MAX);
372 static DECLARE_BITMAP(enabled_prof_exit_syscalls, FTRACE_SYSCALL_MAX);
373 static int sys_prof_refcount_enter;
374 static int sys_prof_refcount_exit;
375
376 static void prof_syscall_enter(struct pt_regs *regs, long id)
377 {
378         struct syscall_trace_enter *rec;
379         struct syscall_metadata *sys_data;
380         int syscall_nr;
381         int size;
382
383         syscall_nr = syscall_get_nr(current, regs);
384         if (!test_bit(syscall_nr, enabled_prof_enter_syscalls))
385                 return;
386
387         sys_data = syscall_nr_to_meta(syscall_nr);
388         if (!sys_data)
389                 return;
390
391         /* get the size after alignment with the u32 buffer size field */
392         size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
393         size = ALIGN(size + sizeof(u32), sizeof(u64));
394         size -= sizeof(u32);
395
396         do {
397                 char raw_data[size];
398
399                 /* zero the dead bytes from align to not leak stack to user */
400                 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
401
402                 rec = (struct syscall_trace_enter *) raw_data;
403                 tracing_generic_entry_update(&rec->ent, 0, 0);
404                 rec->ent.type = sys_data->enter_id;
405                 rec->nr = syscall_nr;
406                 syscall_get_arguments(current, regs, 0, sys_data->nb_args,
407                                        (unsigned long *)&rec->args);
408                 perf_tpcounter_event(sys_data->enter_id, 0, 1, rec, size);
409         } while(0);
410 }
411
412 int reg_prof_syscall_enter(char *name)
413 {
414         int ret = 0;
415         int num;
416
417         num = syscall_name_to_nr(name);
418         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
419                 return -ENOSYS;
420
421         mutex_lock(&syscall_trace_lock);
422         if (!sys_prof_refcount_enter)
423                 ret = register_trace_syscall_enter(prof_syscall_enter);
424         if (ret) {
425                 pr_info("event trace: Could not activate"
426                                 "syscall entry trace point");
427         } else {
428                 set_bit(num, enabled_prof_enter_syscalls);
429                 sys_prof_refcount_enter++;
430         }
431         mutex_unlock(&syscall_trace_lock);
432         return ret;
433 }
434
435 void unreg_prof_syscall_enter(char *name)
436 {
437         int num;
438
439         num = syscall_name_to_nr(name);
440         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
441                 return;
442
443         mutex_lock(&syscall_trace_lock);
444         sys_prof_refcount_enter--;
445         clear_bit(num, enabled_prof_enter_syscalls);
446         if (!sys_prof_refcount_enter)
447                 unregister_trace_syscall_enter(prof_syscall_enter);
448         mutex_unlock(&syscall_trace_lock);
449 }
450
451 static void prof_syscall_exit(struct pt_regs *regs, long ret)
452 {
453         struct syscall_metadata *sys_data;
454         struct syscall_trace_exit rec;
455         int syscall_nr;
456
457         syscall_nr = syscall_get_nr(current, regs);
458         if (!test_bit(syscall_nr, enabled_prof_exit_syscalls))
459                 return;
460
461         sys_data = syscall_nr_to_meta(syscall_nr);
462         if (!sys_data)
463                 return;
464
465         tracing_generic_entry_update(&rec.ent, 0, 0);
466         rec.ent.type = sys_data->exit_id;
467         rec.nr = syscall_nr;
468         rec.ret = syscall_get_return_value(current, regs);
469
470         perf_tpcounter_event(sys_data->exit_id, 0, 1, &rec, sizeof(rec));
471 }
472
473 int reg_prof_syscall_exit(char *name)
474 {
475         int ret = 0;
476         int num;
477
478         num = syscall_name_to_nr(name);
479         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
480                 return -ENOSYS;
481
482         mutex_lock(&syscall_trace_lock);
483         if (!sys_prof_refcount_exit)
484                 ret = register_trace_syscall_exit(prof_syscall_exit);
485         if (ret) {
486                 pr_info("event trace: Could not activate"
487                                 "syscall entry trace point");
488         } else {
489                 set_bit(num, enabled_prof_exit_syscalls);
490                 sys_prof_refcount_exit++;
491         }
492         mutex_unlock(&syscall_trace_lock);
493         return ret;
494 }
495
496 void unreg_prof_syscall_exit(char *name)
497 {
498         int num;
499
500         num = syscall_name_to_nr(name);
501         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
502                 return;
503
504         mutex_lock(&syscall_trace_lock);
505         sys_prof_refcount_exit--;
506         clear_bit(num, enabled_prof_exit_syscalls);
507         if (!sys_prof_refcount_exit)
508                 unregister_trace_syscall_exit(prof_syscall_exit);
509         mutex_unlock(&syscall_trace_lock);
510 }
511
512 #endif
513
514