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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #include <sys/dtrace.h>
28 #include <sys/systrace.h>
30 #include <sys/systm.h>
33 #include <sys/sunddi.h>
34 #include <sys/atomic.h>
36 #define SYSTRACE_ARTIFICIAL_FRAMES 1
38 #define SYSTRACE_SHIFT 16
39 #define SYSTRACE_ISENTRY(x) ((int)(x) >> SYSTRACE_SHIFT)
40 #define SYSTRACE_SYSNUM(x) ((int)(x) & ((1 << SYSTRACE_SHIFT) - 1))
41 #define SYSTRACE_ENTRY(id) ((1 << SYSTRACE_SHIFT) | (id))
42 #define SYSTRACE_RETURN(id) (id)
44 #if ((1 << SYSTRACE_SHIFT) <= NSYSCALL)
45 #error 1 << SYSTRACE_SHIFT must exceed number of system calls
48 static dev_info_t
*systrace_devi
;
49 static dtrace_provider_id_t systrace_id
;
52 systrace_init(struct sysent
*actual
, systrace_sysent_t
**interposed
)
54 systrace_sysent_t
*sysent
= *interposed
;
58 *interposed
= sysent
= kmem_zalloc(sizeof (systrace_sysent_t
) *
62 for (i
= 0; i
< NSYSCALL
; i
++) {
63 struct sysent
*a
= &actual
[i
];
64 systrace_sysent_t
*s
= &sysent
[i
];
66 if (LOADABLE_SYSCALL(a
) && !LOADED_SYSCALL(a
))
69 if (a
->sy_callc
== dtrace_systrace_syscall
)
72 #ifdef _SYSCALL32_IMPL
73 if (a
->sy_callc
== dtrace_systrace_syscall32
)
77 s
->stsy_underlying
= a
->sy_callc
;
83 systrace_provide(void *arg
, const dtrace_probedesc_t
*desc
)
90 systrace_init(sysent
, &systrace_sysent
);
91 #ifdef _SYSCALL32_IMPL
92 systrace_init(sysent32
, &systrace_sysent32
);
95 for (i
= 0; i
< NSYSCALL
; i
++) {
96 if (systrace_sysent
[i
].stsy_underlying
== NULL
)
99 if (dtrace_probe_lookup(systrace_id
, NULL
,
100 syscallnames
[i
], "entry") != 0)
103 (void) dtrace_probe_create(systrace_id
, NULL
, syscallnames
[i
],
104 "entry", SYSTRACE_ARTIFICIAL_FRAMES
,
105 (void *)((uintptr_t)SYSTRACE_ENTRY(i
)));
106 (void) dtrace_probe_create(systrace_id
, NULL
, syscallnames
[i
],
107 "return", SYSTRACE_ARTIFICIAL_FRAMES
,
108 (void *)((uintptr_t)SYSTRACE_RETURN(i
)));
110 systrace_sysent
[i
].stsy_entry
= DTRACE_IDNONE
;
111 systrace_sysent
[i
].stsy_return
= DTRACE_IDNONE
;
112 #ifdef _SYSCALL32_IMPL
113 systrace_sysent32
[i
].stsy_entry
= DTRACE_IDNONE
;
114 systrace_sysent32
[i
].stsy_return
= DTRACE_IDNONE
;
121 systrace_destroy(void *arg
, dtrace_id_t id
, void *parg
)
123 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
126 * There's nothing to do here but assert that we have actually been
129 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
130 ASSERT(systrace_sysent
[sysnum
].stsy_entry
== DTRACE_IDNONE
);
131 #ifdef _SYSCALL32_IMPL
132 ASSERT(systrace_sysent32
[sysnum
].stsy_entry
== DTRACE_IDNONE
);
135 ASSERT(systrace_sysent
[sysnum
].stsy_return
== DTRACE_IDNONE
);
136 #ifdef _SYSCALL32_IMPL
137 ASSERT(systrace_sysent32
[sysnum
].stsy_return
== DTRACE_IDNONE
);
144 systrace_enable(void *arg
, dtrace_id_t id
, void *parg
)
146 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
147 int enabled
= (systrace_sysent
[sysnum
].stsy_entry
!= DTRACE_IDNONE
||
148 systrace_sysent
[sysnum
].stsy_return
!= DTRACE_IDNONE
);
150 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
151 systrace_sysent
[sysnum
].stsy_entry
= id
;
152 #ifdef _SYSCALL32_IMPL
153 systrace_sysent32
[sysnum
].stsy_entry
= id
;
156 systrace_sysent
[sysnum
].stsy_return
= id
;
157 #ifdef _SYSCALL32_IMPL
158 systrace_sysent32
[sysnum
].stsy_return
= id
;
163 ASSERT(sysent
[sysnum
].sy_callc
== dtrace_systrace_syscall
);
167 (void) atomic_cas_ptr(&sysent
[sysnum
].sy_callc
,
168 (void *)systrace_sysent
[sysnum
].stsy_underlying
,
169 (void *)dtrace_systrace_syscall
);
170 #ifdef _SYSCALL32_IMPL
171 (void) atomic_cas_ptr(&sysent32
[sysnum
].sy_callc
,
172 (void *)systrace_sysent32
[sysnum
].stsy_underlying
,
173 (void *)dtrace_systrace_syscall32
);
180 systrace_disable(void *arg
, dtrace_id_t id
, void *parg
)
182 int sysnum
= SYSTRACE_SYSNUM((uintptr_t)parg
);
183 int disable
= (systrace_sysent
[sysnum
].stsy_entry
== DTRACE_IDNONE
||
184 systrace_sysent
[sysnum
].stsy_return
== DTRACE_IDNONE
);
187 (void) atomic_cas_ptr(&sysent
[sysnum
].sy_callc
,
188 (void *)dtrace_systrace_syscall
,
189 (void *)systrace_sysent
[sysnum
].stsy_underlying
);
191 #ifdef _SYSCALL32_IMPL
192 (void) atomic_cas_ptr(&sysent32
[sysnum
].sy_callc
,
193 (void *)dtrace_systrace_syscall32
,
194 (void *)systrace_sysent32
[sysnum
].stsy_underlying
);
198 if (SYSTRACE_ISENTRY((uintptr_t)parg
)) {
199 systrace_sysent
[sysnum
].stsy_entry
= DTRACE_IDNONE
;
200 #ifdef _SYSCALL32_IMPL
201 systrace_sysent32
[sysnum
].stsy_entry
= DTRACE_IDNONE
;
204 systrace_sysent
[sysnum
].stsy_return
= DTRACE_IDNONE
;
205 #ifdef _SYSCALL32_IMPL
206 systrace_sysent32
[sysnum
].stsy_return
= DTRACE_IDNONE
;
211 static dtrace_pattr_t systrace_attr
= {
212 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
213 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_UNKNOWN
},
214 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_ISA
},
215 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
216 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_ISA
},
219 static dtrace_pops_t systrace_pops
= {
233 systrace_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
239 return (DDI_SUCCESS
);
241 return (DDI_FAILURE
);
244 systrace_probe
= (void (*)())dtrace_probe
;
247 if (ddi_create_minor_node(devi
, "systrace", S_IFCHR
, 0,
248 DDI_PSEUDO
, 0) == DDI_FAILURE
||
249 dtrace_register("syscall", &systrace_attr
, DTRACE_PRIV_USER
, NULL
,
250 &systrace_pops
, NULL
, &systrace_id
) != 0) {
251 systrace_probe
= systrace_stub
;
252 ddi_remove_minor_node(devi
, NULL
);
253 return (DDI_FAILURE
);
256 ddi_report_dev(devi
);
257 systrace_devi
= devi
;
259 return (DDI_SUCCESS
);
263 systrace_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
269 return (DDI_SUCCESS
);
271 return (DDI_FAILURE
);
274 if (dtrace_unregister(systrace_id
) != 0)
275 return (DDI_FAILURE
);
277 ddi_remove_minor_node(devi
, NULL
);
278 systrace_probe
= systrace_stub
;
279 return (DDI_SUCCESS
);
284 systrace_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
289 case DDI_INFO_DEVT2DEVINFO
:
290 *result
= (void *)systrace_devi
;
293 case DDI_INFO_DEVT2INSTANCE
:
305 systrace_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cred_p
)
310 static struct cb_ops systrace_cb_ops
= {
311 systrace_open
, /* open */
313 nulldev
, /* strategy */
323 ddi_prop_op
, /* cb_prop_op */
325 D_NEW
| D_MP
/* Driver compatibility flag */
328 static struct dev_ops systrace_ops
= {
329 DEVO_REV
, /* devo_rev, */
331 systrace_info
, /* get_dev_info */
332 nulldev
, /* identify */
334 systrace_attach
, /* attach */
335 systrace_detach
, /* detach */
337 &systrace_cb_ops
, /* driver operations */
338 NULL
, /* bus operations */
339 nodev
, /* dev power */
340 ddi_quiesce_not_needed
, /* quiesce */
344 * Module linkage information for the kernel.
346 static struct modldrv modldrv
= {
347 &mod_driverops
, /* module type (this is a pseudo driver) */
348 "System Call Tracing", /* name of module */
349 &systrace_ops
, /* driver ops */
352 static struct modlinkage modlinkage
= {
361 return (mod_install(&modlinkage
));
365 _info(struct modinfo
*modinfop
)
367 return (mod_info(&modlinkage
, modinfop
));
373 return (mod_remove(&modlinkage
));