Rename syscalls -> syscall in LTTng ABI
[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 #define CREATE_SYSCALL_TABLE
63
64 #undef TRACE_SYSCALL_TABLE
65 #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
66 [ _nr ] = { \
67 .func = __event_probe__##_template, \
68 .nrargs = (_nrargs), \
69 .fields = __event_fields___##_template, \
70 .desc = &__event_desc___##_name, \
71 },
72
73 static 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 #undef CREATE_SYSCALL_TABLE
79
80 static void syscall_entry_unknown(struct ltt_event *event,
81 struct pt_regs *regs, unsigned int id)
82 {
83 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
84
85 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
86 __event_probe__sys_unknown(event, id, args);
87 }
88
89 /*
90 * Currently, given that the kernel syscall metadata extraction only
91 * considers native system calls (not 32-bit compability ones), we
92 * fall-back on the "unknown" system call tracing for 32-bit compat.
93 */
94 static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
95 {
96 struct trace_syscall_entry *entry;
97 struct ltt_channel *chan = __data;
98 struct ltt_event *event;
99
100 if (unlikely(is_compat_task())) {
101 syscall_entry_unknown(chan->sc_compat_unknown, regs, id);
102 return;
103 }
104 if (unlikely(id >= ARRAY_SIZE(sc_table))) {
105 syscall_entry_unknown(chan->sc_unknown, regs, id);
106 return;
107 }
108 event = chan->sc_table[id];
109 if (unlikely(!event)) {
110 syscall_entry_unknown(chan->sc_unknown, regs, id);
111 return;
112 }
113 entry = &sc_table[id];
114 WARN_ON_ONCE(!entry);
115
116 switch (entry->nrargs) {
117 case 0:
118 {
119 void (*fptr)(void *__data) = entry->func;
120
121 fptr(event);
122 break;
123 }
124 case 1:
125 {
126 void (*fptr)(void *__data, unsigned long arg0) = entry->func;
127 unsigned long args[1];
128
129 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
130 fptr(event, args[0]);
131 break;
132 }
133 case 2:
134 {
135 void (*fptr)(void *__data,
136 unsigned long arg0,
137 unsigned long arg1) = entry->func;
138 unsigned long args[2];
139
140 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
141 fptr(event, args[0], args[1]);
142 break;
143 }
144 case 3:
145 {
146 void (*fptr)(void *__data,
147 unsigned long arg0,
148 unsigned long arg1,
149 unsigned long arg2) = entry->func;
150 unsigned long args[3];
151
152 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
153 fptr(event, args[0], args[1], args[2]);
154 break;
155 }
156 case 4:
157 {
158 void (*fptr)(void *__data,
159 unsigned long arg0,
160 unsigned long arg1,
161 unsigned long arg2,
162 unsigned long arg3) = entry->func;
163 unsigned long args[4];
164
165 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
166 fptr(event, args[0], args[1], args[2], args[3]);
167 break;
168 }
169 case 5:
170 {
171 void (*fptr)(void *__data,
172 unsigned long arg0,
173 unsigned long arg1,
174 unsigned long arg2,
175 unsigned long arg3,
176 unsigned long arg4) = entry->func;
177 unsigned long args[5];
178
179 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
180 fptr(event, args[0], args[1], args[2], args[3], args[4]);
181 break;
182 }
183 case 6:
184 {
185 void (*fptr)(void *__data,
186 unsigned long arg0,
187 unsigned long arg1,
188 unsigned long arg2,
189 unsigned long arg3,
190 unsigned long arg4,
191 unsigned long arg5) = entry->func;
192 unsigned long args[6];
193
194 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
195 fptr(event, args[0], args[1], args[2],
196 args[3], args[4], args[5]);
197 break;
198 }
199 default:
200 break;
201 }
202 }
203
204 int lttng_syscalls_register(struct ltt_channel *chan, void *filter)
205 {
206 unsigned int i;
207 int ret;
208
209 wrapper_vmalloc_sync_all();
210
211 if (!chan->sc_table) {
212 /* create syscall table mapping syscall to events */
213 chan->sc_table = kzalloc(sizeof(struct ltt_event *)
214 * ARRAY_SIZE(sc_table), GFP_KERNEL);
215 if (!chan->sc_table)
216 return -ENOMEM;
217 }
218
219 if (!chan->sc_unknown) {
220 struct lttng_kernel_event ev;
221 const struct lttng_event_desc *desc =
222 &__event_desc___sys_unknown;
223
224 memset(&ev, 0, sizeof(ev));
225 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
226 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
227 ev.instrumentation = LTTNG_KERNEL_NOOP;
228 chan->sc_unknown = ltt_event_create(chan, &ev, filter,
229 desc);
230 if (!chan->sc_unknown) {
231 return -EINVAL;
232 }
233 }
234
235 if (!chan->sc_compat_unknown) {
236 struct lttng_kernel_event ev;
237 const struct lttng_event_desc *desc =
238 &__event_desc___compat_sys_unknown;
239
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->sc_compat_unknown = ltt_event_create(chan, &ev, filter,
245 desc);
246 if (!chan->sc_compat_unknown) {
247 return -EINVAL;
248 }
249 }
250
251 if (!chan->sc_exit) {
252 struct lttng_kernel_event ev;
253 const struct lttng_event_desc *desc =
254 &__event_desc___exit_syscall;
255
256 memset(&ev, 0, sizeof(ev));
257 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
258 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
259 ev.instrumentation = LTTNG_KERNEL_NOOP;
260 chan->sc_exit = ltt_event_create(chan, &ev, filter,
261 desc);
262 if (!chan->sc_exit) {
263 return -EINVAL;
264 }
265 }
266
267 /* Allocate events for each syscall, insert into table */
268 for (i = 0; i < ARRAY_SIZE(sc_table); i++) {
269 struct lttng_kernel_event ev;
270 const struct lttng_event_desc *desc = sc_table[i].desc;
271
272 if (!desc) {
273 /* Unknown syscall */
274 continue;
275 }
276 /*
277 * Skip those already populated by previous failed
278 * register for this channel.
279 */
280 if (chan->sc_table[i])
281 continue;
282 memset(&ev, 0, sizeof(ev));
283 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
284 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
285 ev.instrumentation = LTTNG_KERNEL_NOOP;
286 chan->sc_table[i] = ltt_event_create(chan, &ev, filter,
287 desc);
288 if (!chan->sc_table[i]) {
289 /*
290 * If something goes wrong in event registration
291 * after the first one, we have no choice but to
292 * leave the previous events in there, until
293 * deleted by session teardown.
294 */
295 return -EINVAL;
296 }
297 }
298 ret = tracepoint_probe_register("sys_enter",
299 (void *) syscall_entry_probe, chan);
300 if (ret)
301 return ret;
302 /*
303 * We change the name of sys_exit tracepoint due to namespace
304 * conflict with sys_exit syscall entry.
305 */
306 ret = tracepoint_probe_register("sys_exit",
307 (void *) __event_probe__exit_syscall,
308 chan->sc_exit);
309 if (ret) {
310 WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter",
311 (void *) syscall_entry_probe, chan));
312 }
313 return ret;
314 }
315
316 /*
317 * Only called at session destruction.
318 */
319 int lttng_syscalls_unregister(struct ltt_channel *chan)
320 {
321 int ret;
322
323 if (!chan->sc_table)
324 return 0;
325 ret = tracepoint_probe_unregister("sys_exit",
326 (void *) __event_probe__exit_syscall,
327 chan->sc_exit);
328 if (ret)
329 return ret;
330 ret = tracepoint_probe_unregister("sys_enter",
331 (void *) syscall_entry_probe, chan);
332 if (ret)
333 return ret;
334 /* ltt_event destroy will be performed by ltt_session_destroy() */
335 kfree(chan->sc_table);
336 return 0;
337 }
This page took 0.047454 seconds and 4 git commands to generate.