Handle newer 3.1+ perf API
[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
b76dc1a0 80static void syscall_entry_unknown(struct ltt_event *event,
f405cfce
MD
81 struct pt_regs *regs, unsigned int id)
82{
83 unsigned long args[UNKNOWN_SYSCALL_NRARGS];
f405cfce 84
f405cfce
MD
85 syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args);
86 __event_probe__sys_unknown(event, id, args);
87}
88
6333ace3
MD
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 */
259b6cb3
MD
94static 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
b76dc1a0
MD
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);
259b6cb3 106 return;
f405cfce 107 }
259b6cb3 108 event = chan->sc_table[id];
f405cfce 109 if (unlikely(!event)) {
b76dc1a0 110 syscall_entry_unknown(chan->sc_unknown, regs, id);
f405cfce
MD
111 return;
112 }
113 entry = &sc_table[id];
114 WARN_ON_ONCE(!entry);
259b6cb3
MD
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
259b6cb3
MD
204int lttng_syscalls_register(struct ltt_channel *chan, void *filter)
205{
206 unsigned int i;
207 int ret;
208
209 wrapper_vmalloc_sync_all();
259b6cb3
MD
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
f405cfce
MD
219 if (!chan->sc_unknown) {
220 struct lttng_kernel_event ev;
f405cfce
MD
221 const struct lttng_event_desc *desc =
222 &__event_desc___sys_unknown;
2f804c0a 223
f405cfce
MD
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
b76dc1a0
MD
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
2f804c0a
MD
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
259b6cb3
MD
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
f405cfce
MD
272 if (!desc) {
273 /* Unknown syscall */
274 continue;
275 }
259b6cb3
MD
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);
63728b02
MD
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",
2c01ec07 307 (void *) __event_probe__exit_syscall,
2f804c0a 308 chan->sc_exit);
63728b02
MD
309 if (ret) {
310 WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter",
311 (void *) syscall_entry_probe, chan));
312 }
259b6cb3
MD
313 return ret;
314}
315
316/*
317 * Only called at session destruction.
318 */
319int lttng_syscalls_unregister(struct ltt_channel *chan)
320{
321 int ret;
322
323 if (!chan->sc_table)
324 return 0;
63728b02 325 ret = tracepoint_probe_unregister("sys_exit",
2c01ec07 326 (void *) __event_probe__exit_syscall,
2f804c0a 327 chan->sc_exit);
63728b02
MD
328 if (ret)
329 return ret;
259b6cb3
MD
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.03644 seconds and 4 git commands to generate.