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