258 lines
5.9 KiB
C
Raw Normal View History

/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Portions Copyright 2006-2008 John Birrell jb@freebsd.org
*
* $FreeBSD$
*
*/
#ifndef KDTRACE_HOOKS
#define KDTRACE_HOOKS
#endif
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/dtrace.h>
#include <sys/sdt.h>
#define SDT_ADDR2NDX(addr) (((uintptr_t)(addr)) >> 4)
static d_open_t sdt_open;
static int sdt_unload(void);
static void sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
static void sdt_provide_probes(void *, dtrace_probedesc_t *);
static void sdt_destroy(void *, dtrace_id_t, void *);
static void sdt_enable(void *, dtrace_id_t, void *);
static void sdt_disable(void *, dtrace_id_t, void *);
static void sdt_load(void *);
static int sdt_provider_unreg_callback(struct sdt_provider *prov,
void *arg);
static struct cdevsw sdt_cdevsw = {
.d_version = D_VERSION,
.d_open = sdt_open,
.d_name = "sdt",
};
static dtrace_pattr_t sdt_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};
static dtrace_pops_t sdt_pops = {
sdt_provide_probes,
NULL,
sdt_enable,
sdt_disable,
NULL,
NULL,
sdt_getargdesc,
NULL,
NULL,
sdt_destroy
};
static struct cdev *sdt_cdev;
static int
sdt_argtype_callback(struct sdt_argtype *argtype, void *arg)
{
dtrace_argdesc_t *desc = arg;
if (desc->dtargd_ndx == argtype->ndx) {
desc->dtargd_mapping = desc->dtargd_ndx; /* XXX */
strlcpy(desc->dtargd_native, argtype->type,
sizeof(desc->dtargd_native));
desc->dtargd_xlate[0] = '\0'; /* XXX */
}
return (0);
}
static void
sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
{
struct sdt_probe *probe = parg;
if (desc->dtargd_ndx < probe->n_args)
(void) (sdt_argtype_listall(probe, sdt_argtype_callback, desc));
else
desc->dtargd_ndx = DTRACE_ARGNONE;
return;
}
static int
sdt_probe_callback(struct sdt_probe *probe, void *arg __unused)
{
struct sdt_provider *prov = probe->prov;
char mod[64];
char func[64];
char name[64];
/*
* Unfortunately this is necessary because the Solaris DTrace
* code mixes consts and non-consts with casts to override
* the incompatibilies. On FreeBSD, we use strict warnings
* in gcc, so we have to respect const vs non-const.
*/
strlcpy(mod, probe->mod, sizeof(mod));
strlcpy(func, probe->func, sizeof(func));
strlcpy(name, probe->name, sizeof(name));
if (dtrace_probe_lookup(prov->id, mod, func, name) != 0)
return (0);
(void) dtrace_probe_create(prov->id, probe->mod, probe->func,
SDT probes can directly pass up to five arguments as arguments to dtrace_probe(). Arguments beyond these five must be obtained in an architecture-specific way; this can be done through the getargval provider method, and through dtrace_getarg() if getargval isn't overridden. This change fixes two off-by-one bugs in the way these arguments are fetched in FreeBSD's DTrace implementation. First, the SDT provider must set the aframes parameter to 1 when creating a probe. The aframes parameter controls the number of frames that dtrace_getarg() will step over in order to find the frame containing the extra arguments. On FreeBSD, dtrace_getarg() is called in SDT probe context via dtrace_probe()->dtrace_dif_emulate()->dtrace_dif_variable->dtrace_getarg() so aframes must be 3 since the arguments are in dtrace_probe()'s frame; it was previously being called with a value of 2 instead. illumos uses a different aframes value for SDT probes, but this is because illumos SDT probes fire by triggering the #UD fault handler rather than calling dtrace_probe() directly. The second bug has to do with the way arguments are grabbed out dtrace_probe()'s frame on amd64. The code currently jumps over the first stack argument and retrieves the rest of them using a pointer into the stack. This works on i386 because all of dtrace_probe()'s arguments will be on the stack and the first argument is the probe ID, which should be ignored. However, it is incorrect to ignore the first stack argument on amd64, so we correct the pointer used to access the arguments. MFC after: 2 weeks
2013-06-02 01:05:36 +00:00
probe->name, 1, probe);
return (0);
}
static int
sdt_provider_entry(struct sdt_provider *prov, void *arg)
{
return (sdt_probe_listall(prov, sdt_probe_callback, NULL));
}
static void
sdt_provide_probes(void *arg, dtrace_probedesc_t *desc)
{
if (desc != NULL)
return;
(void) sdt_provider_listall(sdt_provider_entry, NULL);
}
static void
sdt_destroy(void *arg, dtrace_id_t id, void *parg)
{
/* Nothing to do here. */
}
static void
sdt_enable(void *arg, dtrace_id_t id, void *parg)
{
struct sdt_probe *probe = parg;
probe->id = id;
}
static void
sdt_disable(void *arg, dtrace_id_t id, void *parg)
{
struct sdt_probe *probe = parg;
probe->id = 0;
}
static int
sdt_provider_reg_callback(struct sdt_provider *prov, void *arg __unused)
{
return (dtrace_register(prov->name, &sdt_attr, DTRACE_PRIV_USER,
NULL, &sdt_pops, NULL, (dtrace_provider_id_t *) &prov->id));
}
static void
sdt_load(void *dummy)
{
/* Create the /dev/dtrace/sdt entry. */
sdt_cdev = make_dev(&sdt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/sdt");
sdt_probe_func = dtrace_probe;
sdt_register_callbacks(sdt_provider_reg_callback, NULL,
sdt_provider_unreg_callback, NULL, sdt_probe_callback, NULL);
}
static int
sdt_provider_unreg_callback(struct sdt_provider *prov, void *arg __unused)
{
return (dtrace_unregister(prov->id));
}
static int
sdt_unload()
{
int error = 0;
sdt_probe_func = sdt_probe_stub;
sdt_deregister_callbacks();
destroy_dev(sdt_cdev);
return (error);
}
/* ARGSUSED */
static int
sdt_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
/* ARGSUSED */
static int
sdt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
{
return (0);
}
SYSINIT(sdt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_load, NULL);
SYSUNINIT(sdt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_unload, NULL);
DEV_MODULE(sdt, sdt_modevent, NULL);
MODULE_VERSION(sdt, 1);
MODULE_DEPEND(sdt, dtrace, 1, 1, 1);
MODULE_DEPEND(sdt, opensolaris, 1, 1, 1);