Allow trace syscall table override
[lttng-modules.git] / lttng-syscalls.c
CommitLineData
259b6cb3
MD
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>
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
259b6cb3
MD
26static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id);
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
55struct trace_syscall_entry {
56 void *func;
f7bdf4db 57 const struct lttng_event_desc *desc;
259b6cb3
MD
58 const struct lttng_event_field *fields;
59 unsigned int nrargs;
60};
61
259b6cb3
MD
62#define CREATE_SYSCALL_TABLE
63
64#undef TRACE_SYSCALL_TABLE
f7bdf4db 65#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
259b6cb3 66 [ _nr ] = { \
f7bdf4db 67 .func = __event_probe__##_template, \
259b6cb3 68 .nrargs = (_nrargs), \
f7bdf4db
MD
69 .fields = __event_fields___##_template, \
70 .desc = &__event_desc___##_name, \
259b6cb3
MD
71 },
72
73static struct trace_syscall_entry sc_table[] = {
177b3692
MD
74#include "instrumentation/syscalls/headers/syscalls_integers.h"
75#include "instrumentation/syscalls/headers/syscalls_pointers.h"
259b6cb3
MD
76};
77
78#undef CREATE_SYSCALL_TABLE
79
f405cfce
MD
80static 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
6333ace3
MD
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 */
259b6cb3
MD
96static 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
6333ace3 102 if (unlikely(is_compat_task() || id >= ARRAY_SIZE(sc_table))) {
f405cfce 103 syscall_entry_unknown(chan, regs, id);
259b6cb3 104 return;
f405cfce 105 }
259b6cb3 106 event = chan->sc_table[id];
f405cfce
MD
107 if (unlikely(!event)) {
108 syscall_entry_unknown(chan, regs, id);
109 return;
110 }
111 entry = &sc_table[id];
112 WARN_ON_ONCE(!entry);
259b6cb3
MD
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
259b6cb3
MD
202int lttng_syscalls_register(struct ltt_channel *chan, void *filter)
203{
204 unsigned int i;
205 int ret;
206
207 wrapper_vmalloc_sync_all();
259b6cb3
MD
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
f405cfce
MD
217 if (!chan->sc_unknown) {
218 struct lttng_kernel_event ev;
f405cfce
MD
219 const struct lttng_event_desc *desc =
220 &__event_desc___sys_unknown;
2f804c0a 221
f405cfce
MD
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
2f804c0a
MD
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
259b6cb3
MD
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
f405cfce
MD
254 if (!desc) {
255 /* Unknown syscall */
256 continue;
257 }
259b6cb3
MD
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);
63728b02
MD
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",
2c01ec07 289 (void *) __event_probe__exit_syscall,
2f804c0a 290 chan->sc_exit);
63728b02
MD
291 if (ret) {
292 WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter",
293 (void *) syscall_entry_probe, chan));
294 }
259b6cb3
MD
295 return ret;
296}
297
298/*
299 * Only called at session destruction.
300 */
301int lttng_syscalls_unregister(struct ltt_channel *chan)
302{
303 int ret;
304
305 if (!chan->sc_table)
306 return 0;
63728b02 307 ret = tracepoint_probe_unregister("sys_exit",
2c01ec07 308 (void *) __event_probe__exit_syscall,
2f804c0a 309 chan->sc_exit);
63728b02
MD
310 if (ret)
311 return ret;
259b6cb3
MD
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.058425 seconds and 4 git commands to generate.