Add config compat support for detailed x86_32 syscalls
[lttng-modules.git] / lttng-syscalls.c
CommitLineData
259b6cb3
MD
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>
6333ace3 13#include <linux/compat.h>
259b6cb3
MD
14#include <asm/ptrace.h>
15#include <asm/syscall.h>
16
17#include "ltt-events.h"
18
6333ace3
MD
19#ifndef CONFIG_COMPAT
20static inline int is_compat_task(void)
21{
22 return 0;
23}
24#endif
25
259b6cb3
MD
26static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id);
27
f7bdf4db
MD
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
259b6cb3
MD
35/*
36 * Create LTTng tracepoint probes.
37 */
38#define LTTNG_PACKAGE_BUILD
39#define CREATE_TRACE_POINTS
2f804c0a
MD
40#define TP_MODULE_OVERRIDE
41#define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers
259b6cb3
MD
42
43/* Hijack probe callback for system calls */
44#define TP_PROBE_CB(_template) &syscall_entry_probe
177b3692
MD
45#include "instrumentation/syscalls/headers/syscalls_integers.h"
46#include "instrumentation/syscalls/headers/syscalls_pointers.h"
2f804c0a
MD
47#undef TP_PROBE_CB
48
63728b02 49#include "instrumentation/syscalls/headers/syscalls_unknown.h"
259b6cb3
MD
50
51#undef TP_MODULE_OVERRIDE
259b6cb3
MD
52#undef LTTNG_PACKAGE_BUILD
53#undef CREATE_TRACE_POINTS
54
55struct trace_syscall_entry {
56 void *func;
f7bdf4db 57 const struct lttng_event_desc *desc;
259b6cb3
MD
58 const struct lttng_event_field *fields;
59 unsigned int nrargs;
60};
61
259b6cb3 62#undef TRACE_SYSCALL_TABLE
f7bdf4db 63#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
259b6cb3 64 [ _nr ] = { \
f7bdf4db 65 .func = __event_probe__##_template, \
259b6cb3 66 .nrargs = (_nrargs), \
f7bdf4db
MD
67 .fields = __event_fields___##_template, \
68 .desc = &__event_desc___##_name, \
259b6cb3
MD
69 },
70
49c50022
MD
71#define CREATE_SYSCALL_TABLE
72
73static const struct trace_syscall_entry sc_table[] = {
177b3692
MD
74#include "instrumentation/syscalls/headers/syscalls_integers.h"
75#include "instrumentation/syscalls/headers/syscalls_pointers.h"
259b6cb3
MD
76};
77
49c50022
MD
78/* Create compatibility syscall table */
79static 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
259b6cb3
MD
84#undef CREATE_SYSCALL_TABLE
85
b76dc1a0 86static void syscall_entry_unknown(struct ltt_event *event,
f405cfce
MD
87 struct pt_regs *regs, unsigned int id)
88{
89 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
f405cfce 90
f405cfce
MD
91 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
92 __event_probe__sys_unknown(event, id, args);
93}
94
6333ace3
MD
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 */
259b6cb3
MD
100static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
101{
259b6cb3 102 struct ltt_channel *chan = __data;
49c50022
MD
103 struct ltt_event *event, *unknown_event;
104 const struct trace_syscall_entry *table, *entry;
105 size_t table_len;
259b6cb3 106
b76dc1a0 107 if (unlikely(is_compat_task())) {
49c50022
MD
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;
b76dc1a0 115 }
49c50022
MD
116 if (unlikely(id >= table_len)) {
117 syscall_entry_unknown(unknown_event, regs, id);
259b6cb3 118 return;
f405cfce 119 }
49c50022
MD
120 if (unlikely(is_compat_task()))
121 event = chan->compat_sc_table[id];
122 else
123 event = chan->sc_table[id];
f405cfce 124 if (unlikely(!event)) {
49c50022 125 syscall_entry_unknown(unknown_event, regs, id);
f405cfce
MD
126 return;
127 }
49c50022 128 entry = &table[id];
f405cfce 129 WARN_ON_ONCE(!entry);
259b6cb3
MD
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
49c50022
MD
219static
220int fill_table(const struct trace_syscall_entry *table, size_t table_len,
221 struct ltt_event **chan_table, struct ltt_channel *chan, void *filter)
259b6cb3
MD
222{
223 unsigned int i;
49c50022
MD
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
259int lttng_syscalls_register(struct ltt_channel *chan, void *filter)
260{
259b6cb3
MD
261 int ret;
262
263 wrapper_vmalloc_sync_all();
259b6cb3
MD
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
49c50022
MD
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
f405cfce
MD
282 if (!chan->sc_unknown) {
283 struct lttng_kernel_event ev;
f405cfce
MD
284 const struct lttng_event_desc *desc =
285 &__event_desc___sys_unknown;
2f804c0a 286
f405cfce
MD
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
b76dc1a0
MD
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
2f804c0a
MD
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
49c50022
MD
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
259b6cb3
MD
340 ret = tracepoint_probe_register("sys_enter",
341 (void *) syscall_entry_probe, chan);
63728b02
MD
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",
2c01ec07 349 (void *) __event_probe__exit_syscall,
2f804c0a 350 chan->sc_exit);
63728b02
MD
351 if (ret) {
352 WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter",
353 (void *) syscall_entry_probe, chan));
354 }
259b6cb3
MD
355 return ret;
356}
357
358/*
359 * Only called at session destruction.
360 */
361int lttng_syscalls_unregister(struct ltt_channel *chan)
362{
363 int ret;
364
365 if (!chan->sc_table)
366 return 0;
63728b02 367 ret = tracepoint_probe_unregister("sys_exit",
2c01ec07 368 (void *) __event_probe__exit_syscall,
2f804c0a 369 chan->sc_exit);
63728b02
MD
370 if (ret)
371 return ret;
259b6cb3
MD
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);
49c50022
MD
378#ifdef CONFIG_COMPAT
379 kfree(chan->compat_sc_table);
380#endif
259b6cb3
MD
381 return 0;
382}
This page took 0.038367 seconds and 4 git commands to generate.