xref: /freebsd/sys/kern/kern_sdt.c (revision f05cddf9)
1 /*-
2  * Copyright 2006-2008 John Birrell <jb@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  *
27  * Backend for the Statically Defined Tracing (SDT) kernel support. This is
28  * required to allow a module to load even though DTrace kernel support may
29  * not be present. A module may be built with SDT probes in it which are
30  * registered and deregistered via SYSINIT/SYSUNINIT.
31  *
32  */
33 
34 #include "opt_kdtrace.h"
35 
36 #include <sys/cdefs.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/linker.h>
41 #include <sys/lock.h>
42 #include <sys/proc.h>
43 #include <sys/sx.h>
44 #include <sys/sdt.h>
45 
46 /*
47  * This is the list of statically defined tracing providers.
48  */
49 static TAILQ_HEAD(sdt_provider_list_head, sdt_provider) sdt_provider_list;
50 
51 /*
52  * Mutex to serialise access to the SDT provider list.
53  */
54 static struct sx sdt_sx;
55 
56 /*
57  * Hook for the DTrace probe function. The 'sdt' provider will set this
58  * to dtrace_probe when it loads.
59  */
60 sdt_probe_func_t sdt_probe_func = sdt_probe_stub;
61 
62 static sdt_provider_listall_func_t sdt_provider_register_func = NULL;
63 static sdt_provider_listall_func_t sdt_provider_deregister_func = NULL;
64 static sdt_probe_listall_func_t sdt_probe_register_func = NULL;
65 
66 static void *sdt_provider_register_arg;
67 static void *sdt_provider_deregister_arg;
68 static void *sdt_probe_register_arg;
69 
70 static int sdt_provider_listall_locked(sdt_provider_listall_func_t, void *);
71 
72 /*
73  * This is a stub for probe calls in case kernel DTrace support isn't
74  * compiled in. It should never get called because there is no DTrace
75  * support to enable it.
76  */
77 void
78 sdt_probe_stub(uint32_t id, uintptr_t arg0, uintptr_t arg1,
79     uintptr_t arg2, uintptr_t arg3, uintptr_t arg4)
80 {
81 	printf("sdt_probe_stub: Why did this get called?\n");
82 }
83 
84 /*
85  * Called from SYSINIT to register a provider.
86  */
87 void
88 sdt_provider_register(void *arg)
89 {
90 	struct sdt_provider *prov = arg;
91 
92 	sx_xlock(&sdt_sx);
93 
94 	TAILQ_INSERT_TAIL(&sdt_provider_list, prov, prov_entry);
95 
96 	TAILQ_INIT(&prov->probe_list);
97 
98 	if (sdt_provider_register_func != NULL)
99 		sdt_provider_register_func(prov, sdt_provider_register_arg);
100 
101 	sx_xunlock(&sdt_sx);
102 }
103 
104 /*
105  * Called from SYSUNINIT to de-register a provider.
106  */
107 void
108 sdt_provider_deregister(void *arg)
109 {
110 	struct sdt_provider *prov = arg;
111 
112 	sx_xlock(&sdt_sx);
113 
114 	TAILQ_REMOVE(&sdt_provider_list, prov, prov_entry);
115 
116 	if (sdt_provider_deregister_func != NULL)
117 		sdt_provider_deregister_func(prov, sdt_provider_deregister_arg);
118 
119 	sx_xunlock(&sdt_sx);
120 }
121 
122 /*
123  * Called from SYSINIT to register a statically defined trace probe.
124  */
125 void
126 sdt_probe_register(void *arg)
127 {
128 	struct sdt_probe *probe = arg;
129 
130 	/*
131 	 * Check the reference structure version. Only version 1 is
132 	 * supported at the moment.
133 	 */
134 	if (probe->version != sizeof(struct sdt_probe)) {
135 		printf("%s:%s:%s has version %d when %d required\n", probe->mod, probe->func, probe->name, probe->version, (int) sizeof(struct sdt_probe));
136 		return;
137 	}
138 
139 	sx_xlock(&sdt_sx);
140 
141 	TAILQ_INSERT_TAIL(&probe->prov->probe_list, probe, probe_entry);
142 
143 	TAILQ_INIT(&probe->argtype_list);
144 
145 	probe->state = SDT_INIT;
146 
147 	if (sdt_probe_register_func != NULL)
148 		sdt_probe_register_func(probe, sdt_provider_register_arg);
149 
150 	sx_xunlock(&sdt_sx);
151 }
152 
153 /*
154  * Called from SYSUNINIT to de-register a statically defined trace probe.
155  */
156 void
157 sdt_probe_deregister(void *arg)
158 {
159 	struct sdt_probe *probe = arg;
160 
161 	sx_xlock(&sdt_sx);
162 
163 	if (probe->state == SDT_INIT) {
164 		TAILQ_REMOVE(&probe->prov->probe_list, probe, probe_entry);
165 		probe->state = SDT_UNINIT;
166 	}
167 
168 	sx_xunlock(&sdt_sx);
169 }
170 
171 /*
172  * Called from SYSINIT to register a statically defined trace probe argument.
173  */
174 void
175 sdt_argtype_register(void *arg)
176 {
177 	struct sdt_argtype *argtype = arg;
178 
179 	sx_xlock(&sdt_sx);
180 
181 	TAILQ_INSERT_TAIL(&argtype->probe->argtype_list, argtype, argtype_entry);
182 
183 	argtype->probe->n_args++;
184 
185 	sx_xunlock(&sdt_sx);
186 }
187 
188 /*
189  * Called from SYSUNINIT to de-register a statically defined trace probe argument.
190  */
191 void
192 sdt_argtype_deregister(void *arg)
193 {
194 	struct sdt_argtype *argtype = arg;
195 
196 	sx_xlock(&sdt_sx);
197 
198 	TAILQ_REMOVE(&argtype->probe->argtype_list, argtype, argtype_entry);
199 
200 	sx_xunlock(&sdt_sx);
201 }
202 
203 static void
204 sdt_init(void *arg)
205 {
206 	sx_init_flags(&sdt_sx, "Statically Defined Tracing", SX_NOWITNESS);
207 
208 	TAILQ_INIT(&sdt_provider_list);
209 }
210 
211 SYSINIT(sdt, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_init, NULL);
212 
213 static void
214 sdt_uninit(void *arg)
215 {
216 	sx_destroy(&sdt_sx);
217 }
218 
219 SYSUNINIT(sdt, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_uninit, NULL);
220 
221 /*
222  * List statically defined tracing providers.
223  */
224 int
225 sdt_provider_listall(sdt_provider_listall_func_t callback_func, void *arg)
226 {
227 	int error;
228 
229 	sx_xlock(&sdt_sx);
230 	error = sdt_provider_listall_locked(callback_func, arg);
231 	sx_xunlock(&sdt_sx);
232 
233 	return (error);
234 }
235 
236 static int
237 sdt_provider_listall_locked(sdt_provider_listall_func_t callback_func,
238     void *arg)
239 {
240 	int error = 0;
241 	struct sdt_provider *prov;
242 
243 	sx_assert(&sdt_sx, SX_XLOCKED);
244 
245 	TAILQ_FOREACH(prov, &sdt_provider_list, prov_entry) {
246 		if ((error = callback_func(prov, arg)) != 0)
247 			break;
248 	}
249 
250 	return (error);
251 }
252 
253 /*
254  * List statically defined tracing probes.
255  */
256 int
257 sdt_probe_listall(struct sdt_provider *prov,
258     sdt_probe_listall_func_t callback_func,void *arg)
259 {
260 	int error = 0;
261 	int locked;
262 	struct sdt_probe *probe;
263 
264 	locked = sx_xlocked(&sdt_sx);
265 	if (!locked)
266 		sx_xlock(&sdt_sx);
267 
268 	TAILQ_FOREACH(probe, &prov->probe_list, probe_entry) {
269 		if ((error = callback_func(probe, arg)) != 0)
270 			break;
271 	}
272 
273 	if (!locked)
274 		sx_xunlock(&sdt_sx);
275 
276 	return (error);
277 }
278 
279 /*
280  * List statically defined tracing probe arguments.
281  */
282 int
283 sdt_argtype_listall(struct sdt_probe *probe,
284     sdt_argtype_listall_func_t callback_func,void *arg)
285 {
286 	int error = 0;
287 	int locked;
288 	struct sdt_argtype *argtype;
289 
290 	locked = sx_xlocked(&sdt_sx);
291 	if (!locked)
292 		sx_xlock(&sdt_sx);
293 
294 	TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) {
295 		if ((error = callback_func(argtype, arg)) != 0)
296 			break;
297 	}
298 
299 	if (!locked)
300 		sx_xunlock(&sdt_sx);
301 
302 	return (error);
303 }
304 
305 void sdt_register_callbacks(sdt_provider_listall_func_t register_prov,
306     void *reg_prov_arg, sdt_provider_listall_func_t deregister_prov,
307     void *dereg_prov_arg, sdt_probe_listall_func_t register_probe,
308     void * reg_probe_arg)
309 {
310 
311 	sx_xlock(&sdt_sx);
312 	sdt_provider_register_func = register_prov;
313 	sdt_provider_deregister_func = deregister_prov;
314 	sdt_probe_register_func = register_probe;
315 
316 	sdt_provider_register_arg = reg_prov_arg;
317 	sdt_provider_deregister_arg = dereg_prov_arg;
318 	sdt_probe_register_arg = reg_probe_arg;
319 
320 	sdt_provider_listall_locked(register_prov, reg_prov_arg);
321 	sx_xunlock(&sdt_sx);
322 }
323 
324 void sdt_deregister_callbacks(void)
325 {
326 
327 	sx_xlock(&sdt_sx);
328 	sdt_provider_listall_locked(sdt_provider_deregister_func,
329 	    sdt_provider_deregister_arg);
330 
331 	sdt_provider_register_func = NULL;
332 	sdt_provider_deregister_func = NULL;
333 	sdt_probe_register_func = NULL;
334 
335 	sdt_provider_register_arg = NULL;
336 	sdt_provider_deregister_arg = NULL;
337 	sdt_probe_register_arg = NULL;
338 	sx_xunlock(&sdt_sx);
339 }
340