Fix syscall exit
[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 <asm/ptrace.h>
14 #include <asm/syscall.h>
15
16 #include "ltt-events.h"
17
18 static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id);
19
20 /*
21 * Take care of NOARGS not supported by mainline.
22 */
23 #define DECLARE_EVENT_CLASS_NOARGS(name, tstruct, assign, print)
24 #define DEFINE_EVENT_NOARGS(template, name)
25 #define TRACE_EVENT_NOARGS(name, struct, assign, print)
26
27 /*
28 * Create LTTng tracepoint probes.
29 */
30 #define LTTNG_PACKAGE_BUILD
31 #define CREATE_TRACE_POINTS
32 #define TP_MODULE_OVERRIDE
33 #define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers
34
35 /* Hijack probe callback for system calls */
36 #define TP_PROBE_CB(_template) &syscall_entry_probe
37 #include "instrumentation/syscalls/headers/syscalls_integers.h"
38 #include "instrumentation/syscalls/headers/syscalls_pointers.h"
39 #undef TP_PROBE_CB
40
41 #include "instrumentation/syscalls/headers/syscalls_unknown.h"
42
43 #undef TP_MODULE_OVERRIDE
44 #undef LTTNG_PACKAGE_BUILD
45 #undef CREATE_TRACE_POINTS
46
47 struct trace_syscall_entry {
48 void *func;
49 const struct lttng_event_desc *desc;
50 const struct lttng_event_field *fields;
51 unsigned int nrargs;
52 };
53
54 #define CREATE_SYSCALL_TABLE
55
56 #undef TRACE_SYSCALL_TABLE
57 #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
58 [ _nr ] = { \
59 .func = __event_probe__##_template, \
60 .nrargs = (_nrargs), \
61 .fields = __event_fields___##_template, \
62 .desc = &__event_desc___##_name, \
63 },
64
65 static struct trace_syscall_entry sc_table[] = {
66 #include "instrumentation/syscalls/headers/syscalls_integers.h"
67 #include "instrumentation/syscalls/headers/syscalls_pointers.h"
68 };
69
70 #undef CREATE_SYSCALL_TABLE
71
72 static void syscall_entry_unknown(struct ltt_channel *chan,
73 struct pt_regs *regs, unsigned int id)
74 {
75 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
76 struct ltt_event *event;
77
78 event = chan->sc_unknown;
79 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
80 __event_probe__sys_unknown(event, id, args);
81 }
82
83 static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
84 {
85 struct trace_syscall_entry *entry;
86 struct ltt_channel *chan = __data;
87 struct ltt_event *event;
88
89 if (unlikely(id >= ARRAY_SIZE(sc_table))) {
90 syscall_entry_unknown(chan, regs, id);
91 return;
92 }
93 event = chan->sc_table[id];
94 if (unlikely(!event)) {
95 syscall_entry_unknown(chan, regs, id);
96 return;
97 }
98 entry = &sc_table[id];
99 WARN_ON_ONCE(!entry);
100
101 switch (entry->nrargs) {
102 case 0:
103 {
104 void (*fptr)(void *__data) = entry->func;
105
106 fptr(event);
107 break;
108 }
109 case 1:
110 {
111 void (*fptr)(void *__data, unsigned long arg0) = entry->func;
112 unsigned long args[1];
113
114 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
115 fptr(event, args[0]);
116 break;
117 }
118 case 2:
119 {
120 void (*fptr)(void *__data,
121 unsigned long arg0,
122 unsigned long arg1) = entry->func;
123 unsigned long args[2];
124
125 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
126 fptr(event, args[0], args[1]);
127 break;
128 }
129 case 3:
130 {
131 void (*fptr)(void *__data,
132 unsigned long arg0,
133 unsigned long arg1,
134 unsigned long arg2) = entry->func;
135 unsigned long args[3];
136
137 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
138 fptr(event, args[0], args[1], args[2]);
139 break;
140 }
141 case 4:
142 {
143 void (*fptr)(void *__data,
144 unsigned long arg0,
145 unsigned long arg1,
146 unsigned long arg2,
147 unsigned long arg3) = entry->func;
148 unsigned long args[4];
149
150 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
151 fptr(event, args[0], args[1], args[2], args[3]);
152 break;
153 }
154 case 5:
155 {
156 void (*fptr)(void *__data,
157 unsigned long arg0,
158 unsigned long arg1,
159 unsigned long arg2,
160 unsigned long arg3,
161 unsigned long arg4) = entry->func;
162 unsigned long args[5];
163
164 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
165 fptr(event, args[0], args[1], args[2], args[3], args[4]);
166 break;
167 }
168 case 6:
169 {
170 void (*fptr)(void *__data,
171 unsigned long arg0,
172 unsigned long arg1,
173 unsigned long arg2,
174 unsigned long arg3,
175 unsigned long arg4,
176 unsigned long arg5) = entry->func;
177 unsigned long args[6];
178
179 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
180 fptr(event, args[0], args[1], args[2],
181 args[3], args[4], args[5]);
182 break;
183 }
184 default:
185 break;
186 }
187 }
188
189 int lttng_syscalls_register(struct ltt_channel *chan, void *filter)
190 {
191 unsigned int i;
192 int ret;
193
194 wrapper_vmalloc_sync_all();
195
196 if (!chan->sc_table) {
197 /* create syscall table mapping syscall to events */
198 chan->sc_table = kzalloc(sizeof(struct ltt_event *)
199 * ARRAY_SIZE(sc_table), GFP_KERNEL);
200 if (!chan->sc_table)
201 return -ENOMEM;
202 }
203
204 if (!chan->sc_unknown) {
205 struct lttng_kernel_event ev;
206 const struct lttng_event_desc *desc =
207 &__event_desc___sys_unknown;
208
209 memset(&ev, 0, sizeof(ev));
210 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
211 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
212 ev.instrumentation = LTTNG_KERNEL_NOOP;
213 chan->sc_unknown = ltt_event_create(chan, &ev, filter,
214 desc);
215 if (!chan->sc_unknown) {
216 return -EINVAL;
217 }
218 }
219
220 if (!chan->sc_exit) {
221 struct lttng_kernel_event ev;
222 const struct lttng_event_desc *desc =
223 &__event_desc___exit_syscall;
224
225 memset(&ev, 0, sizeof(ev));
226 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
227 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
228 ev.instrumentation = LTTNG_KERNEL_NOOP;
229 chan->sc_exit = ltt_event_create(chan, &ev, filter,
230 desc);
231 if (!chan->sc_exit) {
232 return -EINVAL;
233 }
234 }
235
236 /* Allocate events for each syscall, insert into table */
237 for (i = 0; i < ARRAY_SIZE(sc_table); i++) {
238 struct lttng_kernel_event ev;
239 const struct lttng_event_desc *desc = sc_table[i].desc;
240
241 if (!desc) {
242 /* Unknown syscall */
243 continue;
244 }
245 /*
246 * Skip those already populated by previous failed
247 * register for this channel.
248 */
249 if (chan->sc_table[i])
250 continue;
251 memset(&ev, 0, sizeof(ev));
252 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
253 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
254 ev.instrumentation = LTTNG_KERNEL_NOOP;
255 chan->sc_table[i] = ltt_event_create(chan, &ev, filter,
256 desc);
257 if (!chan->sc_table[i]) {
258 /*
259 * If something goes wrong in event registration
260 * after the first one, we have no choice but to
261 * leave the previous events in there, until
262 * deleted by session teardown.
263 */
264 return -EINVAL;
265 }
266 }
267 ret = tracepoint_probe_register("sys_enter",
268 (void *) syscall_entry_probe, chan);
269 if (ret)
270 return ret;
271 /*
272 * We change the name of sys_exit tracepoint due to namespace
273 * conflict with sys_exit syscall entry.
274 */
275 ret = tracepoint_probe_register("sys_exit",
276 (void *) __event_probe__exit_syscall,
277 chan->sc_exit);
278 if (ret) {
279 WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter",
280 (void *) syscall_entry_probe, chan));
281 }
282 return ret;
283 }
284
285 /*
286 * Only called at session destruction.
287 */
288 int lttng_syscalls_unregister(struct ltt_channel *chan)
289 {
290 int ret;
291
292 if (!chan->sc_table)
293 return 0;
294 ret = tracepoint_probe_unregister("sys_exit",
295 (void *) __event_probe__exit_syscall,
296 chan->sc_exit);
297 if (ret)
298 return ret;
299 ret = tracepoint_probe_unregister("sys_enter",
300 (void *) syscall_entry_probe, chan);
301 if (ret)
302 return ret;
303 /* ltt_event destroy will be performed by ltt_session_destroy() */
304 kfree(chan->sc_table);
305 return 0;
306 }
This page took 0.039328 seconds and 5 git commands to generate.