Merge branches 'tracing/ftrace', 'tracing/syscalls' and 'linus' into tracing/core
[linux-2.6] / kernel / trace / trace_syscalls.c
1 #include <linux/kernel.h>
2 #include <linux/ftrace.h>
3 #include <asm/syscall.h>
4
5 #include "trace_output.h"
6 #include "trace.h"
7
8 static atomic_t refcount;
9
10 /* Our two options */
11 enum {
12         TRACE_SYSCALLS_OPT_TYPES = 0x1,
13 };
14
15 static struct tracer_opt syscalls_opts[] = {
16         { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) },
17         { }
18 };
19
20 static struct tracer_flags syscalls_flags = {
21         .val = 0, /* By default: no args types */
22         .opts = syscalls_opts
23 };
24
25 enum print_line_t
26 print_syscall_enter(struct trace_iterator *iter, int flags)
27 {
28         struct trace_seq *s = &iter->seq;
29         struct trace_entry *ent = iter->ent;
30         struct syscall_trace_enter *trace;
31         struct syscall_metadata *entry;
32         int i, ret, syscall;
33
34         trace_assign_type(trace, ent);
35
36         syscall = trace->nr;
37
38         entry = syscall_nr_to_meta(syscall);
39         if (!entry)
40                 goto end;
41
42         ret = trace_seq_printf(s, "%s(", entry->name);
43         if (!ret)
44                 return TRACE_TYPE_PARTIAL_LINE;
45
46         for (i = 0; i < entry->nb_args; i++) {
47                 /* parameter types */
48                 if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) {
49                         ret = trace_seq_printf(s, "%s ", entry->types[i]);
50                         if (!ret)
51                                 return TRACE_TYPE_PARTIAL_LINE;
52                 }
53                 /* parameter values */
54                 ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i],
55                                        trace->args[i],
56                                        i == entry->nb_args - 1 ? ")" : ",");
57                 if (!ret)
58                         return TRACE_TYPE_PARTIAL_LINE;
59         }
60
61 end:
62         trace_seq_printf(s, "\n");
63         return TRACE_TYPE_HANDLED;
64 }
65
66 enum print_line_t
67 print_syscall_exit(struct trace_iterator *iter, int flags)
68 {
69         struct trace_seq *s = &iter->seq;
70         struct trace_entry *ent = iter->ent;
71         struct syscall_trace_exit *trace;
72         int syscall;
73         struct syscall_metadata *entry;
74         int ret;
75
76         trace_assign_type(trace, ent);
77
78         syscall = trace->nr;
79
80         entry = syscall_nr_to_meta(syscall);
81         if (!entry) {
82                 trace_seq_printf(s, "\n");
83                 return TRACE_TYPE_HANDLED;
84         }
85
86         ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
87                                 trace->ret);
88         if (!ret)
89                 return TRACE_TYPE_PARTIAL_LINE;
90
91         return TRACE_TYPE_HANDLED;
92 }
93
94 void start_ftrace_syscalls(void)
95 {
96         unsigned long flags;
97         struct task_struct *g, *t;
98
99         if (atomic_inc_return(&refcount) != 1)
100                 goto out;
101
102         arch_init_ftrace_syscalls();
103         read_lock_irqsave(&tasklist_lock, flags);
104
105         do_each_thread(g, t) {
106                 set_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
107         } while_each_thread(g, t);
108
109         read_unlock_irqrestore(&tasklist_lock, flags);
110 out:
111         atomic_dec(&refcount);
112 }
113
114 void stop_ftrace_syscalls(void)
115 {
116         unsigned long flags;
117         struct task_struct *g, *t;
118
119         if (atomic_dec_return(&refcount))
120                 goto out;
121
122         read_lock_irqsave(&tasklist_lock, flags);
123
124         do_each_thread(g, t) {
125                 clear_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
126         } while_each_thread(g, t);
127
128         read_unlock_irqrestore(&tasklist_lock, flags);
129 out:
130         atomic_inc(&refcount);
131 }
132
133 void ftrace_syscall_enter(struct pt_regs *regs)
134 {
135         struct syscall_trace_enter *entry;
136         struct syscall_metadata *sys_data;
137         struct ring_buffer_event *event;
138         int size;
139         int syscall_nr;
140         int cpu;
141
142         syscall_nr = syscall_get_nr(current, regs);
143
144         cpu = raw_smp_processor_id();
145
146         sys_data = syscall_nr_to_meta(syscall_nr);
147         if (!sys_data)
148                 return;
149
150         size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
151
152         event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size,
153                                                         0, 0);
154         if (!event)
155                 return;
156
157         entry = ring_buffer_event_data(event);
158         entry->nr = syscall_nr;
159         syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
160
161         trace_current_buffer_unlock_commit(event, 0, 0);
162         trace_wake_up();
163 }
164
165 void ftrace_syscall_exit(struct pt_regs *regs)
166 {
167         struct syscall_trace_exit *entry;
168         struct syscall_metadata *sys_data;
169         struct ring_buffer_event *event;
170         int syscall_nr;
171         int cpu;
172
173         syscall_nr = syscall_get_nr(current, regs);
174
175         cpu = raw_smp_processor_id();
176
177         sys_data = syscall_nr_to_meta(syscall_nr);
178         if (!sys_data)
179                 return;
180
181         event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT,
182                                 sizeof(*entry), 0, 0);
183         if (!event)
184                 return;
185
186         entry = ring_buffer_event_data(event);
187         entry->nr = syscall_nr;
188         entry->ret = syscall_get_return_value(current, regs);
189
190         trace_current_buffer_unlock_commit(event, 0, 0);
191         trace_wake_up();
192 }
193
194 static int init_syscall_tracer(struct trace_array *tr)
195 {
196         start_ftrace_syscalls();
197
198         return 0;
199 }
200
201 static void reset_syscall_tracer(struct trace_array *tr)
202 {
203         stop_ftrace_syscalls();
204 }
205
206 static struct trace_event syscall_enter_event = {
207         .type           = TRACE_SYSCALL_ENTER,
208         .trace          = print_syscall_enter,
209 };
210
211 static struct trace_event syscall_exit_event = {
212         .type           = TRACE_SYSCALL_EXIT,
213         .trace          = print_syscall_exit,
214 };
215
216 static struct tracer syscall_tracer __read_mostly = {
217         .name           = "syscall",
218         .init           = init_syscall_tracer,
219         .reset          = reset_syscall_tracer,
220         .flags          = &syscalls_flags,
221 };
222
223 __init int register_ftrace_syscalls(void)
224 {
225         int ret;
226
227         ret = register_ftrace_event(&syscall_enter_event);
228         if (!ret) {
229                 printk(KERN_WARNING "event %d failed to register\n",
230                        syscall_enter_event.type);
231                 WARN_ON_ONCE(1);
232         }
233
234         ret = register_ftrace_event(&syscall_exit_event);
235         if (!ret) {
236                 printk(KERN_WARNING "event %d failed to register\n",
237                        syscall_exit_event.type);
238                 WARN_ON_ONCE(1);
239         }
240
241         return register_tracer(&syscall_tracer);
242 }
243 device_initcall(register_ftrace_syscalls);