diff options
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/trace.h | 17 | ||||
-rw-r--r-- | kernel/trace/trace_syscalls.c | 146 |
2 files changed, 155 insertions, 8 deletions
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 56ce34d90b03..f56162806f50 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -202,6 +202,19 @@ struct kmemtrace_free_entry { | |||
202 | const void *ptr; | 202 | const void *ptr; |
203 | }; | 203 | }; |
204 | 204 | ||
205 | struct syscall_trace_enter { | ||
206 | struct trace_entry ent; | ||
207 | int nr; | ||
208 | unsigned long args[]; | ||
209 | }; | ||
210 | |||
211 | struct syscall_trace_exit { | ||
212 | struct trace_entry ent; | ||
213 | int nr; | ||
214 | unsigned long ret; | ||
215 | }; | ||
216 | |||
217 | |||
205 | /* | 218 | /* |
206 | * trace_flag_type is an enumeration that holds different | 219 | * trace_flag_type is an enumeration that holds different |
207 | * states when a trace occurs. These are: | 220 | * states when a trace occurs. These are: |
@@ -315,6 +328,10 @@ extern void __ftrace_bad_type(void); | |||
315 | TRACE_KMEM_ALLOC); \ | 328 | TRACE_KMEM_ALLOC); \ |
316 | IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \ | 329 | IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \ |
317 | TRACE_KMEM_FREE); \ | 330 | TRACE_KMEM_FREE); \ |
331 | IF_ASSIGN(var, ent, struct syscall_trace_enter, \ | ||
332 | TRACE_SYSCALL_ENTER); \ | ||
333 | IF_ASSIGN(var, ent, struct syscall_trace_exit, \ | ||
334 | TRACE_SYSCALL_EXIT); \ | ||
318 | __ftrace_bad_type(); \ | 335 | __ftrace_bad_type(); \ |
319 | } while (0) | 336 | } while (0) |
320 | 337 | ||
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 66cf97449af3..c72e599230ff 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include <linux/ftrace.h> | ||
2 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
3 | 2 | #include <linux/ftrace.h> | |
4 | #include <asm/syscall.h> | 3 | #include <asm/syscall.h> |
5 | 4 | ||
6 | #include "trace_output.h" | 5 | #include "trace_output.h" |
@@ -8,6 +7,90 @@ | |||
8 | 7 | ||
9 | static atomic_t refcount; | 8 | static atomic_t refcount; |
10 | 9 | ||
10 | /* Our two options */ | ||
11 | enum { | ||
12 | TRACE_SYSCALLS_OPT_TYPES = 0x1, | ||
13 | }; | ||
14 | |||
15 | static struct tracer_opt syscalls_opts[] = { | ||
16 | { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) }, | ||
17 | { } | ||
18 | }; | ||
19 | |||
20 | static struct tracer_flags syscalls_flags = { | ||
21 | .val = 0, /* By default: no args types */ | ||
22 | .opts = syscalls_opts | ||
23 | }; | ||
24 | |||
25 | enum print_line_t | ||
26 | print_syscall_enter(struct trace_iterator *iter, int flags) | ||
27 | { | ||
28 | struct trace_seq *s = &iter->seq; | ||
29 | struct trace_entry *ent = iter->ent; | ||
30 | struct syscall_trace_enter *trace; | ||
31 | struct syscall_metadata *entry; | ||
32 | int i, ret, syscall; | ||
33 | |||
34 | trace_assign_type(trace, ent); | ||
35 | |||
36 | syscall = trace->nr; | ||
37 | |||
38 | entry = syscall_nr_to_meta(syscall); | ||
39 | if (!entry) | ||
40 | goto end; | ||
41 | |||
42 | ret = trace_seq_printf(s, "%s(", entry->name); | ||
43 | if (!ret) | ||
44 | return TRACE_TYPE_PARTIAL_LINE; | ||
45 | |||
46 | for (i = 0; i < entry->nb_args; i++) { | ||
47 | /* parameter types */ | ||
48 | if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) { | ||
49 | ret = trace_seq_printf(s, "%s ", entry->types[i]); | ||
50 | if (!ret) | ||
51 | return TRACE_TYPE_PARTIAL_LINE; | ||
52 | } | ||
53 | /* parameter values */ | ||
54 | ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i], | ||
55 | trace->args[i], | ||
56 | i == entry->nb_args - 1 ? ")" : ","); | ||
57 | if (!ret) | ||
58 | return TRACE_TYPE_PARTIAL_LINE; | ||
59 | } | ||
60 | |||
61 | end: | ||
62 | trace_seq_printf(s, "\n"); | ||
63 | return TRACE_TYPE_HANDLED; | ||
64 | } | ||
65 | |||
66 | enum print_line_t | ||
67 | print_syscall_exit(struct trace_iterator *iter, int flags) | ||
68 | { | ||
69 | struct trace_seq *s = &iter->seq; | ||
70 | struct trace_entry *ent = iter->ent; | ||
71 | struct syscall_trace_exit *trace; | ||
72 | int syscall; | ||
73 | struct syscall_metadata *entry; | ||
74 | int ret; | ||
75 | |||
76 | trace_assign_type(trace, ent); | ||
77 | |||
78 | syscall = trace->nr; | ||
79 | |||
80 | entry = syscall_nr_to_meta(syscall); | ||
81 | if (!entry) { | ||
82 | trace_seq_printf(s, "\n"); | ||
83 | return TRACE_TYPE_HANDLED; | ||
84 | } | ||
85 | |||
86 | ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, | ||
87 | trace->ret); | ||
88 | if (!ret) | ||
89 | return TRACE_TYPE_PARTIAL_LINE; | ||
90 | |||
91 | return TRACE_TYPE_HANDLED; | ||
92 | } | ||
93 | |||
11 | void start_ftrace_syscalls(void) | 94 | void start_ftrace_syscalls(void) |
12 | { | 95 | { |
13 | unsigned long flags; | 96 | unsigned long flags; |
@@ -16,6 +99,7 @@ void start_ftrace_syscalls(void) | |||
16 | if (atomic_inc_return(&refcount) != 1) | 99 | if (atomic_inc_return(&refcount) != 1) |
17 | goto out; | 100 | goto out; |
18 | 101 | ||
102 | arch_init_ftrace_syscalls(); | ||
19 | read_lock_irqsave(&tasklist_lock, flags); | 103 | read_lock_irqsave(&tasklist_lock, flags); |
20 | 104 | ||
21 | do_each_thread(g, t) { | 105 | do_each_thread(g, t) { |
@@ -48,20 +132,63 @@ out: | |||
48 | 132 | ||
49 | void ftrace_syscall_enter(struct pt_regs *regs) | 133 | void ftrace_syscall_enter(struct pt_regs *regs) |
50 | { | 134 | { |
135 | struct syscall_trace_enter *entry; | ||
136 | struct syscall_metadata *sys_data; | ||
137 | struct ring_buffer_event *event; | ||
138 | int size; | ||
51 | int syscall_nr; | 139 | int syscall_nr; |
140 | int cpu; | ||
52 | 141 | ||
53 | syscall_nr = syscall_get_nr(current, regs); | 142 | syscall_nr = syscall_get_nr(current, regs); |
54 | 143 | ||
55 | trace_printk("syscall %d enter\n", syscall_nr); | 144 | cpu = raw_smp_processor_id(); |
145 | |||
146 | sys_data = syscall_nr_to_meta(syscall_nr); | ||
147 | if (!sys_data) | ||
148 | return; | ||
149 | |||
150 | size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; | ||
151 | |||
152 | event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size, | ||
153 | 0, 0); | ||
154 | if (!event) | ||
155 | return; | ||
156 | |||
157 | entry = ring_buffer_event_data(event); | ||
158 | entry->nr = syscall_nr; | ||
159 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args); | ||
160 | |||
161 | trace_current_buffer_unlock_commit(event, 0, 0); | ||
162 | trace_wake_up(); | ||
56 | } | 163 | } |
57 | 164 | ||
58 | void ftrace_syscall_exit(struct pt_regs *regs) | 165 | void ftrace_syscall_exit(struct pt_regs *regs) |
59 | { | 166 | { |
167 | struct syscall_trace_exit *entry; | ||
168 | struct syscall_metadata *sys_data; | ||
169 | struct ring_buffer_event *event; | ||
60 | int syscall_nr; | 170 | int syscall_nr; |
171 | int cpu; | ||
61 | 172 | ||
62 | syscall_nr = syscall_get_nr(current, regs); | 173 | syscall_nr = syscall_get_nr(current, regs); |
63 | 174 | ||
64 | trace_printk("syscall %d exit\n", syscall_nr); | 175 | cpu = raw_smp_processor_id(); |
176 | |||
177 | sys_data = syscall_nr_to_meta(syscall_nr); | ||
178 | if (!sys_data) | ||
179 | return; | ||
180 | |||
181 | event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT, | ||
182 | sizeof(*entry), 0, 0); | ||
183 | if (!event) | ||
184 | return; | ||
185 | |||
186 | entry = ring_buffer_event_data(event); | ||
187 | entry->nr = syscall_nr; | ||
188 | entry->ret = syscall_get_return_value(current, regs); | ||
189 | |||
190 | trace_current_buffer_unlock_commit(event, 0, 0); | ||
191 | trace_wake_up(); | ||
65 | } | 192 | } |
66 | 193 | ||
67 | static int init_syscall_tracer(struct trace_array *tr) | 194 | static int init_syscall_tracer(struct trace_array *tr) |
@@ -77,17 +204,20 @@ static void reset_syscall_tracer(struct trace_array *tr) | |||
77 | } | 204 | } |
78 | 205 | ||
79 | static struct trace_event syscall_enter_event = { | 206 | static struct trace_event syscall_enter_event = { |
80 | .type = TRACE_SYSCALL_ENTER, | 207 | .type = TRACE_SYSCALL_ENTER, |
208 | .trace = print_syscall_enter, | ||
81 | }; | 209 | }; |
82 | 210 | ||
83 | static struct trace_event syscall_exit_event = { | 211 | static struct trace_event syscall_exit_event = { |
84 | .type = TRACE_SYSCALL_EXIT, | 212 | .type = TRACE_SYSCALL_EXIT, |
213 | .trace = print_syscall_exit, | ||
85 | }; | 214 | }; |
86 | 215 | ||
87 | static struct tracer syscall_tracer __read_mostly = { | 216 | static struct tracer syscall_tracer __read_mostly = { |
88 | .name = "syscall", | 217 | .name = "syscall", |
89 | .init = init_syscall_tracer, | 218 | .init = init_syscall_tracer, |
90 | .reset = reset_syscall_tracer | 219 | .reset = reset_syscall_tracer, |
220 | .flags = &syscalls_flags, | ||
91 | }; | 221 | }; |
92 | 222 | ||
93 | __init int register_ftrace_syscalls(void) | 223 | __init int register_ftrace_syscalls(void) |