Home | History | Annotate | Download | only in dtrace
      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	"@(#)systrace.c	1.6	06/09/19 SMI"
     27 
     28 #include <sys/dtrace.h>
     29 #include <sys/systrace.h>
     30 #include <sys/stat.h>
     31 #include <sys/systm.h>
     32 #include <sys/conf.h>
     33 #include <sys/ddi.h>
     34 #include <sys/sunddi.h>
     35 #include <sys/atomic.h>
     36 
     37 #define	SYSTRACE_ARTIFICIAL_FRAMES	1
     38 
     39 #define	SYSTRACE_SHIFT			16
     40 #define	SYSTRACE_ISENTRY(x)		((int)(x) >> SYSTRACE_SHIFT)
     41 #define	SYSTRACE_SYSNUM(x)		((int)(x) & ((1 << SYSTRACE_SHIFT) - 1))
     42 #define	SYSTRACE_ENTRY(id)		((1 << SYSTRACE_SHIFT) | (id))
     43 #define	SYSTRACE_RETURN(id)		(id)
     44 
     45 #if ((1 << SYSTRACE_SHIFT) <= NSYSCALL)
     46 #error 1 << SYSTRACE_SHIFT must exceed number of system calls
     47 #endif
     48 
     49 static dev_info_t *systrace_devi;
     50 static dtrace_provider_id_t systrace_id;
     51 
     52 static void
     53 systrace_init(struct sysent *actual, systrace_sysent_t **interposed)
     54 {
     55 	systrace_sysent_t *sysent = *interposed;
     56 	int i;
     57 
     58 	if (sysent == NULL) {
     59 		*interposed = sysent = kmem_zalloc(sizeof (systrace_sysent_t) *
     60 		    NSYSCALL, KM_SLEEP);
     61 	}
     62 
     63 	for (i = 0; i < NSYSCALL; i++) {
     64 		struct sysent *a = &actual[i];
     65 		systrace_sysent_t *s = &sysent[i];
     66 
     67 		if (LOADABLE_SYSCALL(a) && !LOADED_SYSCALL(a))
     68 			continue;
     69 
     70 		if (a->sy_callc == dtrace_systrace_syscall)
     71 			continue;
     72 
     73 #ifdef _SYSCALL32_IMPL
     74 		if (a->sy_callc == dtrace_systrace_syscall32)
     75 			continue;
     76 #endif
     77 
     78 		s->stsy_underlying = a->sy_callc;
     79 	}
     80 }
     81 
     82 /*ARGSUSED*/
     83 static void
     84 systrace_provide(void *arg, const dtrace_probedesc_t *desc)
     85 {
     86 	int i;
     87 
     88 	if (desc != NULL)
     89 		return;
     90 
     91 	systrace_init(sysent, &systrace_sysent);
     92 #ifdef _SYSCALL32_IMPL
     93 	systrace_init(sysent32, &systrace_sysent32);
     94 #endif
     95 
     96 	for (i = 0; i < NSYSCALL; i++) {
     97 		if (systrace_sysent[i].stsy_underlying == NULL)
     98 			continue;
     99 
    100 		if (dtrace_probe_lookup(systrace_id, NULL,
    101 		    syscallnames[i], "entry") != 0)
    102 			continue;
    103 
    104 		(void) dtrace_probe_create(systrace_id, NULL, syscallnames[i],
    105 		    "entry", SYSTRACE_ARTIFICIAL_FRAMES,
    106 		    (void *)((uintptr_t)SYSTRACE_ENTRY(i)));
    107 		(void) dtrace_probe_create(systrace_id, NULL, syscallnames[i],
    108 		    "return", SYSTRACE_ARTIFICIAL_FRAMES,
    109 		    (void *)((uintptr_t)SYSTRACE_RETURN(i)));
    110 
    111 		systrace_sysent[i].stsy_entry = DTRACE_IDNONE;
    112 		systrace_sysent[i].stsy_return = DTRACE_IDNONE;
    113 #ifdef _SYSCALL32_IMPL
    114 		systrace_sysent32[i].stsy_entry = DTRACE_IDNONE;
    115 		systrace_sysent32[i].stsy_return = DTRACE_IDNONE;
    116 #endif
    117 	}
    118 }
    119 
    120 /*ARGSUSED*/
    121 static void
    122 systrace_destroy(void *arg, dtrace_id_t id, void *parg)
    123 {
    124 	int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
    125 
    126 	/*
    127 	 * There's nothing to do here but assert that we have actually been
    128 	 * disabled.
    129 	 */
    130 	if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
    131 		ASSERT(systrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE);
    132 #ifdef _SYSCALL32_IMPL
    133 		ASSERT(systrace_sysent32[sysnum].stsy_entry == DTRACE_IDNONE);
    134 #endif
    135 	} else {
    136 		ASSERT(systrace_sysent[sysnum].stsy_return == DTRACE_IDNONE);
    137 #ifdef _SYSCALL32_IMPL
    138 		ASSERT(systrace_sysent32[sysnum].stsy_return == DTRACE_IDNONE);
    139 #endif
    140 	}
    141 }
    142 
    143 /*ARGSUSED*/
    144 static void
    145 systrace_enable(void *arg, dtrace_id_t id, void *parg)
    146 {
    147 	int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
    148 	int enabled = (systrace_sysent[sysnum].stsy_entry != DTRACE_IDNONE ||
    149 	    systrace_sysent[sysnum].stsy_return != DTRACE_IDNONE);
    150 
    151 	if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
    152 		systrace_sysent[sysnum].stsy_entry = id;
    153 #ifdef _SYSCALL32_IMPL
    154 		systrace_sysent32[sysnum].stsy_entry = id;
    155 #endif
    156 	} else {
    157 		systrace_sysent[sysnum].stsy_return = id;
    158 #ifdef _SYSCALL32_IMPL
    159 		systrace_sysent32[sysnum].stsy_return = id;
    160 #endif
    161 	}
    162 
    163 	if (enabled) {
    164 		ASSERT(sysent[sysnum].sy_callc == dtrace_systrace_syscall);
    165 		return;
    166 	}
    167 
    168 	(void) casptr(&sysent[sysnum].sy_callc,
    169 	    (void *)systrace_sysent[sysnum].stsy_underlying,
    170 	    (void *)dtrace_systrace_syscall);
    171 #ifdef _SYSCALL32_IMPL
    172 	(void) casptr(&sysent32[sysnum].sy_callc,
    173 	    (void *)systrace_sysent32[sysnum].stsy_underlying,
    174 	    (void *)dtrace_systrace_syscall32);
    175 #endif
    176 }
    177 
    178 /*ARGSUSED*/
    179 static void
    180 systrace_disable(void *arg, dtrace_id_t id, void *parg)
    181 {
    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);
    185 
    186 	if (disable) {
    187 		(void) casptr(&sysent[sysnum].sy_callc,
    188 		    (void *)dtrace_systrace_syscall,
    189 		    (void *)systrace_sysent[sysnum].stsy_underlying);
    190 
    191 #ifdef _SYSCALL32_IMPL
    192 		(void) casptr(&sysent32[sysnum].sy_callc,
    193 		    (void *)dtrace_systrace_syscall32,
    194 		    (void *)systrace_sysent32[sysnum].stsy_underlying);
    195 #endif
    196 	}
    197 
    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;
    202 #endif
    203 	} else {
    204 		systrace_sysent[sysnum].stsy_return = DTRACE_IDNONE;
    205 #ifdef _SYSCALL32_IMPL
    206 		systrace_sysent32[sysnum].stsy_return = DTRACE_IDNONE;
    207 #endif
    208 	}
    209 }
    210 
    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 },
    217 };
    218 
    219 static dtrace_pops_t systrace_pops = {
    220 	systrace_provide,
    221 	NULL,
    222 	systrace_enable,
    223 	systrace_disable,
    224 	NULL,
    225 	NULL,
    226 	NULL,
    227 	NULL,
    228 	NULL,
    229 	systrace_destroy
    230 };
    231 
    232 static int
    233 systrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
    234 {
    235 	switch (cmd) {
    236 	case DDI_ATTACH:
    237 		break;
    238 	case DDI_RESUME:
    239 		return (DDI_SUCCESS);
    240 	default:
    241 		return (DDI_FAILURE);
    242 	}
    243 
    244 	systrace_probe = (void (*)())dtrace_probe;
    245 	membar_enter();
    246 
    247 	if (ddi_create_minor_node(devi, "systrace", S_IFCHR, 0,
    248 	    DDI_PSEUDO, NULL) == 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);
    254 	}
    255 
    256 	ddi_report_dev(devi);
    257 	systrace_devi = devi;
    258 
    259 	return (DDI_SUCCESS);
    260 }
    261 
    262 static int
    263 systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
    264 {
    265 	switch (cmd) {
    266 	case DDI_DETACH:
    267 		break;
    268 	case DDI_SUSPEND:
    269 		return (DDI_SUCCESS);
    270 	default:
    271 		return (DDI_FAILURE);
    272 	}
    273 
    274 	if (dtrace_unregister(systrace_id) != 0)
    275 		return (DDI_FAILURE);
    276 
    277 	ddi_remove_minor_node(devi, NULL);
    278 	systrace_probe = systrace_stub;
    279 	return (DDI_SUCCESS);
    280 }
    281 
    282 /*ARGSUSED*/
    283 static int
    284 systrace_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
    285 {
    286 	int error;
    287 
    288 	switch (infocmd) {
    289 	case DDI_INFO_DEVT2DEVINFO:
    290 		*result = (void *)systrace_devi;
    291 		error = DDI_SUCCESS;
    292 		break;
    293 	case DDI_INFO_DEVT2INSTANCE:
    294 		*result = (void *)0;
    295 		error = DDI_SUCCESS;
    296 		break;
    297 	default:
    298 		error = DDI_FAILURE;
    299 	}
    300 	return (error);
    301 }
    302 
    303 /*ARGSUSED*/
    304 static int
    305 systrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
    306 {
    307 	return (0);
    308 }
    309 
    310 static struct cb_ops systrace_cb_ops = {
    311 	systrace_open,		/* open */
    312 	nodev,			/* close */
    313 	nulldev,		/* strategy */
    314 	nulldev,		/* print */
    315 	nodev,			/* dump */
    316 	nodev,			/* read */
    317 	nodev,			/* write */
    318 	nodev,			/* ioctl */
    319 	nodev,			/* devmap */
    320 	nodev,			/* mmap */
    321 	nodev,			/* segmap */
    322 	nochpoll,		/* poll */
    323 	ddi_prop_op,		/* cb_prop_op */
    324 	0,			/* streamtab  */
    325 	D_NEW | D_MP		/* Driver compatibility flag */
    326 };
    327 
    328 static struct dev_ops systrace_ops = {
    329 	DEVO_REV,		/* devo_rev, */
    330 	0,			/* refcnt  */
    331 	systrace_info,		/* get_dev_info */
    332 	nulldev,		/* identify */
    333 	nulldev,		/* probe */
    334 	systrace_attach,	/* attach */
    335 	systrace_detach,	/* detach */
    336 	nodev,			/* reset */
    337 	&systrace_cb_ops,	/* driver operations */
    338 	NULL,			/* bus operations */
    339 	nodev			/* dev power */
    340 };
    341 
    342 /*
    343  * Module linkage information for the kernel.
    344  */
    345 static struct modldrv modldrv = {
    346 	&mod_driverops,		/* module type (this is a pseudo driver) */
    347 	"System Call Tracing",	/* name of module */
    348 	&systrace_ops,		/* driver ops */
    349 };
    350 
    351 static struct modlinkage modlinkage = {
    352 	MODREV_1,
    353 	(void *)&modldrv,
    354 	NULL
    355 };
    356 
    357 int
    358 _init(void)
    359 {
    360 	return (mod_install(&modlinkage));
    361 }
    362 
    363 int
    364 _info(struct modinfo *modinfop)
    365 {
    366 	return (mod_info(&modlinkage, modinfop));
    367 }
    368 
    369 int
    370 _fini(void)
    371 {
    372 	return (mod_remove(&modlinkage));
    373 }
    374