blob: 4d6d711717f2958a9c502f0e53bbb8a89ee41ff7 [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
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020018extern unsigned long __start_syscalls_metadata[];
19extern unsigned long __stop_syscalls_metadata[];
20
21static struct syscall_metadata **syscalls_metadata;
22
23static struct syscall_metadata *find_syscall_meta(unsigned long syscall)
24{
25 struct syscall_metadata *start;
26 struct syscall_metadata *stop;
27 char str[KSYM_SYMBOL_LEN];
28
29
30 start = (struct syscall_metadata *)__start_syscalls_metadata;
31 stop = (struct syscall_metadata *)__stop_syscalls_metadata;
32 kallsyms_lookup(syscall, NULL, NULL, NULL, str);
33
34 for ( ; start < stop; start++) {
35 /*
36 * Only compare after the "sys" prefix. Archs that use
37 * syscall wrappers may have syscalls symbols aliases prefixed
38 * with "SyS" instead of "sys", leading to an unwanted
39 * mismatch.
40 */
41 if (start->name && !strcmp(start->name + 3, str + 3))
42 return start;
43 }
44 return NULL;
45}
46
47static struct syscall_metadata *syscall_nr_to_meta(int nr)
48{
49 if (!syscalls_metadata || nr >= NR_syscalls || nr < 0)
50 return NULL;
51
52 return syscalls_metadata[nr];
53}
54
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010055enum print_line_t
56print_syscall_enter(struct trace_iterator *iter, int flags)
57{
58 struct trace_seq *s = &iter->seq;
59 struct trace_entry *ent = iter->ent;
60 struct syscall_trace_enter *trace;
61 struct syscall_metadata *entry;
62 int i, ret, syscall;
63
Jason Baron64c12e02009-08-10 16:52:53 -040064 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010065 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010066 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -040067
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010068 if (!entry)
69 goto end;
70
Lai Jiangshanfcc19432009-12-01 16:23:36 +080071 if (entry->enter_event->id != ent->type) {
Jason Baron64c12e02009-08-10 16:52:53 -040072 WARN_ON_ONCE(1);
73 goto end;
74 }
75
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010076 ret = trace_seq_printf(s, "%s(", entry->name);
77 if (!ret)
78 return TRACE_TYPE_PARTIAL_LINE;
79
80 for (i = 0; i < entry->nb_args; i++) {
81 /* parameter types */
Li Zefanba8b3a42009-08-17 16:55:18 +080082 if (trace_flags & TRACE_ITER_VERBOSE) {
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010083 ret = trace_seq_printf(s, "%s ", entry->types[i]);
84 if (!ret)
85 return TRACE_TYPE_PARTIAL_LINE;
86 }
87 /* parameter values */
Li Zefan4539f072009-08-20 16:13:35 +080088 ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i],
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010089 trace->args[i],
Li Zefan4539f072009-08-20 16:13:35 +080090 i == entry->nb_args - 1 ? "" : ", ");
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010091 if (!ret)
92 return TRACE_TYPE_PARTIAL_LINE;
93 }
94
Li Zefan4539f072009-08-20 16:13:35 +080095 ret = trace_seq_putc(s, ')');
96 if (!ret)
97 return TRACE_TYPE_PARTIAL_LINE;
98
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010099end:
Li Zefan4539f072009-08-20 16:13:35 +0800100 ret = trace_seq_putc(s, '\n');
101 if (!ret)
102 return TRACE_TYPE_PARTIAL_LINE;
103
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100104 return TRACE_TYPE_HANDLED;
105}
106
107enum print_line_t
108print_syscall_exit(struct trace_iterator *iter, int flags)
109{
110 struct trace_seq *s = &iter->seq;
111 struct trace_entry *ent = iter->ent;
112 struct syscall_trace_exit *trace;
113 int syscall;
114 struct syscall_metadata *entry;
115 int ret;
116
Jason Baron64c12e02009-08-10 16:52:53 -0400117 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100118 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100119 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -0400120
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100121 if (!entry) {
122 trace_seq_printf(s, "\n");
123 return TRACE_TYPE_HANDLED;
124 }
125
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800126 if (entry->exit_event->id != ent->type) {
Jason Baron64c12e02009-08-10 16:52:53 -0400127 WARN_ON_ONCE(1);
128 return TRACE_TYPE_UNHANDLED;
129 }
130
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100131 ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
132 trace->ret);
133 if (!ret)
134 return TRACE_TYPE_PARTIAL_LINE;
135
136 return TRACE_TYPE_HANDLED;
137}
138
Li Zefane6971962009-08-19 15:52:25 +0800139extern char *__bad_type_size(void);
140
141#define SYSCALL_FIELD(type, name) \
142 sizeof(type) != sizeof(trace.name) ? \
143 __bad_type_size() : \
Tom Zanussi26a50742009-10-06 01:09:50 -0500144 #type, #name, offsetof(typeof(trace), name), \
145 sizeof(trace.name), is_signed_type(type)
Li Zefane6971962009-08-19 15:52:25 +0800146
Lai Jiangshan50307a42009-12-15 15:39:45 +0800147static
148int __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len)
149{
150 int i;
151 int pos = 0;
152
153 /* When len=0, we just calculate the needed length */
154#define LEN_OR_ZERO (len ? len - pos : 0)
155
156 pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
157 for (i = 0; i < entry->nb_args; i++) {
158 pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx%s",
159 entry->args[i], sizeof(unsigned long),
160 i == entry->nb_args - 1 ? "" : ", ");
161 }
162 pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
163
164 for (i = 0; i < entry->nb_args; i++) {
165 pos += snprintf(buf + pos, LEN_OR_ZERO,
166 ", ((unsigned long)(REC->%s))", entry->args[i]);
167 }
168
169#undef LEN_OR_ZERO
170
171 /* return the length of print_fmt */
172 return pos;
173}
174
175static int set_syscall_print_fmt(struct ftrace_event_call *call)
176{
177 char *print_fmt;
178 int len;
179 struct syscall_metadata *entry = call->data;
180
181 if (entry->enter_event != call) {
182 call->print_fmt = "\"0x%lx\", REC->ret";
183 return 0;
184 }
185
186 /* First: called with 0 length to calculate the needed length */
187 len = __set_enter_print_fmt(entry, NULL, 0);
188
189 print_fmt = kmalloc(len + 1, GFP_KERNEL);
190 if (!print_fmt)
191 return -ENOMEM;
192
193 /* Second: actually write the @print_fmt */
194 __set_enter_print_fmt(entry, print_fmt, len + 1);
195 call->print_fmt = print_fmt;
196
197 return 0;
198}
199
200static void free_syscall_print_fmt(struct ftrace_event_call *call)
201{
202 struct syscall_metadata *entry = call->data;
203
204 if (entry->enter_event == call)
205 kfree(call->print_fmt);
206}
207
Li Zefan540b7b82009-08-19 15:54:51 +0800208int syscall_enter_define_fields(struct ftrace_event_call *call)
209{
210 struct syscall_trace_enter trace;
Lai Jiangshan31c16b12009-12-01 16:23:30 +0800211 struct syscall_metadata *meta = call->data;
Li Zefan540b7b82009-08-19 15:54:51 +0800212 int ret;
Li Zefan540b7b82009-08-19 15:54:51 +0800213 int i;
214 int offset = offsetof(typeof(trace), args);
215
Lai Jiangshan0f1ef512009-11-26 15:49:33 +0800216 ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER);
217 if (ret)
218 return ret;
219
Li Zefan540b7b82009-08-19 15:54:51 +0800220 for (i = 0; i < meta->nb_args; i++) {
Frederic Weisbeckeraeaeae12009-08-27 05:09:51 +0200221 ret = trace_define_field(call, meta->types[i],
222 meta->args[i], offset,
Li Zefan43b51ea2009-08-07 10:33:22 +0800223 sizeof(unsigned long), 0,
224 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800225 offset += sizeof(unsigned long);
226 }
227
228 return ret;
229}
230
231int syscall_exit_define_fields(struct ftrace_event_call *call)
232{
233 struct syscall_trace_exit trace;
234 int ret;
235
Lai Jiangshan0f1ef512009-11-26 15:49:33 +0800236 ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER);
237 if (ret)
238 return ret;
239
Tom Zanussi26a50742009-10-06 01:09:50 -0500240 ret = trace_define_field(call, SYSCALL_FIELD(long, ret),
Li Zefan43b51ea2009-08-07 10:33:22 +0800241 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800242
243 return ret;
244}
245
Jason Baronfb34a082009-08-10 16:52:47 -0400246void ftrace_syscall_enter(struct pt_regs *regs, long id)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100247{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100248 struct syscall_trace_enter *entry;
249 struct syscall_metadata *sys_data;
250 struct ring_buffer_event *event;
Steven Rostedte77405a2009-09-02 14:17:06 -0400251 struct ring_buffer *buffer;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100252 int size;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100253 int syscall_nr;
254
255 syscall_nr = syscall_get_nr(current, regs);
Hendrik Bruecknercd0980f2009-08-25 14:50:27 +0200256 if (syscall_nr < 0)
257 return;
Jason Baronfb34a082009-08-10 16:52:47 -0400258 if (!test_bit(syscall_nr, enabled_enter_syscalls))
259 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100260
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100261 sys_data = syscall_nr_to_meta(syscall_nr);
262 if (!sys_data)
263 return;
264
265 size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
266
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800267 event = trace_current_buffer_lock_reserve(&buffer,
268 sys_data->enter_event->id, size, 0, 0);
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100269 if (!event)
270 return;
271
272 entry = ring_buffer_event_data(event);
273 entry->nr = syscall_nr;
274 syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
275
Steven Rostedte77405a2009-09-02 14:17:06 -0400276 if (!filter_current_check_discard(buffer, sys_data->enter_event,
277 entry, event))
278 trace_current_buffer_unlock_commit(buffer, event, 0, 0);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100279}
280
Jason Baronfb34a082009-08-10 16:52:47 -0400281void ftrace_syscall_exit(struct pt_regs *regs, long ret)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100282{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100283 struct syscall_trace_exit *entry;
284 struct syscall_metadata *sys_data;
285 struct ring_buffer_event *event;
Steven Rostedte77405a2009-09-02 14:17:06 -0400286 struct ring_buffer *buffer;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100287 int syscall_nr;
288
289 syscall_nr = syscall_get_nr(current, regs);
Hendrik Bruecknercd0980f2009-08-25 14:50:27 +0200290 if (syscall_nr < 0)
291 return;
Jason Baronfb34a082009-08-10 16:52:47 -0400292 if (!test_bit(syscall_nr, enabled_exit_syscalls))
293 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100294
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100295 sys_data = syscall_nr_to_meta(syscall_nr);
296 if (!sys_data)
297 return;
298
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800299 event = trace_current_buffer_lock_reserve(&buffer,
300 sys_data->exit_event->id, sizeof(*entry), 0, 0);
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100301 if (!event)
302 return;
303
304 entry = ring_buffer_event_data(event);
305 entry->nr = syscall_nr;
306 entry->ret = syscall_get_return_value(current, regs);
307
Steven Rostedte77405a2009-09-02 14:17:06 -0400308 if (!filter_current_check_discard(buffer, sys_data->exit_event,
309 entry, event))
310 trace_current_buffer_unlock_commit(buffer, event, 0, 0);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100311}
312
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400313int reg_event_syscall_enter(struct ftrace_event_call *call)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100314{
Jason Baronfb34a082009-08-10 16:52:47 -0400315 int ret = 0;
316 int num;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100317
Lai Jiangshanc252f652009-12-01 16:23:47 +0800318 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400319 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400320 return -ENOSYS;
321 mutex_lock(&syscall_trace_lock);
322 if (!sys_refcount_enter)
Josh Stone1c569f02009-08-24 14:43:14 -0700323 ret = register_trace_sys_enter(ftrace_syscall_enter);
Li Zefan3b8e4272009-12-08 11:14:52 +0800324 if (!ret) {
Jason Baronfb34a082009-08-10 16:52:47 -0400325 set_bit(num, enabled_enter_syscalls);
326 sys_refcount_enter++;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100327 }
Jason Baronfb34a082009-08-10 16:52:47 -0400328 mutex_unlock(&syscall_trace_lock);
329 return ret;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100330}
Jason Baronfb34a082009-08-10 16:52:47 -0400331
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400332void unreg_event_syscall_enter(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400333{
334 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400335
Lai Jiangshanc252f652009-12-01 16:23:47 +0800336 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400337 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400338 return;
339 mutex_lock(&syscall_trace_lock);
340 sys_refcount_enter--;
341 clear_bit(num, enabled_enter_syscalls);
342 if (!sys_refcount_enter)
Josh Stone1c569f02009-08-24 14:43:14 -0700343 unregister_trace_sys_enter(ftrace_syscall_enter);
Jason Baronfb34a082009-08-10 16:52:47 -0400344 mutex_unlock(&syscall_trace_lock);
345}
346
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400347int reg_event_syscall_exit(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400348{
349 int ret = 0;
350 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400351
Lai Jiangshanc252f652009-12-01 16:23:47 +0800352 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400353 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400354 return -ENOSYS;
355 mutex_lock(&syscall_trace_lock);
356 if (!sys_refcount_exit)
Josh Stone1c569f02009-08-24 14:43:14 -0700357 ret = register_trace_sys_exit(ftrace_syscall_exit);
Li Zefan3b8e4272009-12-08 11:14:52 +0800358 if (!ret) {
Jason Baronfb34a082009-08-10 16:52:47 -0400359 set_bit(num, enabled_exit_syscalls);
360 sys_refcount_exit++;
361 }
362 mutex_unlock(&syscall_trace_lock);
363 return ret;
364}
365
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400366void unreg_event_syscall_exit(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400367{
368 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400369
Lai Jiangshanc252f652009-12-01 16:23:47 +0800370 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400371 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400372 return;
373 mutex_lock(&syscall_trace_lock);
374 sys_refcount_exit--;
375 clear_bit(num, enabled_exit_syscalls);
376 if (!sys_refcount_exit)
Josh Stone1c569f02009-08-24 14:43:14 -0700377 unregister_trace_sys_exit(ftrace_syscall_exit);
Jason Baronfb34a082009-08-10 16:52:47 -0400378 mutex_unlock(&syscall_trace_lock);
379}
380
Lai Jiangshana1301da2009-12-01 16:23:55 +0800381int init_syscall_trace(struct ftrace_event_call *call)
382{
383 int id;
384
Lai Jiangshan50307a42009-12-15 15:39:45 +0800385 if (set_syscall_print_fmt(call) < 0)
386 return -ENOMEM;
387
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500388 id = trace_event_raw_init(call);
389
390 if (id < 0) {
Lai Jiangshan50307a42009-12-15 15:39:45 +0800391 free_syscall_print_fmt(call);
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500392 return id;
Lai Jiangshan50307a42009-12-15 15:39:45 +0800393 }
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500394
395 return id;
Lai Jiangshana1301da2009-12-01 16:23:55 +0800396}
397
Mike Frysingere7b8e672010-01-26 04:40:03 -0500398unsigned long __init arch_syscall_addr(int nr)
399{
400 return (unsigned long)sys_call_table[nr];
401}
402
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200403int __init init_ftrace_syscalls(void)
404{
405 struct syscall_metadata *meta;
406 unsigned long addr;
407 int i;
408
409 syscalls_metadata = kzalloc(sizeof(*syscalls_metadata) *
410 NR_syscalls, GFP_KERNEL);
411 if (!syscalls_metadata) {
412 WARN_ON(1);
413 return -ENOMEM;
414 }
415
416 for (i = 0; i < NR_syscalls; i++) {
417 addr = arch_syscall_addr(i);
418 meta = find_syscall_meta(addr);
Lai Jiangshanc252f652009-12-01 16:23:47 +0800419 if (!meta)
420 continue;
421
422 meta->syscall_nr = i;
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200423 syscalls_metadata[i] = meta;
424 }
425
426 return 0;
427}
428core_initcall(init_ftrace_syscalls);
429
Li Zefan07b139c2009-12-21 14:27:35 +0800430#ifdef CONFIG_PERF_EVENTS
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200431
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100432static DECLARE_BITMAP(enabled_perf_enter_syscalls, NR_syscalls);
433static DECLARE_BITMAP(enabled_perf_exit_syscalls, NR_syscalls);
434static int sys_perf_refcount_enter;
435static int sys_perf_refcount_exit;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400436
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100437static void perf_syscall_enter(struct pt_regs *regs, long id)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400438{
439 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200440 struct syscall_trace_enter *rec;
441 unsigned long flags;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400442 int syscall_nr;
Peter Zijlstra4ed7c922009-11-23 11:37:29 +0100443 int rctx;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200444 int size;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400445
446 syscall_nr = syscall_get_nr(current, regs);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100447 if (!test_bit(syscall_nr, enabled_perf_enter_syscalls))
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400448 return;
449
450 sys_data = syscall_nr_to_meta(syscall_nr);
451 if (!sys_data)
452 return;
453
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200454 /* get the size after alignment with the u32 buffer size field */
455 size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
456 size = ALIGN(size + sizeof(u32), sizeof(u64));
457 size -= sizeof(u32);
458
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100459 if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
460 "perf buffer not large enough"))
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200461 return;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200462
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100463 rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size,
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800464 sys_data->enter_event->id, &rctx, &flags);
465 if (!rec)
466 return;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200467
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200468 rec->nr = syscall_nr;
469 syscall_get_arguments(current, regs, 0, sys_data->nb_args,
470 (unsigned long *)&rec->args);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100471 perf_trace_buf_submit(rec, size, rctx, 0, 1, flags, regs);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400472}
473
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100474int perf_sysenter_enable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400475{
476 int ret = 0;
477 int num;
478
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800479 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400480
481 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100482 if (!sys_perf_refcount_enter)
483 ret = register_trace_sys_enter(perf_syscall_enter);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400484 if (ret) {
485 pr_info("event trace: Could not activate"
486 "syscall entry trace point");
487 } else {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100488 set_bit(num, enabled_perf_enter_syscalls);
489 sys_perf_refcount_enter++;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400490 }
491 mutex_unlock(&syscall_trace_lock);
492 return ret;
493}
494
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100495void perf_sysenter_disable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400496{
497 int num;
498
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800499 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400500
501 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100502 sys_perf_refcount_enter--;
503 clear_bit(num, enabled_perf_enter_syscalls);
504 if (!sys_perf_refcount_enter)
505 unregister_trace_sys_enter(perf_syscall_enter);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400506 mutex_unlock(&syscall_trace_lock);
507}
508
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100509static void perf_syscall_exit(struct pt_regs *regs, long ret)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400510{
511 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200512 struct syscall_trace_exit *rec;
513 unsigned long flags;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400514 int syscall_nr;
Peter Zijlstra4ed7c922009-11-23 11:37:29 +0100515 int rctx;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200516 int size;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400517
518 syscall_nr = syscall_get_nr(current, regs);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100519 if (!test_bit(syscall_nr, enabled_perf_exit_syscalls))
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400520 return;
521
522 sys_data = syscall_nr_to_meta(syscall_nr);
523 if (!sys_data)
524 return;
525
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200526 /* We can probably do that at build time */
527 size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64));
528 size -= sizeof(u32);
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200529
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200530 /*
531 * Impossible, but be paranoid with the future
532 * How to put this check outside runtime?
533 */
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100534 if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
535 "exit event has grown above perf buffer size"))
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200536 return;
537
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100538 rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size,
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800539 sys_data->exit_event->id, &rctx, &flags);
540 if (!rec)
541 return;
Frederic Weisbeckerce71b9d2009-11-22 05:26:55 +0100542
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200543 rec->nr = syscall_nr;
544 rec->ret = syscall_get_return_value(current, regs);
545
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100546 perf_trace_buf_submit(rec, size, rctx, 0, 1, flags, regs);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400547}
548
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100549int perf_sysexit_enable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400550{
551 int ret = 0;
552 int num;
553
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800554 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400555
556 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100557 if (!sys_perf_refcount_exit)
558 ret = register_trace_sys_exit(perf_syscall_exit);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400559 if (ret) {
560 pr_info("event trace: Could not activate"
Wenji Huang65746582010-02-24 15:40:22 +0800561 "syscall exit trace point");
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400562 } else {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100563 set_bit(num, enabled_perf_exit_syscalls);
564 sys_perf_refcount_exit++;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400565 }
566 mutex_unlock(&syscall_trace_lock);
567 return ret;
568}
569
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100570void perf_sysexit_disable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400571{
572 int num;
573
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800574 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400575
576 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100577 sys_perf_refcount_exit--;
578 clear_bit(num, enabled_perf_exit_syscalls);
579 if (!sys_perf_refcount_exit)
580 unregister_trace_sys_exit(perf_syscall_exit);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400581 mutex_unlock(&syscall_trace_lock);
582}
583
Li Zefan07b139c2009-12-21 14:27:35 +0800584#endif /* CONFIG_PERF_EVENTS */
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400585