blob: 7a809e3210583a77f5eb8e8a64ce1cb6552e7f6f [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>
Steven Rostedtf431b632013-02-12 16:18:59 -05003#include <linux/syscalls.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09004#include <linux/slab.h>
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +01005#include <linux/kernel.h>
Paul Gortmaker56d82e02011-05-26 17:53:52 -04006#include <linux/module.h> /* for MODULE_NAME_LEN via KSYM_SYMBOL_LEN */
Jason Baronfb34a082009-08-10 16:52:47 -04007#include <linux/ftrace.h>
Ingo Molnarcdd6c482009-09-21 12:02:48 +02008#include <linux/perf_event.h>
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +01009#include <asm/syscall.h>
10
11#include "trace_output.h"
12#include "trace.h"
13
Frederic Weisbecker5be71b62009-03-15 22:10:37 +010014static DEFINE_MUTEX(syscall_trace_lock);
Jason Baronfb34a082009-08-10 16:52:47 -040015static int sys_refcount_enter;
16static int sys_refcount_exit;
Jason Baron57421db2009-08-24 17:40:22 -040017static DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls);
18static DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +010019
Steven Rostedt22392912010-04-21 12:27:06 -040020static int syscall_enter_register(struct ftrace_event_call *event,
Jiri Olsaceec0b62012-02-15 15:51:49 +010021 enum trace_reg type, void *data);
Steven Rostedt22392912010-04-21 12:27:06 -040022static int syscall_exit_register(struct ftrace_event_call *event,
Jiri Olsaceec0b62012-02-15 15:51:49 +010023 enum trace_reg type, void *data);
Steven Rostedt22392912010-04-21 12:27:06 -040024
Steven Rostedt2e33af02010-04-22 10:35:55 -040025static struct list_head *
26syscall_get_enter_fields(struct ftrace_event_call *call)
27{
28 struct syscall_metadata *entry = call->data;
29
30 return &entry->enter_fields;
31}
32
Steven Rostedt3d56e332011-02-02 17:06:09 -050033extern struct syscall_metadata *__start_syscalls_metadata[];
34extern struct syscall_metadata *__stop_syscalls_metadata[];
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020035
36static struct syscall_metadata **syscalls_metadata;
37
Ian Munsieb2d55492011-02-03 14:27:23 +110038#ifndef ARCH_HAS_SYSCALL_MATCH_SYM_NAME
39static inline bool arch_syscall_match_sym_name(const char *sym, const char *name)
40{
41 /*
42 * Only compare after the "sys" prefix. Archs that use
43 * syscall wrappers may have syscalls symbols aliases prefixed
44 * with "SyS" instead of "sys", leading to an unwanted
45 * mismatch.
46 */
47 return !strcmp(sym + 3, name + 3);
48}
49#endif
50
Steven Rostedtf431b632013-02-12 16:18:59 -050051#ifdef ARCH_TRACE_IGNORE_COMPAT_SYSCALLS
52/*
53 * Some architectures that allow for 32bit applications
54 * to run on a 64bit kernel, do not map the syscalls for
55 * the 32bit tasks the same as they do for 64bit tasks.
56 *
57 * *cough*x86*cough*
58 *
59 * In such a case, instead of reporting the wrong syscalls,
60 * simply ignore them.
61 *
62 * For an arch to ignore the compat syscalls it needs to
63 * define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS as well as
64 * define the function arch_trace_is_compat_syscall() to let
65 * the tracing system know that it should ignore it.
66 */
67static int
68trace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs)
69{
70 if (unlikely(arch_trace_is_compat_syscall(regs)))
71 return -1;
72
73 return syscall_get_nr(task, regs);
74}
75#else
76static inline int
77trace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs)
78{
79 return syscall_get_nr(task, regs);
80}
81#endif /* ARCH_TRACE_IGNORE_COMPAT_SYSCALLS */
82
Steven Rostedt3d56e332011-02-02 17:06:09 -050083static __init struct syscall_metadata *
84find_syscall_meta(unsigned long syscall)
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020085{
Steven Rostedt3d56e332011-02-02 17:06:09 -050086 struct syscall_metadata **start;
87 struct syscall_metadata **stop;
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020088 char str[KSYM_SYMBOL_LEN];
89
90
Steven Rostedt3d56e332011-02-02 17:06:09 -050091 start = __start_syscalls_metadata;
92 stop = __stop_syscalls_metadata;
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020093 kallsyms_lookup(syscall, NULL, NULL, NULL, str);
94
Ian Munsieae07f552011-02-03 14:27:25 +110095 if (arch_syscall_match_sym_name(str, "sys_ni_syscall"))
96 return NULL;
97
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020098 for ( ; start < stop; start++) {
Ian Munsieb2d55492011-02-03 14:27:23 +110099 if ((*start)->name && arch_syscall_match_sym_name(str, (*start)->name))
Steven Rostedt3d56e332011-02-02 17:06:09 -0500100 return *start;
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200101 }
102 return NULL;
103}
104
105static struct syscall_metadata *syscall_nr_to_meta(int nr)
106{
107 if (!syscalls_metadata || nr >= NR_syscalls || nr < 0)
108 return NULL;
109
110 return syscalls_metadata[nr];
111}
112
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800113static enum print_line_t
Steven Rostedta9a57762010-04-22 18:46:14 -0400114print_syscall_enter(struct trace_iterator *iter, int flags,
115 struct trace_event *event)
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100116{
117 struct trace_seq *s = &iter->seq;
118 struct trace_entry *ent = iter->ent;
119 struct syscall_trace_enter *trace;
120 struct syscall_metadata *entry;
121 int i, ret, syscall;
122
Jason Baron64c12e02009-08-10 16:52:53 -0400123 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100124 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100125 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -0400126
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100127 if (!entry)
128 goto end;
129
Steven Rostedt32c0eda2010-04-23 10:38:03 -0400130 if (entry->enter_event->event.type != ent->type) {
Jason Baron64c12e02009-08-10 16:52:53 -0400131 WARN_ON_ONCE(1);
132 goto end;
133 }
134
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100135 ret = trace_seq_printf(s, "%s(", entry->name);
136 if (!ret)
137 return TRACE_TYPE_PARTIAL_LINE;
138
139 for (i = 0; i < entry->nb_args; i++) {
140 /* parameter types */
Li Zefanba8b3a42009-08-17 16:55:18 +0800141 if (trace_flags & TRACE_ITER_VERBOSE) {
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100142 ret = trace_seq_printf(s, "%s ", entry->types[i]);
143 if (!ret)
144 return TRACE_TYPE_PARTIAL_LINE;
145 }
146 /* parameter values */
Li Zefan4539f072009-08-20 16:13:35 +0800147 ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i],
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100148 trace->args[i],
Li Zefan4539f072009-08-20 16:13:35 +0800149 i == entry->nb_args - 1 ? "" : ", ");
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100150 if (!ret)
151 return TRACE_TYPE_PARTIAL_LINE;
152 }
153
Li Zefan4539f072009-08-20 16:13:35 +0800154 ret = trace_seq_putc(s, ')');
155 if (!ret)
156 return TRACE_TYPE_PARTIAL_LINE;
157
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100158end:
Li Zefan4539f072009-08-20 16:13:35 +0800159 ret = trace_seq_putc(s, '\n');
160 if (!ret)
161 return TRACE_TYPE_PARTIAL_LINE;
162
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100163 return TRACE_TYPE_HANDLED;
164}
165
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800166static enum print_line_t
Steven Rostedta9a57762010-04-22 18:46:14 -0400167print_syscall_exit(struct trace_iterator *iter, int flags,
168 struct trace_event *event)
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100169{
170 struct trace_seq *s = &iter->seq;
171 struct trace_entry *ent = iter->ent;
172 struct syscall_trace_exit *trace;
173 int syscall;
174 struct syscall_metadata *entry;
175 int ret;
176
Jason Baron64c12e02009-08-10 16:52:53 -0400177 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100178 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100179 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -0400180
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100181 if (!entry) {
182 trace_seq_printf(s, "\n");
183 return TRACE_TYPE_HANDLED;
184 }
185
Steven Rostedt32c0eda2010-04-23 10:38:03 -0400186 if (entry->exit_event->event.type != ent->type) {
Jason Baron64c12e02009-08-10 16:52:53 -0400187 WARN_ON_ONCE(1);
188 return TRACE_TYPE_UNHANDLED;
189 }
190
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100191 ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
192 trace->ret);
193 if (!ret)
194 return TRACE_TYPE_PARTIAL_LINE;
195
196 return TRACE_TYPE_HANDLED;
197}
198
Li Zefane6971962009-08-19 15:52:25 +0800199extern char *__bad_type_size(void);
200
201#define SYSCALL_FIELD(type, name) \
202 sizeof(type) != sizeof(trace.name) ? \
203 __bad_type_size() : \
Tom Zanussi26a50742009-10-06 01:09:50 -0500204 #type, #name, offsetof(typeof(trace), name), \
205 sizeof(trace.name), is_signed_type(type)
Li Zefane6971962009-08-19 15:52:25 +0800206
Lai Jiangshan50307a42009-12-15 15:39:45 +0800207static
208int __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len)
209{
210 int i;
211 int pos = 0;
212
213 /* When len=0, we just calculate the needed length */
214#define LEN_OR_ZERO (len ? len - pos : 0)
215
216 pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
217 for (i = 0; i < entry->nb_args; i++) {
218 pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx%s",
219 entry->args[i], sizeof(unsigned long),
220 i == entry->nb_args - 1 ? "" : ", ");
221 }
222 pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
223
224 for (i = 0; i < entry->nb_args; i++) {
225 pos += snprintf(buf + pos, LEN_OR_ZERO,
226 ", ((unsigned long)(REC->%s))", entry->args[i]);
227 }
228
229#undef LEN_OR_ZERO
230
231 /* return the length of print_fmt */
232 return pos;
233}
234
235static int set_syscall_print_fmt(struct ftrace_event_call *call)
236{
237 char *print_fmt;
238 int len;
239 struct syscall_metadata *entry = call->data;
240
241 if (entry->enter_event != call) {
242 call->print_fmt = "\"0x%lx\", REC->ret";
243 return 0;
244 }
245
246 /* First: called with 0 length to calculate the needed length */
247 len = __set_enter_print_fmt(entry, NULL, 0);
248
249 print_fmt = kmalloc(len + 1, GFP_KERNEL);
250 if (!print_fmt)
251 return -ENOMEM;
252
253 /* Second: actually write the @print_fmt */
254 __set_enter_print_fmt(entry, print_fmt, len + 1);
255 call->print_fmt = print_fmt;
256
257 return 0;
258}
259
260static void free_syscall_print_fmt(struct ftrace_event_call *call)
261{
262 struct syscall_metadata *entry = call->data;
263
264 if (entry->enter_event == call)
265 kfree(call->print_fmt);
266}
267
Steven Rostedt2e33af02010-04-22 10:35:55 -0400268static int syscall_enter_define_fields(struct ftrace_event_call *call)
Li Zefan540b7b82009-08-19 15:54:51 +0800269{
270 struct syscall_trace_enter trace;
Lai Jiangshan31c16b12009-12-01 16:23:30 +0800271 struct syscall_metadata *meta = call->data;
Li Zefan540b7b82009-08-19 15:54:51 +0800272 int ret;
Li Zefan540b7b82009-08-19 15:54:51 +0800273 int i;
274 int offset = offsetof(typeof(trace), args);
275
Lai Jiangshan0f1ef512009-11-26 15:49:33 +0800276 ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER);
277 if (ret)
278 return ret;
279
Li Zefan540b7b82009-08-19 15:54:51 +0800280 for (i = 0; i < meta->nb_args; i++) {
Frederic Weisbeckeraeaeae12009-08-27 05:09:51 +0200281 ret = trace_define_field(call, meta->types[i],
282 meta->args[i], offset,
Li Zefan43b51ea2009-08-07 10:33:22 +0800283 sizeof(unsigned long), 0,
284 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800285 offset += sizeof(unsigned long);
286 }
287
288 return ret;
289}
290
Steven Rostedt2e33af02010-04-22 10:35:55 -0400291static int syscall_exit_define_fields(struct ftrace_event_call *call)
Li Zefan540b7b82009-08-19 15:54:51 +0800292{
293 struct syscall_trace_exit trace;
294 int ret;
295
Lai Jiangshan0f1ef512009-11-26 15:49:33 +0800296 ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER);
297 if (ret)
298 return ret;
299
Tom Zanussi26a50742009-10-06 01:09:50 -0500300 ret = trace_define_field(call, SYSCALL_FIELD(long, ret),
Li Zefan43b51ea2009-08-07 10:33:22 +0800301 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800302
303 return ret;
304}
305
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800306static void ftrace_syscall_enter(void *ignore, struct pt_regs *regs, long id)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100307{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100308 struct syscall_trace_enter *entry;
309 struct syscall_metadata *sys_data;
310 struct ring_buffer_event *event;
Steven Rostedte77405a2009-09-02 14:17:06 -0400311 struct ring_buffer *buffer;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100312 int syscall_nr;
Steven Rostedtf431b632013-02-12 16:18:59 -0500313 int size;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100314
Steven Rostedtf431b632013-02-12 16:18:59 -0500315 syscall_nr = trace_get_syscall_nr(current, regs);
Hendrik Bruecknercd0980f2009-08-25 14:50:27 +0200316 if (syscall_nr < 0)
317 return;
Jason Baronfb34a082009-08-10 16:52:47 -0400318 if (!test_bit(syscall_nr, enabled_enter_syscalls))
319 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100320
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100321 sys_data = syscall_nr_to_meta(syscall_nr);
322 if (!sys_data)
323 return;
324
325 size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
326
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800327 event = trace_current_buffer_lock_reserve(&buffer,
Steven Rostedt32c0eda2010-04-23 10:38:03 -0400328 sys_data->enter_event->event.type, size, 0, 0);
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100329 if (!event)
330 return;
331
332 entry = ring_buffer_event_data(event);
333 entry->nr = syscall_nr;
334 syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
335
Steven Rostedte77405a2009-09-02 14:17:06 -0400336 if (!filter_current_check_discard(buffer, sys_data->enter_event,
337 entry, event))
338 trace_current_buffer_unlock_commit(buffer, event, 0, 0);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100339}
340
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800341static void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100342{
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100343 struct syscall_trace_exit *entry;
344 struct syscall_metadata *sys_data;
345 struct ring_buffer_event *event;
Steven Rostedte77405a2009-09-02 14:17:06 -0400346 struct ring_buffer *buffer;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100347 int syscall_nr;
348
Steven Rostedtf431b632013-02-12 16:18:59 -0500349 syscall_nr = trace_get_syscall_nr(current, regs);
Hendrik Bruecknercd0980f2009-08-25 14:50:27 +0200350 if (syscall_nr < 0)
351 return;
Jason Baronfb34a082009-08-10 16:52:47 -0400352 if (!test_bit(syscall_nr, enabled_exit_syscalls))
353 return;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100354
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100355 sys_data = syscall_nr_to_meta(syscall_nr);
356 if (!sys_data)
357 return;
358
Lai Jiangshanfcc19432009-12-01 16:23:36 +0800359 event = trace_current_buffer_lock_reserve(&buffer,
Steven Rostedt32c0eda2010-04-23 10:38:03 -0400360 sys_data->exit_event->event.type, sizeof(*entry), 0, 0);
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100361 if (!event)
362 return;
363
364 entry = ring_buffer_event_data(event);
365 entry->nr = syscall_nr;
366 entry->ret = syscall_get_return_value(current, regs);
367
Steven Rostedte77405a2009-09-02 14:17:06 -0400368 if (!filter_current_check_discard(buffer, sys_data->exit_event,
369 entry, event))
370 trace_current_buffer_unlock_commit(buffer, event, 0, 0);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100371}
372
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800373static int reg_event_syscall_enter(struct ftrace_event_call *call)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100374{
Jason Baronfb34a082009-08-10 16:52:47 -0400375 int ret = 0;
376 int num;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100377
Lai Jiangshanc252f652009-12-01 16:23:47 +0800378 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Ian Munsie3773b3892011-02-03 14:27:21 +1100379 if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls))
Jason Baronfb34a082009-08-10 16:52:47 -0400380 return -ENOSYS;
381 mutex_lock(&syscall_trace_lock);
382 if (!sys_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400383 ret = register_trace_sys_enter(ftrace_syscall_enter, NULL);
Li Zefan3b8e4272009-12-08 11:14:52 +0800384 if (!ret) {
Jason Baronfb34a082009-08-10 16:52:47 -0400385 set_bit(num, enabled_enter_syscalls);
386 sys_refcount_enter++;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100387 }
Jason Baronfb34a082009-08-10 16:52:47 -0400388 mutex_unlock(&syscall_trace_lock);
389 return ret;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100390}
Jason Baronfb34a082009-08-10 16:52:47 -0400391
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800392static void unreg_event_syscall_enter(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400393{
394 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400395
Lai Jiangshanc252f652009-12-01 16:23:47 +0800396 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Ian Munsie3773b3892011-02-03 14:27:21 +1100397 if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls))
Jason Baronfb34a082009-08-10 16:52:47 -0400398 return;
399 mutex_lock(&syscall_trace_lock);
400 sys_refcount_enter--;
401 clear_bit(num, enabled_enter_syscalls);
402 if (!sys_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400403 unregister_trace_sys_enter(ftrace_syscall_enter, NULL);
Jason Baronfb34a082009-08-10 16:52:47 -0400404 mutex_unlock(&syscall_trace_lock);
405}
406
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800407static int reg_event_syscall_exit(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400408{
409 int ret = 0;
410 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400411
Lai Jiangshanc252f652009-12-01 16:23:47 +0800412 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Ian Munsie3773b3892011-02-03 14:27:21 +1100413 if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls))
Jason Baronfb34a082009-08-10 16:52:47 -0400414 return -ENOSYS;
415 mutex_lock(&syscall_trace_lock);
416 if (!sys_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400417 ret = register_trace_sys_exit(ftrace_syscall_exit, NULL);
Li Zefan3b8e4272009-12-08 11:14:52 +0800418 if (!ret) {
Jason Baronfb34a082009-08-10 16:52:47 -0400419 set_bit(num, enabled_exit_syscalls);
420 sys_refcount_exit++;
421 }
422 mutex_unlock(&syscall_trace_lock);
423 return ret;
424}
425
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800426static void unreg_event_syscall_exit(struct ftrace_event_call *call)
Jason Baronfb34a082009-08-10 16:52:47 -0400427{
428 int num;
Jason Baronfb34a082009-08-10 16:52:47 -0400429
Lai Jiangshanc252f652009-12-01 16:23:47 +0800430 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Ian Munsie3773b3892011-02-03 14:27:21 +1100431 if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls))
Jason Baronfb34a082009-08-10 16:52:47 -0400432 return;
433 mutex_lock(&syscall_trace_lock);
434 sys_refcount_exit--;
435 clear_bit(num, enabled_exit_syscalls);
436 if (!sys_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400437 unregister_trace_sys_exit(ftrace_syscall_exit, NULL);
Jason Baronfb34a082009-08-10 16:52:47 -0400438 mutex_unlock(&syscall_trace_lock);
439}
440
Vaibhav Nagarnaik6f86ab92012-06-07 16:46:25 -0700441static int init_syscall_trace(struct ftrace_event_call *call)
Lai Jiangshana1301da2009-12-01 16:23:55 +0800442{
443 int id;
Ian Munsieba976972011-02-03 14:27:20 +1100444 int num;
445
446 num = ((struct syscall_metadata *)call->data)->syscall_nr;
447 if (num < 0 || num >= NR_syscalls) {
448 pr_debug("syscall %s metadata not mapped, disabling ftrace event\n",
449 ((struct syscall_metadata *)call->data)->name);
450 return -ENOSYS;
451 }
Lai Jiangshana1301da2009-12-01 16:23:55 +0800452
Lai Jiangshan50307a42009-12-15 15:39:45 +0800453 if (set_syscall_print_fmt(call) < 0)
454 return -ENOMEM;
455
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500456 id = trace_event_raw_init(call);
457
458 if (id < 0) {
Lai Jiangshan50307a42009-12-15 15:39:45 +0800459 free_syscall_print_fmt(call);
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500460 return id;
Lai Jiangshan50307a42009-12-15 15:39:45 +0800461 }
Steven Rostedtc7ef3a92009-12-28 21:13:59 -0500462
463 return id;
Lai Jiangshana1301da2009-12-01 16:23:55 +0800464}
465
Vaibhav Nagarnaik6f86ab92012-06-07 16:46:25 -0700466struct trace_event_functions enter_syscall_print_funcs = {
467 .trace = print_syscall_enter,
468};
469
470struct trace_event_functions exit_syscall_print_funcs = {
471 .trace = print_syscall_exit,
472};
473
474struct ftrace_event_class event_class_syscall_enter = {
475 .system = "syscalls",
476 .reg = syscall_enter_register,
477 .define_fields = syscall_enter_define_fields,
478 .get_fields = syscall_get_enter_fields,
479 .raw_init = init_syscall_trace,
480};
481
482struct ftrace_event_class event_class_syscall_exit = {
483 .system = "syscalls",
484 .reg = syscall_exit_register,
485 .define_fields = syscall_exit_define_fields,
486 .fields = LIST_HEAD_INIT(event_class_syscall_exit.fields),
487 .raw_init = init_syscall_trace,
488};
489
Ian Munsiec763ba02011-02-03 14:27:22 +1100490unsigned long __init __weak arch_syscall_addr(int nr)
Mike Frysingere7b8e672010-01-26 04:40:03 -0500491{
492 return (unsigned long)sys_call_table[nr];
493}
494
Fengguang Wu6aea49c2012-11-21 15:13:47 +0800495static int __init init_ftrace_syscalls(void)
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200496{
497 struct syscall_metadata *meta;
498 unsigned long addr;
499 int i;
500
Thomas Meyer47b0edc2011-11-29 22:08:00 +0100501 syscalls_metadata = kcalloc(NR_syscalls, sizeof(*syscalls_metadata),
502 GFP_KERNEL);
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200503 if (!syscalls_metadata) {
504 WARN_ON(1);
505 return -ENOMEM;
506 }
507
508 for (i = 0; i < NR_syscalls; i++) {
509 addr = arch_syscall_addr(i);
510 meta = find_syscall_meta(addr);
Lai Jiangshanc252f652009-12-01 16:23:47 +0800511 if (!meta)
512 continue;
513
514 meta->syscall_nr = i;
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200515 syscalls_metadata[i] = meta;
516 }
517
518 return 0;
519}
Ezequiel Garcia87819152012-09-12 11:47:57 -0300520early_initcall(init_ftrace_syscalls);
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +0200521
Li Zefan07b139c2009-12-21 14:27:35 +0800522#ifdef CONFIG_PERF_EVENTS
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200523
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100524static DECLARE_BITMAP(enabled_perf_enter_syscalls, NR_syscalls);
525static DECLARE_BITMAP(enabled_perf_exit_syscalls, NR_syscalls);
526static int sys_perf_refcount_enter;
527static int sys_perf_refcount_exit;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400528
Steven Rostedt38516ab2010-04-20 17:04:50 -0400529static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400530{
531 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200532 struct syscall_trace_enter *rec;
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200533 struct hlist_head *head;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400534 int syscall_nr;
Peter Zijlstra4ed7c922009-11-23 11:37:29 +0100535 int rctx;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200536 int size;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400537
Steven Rostedtf431b632013-02-12 16:18:59 -0500538 syscall_nr = trace_get_syscall_nr(current, regs);
Will Deacon60916a92012-08-16 18:14:14 +0100539 if (syscall_nr < 0)
540 return;
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100541 if (!test_bit(syscall_nr, enabled_perf_enter_syscalls))
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400542 return;
543
544 sys_data = syscall_nr_to_meta(syscall_nr);
545 if (!sys_data)
546 return;
547
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200548 /* get the size after alignment with the u32 buffer size field */
549 size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
550 size = ALIGN(size + sizeof(u32), sizeof(u64));
551 size -= sizeof(u32);
552
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100553 if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
554 "perf buffer not large enough"))
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200555 return;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200556
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100557 rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size,
Steven Rostedtff5f1492010-05-21 11:49:57 -0400558 sys_data->enter_event->event.type, regs, &rctx);
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800559 if (!rec)
560 return;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200561
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200562 rec->nr = syscall_nr;
563 syscall_get_arguments(current, regs, 0, sys_data->nb_args,
564 (unsigned long *)&rec->args);
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200565
Peter Zijlstra3771f072010-05-21 12:31:09 +0200566 head = this_cpu_ptr(sys_data->enter_event->perf_events);
Andrew Vagine6dab5f2012-07-11 18:14:58 +0400567 perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400568}
569
Vaibhav Nagarnaik6f86ab92012-06-07 16:46:25 -0700570static int perf_sysenter_enable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400571{
572 int ret = 0;
573 int num;
574
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800575 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400576
577 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100578 if (!sys_perf_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400579 ret = register_trace_sys_enter(perf_syscall_enter, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400580 if (ret) {
581 pr_info("event trace: Could not activate"
582 "syscall entry trace point");
583 } else {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100584 set_bit(num, enabled_perf_enter_syscalls);
585 sys_perf_refcount_enter++;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400586 }
587 mutex_unlock(&syscall_trace_lock);
588 return ret;
589}
590
Vaibhav Nagarnaik6f86ab92012-06-07 16:46:25 -0700591static void perf_sysenter_disable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400592{
593 int num;
594
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800595 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400596
597 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100598 sys_perf_refcount_enter--;
599 clear_bit(num, enabled_perf_enter_syscalls);
600 if (!sys_perf_refcount_enter)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400601 unregister_trace_sys_enter(perf_syscall_enter, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400602 mutex_unlock(&syscall_trace_lock);
603}
604
Steven Rostedt38516ab2010-04-20 17:04:50 -0400605static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400606{
607 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200608 struct syscall_trace_exit *rec;
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200609 struct hlist_head *head;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400610 int syscall_nr;
Peter Zijlstra4ed7c922009-11-23 11:37:29 +0100611 int rctx;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200612 int size;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400613
Steven Rostedtf431b632013-02-12 16:18:59 -0500614 syscall_nr = trace_get_syscall_nr(current, regs);
Will Deacon60916a92012-08-16 18:14:14 +0100615 if (syscall_nr < 0)
616 return;
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100617 if (!test_bit(syscall_nr, enabled_perf_exit_syscalls))
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400618 return;
619
620 sys_data = syscall_nr_to_meta(syscall_nr);
621 if (!sys_data)
622 return;
623
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200624 /* We can probably do that at build time */
625 size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64));
626 size -= sizeof(u32);
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200627
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200628 /*
629 * Impossible, but be paranoid with the future
630 * How to put this check outside runtime?
631 */
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100632 if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
633 "exit event has grown above perf buffer size"))
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200634 return;
635
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100636 rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size,
Steven Rostedtff5f1492010-05-21 11:49:57 -0400637 sys_data->exit_event->event.type, regs, &rctx);
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800638 if (!rec)
639 return;
Frederic Weisbeckerce71b9d2009-11-22 05:26:55 +0100640
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200641 rec->nr = syscall_nr;
642 rec->ret = syscall_get_return_value(current, regs);
643
Peter Zijlstra3771f072010-05-21 12:31:09 +0200644 head = this_cpu_ptr(sys_data->exit_event->perf_events);
Andrew Vagine6dab5f2012-07-11 18:14:58 +0400645 perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400646}
647
Vaibhav Nagarnaik6f86ab92012-06-07 16:46:25 -0700648static int perf_sysexit_enable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400649{
650 int ret = 0;
651 int num;
652
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800653 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400654
655 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100656 if (!sys_perf_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400657 ret = register_trace_sys_exit(perf_syscall_exit, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400658 if (ret) {
659 pr_info("event trace: Could not activate"
Wenji Huang65746582010-02-24 15:40:22 +0800660 "syscall exit trace point");
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400661 } else {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100662 set_bit(num, enabled_perf_exit_syscalls);
663 sys_perf_refcount_exit++;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400664 }
665 mutex_unlock(&syscall_trace_lock);
666 return ret;
667}
668
Vaibhav Nagarnaik6f86ab92012-06-07 16:46:25 -0700669static void perf_sysexit_disable(struct ftrace_event_call *call)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400670{
671 int num;
672
Lai Jiangshan3bbe84e2009-12-01 16:24:01 +0800673 num = ((struct syscall_metadata *)call->data)->syscall_nr;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400674
675 mutex_lock(&syscall_trace_lock);
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100676 sys_perf_refcount_exit--;
677 clear_bit(num, enabled_perf_exit_syscalls);
678 if (!sys_perf_refcount_exit)
Steven Rostedt38516ab2010-04-20 17:04:50 -0400679 unregister_trace_sys_exit(perf_syscall_exit, NULL);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400680 mutex_unlock(&syscall_trace_lock);
681}
682
Li Zefan07b139c2009-12-21 14:27:35 +0800683#endif /* CONFIG_PERF_EVENTS */
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400684
Steven Rostedt22392912010-04-21 12:27:06 -0400685static int syscall_enter_register(struct ftrace_event_call *event,
Jiri Olsaceec0b62012-02-15 15:51:49 +0100686 enum trace_reg type, void *data)
Steven Rostedt22392912010-04-21 12:27:06 -0400687{
688 switch (type) {
689 case TRACE_REG_REGISTER:
690 return reg_event_syscall_enter(event);
691 case TRACE_REG_UNREGISTER:
692 unreg_event_syscall_enter(event);
693 return 0;
694
695#ifdef CONFIG_PERF_EVENTS
696 case TRACE_REG_PERF_REGISTER:
697 return perf_sysenter_enable(event);
698 case TRACE_REG_PERF_UNREGISTER:
699 perf_sysenter_disable(event);
700 return 0;
Jiri Olsaceec0b62012-02-15 15:51:49 +0100701 case TRACE_REG_PERF_OPEN:
702 case TRACE_REG_PERF_CLOSE:
Jiri Olsa489c75c2012-02-15 15:51:50 +0100703 case TRACE_REG_PERF_ADD:
704 case TRACE_REG_PERF_DEL:
Jiri Olsaceec0b62012-02-15 15:51:49 +0100705 return 0;
Steven Rostedt22392912010-04-21 12:27:06 -0400706#endif
707 }
708 return 0;
709}
710
711static int syscall_exit_register(struct ftrace_event_call *event,
Jiri Olsaceec0b62012-02-15 15:51:49 +0100712 enum trace_reg type, void *data)
Steven Rostedt22392912010-04-21 12:27:06 -0400713{
714 switch (type) {
715 case TRACE_REG_REGISTER:
716 return reg_event_syscall_exit(event);
717 case TRACE_REG_UNREGISTER:
718 unreg_event_syscall_exit(event);
719 return 0;
720
721#ifdef CONFIG_PERF_EVENTS
722 case TRACE_REG_PERF_REGISTER:
723 return perf_sysexit_enable(event);
724 case TRACE_REG_PERF_UNREGISTER:
725 perf_sysexit_disable(event);
726 return 0;
Jiri Olsaceec0b62012-02-15 15:51:49 +0100727 case TRACE_REG_PERF_OPEN:
728 case TRACE_REG_PERF_CLOSE:
Jiri Olsa489c75c2012-02-15 15:51:50 +0100729 case TRACE_REG_PERF_ADD:
730 case TRACE_REG_PERF_DEL:
Jiri Olsaceec0b62012-02-15 15:51:49 +0100731 return 0;
Steven Rostedt22392912010-04-21 12:27:06 -0400732#endif
733 }
734 return 0;
735}