tracing/syscalls: Fix to print parameter types
[safe/jmp/linux-2.6] / 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 end:
57         trace_seq_printf(s, "\n");
58         return TRACE_TYPE_HANDLED;
59 }
60
61 enum print_line_t
62 print_syscall_exit(struct trace_iterator *iter, int flags)
63 {
64         struct trace_seq *s = &iter->seq;
65         struct trace_entry *ent = iter->ent;
66         struct syscall_trace_exit *trace;
67         int syscall;
68         struct syscall_metadata *entry;
69         int ret;
70
71         trace = (typeof(trace))ent;
72         syscall = trace->nr;
73         entry = syscall_nr_to_meta(syscall);
74
75         if (!entry) {
76                 trace_seq_printf(s, "\n");
77                 return TRACE_TYPE_HANDLED;
78         }
79
80         if (entry->exit_id != ent->type) {
81                 WARN_ON_ONCE(1);
82                 return TRACE_TYPE_UNHANDLED;
83         }
84
85         ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
86                                 trace->ret);
87         if (!ret)
88                 return TRACE_TYPE_PARTIAL_LINE;
89
90         return TRACE_TYPE_HANDLED;
91 }
92
93 int ftrace_format_syscall(struct ftrace_event_call *call, struct trace_seq *s)
94 {
95         int i;
96         int nr;
97         int ret = 0;
98         struct syscall_metadata *entry;
99         int offset = sizeof(struct trace_entry);
100
101         nr = syscall_name_to_nr((char *)call->data);
102         entry = syscall_nr_to_meta(nr);
103
104         if (!entry)
105                 return ret;
106
107         for (i = 0; i < entry->nb_args; i++) {
108                 ret = trace_seq_printf(s, "\tfield:%s %s;", entry->types[i],
109                                         entry->args[i]);
110                 if (!ret)
111                         return 0;
112                 ret = trace_seq_printf(s, "\toffset:%d;\tsize:%lu;\n", offset,
113                                        sizeof(unsigned long));
114                 if (!ret)
115                         return 0;
116                 offset += sizeof(unsigned long);
117         }
118
119         trace_seq_printf(s, "\nprint fmt: \"");
120         for (i = 0; i < entry->nb_args; i++) {
121                 ret = trace_seq_printf(s, "%s: 0x%%0%lulx%s", entry->args[i],
122                                         sizeof(unsigned long),
123                                         i == entry->nb_args - 1 ? "\", " : ", ");
124                 if (!ret)
125                         return 0;
126         }
127
128         for (i = 0; i < entry->nb_args; i++) {
129                 ret = trace_seq_printf(s, "((unsigned long)(REC->%s))%s",
130                                         entry->args[i],
131                                         i == entry->nb_args - 1 ? "\n" : ", ");
132                 if (!ret)
133                         return 0;
134         }
135
136         return ret;
137 }
138
139 void ftrace_syscall_enter(struct pt_regs *regs, long id)
140 {
141         struct syscall_trace_enter *entry;
142         struct syscall_metadata *sys_data;
143         struct ring_buffer_event *event;
144         int size;
145         int syscall_nr;
146
147         syscall_nr = syscall_get_nr(current, regs);
148         if (!test_bit(syscall_nr, enabled_enter_syscalls))
149                 return;
150
151         sys_data = syscall_nr_to_meta(syscall_nr);
152         if (!sys_data)
153                 return;
154
155         size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
156
157         event = trace_current_buffer_lock_reserve(sys_data->enter_id, size,
158                                                         0, 0);
159         if (!event)
160                 return;
161
162         entry = ring_buffer_event_data(event);
163         entry->nr = syscall_nr;
164         syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
165
166         trace_current_buffer_unlock_commit(event, 0, 0);
167         trace_wake_up();
168 }
169
170 void ftrace_syscall_exit(struct pt_regs *regs, long ret)
171 {
172         struct syscall_trace_exit *entry;
173         struct syscall_metadata *sys_data;
174         struct ring_buffer_event *event;
175         int syscall_nr;
176
177         syscall_nr = syscall_get_nr(current, regs);
178         if (!test_bit(syscall_nr, enabled_exit_syscalls))
179                 return;
180
181         sys_data = syscall_nr_to_meta(syscall_nr);
182         if (!sys_data)
183                 return;
184
185         event = trace_current_buffer_lock_reserve(sys_data->exit_id,
186                                 sizeof(*entry), 0, 0);
187         if (!event)
188                 return;
189
190         entry = ring_buffer_event_data(event);
191         entry->nr = syscall_nr;
192         entry->ret = syscall_get_return_value(current, regs);
193
194         trace_current_buffer_unlock_commit(event, 0, 0);
195         trace_wake_up();
196 }
197
198 int reg_event_syscall_enter(void *ptr)
199 {
200         int ret = 0;
201         int num;
202         char *name;
203
204         name = (char *)ptr;
205         num = syscall_name_to_nr(name);
206         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
207                 return -ENOSYS;
208         mutex_lock(&syscall_trace_lock);
209         if (!sys_refcount_enter)
210                 ret = register_trace_syscall_enter(ftrace_syscall_enter);
211         if (ret) {
212                 pr_info("event trace: Could not activate"
213                                 "syscall entry trace point");
214         } else {
215                 set_bit(num, enabled_enter_syscalls);
216                 sys_refcount_enter++;
217         }
218         mutex_unlock(&syscall_trace_lock);
219         return ret;
220 }
221
222 void unreg_event_syscall_enter(void *ptr)
223 {
224         int num;
225         char *name;
226
227         name = (char *)ptr;
228         num = syscall_name_to_nr(name);
229         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
230                 return;
231         mutex_lock(&syscall_trace_lock);
232         sys_refcount_enter--;
233         clear_bit(num, enabled_enter_syscalls);
234         if (!sys_refcount_enter)
235                 unregister_trace_syscall_enter(ftrace_syscall_enter);
236         mutex_unlock(&syscall_trace_lock);
237 }
238
239 int reg_event_syscall_exit(void *ptr)
240 {
241         int ret = 0;
242         int num;
243         char *name;
244
245         name = (char *)ptr;
246         num = syscall_name_to_nr(name);
247         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
248                 return -ENOSYS;
249         mutex_lock(&syscall_trace_lock);
250         if (!sys_refcount_exit)
251                 ret = register_trace_syscall_exit(ftrace_syscall_exit);
252         if (ret) {
253                 pr_info("event trace: Could not activate"
254                                 "syscall exit trace point");
255         } else {
256                 set_bit(num, enabled_exit_syscalls);
257                 sys_refcount_exit++;
258         }
259         mutex_unlock(&syscall_trace_lock);
260         return ret;
261 }
262
263 void unreg_event_syscall_exit(void *ptr)
264 {
265         int num;
266         char *name;
267
268         name = (char *)ptr;
269         num = syscall_name_to_nr(name);
270         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
271                 return;
272         mutex_lock(&syscall_trace_lock);
273         sys_refcount_exit--;
274         clear_bit(num, enabled_exit_syscalls);
275         if (!sys_refcount_exit)
276                 unregister_trace_syscall_exit(ftrace_syscall_exit);
277         mutex_unlock(&syscall_trace_lock);
278 }
279
280 struct trace_event event_syscall_enter = {
281         .trace                  = print_syscall_enter,
282 };
283
284 struct trace_event event_syscall_exit = {
285         .trace                  = print_syscall_exit,
286 };
287
288 #ifdef CONFIG_EVENT_PROFILE
289
290 struct syscall_enter_record {
291         struct trace_entry      entry;
292         unsigned long           args[0];
293 };
294
295 struct syscall_exit_record {
296         struct trace_entry      entry;
297         unsigned long           ret;
298 };
299
300 static DECLARE_BITMAP(enabled_prof_enter_syscalls, FTRACE_SYSCALL_MAX);
301 static DECLARE_BITMAP(enabled_prof_exit_syscalls, FTRACE_SYSCALL_MAX);
302 static int sys_prof_refcount_enter;
303 static int sys_prof_refcount_exit;
304
305 static void prof_syscall_enter(struct pt_regs *regs, long id)
306 {
307         struct syscall_enter_record *rec;
308         struct syscall_metadata *sys_data;
309         int syscall_nr;
310         int size;
311
312         syscall_nr = syscall_get_nr(current, regs);
313         if (!test_bit(syscall_nr, enabled_prof_enter_syscalls))
314                 return;
315
316         sys_data = syscall_nr_to_meta(syscall_nr);
317         if (!sys_data)
318                 return;
319
320         /* get the size after alignment with the u32 buffer size field */
321         size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
322         size = ALIGN(size + sizeof(u32), sizeof(u64));
323         size -= sizeof(u32);
324
325         do {
326                 char raw_data[size];
327
328                 /* zero the dead bytes from align to not leak stack to user */
329                 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
330
331                 rec = (struct syscall_enter_record *) raw_data;
332                 tracing_generic_entry_update(&rec->entry, 0, 0);
333                 rec->entry.type = sys_data->enter_id;
334                 syscall_get_arguments(current, regs, 0, sys_data->nb_args,
335                                        (unsigned long *)&rec->args);
336                 perf_tpcounter_event(sys_data->enter_id, 0, 1, rec, size);
337         } while(0);
338 }
339
340 int reg_prof_syscall_enter(char *name)
341 {
342         int ret = 0;
343         int num;
344
345         num = syscall_name_to_nr(name);
346         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
347                 return -ENOSYS;
348
349         mutex_lock(&syscall_trace_lock);
350         if (!sys_prof_refcount_enter)
351                 ret = register_trace_syscall_enter(prof_syscall_enter);
352         if (ret) {
353                 pr_info("event trace: Could not activate"
354                                 "syscall entry trace point");
355         } else {
356                 set_bit(num, enabled_prof_enter_syscalls);
357                 sys_prof_refcount_enter++;
358         }
359         mutex_unlock(&syscall_trace_lock);
360         return ret;
361 }
362
363 void unreg_prof_syscall_enter(char *name)
364 {
365         int num;
366
367         num = syscall_name_to_nr(name);
368         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
369                 return;
370
371         mutex_lock(&syscall_trace_lock);
372         sys_prof_refcount_enter--;
373         clear_bit(num, enabled_prof_enter_syscalls);
374         if (!sys_prof_refcount_enter)
375                 unregister_trace_syscall_enter(prof_syscall_enter);
376         mutex_unlock(&syscall_trace_lock);
377 }
378
379 static void prof_syscall_exit(struct pt_regs *regs, long ret)
380 {
381         struct syscall_metadata *sys_data;
382         struct syscall_exit_record rec;
383         int syscall_nr;
384
385         syscall_nr = syscall_get_nr(current, regs);
386         if (!test_bit(syscall_nr, enabled_prof_exit_syscalls))
387                 return;
388
389         sys_data = syscall_nr_to_meta(syscall_nr);
390         if (!sys_data)
391                 return;
392
393         tracing_generic_entry_update(&rec.entry, 0, 0);
394         rec.entry.type = sys_data->exit_id;
395         rec.ret = syscall_get_return_value(current, regs);
396
397         perf_tpcounter_event(sys_data->exit_id, 0, 1, &rec, sizeof(rec));
398 }
399
400 int reg_prof_syscall_exit(char *name)
401 {
402         int ret = 0;
403         int num;
404
405         num = syscall_name_to_nr(name);
406         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
407                 return -ENOSYS;
408
409         mutex_lock(&syscall_trace_lock);
410         if (!sys_prof_refcount_exit)
411                 ret = register_trace_syscall_exit(prof_syscall_exit);
412         if (ret) {
413                 pr_info("event trace: Could not activate"
414                                 "syscall entry trace point");
415         } else {
416                 set_bit(num, enabled_prof_exit_syscalls);
417                 sys_prof_refcount_exit++;
418         }
419         mutex_unlock(&syscall_trace_lock);
420         return ret;
421 }
422
423 void unreg_prof_syscall_exit(char *name)
424 {
425         int num;
426
427         num = syscall_name_to_nr(name);
428         if (num < 0 || num >= FTRACE_SYSCALL_MAX)
429                 return;
430
431         mutex_lock(&syscall_trace_lock);
432         sys_prof_refcount_exit--;
433         clear_bit(num, enabled_prof_exit_syscalls);
434         if (!sys_prof_refcount_exit)
435                 unregister_trace_syscall_exit(prof_syscall_exit);
436         mutex_unlock(&syscall_trace_lock);
437 }
438
439 #endif
440
441