blob: cceccf0d2e91a2b5260a6f3d5b62a101893178c1 [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
Steven Rostedt2e33af02010-04-22 10:35:55 -040023static int syscall_enter_define_fields(struct ftrace_event_call *call);
24static int syscall_exit_define_fields(struct ftrace_event_call *call);
25
26static struct list_head *
27syscall_get_enter_fields(struct ftrace_event_call *call)
28{
29 struct syscall_metadata *entry = call->data;
30
31 return &entry->enter_fields;
32}
33
34static struct list_head *
35syscall_get_exit_fields(struct ftrace_event_call *call)
36{
37 struct syscall_metadata *entry = call->data;
38
39 return &entry->exit_fields;
40}
41
Steven Rostedt22392912010-04-21 12:27:06 -040042struct ftrace_event_class event_class_syscall_enter = {
43 .system = "syscalls",
Steven Rostedt2e33af02010-04-22 10:35:55 -040044 .reg = syscall_enter_register,
45 .define_fields = syscall_enter_define_fields,
46 .get_fields = syscall_get_enter_fields,
Steven Rostedt22392912010-04-21 12:27:06 -040047};
48
49struct ftrace_event_class event_class_syscall_exit = {
50 .system = "syscalls",
Steven Rostedt2e33af02010-04-22 10:35:55 -040051 .reg = syscall_exit_register,
52 .define_fields = syscall_exit_define_fields,
53 .get_fields = syscall_get_exit_fields,
Steven Rostedt8f082012010-04-20 10:47:33 -040054};
55
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020056extern unsigned long __start_syscalls_metadata[];
57extern unsigned long __stop_syscalls_metadata[];
58
59static struct syscall_metadata **syscalls_metadata;
60
61static struct syscall_metadata *find_syscall_meta(unsigned long syscall)
62{
63 struct syscall_metadata *start;
64 struct syscall_metadata *stop;
65 char str[KSYM_SYMBOL_LEN];
66
67
68 start = (struct syscall_metadata *)__start_syscalls_metadata;
69 stop = (struct syscall_metadata *)__stop_syscalls_metadata;
70 kallsyms_lookup(syscall, NULL, NULL, NULL, str);
71
72 for ( ; start < stop; start++) {
73 /*
74 * Only compare after the "sys" prefix. Archs that use
75 * syscall wrappers may have syscalls symbols aliases prefixed
76 * with "SyS" instead of "sys", leading to an unwanted
77 * mismatch.
78 */
79 if (start->name && !strcmp(start->name + 3, str + 3))
80 return start;
81 }
82 return NULL;
83}
84
85static struct syscall_metadata *syscall_nr_to_meta(int nr)
86{
87 if (!syscalls_metadata || nr >= NR_syscalls || nr < 0)
88 return NULL;
89
90 return syscalls_metadata[nr];
91}
92
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010093enum print_line_t
94print_syscall_enter(struct trace_iterator *iter, int flags)
95{
96 struct trace_seq *s = &iter->seq;
97 struct trace_entry *ent = iter->ent;
98 struct syscall_trace_enter *trace;
99 struct syscall_metadata *entry;
100 int i, ret, syscall;
101
Jason Baron64c12e02009-08-10 16:52:53 -0400102 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100103 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100104 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -0400105
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100106 if (!entry)
107 goto end;
108
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800109 if (entry->enter_event->id != ent->type) {
Jason Baron64c12e02009-08-10 16:52:53 -0400110 WARN_ON_ONCE(1);
111 goto end;
112 }
113
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100114 ret = trace_seq_printf(s, "%s(", entry->name);
115 if (!ret)
116 return TRACE_TYPE_PARTIAL_LINE;
117
118 for (i = 0; i < entry->nb_args; i++) {
119 /* parameter types */
Li Zefanba8b3a42009-08-17 16:55:18 +0800120 if (trace_flags & TRACE_ITER_VERBOSE) {
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100121 ret = trace_seq_printf(s, "%s ", entry->types[i]);
122 if (!ret)
123 return TRACE_TYPE_PARTIAL_LINE;
124 }
125 /* parameter values */
Li Zefan4539f072009-08-20 16:13:35 +0800126 ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i],
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100127 trace->args[i],
Li Zefan4539f072009-08-20 16:13:35 +0800128 i == entry->nb_args - 1 ? "" : ", ");
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100129 if (!ret)
130 return TRACE_TYPE_PARTIAL_LINE;
131 }
132
Li Zefan4539f072009-08-20 16:13:35 +0800133 ret = trace_seq_putc(s, ')');
134 if (!ret)
135 return TRACE_TYPE_PARTIAL_LINE;
136
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100137end:
Li Zefan4539f072009-08-20 16:13:35 +0800138 ret = trace_seq_putc(s, '\n');
139 if (!ret)
140 return TRACE_TYPE_PARTIAL_LINE;
141
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100142 return TRACE_TYPE_HANDLED;
143}
144
145enum print_line_t
146print_syscall_exit(struct trace_iterator *iter, int flags)
147{
148 struct trace_seq *s = &iter->seq;
149 struct trace_entry *ent = iter->ent;
150 struct syscall_trace_exit *trace;
151 int syscall;
152 struct syscall_metadata *entry;
153 int ret;
154
Jason Baron64c12e02009-08-10 16:52:53 -0400155 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100156 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100157 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -0400158
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100159 if (!entry) {
160 trace_seq_printf(s, "\n");
161 return TRACE_TYPE_HANDLED;
162 }
163
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800164 if (entry->exit_event->id != ent->type) {
Jason Baron64c12e02009-08-10 16:52:53 -0400165 WARN_ON_ONCE(1);
166 return TRACE_TYPE_UNHANDLED;
167 }
168
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100169 ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
170 trace->ret);
171 if (!ret)
172 return TRACE_TYPE_PARTIAL_LINE;
173
174 return TRACE_TYPE_HANDLED;
175}
176
Li Zefane6971962009-08-19 15:52:25 +0800177extern char *__bad_type_size(void);
178
179#define SYSCALL_FIELD(type, name) \
180 sizeof(type) != sizeof(trace.name) ? \
181 __bad_type_size() : \
Tom Zanussi26a50742009-10-06 01:09:50 -0500182 #type, #name, offsetof(typeof(trace), name), \
183 sizeof(trace.name), is_signed_type(type)
Li Zefane6971962009-08-19 15:52:25 +0800184
Lai Jiangshan50307a42009-12-15 15:39:45 +0800185static
186int __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len)
187{
188 int i;
189 int pos = 0;
190
191 /* When len=0, we just calculate the needed length */
192#define LEN_OR_ZERO (len ? len - pos : 0)
193
194 pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
195 for (i = 0; i < entry->nb_args; i++) {
196 pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx%s",
197 entry->args[i], sizeof(unsigned long),
198 i == entry->nb_args - 1 ? "" : ", ");
199 }
200 pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
201
202 for (i = 0; i < entry->nb_args; i++) {
203 pos += snprintf(buf + pos, LEN_OR_ZERO,
204 ", ((unsigned long)(REC->%s))", entry->args[i]);
205 }
206
207#undef LEN_OR_ZERO
208
209 /* return the length of print_fmt */
210 return pos;
211}
212
213static int set_syscall_print_fmt(struct ftrace_event_call *call)
214{
215 char *print_fmt;
216 int len;
217 struct syscall_metadata *entry = call->data;
218
219 if (entry->enter_event != call) {
220 call->print_fmt = "\"0x%lx\", REC->ret";
221 return 0;
222 }
223
224 /* First: called with 0 length to calculate the needed length */
225 len = __set_enter_print_fmt(entry, NULL, 0);
226
227 print_fmt = kmalloc(len + 1, GFP_KERNEL);
228 if (!print_fmt)
229 return -ENOMEM;
230
231 /* Second: actually write the @print_fmt */
232 __set_enter_print_fmt(entry, print_fmt, len + 1);
233 call->print_fmt = print_fmt;
234
235 return 0;
236}
237
238static void free_syscall_print_fmt(struct ftrace_event_call *call)
239{
240 struct syscall_metadata *entry = call->data;
241
242 if (entry->enter_event == call)
243 kfree(call->print_fmt);
244}
245
Steven Rostedt2e33af02010-04-22 10:35:55 -0400246static int syscall_enter_define_fields(struct ftrace_event_call *call)
Li Zefan540b7b82009-08-19 15:54:51 +0800247{
248 struct syscall_trace_enter trace;
Lai Jiangshan31c16b12009-12-01 16:23:30 +0800249 struct syscall_metadata *meta = call->data;
Li Zefan540b7b82009-08-19 15:54:51 +0800250 int ret;
Li Zefan540b7b82009-08-19 15:54:51 +0800251 int i;
252 int offset = offsetof(typeof(trace), args);
253
Lai Jiangshan0f1ef512009-11-26 15:49:33 +0800254 ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER);
255 if (ret)
256 return ret;
257
Li Zefan540b7b82009-08-19 15:54:51 +0800258 for (i = 0; i < meta->nb_args; i++) {
Frederic Weisbeckeraeaeae12009-08-27 05:09:51 +0200259 ret = trace_define_field(call, meta->types[i],
260 meta->args[i], offset,
Li Zefan43b51ea2009-08-07 10:33:22 +0800261 sizeof(unsigned long), 0,
262 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800263 offset += sizeof(unsigned long);
264 }
265
266 return ret;
267}
268
Steven Rostedt2e33af02010-04-22 10:35:55 -0400269static int syscall_exit_define_fields(struct ftrace_event_call *call)
Li Zefan540b7b82009-08-19 15:54:51 +0800270{
271 struct syscall_trace_exit trace;
272 int ret;
273
Lai Jiangshan0f1ef512009-11-26 15:49:33 +0800274 ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER);
275 if (ret)
276 return ret;
277
Tom Zanussi26a50742009-10-06 01:09:50 -0500278 ret = trace_define_field(call, SYSCALL_FIELD(long, ret),
Li Zefan43b51ea2009-08-07 10:33:22 +0800279 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800280
281 return ret;
282}
283
Steven Rostedt38516ab2010-04-20 17:04:50 -0400284void ftrace_syscall_enter(void *ignore, struct pt_regs *regs, long id)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100285{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100286 struct syscall_trace_enter *entry;
287 struct syscall_metadata *sys_data;
288 struct ring_buffer_event *event;
Steven Rostedte77405a2009-09-02 14:17:06 -0400289 struct ring_buffer *buffer;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100290 int size;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100291 int syscall_nr;
292
293 syscall_nr = syscall_get_nr(current, regs);
Hendrik Bruecknercd0980f2009-08-25 14:50:27 +0200294 if (syscall_nr < 0)
295 return;
Jason Baronfb34a082009-08-10 16:52:47 -0400296 if (!test_bit(syscall_nr, enabled_enter_syscalls))
297 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100298
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100299 sys_data = syscall_nr_to_meta(syscall_nr);
300 if (!sys_data)
301 return;
302
303 size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
304
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800305 event = trace_current_buffer_lock_reserve(&buffer,
306 sys_data->enter_event->id, size, 0, 0);
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100307 if (!event)
308 return;
309
310 entry = ring_buffer_event_data(event);
311 entry->nr = syscall_nr;
312 syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
313
Steven Rostedte77405a2009-09-02 14:17:06 -0400314 if (!filter_current_check_discard(buffer, sys_data->enter_event,
315 entry, event))
316 trace_current_buffer_unlock_commit(buffer, event, 0, 0);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100317}
318
Steven Rostedt38516ab2010-04-20 17:04:50 -0400319void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100320{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100321 struct syscall_trace_exit *entry;
322 struct syscall_metadata *sys_data;
323 struct ring_buffer_event *event;
Steven Rostedte77405a2009-09-02 14:17:06 -0400324 struct ring_buffer *buffer;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100325 int syscall_nr;
326
327 syscall_nr = syscall_get_nr(current, regs);
Hendrik Bruecknercd0980f2009-08-25 14:50:27 +0200328 if (syscall_nr < 0)
329 return;
Jason Baronfb34a082009-08-10 16:52:47 -0400330 if (!test_bit(syscall_nr, enabled_exit_syscalls))
331 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100332
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100333 sys_data = syscall_nr_to_meta(syscall_nr);
334 if (!sys_data)
335 return;
336
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800337 event = trace_current_buffer_lock_reserve(&buffer,
338 sys_data->exit_event->id, sizeof(*entry), 0, 0);
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100339 if (!event)
340 return;
341
342 entry = ring_buffer_event_data(event);
343 entry->nr = syscall_nr;
344 entry->ret = syscall_get_return_value(current, regs);
345
Steven Rostedte77405a2009-09-02 14:17:06 -0400346 if (!filter_current_check_discard(buffer, sys_data->exit_event,
347 entry, event))
348 trace_current_buffer_unlock_commit(buffer, event, 0, 0);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100349}
350
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400351int reg_event_syscall_enter(struct ftrace_event_call *call)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100352{
Jason Baronfb34a082009-08-10 16:52:47 -0400353 int ret = 0;
354 int num;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100355
Lai Jiangshanc252f652009-12-01 16:23:47 +0800356 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400357 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400358 return -ENOSYS;
359 mutex_lock(&syscall_trace_lock);
360 if (!sys_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400361 ret = register_trace_sys_enter(ftrace_syscall_enter, NULL);
Li Zefan3b8e4272009-12-08 11:14:52 +0800362 if (!ret) {
Jason Baronfb34a082009-08-10 16:52:47 -0400363 set_bit(num, enabled_enter_syscalls);
364 sys_refcount_enter++;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100365 }
Jason Baronfb34a082009-08-10 16:52:47 -0400366 mutex_unlock(&syscall_trace_lock);
367 return ret;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100368}
Jason Baronfb34a082009-08-10 16:52:47 -0400369
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400370void unreg_event_syscall_enter(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400371{
372 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400373
Lai Jiangshanc252f652009-12-01 16:23:47 +0800374 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400375 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400376 return;
377 mutex_lock(&syscall_trace_lock);
378 sys_refcount_enter--;
379 clear_bit(num, enabled_enter_syscalls);
380 if (!sys_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400381 unregister_trace_sys_enter(ftrace_syscall_enter, NULL);
Jason Baronfb34a082009-08-10 16:52:47 -0400382 mutex_unlock(&syscall_trace_lock);
383}
384
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400385int reg_event_syscall_exit(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400386{
387 int ret = 0;
388 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400389
Lai Jiangshanc252f652009-12-01 16:23:47 +0800390 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400391 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400392 return -ENOSYS;
393 mutex_lock(&syscall_trace_lock);
394 if (!sys_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400395 ret = register_trace_sys_exit(ftrace_syscall_exit, NULL);
Li Zefan3b8e4272009-12-08 11:14:52 +0800396 if (!ret) {
Jason Baronfb34a082009-08-10 16:52:47 -0400397 set_bit(num, enabled_exit_syscalls);
398 sys_refcount_exit++;
399 }
400 mutex_unlock(&syscall_trace_lock);
401 return ret;
402}
403
Masami Hiramatsubd1a5c82009-08-13 16:34:53 -0400404void unreg_event_syscall_exit(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400405{
406 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400407
Lai Jiangshanc252f652009-12-01 16:23:47 +0800408 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baron57421db2009-08-24 17:40:22 -0400409 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400410 return;
411 mutex_lock(&syscall_trace_lock);
412 sys_refcount_exit--;
413 clear_bit(num, enabled_exit_syscalls);
414 if (!sys_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400415 unregister_trace_sys_exit(ftrace_syscall_exit, NULL);
Jason Baronfb34a082009-08-10 16:52:47 -0400416 mutex_unlock(&syscall_trace_lock);
417}
418
Lai Jiangshana1301da2009-12-01 16:23:55 +0800419int init_syscall_trace(struct ftrace_event_call *call)
420{
421 int id;
422
Lai Jiangshan50307a42009-12-15 15:39:45 +0800423 if (set_syscall_print_fmt(call) < 0)
424 return -ENOMEM;
425
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500426 id = trace_event_raw_init(call);
427
428 if (id < 0) {
Lai Jiangshan50307a42009-12-15 15:39:45 +0800429 free_syscall_print_fmt(call);
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500430 return id;
Lai Jiangshan50307a42009-12-15 15:39:45 +0800431 }
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500432
433 return id;
Lai Jiangshana1301da2009-12-01 16:23:55 +0800434}
435
Mike Frysingere7b8e672010-01-26 04:40:03 -0500436unsigned long __init arch_syscall_addr(int nr)
437{
438 return (unsigned long)sys_call_table[nr];
439}
440
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200441int __init init_ftrace_syscalls(void)
442{
443 struct syscall_metadata *meta;
444 unsigned long addr;
445 int i;
446
447 syscalls_metadata = kzalloc(sizeof(*syscalls_metadata) *
448 NR_syscalls, GFP_KERNEL);
449 if (!syscalls_metadata) {
450 WARN_ON(1);
451 return -ENOMEM;
452 }
453
454 for (i = 0; i < NR_syscalls; i++) {
455 addr = arch_syscall_addr(i);
456 meta = find_syscall_meta(addr);
Lai Jiangshanc252f652009-12-01 16:23:47 +0800457 if (!meta)
458 continue;
459
460 meta->syscall_nr = i;
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200461 syscalls_metadata[i] = meta;
462 }
463
464 return 0;
465}
466core_initcall(init_ftrace_syscalls);
467
Li Zefan07b139c2009-12-21 14:27:35 +0800468#ifdef CONFIG_PERF_EVENTS
Frederic Weisbecker19007a672009-08-11 20:22:53 +0200469
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100470static DECLARE_BITMAP(enabled_perf_enter_syscalls, NR_syscalls);
471static DECLARE_BITMAP(enabled_perf_exit_syscalls, NR_syscalls);
472static int sys_perf_refcount_enter;
473static int sys_perf_refcount_exit;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400474
Steven Rostedt38516ab2010-04-20 17:04:50 -0400475static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400476{
477 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200478 struct syscall_trace_enter *rec;
479 unsigned long flags;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400480 int syscall_nr;
Peter Zijlstra4ed7c922009-11-23 11:37:29 +0100481 int rctx;
Frederic Weisbecker19007a672009-08-11 20:22:53 +0200482 int size;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400483
484 syscall_nr = syscall_get_nr(current, regs);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100485 if (!test_bit(syscall_nr, enabled_perf_enter_syscalls))
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400486 return;
487
488 sys_data = syscall_nr_to_meta(syscall_nr);
489 if (!sys_data)
490 return;
491
Frederic Weisbecker19007a672009-08-11 20:22:53 +0200492 /* get the size after alignment with the u32 buffer size field */
493 size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
494 size = ALIGN(size + sizeof(u32), sizeof(u64));
495 size -= sizeof(u32);
496
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100497 if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
498 "perf buffer not large enough"))
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200499 return;
Frederic Weisbecker19007a672009-08-11 20:22:53 +0200500
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100501 rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size,
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800502 sys_data->enter_event->id, &rctx, &flags);
503 if (!rec)
504 return;
Frederic Weisbecker19007a672009-08-11 20:22:53 +0200505
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200506 rec->nr = syscall_nr;
507 syscall_get_arguments(current, regs, 0, sys_data->nb_args,
508 (unsigned long *)&rec->args);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100509 perf_trace_buf_submit(rec, size, rctx, 0, 1, flags, regs);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400510}
511
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100512int perf_sysenter_enable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400513{
514 int ret = 0;
515 int num;
516
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800517 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400518
519 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100520 if (!sys_perf_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400521 ret = register_trace_sys_enter(perf_syscall_enter, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400522 if (ret) {
523 pr_info("event trace: Could not activate"
524 "syscall entry trace point");
525 } else {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100526 set_bit(num, enabled_perf_enter_syscalls);
527 sys_perf_refcount_enter++;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400528 }
529 mutex_unlock(&syscall_trace_lock);
530 return ret;
531}
532
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100533void perf_sysenter_disable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400534{
535 int num;
536
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800537 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400538
539 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100540 sys_perf_refcount_enter--;
541 clear_bit(num, enabled_perf_enter_syscalls);
542 if (!sys_perf_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400543 unregister_trace_sys_enter(perf_syscall_enter, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400544 mutex_unlock(&syscall_trace_lock);
545}
546
Steven Rostedt38516ab2010-04-20 17:04:50 -0400547static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400548{
549 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200550 struct syscall_trace_exit *rec;
551 unsigned long flags;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400552 int syscall_nr;
Peter Zijlstra4ed7c922009-11-23 11:37:29 +0100553 int rctx;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200554 int size;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400555
556 syscall_nr = syscall_get_nr(current, regs);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100557 if (!test_bit(syscall_nr, enabled_perf_exit_syscalls))
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400558 return;
559
560 sys_data = syscall_nr_to_meta(syscall_nr);
561 if (!sys_data)
562 return;
563
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200564 /* We can probably do that at build time */
565 size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64));
566 size -= sizeof(u32);
Frederic Weisbecker19007a672009-08-11 20:22:53 +0200567
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200568 /*
569 * Impossible, but be paranoid with the future
570 * How to put this check outside runtime?
571 */
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100572 if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
573 "exit event has grown above perf buffer size"))
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200574 return;
575
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100576 rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size,
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800577 sys_data->exit_event->id, &rctx, &flags);
578 if (!rec)
579 return;
Frederic Weisbeckerce71b9d2009-11-22 05:26:55 +0100580
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200581 rec->nr = syscall_nr;
582 rec->ret = syscall_get_return_value(current, regs);
583
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100584 perf_trace_buf_submit(rec, size, rctx, 0, 1, flags, regs);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400585}
586
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100587int perf_sysexit_enable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400588{
589 int ret = 0;
590 int num;
591
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800592 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400593
594 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100595 if (!sys_perf_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400596 ret = register_trace_sys_exit(perf_syscall_exit, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400597 if (ret) {
598 pr_info("event trace: Could not activate"
Wenji Huang65746582010-02-24 15:40:22 +0800599 "syscall exit trace point");
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400600 } else {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100601 set_bit(num, enabled_perf_exit_syscalls);
602 sys_perf_refcount_exit++;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400603 }
604 mutex_unlock(&syscall_trace_lock);
605 return ret;
606}
607
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100608void perf_sysexit_disable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400609{
610 int num;
611
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800612 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400613
614 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100615 sys_perf_refcount_exit--;
616 clear_bit(num, enabled_perf_exit_syscalls);
617 if (!sys_perf_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400618 unregister_trace_sys_exit(perf_syscall_exit, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400619 mutex_unlock(&syscall_trace_lock);
620}
621
Li Zefan07b139c2009-12-21 14:27:35 +0800622#endif /* CONFIG_PERF_EVENTS */
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400623
Steven Rostedt22392912010-04-21 12:27:06 -0400624static int syscall_enter_register(struct ftrace_event_call *event,
625 enum trace_reg type)
626{
627 switch (type) {
628 case TRACE_REG_REGISTER:
629 return reg_event_syscall_enter(event);
630 case TRACE_REG_UNREGISTER:
631 unreg_event_syscall_enter(event);
632 return 0;
633
634#ifdef CONFIG_PERF_EVENTS
635 case TRACE_REG_PERF_REGISTER:
636 return perf_sysenter_enable(event);
637 case TRACE_REG_PERF_UNREGISTER:
638 perf_sysenter_disable(event);
639 return 0;
640#endif
641 }
642 return 0;
643}
644
645static int syscall_exit_register(struct ftrace_event_call *event,
646 enum trace_reg type)
647{
648 switch (type) {
649 case TRACE_REG_REGISTER:
650 return reg_event_syscall_exit(event);
651 case TRACE_REG_UNREGISTER:
652 unreg_event_syscall_exit(event);
653 return 0;
654
655#ifdef CONFIG_PERF_EVENTS
656 case TRACE_REG_PERF_REGISTER:
657 return perf_sysexit_enable(event);
658 case TRACE_REG_PERF_UNREGISTER:
659 perf_sysexit_disable(event);
660 return 0;
661#endif
662 }
663 return 0;
664}