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 | ||
64c12e04 | 39 | trace = (typeof(trace))ent; |
bed1ffca | 40 | syscall = trace->nr; |
bed1ffca | 41 | entry = syscall_nr_to_meta(syscall); |
64c12e04 | 42 | |
bed1ffca FW |
43 | if (!entry) |
44 | goto end; | |
45 | ||
64c12e04 JB |
46 | if (entry->enter_id != ent->type) { |
47 | WARN_ON_ONCE(1); | |
48 | goto end; | |
49 | } | |
50 | ||
bed1ffca FW |
51 | ret = trace_seq_printf(s, "%s(", entry->name); |
52 | if (!ret) | |
53 | return TRACE_TYPE_PARTIAL_LINE; | |
54 | ||
55 | for (i = 0; i < entry->nb_args; i++) { | |
56 | /* parameter types */ | |
57 | if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) { | |
58 | ret = trace_seq_printf(s, "%s ", entry->types[i]); | |
59 | if (!ret) | |
60 | return TRACE_TYPE_PARTIAL_LINE; | |
61 | } | |
62 | /* parameter values */ | |
63 | ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i], | |
64 | trace->args[i], | |
65 | i == entry->nb_args - 1 ? ")" : ","); | |
66 | if (!ret) | |
67 | return TRACE_TYPE_PARTIAL_LINE; | |
68 | } | |
69 | ||
70 | end: | |
71 | trace_seq_printf(s, "\n"); | |
72 | return TRACE_TYPE_HANDLED; | |
73 | } | |
74 | ||
75 | enum print_line_t | |
76 | print_syscall_exit(struct trace_iterator *iter, int flags) | |
77 | { | |
78 | struct trace_seq *s = &iter->seq; | |
79 | struct trace_entry *ent = iter->ent; | |
80 | struct syscall_trace_exit *trace; | |
81 | int syscall; | |
82 | struct syscall_metadata *entry; | |
83 | int ret; | |
84 | ||
64c12e04 | 85 | trace = (typeof(trace))ent; |
bed1ffca | 86 | syscall = trace->nr; |
bed1ffca | 87 | entry = syscall_nr_to_meta(syscall); |
64c12e04 | 88 | |
bed1ffca FW |
89 | if (!entry) { |
90 | trace_seq_printf(s, "\n"); | |
91 | return TRACE_TYPE_HANDLED; | |
92 | } | |
93 | ||
64c12e04 JB |
94 | if (entry->exit_id != ent->type) { |
95 | WARN_ON_ONCE(1); | |
96 | return TRACE_TYPE_UNHANDLED; | |
97 | } | |
98 | ||
bed1ffca FW |
99 | ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, |
100 | trace->ret); | |
101 | if (!ret) | |
102 | return TRACE_TYPE_PARTIAL_LINE; | |
103 | ||
104 | return TRACE_TYPE_HANDLED; | |
105 | } | |
106 | ||
fb34a08c | 107 | void ftrace_syscall_enter(struct pt_regs *regs, long id) |
ee08c6ec | 108 | { |
bed1ffca FW |
109 | struct syscall_trace_enter *entry; |
110 | struct syscall_metadata *sys_data; | |
111 | struct ring_buffer_event *event; | |
112 | int size; | |
ee08c6ec FW |
113 | int syscall_nr; |
114 | ||
115 | syscall_nr = syscall_get_nr(current, regs); | |
fb34a08c JB |
116 | if (!test_bit(syscall_nr, enabled_enter_syscalls)) |
117 | return; | |
ee08c6ec | 118 | |
bed1ffca FW |
119 | sys_data = syscall_nr_to_meta(syscall_nr); |
120 | if (!sys_data) | |
121 | return; | |
122 | ||
123 | size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; | |
124 | ||
64c12e04 | 125 | event = trace_current_buffer_lock_reserve(sys_data->enter_id, size, |
bed1ffca FW |
126 | 0, 0); |
127 | if (!event) | |
128 | return; | |
129 | ||
130 | entry = ring_buffer_event_data(event); | |
131 | entry->nr = syscall_nr; | |
132 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args); | |
133 | ||
134 | trace_current_buffer_unlock_commit(event, 0, 0); | |
135 | trace_wake_up(); | |
ee08c6ec FW |
136 | } |
137 | ||
fb34a08c | 138 | void ftrace_syscall_exit(struct pt_regs *regs, long ret) |
ee08c6ec | 139 | { |
bed1ffca FW |
140 | struct syscall_trace_exit *entry; |
141 | struct syscall_metadata *sys_data; | |
142 | struct ring_buffer_event *event; | |
ee08c6ec FW |
143 | int syscall_nr; |
144 | ||
145 | syscall_nr = syscall_get_nr(current, regs); | |
fb34a08c JB |
146 | if (!test_bit(syscall_nr, enabled_exit_syscalls)) |
147 | return; | |
ee08c6ec | 148 | |
bed1ffca FW |
149 | sys_data = syscall_nr_to_meta(syscall_nr); |
150 | if (!sys_data) | |
151 | return; | |
152 | ||
64c12e04 | 153 | event = trace_current_buffer_lock_reserve(sys_data->exit_id, |
bed1ffca FW |
154 | sizeof(*entry), 0, 0); |
155 | if (!event) | |
156 | return; | |
157 | ||
158 | entry = ring_buffer_event_data(event); | |
159 | entry->nr = syscall_nr; | |
160 | entry->ret = syscall_get_return_value(current, regs); | |
161 | ||
162 | trace_current_buffer_unlock_commit(event, 0, 0); | |
163 | trace_wake_up(); | |
ee08c6ec FW |
164 | } |
165 | ||
fb34a08c | 166 | int reg_event_syscall_enter(void *ptr) |
ee08c6ec | 167 | { |
fb34a08c JB |
168 | int ret = 0; |
169 | int num; | |
170 | char *name; | |
171 | ||
172 | name = (char *)ptr; | |
173 | num = syscall_name_to_nr(name); | |
174 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
175 | return -ENOSYS; | |
176 | mutex_lock(&syscall_trace_lock); | |
177 | if (!sys_refcount_enter) | |
178 | ret = register_trace_syscall_enter(ftrace_syscall_enter); | |
179 | if (ret) { | |
180 | pr_info("event trace: Could not activate" | |
181 | "syscall entry trace point"); | |
182 | } else { | |
183 | set_bit(num, enabled_enter_syscalls); | |
184 | sys_refcount_enter++; | |
185 | } | |
186 | mutex_unlock(&syscall_trace_lock); | |
187 | return ret; | |
ee08c6ec FW |
188 | } |
189 | ||
fb34a08c | 190 | void unreg_event_syscall_enter(void *ptr) |
ee08c6ec | 191 | { |
fb34a08c JB |
192 | int num; |
193 | char *name; | |
ee08c6ec | 194 | |
fb34a08c JB |
195 | name = (char *)ptr; |
196 | num = syscall_name_to_nr(name); | |
197 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
198 | return; | |
199 | mutex_lock(&syscall_trace_lock); | |
200 | sys_refcount_enter--; | |
201 | clear_bit(num, enabled_enter_syscalls); | |
202 | if (!sys_refcount_enter) | |
203 | unregister_trace_syscall_enter(ftrace_syscall_enter); | |
204 | mutex_unlock(&syscall_trace_lock); | |
205 | } | |
ee08c6ec | 206 | |
fb34a08c | 207 | int reg_event_syscall_exit(void *ptr) |
ee08c6ec | 208 | { |
fb34a08c JB |
209 | int ret = 0; |
210 | int num; | |
211 | char *name; | |
212 | ||
213 | name = (char *)ptr; | |
214 | num = syscall_name_to_nr(name); | |
215 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
216 | return -ENOSYS; | |
217 | mutex_lock(&syscall_trace_lock); | |
218 | if (!sys_refcount_exit) | |
219 | ret = register_trace_syscall_exit(ftrace_syscall_exit); | |
220 | if (ret) { | |
221 | pr_info("event trace: Could not activate" | |
222 | "syscall exit trace point"); | |
223 | } else { | |
224 | set_bit(num, enabled_exit_syscalls); | |
225 | sys_refcount_exit++; | |
ee08c6ec | 226 | } |
fb34a08c JB |
227 | mutex_unlock(&syscall_trace_lock); |
228 | return ret; | |
229 | } | |
ee08c6ec | 230 | |
fb34a08c JB |
231 | void unreg_event_syscall_exit(void *ptr) |
232 | { | |
233 | int num; | |
234 | char *name; | |
ee08c6ec | 235 | |
fb34a08c JB |
236 | name = (char *)ptr; |
237 | num = syscall_name_to_nr(name); | |
238 | if (num < 0 || num >= FTRACE_SYSCALL_MAX) | |
239 | return; | |
240 | mutex_lock(&syscall_trace_lock); | |
241 | sys_refcount_exit--; | |
242 | clear_bit(num, enabled_exit_syscalls); | |
243 | if (!sys_refcount_exit) | |
244 | unregister_trace_syscall_exit(ftrace_syscall_exit); | |
245 | mutex_unlock(&syscall_trace_lock); | |
ee08c6ec | 246 | } |
fb34a08c JB |
247 | |
248 | struct trace_event event_syscall_enter = { | |
249 | .trace = print_syscall_enter, | |
fb34a08c JB |
250 | }; |
251 | ||
252 | struct trace_event event_syscall_exit = { | |
253 | .trace = print_syscall_exit, | |
fb34a08c | 254 | }; |