blob: a21d366cae46b161387467b6013fdaa33b23c62a [file] [log] [blame]
Frederic Weisbecker47788c52009-04-08 20:40:59 +02001#include <trace/syscall.h>
Josh Stone1c569f02009-08-24 14:43:14 -07002#include <trace/events/syscalls.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09003#include <linux/slab.h>
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +01004#include <linux/kernel.h>
Jason Baronfb34a082009-08-10 16:52:47 -04005#include <linux/ftrace.h>
Ingo Molnarcdd6c482009-09-21 12:02:48 +02006#include <linux/perf_event.h>
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +01007#include <asm/syscall.h>
8
9#include "trace_output.h"
10#include "trace.h"
11
Frederic Weisbecker5be71b62009-03-15 22:10:37 +010012static DEFINE_MUTEX(syscall_trace_lock);
Jason Baronfb34a082009-08-10 16:52:47 -040013static int sys_refcount_enter;
14static int sys_refcount_exit;
Jason Baron57421db2009-08-24 17:40:22 -040015static DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls);
16static DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +010017
Steven Rostedt22392912010-04-21 12:27:06 -040018static int syscall_enter_register(struct ftrace_event_call *event,
19 enum trace_reg type);
20static int syscall_exit_register(struct ftrace_event_call *event,
21 enum trace_reg type);
22
23struct ftrace_event_class event_class_syscall_enter = {
24 .system = "syscalls",
25 .reg = syscall_enter_register
26};
27
28struct ftrace_event_class event_class_syscall_exit = {
29 .system = "syscalls",
30 .reg = syscall_exit_register
Steven Rostedt8f082012010-04-20 10:47:33 -040031};
32
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020033extern unsigned long __start_syscalls_metadata[];
34extern unsigned long __stop_syscalls_metadata[];
35
36static struct syscall_metadata **syscalls_metadata;
37
38static struct syscall_metadata *find_syscall_meta(unsigned long syscall)
39{
40 struct syscall_metadata *start;
41 struct syscall_metadata *stop;
42 char str[KSYM_SYMBOL_LEN];
43
44
45 start = (struct syscall_metadata *)__start_syscalls_metadata;
46 stop = (struct syscall_metadata *)__stop_syscalls_metadata;
47 kallsyms_lookup(syscall, NULL, NULL, NULL, str);
48
49 for ( ; start < stop; start++) {
50 /*
51 * Only compare after the "sys" prefix. Archs that use
52 * syscall wrappers may have syscalls symbols aliases prefixed
53 * with "SyS" instead of "sys", leading to an unwanted
54 * mismatch.
55 */
56 if (start->name && !strcmp(start->name + 3, str + 3))
57 return start;
58 }
59 return NULL;
60}
61
62static struct syscall_metadata *syscall_nr_to_meta(int nr)
63{
64 if (!syscalls_metadata || nr >= NR_syscalls || nr < 0)
65 return NULL;
66
67 return syscalls_metadata[nr];
68}
69
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010070enum print_line_t
71print_syscall_enter(struct trace_iterator *iter, int flags)
72{
73 struct trace_seq *s = &iter->seq;
74 struct trace_entry *ent = iter->ent;
75 struct syscall_trace_enter *trace;
76 struct syscall_metadata *entry;
77 int i, ret, syscall;
78
Jason Baron64c12e02009-08-10 16:52:53 -040079 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010080 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010081 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -040082
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010083 if (!entry)
84 goto end;
85
Lai Jiangshanfcc19432009-12-01 16:23:36 +080086 if (entry->enter_event->id != ent->type) {
Jason Baron64c12e02009-08-10 16:52:53 -040087 WARN_ON_ONCE(1);
88 goto end;
89 }
90
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010091 ret = trace_seq_printf(s, "%s(", entry->name);
92 if (!ret)
93 return TRACE_TYPE_PARTIAL_LINE;
94
95 for (i = 0; i < entry->nb_args; i++) {
96 /* parameter types */
Li Zefanba8b3a42009-08-17 16:55:18 +080097 if (trace_flags & TRACE_ITER_VERBOSE) {
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010098 ret = trace_seq_printf(s, "%s ", entry->types[i]);
99 if (!ret)
100 return TRACE_TYPE_PARTIAL_LINE;
101 }
102 /* parameter values */
Li Zefan4539f072009-08-20 16:13:35 +0800103 ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i],
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100104 trace->args[i],
Li Zefan4539f072009-08-20 16:13:35 +0800105 i == entry->nb_args - 1 ? "" : ", ");
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100106 if (!ret)
107 return TRACE_TYPE_PARTIAL_LINE;
108 }
109
Li Zefan4539f072009-08-20 16:13:35 +0800110 ret = trace_seq_putc(s, ')');
111 if (!ret)
112 return TRACE_TYPE_PARTIAL_LINE;
113
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100114end:
Li Zefan4539f072009-08-20 16:13:35 +0800115 ret = trace_seq_putc(s, '\n');
116 if (!ret)
117 return TRACE_TYPE_PARTIAL_LINE;
118
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100119 return TRACE_TYPE_HANDLED;
120}
121
122enum print_line_t
123print_syscall_exit(struct trace_iterator *iter, int flags)
124{
125 struct trace_seq *s = &iter->seq;
126 struct trace_entry *ent = iter->ent;
127 struct syscall_trace_exit *trace;
128 int syscall;
129 struct syscall_metadata *entry;
130 int ret;
131
Jason Baron64c12e02009-08-10 16:52:53 -0400132 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100133 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100134 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -0400135
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100136 if (!entry) {
137 trace_seq_printf(s, "\n");
138 return TRACE_TYPE_HANDLED;
139 }
140
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800141 if (entry->exit_event->id != ent->type) {
Jason Baron64c12e02009-08-10 16:52:53 -0400142 WARN_ON_ONCE(1);
143 return TRACE_TYPE_UNHANDLED;
144 }
145
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100146 ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
147 trace->ret);
148 if (!ret)
149 return TRACE_TYPE_PARTIAL_LINE;
150
151 return TRACE_TYPE_HANDLED;
152}
153
Li Zefane6971962009-08-19 15:52:25 +0800154extern char *__bad_type_size(void);
155
156#define SYSCALL_FIELD(type, name) \
157 sizeof(type) != sizeof(trace.name) ? \
158 __bad_type_size() : \
Tom Zanussi26a50742009-10-06 01:09:50 -0500159 #type, #name, offsetof(typeof(trace), name), \
160 sizeof(trace.name), is_signed_type(type)
Li Zefane6971962009-08-19 15:52:25 +0800161
Lai Jiangshan50307a42009-12-15 15:39:45 +0800162static
163int __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len)
164{
165 int i;
166 int pos = 0;
167
168 /* When len=0, we just calculate the needed length */
169#define LEN_OR_ZERO (len ? len - pos : 0)
170
171 pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
172 for (i = 0; i < entry->nb_args; i++) {
173 pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx%s",
174 entry->args[i], sizeof(unsigned long),
175 i == entry->nb_args - 1 ? "" : ", ");
176 }
177 pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
178
179 for (i = 0; i < entry->nb_args; i++) {
180 pos += snprintf(buf + pos, LEN_OR_ZERO,
181 ", ((unsigned long)(REC->%s))", entry->args[i]);
182 }
183
184#undef LEN_OR_ZERO
185
186 /* return the length of print_fmt */
187 return pos;
188}
189
190static int set_syscall_print_fmt(struct ftrace_event_call *call)
191{
192 char *print_fmt;
193 int len;
194 struct syscall_metadata *entry = call->data;
195
196 if (entry->enter_event != call) {
197 call->print_fmt = "\"0x%lx\", REC->ret";
198 return 0;
199 }
200
201 /* First: called with 0 length to calculate the needed length */
202 len = __set_enter_print_fmt(entry, NULL, 0);
203
204 print_fmt = kmalloc(len + 1, GFP_KERNEL);
205 if (!print_fmt)
206 return -ENOMEM;
207
208 /* Second: actually write the @print_fmt */
209 __set_enter_print_fmt(entry, print_fmt, len + 1);
210 call->print_fmt = print_fmt;
211
212 return 0;
213}
214
215static void free_syscall_print_fmt(struct ftrace_event_call *call)
216{
217 struct syscall_metadata *entry = call->data;
218
219 if (entry->enter_event == call)
220 kfree(call->print_fmt);
221}
222
Li Zefan540b7b82009-08-19 15:54:51 +0800223int syscall_enter_define_fields(struct ftrace_event_call *call)
224{
225 struct syscall_trace_enter trace;
Lai Jiangshan31c16b12009-12-01 16:23:30 +0800226 struct syscall_metadata *meta = call->data;
Li Zefan540b7b82009-08-19 15:54:51 +0800227 int ret;
Li Zefan540b7b82009-08-19 15:54:51 +0800228 int i;
229 int offset = offsetof(typeof(trace), args);
230
Lai Jiangshan0f1ef512009-11-26 15:49:33 +0800231 ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER);
232 if (ret)
233 return ret;
234
Li Zefan540b7b82009-08-19 15:54:51 +0800235 for (i = 0; i < meta->nb_args; i++) {
Frederic Weisbeckeraeaeae12009-08-27 05:09:51 +0200236 ret = trace_define_field(call, meta->types[i],
237 meta->args[i], offset,
Li Zefan43b51ea2009-08-07 10:33:22 +0800238 sizeof(unsigned long), 0,
239 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800240 offset += sizeof(unsigned long);
241 }
242
243 return ret;
244}
245
246int syscall_exit_define_fields(struct ftrace_event_call *call)
247{
248 struct syscall_trace_exit trace;
249 int ret;
250
Lai Jiangshan0f1ef512009-11-26 15:49:33 +0800251 ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER);
252 if (ret)
253 return ret;
254
Tom Zanussi26a50742009-10-06 01:09:50 -0500255 ret = trace_define_field(call, SYSCALL_FIELD(long, ret),
Li Zefan43b51ea2009-08-07 10:33:22 +0800256 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800257
258 return ret;
259}
260
Steven Rostedt38516ab2010-04-20 17:04:50 -0400261void ftrace_syscall_enter(void *ignore, struct pt_regs *regs, long id)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100262{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100263 struct syscall_trace_enter *entry;
264 struct syscall_metadata *sys_data;
265 struct ring_buffer_event *event;
Steven Rostedte77405a2009-09-02 14:17:06 -0400266 struct ring_buffer *buffer;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100267 int size;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100268 int syscall_nr;
269
270 syscall_nr = syscall_get_nr(current, regs);
Hendrik Bruecknercd0980f2009-08-25 14:50:27 +0200271 if (syscall_nr < 0)
272 return;
Jason Baronfb34a082009-08-10 16:52:47 -0400273 if (!test_bit(syscall_nr, enabled_enter_syscalls))
274 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100275
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100276 sys_data = syscall_nr_to_meta(syscall_nr);
277 if (!sys_data)
278 return;
279
280 size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
281
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800282 event = trace_current_buffer_lock_reserve(&buffer,
283 sys_data->enter_event->id, size, 0, 0);
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100284 if (!event)
285 return;
286
287 entry = ring_buffer_event_data(event);
288 entry->nr = syscall_nr;
289 syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
290
Steven Rostedte77405a2009-09-02 14:17:06 -0400291 if (!filter_current_check_discard(buffer, sys_data->enter_event,
292 entry, event))
293 trace_current_buffer_unlock_commit(buffer, event, 0, 0);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100294}
295
Steven Rostedt38516ab2010-04-20 17:04:50 -0400296void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100297{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100298 struct syscall_trace_exit *entry;
299 struct syscall_metadata *sys_data;
300 struct ring_buffer_event *event;
Steven Rostedte77405a2009-09-02 14:17:06 -0400301 struct ring_buffer *buffer;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100302 int syscall_nr;
303
304 syscall_nr = syscall_get_nr(current, regs);
Hendrik Bruecknercd0980f2009-08-25 14:50:27 +0200305 if (syscall_nr < 0)
306 return;
Jason Baronfb34a082009-08-10 16:52:47 -0400307 if (!test_bit(syscall_nr, enabled_exit_syscalls))
308 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100309
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100310 sys_data = syscall_nr_to_meta(syscall_nr);
311 if (!sys_data)
312 return;
313
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800314 event = trace_current_buffer_lock_reserve(&buffer,
315 sys_data->exit_event->id, sizeof(*entry), 0, 0);
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100316 if (!event)
317 return;
318
319 entry = ring_buffer_event_data(event);
320 entry->nr = syscall_nr;
321 entry->ret = syscall_get_return_value(current, regs);
322
Steven Rostedte77405a2009-09-02 14:17:06 -0400323 if (!filter_current_check_discard(buffer, sys_data->exit_event,
324 entry, event))
325 trace_current_buffer_unlock_commit(buffer, event, 0, 0);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100326}
327
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400328int reg_event_syscall_enter(struct ftrace_event_call *call)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100329{
Jason Baronfb34a082009-08-10 16:52:47 -0400330 int ret = 0;
331 int num;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100332
Lai Jiangshanc252f652009-12-01 16:23:47 +0800333 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400334 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400335 return -ENOSYS;
336 mutex_lock(&syscall_trace_lock);
337 if (!sys_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400338 ret = register_trace_sys_enter(ftrace_syscall_enter, NULL);
Li Zefan3b8e4272009-12-08 11:14:52 +0800339 if (!ret) {
Jason Baronfb34a082009-08-10 16:52:47 -0400340 set_bit(num, enabled_enter_syscalls);
341 sys_refcount_enter++;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100342 }
Jason Baronfb34a082009-08-10 16:52:47 -0400343 mutex_unlock(&syscall_trace_lock);
344 return ret;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100345}
Jason Baronfb34a082009-08-10 16:52:47 -0400346
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400347void unreg_event_syscall_enter(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400348{
349 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400350
Lai Jiangshanc252f652009-12-01 16:23:47 +0800351 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400352 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400353 return;
354 mutex_lock(&syscall_trace_lock);
355 sys_refcount_enter--;
356 clear_bit(num, enabled_enter_syscalls);
357 if (!sys_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400358 unregister_trace_sys_enter(ftrace_syscall_enter, NULL);
Jason Baronfb34a082009-08-10 16:52:47 -0400359 mutex_unlock(&syscall_trace_lock);
360}
361
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400362int reg_event_syscall_exit(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400363{
364 int ret = 0;
365 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400366
Lai Jiangshanc252f652009-12-01 16:23:47 +0800367 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400368 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400369 return -ENOSYS;
370 mutex_lock(&syscall_trace_lock);
371 if (!sys_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400372 ret = register_trace_sys_exit(ftrace_syscall_exit, NULL);
Li Zefan3b8e4272009-12-08 11:14:52 +0800373 if (!ret) {
Jason Baronfb34a082009-08-10 16:52:47 -0400374 set_bit(num, enabled_exit_syscalls);
375 sys_refcount_exit++;
376 }
377 mutex_unlock(&syscall_trace_lock);
378 return ret;
379}
380
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400381void unreg_event_syscall_exit(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400382{
383 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400384
Lai Jiangshanc252f652009-12-01 16:23:47 +0800385 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400386 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400387 return;
388 mutex_lock(&syscall_trace_lock);
389 sys_refcount_exit--;
390 clear_bit(num, enabled_exit_syscalls);
391 if (!sys_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400392 unregister_trace_sys_exit(ftrace_syscall_exit, NULL);
Jason Baronfb34a082009-08-10 16:52:47 -0400393 mutex_unlock(&syscall_trace_lock);
394}
395
Lai Jiangshana1301da2009-12-01 16:23:55 +0800396int init_syscall_trace(struct ftrace_event_call *call)
397{
398 int id;
399
Lai Jiangshan50307a42009-12-15 15:39:45 +0800400 if (set_syscall_print_fmt(call) < 0)
401 return -ENOMEM;
402
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500403 id = trace_event_raw_init(call);
404
405 if (id < 0) {
Lai Jiangshan50307a42009-12-15 15:39:45 +0800406 free_syscall_print_fmt(call);
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500407 return id;
Lai Jiangshan50307a42009-12-15 15:39:45 +0800408 }
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500409
410 return id;
Lai Jiangshana1301da2009-12-01 16:23:55 +0800411}
412
Mike Frysingere7b8e672010-01-26 04:40:03 -0500413unsigned long __init arch_syscall_addr(int nr)
414{
415 return (unsigned long)sys_call_table[nr];
416}
417
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200418int __init init_ftrace_syscalls(void)
419{
420 struct syscall_metadata *meta;
421 unsigned long addr;
422 int i;
423
424 syscalls_metadata = kzalloc(sizeof(*syscalls_metadata) *
425 NR_syscalls, GFP_KERNEL);
426 if (!syscalls_metadata) {
427 WARN_ON(1);
428 return -ENOMEM;
429 }
430
431 for (i = 0; i < NR_syscalls; i++) {
432 addr = arch_syscall_addr(i);
433 meta = find_syscall_meta(addr);
Lai Jiangshanc252f652009-12-01 16:23:47 +0800434 if (!meta)
435 continue;
436
437 meta->syscall_nr = i;
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200438 syscalls_metadata[i] = meta;
439 }
440
441 return 0;
442}
443core_initcall(init_ftrace_syscalls);
444
Li Zefan07b139c2009-12-21 14:27:35 +0800445#ifdef CONFIG_PERF_EVENTS
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200446
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100447static DECLARE_BITMAP(enabled_perf_enter_syscalls, NR_syscalls);
448static DECLARE_BITMAP(enabled_perf_exit_syscalls, NR_syscalls);
449static int sys_perf_refcount_enter;
450static int sys_perf_refcount_exit;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400451
Steven Rostedt38516ab2010-04-20 17:04:50 -0400452static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400453{
454 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200455 struct syscall_trace_enter *rec;
456 unsigned long flags;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400457 int syscall_nr;
Peter Zijlstra4ed7c922009-11-23 11:37:29 +0100458 int rctx;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200459 int size;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400460
461 syscall_nr = syscall_get_nr(current, regs);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100462 if (!test_bit(syscall_nr, enabled_perf_enter_syscalls))
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400463 return;
464
465 sys_data = syscall_nr_to_meta(syscall_nr);
466 if (!sys_data)
467 return;
468
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200469 /* get the size after alignment with the u32 buffer size field */
470 size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
471 size = ALIGN(size + sizeof(u32), sizeof(u64));
472 size -= sizeof(u32);
473
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100474 if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
475 "perf buffer not large enough"))
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200476 return;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200477
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100478 rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size,
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800479 sys_data->enter_event->id, &rctx, &flags);
480 if (!rec)
481 return;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200482
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200483 rec->nr = syscall_nr;
484 syscall_get_arguments(current, regs, 0, sys_data->nb_args,
485 (unsigned long *)&rec->args);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100486 perf_trace_buf_submit(rec, size, rctx, 0, 1, flags, regs);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400487}
488
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100489int perf_sysenter_enable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400490{
491 int ret = 0;
492 int num;
493
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800494 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400495
496 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100497 if (!sys_perf_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400498 ret = register_trace_sys_enter(perf_syscall_enter, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400499 if (ret) {
500 pr_info("event trace: Could not activate"
501 "syscall entry trace point");
502 } else {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100503 set_bit(num, enabled_perf_enter_syscalls);
504 sys_perf_refcount_enter++;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400505 }
506 mutex_unlock(&syscall_trace_lock);
507 return ret;
508}
509
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100510void perf_sysenter_disable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400511{
512 int num;
513
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800514 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400515
516 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100517 sys_perf_refcount_enter--;
518 clear_bit(num, enabled_perf_enter_syscalls);
519 if (!sys_perf_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400520 unregister_trace_sys_enter(perf_syscall_enter, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400521 mutex_unlock(&syscall_trace_lock);
522}
523
Steven Rostedt38516ab2010-04-20 17:04:50 -0400524static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400525{
526 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200527 struct syscall_trace_exit *rec;
528 unsigned long flags;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400529 int syscall_nr;
Peter Zijlstra4ed7c922009-11-23 11:37:29 +0100530 int rctx;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200531 int size;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400532
533 syscall_nr = syscall_get_nr(current, regs);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100534 if (!test_bit(syscall_nr, enabled_perf_exit_syscalls))
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400535 return;
536
537 sys_data = syscall_nr_to_meta(syscall_nr);
538 if (!sys_data)
539 return;
540
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200541 /* We can probably do that at build time */
542 size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64));
543 size -= sizeof(u32);
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200544
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200545 /*
546 * Impossible, but be paranoid with the future
547 * How to put this check outside runtime?
548 */
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100549 if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
550 "exit event has grown above perf buffer size"))
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200551 return;
552
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100553 rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size,
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800554 sys_data->exit_event->id, &rctx, &flags);
555 if (!rec)
556 return;
Frederic Weisbeckerce71b9d2009-11-22 05:26:55 +0100557
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200558 rec->nr = syscall_nr;
559 rec->ret = syscall_get_return_value(current, regs);
560
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100561 perf_trace_buf_submit(rec, size, rctx, 0, 1, flags, regs);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400562}
563
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100564int perf_sysexit_enable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400565{
566 int ret = 0;
567 int num;
568
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800569 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400570
571 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100572 if (!sys_perf_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400573 ret = register_trace_sys_exit(perf_syscall_exit, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400574 if (ret) {
575 pr_info("event trace: Could not activate"
Wenji Huang65746582010-02-24 15:40:22 +0800576 "syscall exit trace point");
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400577 } else {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100578 set_bit(num, enabled_perf_exit_syscalls);
579 sys_perf_refcount_exit++;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400580 }
581 mutex_unlock(&syscall_trace_lock);
582 return ret;
583}
584
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100585void perf_sysexit_disable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400586{
587 int num;
588
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800589 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400590
591 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100592 sys_perf_refcount_exit--;
593 clear_bit(num, enabled_perf_exit_syscalls);
594 if (!sys_perf_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400595 unregister_trace_sys_exit(perf_syscall_exit, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400596 mutex_unlock(&syscall_trace_lock);
597}
598
Li Zefan07b139c2009-12-21 14:27:35 +0800599#endif /* CONFIG_PERF_EVENTS */
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400600
Steven Rostedt22392912010-04-21 12:27:06 -0400601static int syscall_enter_register(struct ftrace_event_call *event,
602 enum trace_reg type)
603{
604 switch (type) {
605 case TRACE_REG_REGISTER:
606 return reg_event_syscall_enter(event);
607 case TRACE_REG_UNREGISTER:
608 unreg_event_syscall_enter(event);
609 return 0;
610
611#ifdef CONFIG_PERF_EVENTS
612 case TRACE_REG_PERF_REGISTER:
613 return perf_sysenter_enable(event);
614 case TRACE_REG_PERF_UNREGISTER:
615 perf_sysenter_disable(event);
616 return 0;
617#endif
618 }
619 return 0;
620}
621
622static int syscall_exit_register(struct ftrace_event_call *event,
623 enum trace_reg type)
624{
625 switch (type) {
626 case TRACE_REG_REGISTER:
627 return reg_event_syscall_exit(event);
628 case TRACE_REG_UNREGISTER:
629 unreg_event_syscall_exit(event);
630 return 0;
631
632#ifdef CONFIG_PERF_EVENTS
633 case TRACE_REG_PERF_REGISTER:
634 return perf_sysexit_enable(event);
635 case TRACE_REG_PERF_UNREGISTER:
636 perf_sysexit_disable(event);
637 return 0;
638#endif
639 }
640 return 0;
641}