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