4 * Copyright 2010-2011 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6 * LTTng syscall probes.
8 * Dual LGPL v2.1/GPL v2 license.
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>
17 #include "ltt-events.h"
20 static inline int is_compat_task(void)
27 void syscall_entry_probe(void *__data
, struct pt_regs
*regs
, long id
);
30 * Take care of NOARGS not supported by mainline.
32 #define DECLARE_EVENT_CLASS_NOARGS(name, tstruct, assign, print)
33 #define DEFINE_EVENT_NOARGS(template, name)
34 #define TRACE_EVENT_NOARGS(name, struct, assign, print)
37 * Create LTTng tracepoint probes.
39 #define LTTNG_PACKAGE_BUILD
40 #define CREATE_TRACE_POINTS
41 #define TP_MODULE_OVERRIDE
42 #define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers
44 #define PARAMS(args...) args
46 /* Hijack probe callback for system calls */
48 #define TP_PROBE_CB(_template) &syscall_entry_probe
49 #define SC_TRACE_EVENT(_name, _proto, _args, _struct, _assign, _printk) \
50 TRACE_EVENT(_name, PARAMS(_proto), PARAMS(_args),\
51 PARAMS(_struct), PARAMS(_assign), PARAMS(_printk))
52 #define SC_DECLARE_EVENT_CLASS_NOARGS(_name, _struct, _assign, _printk) \
53 DECLARE_EVENT_CLASS_NOARGS(_name, PARAMS(_struct), PARAMS(_assign),\
55 #define SC_DEFINE_EVENT_NOARGS(_template, _name) \
56 DEFINE_EVENT_NOARGS(_template, _name)
58 #define TRACE_SYSTEM syscalls_integers
59 #include "instrumentation/syscalls/headers/syscalls_integers.h"
61 #define TRACE_SYSTEM syscalls_pointers
62 #include "instrumentation/syscalls/headers/syscalls_pointers.h"
65 #undef SC_DECLARE_EVENT_CLASS_NOARGS
66 #undef SC_DEFINE_EVENT_NOARGS
68 #define TRACE_SYSTEM syscalls_unknown
69 #include "instrumentation/syscalls/headers/syscalls_unknown.h"
72 /* For compat syscalls */
73 #undef _TRACE_SYSCALLS_integers_H
74 #undef _TRACE_SYSCALLS_pointers_H
76 /* Hijack probe callback for system calls */
78 #define TP_PROBE_CB(_template) &syscall_entry_probe
79 #define SC_TRACE_EVENT(_name, _proto, _args, _struct, _assign, _printk) \
80 TRACE_EVENT(compat_##_name, PARAMS(_proto), PARAMS(_args), \
81 PARAMS(_struct), PARAMS(_assign), \
83 #define SC_DECLARE_EVENT_CLASS_NOARGS(_name, _struct, _assign, _printk) \
84 DECLARE_EVENT_CLASS_NOARGS(compat_##_name, PARAMS(_struct), \
85 PARAMS(_assign), PARAMS(_printk))
86 #define SC_DEFINE_EVENT_NOARGS(_template, _name) \
87 DEFINE_EVENT_NOARGS(compat_##_template, compat_##_name)
88 #define TRACE_SYSTEM compat_syscalls_integers
89 #include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
91 #define TRACE_SYSTEM compat_syscalls_pointers
92 #include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
95 #undef SC_DECLARE_EVENT_CLASS_NOARGS
96 #undef SC_DEFINE_EVENT_NOARGS
99 #undef TP_MODULE_OVERRIDE
100 #undef LTTNG_PACKAGE_BUILD
101 #undef CREATE_TRACE_POINTS
103 struct trace_syscall_entry
{
105 const struct lttng_event_desc
*desc
;
106 const struct lttng_event_field
*fields
;
110 #define CREATE_SYSCALL_TABLE
112 #undef TRACE_SYSCALL_TABLE
113 #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
115 .func = __event_probe__##_template, \
116 .nrargs = (_nrargs), \
117 .fields = __event_fields___##_template, \
118 .desc = &__event_desc___##_name, \
121 static const struct trace_syscall_entry sc_table
[] = {
122 #include "instrumentation/syscalls/headers/syscalls_integers.h"
123 #include "instrumentation/syscalls/headers/syscalls_pointers.h"
126 #undef TRACE_SYSCALL_TABLE
127 #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
129 .func = __event_probe__##compat_##_template, \
130 .nrargs = (_nrargs), \
131 .fields = __event_fields___##compat_##_template,\
132 .desc = &__event_desc___##compat_##_name, \
135 /* Create compatibility syscall table */
136 const struct trace_syscall_entry compat_sc_table
[] = {
137 #include "instrumentation/syscalls/headers/compat_syscalls_integers.h"
138 #include "instrumentation/syscalls/headers/compat_syscalls_pointers.h"
141 #undef CREATE_SYSCALL_TABLE
143 static void syscall_entry_unknown(struct ltt_event
*event
,
144 struct pt_regs
*regs
, unsigned int id
)
146 unsigned long args
[UNKNOWN_SYSCALL_NRARGS
];
148 syscall_get_arguments(current
, regs
, 0, UNKNOWN_SYSCALL_NRARGS
, args
);
149 if (unlikely(is_compat_task()))
150 __event_probe__compat_sys_unknown(event
, id
, args
);
152 __event_probe__sys_unknown(event
, id
, args
);
155 void syscall_entry_probe(void *__data
, struct pt_regs
*regs
, long id
)
157 struct ltt_channel
*chan
= __data
;
158 struct ltt_event
*event
, *unknown_event
;
159 const struct trace_syscall_entry
*table
, *entry
;
162 if (unlikely(is_compat_task())) {
163 table
= compat_sc_table
;
164 table_len
= ARRAY_SIZE(compat_sc_table
);
165 unknown_event
= chan
->sc_compat_unknown
;
168 table_len
= ARRAY_SIZE(sc_table
);
169 unknown_event
= chan
->sc_unknown
;
171 if (unlikely(id
>= table_len
)) {
172 syscall_entry_unknown(unknown_event
, regs
, id
);
175 if (unlikely(is_compat_task()))
176 event
= chan
->compat_sc_table
[id
];
178 event
= chan
->sc_table
[id
];
179 if (unlikely(!event
)) {
180 syscall_entry_unknown(unknown_event
, regs
, id
);
184 WARN_ON_ONCE(!entry
);
186 switch (entry
->nrargs
) {
189 void (*fptr
)(void *__data
) = entry
->func
;
196 void (*fptr
)(void *__data
, unsigned long arg0
) = entry
->func
;
197 unsigned long args
[1];
199 syscall_get_arguments(current
, regs
, 0, entry
->nrargs
, args
);
200 fptr(event
, args
[0]);
205 void (*fptr
)(void *__data
,
207 unsigned long arg1
) = entry
->func
;
208 unsigned long args
[2];
210 syscall_get_arguments(current
, regs
, 0, entry
->nrargs
, args
);
211 fptr(event
, args
[0], args
[1]);
216 void (*fptr
)(void *__data
,
219 unsigned long arg2
) = entry
->func
;
220 unsigned long args
[3];
222 syscall_get_arguments(current
, regs
, 0, entry
->nrargs
, args
);
223 fptr(event
, args
[0], args
[1], args
[2]);
228 void (*fptr
)(void *__data
,
232 unsigned long arg3
) = entry
->func
;
233 unsigned long args
[4];
235 syscall_get_arguments(current
, regs
, 0, entry
->nrargs
, args
);
236 fptr(event
, args
[0], args
[1], args
[2], args
[3]);
241 void (*fptr
)(void *__data
,
246 unsigned long arg4
) = entry
->func
;
247 unsigned long args
[5];
249 syscall_get_arguments(current
, regs
, 0, entry
->nrargs
, args
);
250 fptr(event
, args
[0], args
[1], args
[2], args
[3], args
[4]);
255 void (*fptr
)(void *__data
,
261 unsigned long arg5
) = entry
->func
;
262 unsigned long args
[6];
264 syscall_get_arguments(current
, regs
, 0, entry
->nrargs
, args
);
265 fptr(event
, args
[0], args
[1], args
[2],
266 args
[3], args
[4], args
[5]);
274 /* noinline to diminish caller stack size */
276 int fill_table(const struct trace_syscall_entry
*table
, size_t table_len
,
277 struct ltt_event
**chan_table
, struct ltt_channel
*chan
, void *filter
)
279 const struct lttng_event_desc
*desc
;
282 /* Allocate events for each syscall, insert into table */
283 for (i
= 0; i
< table_len
; i
++) {
284 struct lttng_kernel_event ev
;
285 desc
= table
[i
].desc
;
288 /* Unknown syscall */
292 * Skip those already populated by previous failed
293 * register for this channel.
297 memset(&ev
, 0, sizeof(ev
));
298 strncpy(ev
.name
, desc
->name
, LTTNG_SYM_NAME_LEN
);
299 ev
.name
[LTTNG_SYM_NAME_LEN
- 1] = '\0';
300 ev
.instrumentation
= LTTNG_KERNEL_NOOP
;
301 chan_table
[i
] = ltt_event_create(chan
, &ev
, filter
,
303 if (!chan_table
[i
]) {
305 * If something goes wrong in event registration
306 * after the first one, we have no choice but to
307 * leave the previous events in there, until
308 * deleted by session teardown.
316 int lttng_syscalls_register(struct ltt_channel
*chan
, void *filter
)
318 struct lttng_kernel_event ev
;
321 wrapper_vmalloc_sync_all();
323 if (!chan
->sc_table
) {
324 /* create syscall table mapping syscall to events */
325 chan
->sc_table
= kzalloc(sizeof(struct ltt_event
*)
326 * ARRAY_SIZE(sc_table
), GFP_KERNEL
);
332 if (!chan
->compat_sc_table
) {
333 /* create syscall table mapping compat syscall to events */
334 chan
->compat_sc_table
= kzalloc(sizeof(struct ltt_event
*)
335 * ARRAY_SIZE(compat_sc_table
), GFP_KERNEL
);
336 if (!chan
->compat_sc_table
)
340 if (!chan
->sc_unknown
) {
341 const struct lttng_event_desc
*desc
=
342 &__event_desc___sys_unknown
;
344 memset(&ev
, 0, sizeof(ev
));
345 strncpy(ev
.name
, desc
->name
, LTTNG_SYM_NAME_LEN
);
346 ev
.name
[LTTNG_SYM_NAME_LEN
- 1] = '\0';
347 ev
.instrumentation
= LTTNG_KERNEL_NOOP
;
348 chan
->sc_unknown
= ltt_event_create(chan
, &ev
, filter
,
350 if (!chan
->sc_unknown
) {
355 if (!chan
->sc_compat_unknown
) {
356 const struct lttng_event_desc
*desc
=
357 &__event_desc___compat_sys_unknown
;
359 memset(&ev
, 0, sizeof(ev
));
360 strncpy(ev
.name
, desc
->name
, LTTNG_SYM_NAME_LEN
);
361 ev
.name
[LTTNG_SYM_NAME_LEN
- 1] = '\0';
362 ev
.instrumentation
= LTTNG_KERNEL_NOOP
;
363 chan
->sc_compat_unknown
= ltt_event_create(chan
, &ev
, filter
,
365 if (!chan
->sc_compat_unknown
) {
370 if (!chan
->sc_exit
) {
371 const struct lttng_event_desc
*desc
=
372 &__event_desc___exit_syscall
;
374 memset(&ev
, 0, sizeof(ev
));
375 strncpy(ev
.name
, desc
->name
, LTTNG_SYM_NAME_LEN
);
376 ev
.name
[LTTNG_SYM_NAME_LEN
- 1] = '\0';
377 ev
.instrumentation
= LTTNG_KERNEL_NOOP
;
378 chan
->sc_exit
= ltt_event_create(chan
, &ev
, filter
,
380 if (!chan
->sc_exit
) {
385 ret
= fill_table(sc_table
, ARRAY_SIZE(sc_table
),
386 chan
->sc_table
, chan
, filter
);
390 ret
= fill_table(compat_sc_table
, ARRAY_SIZE(compat_sc_table
),
391 chan
->compat_sc_table
, chan
, filter
);
395 ret
= tracepoint_probe_register("sys_enter",
396 (void *) syscall_entry_probe
, chan
);
400 * We change the name of sys_exit tracepoint due to namespace
401 * conflict with sys_exit syscall entry.
403 ret
= tracepoint_probe_register("sys_exit",
404 (void *) __event_probe__exit_syscall
,
407 WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter",
408 (void *) syscall_entry_probe
, chan
));
414 * Only called at session destruction.
416 int lttng_syscalls_unregister(struct ltt_channel
*chan
)
422 ret
= tracepoint_probe_unregister("sys_exit",
423 (void *) __event_probe__exit_syscall
,
427 ret
= tracepoint_probe_unregister("sys_enter",
428 (void *) syscall_entry_probe
, chan
);
431 /* ltt_event destroy will be performed by ltt_session_destroy() */
432 kfree(chan
->sc_table
);
434 kfree(chan
->compat_sc_table
);