xref: /src/sys/cddl/dev/sdt/sdt.c (revision 66eedcb0224df03e56513f3caf1df93a52b6a919) !
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
22  * Copyright 2024 Mark Johnston <markj@FreeBSD.org>
23  */
24 
25 /*
26  * This file contains a reimplementation of the statically-defined tracing (SDT)
27  * framework for DTrace. Probes and SDT providers are defined using the macros
28  * in sys/sdt.h, which append all the needed structures to linker sets. When
29  * this module is loaded, it iterates over all of the loaded modules and
30  * registers probes and providers with the DTrace framework based on the
31  * contents of these linker sets.
32  *
33  * A list of SDT providers is maintained here since a provider may span multiple
34  * modules. When a kernel module is unloaded, a provider defined in that module
35  * is unregistered only if no other modules refer to it. The DTrace framework is
36  * responsible for destroying individual probes when a kernel module is
37  * unloaded; in particular, probes may not span multiple kernel modules.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 
43 #include <sys/conf.h>
44 #include <sys/endian.h>
45 #include <sys/eventhandler.h>
46 #include <sys/kernel.h>
47 #include <sys/limits.h>
48 #include <sys/linker.h>
49 #include <sys/linker_set.h>
50 #include <sys/lock.h>
51 #include <sys/lockstat.h>
52 #include <sys/malloc.h>
53 #include <sys/module.h>
54 #include <sys/mutex.h>
55 #include <sys/queue.h>
56 #include <sys/sdt.h>
57 
58 #include <sys/dtrace.h>
59 #include <sys/dtrace_bsd.h>
60 
61 #include <cddl/dev/dtrace/dtrace_cddl.h>
62 
63 _Static_assert(sizeof((struct sdt_probe *)NULL)->id == sizeof(dtrace_id_t),
64     "sdt_probe.id and dtrace_id_t size mismatch");
65 
66 /* DTrace methods. */
67 static void	sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
68 static uint64_t	sdt_getargval(void *, dtrace_id_t, void *, int, int);
69 static void	sdt_provide_probes(void *, dtrace_probedesc_t *);
70 static void	sdt_destroy(void *, dtrace_id_t, void *);
71 static void	sdt_enable(void *, dtrace_id_t, void *);
72 static void	sdt_disable(void *, dtrace_id_t, void *);
73 
74 static void	sdt_load(void);
75 static int	sdt_unload(void);
76 static void	sdt_create_provider(struct sdt_provider *);
77 static void	sdt_create_probe(struct sdt_probe *);
78 static void	sdt_init_probe(struct sdt_probe *, linker_file_t);
79 static void	sdt_kld_load(void *, struct linker_file *);
80 static void	sdt_kld_unload_try(void *, struct linker_file *, int *);
81 
82 static MALLOC_DEFINE(M_SDT, "SDT", "DTrace SDT providers");
83 
84 static int sdt_probes_enabled_count;
85 static int lockstat_enabled_count;
86 
87 static dtrace_pattr_t sdt_attr = {
88 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
89 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
90 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
91 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
92 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
93 };
94 
95 static dtrace_pops_t sdt_pops = {
96 	.dtps_provide =		sdt_provide_probes,
97 	.dtps_provide_module =	NULL,
98 	.dtps_enable =		sdt_enable,
99 	.dtps_disable =		sdt_disable,
100 	.dtps_suspend =		NULL,
101 	.dtps_resume =		NULL,
102 	.dtps_getargdesc =	sdt_getargdesc,
103 	.dtps_getargval =	sdt_getargval,
104 	.dtps_usermode =	NULL,
105 	.dtps_destroy =		sdt_destroy,
106 };
107 
108 static TAILQ_HEAD(, sdt_provider) sdt_prov_list;
109 
110 static eventhandler_tag	sdt_kld_load_tag;
111 static eventhandler_tag	sdt_kld_unload_try_tag;
112 
113 static void
sdt_create_provider(struct sdt_provider * prov)114 sdt_create_provider(struct sdt_provider *prov)
115 {
116 	struct sdt_provider *curr, *newprov;
117 
118 	TAILQ_FOREACH(curr, &sdt_prov_list, prov_entry)
119 		if (strcmp(prov->name, curr->name) == 0) {
120 			/* The provider has already been defined. */
121 			curr->sdt_refs++;
122 			return;
123 		}
124 
125 	/*
126 	 * Make a copy of prov so that we don't lose fields if its module is
127 	 * unloaded but the provider isn't destroyed. This could happen with
128 	 * a provider that spans multiple modules.
129 	 */
130 	newprov = malloc(sizeof(*newprov), M_SDT, M_WAITOK | M_ZERO);
131 	newprov->name = strdup(prov->name, M_SDT);
132 	prov->sdt_refs = newprov->sdt_refs = 1;
133 
134 	TAILQ_INSERT_TAIL(&sdt_prov_list, newprov, prov_entry);
135 
136 	(void)dtrace_register(newprov->name, &sdt_attr, DTRACE_PRIV_USER, NULL,
137 	    &sdt_pops, NULL, (dtrace_provider_id_t *)&newprov->id);
138 	prov->id = newprov->id;
139 }
140 
141 static void
sdt_create_probe(struct sdt_probe * probe)142 sdt_create_probe(struct sdt_probe *probe)
143 {
144 	struct sdt_provider *prov;
145 	char mod[DTRACE_MODNAMELEN];
146 	char func[DTRACE_FUNCNAMELEN];
147 	char name[DTRACE_NAMELEN];
148 	const char *from;
149 	char *to;
150 	size_t len;
151 	int aframes;
152 
153 	if (probe->version != (int)sizeof(*probe)) {
154 		printf("ignoring probe %p, version %u expected %u\n",
155 		    probe, probe->version, (int)sizeof(*probe));
156 		return;
157 	}
158 
159 	TAILQ_FOREACH(prov, &sdt_prov_list, prov_entry)
160 		if (strcmp(prov->name, probe->prov->name) == 0)
161 			break;
162 
163 	KASSERT(prov != NULL, ("probe defined without a provider"));
164 
165 	/* If no module name was specified, use the module filename. */
166 	if (*probe->mod == 0) {
167 		len = strlcpy(mod, probe->sdtp_lf->filename, sizeof(mod));
168 		if (len > 3 && strcmp(mod + len - 3, ".ko") == 0)
169 			mod[len - 3] = '\0';
170 	} else
171 		strlcpy(mod, probe->mod, sizeof(mod));
172 
173 	/*
174 	 * Unfortunately this is necessary because the Solaris DTrace
175 	 * code mixes consts and non-consts with casts to override
176 	 * the incompatibilies. On FreeBSD, we use strict warnings
177 	 * in the C compiler, so we have to respect const vs non-const.
178 	 */
179 	strlcpy(func, probe->func, sizeof(func));
180 	if (func[0] == '\0')
181 		strcpy(func, "none");
182 
183 	from = probe->name;
184 	to = name;
185 	for (len = 0; len < (sizeof(name) - 1) && *from != '\0';
186 	    len++, from++, to++) {
187 		if (from[0] == '_' && from[1] == '_') {
188 			*to = '-';
189 			from++;
190 		} else
191 			*to = *from;
192 	}
193 	*to = '\0';
194 
195 	if (dtrace_probe_lookup(prov->id, mod, func, name) != DTRACE_IDNONE)
196 		return;
197 
198 	aframes = 1; /* unwind past sdt_probe() */
199 	if (strcmp(prov->name, "lockstat") == 0) {
200 		/*
201 		 * Locking primitives instrumented by lockstat automatically
202 		 * disable inlining.  Step forward an extra frame so that DTrace
203 		 * variables like "caller" provide the function trying to
204 		 * acquire or release the lock rather than an internal function.
205 		 */
206 		aframes++;
207 	}
208 	(void)dtrace_probe_create(prov->id, mod, func, name, aframes, probe);
209 }
210 
211 static void
sdt_init_probe(struct sdt_probe * probe,linker_file_t lf)212 sdt_init_probe(struct sdt_probe *probe, linker_file_t lf)
213 {
214 	probe->sdtp_lf = lf;
215 	TAILQ_INIT(&probe->argtype_list);
216 	STAILQ_INIT(&probe->tracepoint_list);
217 }
218 
219 /*
220  * Probes are created through the SDT module load/unload hook, so this function
221  * has nothing to do. It only exists because the DTrace provider framework
222  * requires one of provide_probes and provide_module to be defined.
223  */
224 static void
sdt_provide_probes(void * arg,dtrace_probedesc_t * desc)225 sdt_provide_probes(void *arg, dtrace_probedesc_t *desc)
226 {
227 }
228 
229 struct sdt_enable_cb_arg {
230 	struct sdt_probe *probe;
231 	int cpu;
232 	int arrived;
233 	int done;
234 	bool enable;
235 };
236 
237 static void
sdt_probe_update_cb(void * _arg)238 sdt_probe_update_cb(void *_arg)
239 {
240 	struct sdt_enable_cb_arg *arg;
241 	struct sdt_tracepoint *tp;
242 
243 	arg = _arg;
244 	if (arg->cpu != curcpu) {
245 		atomic_add_rel_int(&arg->arrived, 1);
246 		while (atomic_load_acq_int(&arg->done) == 0)
247 			cpu_spinwait();
248 		return;
249 	} else {
250 		while (atomic_load_acq_int(&arg->arrived) != mp_ncpus - 1)
251 			cpu_spinwait();
252 	}
253 
254 	STAILQ_FOREACH(tp, &arg->probe->tracepoint_list, tracepoint_entry) {
255 		if (arg->enable)
256 			sdt_tracepoint_patch(tp->patchpoint, tp->target);
257 		else
258 			sdt_tracepoint_restore(tp->patchpoint);
259 	}
260 
261 	atomic_store_rel_int(&arg->done, 1);
262 }
263 
264 static void
sdt_probe_update(struct sdt_probe * probe,bool enable)265 sdt_probe_update(struct sdt_probe *probe, bool enable)
266 {
267 	struct sdt_enable_cb_arg cbarg;
268 
269 	sched_pin();
270 	cbarg.probe = probe;
271 	cbarg.cpu = curcpu;
272 	atomic_store_rel_int(&cbarg.arrived, 0);
273 	atomic_store_rel_int(&cbarg.done, 0);
274 	cbarg.enable = enable;
275 	smp_rendezvous(NULL, sdt_probe_update_cb, NULL, &cbarg);
276 	sched_unpin();
277 }
278 
279 static void
sdt_enable(void * arg __unused,dtrace_id_t id,void * parg)280 sdt_enable(void *arg __unused, dtrace_id_t id, void *parg)
281 {
282 	struct sdt_probe *probe;
283 
284 	probe = parg;
285 
286 	probe->id = id;
287 	probe->sdtp_lf->nenabled++;
288 	if (strcmp(probe->prov->name, "lockstat") == 0) {
289 		lockstat_enabled_count++;
290 		if (lockstat_enabled_count == 1)
291 			lockstat_enabled = true;
292 	}
293 	sdt_probes_enabled_count++;
294 	if (sdt_probes_enabled_count == 1)
295 		sdt_probes_enabled = true;
296 
297 	sdt_probe_update(probe, true);
298 }
299 
300 static void
sdt_disable(void * arg __unused,dtrace_id_t id,void * parg)301 sdt_disable(void *arg __unused, dtrace_id_t id, void *parg)
302 {
303 	struct sdt_probe *probe;
304 
305 	probe = parg;
306 	KASSERT(probe->sdtp_lf->nenabled > 0, ("no probes enabled"));
307 
308 	sdt_probe_update(probe, false);
309 
310 	sdt_probes_enabled_count--;
311 	if (sdt_probes_enabled_count == 0)
312 		sdt_probes_enabled = false;
313 	if (strcmp(probe->prov->name, "lockstat") == 0) {
314 		lockstat_enabled_count--;
315 		if (lockstat_enabled_count == 0)
316 			lockstat_enabled = false;
317 	}
318 	probe->id = 0;
319 	probe->sdtp_lf->nenabled--;
320 }
321 
322 static void
sdt_getargdesc(void * arg,dtrace_id_t id,void * parg,dtrace_argdesc_t * desc)323 sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
324 {
325 	struct sdt_argtype *argtype;
326 	struct sdt_probe *probe = parg;
327 
328 	if (desc->dtargd_ndx >= probe->n_args) {
329 		desc->dtargd_ndx = DTRACE_ARGNONE;
330 		return;
331 	}
332 
333 	TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) {
334 		if (desc->dtargd_ndx == argtype->ndx) {
335 			desc->dtargd_mapping = desc->dtargd_ndx;
336 			if (argtype->type == NULL) {
337 				desc->dtargd_native[0] = '\0';
338 				desc->dtargd_xlate[0] = '\0';
339 				continue;
340 			}
341 			strlcpy(desc->dtargd_native, argtype->type,
342 			    sizeof(desc->dtargd_native));
343 			if (argtype->xtype != NULL)
344 				strlcpy(desc->dtargd_xlate, argtype->xtype,
345 				    sizeof(desc->dtargd_xlate));
346 		}
347 	}
348 }
349 
350 /*
351  * Fetch arguments beyond the first five passed directly to dtrace_probe().
352  * FreeBSD's SDT implement currently only supports up to 6 arguments, so we just
353  * need to handle arg5 here.
354  */
355 static uint64_t
sdt_getargval(void * arg __unused,dtrace_id_t id __unused,void * parg __unused,int argno,int aframes __unused)356 sdt_getargval(void *arg __unused, dtrace_id_t id __unused,
357     void *parg __unused, int argno, int aframes __unused)
358 {
359 	if (argno != 5) {
360 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
361 		return (0);
362 	} else {
363 		return (curthread->t_dtrace_sdt_arg[argno - 5]);
364 	}
365 }
366 
367 static void
sdt_destroy(void * arg,dtrace_id_t id,void * parg)368 sdt_destroy(void *arg, dtrace_id_t id, void *parg)
369 {
370 }
371 
372 static void
sdt_kld_load_providers(struct linker_file * lf)373 sdt_kld_load_providers(struct linker_file *lf)
374 {
375 	struct sdt_provider **prov, **begin, **end;
376 	struct sdt_probe **p_begin, **p_end;
377 
378 	if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end,
379 	    NULL) == 0) {
380 		for (prov = begin; prov < end; prov++)
381 			sdt_create_provider(*prov);
382 	}
383 
384 	if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end,
385 	    NULL) == 0) {
386 		for (struct sdt_probe **probe = p_begin; probe < p_end; probe++)
387 			sdt_init_probe(*probe, lf);
388 	}
389 }
390 
391 static void
sdt_kld_load_probes(struct linker_file * lf)392 sdt_kld_load_probes(struct linker_file *lf)
393 {
394 	struct sdt_probe **p_begin, **p_end;
395 	struct sdt_argtype **a_begin, **a_end;
396 	struct sdt_tracepoint *tp_begin, *tp_end;
397 
398 	if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end,
399 	    NULL) == 0) {
400 		for (struct sdt_probe **probe = p_begin; probe < p_end; probe++)
401 			sdt_create_probe(*probe);
402 	}
403 
404 	if (linker_file_lookup_set(lf, "sdt_argtypes_set", &a_begin, &a_end,
405 	    NULL) == 0) {
406 		for (struct sdt_argtype **argtype = a_begin; argtype < a_end;
407 		    argtype++) {
408 			(*argtype)->probe->n_args++;
409 			TAILQ_INSERT_TAIL(&(*argtype)->probe->argtype_list,
410 			    *argtype, argtype_entry);
411 		}
412 	}
413 
414 	if (linker_file_lookup_set(lf, __XSTRING(_SDT_TRACEPOINT_SET),
415 	    &tp_begin, &tp_end, NULL) == 0) {
416 		for (struct sdt_tracepoint *tp = tp_begin; tp < tp_end; tp++) {
417 			if (!sdt_tracepoint_valid(tp->patchpoint, tp->target)) {
418 				printf(
419 			    "invalid tracepoint %#jx->%#jx for %s:%s:%s:%s\n",
420 				    (uintmax_t)tp->patchpoint,
421 				    (uintmax_t)tp->target,
422 				    tp->probe->prov->name, tp->probe->mod,
423 				    tp->probe->func, tp->probe->name);
424 				continue;
425 			}
426 			STAILQ_INSERT_TAIL(&tp->probe->tracepoint_list, tp,
427 			    tracepoint_entry);
428 		}
429 	}
430 }
431 
432 /*
433  * Called from the kernel linker when a module is loaded, before
434  * dtrace_module_loaded() is called. This is done so that it's possible to
435  * register new providers when modules are loaded. The DTrace framework
436  * explicitly disallows calling into the framework from the provide_module
437  * provider method, so we cannot do this there.
438  */
439 static void
sdt_kld_load(void * arg __unused,struct linker_file * lf)440 sdt_kld_load(void *arg __unused, struct linker_file *lf)
441 {
442 	sdt_kld_load_providers(lf);
443 	sdt_kld_load_probes(lf);
444 }
445 
446 static bool
sdt_kld_unload_providers(struct linker_file * lf)447 sdt_kld_unload_providers(struct linker_file *lf)
448 {
449 	struct sdt_provider *prov, **curr, **begin, **end, *tmp;
450 
451 	if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end,
452 	    NULL))
453 		/* No DTrace providers are declared in this file. */
454 		return (true);
455 
456 	/*
457 	 * Go through all the providers declared in this linker file and
458 	 * unregister any that aren't declared in another loaded file.
459 	 */
460 	for (curr = begin; curr < end; curr++) {
461 		TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) {
462 			if (strcmp(prov->name, (*curr)->name) != 0)
463 				continue;
464 
465 			if (prov->sdt_refs == 1) {
466 				if (dtrace_unregister(prov->id) != 0) {
467 					return (false);
468 				}
469 				TAILQ_REMOVE(&sdt_prov_list, prov, prov_entry);
470 				free(prov->name, M_SDT);
471 				free(prov, M_SDT);
472 			} else
473 				prov->sdt_refs--;
474 			break;
475 		}
476 	}
477 
478 	return (true);
479 }
480 
481 static bool
sdt_kld_unload_probes(struct linker_file * lf)482 sdt_kld_unload_probes(struct linker_file *lf)
483 {
484 	struct sdt_probe **p_begin, **p_end;
485 	struct sdt_argtype **a_begin, **a_end;
486 	struct sdt_tracepoint *tp_begin, *tp_end;
487 
488 	if (linker_file_lookup_set(lf, __XSTRING(_SDT_TRACEPOINT_SET),
489 	    &tp_begin, &tp_end, NULL) == 0) {
490 		for (struct sdt_tracepoint *tp = tp_begin; tp < tp_end; tp++) {
491 			struct sdt_tracepoint *tp2;
492 
493 			if (!sdt_tracepoint_valid(tp->patchpoint, tp->target))
494 				continue;
495 
496 			/* Only remove the entry if it is in the list. */
497 			tp2 = STAILQ_FIRST(&tp->probe->tracepoint_list);
498 			if (tp2 == tp) {
499 				STAILQ_REMOVE_HEAD(&tp->probe->tracepoint_list,
500 				    tracepoint_entry);
501 			} else if (tp2 != NULL) {
502 				struct sdt_tracepoint *tp3;
503 
504 				for (;;) {
505 					tp3 = STAILQ_NEXT(tp2,
506 					    tracepoint_entry);
507 					if (tp3 == NULL)
508 						break;
509 					if (tp3 == tp) {
510 						STAILQ_REMOVE_AFTER(
511 						    &tp->probe->tracepoint_list,
512 						    tp2, tracepoint_entry);
513 						break;
514 					}
515 					tp2 = tp3;
516 				}
517 			}
518 		}
519 	}
520 
521 	if (linker_file_lookup_set(lf, "sdt_argtypes_set", &a_begin, &a_end,
522 	    NULL) == 0) {
523 		for (struct sdt_argtype **argtype = a_begin; argtype < a_end;
524 		    argtype++) {
525 			struct sdt_argtype *argtype2;
526 
527 			/* Only remove the entry if it is in the list. */
528 			TAILQ_FOREACH(argtype2,
529 			    &(*argtype)->probe->argtype_list, argtype_entry) {
530 				if (argtype2 == *argtype) {
531 					(*argtype)->probe->n_args--;
532 					TAILQ_REMOVE(
533 					    &(*argtype)->probe->argtype_list,
534 					    *argtype, argtype_entry);
535 					break;
536 				}
537 			}
538 		}
539 	}
540 
541 	if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end,
542 	    NULL) == 0) {
543 		for (struct sdt_probe **probe = p_begin; probe < p_end;
544 		    probe++) {
545 			if ((*probe)->sdtp_lf == lf) {
546 				if (!TAILQ_EMPTY(&(*probe)->argtype_list))
547 					return (false);
548 				if (!STAILQ_EMPTY(&(*probe)->tracepoint_list))
549 					return (false);
550 
551 				/*
552 				 * Don't destroy the probe as there
553 				 * might be multiple instances of the
554 				 * same probe in different modules.
555 				 */
556 			}
557 		}
558 	}
559 
560 	return (true);
561 }
562 
563 static void
sdt_kld_unload_try(void * arg __unused,struct linker_file * lf,int * error)564 sdt_kld_unload_try(void *arg __unused, struct linker_file *lf, int *error)
565 {
566 	if (*error != 0)
567 		/* We already have an error, so don't do anything. */
568 		return;
569 
570 	if (!sdt_kld_unload_probes(lf))
571 		*error = 1;
572 	else if (!sdt_kld_unload_providers(lf))
573 		*error = 1;
574 }
575 
576 static int
sdt_load_providers_cb(linker_file_t lf,void * arg __unused)577 sdt_load_providers_cb(linker_file_t lf, void *arg __unused)
578 {
579 	sdt_kld_load_providers(lf);
580 	return (0);
581 }
582 
583 static int
sdt_load_probes_cb(linker_file_t lf,void * arg __unused)584 sdt_load_probes_cb(linker_file_t lf, void *arg __unused)
585 {
586 	sdt_kld_load_probes(lf);
587 	return (0);
588 }
589 
590 static void
sdt_dtrace_probe(dtrace_id_t id,uintptr_t arg0,uintptr_t arg1,uintptr_t arg2,uintptr_t arg3,uintptr_t arg4,uintptr_t arg5)591 sdt_dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
592     uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
593 {
594 	curthread->t_dtrace_sdt_arg[0] = arg5;
595 	dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
596 }
597 
598 static void
sdt_load(void)599 sdt_load(void)
600 {
601 
602 	TAILQ_INIT(&sdt_prov_list);
603 
604 	sdt_probe_func = sdt_dtrace_probe;
605 
606 	sdt_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, sdt_kld_load, NULL,
607 	    EVENTHANDLER_PRI_ANY);
608 	sdt_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try,
609 	    sdt_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY);
610 
611 	/*
612 	 * Pick up probes from the kernel and already-loaded linker files.
613 	 * Define providers in a separate pass since a linker file may be using
614 	 * providers defined in a file that appears later in the list.
615 	 */
616 	linker_file_foreach(sdt_load_providers_cb, NULL);
617 	linker_file_foreach(sdt_load_probes_cb, NULL);
618 }
619 
620 static int
sdt_unload(void)621 sdt_unload(void)
622 {
623 	struct sdt_provider *prov, *tmp;
624 	int ret;
625 
626 	EVENTHANDLER_DEREGISTER(kld_load, sdt_kld_load_tag);
627 	EVENTHANDLER_DEREGISTER(kld_unload_try, sdt_kld_unload_try_tag);
628 
629 	sdt_probe_func = sdt_probe_stub;
630 
631 	TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) {
632 		ret = dtrace_unregister(prov->id);
633 		if (ret != 0)
634 			return (ret);
635 		TAILQ_REMOVE(&sdt_prov_list, prov, prov_entry);
636 		free(prov->name, M_SDT);
637 		free(prov, M_SDT);
638 	}
639 
640 	return (0);
641 }
642 
643 static int
sdt_modevent(module_t mod __unused,int type,void * data __unused)644 sdt_modevent(module_t mod __unused, int type, void *data __unused)
645 {
646 	switch (type) {
647 	case MOD_LOAD:
648 	case MOD_UNLOAD:
649 	case MOD_SHUTDOWN:
650 		return (0);
651 	default:
652 		return (EOPNOTSUPP);
653 	}
654 }
655 
656 SYSINIT(sdt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_load, NULL);
657 SYSUNINIT(sdt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_unload, NULL);
658 
659 DEV_MODULE(sdt, sdt_modevent, NULL);
660 MODULE_VERSION(sdt, 1);
661 MODULE_DEPEND(sdt, dtrace, 1, 1, 1);
662 MODULE_DEPEND(sdt, opensolaris, 1, 1, 1);
663