]>
Commit | Line | Data |
---|---|---|
47788c58 | 1 | #include <trace/syscall.h> |
ee08c6ec | 2 | #include <linux/kernel.h> |
fb34a08c | 3 | #include <linux/ftrace.h> |
ee08c6ec FW |
4 | #include <asm/syscall.h> |
5 | ||
6 | #include "trace_output.h" | |
7 | #include "trace.h" | |
8 | ||
5be71b61 | 9 | static DEFINE_MUTEX(syscall_trace_lock); |
fb34a08c JB |
10 | static int sys_refcount_enter; |
11 | static int sys_refcount_exit; | |
12 | static DECLARE_BITMAP(enabled_enter_syscalls, FTRACE_SYSCALL_MAX); | |
13 | static DECLARE_BITMAP(enabled_exit_syscalls, FTRACE_SYSCALL_MAX); | |
ee08c6ec | 14 | |
64044345 | 15 | /* Option to display the parameters types */ |
bed1ffca FW |
16 | enum { |
17 | TRACE_SYSCALLS_OPT_TYPES = 0x1, | |
18 | }; | |
19 | ||
20 | static struct tracer_opt syscalls_opts[] = { | |
21 | { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) }, | |
22 | { } | |
23 | }; | |
24 | ||
25 | static struct tracer_flags syscalls_flags = { | |
64044345 | 26 | .val = 0, /* By default: no parameters types */ |
bed1ffca FW |
27 | .opts = syscalls_opts |
28 | }; | |
29 | ||
30 | enum print_line_t | |
31 | print_syscall_enter(struct trace_iterator *iter, int flags) | |
32 | { | |
33 | struct trace_seq *s = &iter->seq; | |
34 | struct trace_entry *ent = iter->ent; | |
35 | struct syscall_trace_enter *trace; | |
36 | struct syscall_metadata *entry; | |
37 | int i, ret, syscall; | |
38 | ||
39 | trace_assign_type(trace, ent); | |
40 | ||
41 | syscall = trace->nr; | |
42 | ||
43 | entry = syscall_nr_to_meta(syscall); | |
44 | if (!entry) | |
45 | goto end; | |
46 | ||
47 | ret = trace_seq_printf(s, "%s(", entry->name); | |
48 | if (!ret) | |
49 | return TRACE_TYPE_PARTIAL_LINE; | |
50 | ||
51 | for (i = 0; i < entry->nb_args; i++) { | |
52 | /* parameter types */ | |
53 | if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) { | |
54 | ret = trace_seq_printf(s, "%s ", entry->types[i]); | |
55 | if (!ret) | |
56 | return TRACE_TYPE_PARTIAL_LINE; | |
57 | } | |
58 | /* parameter values */ | |
59 | ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i], | |
60 | trace->args[i], | |
61 | i == entry->nb_args - 1 ? ")" : ","); | |
62 | if (!ret) | |
63 | return TRACE_TYPE_PARTIAL_LINE; | |
64 | } | |
65 | ||
66 | end: | |
67 | trace_seq_printf(s, "\n"); | |
68 | return TRACE_TYPE_HANDLED; | |
69 | } | |
70 | ||
71 | enum print_line_t | |
72 | print_syscall_exit(struct trace_iterator *iter, int flags) | |
73 | { | |
74 | struct trace_seq *s = &iter->seq; | |
75 | struct trace_entry *ent = iter->ent; | |
76 | struct syscall_trace_exit *trace; | |
77 | int syscall; | |
78 | struct syscall_metadata *entry; | |
79 | int ret; | |
80 | ||
81 | trace_assign_type(trace, ent); | |
82 | ||
83 | syscall = trace->nr; | |
84 | ||
85 | entry = syscall_nr_to_meta(syscall); | |
86 | if (!entry) { | |
87 | trace_seq_printf(s, "\n"); | |
88 | return TRACE_TYPE_HANDLED; | |
89 | } | |
90 | ||
91 | ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, | |
92 | trace->ret); | |
93 | if (!ret) | |
94 | return TRACE_TYPE_PARTIAL_LINE; | |
95 | ||
96 | return TRACE_TYPE_HANDLED; | |
97 | } | |
98 | ||
fb34a08c | 99 | void ftrace_syscall_enter(struct pt_regs *regs, long id) |
ee08c6ec | 100 | { |
bed1ffca FW |
101 | struct syscall_trace_enter *entry; |
102 | struct syscall_metadata *sys_data; | |
103 | struct ring_buffer_event *event; | |
104 | int size; | |
ee08c6ec FW |
105 | int syscall_nr; |
106 | ||
107 | syscall_nr = syscall_get_nr(current, regs); | |
fb34a08c JB |
108 | if (!test_bit(syscall_nr, enabled_enter_syscalls)) |
109 | return; | |
ee08c6ec | 110 | |
bed1ffca FW |
111 | sys_data = syscall_nr_to_meta(syscall_nr); |
112 | if (!sys_data) | |
113 | return; | |
114 | ||
115 | size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; | |
116 | ||
117 | event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size, | |
118 | 0, 0); | |
119 | if (!event) | |
120 | return; | |
121 | ||
122 | entry = ring_buffer_event_data(event); | |
123 | entry->nr = syscall_nr; | |
124 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args); | |
125 | ||
126 | trace_current_buffer_unlock_commit(event, 0, 0); | |
127 | trace_wake_up(); | |
ee08c6ec FW |
128 | } |
129 | ||
fb34a08c | 130 | void ftrace_syscall_exit(struct pt_regs *regs, long ret) |
ee08c6ec | 131 | { |
bed1ffca FW |
132 | struct syscall_trace_exit *entry; |
133 | struct syscall_metadata *sys_data; | |
134 | struct ring_buffer_event *event; | |
ee08c6ec FW |
135 | int syscall_nr; |
136 | ||
137 | syscall_nr = syscall_get_nr(current, regs); | |
fb34a08c JB |
138 | if (!test_bit(syscall_nr, enabled_exit_syscalls)) |
139 | return; | |
ee08c6ec | 140 | |
bed1ffca FW |
141 | sys_data = syscall_nr_to_meta(syscall_nr); |
142 | if (!sys_data) | |
143 | return; | |
144 | ||
145 | event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT, | |
146 | sizeof(*entry), 0, 0); | |
147 | if (!event) | |
148 | return; | |
149 | ||
150 | entry = ring_buffer_event_data(event); | |
151 | entry->nr = syscall_nr; | |
152 | entry->ret = syscall_get_return_value(current, regs); | |
153 | ||
154 | trace_current_buffer_unlock_commit(event, 0, 0); | |
155 | trace_wake_up(); | |
ee08c6ec FW |
156 | } |
157 | ||
fb34a08c | 158 | int reg_event_syscall_enter(void *ptr) |
ee08c6ec | 159 | { |
fb34a08c JB |
160 | int ret = 0; |
161 | int num; | |
162 | char *name; | |
163 | ||
164 | name = (char *)ptr; | |
165 | num = syscall_name_to_nr(name); | |
166 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
167 | return -ENOSYS; | |
168 | mutex_lock(&syscall_trace_lock); | |
169 | if (!sys_refcount_enter) | |
170 | ret = register_trace_syscall_enter(ftrace_syscall_enter); | |
171 | if (ret) { | |
172 | pr_info("event trace: Could not activate" | |
173 | "syscall entry trace point"); | |
174 | } else { | |
175 | set_bit(num, enabled_enter_syscalls); | |
176 | sys_refcount_enter++; | |
177 | } | |
178 | mutex_unlock(&syscall_trace_lock); | |
179 | return ret; | |
ee08c6ec FW |
180 | } |
181 | ||
fb34a08c | 182 | void unreg_event_syscall_enter(void *ptr) |
ee08c6ec | 183 | { |
fb34a08c JB |
184 | int num; |
185 | char *name; | |
ee08c6ec | 186 | |
fb34a08c JB |
187 | name = (char *)ptr; |
188 | num = syscall_name_to_nr(name); | |
189 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
190 | return; | |
191 | mutex_lock(&syscall_trace_lock); | |
192 | sys_refcount_enter--; | |
193 | clear_bit(num, enabled_enter_syscalls); | |
194 | if (!sys_refcount_enter) | |
195 | unregister_trace_syscall_enter(ftrace_syscall_enter); | |
196 | mutex_unlock(&syscall_trace_lock); | |
197 | } | |
ee08c6ec | 198 | |
fb34a08c | 199 | int reg_event_syscall_exit(void *ptr) |
ee08c6ec | 200 | { |
fb34a08c JB |
201 | int ret = 0; |
202 | int num; | |
203 | char *name; | |
204 | ||
205 | name = (char *)ptr; | |
206 | num = syscall_name_to_nr(name); | |
207 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
208 | return -ENOSYS; | |
209 | mutex_lock(&syscall_trace_lock); | |
210 | if (!sys_refcount_exit) | |
211 | ret = register_trace_syscall_exit(ftrace_syscall_exit); | |
212 | if (ret) { | |
213 | pr_info("event trace: Could not activate" | |
214 | "syscall exit trace point"); | |
215 | } else { | |
216 | set_bit(num, enabled_exit_syscalls); | |
217 | sys_refcount_exit++; | |
ee08c6ec | 218 | } |
fb34a08c JB |
219 | mutex_unlock(&syscall_trace_lock); |
220 | return ret; | |
221 | } | |
ee08c6ec | 222 | |
fb34a08c JB |
223 | void unreg_event_syscall_exit(void *ptr) |
224 | { | |
225 | int num; | |
226 | char *name; | |
ee08c6ec | 227 | |
fb34a08c JB |
228 | name = (char *)ptr; |
229 | num = syscall_name_to_nr(name); | |
230 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
231 | return; | |
232 | mutex_lock(&syscall_trace_lock); | |
233 | sys_refcount_exit--; | |
234 | clear_bit(num, enabled_exit_syscalls); | |
235 | if (!sys_refcount_exit) | |
236 | unregister_trace_syscall_exit(ftrace_syscall_exit); | |
237 | mutex_unlock(&syscall_trace_lock); | |
ee08c6ec | 238 | } |
fb34a08c JB |
239 | |
240 | struct trace_event event_syscall_enter = { | |
241 | .trace = print_syscall_enter, | |
242 | .type = TRACE_SYSCALL_ENTER | |
243 | }; | |
244 | ||
245 | struct trace_event event_syscall_exit = { | |
246 | .trace = print_syscall_exit, | |
247 | .type = TRACE_SYSCALL_EXIT | |
248 | }; |