xref: /freebsd/sys/cddl/dev/profile/profile.c (revision 5f0216bd883edee71bf81051e3c20505e4820903)
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 /*
138  * At least on ARMv7, this appears to work quite well.
139  */
140 #define	PROF_ARTIFICIAL_FRAMES	10
141 #endif
142 
143 typedef struct profile_probe {
144 	char		prof_name[PROF_NAMELEN];
145 	dtrace_id_t	prof_id;
146 	int		prof_kind;
147 #ifdef illumos
148 	hrtime_t	prof_interval;
149 	cyclic_id_t	prof_cyclic;
150 #else
151 	sbintime_t	prof_interval;
152 	struct callout	prof_cyclic;
153 	sbintime_t	prof_expected;
154 	struct profile_probe_percpu **prof_pcpus;
155 #endif
156 } profile_probe_t;
157 
158 typedef struct profile_probe_percpu {
159 	hrtime_t	profc_expected;
160 	hrtime_t	profc_interval;
161 	profile_probe_t	*profc_probe;
162 #ifdef __FreeBSD__
163 	struct callout	profc_cyclic;
164 #endif
165 } profile_probe_percpu_t;
166 
167 static d_open_t	profile_open;
168 static int	profile_unload(void);
169 static void	profile_create(hrtime_t, char *, int);
170 static void	profile_destroy(void *, dtrace_id_t, void *);
171 static void	profile_enable(void *, dtrace_id_t, void *);
172 static void	profile_disable(void *, dtrace_id_t, void *);
173 static void	profile_load(void *);
174 static void	profile_provide(void *, dtrace_probedesc_t *);
175 
176 static int profile_rates[] = {
177     97, 199, 499, 997, 1999,
178     4001, 4999, 0, 0, 0,
179     0, 0, 0, 0, 0,
180     0, 0, 0, 0, 0
181 };
182 
183 static int profile_ticks[] = {
184     1, 10, 100, 500, 1000,
185     5000, 0, 0, 0, 0,
186     0, 0, 0, 0, 0
187 };
188 
189 /*
190  * profile_max defines the upper bound on the number of profile probes that
191  * can exist (this is to prevent malicious or clumsy users from exhausing
192  * system resources by creating a slew of profile probes). At mod load time,
193  * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
194  * present in the profile.conf file.
195  */
196 #define	PROFILE_MAX_DEFAULT	1000	/* default max. number of probes */
197 static uint32_t profile_max = PROFILE_MAX_DEFAULT;
198 					/* maximum number of profile probes */
199 static uint32_t profile_total;		/* current number of profile probes */
200 
201 static struct cdevsw profile_cdevsw = {
202 	.d_version	= D_VERSION,
203 	.d_open		= profile_open,
204 	.d_name		= "profile",
205 };
206 
207 static dtrace_pattr_t profile_attr = {
208 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
209 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
210 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
211 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
212 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
213 };
214 
215 static dtrace_pops_t profile_pops = {
216 	profile_provide,
217 	NULL,
218 	profile_enable,
219 	profile_disable,
220 	NULL,
221 	NULL,
222 	NULL,
223 	NULL,
224 	NULL,
225 	profile_destroy
226 };
227 
228 static struct cdev		*profile_cdev;
229 static dtrace_provider_id_t	profile_id;
230 static hrtime_t			profile_interval_min = NANOSEC / 5000;	/* 5000 hz */
231 static int			profile_aframes = 0;			/* override */
232 
233 static sbintime_t
234 nsec_to_sbt(hrtime_t nsec)
235 {
236 	time_t sec;
237 
238 	/*
239 	 * We need to calculate nsec * 2^32 / 10^9
240 	 * Seconds and nanoseconds are split to avoid overflow.
241 	 */
242 	sec = nsec / NANOSEC;
243 	nsec = nsec % NANOSEC;
244 	return (((sbintime_t)sec << 32) | ((sbintime_t)nsec << 32) / NANOSEC);
245 }
246 
247 static hrtime_t
248 sbt_to_nsec(sbintime_t sbt)
249 {
250 
251 	return ((sbt >> 32) * NANOSEC +
252 	    (((uint32_t)sbt * (hrtime_t)NANOSEC) >> 32));
253 }
254 
255 static void
256 profile_fire(void *arg)
257 {
258 	profile_probe_percpu_t *pcpu = arg;
259 	profile_probe_t *prof = pcpu->profc_probe;
260 	hrtime_t late;
261 	struct trapframe *frame;
262 	uintfptr_t pc, upc;
263 
264 #ifdef illumos
265 	late = gethrtime() - pcpu->profc_expected;
266 #else
267 	late = sbt_to_nsec(sbinuptime() - pcpu->profc_expected);
268 #endif
269 
270 	pc = 0;
271 	upc = 0;
272 
273 	/*
274 	 * td_intr_frame can be unset if this is a catch up event
275 	 * after waking up from idle sleep.
276 	 * This can only happen on a CPU idle thread.
277 	 */
278 	frame = curthread->td_intr_frame;
279 	if (frame != NULL) {
280 		if (TRAPF_USERMODE(frame))
281 			upc = TRAPF_PC(frame);
282 		else
283 			pc = TRAPF_PC(frame);
284 	}
285 	dtrace_probe(prof->prof_id, pc, upc, late, 0, 0);
286 
287 	pcpu->profc_expected += pcpu->profc_interval;
288 	callout_schedule_sbt_curcpu(&pcpu->profc_cyclic,
289 	    pcpu->profc_expected, 0, C_DIRECT_EXEC | C_ABSOLUTE);
290 }
291 
292 static void
293 profile_tick(void *arg)
294 {
295 	profile_probe_t *prof = arg;
296 	struct trapframe *frame;
297 	uintfptr_t pc, upc;
298 
299 	pc = 0;
300 	upc = 0;
301 
302 	/*
303 	 * td_intr_frame can be unset if this is a catch up event
304 	 * after waking up from idle sleep.
305 	 * This can only happen on a CPU idle thread.
306 	 */
307 	frame = curthread->td_intr_frame;
308 	if (frame != NULL) {
309 		if (TRAPF_USERMODE(frame))
310 			upc = TRAPF_PC(frame);
311 		else
312 			pc = TRAPF_PC(frame);
313 	}
314 	dtrace_probe(prof->prof_id, pc, upc, 0, 0, 0);
315 
316 	prof->prof_expected += prof->prof_interval;
317 	callout_schedule_sbt(&prof->prof_cyclic,
318 	    prof->prof_expected, 0, C_DIRECT_EXEC | C_ABSOLUTE);
319 }
320 
321 static void
322 profile_create(hrtime_t interval, char *name, int kind)
323 {
324 	profile_probe_t *prof;
325 
326 	if (interval < profile_interval_min)
327 		return;
328 
329 	if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
330 		return;
331 
332 	atomic_add_32(&profile_total, 1);
333 	if (profile_total > profile_max) {
334 		atomic_add_32(&profile_total, -1);
335 		return;
336 	}
337 
338 	prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
339 	(void) strcpy(prof->prof_name, name);
340 #ifdef illumos
341 	prof->prof_interval = interval;
342 	prof->prof_cyclic = CYCLIC_NONE;
343 #else
344 	prof->prof_interval = nsec_to_sbt(interval);
345 	callout_init(&prof->prof_cyclic, 1);
346 #endif
347 	prof->prof_kind = kind;
348 	prof->prof_id = dtrace_probe_create(profile_id,
349 	    NULL, NULL, name,
350 	    profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
351 }
352 
353 /*ARGSUSED*/
354 static void
355 profile_provide(void *arg, dtrace_probedesc_t *desc)
356 {
357 	int i, j, rate, kind;
358 	hrtime_t val = 0, mult = 1, len = 0;
359 	char *name, *suffix = NULL;
360 
361 	const struct {
362 		char *prefix;
363 		int kind;
364 	} types[] = {
365 		{ PROF_PREFIX_PROFILE, PROF_PROFILE },
366 		{ PROF_PREFIX_TICK, PROF_TICK },
367 		{ 0, 0 }
368 	};
369 
370 	const struct {
371 		char *name;
372 		hrtime_t mult;
373 	} suffixes[] = {
374 		{ "ns", 	NANOSEC / NANOSEC },
375 		{ "nsec",	NANOSEC / NANOSEC },
376 		{ "us",		NANOSEC / MICROSEC },
377 		{ "usec",	NANOSEC / MICROSEC },
378 		{ "ms",		NANOSEC / MILLISEC },
379 		{ "msec",	NANOSEC / MILLISEC },
380 		{ "s",		NANOSEC / SEC },
381 		{ "sec",	NANOSEC / SEC },
382 		{ "m",		NANOSEC * (hrtime_t)60 },
383 		{ "min",	NANOSEC * (hrtime_t)60 },
384 		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
385 		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
386 		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
387 		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
388 		{ "hz",		0 },
389 		{ NULL }
390 	};
391 
392 	if (desc == NULL) {
393 		char n[PROF_NAMELEN];
394 
395 		/*
396 		 * If no description was provided, provide all of our probes.
397 		 */
398 		for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
399 			if ((rate = profile_rates[i]) == 0)
400 				continue;
401 
402 			(void) snprintf(n, PROF_NAMELEN, "%s%d",
403 			    PROF_PREFIX_PROFILE, rate);
404 			profile_create(NANOSEC / rate, n, PROF_PROFILE);
405 		}
406 
407 		for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
408 			if ((rate = profile_ticks[i]) == 0)
409 				continue;
410 
411 			(void) snprintf(n, PROF_NAMELEN, "%s%d",
412 			    PROF_PREFIX_TICK, rate);
413 			profile_create(NANOSEC / rate, n, PROF_TICK);
414 		}
415 
416 		return;
417 	}
418 
419 	name = desc->dtpd_name;
420 
421 	for (i = 0; types[i].prefix != NULL; i++) {
422 		len = strlen(types[i].prefix);
423 
424 		if (strncmp(name, types[i].prefix, len) != 0)
425 			continue;
426 		break;
427 	}
428 
429 	if (types[i].prefix == NULL)
430 		return;
431 
432 	kind = types[i].kind;
433 	j = strlen(name) - len;
434 
435 	/*
436 	 * We need to start before any time suffix.
437 	 */
438 	for (j = strlen(name); j >= len; j--) {
439 		if (name[j] >= '0' && name[j] <= '9')
440 			break;
441 		suffix = &name[j];
442 	}
443 
444 	ASSERT(suffix != NULL);
445 
446 	/*
447 	 * Now determine the numerical value present in the probe name.
448 	 */
449 	for (; j >= len; j--) {
450 		if (name[j] < '0' || name[j] > '9')
451 			return;
452 
453 		val += (name[j] - '0') * mult;
454 		mult *= (hrtime_t)10;
455 	}
456 
457 	if (val == 0)
458 		return;
459 
460 	/*
461 	 * Look-up the suffix to determine the multiplier.
462 	 */
463 	for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
464 		if (strcasecmp(suffixes[i].name, suffix) == 0) {
465 			mult = suffixes[i].mult;
466 			break;
467 		}
468 	}
469 
470 	if (suffixes[i].name == NULL && *suffix != '\0')
471 		return;
472 
473 	if (mult == 0) {
474 		/*
475 		 * The default is frequency-per-second.
476 		 */
477 		val = NANOSEC / val;
478 	} else {
479 		val *= mult;
480 	}
481 
482 	profile_create(val, name, kind);
483 }
484 
485 /* ARGSUSED */
486 static void
487 profile_destroy(void *arg, dtrace_id_t id, void *parg)
488 {
489 	profile_probe_t *prof = parg;
490 
491 #ifdef illumos
492 	ASSERT(prof->prof_cyclic == CYCLIC_NONE);
493 #else
494 	ASSERT(!callout_active(&prof->prof_cyclic) && prof->prof_pcpus == NULL);
495 #endif
496 	kmem_free(prof, sizeof (profile_probe_t));
497 
498 	ASSERT(profile_total >= 1);
499 	atomic_add_32(&profile_total, -1);
500 }
501 
502 #ifdef illumos
503 /*ARGSUSED*/
504 static void
505 profile_online(void *arg, cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
506 {
507 	profile_probe_t *prof = arg;
508 	profile_probe_percpu_t *pcpu;
509 
510 	pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
511 	pcpu->profc_probe = prof;
512 
513 	hdlr->cyh_func = profile_fire;
514 	hdlr->cyh_arg = pcpu;
515 
516 	when->cyt_interval = prof->prof_interval;
517 	when->cyt_when = gethrtime() + when->cyt_interval;
518 
519 	pcpu->profc_expected = when->cyt_when;
520 	pcpu->profc_interval = when->cyt_interval;
521 }
522 
523 /*ARGSUSED*/
524 static void
525 profile_offline(void *arg, cpu_t *cpu, void *oarg)
526 {
527 	profile_probe_percpu_t *pcpu = oarg;
528 
529 	ASSERT(pcpu->profc_probe == arg);
530 	kmem_free(pcpu, sizeof (profile_probe_percpu_t));
531 }
532 
533 /* ARGSUSED */
534 static void
535 profile_enable(void *arg, dtrace_id_t id, void *parg)
536 {
537 	profile_probe_t *prof = parg;
538 	cyc_omni_handler_t omni;
539 	cyc_handler_t hdlr;
540 	cyc_time_t when;
541 
542 	ASSERT(prof->prof_interval != 0);
543 	ASSERT(MUTEX_HELD(&cpu_lock));
544 
545 	if (prof->prof_kind == PROF_TICK) {
546 		hdlr.cyh_func = profile_tick;
547 		hdlr.cyh_arg = prof;
548 
549 		when.cyt_interval = prof->prof_interval;
550 		when.cyt_when = gethrtime() + when.cyt_interval;
551 	} else {
552 		ASSERT(prof->prof_kind == PROF_PROFILE);
553 		omni.cyo_online = profile_online;
554 		omni.cyo_offline = profile_offline;
555 		omni.cyo_arg = prof;
556 	}
557 
558 	if (prof->prof_kind == PROF_TICK) {
559 		prof->prof_cyclic = cyclic_add(&hdlr, &when);
560 	} else {
561 		prof->prof_cyclic = cyclic_add_omni(&omni);
562 	}
563 }
564 
565 /* ARGSUSED */
566 static void
567 profile_disable(void *arg, dtrace_id_t id, void *parg)
568 {
569 	profile_probe_t *prof = parg;
570 
571 	ASSERT(prof->prof_cyclic != CYCLIC_NONE);
572 	ASSERT(MUTEX_HELD(&cpu_lock));
573 
574 	cyclic_remove(prof->prof_cyclic);
575 	prof->prof_cyclic = CYCLIC_NONE;
576 }
577 
578 #else
579 
580 static void
581 profile_enable_omni(profile_probe_t *prof)
582 {
583 	profile_probe_percpu_t *pcpu;
584 	int cpu;
585 
586 	prof->prof_pcpus = kmem_zalloc((mp_maxid + 1) * sizeof(pcpu), KM_SLEEP);
587 	CPU_FOREACH(cpu) {
588 		pcpu = kmem_zalloc(sizeof(profile_probe_percpu_t), KM_SLEEP);
589 		prof->prof_pcpus[cpu] = pcpu;
590 		pcpu->profc_probe = prof;
591 		pcpu->profc_expected = sbinuptime() + prof->prof_interval;
592 		pcpu->profc_interval = prof->prof_interval;
593 		callout_init(&pcpu->profc_cyclic, 1);
594 		callout_reset_sbt_on(&pcpu->profc_cyclic,
595 		    pcpu->profc_expected, 0, profile_fire, pcpu,
596 		    cpu, C_DIRECT_EXEC | C_ABSOLUTE);
597 	}
598 }
599 
600 static void
601 profile_disable_omni(profile_probe_t *prof)
602 {
603 	profile_probe_percpu_t *pcpu;
604 	int cpu;
605 
606 	ASSERT(prof->prof_pcpus != NULL);
607 	CPU_FOREACH(cpu) {
608 		pcpu = prof->prof_pcpus[cpu];
609 		ASSERT(pcpu->profc_probe == prof);
610 		ASSERT(callout_active(&pcpu->profc_cyclic));
611 		callout_stop(&pcpu->profc_cyclic);
612 		callout_drain(&pcpu->profc_cyclic);
613 		kmem_free(pcpu, sizeof(profile_probe_percpu_t));
614 	}
615 	kmem_free(prof->prof_pcpus, (mp_maxid + 1) * sizeof(pcpu));
616 	prof->prof_pcpus = NULL;
617 }
618 
619 /* ARGSUSED */
620 static void
621 profile_enable(void *arg, dtrace_id_t id, void *parg)
622 {
623 	profile_probe_t *prof = parg;
624 
625 	if (prof->prof_kind == PROF_TICK) {
626 		prof->prof_expected = sbinuptime() + prof->prof_interval;
627 		callout_reset_sbt(&prof->prof_cyclic,
628 		    prof->prof_expected, 0, profile_tick, prof,
629 		    C_DIRECT_EXEC | C_ABSOLUTE);
630 	} else {
631 		ASSERT(prof->prof_kind == PROF_PROFILE);
632 		profile_enable_omni(prof);
633 	}
634 }
635 
636 /* ARGSUSED */
637 static void
638 profile_disable(void *arg, dtrace_id_t id, void *parg)
639 {
640 	profile_probe_t *prof = parg;
641 
642 	if (prof->prof_kind == PROF_TICK) {
643 		ASSERT(callout_active(&prof->prof_cyclic));
644 		callout_stop(&prof->prof_cyclic);
645 		callout_drain(&prof->prof_cyclic);
646 	} else {
647 		ASSERT(prof->prof_kind == PROF_PROFILE);
648 		profile_disable_omni(prof);
649 	}
650 }
651 #endif
652 
653 static void
654 profile_load(void *dummy)
655 {
656 	/* Create the /dev/dtrace/profile entry. */
657 	profile_cdev = make_dev(&profile_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
658 	    "dtrace/profile");
659 
660 	if (dtrace_register("profile", &profile_attr, DTRACE_PRIV_USER,
661 	    NULL, &profile_pops, NULL, &profile_id) != 0)
662 		return;
663 }
664 
665 
666 static int
667 profile_unload()
668 {
669 	int error = 0;
670 
671 	if ((error = dtrace_unregister(profile_id)) != 0)
672 		return (error);
673 
674 	destroy_dev(profile_cdev);
675 
676 	return (error);
677 }
678 
679 /* ARGSUSED */
680 static int
681 profile_modevent(module_t mod __unused, int type, void *data __unused)
682 {
683 	int error = 0;
684 
685 	switch (type) {
686 	case MOD_LOAD:
687 		break;
688 
689 	case MOD_UNLOAD:
690 		break;
691 
692 	case MOD_SHUTDOWN:
693 		break;
694 
695 	default:
696 		error = EOPNOTSUPP;
697 		break;
698 
699 	}
700 	return (error);
701 }
702 
703 /* ARGSUSED */
704 static int
705 profile_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
706 {
707 	return (0);
708 }
709 
710 SYSINIT(profile_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, profile_load, NULL);
711 SYSUNINIT(profile_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, profile_unload, NULL);
712 
713 DEV_MODULE(profile, profile_modevent, NULL);
714 MODULE_VERSION(profile, 1);
715 MODULE_DEPEND(profile, dtrace, 1, 1, 1);
716 MODULE_DEPEND(profile, opensolaris, 1, 1, 1);
717