xref: /freebsd/sys/cddl/dev/profile/profile.c (revision ec0e626bafb335b30c499d06066997f54b10c092)
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  *
23  * $FreeBSD$
24  *
25  */
26 
27 /*
28  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
29  * Use is subject to license terms.
30  */
31 
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/conf.h>
36 #include <sys/cpuvar.h>
37 #include <sys/fcntl.h>
38 #include <sys/filio.h>
39 #include <sys/kdb.h>
40 #include <sys/kernel.h>
41 #include <sys/kmem.h>
42 #include <sys/kthread.h>
43 #include <sys/limits.h>
44 #include <sys/linker.h>
45 #include <sys/lock.h>
46 #include <sys/malloc.h>
47 #include <sys/module.h>
48 #include <sys/mutex.h>
49 #include <sys/poll.h>
50 #include <sys/proc.h>
51 #include <sys/selinfo.h>
52 #include <sys/smp.h>
53 #include <sys/uio.h>
54 #include <sys/unistd.h>
55 #include <machine/cpu.h>
56 #include <machine/stdarg.h>
57 
58 #include <sys/dtrace.h>
59 #include <sys/dtrace_bsd.h>
60 
61 #define	PROF_NAMELEN		15
62 
63 #define	PROF_PROFILE		0
64 #define	PROF_TICK		1
65 #define	PROF_PREFIX_PROFILE	"profile-"
66 #define	PROF_PREFIX_TICK	"tick-"
67 
68 /*
69  * Regardless of platform, there are five artificial frames in the case of the
70  * profile provider:
71  *
72  *	profile_fire
73  *	cyclic_expire
74  *	cyclic_fire
75  *	[ cbe ]
76  *	[ locore ]
77  *
78  * On amd64, there are two frames associated with locore:  one in locore, and
79  * another in common interrupt dispatch code.  (i386 has not been modified to
80  * use this common layer.)  Further, on i386, the interrupted instruction
81  * appears as its own stack frame.  All of this means that we need to add one
82  * frame for amd64, and then take one away for both amd64 and i386.
83  *
84  * On SPARC, the picture is further complicated because the compiler
85  * optimizes away tail-calls -- so the following frames are optimized away:
86  *
87  * 	profile_fire
88  *	cyclic_expire
89  *
90  * This gives three frames.  However, on DEBUG kernels, the cyclic_expire
91  * frame cannot be tail-call eliminated, yielding four frames in this case.
92  *
93  * All of the above constraints lead to the mess below.  Yes, the profile
94  * provider should ideally figure this out on-the-fly by hiting one of its own
95  * probes and then walking its own stack trace.  This is complicated, however,
96  * and the static definition doesn't seem to be overly brittle.  Still, we
97  * allow for a manual override in case we get it completely wrong.
98  */
99 #ifdef __amd64
100 #define	PROF_ARTIFICIAL_FRAMES	10
101 #else
102 #ifdef __i386
103 #define	PROF_ARTIFICIAL_FRAMES	6
104 #else
105 #ifdef __sparc
106 #ifdef DEBUG
107 #define	PROF_ARTIFICIAL_FRAMES	4
108 #else
109 #define	PROF_ARTIFICIAL_FRAMES	3
110 #endif
111 #endif
112 #endif
113 #endif
114 
115 #ifdef __mips
116 /*
117  * This value is bogus just to make module compilable on mips
118  */
119 #define	PROF_ARTIFICIAL_FRAMES	3
120 #endif
121 
122 #ifdef __powerpc__
123 /*
124  * This value is bogus just to make module compilable on powerpc
125  */
126 #define	PROF_ARTIFICIAL_FRAMES	3
127 #endif
128 
129 struct profile_probe_percpu;
130 
131 #ifdef __mips
132 /* bogus */
133 #define	PROF_ARTIFICIAL_FRAMES	3
134 #endif
135 
136 #ifdef __arm__
137 /* bogus */
138 #define	PROF_ARTIFICIAL_FRAMES	9
139 #endif
140 
141 typedef struct profile_probe {
142 	char		prof_name[PROF_NAMELEN];
143 	dtrace_id_t	prof_id;
144 	int		prof_kind;
145 #ifdef illumos
146 	hrtime_t	prof_interval;
147 	cyclic_id_t	prof_cyclic;
148 #else
149 	sbintime_t	prof_interval;
150 	struct callout	prof_cyclic;
151 	sbintime_t	prof_expected;
152 	struct profile_probe_percpu **prof_pcpus;
153 #endif
154 } profile_probe_t;
155 
156 typedef struct profile_probe_percpu {
157 	hrtime_t	profc_expected;
158 	hrtime_t	profc_interval;
159 	profile_probe_t	*profc_probe;
160 #ifdef __FreeBSD__
161 	struct callout	profc_cyclic;
162 #endif
163 } profile_probe_percpu_t;
164 
165 static d_open_t	profile_open;
166 static int	profile_unload(void);
167 static void	profile_create(hrtime_t, char *, int);
168 static void	profile_destroy(void *, dtrace_id_t, void *);
169 static void	profile_enable(void *, dtrace_id_t, void *);
170 static void	profile_disable(void *, dtrace_id_t, void *);
171 static void	profile_load(void *);
172 static void	profile_provide(void *, dtrace_probedesc_t *);
173 
174 static int profile_rates[] = {
175     97, 199, 499, 997, 1999,
176     4001, 4999, 0, 0, 0,
177     0, 0, 0, 0, 0,
178     0, 0, 0, 0, 0
179 };
180 
181 static int profile_ticks[] = {
182     1, 10, 100, 500, 1000,
183     5000, 0, 0, 0, 0,
184     0, 0, 0, 0, 0
185 };
186 
187 /*
188  * profile_max defines the upper bound on the number of profile probes that
189  * can exist (this is to prevent malicious or clumsy users from exhausing
190  * system resources by creating a slew of profile probes). At mod load time,
191  * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
192  * present in the profile.conf file.
193  */
194 #define	PROFILE_MAX_DEFAULT	1000	/* default max. number of probes */
195 static uint32_t profile_max = PROFILE_MAX_DEFAULT;
196 					/* maximum number of profile probes */
197 static uint32_t profile_total;		/* current number of profile probes */
198 
199 static struct cdevsw profile_cdevsw = {
200 	.d_version	= D_VERSION,
201 	.d_open		= profile_open,
202 	.d_name		= "profile",
203 };
204 
205 static dtrace_pattr_t profile_attr = {
206 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
207 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
208 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
209 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
210 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
211 };
212 
213 static dtrace_pops_t profile_pops = {
214 	profile_provide,
215 	NULL,
216 	profile_enable,
217 	profile_disable,
218 	NULL,
219 	NULL,
220 	NULL,
221 	NULL,
222 	NULL,
223 	profile_destroy
224 };
225 
226 static struct cdev		*profile_cdev;
227 static dtrace_provider_id_t	profile_id;
228 static hrtime_t			profile_interval_min = NANOSEC / 5000;	/* 5000 hz */
229 static int			profile_aframes = 0;			/* override */
230 
231 static sbintime_t
232 nsec_to_sbt(hrtime_t nsec)
233 {
234 	time_t sec;
235 
236 	/*
237 	 * We need to calculate nsec * 2^32 / 10^9
238 	 * Seconds and nanoseconds are split to avoid overflow.
239 	 */
240 	sec = nsec / NANOSEC;
241 	nsec = nsec % NANOSEC;
242 	return (((sbintime_t)sec << 32) | ((sbintime_t)nsec << 32) / NANOSEC);
243 }
244 
245 static hrtime_t
246 sbt_to_nsec(sbintime_t sbt)
247 {
248 
249 	return ((sbt >> 32) * NANOSEC +
250 	    (((uint32_t)sbt * (hrtime_t)NANOSEC) >> 32));
251 }
252 
253 static void
254 profile_fire(void *arg)
255 {
256 	profile_probe_percpu_t *pcpu = arg;
257 	profile_probe_t *prof = pcpu->profc_probe;
258 	hrtime_t late;
259 	struct trapframe *frame;
260 	uintfptr_t pc, upc;
261 
262 #ifdef illumos
263 	late = gethrtime() - pcpu->profc_expected;
264 #else
265 	late = sbt_to_nsec(sbinuptime() - pcpu->profc_expected);
266 #endif
267 
268 	pc = 0;
269 	upc = 0;
270 
271 	/*
272 	 * td_intr_frame can be unset if this is a catch up event
273 	 * after waking up from idle sleep.
274 	 * This can only happen on a CPU idle thread.
275 	 */
276 	frame = curthread->td_intr_frame;
277 	if (frame != NULL) {
278 		if (TRAPF_USERMODE(frame))
279 			upc = TRAPF_PC(frame);
280 		else
281 			pc = TRAPF_PC(frame);
282 	}
283 	dtrace_probe(prof->prof_id, pc, upc, late, 0, 0);
284 
285 	pcpu->profc_expected += pcpu->profc_interval;
286 	callout_schedule_sbt_curcpu(&pcpu->profc_cyclic,
287 	    pcpu->profc_expected, 0, C_DIRECT_EXEC | C_ABSOLUTE);
288 }
289 
290 static void
291 profile_tick(void *arg)
292 {
293 	profile_probe_t *prof = arg;
294 	struct trapframe *frame;
295 	uintfptr_t pc, upc;
296 
297 	pc = 0;
298 	upc = 0;
299 
300 	/*
301 	 * td_intr_frame can be unset if this is a catch up event
302 	 * after waking up from idle sleep.
303 	 * This can only happen on a CPU idle thread.
304 	 */
305 	frame = curthread->td_intr_frame;
306 	if (frame != NULL) {
307 		if (TRAPF_USERMODE(frame))
308 			upc = TRAPF_PC(frame);
309 		else
310 			pc = TRAPF_PC(frame);
311 	}
312 	dtrace_probe(prof->prof_id, pc, upc, 0, 0, 0);
313 
314 	prof->prof_expected += prof->prof_interval;
315 	callout_schedule_sbt(&prof->prof_cyclic,
316 	    prof->prof_expected, 0, C_DIRECT_EXEC | C_ABSOLUTE);
317 }
318 
319 static void
320 profile_create(hrtime_t interval, char *name, int kind)
321 {
322 	profile_probe_t *prof;
323 
324 	if (interval < profile_interval_min)
325 		return;
326 
327 	if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
328 		return;
329 
330 	atomic_add_32(&profile_total, 1);
331 	if (profile_total > profile_max) {
332 		atomic_add_32(&profile_total, -1);
333 		return;
334 	}
335 
336 	prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
337 	(void) strcpy(prof->prof_name, name);
338 #ifdef illumos
339 	prof->prof_interval = interval;
340 	prof->prof_cyclic = CYCLIC_NONE;
341 #else
342 	prof->prof_interval = nsec_to_sbt(interval);
343 	callout_init(&prof->prof_cyclic, CALLOUT_MPSAFE);
344 #endif
345 	prof->prof_kind = kind;
346 	prof->prof_id = dtrace_probe_create(profile_id,
347 	    NULL, NULL, name,
348 	    profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
349 }
350 
351 /*ARGSUSED*/
352 static void
353 profile_provide(void *arg, dtrace_probedesc_t *desc)
354 {
355 	int i, j, rate, kind;
356 	hrtime_t val = 0, mult = 1, len = 0;
357 	char *name, *suffix = NULL;
358 
359 	const struct {
360 		char *prefix;
361 		int kind;
362 	} types[] = {
363 		{ PROF_PREFIX_PROFILE, PROF_PROFILE },
364 		{ PROF_PREFIX_TICK, PROF_TICK },
365 		{ 0, 0 }
366 	};
367 
368 	const struct {
369 		char *name;
370 		hrtime_t mult;
371 	} suffixes[] = {
372 		{ "ns", 	NANOSEC / NANOSEC },
373 		{ "nsec",	NANOSEC / NANOSEC },
374 		{ "us",		NANOSEC / MICROSEC },
375 		{ "usec",	NANOSEC / MICROSEC },
376 		{ "ms",		NANOSEC / MILLISEC },
377 		{ "msec",	NANOSEC / MILLISEC },
378 		{ "s",		NANOSEC / SEC },
379 		{ "sec",	NANOSEC / SEC },
380 		{ "m",		NANOSEC * (hrtime_t)60 },
381 		{ "min",	NANOSEC * (hrtime_t)60 },
382 		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
383 		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
384 		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
385 		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
386 		{ "hz",		0 },
387 		{ NULL }
388 	};
389 
390 	if (desc == NULL) {
391 		char n[PROF_NAMELEN];
392 
393 		/*
394 		 * If no description was provided, provide all of our probes.
395 		 */
396 		for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
397 			if ((rate = profile_rates[i]) == 0)
398 				continue;
399 
400 			(void) snprintf(n, PROF_NAMELEN, "%s%d",
401 			    PROF_PREFIX_PROFILE, rate);
402 			profile_create(NANOSEC / rate, n, PROF_PROFILE);
403 		}
404 
405 		for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
406 			if ((rate = profile_ticks[i]) == 0)
407 				continue;
408 
409 			(void) snprintf(n, PROF_NAMELEN, "%s%d",
410 			    PROF_PREFIX_TICK, rate);
411 			profile_create(NANOSEC / rate, n, PROF_TICK);
412 		}
413 
414 		return;
415 	}
416 
417 	name = desc->dtpd_name;
418 
419 	for (i = 0; types[i].prefix != NULL; i++) {
420 		len = strlen(types[i].prefix);
421 
422 		if (strncmp(name, types[i].prefix, len) != 0)
423 			continue;
424 		break;
425 	}
426 
427 	if (types[i].prefix == NULL)
428 		return;
429 
430 	kind = types[i].kind;
431 	j = strlen(name) - len;
432 
433 	/*
434 	 * We need to start before any time suffix.
435 	 */
436 	for (j = strlen(name); j >= len; j--) {
437 		if (name[j] >= '0' && name[j] <= '9')
438 			break;
439 		suffix = &name[j];
440 	}
441 
442 	ASSERT(suffix != NULL);
443 
444 	/*
445 	 * Now determine the numerical value present in the probe name.
446 	 */
447 	for (; j >= len; j--) {
448 		if (name[j] < '0' || name[j] > '9')
449 			return;
450 
451 		val += (name[j] - '0') * mult;
452 		mult *= (hrtime_t)10;
453 	}
454 
455 	if (val == 0)
456 		return;
457 
458 	/*
459 	 * Look-up the suffix to determine the multiplier.
460 	 */
461 	for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
462 		if (strcasecmp(suffixes[i].name, suffix) == 0) {
463 			mult = suffixes[i].mult;
464 			break;
465 		}
466 	}
467 
468 	if (suffixes[i].name == NULL && *suffix != '\0')
469 		return;
470 
471 	if (mult == 0) {
472 		/*
473 		 * The default is frequency-per-second.
474 		 */
475 		val = NANOSEC / val;
476 	} else {
477 		val *= mult;
478 	}
479 
480 	profile_create(val, name, kind);
481 }
482 
483 /* ARGSUSED */
484 static void
485 profile_destroy(void *arg, dtrace_id_t id, void *parg)
486 {
487 	profile_probe_t *prof = parg;
488 
489 #ifdef illumos
490 	ASSERT(prof->prof_cyclic == CYCLIC_NONE);
491 #else
492 	ASSERT(!callout_active(&prof->prof_cyclic) && prof->prof_pcpus == NULL);
493 #endif
494 	kmem_free(prof, sizeof (profile_probe_t));
495 
496 	ASSERT(profile_total >= 1);
497 	atomic_add_32(&profile_total, -1);
498 }
499 
500 #ifdef illumos
501 /*ARGSUSED*/
502 static void
503 profile_online(void *arg, cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
504 {
505 	profile_probe_t *prof = arg;
506 	profile_probe_percpu_t *pcpu;
507 
508 	pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
509 	pcpu->profc_probe = prof;
510 
511 	hdlr->cyh_func = profile_fire;
512 	hdlr->cyh_arg = pcpu;
513 
514 	when->cyt_interval = prof->prof_interval;
515 	when->cyt_when = gethrtime() + when->cyt_interval;
516 
517 	pcpu->profc_expected = when->cyt_when;
518 	pcpu->profc_interval = when->cyt_interval;
519 }
520 
521 /*ARGSUSED*/
522 static void
523 profile_offline(void *arg, cpu_t *cpu, void *oarg)
524 {
525 	profile_probe_percpu_t *pcpu = oarg;
526 
527 	ASSERT(pcpu->profc_probe == arg);
528 	kmem_free(pcpu, sizeof (profile_probe_percpu_t));
529 }
530 
531 /* ARGSUSED */
532 static void
533 profile_enable(void *arg, dtrace_id_t id, void *parg)
534 {
535 	profile_probe_t *prof = parg;
536 	cyc_omni_handler_t omni;
537 	cyc_handler_t hdlr;
538 	cyc_time_t when;
539 
540 	ASSERT(prof->prof_interval != 0);
541 	ASSERT(MUTEX_HELD(&cpu_lock));
542 
543 	if (prof->prof_kind == PROF_TICK) {
544 		hdlr.cyh_func = profile_tick;
545 		hdlr.cyh_arg = prof;
546 
547 		when.cyt_interval = prof->prof_interval;
548 		when.cyt_when = gethrtime() + when.cyt_interval;
549 	} else {
550 		ASSERT(prof->prof_kind == PROF_PROFILE);
551 		omni.cyo_online = profile_online;
552 		omni.cyo_offline = profile_offline;
553 		omni.cyo_arg = prof;
554 	}
555 
556 	if (prof->prof_kind == PROF_TICK) {
557 		prof->prof_cyclic = cyclic_add(&hdlr, &when);
558 	} else {
559 		prof->prof_cyclic = cyclic_add_omni(&omni);
560 	}
561 }
562 
563 /* ARGSUSED */
564 static void
565 profile_disable(void *arg, dtrace_id_t id, void *parg)
566 {
567 	profile_probe_t *prof = parg;
568 
569 	ASSERT(prof->prof_cyclic != CYCLIC_NONE);
570 	ASSERT(MUTEX_HELD(&cpu_lock));
571 
572 	cyclic_remove(prof->prof_cyclic);
573 	prof->prof_cyclic = CYCLIC_NONE;
574 }
575 
576 #else
577 
578 static void
579 profile_enable_omni(profile_probe_t *prof)
580 {
581 	profile_probe_percpu_t *pcpu;
582 	int cpu;
583 
584 	prof->prof_pcpus = kmem_zalloc((mp_maxid + 1) * sizeof(pcpu), KM_SLEEP);
585 	CPU_FOREACH(cpu) {
586 		pcpu = kmem_zalloc(sizeof(profile_probe_percpu_t), KM_SLEEP);
587 		prof->prof_pcpus[cpu] = pcpu;
588 		pcpu->profc_probe = prof;
589 		pcpu->profc_expected = sbinuptime() + prof->prof_interval;
590 		pcpu->profc_interval = prof->prof_interval;
591 		callout_init(&pcpu->profc_cyclic, CALLOUT_MPSAFE);
592 		callout_reset_sbt_on(&pcpu->profc_cyclic,
593 		    pcpu->profc_expected, 0, profile_fire, pcpu,
594 		    cpu, C_DIRECT_EXEC | C_ABSOLUTE);
595 	}
596 }
597 
598 static void
599 profile_disable_omni(profile_probe_t *prof)
600 {
601 	profile_probe_percpu_t *pcpu;
602 	int cpu;
603 
604 	ASSERT(prof->prof_pcpus != NULL);
605 	CPU_FOREACH(cpu) {
606 		pcpu = prof->prof_pcpus[cpu];
607 		ASSERT(pcpu->profc_probe == prof);
608 		ASSERT(callout_active(&pcpu->profc_cyclic));
609 		callout_stop(&pcpu->profc_cyclic);
610 		callout_drain(&pcpu->profc_cyclic);
611 		kmem_free(pcpu, sizeof(profile_probe_percpu_t));
612 	}
613 	kmem_free(prof->prof_pcpus, (mp_maxid + 1) * sizeof(pcpu));
614 	prof->prof_pcpus = NULL;
615 }
616 
617 /* ARGSUSED */
618 static void
619 profile_enable(void *arg, dtrace_id_t id, void *parg)
620 {
621 	profile_probe_t *prof = parg;
622 
623 	if (prof->prof_kind == PROF_TICK) {
624 		prof->prof_expected = sbinuptime() + prof->prof_interval;
625 		callout_reset_sbt(&prof->prof_cyclic,
626 		    prof->prof_expected, 0, profile_tick, prof,
627 		    C_DIRECT_EXEC | C_ABSOLUTE);
628 	} else {
629 		ASSERT(prof->prof_kind == PROF_PROFILE);
630 		profile_enable_omni(prof);
631 	}
632 }
633 
634 /* ARGSUSED */
635 static void
636 profile_disable(void *arg, dtrace_id_t id, void *parg)
637 {
638 	profile_probe_t *prof = parg;
639 
640 	if (prof->prof_kind == PROF_TICK) {
641 		ASSERT(callout_active(&prof->prof_cyclic));
642 		callout_stop(&prof->prof_cyclic);
643 		callout_drain(&prof->prof_cyclic);
644 	} else {
645 		ASSERT(prof->prof_kind == PROF_PROFILE);
646 		profile_disable_omni(prof);
647 	}
648 }
649 #endif
650 
651 static void
652 profile_load(void *dummy)
653 {
654 	/* Create the /dev/dtrace/profile entry. */
655 	profile_cdev = make_dev(&profile_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
656 	    "dtrace/profile");
657 
658 	if (dtrace_register("profile", &profile_attr, DTRACE_PRIV_USER,
659 	    NULL, &profile_pops, NULL, &profile_id) != 0)
660 		return;
661 }
662 
663 
664 static int
665 profile_unload()
666 {
667 	int error = 0;
668 
669 	if ((error = dtrace_unregister(profile_id)) != 0)
670 		return (error);
671 
672 	destroy_dev(profile_cdev);
673 
674 	return (error);
675 }
676 
677 /* ARGSUSED */
678 static int
679 profile_modevent(module_t mod __unused, int type, void *data __unused)
680 {
681 	int error = 0;
682 
683 	switch (type) {
684 	case MOD_LOAD:
685 		break;
686 
687 	case MOD_UNLOAD:
688 		break;
689 
690 	case MOD_SHUTDOWN:
691 		break;
692 
693 	default:
694 		error = EOPNOTSUPP;
695 		break;
696 
697 	}
698 	return (error);
699 }
700 
701 /* ARGSUSED */
702 static int
703 profile_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
704 {
705 	return (0);
706 }
707 
708 SYSINIT(profile_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, profile_load, NULL);
709 SYSUNINIT(profile_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, profile_unload, NULL);
710 
711 DEV_MODULE(profile, profile_modevent, NULL);
712 MODULE_VERSION(profile, 1);
713 MODULE_DEPEND(profile, dtrace, 1, 1, 1);
714 MODULE_DEPEND(profile, opensolaris, 1, 1, 1);
715