blob: 8bda4bff2286644a68d601bbb6af7c0938673be4 [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>
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +01003#include <linux/kernel.h>
Jason Baronfb34a082009-08-10 16:52:47 -04004#include <linux/ftrace.h>
Ingo Molnarcdd6c482009-09-21 12:02:48 +02005#include <linux/perf_event.h>
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +01006#include <asm/syscall.h>
7
8#include "trace_output.h"
9#include "trace.h"
10
Frederic Weisbecker5be71b62009-03-15 22:10:37 +010011static DEFINE_MUTEX(syscall_trace_lock);
Jason Baronfb34a082009-08-10 16:52:47 -040012static int sys_refcount_enter;
13static int sys_refcount_exit;
Jason Baron57421db2009-08-24 17:40:22 -040014static DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls);
15static DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls);
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +010016
Frederic Weisbeckerc44fc772009-09-19 06:50:42 +020017extern unsigned long __start_syscalls_metadata[];
18extern unsigned long __stop_syscalls_metadata[];
19
20static struct syscall_metadata **syscalls_metadata;
21
22static struct syscall_metadata *find_syscall_meta(unsigned long syscall)
23{
24 struct syscall_metadata *start;
25 struct syscall_metadata *stop;
26 char str[KSYM_SYMBOL_LEN];
27
28
29 start = (struct syscall_metadata *)__start_syscalls_metadata;
30 stop = (struct syscall_metadata *)__stop_syscalls_metadata;
31 kallsyms_lookup(syscall, NULL, NULL, NULL, str);
32
33 for ( ; start < stop; start++) {
34 /*
35 * Only compare after the "sys" prefix. Archs that use
36 * syscall wrappers may have syscalls symbols aliases prefixed
37 * with "SyS" instead of "sys", leading to an unwanted
38 * mismatch.
39 */
40 if (start->name && !strcmp(start->name + 3, str + 3))
41 return start;
42 }
43 return NULL;
44}
45
46static struct syscall_metadata *syscall_nr_to_meta(int nr)
47{
48 if (!syscalls_metadata || nr >= NR_syscalls || nr < 0)
49 return NULL;
50
51 return syscalls_metadata[nr];
52}
53
54int syscall_name_to_nr(char *name)
55{
56 int i;
57
58 if (!syscalls_metadata)
59 return -1;
60
61 for (i = 0; i < NR_syscalls; i++) {
62 if (syscalls_metadata[i]) {
63 if (!strcmp(syscalls_metadata[i]->name, name))
64 return i;
65 }
66 }
67 return -1;
68}
69
70void set_syscall_enter_id(int num, int id)
71{
72 syscalls_metadata[num]->enter_id = id;
73}
74
75void set_syscall_exit_id(int num, int id)
76{
77 syscalls_metadata[num]->exit_id = id;
78}
79
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010080enum print_line_t
81print_syscall_enter(struct trace_iterator *iter, int flags)
82{
83 struct trace_seq *s = &iter->seq;
84 struct trace_entry *ent = iter->ent;
85 struct syscall_trace_enter *trace;
86 struct syscall_metadata *entry;
87 int i, ret, syscall;
88
Jason Baron64c12e02009-08-10 16:52:53 -040089 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010090 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010091 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -040092
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +010093 if (!entry)
94 goto end;
95
Jason Baron64c12e02009-08-10 16:52:53 -040096 if (entry->enter_id != ent->type) {
97 WARN_ON_ONCE(1);
98 goto end;
99 }
100
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100101 ret = trace_seq_printf(s, "%s(", entry->name);
102 if (!ret)
103 return TRACE_TYPE_PARTIAL_LINE;
104
105 for (i = 0; i < entry->nb_args; i++) {
106 /* parameter types */
Li Zefanba8b3a42009-08-17 16:55:18 +0800107 if (trace_flags & TRACE_ITER_VERBOSE) {
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100108 ret = trace_seq_printf(s, "%s ", entry->types[i]);
109 if (!ret)
110 return TRACE_TYPE_PARTIAL_LINE;
111 }
112 /* parameter values */
Li Zefan4539f072009-08-20 16:13:35 +0800113 ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i],
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100114 trace->args[i],
Li Zefan4539f072009-08-20 16:13:35 +0800115 i == entry->nb_args - 1 ? "" : ", ");
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100116 if (!ret)
117 return TRACE_TYPE_PARTIAL_LINE;
118 }
119
Li Zefan4539f072009-08-20 16:13:35 +0800120 ret = trace_seq_putc(s, ')');
121 if (!ret)
122 return TRACE_TYPE_PARTIAL_LINE;
123
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100124end:
Li Zefan4539f072009-08-20 16:13:35 +0800125 ret = trace_seq_putc(s, '\n');
126 if (!ret)
127 return TRACE_TYPE_PARTIAL_LINE;
128
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100129 return TRACE_TYPE_HANDLED;
130}
131
132enum print_line_t
133print_syscall_exit(struct trace_iterator *iter, int flags)
134{
135 struct trace_seq *s = &iter->seq;
136 struct trace_entry *ent = iter->ent;
137 struct syscall_trace_exit *trace;
138 int syscall;
139 struct syscall_metadata *entry;
140 int ret;
141
Jason Baron64c12e02009-08-10 16:52:53 -0400142 trace = (typeof(trace))ent;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100143 syscall = trace->nr;
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100144 entry = syscall_nr_to_meta(syscall);
Jason Baron64c12e02009-08-10 16:52:53 -0400145
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100146 if (!entry) {
147 trace_seq_printf(s, "\n");
148 return TRACE_TYPE_HANDLED;
149 }
150
Jason Baron64c12e02009-08-10 16:52:53 -0400151 if (entry->exit_id != ent->type) {
152 WARN_ON_ONCE(1);
153 return TRACE_TYPE_UNHANDLED;
154 }
155
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100156 ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
157 trace->ret);
158 if (!ret)
159 return TRACE_TYPE_PARTIAL_LINE;
160
161 return TRACE_TYPE_HANDLED;
162}
163
Li Zefane6971962009-08-19 15:52:25 +0800164extern char *__bad_type_size(void);
165
166#define SYSCALL_FIELD(type, name) \
167 sizeof(type) != sizeof(trace.name) ? \
168 __bad_type_size() : \
169 #type, #name, offsetof(typeof(trace), name), sizeof(trace.name)
170
Li Zefan10a5b662009-08-19 15:53:05 +0800171int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s)
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200172{
173 int i;
174 int nr;
Li Zefane6971962009-08-19 15:52:25 +0800175 int ret;
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200176 struct syscall_metadata *entry;
Li Zefane6971962009-08-19 15:52:25 +0800177 struct syscall_trace_enter trace;
178 int offset = offsetof(struct syscall_trace_enter, args);
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200179
Li Zefane6971962009-08-19 15:52:25 +0800180 nr = syscall_name_to_nr(call->data);
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200181 entry = syscall_nr_to_meta(nr);
182
183 if (!entry)
Li Zefane6971962009-08-19 15:52:25 +0800184 return 0;
185
186 ret = trace_seq_printf(s, "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n",
187 SYSCALL_FIELD(int, nr));
188 if (!ret)
189 return 0;
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200190
191 for (i = 0; i < entry->nb_args; i++) {
192 ret = trace_seq_printf(s, "\tfield:%s %s;", entry->types[i],
193 entry->args[i]);
194 if (!ret)
195 return 0;
Li Zefane6971962009-08-19 15:52:25 +0800196 ret = trace_seq_printf(s, "\toffset:%d;\tsize:%zu;\n", offset,
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200197 sizeof(unsigned long));
198 if (!ret)
199 return 0;
200 offset += sizeof(unsigned long);
201 }
202
Li Zefan4539f072009-08-20 16:13:35 +0800203 trace_seq_puts(s, "\nprint fmt: \"");
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200204 for (i = 0; i < entry->nb_args; i++) {
Li Zefane6971962009-08-19 15:52:25 +0800205 ret = trace_seq_printf(s, "%s: 0x%%0%zulx%s", entry->args[i],
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200206 sizeof(unsigned long),
Li Zefan4539f072009-08-20 16:13:35 +0800207 i == entry->nb_args - 1 ? "" : ", ");
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200208 if (!ret)
209 return 0;
210 }
Li Zefan4539f072009-08-20 16:13:35 +0800211 trace_seq_putc(s, '"');
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200212
213 for (i = 0; i < entry->nb_args; i++) {
Li Zefan4539f072009-08-20 16:13:35 +0800214 ret = trace_seq_printf(s, ", ((unsigned long)(REC->%s))",
215 entry->args[i]);
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200216 if (!ret)
217 return 0;
218 }
219
Li Zefan4539f072009-08-20 16:13:35 +0800220 return trace_seq_putc(s, '\n');
Frederic Weisbeckerdc4ddb42009-08-11 19:03:54 +0200221}
222
Li Zefan10a5b662009-08-19 15:53:05 +0800223int syscall_exit_format(struct ftrace_event_call *call, struct trace_seq *s)
224{
225 int ret;
226 struct syscall_trace_exit trace;
227
228 ret = trace_seq_printf(s,
229 "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
230 "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n",
231 SYSCALL_FIELD(int, nr),
232 SYSCALL_FIELD(unsigned long, ret));
233 if (!ret)
234 return 0;
235
236 return trace_seq_printf(s, "\nprint fmt: \"0x%%lx\", REC->ret\n");
237}
238
Li Zefan540b7b82009-08-19 15:54:51 +0800239int syscall_enter_define_fields(struct ftrace_event_call *call)
240{
241 struct syscall_trace_enter trace;
242 struct syscall_metadata *meta;
243 int ret;
244 int nr;
245 int i;
246 int offset = offsetof(typeof(trace), args);
247
248 nr = syscall_name_to_nr(call->data);
249 meta = syscall_nr_to_meta(nr);
250
251 if (!meta)
252 return 0;
253
254 ret = trace_define_common_fields(call);
255 if (ret)
256 return ret;
257
258 for (i = 0; i < meta->nb_args; i++) {
259 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
269int syscall_exit_define_fields(struct ftrace_event_call *call)
270{
271 struct syscall_trace_exit trace;
272 int ret;
273
274 ret = trace_define_common_fields(call);
275 if (ret)
276 return ret;
277
Li Zefan43b51ea2009-08-07 10:33:22 +0800278 ret = trace_define_field(call, SYSCALL_FIELD(unsigned long, ret), 0,
279 FILTER_OTHER);
Li Zefan540b7b82009-08-19 15:54:51 +0800280
281 return ret;
282}
283
Jason Baronfb34a082009-08-10 16:52:47 -0400284void ftrace_syscall_enter(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
Steven Rostedte77405a2009-09-02 14:17:06 -0400305 event = trace_current_buffer_lock_reserve(&buffer, sys_data->enter_id,
306 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
Jason Baronfb34a082009-08-10 16:52:47 -0400319void ftrace_syscall_exit(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
Steven Rostedte77405a2009-09-02 14:17:06 -0400337 event = trace_current_buffer_lock_reserve(&buffer, sys_data->exit_id,
Frederic Weisbeckerbed1ffc2009-03-13 15:42:11 +0100338 sizeof(*entry), 0, 0);
339 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
Jason Baronfb34a082009-08-10 16:52:47 -0400351int reg_event_syscall_enter(void *ptr)
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100352{
Jason Baronfb34a082009-08-10 16:52:47 -0400353 int ret = 0;
354 int num;
355 char *name;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100356
Jason Baronfb34a082009-08-10 16:52:47 -0400357 name = (char *)ptr;
358 num = syscall_name_to_nr(name);
Jason Baron57421db2009-08-24 17:40:22 -0400359 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400360 return -ENOSYS;
361 mutex_lock(&syscall_trace_lock);
362 if (!sys_refcount_enter)
Josh Stone1c569f02009-08-24 14:43:14 -0700363 ret = register_trace_sys_enter(ftrace_syscall_enter);
Jason Baronfb34a082009-08-10 16:52:47 -0400364 if (ret) {
365 pr_info("event trace: Could not activate"
366 "syscall entry trace point");
367 } else {
368 set_bit(num, enabled_enter_syscalls);
369 sys_refcount_enter++;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100370 }
Jason Baronfb34a082009-08-10 16:52:47 -0400371 mutex_unlock(&syscall_trace_lock);
372 return ret;
Frederic Weisbeckeree08c6e2009-03-07 05:52:59 +0100373}
Jason Baronfb34a082009-08-10 16:52:47 -0400374
375void unreg_event_syscall_enter(void *ptr)
376{
377 int num;
378 char *name;
379
380 name = (char *)ptr;
381 num = syscall_name_to_nr(name);
Jason Baron57421db2009-08-24 17:40:22 -0400382 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400383 return;
384 mutex_lock(&syscall_trace_lock);
385 sys_refcount_enter--;
386 clear_bit(num, enabled_enter_syscalls);
387 if (!sys_refcount_enter)
Josh Stone1c569f02009-08-24 14:43:14 -0700388 unregister_trace_sys_enter(ftrace_syscall_enter);
Jason Baronfb34a082009-08-10 16:52:47 -0400389 mutex_unlock(&syscall_trace_lock);
390}
391
392int reg_event_syscall_exit(void *ptr)
393{
394 int ret = 0;
395 int num;
396 char *name;
397
398 name = (char *)ptr;
399 num = syscall_name_to_nr(name);
Jason Baron57421db2009-08-24 17:40:22 -0400400 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400401 return -ENOSYS;
402 mutex_lock(&syscall_trace_lock);
403 if (!sys_refcount_exit)
Josh Stone1c569f02009-08-24 14:43:14 -0700404 ret = register_trace_sys_exit(ftrace_syscall_exit);
Jason Baronfb34a082009-08-10 16:52:47 -0400405 if (ret) {
406 pr_info("event trace: Could not activate"
407 "syscall exit trace point");
408 } else {
409 set_bit(num, enabled_exit_syscalls);
410 sys_refcount_exit++;
411 }
412 mutex_unlock(&syscall_trace_lock);
413 return ret;
414}
415
416void unreg_event_syscall_exit(void *ptr)
417{
418 int num;
419 char *name;
420
421 name = (char *)ptr;
422 num = syscall_name_to_nr(name);
Jason Baron57421db2009-08-24 17:40:22 -0400423 if (num < 0 || num >= NR_syscalls)
Jason Baronfb34a082009-08-10 16:52:47 -0400424 return;
425 mutex_lock(&syscall_trace_lock);
426 sys_refcount_exit--;
427 clear_bit(num, enabled_exit_syscalls);
428 if (!sys_refcount_exit)
Josh Stone1c569f02009-08-24 14:43:14 -0700429 unregister_trace_sys_exit(ftrace_syscall_exit);
Jason Baronfb34a082009-08-10 16:52:47 -0400430 mutex_unlock(&syscall_trace_lock);
431}
432
433struct trace_event event_syscall_enter = {
434 .trace = print_syscall_enter,
Jason Baronfb34a082009-08-10 16:52:47 -0400435};
436
437struct trace_event event_syscall_exit = {
438 .trace = print_syscall_exit,
Jason Baronfb34a082009-08-10 16:52:47 -0400439};
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400440
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);
457 syscalls_metadata[i] = meta;
458 }
459
460 return 0;
461}
462core_initcall(init_ftrace_syscalls);
463
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400464#ifdef CONFIG_EVENT_PROFILE
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200465
Jason Baron57421db2009-08-24 17:40:22 -0400466static DECLARE_BITMAP(enabled_prof_enter_syscalls, NR_syscalls);
467static DECLARE_BITMAP(enabled_prof_exit_syscalls, NR_syscalls);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400468static int sys_prof_refcount_enter;
469static int sys_prof_refcount_exit;
470
471static void prof_syscall_enter(struct pt_regs *regs, long id)
472{
473 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200474 struct syscall_trace_enter *rec;
475 unsigned long flags;
476 char *raw_data;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400477 int syscall_nr;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200478 int size;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200479 int cpu;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400480
481 syscall_nr = syscall_get_nr(current, regs);
482 if (!test_bit(syscall_nr, enabled_prof_enter_syscalls))
483 return;
484
485 sys_data = syscall_nr_to_meta(syscall_nr);
486 if (!sys_data)
487 return;
488
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200489 /* get the size after alignment with the u32 buffer size field */
490 size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
491 size = ALIGN(size + sizeof(u32), sizeof(u64));
492 size -= sizeof(u32);
493
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200494 if (WARN_ONCE(size > FTRACE_MAX_PROFILE_SIZE,
495 "profile buffer not large enough"))
496 return;
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200497
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200498 /* Protect the per cpu buffer, begin the rcu read side */
499 local_irq_save(flags);
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200500
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200501 cpu = smp_processor_id();
502
503 if (in_nmi())
504 raw_data = rcu_dereference(trace_profile_buf_nmi);
505 else
506 raw_data = rcu_dereference(trace_profile_buf);
507
508 if (!raw_data)
509 goto end;
510
511 raw_data = per_cpu_ptr(raw_data, cpu);
512
513 /* zero the dead bytes from align to not leak stack to user */
514 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
515
516 rec = (struct syscall_trace_enter *) raw_data;
517 tracing_generic_entry_update(&rec->ent, 0, 0);
518 rec->ent.type = sys_data->enter_id;
519 rec->nr = syscall_nr;
520 syscall_get_arguments(current, regs, 0, sys_data->nb_args,
521 (unsigned long *)&rec->args);
Linus Torvalds43c12662009-09-21 09:15:07 -0700522 perf_tp_event(sys_data->enter_id, 0, 1, rec, size);
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200523
524end:
525 local_irq_restore(flags);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400526}
527
528int reg_prof_syscall_enter(char *name)
529{
530 int ret = 0;
531 int num;
532
533 num = syscall_name_to_nr(name);
Jason Baron57421db2009-08-24 17:40:22 -0400534 if (num < 0 || num >= NR_syscalls)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400535 return -ENOSYS;
536
537 mutex_lock(&syscall_trace_lock);
538 if (!sys_prof_refcount_enter)
Josh Stone1c569f02009-08-24 14:43:14 -0700539 ret = register_trace_sys_enter(prof_syscall_enter);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400540 if (ret) {
541 pr_info("event trace: Could not activate"
542 "syscall entry trace point");
543 } else {
544 set_bit(num, enabled_prof_enter_syscalls);
545 sys_prof_refcount_enter++;
546 }
547 mutex_unlock(&syscall_trace_lock);
548 return ret;
549}
550
551void unreg_prof_syscall_enter(char *name)
552{
553 int num;
554
555 num = syscall_name_to_nr(name);
Jason Baron57421db2009-08-24 17:40:22 -0400556 if (num < 0 || num >= NR_syscalls)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400557 return;
558
559 mutex_lock(&syscall_trace_lock);
560 sys_prof_refcount_enter--;
561 clear_bit(num, enabled_prof_enter_syscalls);
562 if (!sys_prof_refcount_enter)
Josh Stone1c569f02009-08-24 14:43:14 -0700563 unregister_trace_sys_enter(prof_syscall_enter);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400564 mutex_unlock(&syscall_trace_lock);
565}
566
567static void prof_syscall_exit(struct pt_regs *regs, long ret)
568{
569 struct syscall_metadata *sys_data;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200570 struct syscall_trace_exit *rec;
571 unsigned long flags;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400572 int syscall_nr;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200573 char *raw_data;
574 int size;
575 int cpu;
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400576
577 syscall_nr = syscall_get_nr(current, regs);
578 if (!test_bit(syscall_nr, enabled_prof_exit_syscalls))
579 return;
580
581 sys_data = syscall_nr_to_meta(syscall_nr);
582 if (!sys_data)
583 return;
584
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200585 /* We can probably do that at build time */
586 size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64));
587 size -= sizeof(u32);
Frederic Weisbecker19007a62009-08-11 20:22:53 +0200588
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200589 /*
590 * Impossible, but be paranoid with the future
591 * How to put this check outside runtime?
592 */
593 if (WARN_ONCE(size > FTRACE_MAX_PROFILE_SIZE,
594 "exit event has grown above profile buffer size"))
595 return;
596
597 /* Protect the per cpu buffer, begin the rcu read side */
598 local_irq_save(flags);
599 cpu = smp_processor_id();
600
601 if (in_nmi())
602 raw_data = rcu_dereference(trace_profile_buf_nmi);
603 else
604 raw_data = rcu_dereference(trace_profile_buf);
605
606 if (!raw_data)
607 goto end;
608
609 raw_data = per_cpu_ptr(raw_data, cpu);
610
611 /* zero the dead bytes from align to not leak stack to user */
612 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
613
614 rec = (struct syscall_trace_exit *)raw_data;
615
616 tracing_generic_entry_update(&rec->ent, 0, 0);
617 rec->ent.type = sys_data->exit_id;
618 rec->nr = syscall_nr;
619 rec->ret = syscall_get_return_value(current, regs);
620
Linus Torvalds43c12662009-09-21 09:15:07 -0700621 perf_tp_event(sys_data->exit_id, 0, 1, rec, size);
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200622
623end:
624 local_irq_restore(flags);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400625}
626
627int reg_prof_syscall_exit(char *name)
628{
629 int ret = 0;
630 int num;
631
632 num = syscall_name_to_nr(name);
Jason Baron57421db2009-08-24 17:40:22 -0400633 if (num < 0 || num >= NR_syscalls)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400634 return -ENOSYS;
635
636 mutex_lock(&syscall_trace_lock);
637 if (!sys_prof_refcount_exit)
Josh Stone1c569f02009-08-24 14:43:14 -0700638 ret = register_trace_sys_exit(prof_syscall_exit);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400639 if (ret) {
640 pr_info("event trace: Could not activate"
641 "syscall entry trace point");
642 } else {
643 set_bit(num, enabled_prof_exit_syscalls);
644 sys_prof_refcount_exit++;
645 }
646 mutex_unlock(&syscall_trace_lock);
647 return ret;
648}
649
650void unreg_prof_syscall_exit(char *name)
651{
652 int num;
653
654 num = syscall_name_to_nr(name);
Jason Baron57421db2009-08-24 17:40:22 -0400655 if (num < 0 || num >= NR_syscalls)
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400656 return;
657
658 mutex_lock(&syscall_trace_lock);
659 sys_prof_refcount_exit--;
660 clear_bit(num, enabled_prof_exit_syscalls);
661 if (!sys_prof_refcount_exit)
Josh Stone1c569f02009-08-24 14:43:14 -0700662 unregister_trace_sys_exit(prof_syscall_exit);
Jason Baronf4b5ffc2009-08-10 16:53:02 -0400663 mutex_unlock(&syscall_trace_lock);
664}
665
666#endif
667
668