Fix syscall table
[lttng-modules.git] / lttng-syscalls.c
CommitLineData
259b6cb3
MD
1/*
2 * lttng-syscalls.c
3 *
2faf7d1b 4 * Copyright 2010-2011 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
259b6cb3 5 *
2faf7d1b 6 * LTTng syscall probes.
259b6cb3
MD
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
2faf7d1b 26void syscall_entry_probe(void *__data, struct pt_regs *regs, long id);
259b6cb3 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
259b6cb3 55#undef TRACE_SYSCALL_TABLE
f7bdf4db 56#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
259b6cb3 57 [ _nr ] = { \
f7bdf4db 58 .func = __event_probe__##_template, \
259b6cb3 59 .nrargs = (_nrargs), \
f7bdf4db
MD
60 .fields = __event_fields___##_template, \
61 .desc = &__event_desc___##_name, \
259b6cb3
MD
62 },
63
49c50022
MD
64#define CREATE_SYSCALL_TABLE
65
66static const struct trace_syscall_entry sc_table[] = {
177b3692
MD
67#include "instrumentation/syscalls/headers/syscalls_integers.h"
68#include "instrumentation/syscalls/headers/syscalls_pointers.h"
259b6cb3
MD
69};
70
71#undef CREATE_SYSCALL_TABLE
72
2faf7d1b
MD
73//extern const struct trace_syscall_entry compat_sc_table[];
74//extern const size_t compat_sc_table_len;
75//temp disable
91a951f3 76static const struct trace_syscall_entry compat_sc_table[] = { };
2faf7d1b
MD
77static const size_t compat_sc_table_len;
78
b76dc1a0 79static void syscall_entry_unknown(struct ltt_event *event,
f405cfce
MD
80 struct pt_regs *regs, unsigned int id)
81{
82 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
f405cfce 83
f405cfce
MD
84 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
85 __event_probe__sys_unknown(event, id, args);
86}
87
2faf7d1b 88void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
259b6cb3 89{
259b6cb3 90 struct ltt_channel *chan = __data;
49c50022
MD
91 struct ltt_event *event, *unknown_event;
92 const struct trace_syscall_entry *table, *entry;
93 size_t table_len;
259b6cb3 94
b76dc1a0 95 if (unlikely(is_compat_task())) {
49c50022 96 table = compat_sc_table;
2faf7d1b 97 table_len = compat_sc_table_len;
49c50022
MD
98 unknown_event = chan->sc_compat_unknown;
99 } else {
100 table = sc_table;
101 table_len = ARRAY_SIZE(sc_table);
102 unknown_event = chan->sc_unknown;
b76dc1a0 103 }
49c50022
MD
104 if (unlikely(id >= table_len)) {
105 syscall_entry_unknown(unknown_event, regs, id);
259b6cb3 106 return;
f405cfce 107 }
49c50022
MD
108 if (unlikely(is_compat_task()))
109 event = chan->compat_sc_table[id];
110 else
111 event = chan->sc_table[id];
f405cfce 112 if (unlikely(!event)) {
49c50022 113 syscall_entry_unknown(unknown_event, regs, id);
f405cfce
MD
114 return;
115 }
49c50022 116 entry = &table[id];
f405cfce 117 WARN_ON_ONCE(!entry);
259b6cb3
MD
118
119 switch (entry->nrargs) {
120 case 0:
121 {
122 void (*fptr)(void *__data) = entry->func;
123
124 fptr(event);
125 break;
126 }
127 case 1:
128 {
129 void (*fptr)(void *__data, unsigned long arg0) = entry->func;
130 unsigned long args[1];
131
132 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
133 fptr(event, args[0]);
134 break;
135 }
136 case 2:
137 {
138 void (*fptr)(void *__data,
139 unsigned long arg0,
140 unsigned long arg1) = entry->func;
141 unsigned long args[2];
142
143 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
144 fptr(event, args[0], args[1]);
145 break;
146 }
147 case 3:
148 {
149 void (*fptr)(void *__data,
150 unsigned long arg0,
151 unsigned long arg1,
152 unsigned long arg2) = entry->func;
153 unsigned long args[3];
154
155 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
156 fptr(event, args[0], args[1], args[2]);
157 break;
158 }
159 case 4:
160 {
161 void (*fptr)(void *__data,
162 unsigned long arg0,
163 unsigned long arg1,
164 unsigned long arg2,
165 unsigned long arg3) = entry->func;
166 unsigned long args[4];
167
168 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
169 fptr(event, args[0], args[1], args[2], args[3]);
170 break;
171 }
172 case 5:
173 {
174 void (*fptr)(void *__data,
175 unsigned long arg0,
176 unsigned long arg1,
177 unsigned long arg2,
178 unsigned long arg3,
179 unsigned long arg4) = entry->func;
180 unsigned long args[5];
181
182 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
183 fptr(event, args[0], args[1], args[2], args[3], args[4]);
184 break;
185 }
186 case 6:
187 {
188 void (*fptr)(void *__data,
189 unsigned long arg0,
190 unsigned long arg1,
191 unsigned long arg2,
192 unsigned long arg3,
193 unsigned long arg4,
194 unsigned long arg5) = entry->func;
195 unsigned long args[6];
196
197 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
198 fptr(event, args[0], args[1], args[2],
199 args[3], args[4], args[5]);
200 break;
201 }
202 default:
203 break;
204 }
205}
206
49c50022
MD
207static
208int fill_table(const struct trace_syscall_entry *table, size_t table_len,
209 struct ltt_event **chan_table, struct ltt_channel *chan, void *filter)
259b6cb3
MD
210{
211 unsigned int i;
49c50022
MD
212
213 /* Allocate events for each syscall, insert into table */
214 for (i = 0; i < table_len; i++) {
215 struct lttng_kernel_event ev;
216 const struct lttng_event_desc *desc = table[i].desc;
217
218 if (!desc) {
219 /* Unknown syscall */
220 continue;
221 }
222 /*
223 * Skip those already populated by previous failed
224 * register for this channel.
225 */
226 if (chan_table[i])
227 continue;
228 memset(&ev, 0, sizeof(ev));
229 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
230 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
231 ev.instrumentation = LTTNG_KERNEL_NOOP;
232 chan_table[i] = ltt_event_create(chan, &ev, filter,
233 desc);
234 if (!chan_table[i]) {
235 /*
236 * If something goes wrong in event registration
237 * after the first one, we have no choice but to
238 * leave the previous events in there, until
239 * deleted by session teardown.
240 */
241 return -EINVAL;
242 }
243 }
244 return 0;
245}
246
247int lttng_syscalls_register(struct ltt_channel *chan, void *filter)
248{
259b6cb3
MD
249 int ret;
250
251 wrapper_vmalloc_sync_all();
259b6cb3
MD
252
253 if (!chan->sc_table) {
254 /* create syscall table mapping syscall to events */
255 chan->sc_table = kzalloc(sizeof(struct ltt_event *)
256 * ARRAY_SIZE(sc_table), GFP_KERNEL);
257 if (!chan->sc_table)
258 return -ENOMEM;
259 }
260
49c50022
MD
261#ifdef CONFIG_COMPAT
262 if (!chan->compat_sc_table) {
263 /* create syscall table mapping compat syscall to events */
264 chan->compat_sc_table = kzalloc(sizeof(struct ltt_event *)
2faf7d1b 265 * compat_sc_table_len, GFP_KERNEL);
49c50022
MD
266 if (!chan->compat_sc_table)
267 return -ENOMEM;
268 }
269#endif
f405cfce
MD
270 if (!chan->sc_unknown) {
271 struct lttng_kernel_event ev;
f405cfce
MD
272 const struct lttng_event_desc *desc =
273 &__event_desc___sys_unknown;
2f804c0a 274
f405cfce
MD
275 memset(&ev, 0, sizeof(ev));
276 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
277 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
278 ev.instrumentation = LTTNG_KERNEL_NOOP;
279 chan->sc_unknown = ltt_event_create(chan, &ev, filter,
280 desc);
281 if (!chan->sc_unknown) {
282 return -EINVAL;
283 }
284 }
285
b76dc1a0
MD
286 if (!chan->sc_compat_unknown) {
287 struct lttng_kernel_event ev;
288 const struct lttng_event_desc *desc =
289 &__event_desc___compat_sys_unknown;
290
291 memset(&ev, 0, sizeof(ev));
292 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
293 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
294 ev.instrumentation = LTTNG_KERNEL_NOOP;
295 chan->sc_compat_unknown = ltt_event_create(chan, &ev, filter,
296 desc);
297 if (!chan->sc_compat_unknown) {
298 return -EINVAL;
299 }
300 }
301
2f804c0a
MD
302 if (!chan->sc_exit) {
303 struct lttng_kernel_event ev;
304 const struct lttng_event_desc *desc =
305 &__event_desc___exit_syscall;
306
307 memset(&ev, 0, sizeof(ev));
308 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
309 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
310 ev.instrumentation = LTTNG_KERNEL_NOOP;
311 chan->sc_exit = ltt_event_create(chan, &ev, filter,
312 desc);
313 if (!chan->sc_exit) {
314 return -EINVAL;
315 }
316 }
317
49c50022
MD
318 ret = fill_table(sc_table, ARRAY_SIZE(sc_table),
319 chan->sc_table, chan, filter);
320 if (ret)
321 return ret;
322#ifdef CONFIG_COMPAT
2faf7d1b 323 ret = fill_table(compat_sc_table, compat_sc_table_len,
49c50022
MD
324 chan->compat_sc_table, chan, filter);
325 if (ret)
326 return ret;
327#endif
259b6cb3
MD
328 ret = tracepoint_probe_register("sys_enter",
329 (void *) syscall_entry_probe, chan);
63728b02
MD
330 if (ret)
331 return ret;
332 /*
333 * We change the name of sys_exit tracepoint due to namespace
334 * conflict with sys_exit syscall entry.
335 */
336 ret = tracepoint_probe_register("sys_exit",
2c01ec07 337 (void *) __event_probe__exit_syscall,
2f804c0a 338 chan->sc_exit);
63728b02
MD
339 if (ret) {
340 WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter",
341 (void *) syscall_entry_probe, chan));
342 }
259b6cb3
MD
343 return ret;
344}
345
346/*
347 * Only called at session destruction.
348 */
349int lttng_syscalls_unregister(struct ltt_channel *chan)
350{
351 int ret;
352
353 if (!chan->sc_table)
354 return 0;
63728b02 355 ret = tracepoint_probe_unregister("sys_exit",
2c01ec07 356 (void *) __event_probe__exit_syscall,
2f804c0a 357 chan->sc_exit);
63728b02
MD
358 if (ret)
359 return ret;
259b6cb3
MD
360 ret = tracepoint_probe_unregister("sys_enter",
361 (void *) syscall_entry_probe, chan);
362 if (ret)
363 return ret;
364 /* ltt_event destroy will be performed by ltt_session_destroy() */
365 kfree(chan->sc_table);
49c50022
MD
366#ifdef CONFIG_COMPAT
367 kfree(chan->compat_sc_table);
368#endif
259b6cb3
MD
369 return 0;
370}
This page took 0.038116 seconds and 4 git commands to generate.