Add config compat support for detailed x86_32 syscalls
[lttng-modules.git] / lttng-syscalls.c
1 /*
2 * lttng-syscalls.c
3 *
4 * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
5 *
6 * LTTng sched probes.
7 *
8 * Dual LGPL v2.1/GPL v2 license.
9 */
10
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <linux/compat.h>
14 #include <asm/ptrace.h>
15 #include <asm/syscall.h>
16
17 #include "ltt-events.h"
18
19 #ifndef CONFIG_COMPAT
20 static inline int is_compat_task(void)
21 {
22 return 0;
23 }
24 #endif
25
26 static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id);
27
28 /*
29 * Take care of NOARGS not supported by mainline.
30 */
31 #define DECLARE_EVENT_CLASS_NOARGS(name, tstruct, assign, print)
32 #define DEFINE_EVENT_NOARGS(template, name)
33 #define TRACE_EVENT_NOARGS(name, struct, assign, print)
34
35 /*
36 * Create LTTng tracepoint probes.
37 */
38 #define LTTNG_PACKAGE_BUILD
39 #define CREATE_TRACE_POINTS
40 #define TP_MODULE_OVERRIDE
41 #define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers
42
43 /* Hijack probe callback for system calls */
44 #define TP_PROBE_CB(_template) &syscall_entry_probe
45 #include "instrumentation/syscalls/headers/syscalls_integers.h"
46 #include "instrumentation/syscalls/headers/syscalls_pointers.h"
47 #undef TP_PROBE_CB
48
49 #include "instrumentation/syscalls/headers/syscalls_unknown.h"
50
51 #undef TP_MODULE_OVERRIDE
52 #undef LTTNG_PACKAGE_BUILD
53 #undef CREATE_TRACE_POINTS
54
55 struct trace_syscall_entry {
56 void *func;
57 const struct lttng_event_desc *desc;
58 const struct lttng_event_field *fields;
59 unsigned int nrargs;
60 };
61
62 #undef TRACE_SYSCALL_TABLE
63 #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
64 [ _nr ] = { \
65 .func = __event_probe__##_template, \
66 .nrargs = (_nrargs), \
67 .fields = __event_fields___##_template, \
68 .desc = &__event_desc___##_name, \
69 },
70
71 #define CREATE_SYSCALL_TABLE
72
73 static const struct trace_syscall_entry sc_table[] = {
74 #include "instrumentation/syscalls/headers/syscalls_integers.h"
75 #include "instrumentation/syscalls/headers/syscalls_pointers.h"
76 };
77
78 /* Create compatibility syscall table */
79 static const struct trace_syscall_entry compat_sc_table[] = {
80 #include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
81 #include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
82 };
83
84 #undef CREATE_SYSCALL_TABLE
85
86 static void syscall_entry_unknown(struct ltt_event *event,
87 struct pt_regs *regs, unsigned int id)
88 {
89 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
90
91 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
92 __event_probe__sys_unknown(event, id, args);
93 }
94
95 /*
96 * Currently, given that the kernel syscall metadata extraction only
97 * considers native system calls (not 32-bit compability ones), we
98 * fall-back on the "unknown" system call tracing for 32-bit compat.
99 */
100 static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
101 {
102 struct ltt_channel *chan = __data;
103 struct ltt_event *event, *unknown_event;
104 const struct trace_syscall_entry *table, *entry;
105 size_t table_len;
106
107 if (unlikely(is_compat_task())) {
108 table = compat_sc_table;
109 table_len = ARRAY_SIZE(compat_sc_table);
110 unknown_event = chan->sc_compat_unknown;
111 } else {
112 table = sc_table;
113 table_len = ARRAY_SIZE(sc_table);
114 unknown_event = chan->sc_unknown;
115 }
116 if (unlikely(id >= table_len)) {
117 syscall_entry_unknown(unknown_event, regs, id);
118 return;
119 }
120 if (unlikely(is_compat_task()))
121 event = chan->compat_sc_table[id];
122 else
123 event = chan->sc_table[id];
124 if (unlikely(!event)) {
125 syscall_entry_unknown(unknown_event, regs, id);
126 return;
127 }
128 entry = &table[id];
129 WARN_ON_ONCE(!entry);
130
131 switch (entry->nrargs) {
132 case 0:
133 {
134 void (*fptr)(void *__data) = entry->func;
135
136 fptr(event);
137 break;
138 }
139 case 1:
140 {
141 void (*fptr)(void *__data, unsigned long arg0) = entry->func;
142 unsigned long args[1];
143
144 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
145 fptr(event, args[0]);
146 break;
147 }
148 case 2:
149 {
150 void (*fptr)(void *__data,
151 unsigned long arg0,
152 unsigned long arg1) = entry->func;
153 unsigned long args[2];
154
155 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
156 fptr(event, args[0], args[1]);
157 break;
158 }
159 case 3:
160 {
161 void (*fptr)(void *__data,
162 unsigned long arg0,
163 unsigned long arg1,
164 unsigned long arg2) = entry->func;
165 unsigned long args[3];
166
167 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
168 fptr(event, args[0], args[1], args[2]);
169 break;
170 }
171 case 4:
172 {
173 void (*fptr)(void *__data,
174 unsigned long arg0,
175 unsigned long arg1,
176 unsigned long arg2,
177 unsigned long arg3) = entry->func;
178 unsigned long args[4];
179
180 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
181 fptr(event, args[0], args[1], args[2], args[3]);
182 break;
183 }
184 case 5:
185 {
186 void (*fptr)(void *__data,
187 unsigned long arg0,
188 unsigned long arg1,
189 unsigned long arg2,
190 unsigned long arg3,
191 unsigned long arg4) = entry->func;
192 unsigned long args[5];
193
194 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
195 fptr(event, args[0], args[1], args[2], args[3], args[4]);
196 break;
197 }
198 case 6:
199 {
200 void (*fptr)(void *__data,
201 unsigned long arg0,
202 unsigned long arg1,
203 unsigned long arg2,
204 unsigned long arg3,
205 unsigned long arg4,
206 unsigned long arg5) = entry->func;
207 unsigned long args[6];
208
209 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
210 fptr(event, args[0], args[1], args[2],
211 args[3], args[4], args[5]);
212 break;
213 }
214 default:
215 break;
216 }
217 }
218
219 static
220 int fill_table(const struct trace_syscall_entry *table, size_t table_len,
221 struct ltt_event **chan_table, struct ltt_channel *chan, void *filter)
222 {
223 unsigned int i;
224
225 /* Allocate events for each syscall, insert into table */
226 for (i = 0; i < table_len; i++) {
227 struct lttng_kernel_event ev;
228 const struct lttng_event_desc *desc = table[i].desc;
229
230 if (!desc) {
231 /* Unknown syscall */
232 continue;
233 }
234 /*
235 * Skip those already populated by previous failed
236 * register for this channel.
237 */
238 if (chan_table[i])
239 continue;
240 memset(&ev, 0, sizeof(ev));
241 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
242 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
243 ev.instrumentation = LTTNG_KERNEL_NOOP;
244 chan_table[i] = ltt_event_create(chan, &ev, filter,
245 desc);
246 if (!chan_table[i]) {
247 /*
248 * If something goes wrong in event registration
249 * after the first one, we have no choice but to
250 * leave the previous events in there, until
251 * deleted by session teardown.
252 */
253 return -EINVAL;
254 }
255 }
256 return 0;
257 }
258
259 int lttng_syscalls_register(struct ltt_channel *chan, void *filter)
260 {
261 int ret;
262
263 wrapper_vmalloc_sync_all();
264
265 if (!chan->sc_table) {
266 /* create syscall table mapping syscall to events */
267 chan->sc_table = kzalloc(sizeof(struct ltt_event *)
268 * ARRAY_SIZE(sc_table), GFP_KERNEL);
269 if (!chan->sc_table)
270 return -ENOMEM;
271 }
272
273 #ifdef CONFIG_COMPAT
274 if (!chan->compat_sc_table) {
275 /* create syscall table mapping compat syscall to events */
276 chan->compat_sc_table = kzalloc(sizeof(struct ltt_event *)
277 * ARRAY_SIZE(compat_sc_table), GFP_KERNEL);
278 if (!chan->compat_sc_table)
279 return -ENOMEM;
280 }
281 #endif
282 if (!chan->sc_unknown) {
283 struct lttng_kernel_event ev;
284 const struct lttng_event_desc *desc =
285 &__event_desc___sys_unknown;
286
287 memset(&ev, 0, sizeof(ev));
288 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
289 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
290 ev.instrumentation = LTTNG_KERNEL_NOOP;
291 chan->sc_unknown = ltt_event_create(chan, &ev, filter,
292 desc);
293 if (!chan->sc_unknown) {
294 return -EINVAL;
295 }
296 }
297
298 if (!chan->sc_compat_unknown) {
299 struct lttng_kernel_event ev;
300 const struct lttng_event_desc *desc =
301 &__event_desc___compat_sys_unknown;
302
303 memset(&ev, 0, sizeof(ev));
304 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
305 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
306 ev.instrumentation = LTTNG_KERNEL_NOOP;
307 chan->sc_compat_unknown = ltt_event_create(chan, &ev, filter,
308 desc);
309 if (!chan->sc_compat_unknown) {
310 return -EINVAL;
311 }
312 }
313
314 if (!chan->sc_exit) {
315 struct lttng_kernel_event ev;
316 const struct lttng_event_desc *desc =
317 &__event_desc___exit_syscall;
318
319 memset(&ev, 0, sizeof(ev));
320 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
321 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
322 ev.instrumentation = LTTNG_KERNEL_NOOP;
323 chan->sc_exit = ltt_event_create(chan, &ev, filter,
324 desc);
325 if (!chan->sc_exit) {
326 return -EINVAL;
327 }
328 }
329
330 ret = fill_table(sc_table, ARRAY_SIZE(sc_table),
331 chan->sc_table, chan, filter);
332 if (ret)
333 return ret;
334 #ifdef CONFIG_COMPAT
335 ret = fill_table(compat_sc_table, ARRAY_SIZE(compat_sc_table),
336 chan->compat_sc_table, chan, filter);
337 if (ret)
338 return ret;
339 #endif
340 ret = tracepoint_probe_register("sys_enter",
341 (void *) syscall_entry_probe, chan);
342 if (ret)
343 return ret;
344 /*
345 * We change the name of sys_exit tracepoint due to namespace
346 * conflict with sys_exit syscall entry.
347 */
348 ret = tracepoint_probe_register("sys_exit",
349 (void *) __event_probe__exit_syscall,
350 chan->sc_exit);
351 if (ret) {
352 WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter",
353 (void *) syscall_entry_probe, chan));
354 }
355 return ret;
356 }
357
358 /*
359 * Only called at session destruction.
360 */
361 int lttng_syscalls_unregister(struct ltt_channel *chan)
362 {
363 int ret;
364
365 if (!chan->sc_table)
366 return 0;
367 ret = tracepoint_probe_unregister("sys_exit",
368 (void *) __event_probe__exit_syscall,
369 chan->sc_exit);
370 if (ret)
371 return ret;
372 ret = tracepoint_probe_unregister("sys_enter",
373 (void *) syscall_entry_probe, chan);
374 if (ret)
375 return ret;
376 /* ltt_event destroy will be performed by ltt_session_destroy() */
377 kfree(chan->sc_table);
378 #ifdef CONFIG_COMPAT
379 kfree(chan->compat_sc_table);
380 #endif
381 return 0;
382 }
This page took 0.03713 seconds and 5 git commands to generate.