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