]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/dev/dtrace/profile_prvd.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / bsd / dev / dtrace / profile_prvd.c
... / ...
CommitLineData
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/*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/* #pragma ident "@(#)profile.c 1.7 07/01/10 SMI" */
27
28#if !defined(__APPLE__)
29#include <sys/errno.h>
30#include <sys/stat.h>
31#include <sys/modctl.h>
32#include <sys/conf.h>
33#include <sys/systm.h>
34#include <sys/ddi.h>
35#include <sys/sunddi.h>
36#include <sys/cpuvar.h>
37#include <sys/kmem.h>
38#include <sys/strsubr.h>
39#include <sys/dtrace.h>
40#include <sys/cyclic.h>
41#include <sys/atomic.h>
42#else
43#ifdef KERNEL
44#ifndef _KERNEL
45#define _KERNEL /* Solaris vs. Darwin */
46#endif
47#endif
48
49#include <kern/cpu_data.h>
50#include <kern/thread.h>
51#include <kern/assert.h>
52#include <mach/thread_status.h>
53
54#include <sys/param.h>
55#include <sys/systm.h>
56#include <sys/errno.h>
57#include <sys/stat.h>
58#include <sys/ioctl.h>
59#include <sys/conf.h>
60#include <sys/fcntl.h>
61#include <miscfs/devfs/devfs.h>
62
63#include <sys/dtrace.h>
64#include <sys/dtrace_impl.h>
65
66#include <sys/dtrace_glue.h>
67
68#include <machine/pal_routines.h>
69
70#if defined(__x86_64__)
71extern x86_saved_state_t *find_kern_regs(thread_t);
72#else
73#error Unknown architecture
74#endif
75
76#undef ASSERT
77#define ASSERT(x) do {} while(0)
78
79extern void profile_init(void);
80#endif /* __APPLE__ */
81
82static dev_info_t *profile_devi;
83static dtrace_provider_id_t profile_id;
84
85/*
86 * Regardless of platform, the stack frames look like this in the case of the
87 * profile provider:
88 *
89 * profile_fire
90 * cyclic_expire
91 * cyclic_fire
92 * [ cbe ]
93 * [ interrupt code ]
94 *
95 * On x86, there are five frames from the generic interrupt code; further, the
96 * interrupted instruction appears as its own stack frame, giving us a total of
97 * 10.
98 *
99 * On SPARC, the picture is further complicated because the compiler
100 * optimizes away tail-calls -- so the following frames are optimized away:
101 *
102 * profile_fire
103 * cyclic_expire
104 *
105 * This gives three frames. However, on DEBUG kernels, the cyclic_expire
106 * frame cannot be tail-call eliminated, yielding four frames in this case.
107 *
108 * All of the above constraints lead to the mess below. Yes, the profile
109 * provider should ideally figure this out on-the-fly by hitting one of its own
110 * probes and then walking its own stack trace. This is complicated, however,
111 * and the static definition doesn't seem to be overly brittle. Still, we
112 * allow for a manual override in case we get it completely wrong.
113 */
114
115#if defined(__x86_64__)
116#define PROF_ARTIFICIAL_FRAMES 9
117#else
118#error Unknown architecture
119#endif
120
121#define PROF_NAMELEN 15
122
123#define PROF_PROFILE 0
124#define PROF_TICK 1
125#define PROF_PREFIX_PROFILE "profile-"
126#define PROF_PREFIX_TICK "tick-"
127
128typedef struct profile_probe {
129 char prof_name[PROF_NAMELEN];
130 dtrace_id_t prof_id;
131 int prof_kind;
132 hrtime_t prof_interval;
133 cyclic_id_t prof_cyclic;
134} profile_probe_t;
135
136typedef struct profile_probe_percpu {
137 hrtime_t profc_expected;
138 hrtime_t profc_interval;
139 profile_probe_t *profc_probe;
140} profile_probe_percpu_t;
141
142hrtime_t profile_interval_min = NANOSEC / 5000; /* 5000 hz */
143int profile_aframes = 0; /* override */
144
145static int profile_rates[] = {
146 97, 199, 499, 997, 1999,
147 4001, 4999, 0, 0, 0,
148 0, 0, 0, 0, 0,
149 0, 0, 0, 0, 0
150};
151
152static int profile_ticks[] = {
153 1, 10, 100, 500, 1000,
154 5000, 0, 0, 0, 0,
155 0, 0, 0, 0, 0
156};
157
158/*
159 * profile_max defines the upper bound on the number of profile probes that
160 * can exist (this is to prevent malicious or clumsy users from exhausing
161 * system resources by creating a slew of profile probes). At mod load time,
162 * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
163 * present in the profile.conf file.
164 */
165#define PROFILE_MAX_DEFAULT 1000 /* default max. number of probes */
166static uint32_t profile_max; /* maximum number of profile probes */
167static uint32_t profile_total; /* current number of profile probes */
168
169static void
170profile_fire(void *arg)
171{
172 profile_probe_percpu_t *pcpu = arg;
173 profile_probe_t *prof = pcpu->profc_probe;
174 hrtime_t late;
175
176 late = dtrace_gethrtime() - pcpu->profc_expected;
177 pcpu->profc_expected += pcpu->profc_interval;
178
179#if !defined(__APPLE__)
180 dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
181 CPU->cpu_profile_upc, late, 0, 0);
182#else
183#if defined(__x86_64__)
184 x86_saved_state_t *kern_regs = find_kern_regs(current_thread());
185
186 if (NULL != kern_regs) {
187 /* Kernel was interrupted. */
188 dtrace_probe(prof->prof_id, saved_state64(kern_regs)->isf.rip, 0x0, 0, 0, 0);
189
190 } else {
191 pal_register_cache_state(current_thread(), VALID);
192 /* Possibly a user interrupt */
193 x86_saved_state_t *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
194
195 if (NULL == tagged_regs) {
196 /* Too bad, so sad, no useful interrupt state. */
197 dtrace_probe(prof->prof_id, 0xcafebabe,
198 0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
199 } else if (is_saved_state64(tagged_regs)) {
200 x86_saved_state64_t *regs = saved_state64(tagged_regs);
201
202 dtrace_probe(prof->prof_id, 0x0, regs->isf.rip, 0, 0, 0);
203 } else {
204 x86_saved_state32_t *regs = saved_state32(tagged_regs);
205
206 dtrace_probe(prof->prof_id, 0x0, regs->eip, 0, 0, 0);
207 }
208 }
209#else
210#error Unknown architecture
211#endif
212#endif /* __APPLE__ */
213}
214
215static void
216profile_tick(void *arg)
217{
218 profile_probe_t *prof = arg;
219
220#if !defined(__APPLE__)
221 dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
222 CPU->cpu_profile_upc, 0, 0, 0);
223#else
224#if defined(__x86_64__)
225 x86_saved_state_t *kern_regs = find_kern_regs(current_thread());
226
227 if (NULL != kern_regs) {
228 /* Kernel was interrupted. */
229 dtrace_probe(prof->prof_id, saved_state64(kern_regs)->isf.rip, 0x0, 0, 0, 0);
230 } else {
231 pal_register_cache_state(current_thread(), VALID);
232 /* Possibly a user interrupt */
233 x86_saved_state_t *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
234
235 if (NULL == tagged_regs) {
236 /* Too bad, so sad, no useful interrupt state. */
237 dtrace_probe(prof->prof_id, 0xcafebabe,
238 0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
239 } else if (is_saved_state64(tagged_regs)) {
240 x86_saved_state64_t *regs = saved_state64(tagged_regs);
241
242 dtrace_probe(prof->prof_id, 0x0, regs->isf.rip, 0, 0, 0);
243 } else {
244 x86_saved_state32_t *regs = saved_state32(tagged_regs);
245
246 dtrace_probe(prof->prof_id, 0x0, regs->eip, 0, 0, 0);
247 }
248 }
249#else
250#error Unknown architecture
251#endif
252#endif /* __APPLE__ */
253}
254
255static void
256profile_create(hrtime_t interval, const char *name, int kind)
257{
258 profile_probe_t *prof;
259
260 if (interval < profile_interval_min)
261 return;
262
263 if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
264 return;
265
266 atomic_add_32(&profile_total, 1);
267 if (profile_total > profile_max) {
268 atomic_add_32(&profile_total, -1);
269 return;
270 }
271
272#if !defined(__APPLE__)
273 prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
274#else
275 if (PROF_TICK == kind)
276 prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
277 else
278 prof = kmem_zalloc(sizeof (profile_probe_t) + NCPU*sizeof(profile_probe_percpu_t), KM_SLEEP);
279#endif /* __APPLE__ */
280 (void) strlcpy(prof->prof_name, name, sizeof(prof->prof_name));
281 prof->prof_interval = interval;
282 prof->prof_cyclic = CYCLIC_NONE;
283 prof->prof_kind = kind;
284 prof->prof_id = dtrace_probe_create(profile_id,
285 NULL, NULL, name,
286 profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
287}
288
289/*ARGSUSED*/
290static void
291profile_provide(void *arg, const dtrace_probedesc_t *desc)
292{
293#pragma unused(arg) /* __APPLE__ */
294 int i, j, rate, kind;
295 hrtime_t val = 0, mult = 1, len;
296 const char *name, *suffix = NULL;
297
298#if !defined(__APPLE__)
299 const struct {
300 char *prefix;
301 int kind;
302 } types[] = {
303 { PROF_PREFIX_PROFILE, PROF_PROFILE },
304 { PROF_PREFIX_TICK, PROF_TICK },
305 { NULL, NULL }
306 };
307
308 const struct {
309 char *name;
310 hrtime_t mult;
311 } suffixes[] = {
312 { "ns", NANOSEC / NANOSEC },
313 { "nsec", NANOSEC / NANOSEC },
314 { "us", NANOSEC / MICROSEC },
315 { "usec", NANOSEC / MICROSEC },
316 { "ms", NANOSEC / MILLISEC },
317 { "msec", NANOSEC / MILLISEC },
318 { "s", NANOSEC / SEC },
319 { "sec", NANOSEC / SEC },
320 { "m", NANOSEC * (hrtime_t)60 },
321 { "min", NANOSEC * (hrtime_t)60 },
322 { "h", NANOSEC * (hrtime_t)(60 * 60) },
323 { "hour", NANOSEC * (hrtime_t)(60 * 60) },
324 { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) },
325 { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) },
326 { "hz", 0 },
327 { NULL }
328 };
329#else
330 const struct {
331 const char *prefix;
332 int kind;
333 } types[] = {
334 { PROF_PREFIX_PROFILE, PROF_PROFILE },
335 { PROF_PREFIX_TICK, PROF_TICK },
336 { NULL, 0 }
337 };
338
339 const struct {
340 const char *name;
341 hrtime_t mult;
342 } suffixes[] = {
343 { "ns", NANOSEC / NANOSEC },
344 { "nsec", NANOSEC / NANOSEC },
345 { "us", NANOSEC / MICROSEC },
346 { "usec", NANOSEC / MICROSEC },
347 { "ms", NANOSEC / MILLISEC },
348 { "msec", NANOSEC / MILLISEC },
349 { "s", NANOSEC / SEC },
350 { "sec", NANOSEC / SEC },
351 { "m", NANOSEC * (hrtime_t)60 },
352 { "min", NANOSEC * (hrtime_t)60 },
353 { "h", NANOSEC * (hrtime_t)(60 * 60) },
354 { "hour", NANOSEC * (hrtime_t)(60 * 60) },
355 { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) },
356 { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) },
357 { "hz", 0 },
358 { NULL, 0 }
359 };
360#endif /* __APPLE__ */
361
362
363 if (desc == NULL) {
364 char n[PROF_NAMELEN];
365
366 /*
367 * If no description was provided, provide all of our probes.
368 */
369#if !defined(__APPLE__)
370 for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
371#else
372 for (i = 0; i < (int)(sizeof (profile_rates) / sizeof (int)); i++) {
373#endif /* __APPLE__ */
374 if ((rate = profile_rates[i]) == 0)
375 continue;
376
377 (void) snprintf(n, PROF_NAMELEN, "%s%d",
378 PROF_PREFIX_PROFILE, rate);
379 profile_create(NANOSEC / rate, n, PROF_PROFILE);
380 }
381
382#if !defined(__APPLE__)
383 for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
384#else
385 for (i = 0; i < (int)(sizeof (profile_ticks) / sizeof (int)); i++) {
386#endif /* __APPLE__ */
387 if ((rate = profile_ticks[i]) == 0)
388 continue;
389
390 (void) snprintf(n, PROF_NAMELEN, "%s%d",
391 PROF_PREFIX_TICK, rate);
392 profile_create(NANOSEC / rate, n, PROF_TICK);
393 }
394
395 return;
396 }
397
398 name = desc->dtpd_name;
399
400 for (i = 0; types[i].prefix != NULL; i++) {
401 len = strlen(types[i].prefix);
402
403 if (strncmp(name, types[i].prefix, len) != 0)
404 continue;
405 break;
406 }
407
408 if (types[i].prefix == NULL)
409 return;
410
411 kind = types[i].kind;
412 j = strlen(name) - len;
413
414 /*
415 * We need to start before any time suffix.
416 */
417 for (j = strlen(name); j >= len; j--) {
418 if (name[j] >= '0' && name[j] <= '9')
419 break;
420 suffix = &name[j];
421 }
422
423 ASSERT(suffix != NULL);
424
425 /*
426 * Now determine the numerical value present in the probe name.
427 */
428 for (; j >= len; j--) {
429 if (name[j] < '0' || name[j] > '9')
430 return;
431
432 val += (name[j] - '0') * mult;
433 mult *= (hrtime_t)10;
434 }
435
436 if (val == 0)
437 return;
438
439 /*
440 * Look-up the suffix to determine the multiplier.
441 */
442 for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
443#if !defined(__APPLE__)
444 if (strcasecmp(suffixes[i].name, suffix) == 0) {
445 mult = suffixes[i].mult;
446 break;
447 }
448#else
449 if (strncasecmp(suffixes[i].name, suffix, strlen(suffixes[i].name) + 1) == 0) {
450 mult = suffixes[i].mult;
451 break;
452 }
453#endif /* __APPLE__ */
454 }
455
456 if (suffixes[i].name == NULL && *suffix != '\0')
457 return;
458
459 if (mult == 0) {
460 /*
461 * The default is frequency-per-second.
462 */
463 val = NANOSEC / val;
464 } else {
465 val *= mult;
466 }
467
468 profile_create(val, name, kind);
469}
470
471/*ARGSUSED*/
472static void
473profile_destroy(void *arg, dtrace_id_t id, void *parg)
474{
475#pragma unused(arg,id) /* __APPLE__ */
476 profile_probe_t *prof = parg;
477
478 ASSERT(prof->prof_cyclic == CYCLIC_NONE);
479#if !defined(__APPLE__)
480 kmem_free(prof, sizeof (profile_probe_t));
481#else
482 if (prof->prof_kind == PROF_TICK)
483 kmem_free(prof, sizeof (profile_probe_t));
484 else
485 kmem_free(prof, sizeof (profile_probe_t) + NCPU*sizeof(profile_probe_percpu_t));
486#endif /* __APPLE__ */
487
488 ASSERT(profile_total >= 1);
489 atomic_add_32(&profile_total, -1);
490}
491
492/*ARGSUSED*/
493static void
494profile_online(void *arg, dtrace_cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
495{
496#pragma unused(cpu) /* __APPLE__ */
497 profile_probe_t *prof = arg;
498 profile_probe_percpu_t *pcpu;
499
500#if !defined(__APPLE__)
501 pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
502#else
503 pcpu = ((profile_probe_percpu_t *)(&(prof[1]))) + cpu_number();
504#endif /* __APPLE__ */
505 pcpu->profc_probe = prof;
506
507 hdlr->cyh_func = profile_fire;
508 hdlr->cyh_arg = pcpu;
509 hdlr->cyh_level = CY_HIGH_LEVEL;
510
511 when->cyt_interval = prof->prof_interval;
512#if !defined(__APPLE__)
513 when->cyt_when = dtrace_gethrtime() + when->cyt_interval;
514#else
515 when->cyt_when = 0;
516#endif /* __APPLE__ */
517
518 pcpu->profc_expected = when->cyt_when;
519 pcpu->profc_interval = when->cyt_interval;
520}
521
522/*ARGSUSED*/
523static void
524profile_offline(void *arg, dtrace_cpu_t *cpu, void *oarg)
525{
526 profile_probe_percpu_t *pcpu = oarg;
527
528 ASSERT(pcpu->profc_probe == arg);
529#if !defined(__APPLE__)
530 kmem_free(pcpu, sizeof (profile_probe_percpu_t));
531#else
532#pragma unused(pcpu,arg,cpu) /* __APPLE__ */
533#endif /* __APPLE__ */
534}
535
536/*ARGSUSED*/
537static int
538profile_enable(void *arg, dtrace_id_t id, void *parg)
539{
540#pragma unused(arg,id) /* __APPLE__ */
541 profile_probe_t *prof = parg;
542 cyc_omni_handler_t omni;
543 cyc_handler_t hdlr;
544 cyc_time_t when;
545
546 ASSERT(prof->prof_interval != 0);
547 ASSERT(MUTEX_HELD(&cpu_lock));
548
549 if (prof->prof_kind == PROF_TICK) {
550 hdlr.cyh_func = profile_tick;
551 hdlr.cyh_arg = prof;
552 hdlr.cyh_level = CY_HIGH_LEVEL;
553
554 when.cyt_interval = prof->prof_interval;
555#if !defined(__APPLE__)
556 when.cyt_when = dtrace_gethrtime() + when.cyt_interval;
557#else
558 when.cyt_when = 0;
559#endif /* __APPLE__ */
560 } else {
561 ASSERT(prof->prof_kind == PROF_PROFILE);
562 omni.cyo_online = profile_online;
563 omni.cyo_offline = profile_offline;
564 omni.cyo_arg = prof;
565 }
566
567#if !defined(__APPLE__)
568 if (prof->prof_kind == PROF_TICK) {
569 prof->prof_cyclic = cyclic_add(&hdlr, &when);
570 } else {
571 prof->prof_cyclic = cyclic_add_omni(&omni);
572 }
573#else
574 if (prof->prof_kind == PROF_TICK) {
575 prof->prof_cyclic = cyclic_timer_add(&hdlr, &when);
576 } else {
577 prof->prof_cyclic = (cyclic_id_t)cyclic_add_omni(&omni); /* cast puns cyclic_id_list_t with cyclic_id_t */
578 }
579#endif /* __APPLE__ */
580 return(0);
581}
582
583/*ARGSUSED*/
584static void
585profile_disable(void *arg, dtrace_id_t id, void *parg)
586{
587 profile_probe_t *prof = parg;
588
589 ASSERT(prof->prof_cyclic != CYCLIC_NONE);
590 ASSERT(MUTEX_HELD(&cpu_lock));
591
592#if !defined(__APPLE__)
593 cyclic_remove(prof->prof_cyclic);
594#else
595#pragma unused(arg,id)
596 if (prof->prof_kind == PROF_TICK) {
597 cyclic_timer_remove(prof->prof_cyclic);
598 } else {
599 cyclic_remove_omni((cyclic_id_list_t)prof->prof_cyclic); /* cast puns cyclic_id_list_t with cyclic_id_t */
600 }
601#endif /* __APPLE__ */
602 prof->prof_cyclic = CYCLIC_NONE;
603}
604
605#if !defined(__APPLE__)
606/*ARGSUSED*/
607static int
608profile_usermode(void *arg, dtrace_id_t id, void *parg)
609{
610 return (CPU->cpu_profile_pc == 0);
611}
612#else
613static int
614profile_usermode(void *arg, dtrace_id_t id, void *parg)
615{
616#pragma unused(arg,id,parg)
617 return 1; /* XXX_BOGUS */
618}
619#endif /* __APPLE__ */
620
621static dtrace_pattr_t profile_attr = {
622{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
623{ DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_UNKNOWN },
624{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
625{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
626{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
627};
628
629static dtrace_pops_t profile_pops = {
630 profile_provide,
631 NULL,
632 profile_enable,
633 profile_disable,
634 NULL,
635 NULL,
636 NULL,
637 NULL,
638 profile_usermode,
639 profile_destroy
640};
641
642static int
643profile_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
644{
645 switch (cmd) {
646 case DDI_ATTACH:
647 break;
648 case DDI_RESUME:
649 return (DDI_SUCCESS);
650 default:
651 return (DDI_FAILURE);
652 }
653
654#if !defined(__APPLE__)
655 if (ddi_create_minor_node(devi, "profile", S_IFCHR, 0,
656 DDI_PSEUDO, NULL) == DDI_FAILURE ||
657 dtrace_register("profile", &profile_attr,
658 DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER, NULL,
659 &profile_pops, NULL, &profile_id) != 0) {
660 ddi_remove_minor_node(devi, NULL);
661 return (DDI_FAILURE);
662 }
663
664 profile_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
665 "profile-max-probes", PROFILE_MAX_DEFAULT);
666#else
667 if (ddi_create_minor_node(devi, "profile", S_IFCHR, 0,
668 DDI_PSEUDO, 0) == DDI_FAILURE ||
669 dtrace_register("profile", &profile_attr,
670 DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER, NULL,
671 &profile_pops, NULL, &profile_id) != 0) {
672 ddi_remove_minor_node(devi, NULL);
673 return (DDI_FAILURE);
674 }
675
676 profile_max = PROFILE_MAX_DEFAULT;
677#endif /* __APPLE__ */
678
679 ddi_report_dev(devi);
680 profile_devi = devi;
681 return (DDI_SUCCESS);
682}
683
684#if !defined(__APPLE__)
685static int
686profile_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
687{
688 switch (cmd) {
689 case DDI_DETACH:
690 break;
691 case DDI_SUSPEND:
692 return (DDI_SUCCESS);
693 default:
694 return (DDI_FAILURE);
695 }
696
697 if (dtrace_unregister(profile_id) != 0)
698 return (DDI_FAILURE);
699
700 ddi_remove_minor_node(devi, NULL);
701 return (DDI_SUCCESS);
702}
703
704/*ARGSUSED*/
705static int
706profile_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
707{
708 int error;
709
710 switch (infocmd) {
711 case DDI_INFO_DEVT2DEVINFO:
712 *result = (void *)profile_devi;
713 error = DDI_SUCCESS;
714 break;
715 case DDI_INFO_DEVT2INSTANCE:
716 *result = (void *)0;
717 error = DDI_SUCCESS;
718 break;
719 default:
720 error = DDI_FAILURE;
721 }
722 return (error);
723}
724
725/*ARGSUSED*/
726static int
727profile_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
728{
729 return (0);
730}
731
732static struct cb_ops profile_cb_ops = {
733 profile_open, /* open */
734 nodev, /* close */
735 nulldev, /* strategy */
736 nulldev, /* print */
737 nodev, /* dump */
738 nodev, /* read */
739 nodev, /* write */
740 nodev, /* ioctl */
741 nodev, /* devmap */
742 nodev, /* mmap */
743 nodev, /* segmap */
744 nochpoll, /* poll */
745 ddi_prop_op, /* cb_prop_op */
746 0, /* streamtab */
747 D_NEW | D_MP /* Driver compatibility flag */
748};
749
750static struct dev_ops profile_ops = {
751 DEVO_REV, /* devo_rev, */
752 0, /* refcnt */
753 profile_info, /* get_dev_info */
754 nulldev, /* identify */
755 nulldev, /* probe */
756 profile_attach, /* attach */
757 profile_detach, /* detach */
758 nodev, /* reset */
759 &profile_cb_ops, /* driver operations */
760 NULL, /* bus operations */
761 nodev /* dev power */
762};
763
764/*
765 * Module linkage information for the kernel.
766 */
767static struct modldrv modldrv = {
768 &mod_driverops, /* module type (this is a pseudo driver) */
769 "Profile Interrupt Tracing", /* name of module */
770 &profile_ops, /* driver ops */
771};
772
773static struct modlinkage modlinkage = {
774 MODREV_1,
775 (void *)&modldrv,
776 NULL
777};
778
779int
780_init(void)
781{
782 return (mod_install(&modlinkage));
783}
784
785int
786_info(struct modinfo *modinfop)
787{
788 return (mod_info(&modlinkage, modinfop));
789}
790
791int
792_fini(void)
793{
794 return (mod_remove(&modlinkage));
795}
796#else
797d_open_t _profile_open;
798
799int _profile_open(dev_t dev, int flags, int devtype, struct proc *p)
800{
801#pragma unused(dev,flags,devtype,p)
802 return 0;
803}
804
805#define PROFILE_MAJOR -24 /* let the kernel pick the device number */
806
807/*
808 * A struct describing which functions will get invoked for certain
809 * actions.
810 */
811static struct cdevsw profile_cdevsw =
812{
813 _profile_open, /* open */
814 eno_opcl, /* close */
815 eno_rdwrt, /* read */
816 eno_rdwrt, /* write */
817 eno_ioctl, /* ioctl */
818 (stop_fcn_t *)nulldev, /* stop */
819 (reset_fcn_t *)nulldev, /* reset */
820 NULL, /* tty's */
821 eno_select, /* select */
822 eno_mmap, /* mmap */
823 eno_strat, /* strategy */
824 eno_getc, /* getc */
825 eno_putc, /* putc */
826 0 /* type */
827};
828
829static int gProfileInited = 0;
830
831void profile_init( void )
832{
833 if (0 == gProfileInited)
834 {
835 int majdevno = cdevsw_add(PROFILE_MAJOR, &profile_cdevsw);
836
837 if (majdevno < 0) {
838 printf("profile_init: failed to allocate a major number!\n");
839 gProfileInited = 0;
840 return;
841 }
842
843 profile_attach( (dev_info_t *)(uintptr_t)majdevno, DDI_ATTACH );
844
845 gProfileInited = 1;
846 } else
847 panic("profile_init: called twice!\n");
848}
849#undef PROFILE_MAJOR
850#endif /* __APPLE__ */