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 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* #pragma ident "@(#)sdt.c 1.6 06/03/24 SMI" */
30 #define _KERNEL /* Solaris vs. Darwin */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/errno.h>
38 #include <sys/ioctl.h>
40 #include <sys/fcntl.h>
41 #include <miscfs/devfs/devfs.h>
43 #include <sys/dtrace.h>
44 #include <sys/dtrace_impl.h>
46 #include <sys/dtrace_glue.h>
48 #include <sys/sdt_impl.h>
50 struct savearea_t
; /* Used anonymously */
51 typedef kern_return_t (*perfCallback
)(int, struct savearea_t
*, int, int);
53 #if defined (__ppc__) || defined (__ppc64__)
54 extern perfCallback tempDTraceTrapHook
, tempDTraceIntHook
;
55 extern kern_return_t
fbt_perfCallback(int, struct savearea_t
*, int, int);
56 extern kern_return_t
fbt_perfIntCallback(int, struct savearea_t
*, int, int);
58 #define SDT_PATCHVAL 0x7c810808
60 #elif defined(__i386__) || defined(__x86_64__)
61 extern perfCallback tempDTraceTrapHook
;
62 extern kern_return_t
fbt_perfCallback(int, struct savearea_t
*, int, int);
64 #define SDT_PATCHVAL 0xf0
67 #error Unknown architecture
70 #define SDT_PROBETAB_SIZE 0x1000 /* 4k entries -- 16K total */
72 static dev_info_t
*sdt_devi
;
73 static int sdt_verbose
= 0;
74 sdt_probe_t
**sdt_probetab
;
75 int sdt_probetab_size
;
76 int sdt_probetab_mask
;
80 __sdt_provide_module(void *arg
, struct modctl
*ctl
)
83 struct module *mp
= (struct module *)ctl
->address
;
84 char *modname
= ctl
->mod_modname
;
85 sdt_probedesc_t
*sdpd
;
86 sdt_probe_t
*sdp
, *old
;
91 * One for all, and all for one: if we haven't yet registered all of
92 * our providers, we'll refuse to provide anything.
94 for (prov
= sdt_providers
; prov
->sdtp_name
!= NULL
; prov
++) {
95 if (prov
->sdtp_id
== DTRACE_PROVNONE
)
99 if (mp
->sdt_nprobes
!= 0 || (sdpd
= mp
->sdt_probes
) == NULL
)
102 for (sdpd
= mp
->sdt_probes
; sdpd
!= NULL
; sdpd
= sdpd
->sdpd_next
) {
103 char *name
= sdpd
->sdpd_name
, *func
, *nname
;
107 for (prov
= sdt_providers
; prov
->sdtp_prefix
!= NULL
; prov
++) {
108 char *prefpart
, *prefix
= prov
->sdtp_prefix
;
110 if ((prefpart
= strstr(name
, prefix
))) {
111 name
= prefpart
+ strlen(prefix
);
116 nname
= kmem_alloc(len
= strlen(name
) + 1, KM_SLEEP
);
118 for (i
= 0, j
= 0; name
[j
] != '\0'; i
++) {
119 if (name
[j
] == '_' && name
[j
+ 1] == '_') {
123 nname
[i
] = name
[j
++];
129 sdp
= kmem_zalloc(sizeof (sdt_probe_t
), KM_SLEEP
);
130 sdp
->sdp_loadcnt
= ctl
->mod_loadcnt
;
132 sdp
->sdp_name
= nname
;
133 sdp
->sdp_namelen
= len
;
134 sdp
->sdp_provider
= prov
;
136 func
= sdpd
->sdpd_func
;
142 * We have our provider. Now create the probe.
144 if ((id
= dtrace_probe_lookup(prov
->sdtp_id
, modname
,
145 func
, nname
)) != DTRACE_IDNONE
) {
146 old
= dtrace_probe_arg(prov
->sdtp_id
, id
);
149 sdp
->sdp_next
= old
->sdp_next
;
153 sdp
->sdp_id
= dtrace_probe_create(prov
->sdtp_id
,
154 modname
, func
, nname
, SDT_AFRAMES
, sdp
);
160 sdt_probetab
[SDT_ADDR2NDX(sdpd
->sdpd_offset
)];
161 sdt_probetab
[SDT_ADDR2NDX(sdpd
->sdpd_offset
)] = sdp
;
163 sdp
->sdp_patchval
= SDT_PATCHVAL
;
164 sdp
->sdp_patchpoint
= (sdt_instr_t
*)sdpd
->sdpd_offset
;
165 sdp
->sdp_savedval
= *sdp
->sdp_patchpoint
;
171 sdt_destroy(void *arg
, dtrace_id_t id
, void *parg
)
173 #pragma unused(arg,id)
174 sdt_probe_t
*sdp
= parg
, *old
, *last
, *hash
;
176 #if !defined(__APPLE__)
177 struct modctl
*ctl
= sdp
->sdp_ctl
;
179 if (ctl
!= NULL
&& ctl
->mod_loadcnt
== sdp
->sdp_loadcnt
) {
180 if ((ctl
->mod_loadcnt
== sdp
->sdp_loadcnt
&&
182 ((struct module *)(ctl
->mod_mp
))->sdt_nprobes
--;
185 #endif /* __APPLE__ */
187 while (sdp
!= NULL
) {
191 * Now we need to remove this probe from the sdt_probetab.
193 ndx
= SDT_ADDR2NDX(sdp
->sdp_patchpoint
);
195 hash
= sdt_probetab
[ndx
];
197 while (hash
!= sdp
) {
198 ASSERT(hash
!= NULL
);
200 hash
= hash
->sdp_hashnext
;
204 last
->sdp_hashnext
= sdp
->sdp_hashnext
;
206 sdt_probetab
[ndx
] = sdp
->sdp_hashnext
;
209 kmem_free(sdp
->sdp_name
, sdp
->sdp_namelen
);
211 kmem_free(old
, sizeof (sdt_probe_t
));
217 sdt_enable(void *arg
, dtrace_id_t id
, void *parg
)
219 #pragma unused(arg,id)
220 sdt_probe_t
*sdp
= parg
;
221 struct modctl
*ctl
= sdp
->sdp_ctl
;
223 #if !defined(__APPLE__)
227 * If this module has disappeared since we discovered its probes,
228 * refuse to enable it.
230 if (!ctl
->mod_loaded
) {
232 cmn_err(CE_NOTE
, "sdt is failing for probe %s "
233 "(module %s unloaded)",
234 sdp
->sdp_name
, ctl
->mod_modname
);
240 * Now check that our modctl has the expected load count. If it
241 * doesn't, this module must have been unloaded and reloaded -- and
242 * we're not going to touch it.
244 if (ctl
->mod_loadcnt
!= sdp
->sdp_loadcnt
) {
246 cmn_err(CE_NOTE
, "sdt is failing for probe %s "
247 "(module %s reloaded)",
248 sdp
->sdp_name
, ctl
->mod_modname
);
252 #endif /* __APPLE__ */
254 #if defined (__ppc__) || defined (__ppc64__)
255 dtrace_casptr(&tempDTraceIntHook
, NULL
, fbt_perfIntCallback
);
256 if (tempDTraceIntHook
!= (perfCallback
)fbt_perfIntCallback
) {
258 cmn_err(CE_NOTE
, "sdt_enable is failing for probe %s "
259 "in module %s: tempDTraceIntHook already occupied.",
260 sdp
->sdp_name
, ctl
->mod_modname
);
266 dtrace_casptr(&tempDTraceTrapHook
, NULL
, fbt_perfCallback
);
267 if (tempDTraceTrapHook
!= (perfCallback
)fbt_perfCallback
) {
269 cmn_err(CE_NOTE
, "sdt_enable is failing for probe %s "
270 "in module %s: tempDTraceTrapHook already occupied.",
271 sdp
->sdp_name
, ctl
->mod_modname
);
276 while (sdp
!= NULL
) {
277 (void)ml_nofault_copy( (vm_offset_t
)&sdp
->sdp_patchval
, (vm_offset_t
)sdp
->sdp_patchpoint
,
278 sizeof(sdp
->sdp_patchval
));
287 sdt_disable(void *arg
, dtrace_id_t id
, void *parg
)
289 #pragma unused(arg,id)
290 sdt_probe_t
*sdp
= parg
;
291 #if !defined(__APPLE__)
292 struct modctl
*ctl
= sdp
->sdp_ctl
;
296 if (!ctl
->mod_loaded
|| ctl
->mod_loadcnt
!= sdp
->sdp_loadcnt
)
298 #endif /* __APPLE__ */
300 while (sdp
!= NULL
) {
301 (void)ml_nofault_copy( (vm_offset_t
)&sdp
->sdp_savedval
, (vm_offset_t
)sdp
->sdp_patchpoint
,
302 sizeof(sdp
->sdp_savedval
));
310 static dtrace_pops_t sdt_pops
= {
325 sdt_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
328 sdt_provider_t
*prov
;
330 if (ddi_create_minor_node(devi
, "sdt", S_IFCHR
,
331 0, DDI_PSEUDO
, 0) == DDI_FAILURE
) {
332 cmn_err(CE_NOTE
, "/dev/sdt couldn't create minor node");
333 ddi_remove_minor_node(devi
, NULL
);
334 return (DDI_FAILURE
);
337 ddi_report_dev(devi
);
340 if (sdt_probetab_size
== 0)
341 sdt_probetab_size
= SDT_PROBETAB_SIZE
;
343 sdt_probetab_mask
= sdt_probetab_size
- 1;
345 kmem_zalloc(sdt_probetab_size
* sizeof (sdt_probe_t
*), KM_SLEEP
);
346 dtrace_invop_add(sdt_invop
);
348 for (prov
= sdt_providers
; prov
->sdtp_name
!= NULL
; prov
++) {
349 if (dtrace_register(prov
->sdtp_name
, prov
->sdtp_attr
,
350 DTRACE_PRIV_KERNEL
, NULL
,
351 &sdt_pops
, prov
, &prov
->sdtp_id
) != 0) {
352 cmn_err(CE_WARN
, "failed to register sdt provider %s",
357 return (DDI_SUCCESS
);
360 #if !defined(__APPLE__)
363 sdt_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
365 sdt_provider_t
*prov
;
372 return (DDI_SUCCESS
);
375 return (DDI_FAILURE
);
378 for (prov
= sdt_providers
; prov
->sdtp_name
!= NULL
; prov
++) {
379 if (prov
->sdtp_id
!= DTRACE_PROVNONE
) {
380 if (dtrace_unregister(prov
->sdtp_id
) != 0)
381 return (DDI_FAILURE
);
383 prov
->sdtp_id
= DTRACE_PROVNONE
;
387 dtrace_invop_remove(sdt_invop
);
388 kmem_free(sdt_probetab
, sdt_probetab_size
* sizeof (sdt_probe_t
*));
390 return (DDI_SUCCESS
);
395 sdt_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
400 case DDI_INFO_DEVT2DEVINFO
:
401 *result
= (void *)sdt_devi
;
404 case DDI_INFO_DEVT2INSTANCE
:
416 sdt_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cred_p
)
421 static struct cb_ops sdt_cb_ops
= {
424 nulldev
, /* strategy */
434 ddi_prop_op
, /* cb_prop_op */
436 D_NEW
| D_MP
/* Driver compatibility flag */
439 static struct dev_ops sdt_ops
= {
440 DEVO_REV
, /* devo_rev, */
442 sdt_info
, /* get_dev_info */
443 nulldev
, /* identify */
445 sdt_attach
, /* attach */
446 sdt_detach
, /* detach */
448 &sdt_cb_ops
, /* driver operations */
449 NULL
, /* bus operations */
450 nodev
/* dev power */
454 * Module linkage information for the kernel.
456 static struct modldrv modldrv
= {
457 &mod_driverops
, /* module type (this is a pseudo driver) */
458 "Statically Defined Tracing", /* name of module */
459 &sdt_ops
, /* driver ops */
462 static struct modlinkage modlinkage
= {
471 return (mod_install(&modlinkage
));
475 _info(struct modinfo
*modinfop
)
477 return (mod_info(&modlinkage
, modinfop
));
483 return (mod_remove(&modlinkage
));
488 int _sdt_open(dev_t dev
, int flags
, int devtype
, struct proc
*p
)
490 #pragma unused(dev,flags,devtype,p)
494 #define SDT_MAJOR -24 /* let the kernel pick the device number */
497 * A struct describing which functions will get invoked for certain
500 static struct cdevsw sdt_cdevsw
=
502 _sdt_open
, /* open */
503 eno_opcl
, /* close */
504 eno_rdwrt
, /* read */
505 eno_rdwrt
, /* write */
506 eno_ioctl
, /* ioctl */
507 (stop_fcn_t
*)nulldev
, /* stop */
508 (reset_fcn_t
*)nulldev
, /* reset */
510 eno_select
, /* select */
512 eno_strat
, /* strategy */
518 static int gSDTInited
= 0;
519 static struct modctl g_sdt_kernctl
;
520 static struct module g_sdt_mach_module
;
522 #include <mach-o/loader.h>
523 #include <mach-o/nlist.h>
525 extern struct mach_header _mh_execute_header
; /* the kernel's mach header */
527 void sdt_init( void )
531 int majdevno
= cdevsw_add(SDT_MAJOR
, &sdt_cdevsw
);
534 printf("sdt_init: failed to allocate a major number!\n");
539 if (MH_MAGIC
!= _mh_execute_header
.magic
) {
540 g_sdt_kernctl
.address
= (vm_address_t
)NULL
;
541 g_sdt_kernctl
.size
= 0;
543 struct mach_header
*mh
;
544 struct load_command
*cmd
;
545 struct segment_command
*orig_ts
= NULL
, *orig_le
= NULL
;
546 struct symtab_command
*orig_st
= NULL
;
547 struct nlist
*sym
= NULL
;
551 g_sdt_mach_module
.sdt_nprobes
= 0;
552 g_sdt_mach_module
.sdt_probes
= NULL
;
554 g_sdt_kernctl
.address
= (vm_address_t
)&g_sdt_mach_module
;
555 g_sdt_kernctl
.size
= 0;
556 strncpy((char *)&(g_sdt_kernctl
.mod_modname
), "mach_kernel", KMOD_MAX_NAME
);
558 mh
= &_mh_execute_header
;
559 cmd
= (struct load_command
*) &mh
[1];
560 for (i
= 0; i
< mh
->ncmds
; i
++) {
561 if (cmd
->cmd
== LC_SEGMENT
) {
562 struct segment_command
*orig_sg
= (struct segment_command
*) cmd
;
564 if (strcmp(SEG_TEXT
, orig_sg
->segname
) == 0)
566 else if (strcmp(SEG_LINKEDIT
, orig_sg
->segname
) == 0)
568 else if (strcmp("", orig_sg
->segname
) == 0)
569 orig_ts
= orig_sg
; /* kexts have a single unnamed segment */
571 else if (cmd
->cmd
== LC_SYMTAB
)
572 orig_st
= (struct symtab_command
*) cmd
;
574 cmd
= (struct load_command
*) ((caddr_t
) cmd
+ cmd
->cmdsize
);
577 if ((orig_ts
== NULL
) || (orig_st
== NULL
) || (orig_le
== NULL
))
580 sym
= (struct nlist
*)orig_le
->vmaddr
;
581 strings
= ((char *)sym
) + orig_st
->nsyms
* sizeof(struct nlist
);
583 for (i
= 0; i
< orig_st
->nsyms
; i
++) {
584 uint8_t n_type
= sym
[i
].n_type
& (N_TYPE
| N_EXT
);
585 char *name
= strings
+ sym
[i
].n_un
.n_strx
;
590 /* Check that the symbol is a global and that it has a name. */
591 if (((N_SECT
| N_EXT
) != n_type
&& (N_ABS
| N_EXT
) != n_type
))
594 if (0 == sym
[i
].n_un
.n_strx
) /* iff a null, "", name. */
597 /* Lop off omnipresent leading underscore. */
601 if (strstr(name
, "_dtrace_probe$")) {
602 sdt_probedesc_t
*sdpd
= kmem_alloc(sizeof(sdt_probedesc_t
), KM_SLEEP
);
603 int len
= strlen(name
) + 1;
605 sdpd
->sdpd_name
= kmem_alloc(len
, KM_SLEEP
);
606 strncpy(sdpd
->sdpd_name
, name
, len
); /* NUL termination is ensured. */
608 prev_name
= "<unknown>";
610 for (j
= 0; j
< orig_st
->nsyms
; j
++) {
611 uint8_t n_type
= sym
[j
].n_type
& (N_TYPE
| N_EXT
);
612 char *name
= strings
+ sym
[j
].n_un
.n_strx
;
614 if (((N_SECT
| N_EXT
) != n_type
&& (N_ABS
| N_EXT
) != n_type
))
617 if (0 == sym
[j
].n_un
.n_strx
) /* iff a null, "", name. */
622 if (strstr(name
, "_dtrace_probe$"))
625 if (*(unsigned long *)sym
[i
].n_value
<= (unsigned long)sym
[j
].n_value
)
628 if ((unsigned long)sym
[j
].n_value
> best
) {
629 best
= (unsigned long)sym
[j
].n_value
;
634 sdpd
->sdpd_func
= kmem_alloc((len
= strlen(prev_name
) + 1), KM_SLEEP
);
635 strncpy(sdpd
->sdpd_func
, prev_name
, len
); /* NUL termination is ensured. */
637 sdpd
->sdpd_offset
= *(unsigned long *)sym
[i
].n_value
;
639 sdpd
->sdpd_next
= g_sdt_mach_module
.sdt_probes
;
640 g_sdt_mach_module
.sdt_probes
= sdpd
;
647 sdt_attach( (dev_info_t
*)majdevno
, DDI_ATTACH
);
651 panic("sdt_init: called twice!\n");
657 sdt_provide_module(void *arg
, struct modctl
*ctl
)
660 __sdt_provide_module(arg
, &g_sdt_kernctl
);
662 sdt_probedesc_t
*sdpd
= g_sdt_mach_module
.sdt_probes
;
664 sdt_probedesc_t
*this_sdpd
= sdpd
;
665 kmem_free((void *)sdpd
->sdpd_name
, strlen(sdpd
->sdpd_name
) + 1);
666 kmem_free((void *)sdpd
->sdpd_func
, strlen(sdpd
->sdpd_func
) + 1);
667 sdpd
= sdpd
->sdpd_next
;
668 kmem_free((void *)this_sdpd
, sizeof(sdt_probedesc_t
));
670 g_sdt_mach_module
.sdt_probes
= NULL
;
673 #endif /* __APPLE__ */