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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* #pragma ident "@(#)profile.c 1.7 07/01/10 SMI" */
28 #if !defined(__APPLE__)
29 #include <sys/errno.h>
31 #include <sys/modctl.h>
33 #include <sys/systm.h>
35 #include <sys/sunddi.h>
36 #include <sys/cpuvar.h>
38 #include <sys/strsubr.h>
39 #include <sys/dtrace.h>
40 #include <sys/cyclic.h>
41 #include <sys/atomic.h>
45 #define _KERNEL /* Solaris vs. Darwin */
49 #define MACH__POSIX_C_SOURCE_PRIVATE 1 /* pulls in suitable savearea from mach/ppc/thread_status.h */
50 #include <kern/cpu_data.h>
51 #include <kern/thread.h>
52 #include <kern/assert.h>
53 #include <mach/thread_status.h>
55 #include <sys/param.h>
56 #include <sys/systm.h>
57 #include <sys/errno.h>
59 #include <sys/ioctl.h>
61 #include <sys/fcntl.h>
62 #include <miscfs/devfs/devfs.h>
64 #include <sys/dtrace.h>
65 #include <sys/dtrace_impl.h>
67 #include <sys/dtrace_glue.h>
69 #include <machine/pal_routines.h>
71 #if defined(__i386__) || defined(__x86_64__)
72 extern x86_saved_state_t
*find_kern_regs(thread_t
);
74 #error Unknown architecture
78 #define ASSERT(x) do {} while(0)
80 extern void profile_init(void);
81 #endif /* __APPLE__ */
83 static dev_info_t
*profile_devi
;
84 static dtrace_provider_id_t profile_id
;
87 * Regardless of platform, the stack frames look like this in the case of the
96 * On x86, there are five frames from the generic interrupt code; further, the
97 * interrupted instruction appears as its own stack frame, giving us a total of
100 * On SPARC, the picture is further complicated because the compiler
101 * optimizes away tail-calls -- so the following frames are optimized away:
106 * This gives three frames. However, on DEBUG kernels, the cyclic_expire
107 * frame cannot be tail-call eliminated, yielding four frames in this case.
109 * All of the above constraints lead to the mess below. Yes, the profile
110 * provider should ideally figure this out on-the-fly by hitting one of its own
111 * probes and then walking its own stack trace. This is complicated, however,
112 * and the static definition doesn't seem to be overly brittle. Still, we
113 * allow for a manual override in case we get it completely wrong.
115 #if !defined(__APPLE__)
118 #define PROF_ARTIFICIAL_FRAMES 10
122 #define PROF_ARTIFICIAL_FRAMES 4
124 #define PROF_ARTIFICIAL_FRAMES 3
129 #else /* is Mac OS X */
131 #if defined(__i386__) || defined(__x86_64__)
132 #define PROF_ARTIFICIAL_FRAMES 9
134 #error Unknown architecture
137 #endif /* __APPLE__ */
139 #define PROF_NAMELEN 15
141 #define PROF_PROFILE 0
143 #define PROF_PREFIX_PROFILE "profile-"
144 #define PROF_PREFIX_TICK "tick-"
146 typedef struct profile_probe
{
147 char prof_name
[PROF_NAMELEN
];
150 hrtime_t prof_interval
;
151 cyclic_id_t prof_cyclic
;
154 typedef struct profile_probe_percpu
{
155 hrtime_t profc_expected
;
156 hrtime_t profc_interval
;
157 profile_probe_t
*profc_probe
;
158 } profile_probe_percpu_t
;
160 hrtime_t profile_interval_min
= NANOSEC
/ 5000; /* 5000 hz */
161 int profile_aframes
= 0; /* override */
163 static int profile_rates
[] = {
164 97, 199, 499, 997, 1999,
170 static int profile_ticks
[] = {
171 1, 10, 100, 500, 1000,
177 * profile_max defines the upper bound on the number of profile probes that
178 * can exist (this is to prevent malicious or clumsy users from exhausing
179 * system resources by creating a slew of profile probes). At mod load time,
180 * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
181 * present in the profile.conf file.
183 #define PROFILE_MAX_DEFAULT 1000 /* default max. number of probes */
184 static uint32_t profile_max
; /* maximum number of profile probes */
185 static uint32_t profile_total
; /* current number of profile probes */
188 profile_fire(void *arg
)
190 profile_probe_percpu_t
*pcpu
= arg
;
191 profile_probe_t
*prof
= pcpu
->profc_probe
;
194 late
= dtrace_gethrtime() - pcpu
->profc_expected
;
195 pcpu
->profc_expected
+= pcpu
->profc_interval
;
197 #if !defined(__APPLE__)
198 dtrace_probe(prof
->prof_id
, CPU
->cpu_profile_pc
,
199 CPU
->cpu_profile_upc
, late
, 0, 0);
201 #if defined(__i386__) || defined(__x86_64__)
202 x86_saved_state_t
*kern_regs
= find_kern_regs(current_thread());
204 if (NULL
!= kern_regs
) {
205 /* Kernel was interrupted. */
206 #if defined(__i386__)
207 dtrace_probe(prof
->prof_id
, saved_state32(kern_regs
)->eip
, 0x0, 0, 0, 0);
208 #elif defined(__x86_64__)
209 dtrace_probe(prof
->prof_id
, saved_state64(kern_regs
)->isf
.rip
, 0x0, 0, 0, 0);
214 pal_register_cache_state(current_thread(), VALID
);
215 /* Possibly a user interrupt */
216 x86_saved_state_t
*tagged_regs
= (x86_saved_state_t
*)find_user_regs(current_thread());
218 if (NULL
== tagged_regs
) {
219 /* Too bad, so sad, no useful interrupt state. */
220 dtrace_probe(prof
->prof_id
, 0xcafebabe,
221 0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
222 } else if (is_saved_state64(tagged_regs
)) {
223 x86_saved_state64_t
*regs
= saved_state64(tagged_regs
);
225 dtrace_probe(prof
->prof_id
, 0x0, regs
->isf
.rip
, 0, 0, 0);
227 x86_saved_state32_t
*regs
= saved_state32(tagged_regs
);
229 dtrace_probe(prof
->prof_id
, 0x0, regs
->eip
, 0, 0, 0);
233 #error Unknown architecture
235 #endif /* __APPLE__ */
239 profile_tick(void *arg
)
241 profile_probe_t
*prof
= arg
;
243 #if !defined(__APPLE__)
244 dtrace_probe(prof
->prof_id
, CPU
->cpu_profile_pc
,
245 CPU
->cpu_profile_upc
, 0, 0, 0);
247 #if defined(__i386__) || defined(__x86_64__)
248 x86_saved_state_t
*kern_regs
= find_kern_regs(current_thread());
250 if (NULL
!= kern_regs
) {
251 /* Kernel was interrupted. */
252 #if defined(__i386__)
253 dtrace_probe(prof
->prof_id
, saved_state32(kern_regs
)->eip
, 0x0, 0, 0, 0);
254 #elif defined(__x86_64__)
255 dtrace_probe(prof
->prof_id
, saved_state64(kern_regs
)->isf
.rip
, 0x0, 0, 0, 0);
260 pal_register_cache_state(current_thread(), VALID
);
261 /* Possibly a user interrupt */
262 x86_saved_state_t
*tagged_regs
= (x86_saved_state_t
*)find_user_regs(current_thread());
264 if (NULL
== tagged_regs
) {
265 /* Too bad, so sad, no useful interrupt state. */
266 dtrace_probe(prof
->prof_id
, 0xcafebabe,
267 0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
268 } else if (is_saved_state64(tagged_regs
)) {
269 x86_saved_state64_t
*regs
= saved_state64(tagged_regs
);
271 dtrace_probe(prof
->prof_id
, 0x0, regs
->isf
.rip
, 0, 0, 0);
273 x86_saved_state32_t
*regs
= saved_state32(tagged_regs
);
275 dtrace_probe(prof
->prof_id
, 0x0, regs
->eip
, 0, 0, 0);
279 #error Unknown architecture
281 #endif /* __APPLE__ */
285 profile_create(hrtime_t interval
, const char *name
, int kind
)
287 profile_probe_t
*prof
;
289 if (interval
< profile_interval_min
)
292 if (dtrace_probe_lookup(profile_id
, NULL
, NULL
, name
) != 0)
295 atomic_add_32(&profile_total
, 1);
296 if (profile_total
> profile_max
) {
297 atomic_add_32(&profile_total
, -1);
301 #if !defined(__APPLE__)
302 prof
= kmem_zalloc(sizeof (profile_probe_t
), KM_SLEEP
);
304 if (PROF_TICK
== kind
)
305 prof
= kmem_zalloc(sizeof (profile_probe_t
), KM_SLEEP
);
307 prof
= kmem_zalloc(sizeof (profile_probe_t
) + NCPU
*sizeof(profile_probe_percpu_t
), KM_SLEEP
);
308 #endif /* __APPLE__ */
309 (void) strlcpy(prof
->prof_name
, name
, sizeof(prof
->prof_name
));
310 prof
->prof_interval
= interval
;
311 prof
->prof_cyclic
= CYCLIC_NONE
;
312 prof
->prof_kind
= kind
;
313 prof
->prof_id
= dtrace_probe_create(profile_id
,
315 profile_aframes
? profile_aframes
: PROF_ARTIFICIAL_FRAMES
, prof
);
320 profile_provide(void *arg
, const dtrace_probedesc_t
*desc
)
322 #pragma unused(arg) /* __APPLE__ */
323 int i
, j
, rate
, kind
;
324 hrtime_t val
= 0, mult
= 1, len
;
325 const char *name
, *suffix
= NULL
;
327 #if !defined(__APPLE__)
332 { PROF_PREFIX_PROFILE
, PROF_PROFILE
},
333 { PROF_PREFIX_TICK
, PROF_TICK
},
341 { "ns", NANOSEC
/ NANOSEC
},
342 { "nsec", NANOSEC
/ NANOSEC
},
343 { "us", NANOSEC
/ MICROSEC
},
344 { "usec", NANOSEC
/ MICROSEC
},
345 { "ms", NANOSEC
/ MILLISEC
},
346 { "msec", NANOSEC
/ MILLISEC
},
347 { "s", NANOSEC
/ SEC
},
348 { "sec", NANOSEC
/ SEC
},
349 { "m", NANOSEC
* (hrtime_t
)60 },
350 { "min", NANOSEC
* (hrtime_t
)60 },
351 { "h", NANOSEC
* (hrtime_t
)(60 * 60) },
352 { "hour", NANOSEC
* (hrtime_t
)(60 * 60) },
353 { "d", NANOSEC
* (hrtime_t
)(24 * 60 * 60) },
354 { "day", NANOSEC
* (hrtime_t
)(24 * 60 * 60) },
363 { PROF_PREFIX_PROFILE
, PROF_PROFILE
},
364 { PROF_PREFIX_TICK
, PROF_TICK
},
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) },
389 #endif /* __APPLE__ */
393 char n
[PROF_NAMELEN
];
396 * If no description was provided, provide all of our probes.
398 #if !defined(__APPLE__)
399 for (i
= 0; i
< sizeof (profile_rates
) / sizeof (int); i
++) {
401 for (i
= 0; i
< (int)(sizeof (profile_rates
) / sizeof (int)); i
++) {
402 #endif /* __APPLE__ */
403 if ((rate
= profile_rates
[i
]) == 0)
406 (void) snprintf(n
, PROF_NAMELEN
, "%s%d",
407 PROF_PREFIX_PROFILE
, rate
);
408 profile_create(NANOSEC
/ rate
, n
, PROF_PROFILE
);
411 #if !defined(__APPLE__)
412 for (i
= 0; i
< sizeof (profile_ticks
) / sizeof (int); i
++) {
414 for (i
= 0; i
< (int)(sizeof (profile_ticks
) / sizeof (int)); i
++) {
415 #endif /* __APPLE__ */
416 if ((rate
= profile_ticks
[i
]) == 0)
419 (void) snprintf(n
, PROF_NAMELEN
, "%s%d",
420 PROF_PREFIX_TICK
, rate
);
421 profile_create(NANOSEC
/ rate
, n
, PROF_TICK
);
427 name
= desc
->dtpd_name
;
429 for (i
= 0; types
[i
].prefix
!= NULL
; i
++) {
430 len
= strlen(types
[i
].prefix
);
432 if (strncmp(name
, types
[i
].prefix
, len
) != 0)
437 if (types
[i
].prefix
== NULL
)
440 kind
= types
[i
].kind
;
441 j
= strlen(name
) - len
;
444 * We need to start before any time suffix.
446 for (j
= strlen(name
); j
>= len
; j
--) {
447 if (name
[j
] >= '0' && name
[j
] <= '9')
452 ASSERT(suffix
!= NULL
);
455 * Now determine the numerical value present in the probe name.
457 for (; j
>= len
; j
--) {
458 if (name
[j
] < '0' || name
[j
] > '9')
461 val
+= (name
[j
] - '0') * mult
;
462 mult
*= (hrtime_t
)10;
469 * Look-up the suffix to determine the multiplier.
471 for (i
= 0, mult
= 0; suffixes
[i
].name
!= NULL
; i
++) {
472 #if !defined(__APPLE__)
473 if (strcasecmp(suffixes
[i
].name
, suffix
) == 0) {
474 mult
= suffixes
[i
].mult
;
478 if (strncasecmp(suffixes
[i
].name
, suffix
, strlen(suffixes
[i
].name
) + 1) == 0) {
479 mult
= suffixes
[i
].mult
;
482 #endif /* __APPLE__ */
485 if (suffixes
[i
].name
== NULL
&& *suffix
!= '\0')
490 * The default is frequency-per-second.
497 profile_create(val
, name
, kind
);
502 profile_destroy(void *arg
, dtrace_id_t id
, void *parg
)
504 #pragma unused(arg,id) /* __APPLE__ */
505 profile_probe_t
*prof
= parg
;
507 ASSERT(prof
->prof_cyclic
== CYCLIC_NONE
);
508 #if !defined(__APPLE__)
509 kmem_free(prof
, sizeof (profile_probe_t
));
511 if (prof
->prof_kind
== PROF_TICK
)
512 kmem_free(prof
, sizeof (profile_probe_t
));
514 kmem_free(prof
, sizeof (profile_probe_t
) + NCPU
*sizeof(profile_probe_percpu_t
));
515 #endif /* __APPLE__ */
517 ASSERT(profile_total
>= 1);
518 atomic_add_32(&profile_total
, -1);
523 profile_online(void *arg
, dtrace_cpu_t
*cpu
, cyc_handler_t
*hdlr
, cyc_time_t
*when
)
525 #pragma unused(cpu) /* __APPLE__ */
526 profile_probe_t
*prof
= arg
;
527 profile_probe_percpu_t
*pcpu
;
529 #if !defined(__APPLE__)
530 pcpu
= kmem_zalloc(sizeof (profile_probe_percpu_t
), KM_SLEEP
);
532 pcpu
= ((profile_probe_percpu_t
*)(&(prof
[1]))) + cpu_number();
533 #endif /* __APPLE__ */
534 pcpu
->profc_probe
= prof
;
536 hdlr
->cyh_func
= profile_fire
;
537 hdlr
->cyh_arg
= pcpu
;
538 hdlr
->cyh_level
= CY_HIGH_LEVEL
;
540 when
->cyt_interval
= prof
->prof_interval
;
541 #if !defined(__APPLE__)
542 when
->cyt_when
= dtrace_gethrtime() + when
->cyt_interval
;
545 #endif /* __APPLE__ */
547 pcpu
->profc_expected
= when
->cyt_when
;
548 pcpu
->profc_interval
= when
->cyt_interval
;
553 profile_offline(void *arg
, dtrace_cpu_t
*cpu
, void *oarg
)
555 profile_probe_percpu_t
*pcpu
= oarg
;
557 ASSERT(pcpu
->profc_probe
== arg
);
558 #if !defined(__APPLE__)
559 kmem_free(pcpu
, sizeof (profile_probe_percpu_t
));
561 #pragma unused(pcpu,arg,cpu) /* __APPLE__ */
562 #endif /* __APPLE__ */
567 profile_enable(void *arg
, dtrace_id_t id
, void *parg
)
569 #pragma unused(arg,id) /* __APPLE__ */
570 profile_probe_t
*prof
= parg
;
571 cyc_omni_handler_t omni
;
575 ASSERT(prof
->prof_interval
!= 0);
576 ASSERT(MUTEX_HELD(&cpu_lock
));
578 if (prof
->prof_kind
== PROF_TICK
) {
579 hdlr
.cyh_func
= profile_tick
;
581 hdlr
.cyh_level
= CY_HIGH_LEVEL
;
583 when
.cyt_interval
= prof
->prof_interval
;
584 #if !defined(__APPLE__)
585 when
.cyt_when
= dtrace_gethrtime() + when
.cyt_interval
;
588 #endif /* __APPLE__ */
590 ASSERT(prof
->prof_kind
== PROF_PROFILE
);
591 omni
.cyo_online
= profile_online
;
592 omni
.cyo_offline
= profile_offline
;
596 #if !defined(__APPLE__)
597 if (prof
->prof_kind
== PROF_TICK
) {
598 prof
->prof_cyclic
= cyclic_add(&hdlr
, &when
);
600 prof
->prof_cyclic
= cyclic_add_omni(&omni
);
603 if (prof
->prof_kind
== PROF_TICK
) {
604 prof
->prof_cyclic
= cyclic_timer_add(&hdlr
, &when
);
606 prof
->prof_cyclic
= (cyclic_id_t
)cyclic_add_omni(&omni
); /* cast puns cyclic_id_list_t with cyclic_id_t */
608 #endif /* __APPLE__ */
614 profile_disable(void *arg
, dtrace_id_t id
, void *parg
)
616 profile_probe_t
*prof
= parg
;
618 ASSERT(prof
->prof_cyclic
!= CYCLIC_NONE
);
619 ASSERT(MUTEX_HELD(&cpu_lock
));
621 #if !defined(__APPLE__)
622 cyclic_remove(prof
->prof_cyclic
);
624 #pragma unused(arg,id)
625 if (prof
->prof_kind
== PROF_TICK
) {
626 cyclic_timer_remove(prof
->prof_cyclic
);
628 cyclic_remove_omni((cyclic_id_list_t
)prof
->prof_cyclic
); /* cast puns cyclic_id_list_t with cyclic_id_t */
630 #endif /* __APPLE__ */
631 prof
->prof_cyclic
= CYCLIC_NONE
;
634 #if !defined(__APPLE__)
637 profile_usermode(void *arg
, dtrace_id_t id
, void *parg
)
639 return (CPU
->cpu_profile_pc
== 0);
643 profile_usermode(void *arg
, dtrace_id_t id
, void *parg
)
645 #pragma unused(arg,id,parg)
646 return 1; /* XXX_BOGUS */
648 #endif /* __APPLE__ */
650 static dtrace_pattr_t profile_attr
= {
651 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
652 { DTRACE_STABILITY_UNSTABLE
, DTRACE_STABILITY_UNSTABLE
, DTRACE_CLASS_UNKNOWN
},
653 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_UNKNOWN
},
654 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
655 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
658 static dtrace_pops_t profile_pops
= {
672 profile_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
678 return (DDI_SUCCESS
);
680 return (DDI_FAILURE
);
683 #if !defined(__APPLE__)
684 if (ddi_create_minor_node(devi
, "profile", S_IFCHR
, 0,
685 DDI_PSEUDO
, NULL
) == DDI_FAILURE
||
686 dtrace_register("profile", &profile_attr
,
687 DTRACE_PRIV_KERNEL
| DTRACE_PRIV_USER
, NULL
,
688 &profile_pops
, NULL
, &profile_id
) != 0) {
689 ddi_remove_minor_node(devi
, NULL
);
690 return (DDI_FAILURE
);
693 profile_max
= ddi_getprop(DDI_DEV_T_ANY
, devi
, DDI_PROP_DONTPASS
,
694 "profile-max-probes", PROFILE_MAX_DEFAULT
);
696 if (ddi_create_minor_node(devi
, "profile", S_IFCHR
, 0,
697 DDI_PSEUDO
, 0) == DDI_FAILURE
||
698 dtrace_register("profile", &profile_attr
,
699 DTRACE_PRIV_KERNEL
| DTRACE_PRIV_USER
, NULL
,
700 &profile_pops
, NULL
, &profile_id
) != 0) {
701 ddi_remove_minor_node(devi
, NULL
);
702 return (DDI_FAILURE
);
705 profile_max
= PROFILE_MAX_DEFAULT
;
706 #endif /* __APPLE__ */
708 ddi_report_dev(devi
);
710 return (DDI_SUCCESS
);
713 #if !defined(__APPLE__)
715 profile_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
721 return (DDI_SUCCESS
);
723 return (DDI_FAILURE
);
726 if (dtrace_unregister(profile_id
) != 0)
727 return (DDI_FAILURE
);
729 ddi_remove_minor_node(devi
, NULL
);
730 return (DDI_SUCCESS
);
735 profile_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
740 case DDI_INFO_DEVT2DEVINFO
:
741 *result
= (void *)profile_devi
;
744 case DDI_INFO_DEVT2INSTANCE
:
756 profile_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cred_p
)
761 static struct cb_ops profile_cb_ops
= {
762 profile_open
, /* open */
764 nulldev
, /* strategy */
774 ddi_prop_op
, /* cb_prop_op */
776 D_NEW
| D_MP
/* Driver compatibility flag */
779 static struct dev_ops profile_ops
= {
780 DEVO_REV
, /* devo_rev, */
782 profile_info
, /* get_dev_info */
783 nulldev
, /* identify */
785 profile_attach
, /* attach */
786 profile_detach
, /* detach */
788 &profile_cb_ops
, /* driver operations */
789 NULL
, /* bus operations */
790 nodev
/* dev power */
794 * Module linkage information for the kernel.
796 static struct modldrv modldrv
= {
797 &mod_driverops
, /* module type (this is a pseudo driver) */
798 "Profile Interrupt Tracing", /* name of module */
799 &profile_ops
, /* driver ops */
802 static struct modlinkage modlinkage
= {
811 return (mod_install(&modlinkage
));
815 _info(struct modinfo
*modinfop
)
817 return (mod_info(&modlinkage
, modinfop
));
823 return (mod_remove(&modlinkage
));
826 d_open_t _profile_open
;
828 int _profile_open(dev_t dev
, int flags
, int devtype
, struct proc
*p
)
830 #pragma unused(dev,flags,devtype,p)
834 #define PROFILE_MAJOR -24 /* let the kernel pick the device number */
837 * A struct describing which functions will get invoked for certain
840 static struct cdevsw profile_cdevsw
=
842 _profile_open
, /* open */
843 eno_opcl
, /* close */
844 eno_rdwrt
, /* read */
845 eno_rdwrt
, /* write */
846 eno_ioctl
, /* ioctl */
847 (stop_fcn_t
*)nulldev
, /* stop */
848 (reset_fcn_t
*)nulldev
, /* reset */
850 eno_select
, /* select */
852 eno_strat
, /* strategy */
858 static int gProfileInited
= 0;
860 void profile_init( void )
862 if (0 == gProfileInited
)
864 int majdevno
= cdevsw_add(PROFILE_MAJOR
, &profile_cdevsw
);
867 printf("profile_init: failed to allocate a major number!\n");
872 profile_attach( (dev_info_t
*)(uintptr_t)majdevno
, DDI_ATTACH
);
876 panic("profile_init: called twice!\n");
879 #endif /* __APPLE__ */