blob: c5fc1d8880f6f5a1e47c60409f581fffcb56dd24 [file] [log] [blame]
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +01001#include <linux/kernel.h>
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +01002#include <linux/ftrace.h>
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +01003#include <asm/syscall.h>
4
5#include "trace_output.h"
6#include "trace.h"
7
8static atomic_t refcount;
9
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010010/* Our two options */
11enum {
12 TRACE_SYSCALLS_OPT_TYPES = 0x1,
13};
14
15static struct tracer_opt syscalls_opts[] = {
16 { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) },
17 { }
18};
19
20static struct tracer_flags syscalls_flags = {
21 .val = 0, /* By default: no args types */
22 .opts = syscalls_opts
23};
24
25enum print_line_t
26print_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
61end:
62 trace_seq_printf(s, "\n");
63 return TRACE_TYPE_HANDLED;
64}
65
66enum print_line_t
67print_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
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +010094void start_ftrace_syscalls(void)
95{
96 unsigned long flags;
97 struct task_struct *g, *t;
98
Frederic Weisbeckerac99c582009-03-15 22:10:35 +010099 /* Don't enable the flag on the tasks twice */
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100100 if (atomic_inc_return(&refcount) != 1)
Frederic Weisbeckerac99c582009-03-15 22:10:35 +0100101 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100102
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100103 arch_init_ftrace_syscalls();
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100104 read_lock_irqsave(&tasklist_lock, flags);
105
106 do_each_thread(g, t) {
107 set_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
108 } while_each_thread(g, t);
109
110 read_unlock_irqrestore(&tasklist_lock, flags);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100111}
112
113void stop_ftrace_syscalls(void)
114{
115 unsigned long flags;
116 struct task_struct *g, *t;
117
Frederic Weisbeckerac99c582009-03-15 22:10:35 +0100118 /* There are perhaps still some users */
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100119 if (atomic_dec_return(&refcount))
Frederic Weisbeckerac99c582009-03-15 22:10:35 +0100120 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100121
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);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100129}
130
131void ftrace_syscall_enter(struct pt_regs *regs)
132{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100133 struct syscall_trace_enter *entry;
134 struct syscall_metadata *sys_data;
135 struct ring_buffer_event *event;
136 int size;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100137 int syscall_nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100138 int cpu;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100139
140 syscall_nr = syscall_get_nr(current, regs);
141
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100142 cpu = raw_smp_processor_id();
143
144 sys_data = syscall_nr_to_meta(syscall_nr);
145 if (!sys_data)
146 return;
147
148 size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
149
150 event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size,
151 0, 0);
152 if (!event)
153 return;
154
155 entry = ring_buffer_event_data(event);
156 entry->nr = syscall_nr;
157 syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
158
159 trace_current_buffer_unlock_commit(event, 0, 0);
160 trace_wake_up();
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100161}
162
163void ftrace_syscall_exit(struct pt_regs *regs)
164{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100165 struct syscall_trace_exit *entry;
166 struct syscall_metadata *sys_data;
167 struct ring_buffer_event *event;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100168 int syscall_nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100169 int cpu;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100170
171 syscall_nr = syscall_get_nr(current, regs);
172
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100173 cpu = raw_smp_processor_id();
174
175 sys_data = syscall_nr_to_meta(syscall_nr);
176 if (!sys_data)
177 return;
178
179 event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT,
180 sizeof(*entry), 0, 0);
181 if (!event)
182 return;
183
184 entry = ring_buffer_event_data(event);
185 entry->nr = syscall_nr;
186 entry->ret = syscall_get_return_value(current, regs);
187
188 trace_current_buffer_unlock_commit(event, 0, 0);
189 trace_wake_up();
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100190}
191
192static int init_syscall_tracer(struct trace_array *tr)
193{
194 start_ftrace_syscalls();
195
196 return 0;
197}
198
199static void reset_syscall_tracer(struct trace_array *tr)
200{
201 stop_ftrace_syscalls();
Frederic Weisbeckerac99c582009-03-15 22:10:35 +0100202 tracing_reset_online_cpus(tr);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100203}
204
205static struct trace_event syscall_enter_event = {
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100206 .type = TRACE_SYSCALL_ENTER,
207 .trace = print_syscall_enter,
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100208};
209
210static struct trace_event syscall_exit_event = {
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100211 .type = TRACE_SYSCALL_EXIT,
212 .trace = print_syscall_exit,
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100213};
214
215static struct tracer syscall_tracer __read_mostly = {
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100216 .name = "syscall",
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100217 .init = init_syscall_tracer,
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100218 .reset = reset_syscall_tracer,
219 .flags = &syscalls_flags,
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100220};
221
222__init int register_ftrace_syscalls(void)
223{
224 int ret;
225
226 ret = register_ftrace_event(&syscall_enter_event);
227 if (!ret) {
228 printk(KERN_WARNING "event %d failed to register\n",
229 syscall_enter_event.type);
230 WARN_ON_ONCE(1);
231 }
232
233 ret = register_ftrace_event(&syscall_exit_event);
234 if (!ret) {
235 printk(KERN_WARNING "event %d failed to register\n",
236 syscall_exit_event.type);
237 WARN_ON_ONCE(1);
238 }
239
240 return register_tracer(&syscall_tracer);
241}
242device_initcall(register_ftrace_syscalls);