Commit | Line | Data |
---|---|---|
47788c58 | 1 | #include <trace/syscall.h> |
ee08c6ec | 2 | #include <linux/kernel.h> |
fb34a08c | 3 | #include <linux/ftrace.h> |
f4b5ffcc | 4 | #include <linux/perf_counter.h> |
ee08c6ec FW |
5 | #include <asm/syscall.h> |
6 | ||
7 | #include "trace_output.h" | |
8 | #include "trace.h" | |
9 | ||
5be71b61 | 10 | static DEFINE_MUTEX(syscall_trace_lock); |
fb34a08c JB |
11 | static int sys_refcount_enter; |
12 | static int sys_refcount_exit; | |
13 | static DECLARE_BITMAP(enabled_enter_syscalls, FTRACE_SYSCALL_MAX); | |
14 | static DECLARE_BITMAP(enabled_exit_syscalls, FTRACE_SYSCALL_MAX); | |
ee08c6ec | 15 | |
64044345 | 16 | /* Option to display the parameters types */ |
bed1ffca FW |
17 | enum { |
18 | TRACE_SYSCALLS_OPT_TYPES = 0x1, | |
19 | }; | |
20 | ||
21 | static struct tracer_opt syscalls_opts[] = { | |
22 | { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) }, | |
23 | { } | |
24 | }; | |
25 | ||
26 | static struct tracer_flags syscalls_flags = { | |
64044345 | 27 | .val = 0, /* By default: no parameters types */ |
bed1ffca FW |
28 | .opts = syscalls_opts |
29 | }; | |
30 | ||
31 | enum print_line_t | |
32 | print_syscall_enter(struct trace_iterator *iter, int flags) | |
33 | { | |
34 | struct trace_seq *s = &iter->seq; | |
35 | struct trace_entry *ent = iter->ent; | |
36 | struct syscall_trace_enter *trace; | |
37 | struct syscall_metadata *entry; | |
38 | int i, ret, syscall; | |
39 | ||
64c12e04 | 40 | trace = (typeof(trace))ent; |
bed1ffca | 41 | syscall = trace->nr; |
bed1ffca | 42 | entry = syscall_nr_to_meta(syscall); |
64c12e04 | 43 | |
bed1ffca FW |
44 | if (!entry) |
45 | goto end; | |
46 | ||
64c12e04 JB |
47 | if (entry->enter_id != ent->type) { |
48 | WARN_ON_ONCE(1); | |
49 | goto end; | |
50 | } | |
51 | ||
bed1ffca FW |
52 | ret = trace_seq_printf(s, "%s(", entry->name); |
53 | if (!ret) | |
54 | return TRACE_TYPE_PARTIAL_LINE; | |
55 | ||
56 | for (i = 0; i < entry->nb_args; i++) { | |
57 | /* parameter types */ | |
58 | if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) { | |
59 | ret = trace_seq_printf(s, "%s ", entry->types[i]); | |
60 | if (!ret) | |
61 | return TRACE_TYPE_PARTIAL_LINE; | |
62 | } | |
63 | /* parameter values */ | |
64 | ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i], | |
65 | trace->args[i], | |
66 | i == entry->nb_args - 1 ? ")" : ","); | |
67 | if (!ret) | |
68 | return TRACE_TYPE_PARTIAL_LINE; | |
69 | } | |
70 | ||
71 | end: | |
72 | trace_seq_printf(s, "\n"); | |
73 | return TRACE_TYPE_HANDLED; | |
74 | } | |
75 | ||
76 | enum print_line_t | |
77 | print_syscall_exit(struct trace_iterator *iter, int flags) | |
78 | { | |
79 | struct trace_seq *s = &iter->seq; | |
80 | struct trace_entry *ent = iter->ent; | |
81 | struct syscall_trace_exit *trace; | |
82 | int syscall; | |
83 | struct syscall_metadata *entry; | |
84 | int ret; | |
85 | ||
64c12e04 | 86 | trace = (typeof(trace))ent; |
bed1ffca | 87 | syscall = trace->nr; |
bed1ffca | 88 | entry = syscall_nr_to_meta(syscall); |
64c12e04 | 89 | |
bed1ffca FW |
90 | if (!entry) { |
91 | trace_seq_printf(s, "\n"); | |
92 | return TRACE_TYPE_HANDLED; | |
93 | } | |
94 | ||
64c12e04 JB |
95 | if (entry->exit_id != ent->type) { |
96 | WARN_ON_ONCE(1); | |
97 | return TRACE_TYPE_UNHANDLED; | |
98 | } | |
99 | ||
bed1ffca FW |
100 | ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, |
101 | trace->ret); | |
102 | if (!ret) | |
103 | return TRACE_TYPE_PARTIAL_LINE; | |
104 | ||
105 | return TRACE_TYPE_HANDLED; | |
106 | } | |
107 | ||
dc4ddb4c FW |
108 | int ftrace_format_syscall(struct ftrace_event_call *call, struct trace_seq *s) |
109 | { | |
110 | int i; | |
111 | int nr; | |
112 | int ret = 0; | |
113 | struct syscall_metadata *entry; | |
114 | int offset = sizeof(struct trace_entry); | |
115 | ||
116 | nr = syscall_name_to_nr((char *)call->data); | |
117 | entry = syscall_nr_to_meta(nr); | |
118 | ||
119 | if (!entry) | |
120 | return ret; | |
121 | ||
122 | for (i = 0; i < entry->nb_args; i++) { | |
123 | ret = trace_seq_printf(s, "\tfield:%s %s;", entry->types[i], | |
124 | entry->args[i]); | |
125 | if (!ret) | |
126 | return 0; | |
127 | ret = trace_seq_printf(s, "\toffset:%d;\tsize:%lu;\n", offset, | |
128 | sizeof(unsigned long)); | |
129 | if (!ret) | |
130 | return 0; | |
131 | offset += sizeof(unsigned long); | |
132 | } | |
133 | ||
134 | trace_seq_printf(s, "\nprint fmt: \""); | |
135 | for (i = 0; i < entry->nb_args; i++) { | |
136 | ret = trace_seq_printf(s, "%s: 0x%%0%lulx%s", entry->args[i], | |
137 | sizeof(unsigned long), | |
138 | i == entry->nb_args - 1 ? "\", " : ", "); | |
139 | if (!ret) | |
140 | return 0; | |
141 | } | |
142 | ||
143 | for (i = 0; i < entry->nb_args; i++) { | |
144 | ret = trace_seq_printf(s, "((unsigned long)(REC->%s))%s", | |
145 | entry->args[i], | |
146 | i == entry->nb_args - 1 ? "\n" : ", "); | |
147 | if (!ret) | |
148 | return 0; | |
149 | } | |
150 | ||
151 | return ret; | |
152 | } | |
153 | ||
fb34a08c | 154 | void ftrace_syscall_enter(struct pt_regs *regs, long id) |
ee08c6ec | 155 | { |
bed1ffca FW |
156 | struct syscall_trace_enter *entry; |
157 | struct syscall_metadata *sys_data; | |
158 | struct ring_buffer_event *event; | |
159 | int size; | |
ee08c6ec FW |
160 | int syscall_nr; |
161 | ||
162 | syscall_nr = syscall_get_nr(current, regs); | |
fb34a08c JB |
163 | if (!test_bit(syscall_nr, enabled_enter_syscalls)) |
164 | return; | |
ee08c6ec | 165 | |
bed1ffca FW |
166 | sys_data = syscall_nr_to_meta(syscall_nr); |
167 | if (!sys_data) | |
168 | return; | |
169 | ||
170 | size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; | |
171 | ||
64c12e04 | 172 | event = trace_current_buffer_lock_reserve(sys_data->enter_id, size, |
bed1ffca FW |
173 | 0, 0); |
174 | if (!event) | |
175 | return; | |
176 | ||
177 | entry = ring_buffer_event_data(event); | |
178 | entry->nr = syscall_nr; | |
179 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args); | |
180 | ||
181 | trace_current_buffer_unlock_commit(event, 0, 0); | |
182 | trace_wake_up(); | |
ee08c6ec FW |
183 | } |
184 | ||
fb34a08c | 185 | void ftrace_syscall_exit(struct pt_regs *regs, long ret) |
ee08c6ec | 186 | { |
bed1ffca FW |
187 | struct syscall_trace_exit *entry; |
188 | struct syscall_metadata *sys_data; | |
189 | struct ring_buffer_event *event; | |
ee08c6ec FW |
190 | int syscall_nr; |
191 | ||
192 | syscall_nr = syscall_get_nr(current, regs); | |
fb34a08c JB |
193 | if (!test_bit(syscall_nr, enabled_exit_syscalls)) |
194 | return; | |
ee08c6ec | 195 | |
bed1ffca FW |
196 | sys_data = syscall_nr_to_meta(syscall_nr); |
197 | if (!sys_data) | |
198 | return; | |
199 | ||
64c12e04 | 200 | event = trace_current_buffer_lock_reserve(sys_data->exit_id, |
bed1ffca FW |
201 | sizeof(*entry), 0, 0); |
202 | if (!event) | |
203 | return; | |
204 | ||
205 | entry = ring_buffer_event_data(event); | |
206 | entry->nr = syscall_nr; | |
207 | entry->ret = syscall_get_return_value(current, regs); | |
208 | ||
209 | trace_current_buffer_unlock_commit(event, 0, 0); | |
210 | trace_wake_up(); | |
ee08c6ec FW |
211 | } |
212 | ||
fb34a08c | 213 | int reg_event_syscall_enter(void *ptr) |
ee08c6ec | 214 | { |
fb34a08c JB |
215 | int ret = 0; |
216 | int num; | |
217 | char *name; | |
218 | ||
219 | name = (char *)ptr; | |
220 | num = syscall_name_to_nr(name); | |
221 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
222 | return -ENOSYS; | |
223 | mutex_lock(&syscall_trace_lock); | |
224 | if (!sys_refcount_enter) | |
225 | ret = register_trace_syscall_enter(ftrace_syscall_enter); | |
226 | if (ret) { | |
227 | pr_info("event trace: Could not activate" | |
228 | "syscall entry trace point"); | |
229 | } else { | |
230 | set_bit(num, enabled_enter_syscalls); | |
231 | sys_refcount_enter++; | |
232 | } | |
233 | mutex_unlock(&syscall_trace_lock); | |
234 | return ret; | |
ee08c6ec FW |
235 | } |
236 | ||
fb34a08c | 237 | void unreg_event_syscall_enter(void *ptr) |
ee08c6ec | 238 | { |
fb34a08c JB |
239 | int num; |
240 | char *name; | |
ee08c6ec | 241 | |
fb34a08c JB |
242 | name = (char *)ptr; |
243 | num = syscall_name_to_nr(name); | |
244 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
245 | return; | |
246 | mutex_lock(&syscall_trace_lock); | |
247 | sys_refcount_enter--; | |
248 | clear_bit(num, enabled_enter_syscalls); | |
249 | if (!sys_refcount_enter) | |
250 | unregister_trace_syscall_enter(ftrace_syscall_enter); | |
251 | mutex_unlock(&syscall_trace_lock); | |
252 | } | |
ee08c6ec | 253 | |
fb34a08c | 254 | int reg_event_syscall_exit(void *ptr) |
ee08c6ec | 255 | { |
fb34a08c JB |
256 | int ret = 0; |
257 | int num; | |
258 | char *name; | |
259 | ||
260 | name = (char *)ptr; | |
261 | num = syscall_name_to_nr(name); | |
262 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
263 | return -ENOSYS; | |
264 | mutex_lock(&syscall_trace_lock); | |
265 | if (!sys_refcount_exit) | |
266 | ret = register_trace_syscall_exit(ftrace_syscall_exit); | |
267 | if (ret) { | |
268 | pr_info("event trace: Could not activate" | |
269 | "syscall exit trace point"); | |
270 | } else { | |
271 | set_bit(num, enabled_exit_syscalls); | |
272 | sys_refcount_exit++; | |
ee08c6ec | 273 | } |
fb34a08c JB |
274 | mutex_unlock(&syscall_trace_lock); |
275 | return ret; | |
276 | } | |
ee08c6ec | 277 | |
fb34a08c JB |
278 | void unreg_event_syscall_exit(void *ptr) |
279 | { | |
280 | int num; | |
281 | char *name; | |
ee08c6ec | 282 | |
fb34a08c JB |
283 | name = (char *)ptr; |
284 | num = syscall_name_to_nr(name); | |
285 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
286 | return; | |
287 | mutex_lock(&syscall_trace_lock); | |
288 | sys_refcount_exit--; | |
289 | clear_bit(num, enabled_exit_syscalls); | |
290 | if (!sys_refcount_exit) | |
291 | unregister_trace_syscall_exit(ftrace_syscall_exit); | |
292 | mutex_unlock(&syscall_trace_lock); | |
ee08c6ec | 293 | } |
fb34a08c JB |
294 | |
295 | struct trace_event event_syscall_enter = { | |
296 | .trace = print_syscall_enter, | |
fb34a08c JB |
297 | }; |
298 | ||
299 | struct trace_event event_syscall_exit = { | |
300 | .trace = print_syscall_exit, | |
fb34a08c | 301 | }; |
f4b5ffcc JB |
302 | |
303 | #ifdef CONFIG_EVENT_PROFILE | |
304 | static DECLARE_BITMAP(enabled_prof_enter_syscalls, FTRACE_SYSCALL_MAX); | |
305 | static DECLARE_BITMAP(enabled_prof_exit_syscalls, FTRACE_SYSCALL_MAX); | |
306 | static int sys_prof_refcount_enter; | |
307 | static int sys_prof_refcount_exit; | |
308 | ||
309 | static void prof_syscall_enter(struct pt_regs *regs, long id) | |
310 | { | |
311 | struct syscall_metadata *sys_data; | |
312 | int syscall_nr; | |
313 | ||
314 | syscall_nr = syscall_get_nr(current, regs); | |
315 | if (!test_bit(syscall_nr, enabled_prof_enter_syscalls)) | |
316 | return; | |
317 | ||
318 | sys_data = syscall_nr_to_meta(syscall_nr); | |
319 | if (!sys_data) | |
320 | return; | |
321 | ||
322 | perf_tpcounter_event(sys_data->enter_id, 0, 1, NULL, 0); | |
323 | } | |
324 | ||
325 | int reg_prof_syscall_enter(char *name) | |
326 | { | |
327 | int ret = 0; | |
328 | int num; | |
329 | ||
330 | num = syscall_name_to_nr(name); | |
331 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
332 | return -ENOSYS; | |
333 | ||
334 | mutex_lock(&syscall_trace_lock); | |
335 | if (!sys_prof_refcount_enter) | |
336 | ret = register_trace_syscall_enter(prof_syscall_enter); | |
337 | if (ret) { | |
338 | pr_info("event trace: Could not activate" | |
339 | "syscall entry trace point"); | |
340 | } else { | |
341 | set_bit(num, enabled_prof_enter_syscalls); | |
342 | sys_prof_refcount_enter++; | |
343 | } | |
344 | mutex_unlock(&syscall_trace_lock); | |
345 | return ret; | |
346 | } | |
347 | ||
348 | void unreg_prof_syscall_enter(char *name) | |
349 | { | |
350 | int num; | |
351 | ||
352 | num = syscall_name_to_nr(name); | |
353 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
354 | return; | |
355 | ||
356 | mutex_lock(&syscall_trace_lock); | |
357 | sys_prof_refcount_enter--; | |
358 | clear_bit(num, enabled_prof_enter_syscalls); | |
359 | if (!sys_prof_refcount_enter) | |
360 | unregister_trace_syscall_enter(prof_syscall_enter); | |
361 | mutex_unlock(&syscall_trace_lock); | |
362 | } | |
363 | ||
364 | static void prof_syscall_exit(struct pt_regs *regs, long ret) | |
365 | { | |
366 | struct syscall_metadata *sys_data; | |
367 | int syscall_nr; | |
368 | ||
369 | syscall_nr = syscall_get_nr(current, regs); | |
370 | if (!test_bit(syscall_nr, enabled_prof_exit_syscalls)) | |
371 | return; | |
372 | ||
373 | sys_data = syscall_nr_to_meta(syscall_nr); | |
374 | if (!sys_data) | |
375 | return; | |
376 | ||
377 | perf_tpcounter_event(sys_data->exit_id, 0, 1, NULL, 0); | |
378 | } | |
379 | ||
380 | int reg_prof_syscall_exit(char *name) | |
381 | { | |
382 | int ret = 0; | |
383 | int num; | |
384 | ||
385 | num = syscall_name_to_nr(name); | |
386 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
387 | return -ENOSYS; | |
388 | ||
389 | mutex_lock(&syscall_trace_lock); | |
390 | if (!sys_prof_refcount_exit) | |
391 | ret = register_trace_syscall_exit(prof_syscall_exit); | |
392 | if (ret) { | |
393 | pr_info("event trace: Could not activate" | |
394 | "syscall entry trace point"); | |
395 | } else { | |
396 | set_bit(num, enabled_prof_exit_syscalls); | |
397 | sys_prof_refcount_exit++; | |
398 | } | |
399 | mutex_unlock(&syscall_trace_lock); | |
400 | return ret; | |
401 | } | |
402 | ||
403 | void unreg_prof_syscall_exit(char *name) | |
404 | { | |
405 | int num; | |
406 | ||
407 | num = syscall_name_to_nr(name); | |
408 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
409 | return; | |
410 | ||
411 | mutex_lock(&syscall_trace_lock); | |
412 | sys_prof_refcount_exit--; | |
413 | clear_bit(num, enabled_prof_exit_syscalls); | |
414 | if (!sys_prof_refcount_exit) | |
415 | unregister_trace_syscall_exit(prof_syscall_exit); | |
416 | mutex_unlock(&syscall_trace_lock); | |
417 | } | |
418 | ||
419 | #endif | |
420 | ||
421 |