]> git.saurik.com Git - apple/xnu.git/blob - bsd/dev/dtrace/profile_prvd.c
14895f8d983e8fd09e8f0263148be40930cfaf0f
[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 struct savearea *sv = find_kern_regs(current_thread());
210
211 if (sv) {
212 if (USERMODE(sv->save_srr1)) {
213 dtrace_probe(prof->prof_id, 0x0, sv->save_srr0, late, 0, 0);
214 } else {
215 dtrace_probe(prof->prof_id, sv->save_srr0, 0x0, late, 0, 0);
216 }
217 } else {
218 dtrace_probe(prof->prof_id, 0xcafebabe,
219 0x0, late, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
220 }
221 #elif defined(__i386__) || defined(__x86_64__)
222 x86_saved_state32_t *kern_regs = find_kern_regs(current_thread());
223
224 if (NULL != kern_regs) {
225 /* Kernel was interrupted. */
226 dtrace_probe(prof->prof_id, kern_regs->eip, 0x0, 0, 0, 0);
227 } else {
228 /* Possibly a user interrupt */
229 x86_saved_state_t *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
230
231 if (NULL == tagged_regs) {
232 /* Too bad, so sad, no useful interrupt state. */
233 dtrace_probe(prof->prof_id, 0xcafebabe,
234 0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
235 } else if (is_saved_state64(tagged_regs)) {
236 x86_saved_state64_t *regs = saved_state64(tagged_regs);
237
238 dtrace_probe(prof->prof_id, 0x0, regs->isf.rip, 0, 0, 0);
239 } else {
240 x86_saved_state32_t *regs = saved_state32(tagged_regs);
241
242 dtrace_probe(prof->prof_id, 0x0, regs->eip, 0, 0, 0);
243 }
244 }
245 #else
246 #error Unknown architecture
247 #endif
248 #endif /* __APPLE__ */
249 }
250
251 static void
252 profile_tick(void *arg)
253 {
254 profile_probe_t *prof = arg;
255
256 #if !defined(__APPLE__)
257 dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
258 CPU->cpu_profile_upc, 0, 0, 0);
259 #else
260 #if defined(__ppc__) || defined(__ppc64__)
261 struct savearea *sv = find_kern_regs(current_thread());
262
263 if (sv) {
264 if (USERMODE(sv->save_srr1)) {
265 dtrace_probe(prof->prof_id, 0x0, sv->save_srr0, 0, 0, 0);
266 } else {
267 dtrace_probe(prof->prof_id, sv->save_srr0, 0x0, 0, 0, 0);
268 }
269 } else {
270 dtrace_probe(prof->prof_id, 0xcafebabe,
271 0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
272 }
273 #elif defined(__i386__) || defined(__x86_64__)
274 x86_saved_state32_t *kern_regs = find_kern_regs(current_thread());
275
276 if (NULL != kern_regs) {
277 /* Kernel was interrupted. */
278 dtrace_probe(prof->prof_id, kern_regs->eip, 0x0, 0, 0, 0);
279 } else {
280 /* Possibly a user interrupt */
281 x86_saved_state_t *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
282
283 if (NULL == tagged_regs) {
284 /* Too bad, so sad, no useful interrupt state. */
285 dtrace_probe(prof->prof_id, 0xcafebabe,
286 0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
287 } else if (is_saved_state64(tagged_regs)) {
288 x86_saved_state64_t *regs = saved_state64(tagged_regs);
289
290 dtrace_probe(prof->prof_id, 0x0, regs->isf.rip, 0, 0, 0);
291 } else {
292 x86_saved_state32_t *regs = saved_state32(tagged_regs);
293
294 dtrace_probe(prof->prof_id, 0x0, regs->eip, 0, 0, 0);
295 }
296 }
297 #else
298 #error Unknown architecture
299 #endif
300 #endif /* __APPLE__ */
301 }
302
303 static void
304 profile_create(hrtime_t interval, const char *name, int kind)
305 {
306 profile_probe_t *prof;
307
308 if (interval < profile_interval_min)
309 return;
310
311 if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
312 return;
313
314 atomic_add_32(&profile_total, 1);
315 if (profile_total > profile_max) {
316 atomic_add_32(&profile_total, -1);
317 return;
318 }
319
320 #if !defined(__APPLE__)
321 prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
322 #else
323 if (PROF_TICK == kind)
324 prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
325 else
326 prof = kmem_zalloc(sizeof (profile_probe_t) + NCPU*sizeof(profile_probe_percpu_t), KM_SLEEP);
327 #endif /* __APPLE__ */
328 (void) strlcpy(prof->prof_name, name, sizeof(prof->prof_name));
329 prof->prof_interval = interval;
330 prof->prof_cyclic = CYCLIC_NONE;
331 prof->prof_kind = kind;
332 prof->prof_id = dtrace_probe_create(profile_id,
333 NULL, NULL, name,
334 profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
335 }
336
337 /*ARGSUSED*/
338 static void
339 profile_provide(void *arg, const dtrace_probedesc_t *desc)
340 {
341 int i, j, rate, kind;
342 hrtime_t val = 0, mult = 1, len;
343 const char *name, *suffix = NULL;
344
345 const struct {
346 char *prefix;
347 int kind;
348 } types[] = {
349 { PROF_PREFIX_PROFILE, PROF_PROFILE },
350 { PROF_PREFIX_TICK, PROF_TICK },
351 { NULL, NULL }
352 };
353
354 const struct {
355 char *name;
356 hrtime_t mult;
357 } suffixes[] = {
358 { "ns", NANOSEC / NANOSEC },
359 { "nsec", NANOSEC / NANOSEC },
360 { "us", NANOSEC / MICROSEC },
361 { "usec", NANOSEC / MICROSEC },
362 { "ms", NANOSEC / MILLISEC },
363 { "msec", NANOSEC / MILLISEC },
364 { "s", NANOSEC / SEC },
365 { "sec", NANOSEC / SEC },
366 { "m", NANOSEC * (hrtime_t)60 },
367 { "min", NANOSEC * (hrtime_t)60 },
368 { "h", NANOSEC * (hrtime_t)(60 * 60) },
369 { "hour", NANOSEC * (hrtime_t)(60 * 60) },
370 { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) },
371 { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) },
372 { "hz", 0 },
373 #if !defined(__APPLE__)
374 { NULL }
375 #else
376 { NULL, 0 }
377 #endif /* __APPLE__ */
378 };
379
380 if (desc == NULL) {
381 char n[PROF_NAMELEN];
382
383 /*
384 * If no description was provided, provide all of our probes.
385 */
386 for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
387 if ((rate = profile_rates[i]) == 0)
388 continue;
389
390 (void) snprintf(n, PROF_NAMELEN, "%s%d",
391 PROF_PREFIX_PROFILE, rate);
392 profile_create(NANOSEC / rate, n, PROF_PROFILE);
393 }
394
395 for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
396 if ((rate = profile_ticks[i]) == 0)
397 continue;
398
399 (void) snprintf(n, PROF_NAMELEN, "%s%d",
400 PROF_PREFIX_TICK, rate);
401 profile_create(NANOSEC / rate, n, PROF_TICK);
402 }
403
404 return;
405 }
406
407 name = desc->dtpd_name;
408
409 for (i = 0; types[i].prefix != NULL; i++) {
410 len = strlen(types[i].prefix);
411
412 if (strncmp(name, types[i].prefix, len) != 0)
413 continue;
414 break;
415 }
416
417 if (types[i].prefix == NULL)
418 return;
419
420 kind = types[i].kind;
421 j = strlen(name) - len;
422
423 /*
424 * We need to start before any time suffix.
425 */
426 for (j = strlen(name); j >= len; j--) {
427 if (name[j] >= '0' && name[j] <= '9')
428 break;
429 suffix = &name[j];
430 }
431
432 ASSERT(suffix != NULL);
433
434 /*
435 * Now determine the numerical value present in the probe name.
436 */
437 for (; j >= len; j--) {
438 if (name[j] < '0' || name[j] > '9')
439 return;
440
441 val += (name[j] - '0') * mult;
442 mult *= (hrtime_t)10;
443 }
444
445 if (val == 0)
446 return;
447
448 /*
449 * Look-up the suffix to determine the multiplier.
450 */
451 for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
452 if (strcasecmp(suffixes[i].name, suffix) == 0) {
453 mult = suffixes[i].mult;
454 break;
455 }
456 }
457
458 if (suffixes[i].name == NULL && *suffix != '\0')
459 return;
460
461 if (mult == 0) {
462 /*
463 * The default is frequency-per-second.
464 */
465 val = NANOSEC / val;
466 } else {
467 val *= mult;
468 }
469
470 profile_create(val, name, kind);
471 }
472
473 /*ARGSUSED*/
474 static void
475 profile_destroy(void *arg, dtrace_id_t id, void *parg)
476 {
477 profile_probe_t *prof = parg;
478
479 ASSERT(prof->prof_cyclic == CYCLIC_NONE);
480 #if !defined(__APPLE__)
481 kmem_free(prof, sizeof (profile_probe_t));
482 #else
483 if (prof->prof_kind == PROF_TICK)
484 kmem_free(prof, sizeof (profile_probe_t));
485 else
486 kmem_free(prof, sizeof (profile_probe_t) + NCPU*sizeof(profile_probe_percpu_t));
487 #endif /* __APPLE__ */
488
489 ASSERT(profile_total >= 1);
490 atomic_add_32(&profile_total, -1);
491 }
492
493 /*ARGSUSED*/
494 static void
495 profile_online(void *arg, cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
496 {
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*/
523 static void
524 profile_offline(void *arg, 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 #endif /* __APPLE__ */
532 }
533
534 /*ARGSUSED*/
535 static void
536 profile_enable(void *arg, dtrace_id_t id, void *parg)
537 {
538 profile_probe_t *prof = parg;
539 cyc_omni_handler_t omni;
540 cyc_handler_t hdlr;
541 cyc_time_t when;
542
543 ASSERT(prof->prof_interval != 0);
544 ASSERT(MUTEX_HELD(&cpu_lock));
545
546 if (prof->prof_kind == PROF_TICK) {
547 hdlr.cyh_func = profile_tick;
548 hdlr.cyh_arg = prof;
549 hdlr.cyh_level = CY_HIGH_LEVEL;
550
551 when.cyt_interval = prof->prof_interval;
552 #if !defined(__APPLE__)
553 when.cyt_when = dtrace_gethrtime() + when.cyt_interval;
554 #else
555 when.cyt_when = 0;
556 #endif /* __APPLE__ */
557 } else {
558 ASSERT(prof->prof_kind == PROF_PROFILE);
559 omni.cyo_online = profile_online;
560 omni.cyo_offline = profile_offline;
561 omni.cyo_arg = prof;
562 }
563
564 #if !defined(__APPLE__)
565 if (prof->prof_kind == PROF_TICK) {
566 prof->prof_cyclic = cyclic_add(&hdlr, &when);
567 } else {
568 prof->prof_cyclic = cyclic_add_omni(&omni);
569 }
570 #else
571 if (prof->prof_kind == PROF_TICK) {
572 prof->prof_cyclic = cyclic_timer_add(&hdlr, &when);
573 } else {
574 prof->prof_cyclic = (cyclic_id_t)cyclic_add_omni(&omni); /* cast puns cyclic_id_list_t with cyclic_id_t */
575 }
576 #endif /* __APPLE__ */
577 }
578
579 /*ARGSUSED*/
580 static void
581 profile_disable(void *arg, dtrace_id_t id, void *parg)
582 {
583 profile_probe_t *prof = parg;
584
585 ASSERT(prof->prof_cyclic != CYCLIC_NONE);
586 ASSERT(MUTEX_HELD(&cpu_lock));
587
588 #if !defined(__APPLE__)
589 cyclic_remove(prof->prof_cyclic);
590 #else
591 if (prof->prof_kind == PROF_TICK) {
592 cyclic_timer_remove(prof->prof_cyclic);
593 } else {
594 cyclic_remove_omni((cyclic_id_list_t)prof->prof_cyclic); /* cast puns cyclic_id_list_t with cyclic_id_t */
595 }
596 #endif /* __APPLE__ */
597 prof->prof_cyclic = CYCLIC_NONE;
598 }
599
600 #if !defined(__APPLE__)
601 /*ARGSUSED*/
602 static int
603 profile_usermode(void *arg, dtrace_id_t id, void *parg)
604 {
605 return (CPU->cpu_profile_pc == 0);
606 }
607 #else
608 static int
609 profile_usermode(void *arg, dtrace_id_t id, void *parg)
610 {
611 #pragma unused(arg,id,parg)
612 return 1; /* XXX_BOGUS */
613 }
614 #endif /* __APPLE__ */
615
616 static dtrace_pattr_t profile_attr = {
617 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
618 { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_UNKNOWN },
619 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
620 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
621 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
622 };
623
624 static dtrace_pops_t profile_pops = {
625 profile_provide,
626 NULL,
627 profile_enable,
628 profile_disable,
629 NULL,
630 NULL,
631 NULL,
632 NULL,
633 profile_usermode,
634 profile_destroy
635 };
636
637 static int
638 profile_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
639 {
640 switch (cmd) {
641 case DDI_ATTACH:
642 break;
643 case DDI_RESUME:
644 return (DDI_SUCCESS);
645 default:
646 return (DDI_FAILURE);
647 }
648
649 if (ddi_create_minor_node(devi, "profile", S_IFCHR, 0,
650 DDI_PSEUDO, NULL) == DDI_FAILURE ||
651 dtrace_register("profile", &profile_attr,
652 DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER, NULL,
653 &profile_pops, NULL, &profile_id) != 0) {
654 ddi_remove_minor_node(devi, NULL);
655 return (DDI_FAILURE);
656 }
657
658 #if !defined(__APPLE__)
659 profile_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
660 "profile-max-probes", PROFILE_MAX_DEFAULT);
661 #else
662 profile_max = PROFILE_MAX_DEFAULT;
663 #endif /* __APPLE__ */
664
665 ddi_report_dev(devi);
666 profile_devi = devi;
667 return (DDI_SUCCESS);
668 }
669
670 #if !defined(__APPLE__)
671 static int
672 profile_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
673 {
674 switch (cmd) {
675 case DDI_DETACH:
676 break;
677 case DDI_SUSPEND:
678 return (DDI_SUCCESS);
679 default:
680 return (DDI_FAILURE);
681 }
682
683 if (dtrace_unregister(profile_id) != 0)
684 return (DDI_FAILURE);
685
686 ddi_remove_minor_node(devi, NULL);
687 return (DDI_SUCCESS);
688 }
689
690 /*ARGSUSED*/
691 static int
692 profile_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
693 {
694 int error;
695
696 switch (infocmd) {
697 case DDI_INFO_DEVT2DEVINFO:
698 *result = (void *)profile_devi;
699 error = DDI_SUCCESS;
700 break;
701 case DDI_INFO_DEVT2INSTANCE:
702 *result = (void *)0;
703 error = DDI_SUCCESS;
704 break;
705 default:
706 error = DDI_FAILURE;
707 }
708 return (error);
709 }
710
711 /*ARGSUSED*/
712 static int
713 profile_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
714 {
715 return (0);
716 }
717
718 static struct cb_ops profile_cb_ops = {
719 profile_open, /* open */
720 nodev, /* close */
721 nulldev, /* strategy */
722 nulldev, /* print */
723 nodev, /* dump */
724 nodev, /* read */
725 nodev, /* write */
726 nodev, /* ioctl */
727 nodev, /* devmap */
728 nodev, /* mmap */
729 nodev, /* segmap */
730 nochpoll, /* poll */
731 ddi_prop_op, /* cb_prop_op */
732 0, /* streamtab */
733 D_NEW | D_MP /* Driver compatibility flag */
734 };
735
736 static struct dev_ops profile_ops = {
737 DEVO_REV, /* devo_rev, */
738 0, /* refcnt */
739 profile_info, /* get_dev_info */
740 nulldev, /* identify */
741 nulldev, /* probe */
742 profile_attach, /* attach */
743 profile_detach, /* detach */
744 nodev, /* reset */
745 &profile_cb_ops, /* driver operations */
746 NULL, /* bus operations */
747 nodev /* dev power */
748 };
749
750 /*
751 * Module linkage information for the kernel.
752 */
753 static struct modldrv modldrv = {
754 &mod_driverops, /* module type (this is a pseudo driver) */
755 "Profile Interrupt Tracing", /* name of module */
756 &profile_ops, /* driver ops */
757 };
758
759 static struct modlinkage modlinkage = {
760 MODREV_1,
761 (void *)&modldrv,
762 NULL
763 };
764
765 int
766 _init(void)
767 {
768 return (mod_install(&modlinkage));
769 }
770
771 int
772 _info(struct modinfo *modinfop)
773 {
774 return (mod_info(&modlinkage, modinfop));
775 }
776
777 int
778 _fini(void)
779 {
780 return (mod_remove(&modlinkage));
781 }
782 #else
783 d_open_t _profile_open;
784
785 int _profile_open(dev_t dev, int flags, int devtype, struct proc *p)
786 {
787 #pragma unused(dev,flags,devtype,p)
788 return 0;
789 }
790
791 #define PROFILE_MAJOR -24 /* let the kernel pick the device number */
792
793 /*
794 * A struct describing which functions will get invoked for certain
795 * actions.
796 */
797 static struct cdevsw profile_cdevsw =
798 {
799 _profile_open, /* open */
800 eno_opcl, /* close */
801 eno_rdwrt, /* read */
802 eno_rdwrt, /* write */
803 eno_ioctl, /* ioctl */
804 (stop_fcn_t *)nulldev, /* stop */
805 (reset_fcn_t *)nulldev, /* reset */
806 NULL, /* tty's */
807 eno_select, /* select */
808 eno_mmap, /* mmap */
809 eno_strat, /* strategy */
810 eno_getc, /* getc */
811 eno_putc, /* putc */
812 0 /* type */
813 };
814
815 static int gProfileInited = 0;
816
817 void profile_init( void )
818 {
819 if (0 == gProfileInited)
820 {
821 int majdevno = cdevsw_add(PROFILE_MAJOR, &profile_cdevsw);
822
823 if (majdevno < 0) {
824 printf("profile_init: failed to allocate a major number!\n");
825 gProfileInited = 0;
826 return;
827 }
828
829 profile_attach( (dev_info_t *)majdevno, DDI_ATTACH );
830
831 gProfileInited = 1;
832 } else
833 panic("profile_init: called twice!\n");
834 }
835 #undef PROFILE_MAJOR
836 #endif /* __APPLE__ */