]> git.saurik.com Git - apple/xnu.git/blob - bsd/dev/dtrace/profile_prvd.c
60ebf9bd7db873c98768b34397e766c7214733c4
[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 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__)
71 extern 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
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, 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
128 typedef 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
136 typedef 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
142 hrtime_t profile_interval_min = NANOSEC / 5000; /* 5000 hz */
143 int profile_aframes = 0; /* override */
144
145 static 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
152 static 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 */
166 static uint32_t profile_max; /* maximum number of profile probes */
167 static uint32_t profile_total; /* current number of profile probes */
168
169 static void
170 profile_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
215 static void
216 profile_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
255 static void
256 profile_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*/
290 static void
291 profile_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*/
472 static void
473 profile_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*/
493 static void
494 profile_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*/
523 static void
524 profile_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*/
537 static int
538 profile_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*/
584 static void
585 profile_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*/
607 static int
608 profile_usermode(void *arg, dtrace_id_t id, void *parg)
609 {
610 return (CPU->cpu_profile_pc == 0);
611 }
612 #else
613 static int
614 profile_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
621 static 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
629 static 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
642 static int
643 profile_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__)
685 static int
686 profile_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*/
705 static int
706 profile_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*/
726 static int
727 profile_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
728 {
729 return (0);
730 }
731
732 static 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
750 static 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 */
767 static 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
773 static struct modlinkage modlinkage = {
774 MODREV_1,
775 (void *)&modldrv,
776 NULL
777 };
778
779 int
780 _init(void)
781 {
782 return (mod_install(&modlinkage));
783 }
784
785 int
786 _info(struct modinfo *modinfop)
787 {
788 return (mod_info(&modlinkage, modinfop));
789 }
790
791 int
792 _fini(void)
793 {
794 return (mod_remove(&modlinkage));
795 }
796 #else
797 d_open_t _profile_open;
798
799 int _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 */
811 static 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
829 static int gProfileInited = 0;
830
831 void 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__ */