Support generation of syscall probes for those with pointers
[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
33 /* Hijack probe callback for system calls */
34 #define TP_PROBE_CB(_template) &syscall_entry_probe
35 #define TP_MODULE_OVERRIDE
36
37 #define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers
38
39 #include "instrumentation/syscalls/headers/syscalls.h"
40
41 #undef TP_MODULE_OVERRIDE
42 #undef TP_PROBE_CB
43 #undef LTTNG_PACKAGE_BUILD
44 #undef CREATE_TRACE_POINTS
45
46 struct trace_syscall_entry {
47 void *func;
48 const struct lttng_event_desc *desc;
49 const struct lttng_event_field *fields;
50 unsigned int nrargs;
51 };
52
53 #define CREATE_SYSCALL_TABLE
54
55 #undef TRACE_SYSCALL_TABLE
56 #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \
57 [ _nr ] = { \
58 .func = __event_probe__##_template, \
59 .nrargs = (_nrargs), \
60 .fields = __event_fields___##_template, \
61 .desc = &__event_desc___##_name, \
62 },
63
64 static struct trace_syscall_entry sc_table[] = {
65 #include "instrumentation/syscalls/headers/syscalls.h"
66 };
67
68 #undef CREATE_SYSCALL_TABLE
69
70 static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id)
71 {
72 struct trace_syscall_entry *entry;
73 struct ltt_channel *chan = __data;
74 struct ltt_event *event;
75
76 if (unlikely(id >= ARRAY_SIZE(sc_table)))
77 return;
78 entry = &sc_table[id];
79 if (unlikely(!entry->func))
80 return;
81 event = chan->sc_table[id];
82 WARN_ON_ONCE(!event);
83
84 switch (entry->nrargs) {
85 case 0:
86 {
87 void (*fptr)(void *__data) = entry->func;
88
89 fptr(event);
90 break;
91 }
92 case 1:
93 {
94 void (*fptr)(void *__data, unsigned long arg0) = entry->func;
95 unsigned long args[1];
96
97 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
98 fptr(event, args[0]);
99 break;
100 }
101 case 2:
102 {
103 void (*fptr)(void *__data,
104 unsigned long arg0,
105 unsigned long arg1) = entry->func;
106 unsigned long args[2];
107
108 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
109 fptr(event, args[0], args[1]);
110 break;
111 }
112 case 3:
113 {
114 void (*fptr)(void *__data,
115 unsigned long arg0,
116 unsigned long arg1,
117 unsigned long arg2) = entry->func;
118 unsigned long args[3];
119
120 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
121 fptr(event, args[0], args[1], args[2]);
122 break;
123 }
124 case 4:
125 {
126 void (*fptr)(void *__data,
127 unsigned long arg0,
128 unsigned long arg1,
129 unsigned long arg2,
130 unsigned long arg3) = entry->func;
131 unsigned long args[4];
132
133 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
134 fptr(event, args[0], args[1], args[2], args[3]);
135 break;
136 }
137 case 5:
138 {
139 void (*fptr)(void *__data,
140 unsigned long arg0,
141 unsigned long arg1,
142 unsigned long arg2,
143 unsigned long arg3,
144 unsigned long arg4) = entry->func;
145 unsigned long args[5];
146
147 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
148 fptr(event, args[0], args[1], args[2], args[3], args[4]);
149 break;
150 }
151 case 6:
152 {
153 void (*fptr)(void *__data,
154 unsigned long arg0,
155 unsigned long arg1,
156 unsigned long arg2,
157 unsigned long arg3,
158 unsigned long arg4,
159 unsigned long arg5) = entry->func;
160 unsigned long args[6];
161
162 syscall_get_arguments(current, regs, 0, entry->nrargs, args);
163 fptr(event, args[0], args[1], args[2],
164 args[3], args[4], args[5]);
165 break;
166 }
167 default:
168 break;
169 }
170 }
171
172 int lttng_syscalls_register(struct ltt_channel *chan, void *filter)
173 {
174 unsigned int i;
175 int ret;
176
177 wrapper_vmalloc_sync_all();
178
179 if (!chan->sc_table) {
180 /* create syscall table mapping syscall to events */
181 chan->sc_table = kzalloc(sizeof(struct ltt_event *)
182 * ARRAY_SIZE(sc_table), GFP_KERNEL);
183 if (!chan->sc_table)
184 return -ENOMEM;
185 }
186
187 /* Allocate events for each syscall, insert into table */
188 for (i = 0; i < ARRAY_SIZE(sc_table); i++) {
189 struct lttng_kernel_event ev;
190 const struct lttng_event_desc *desc = sc_table[i].desc;
191
192 if (!desc)
193 continue;
194 /*
195 * Skip those already populated by previous failed
196 * register for this channel.
197 */
198 if (chan->sc_table[i])
199 continue;
200 memset(&ev, 0, sizeof(ev));
201 strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN);
202 ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0';
203 ev.instrumentation = LTTNG_KERNEL_NOOP;
204 chan->sc_table[i] = ltt_event_create(chan, &ev, filter,
205 desc);
206 if (!chan->sc_table[i]) {
207 /*
208 * If something goes wrong in event registration
209 * after the first one, we have no choice but to
210 * leave the previous events in there, until
211 * deleted by session teardown.
212 */
213 return -EINVAL;
214 }
215 }
216 ret = tracepoint_probe_register("sys_enter",
217 (void *) syscall_entry_probe, chan);
218 return ret;
219 }
220
221 /*
222 * Only called at session destruction.
223 */
224 int lttng_syscalls_unregister(struct ltt_channel *chan)
225 {
226 int ret;
227
228 if (!chan->sc_table)
229 return 0;
230 ret = tracepoint_probe_unregister("sys_enter",
231 (void *) syscall_entry_probe, chan);
232 if (ret)
233 return ret;
234 /* ltt_event destroy will be performed by ltt_session_destroy() */
235 kfree(chan->sc_table);
236 return 0;
237 }
This page took 0.034555 seconds and 5 git commands to generate.