PERFORCE change 101279 for review
Howard Su
howardsu at FreeBSD.org
Tue Jul 11 16:18:39 UTC 2006
http://perforce.freebsd.org/chv.cgi?CH=101279
Change 101279 by howardsu at su_laptop on 2006/07/11 16:17:54
one missing file in last commit
Affected files ...
.. //depot/projects/dtrace/src/sys/cddl/dev/sdt/sdt.c#4 edit
Differences ...
==== //depot/projects/dtrace/src/sys/cddl/dev/sdt/sdt.c#4 (text+ko) ====
@@ -1,33 +1,33 @@
-/*-
- * Copyright 2006 John Birrell <jb at FreeBSD.org>
+
+/*
+ * 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
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * Portions Copyright 2006 John Birrell jb at freebsd.org
*
* $FreeBSD$
*
- * Statically Defined Tracing (SDT) provider for DTrace. This driver relies
- * on the 'sdt' backend kernel intrastructure which registers and
- * de-registers SDT probes using SYSINIT/SYSUNINIT.
- *
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
#include <sys/cdefs.h>
@@ -48,11 +48,13 @@
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
+#include <sys/pcpu.h>
#include <sys/poll.h>
#include <sys/proc.h>
#include <sys/selinfo.h>
#include <sys/smp.h>
#include <sys/syscall.h>
+#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/uio.h>
@@ -60,16 +62,24 @@
#include <machine/stdarg.h>
#include <contrib/opensolaris/uts/common/sys/dtrace_impl.h>
-#include <sys/sdt.h>
+#include <contrib/opensolaris/uts/common/sys/sdt_impl.h>
+
+MALLOC_DECLARE(M_SDT);
+MALLOC_DEFINE(M_SDT, "sdt", "Static Dtrace Tracing");
+
+#define SDT_PATCHVAL 0xf0
+#define SDT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & sdt_probetab_mask)
+#define SDT_PROBETAB_SIZE 0x1000/* 4k entries -- 16K total */
-static d_open_t sdt_open;
-static int sdt_provide_ref(struct sdt_ref *, void *);
+static d_open_t sdt_open;
static int sdt_unload(void);
-static void sdt_provide(void *, dtrace_probedesc_t *);
+static void sdt_provide_module(void *, modctl_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 void sdt_suspend(void *, dtrace_id_t, void *);
+static void sdt_resume(void *, dtrace_id_t, void *);
static struct cdevsw sdt_cdevsw = {
.d_version = D_VERSION,
@@ -77,21 +87,13 @@
.d_name = "sdt",
};
-static dtrace_pattr_t sdt_attr = {
-{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_COMMON },
-{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
-{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
-{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_COMMON },
-{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
-};
-
static dtrace_pops_t sdt_pops = {
- sdt_provide,
NULL,
+ sdt_provide_module,
sdt_enable,
sdt_disable,
- NULL,
- NULL,
+ sdt_suspend,
+ sdt_resume,
NULL,
NULL,
NULL,
@@ -99,90 +101,303 @@
};
static struct cdev *sdt_cdev;
-static dtrace_provider_id_t sdt_id;
+static sdt_probe_t **sdt_probetab;
+static int sdt_probetab_size;
+static int sdt_probetab_mask;
+static int sdt_verbose = 1;
-int
-sdt_provide_ref(struct sdt_ref *ref, void *arg)
+static int
+sdt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
{
- if (dtrace_probe_lookup(sdt_id, ref->mod, ref->func, ref->name) != 0)
+ uintptr_t stack0, stack1, stack2, stack3, stack4;
+ sdt_probe_t *sdt = sdt_probetab[SDT_ADDR2NDX(addr)];
+
+ for (; sdt != NULL; sdt = sdt->sdp_hashnext) {
+ if ((uintptr_t)sdt->sdp_patchpoint == addr) {
+ int i = 0;
+ /*
+ * When accessing the arguments on the stack,
+ * we must protect against accessing beyond
+ * the stack. We can safely set NOFAULT here
+ * -- we know that interrupts are already
+ * disabled.
+ */
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ stack0 = stack[i++];
+ stack1 = stack[i++];
+ stack2 = stack[i++];
+ stack3 = stack[i++];
+ stack4 = stack[i++];
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
+ CPU_DTRACE_BADADDR);
+
+ dtrace_probe(sdt->sdp_id, stack0, stack1,
+ stack2, stack3, stack4);
+ return (DTRACE_INVOP_NOP);
+ }
+ }
+
+ return (0);
+}
+
+static int
+sdt_provide_module_function(linker_file_t lf, linker_symval_t *symval, void *opaque)
+{
+ char *modname = opaque;
+ sdt_probedesc_t *sdpd;
+ sdt_probe_t *sdp, *old;
+ sdt_provider_t *prov;
+ int len;
+
+ /*
+ * One for all, and all for one: if we haven't yet registered all of
+ * our providers, we'll refuse to provide anything.
+ */
+ for (prov = sdt_providers; prov->sdtp_name != NULL; prov++) {
+ if (prov->sdtp_id == DTRACE_PROVNONE)
+ return (0);
+ }
+
+ if (lf->sdt_nprobes != 0 || (sdpd = lf->sdt_probes) == NULL)
return (0);
- ref->id = dtrace_probe_create(sdt_id, ref->mod, ref->func, ref->name, 0, ref);
+ for (; sdpd != NULL; sdpd = sdpd->sdpd_next) {
+ char *name = sdpd->sdpd_name, *func, *nname;
+ int i, j;
+ sdt_provider_t *prov;
+ dtrace_id_t id;
+
+ for (prov = sdt_providers; prov->sdtp_prefix != NULL; prov++) {
+ char *prefix = prov->sdtp_prefix;
+
+ if (strncmp(name, prefix, strlen(prefix)) == 0) {
+ name += strlen(prefix);
+ break;
+ }
+ }
+
+ nname = malloc(len = strlen(name) + 1, M_SDT, M_WAITOK);
+
+ for (i = 0, j = 0; name[j] != '\0'; i++) {
+ if (name[j] == '_' && name[j + 1] == '_') {
+ nname[i] = '-';
+ j += 2;
+ } else {
+ nname[i] = name[j++];
+ }
+ }
+
+ nname[i] = '\0';
+
+ sdp = malloc(sizeof (sdt_probe_t), M_SDT, M_WAITOK | M_ZERO);
+ sdp->sdp_loadcnt = lf->loadcnt;
+ sdp->sdp_ctl = lf;
+ sdp->sdp_name = nname;
+ sdp->sdp_namelen = len;
+ sdp->sdp_provider = prov;
+
+ if (symval->name == NULL)
+ func = "<unknown>";
+ else {
+ /* HACK: we need change prototype of _probe_lookup */
+ func = malloc(strlen(symval->name) + 1, M_SDT, M_WAITOK);
+ strcpy(func, symval->name);
+ }
+
+ /*
+ * We have our provider. Now create the probe.
+ */
+ if ((id = dtrace_probe_lookup(prov->sdtp_id, modname,
+ func, nname)) != DTRACE_IDNONE) {
+ old = dtrace_probe_arg(prov->sdtp_id, id);
+ ASSERT(old != NULL);
+
+ sdp->sdp_next = old->sdp_next;
+ sdp->sdp_id = id;
+ old->sdp_next = sdp;
+ } else {
+ sdp->sdp_id = dtrace_probe_create(prov->sdtp_id,
+ modname, func, nname, 3, sdp);
+
+ lf->sdt_nprobes++;
+ }
+
+ sdp->sdp_hashnext =
+ sdt_probetab[SDT_ADDR2NDX(sdpd->sdpd_offset)];
+ sdt_probetab[SDT_ADDR2NDX(sdpd->sdpd_offset)] = sdp;
- return (0);
+ sdp->sdp_patchval = SDT_PATCHVAL;
+ sdp->sdp_patchpoint = (uint8_t *)sdpd->sdpd_offset;
+ sdp->sdp_savedval = *sdp->sdp_patchpoint;
+ if (symval->name != NULL)
+ free(func, M_SDT);
+ }
+ return (0);
}
/*ARGSUSED*/
static void
-sdt_provide(void *arg, dtrace_probedesc_t *desc)
+sdt_provide_module(void *arg, modctl_t *lf)
{
- sdt_ref_listall(sdt_provide_ref, NULL);
+ char modname[MAXPATHLEN];
+ size_t len;
+
+ strlcpy(modname, lf->filename, sizeof(modname));
+ len = strlen(modname);
+ if (len > 3 && strcmp(modname + len - 3, ".ko") == 0)
+ modname[len - 3] = '\0';
+
+ /*
+ * List the functions in the module and the symbol values.
+ */
+ linker_file_function_listall(lf, sdt_provide_module_function, modname);
}
/* ARGSUSED */
static void
sdt_destroy(void *arg, dtrace_id_t id, void *parg)
{
+ sdt_probe_t *sdt = parg, *next, *hash, *last;
+ modctl_t *ctl;
+ int ndx;
+
+ do {
+ ctl = sdt->sdp_ctl;
+
+ ctl->sdt_nentries--;
+
+ /*
+ * Now we need to remove this probe from the sdt_probetab.
+ */
+ ndx = SDT_ADDR2NDX(sdt->sdp_patchpoint);
+ last = NULL;
+ hash = sdt_probetab[ndx];
+
+ while (hash != sdt) {
+ ASSERT(hash != NULL);
+ last = hash;
+ hash = hash->sdp_hashnext;
+ }
+
+ if (last != NULL) {
+ last->sdp_hashnext = sdt->sdp_hashnext;
+ } else {
+ sdt_probetab[ndx] = sdt->sdp_hashnext;
+ }
+
+ next = sdt->sdp_next;
+ free(sdt, M_SDT);
+
+ sdt = next;
+ } while (sdt != NULL);
}
/* ARGSUSED */
static void
sdt_enable(void *arg, dtrace_id_t id, void *parg)
{
- struct sdt_ref *ref = parg;
+ sdt_probe_t *sdt = parg;
+ modctl_t *ctl = sdt->sdp_ctl;
-#if defined(__i386__)
- u_int16_t *p;
+ ctl->nenabled++;
/*
- * Point to the instruction after the start probe label.
+ * Now check that our modctl has the expected load count. If it
+ * doesn't, this module must have been unloaded and reloaded -- and
+ * we're not going to touch it.
*/
- p = (u_int16_t *) ref->probe_start + 1;
+ if (ctl->loadcnt != sdt->sdp_loadcnt) {
+ if (sdt_verbose) {
+ printf("sdt is failing for probe %s "
+ "(module %s reloaded)",
+ sdt->sdp_name, ctl->filename);
+ }
+
+ return;
+ }
- /*
- * Enable the probe.
- */
- *p = ref->probe_enable;
-#else
-#error "Need machine dependent code!"
-#endif
+ for (; sdt != NULL; sdt = sdt->sdp_next) {
+ if (sdt_verbose)
+ printf("sdt_enable %s\n",sdt->sdp_name);
+ *sdt->sdp_patchpoint = sdt->sdp_patchval;
+ }
}
/* ARGSUSED */
static void
sdt_disable(void *arg, dtrace_id_t id, void *parg)
{
- struct sdt_ref *ref = parg;
+ sdt_probe_t *sdt = parg;
+ modctl_t *ctl = sdt->sdp_ctl;
+
+ ASSERT(ctl->nenabled > 0);
+ ctl->nenabled--;
+
+ if ((ctl->loadcnt != sdt->sdp_loadcnt))
+ return;
+
+ for (; sdt != NULL; sdt = sdt->sdp_next)
+ *sdt->sdp_patchpoint = sdt->sdp_savedval;
+}
+
+/*ARGSUSED*/
+static void
+sdt_suspend(void *arg, dtrace_id_t id, void *parg)
+{
+ sdt_probe_t *sdt = parg;
+ modctl_t *ctl = sdt->sdp_ctl;
+
+ ASSERT(ctl->nenabled > 0);
+
+ if ((ctl->loadcnt != sdt->sdp_loadcnt))
+ return;
+
+ for (; sdt != NULL; sdt = sdt->sdp_next)
+ *sdt->sdp_patchpoint = sdt->sdp_savedval;
+}
+
+/*ARGSUSED*/
+static void
+sdt_resume(void *arg, dtrace_id_t id, void *parg)
+{
+ sdt_probe_t *sdt = parg;
+ modctl_t *ctl = sdt->sdp_ctl;
-#if defined(__i386__)
- u_int16_t *p;
+ ASSERT(ctl->nenabled > 0);
- /*
- * Point to the instruction after the start probe label.
- */
- p = (u_int16_t *) ref->probe_start + 1;
+ if ((ctl->loadcnt != sdt->sdp_loadcnt))
+ return;
- /*
- * Disable the probe.
- */
- *p = ref->probe_disable;
-#else
-#error "Need machine dependent code!"
-#endif
+ for (; sdt != NULL; sdt = sdt->sdp_next)
+ *sdt->sdp_patchpoint = sdt->sdp_patchval;
}
static void
sdt_load(void *dummy)
{
- if (dtrace_register("sdt", &sdt_attr, DTRACE_PRIV_USER,
- NULL, &sdt_pops, NULL, &sdt_id) != 0)
- return;
+ sdt_provider_t *prov;
+
+ /* Default the probe table size if not specified. */
+ if (sdt_probetab_size == 0)
+ sdt_probetab_size = SDT_PROBETAB_SIZE;
+
+ /* Choose the hash mask for the probe table. */
+ sdt_probetab_mask = sdt_probetab_size - 1;
+
+ /* Allocate memory for the probe table. */
+ sdt_probetab = malloc(sdt_probetab_size * sizeof
+ (sdt_probe_t *), M_SDT, M_WAITOK | M_ZERO);
+
+ dtrace_invop_add(sdt_invop);
- /*
- * Register the read DTrace probe function pointer in place
- * of the stub.
- */
- sdt_probe_func = dtrace_probe;
+ for (prov = sdt_providers; prov->sdtp_name != NULL; prov++) {
+ if (dtrace_register(prov->sdtp_name, prov->sdtp_attr,
+ DTRACE_PRIV_USER, NULL,
+ &sdt_pops, prov, &prov->sdtp_id) != 0) {
+ printf("failed to register sdt provider %s",
+ prov->sdtp_name);
+ }
+ }
}
@@ -190,14 +405,25 @@
sdt_unload()
{
int error = 0;
+ sdt_provider_t *prov;
+
+ /* De-register the invalid opcode handler. */
+ dtrace_invop_remove(sdt_invop);
- /*
- * Restore the probe stub.
- */
- sdt_probe_func = sdt_probe_stub;
-
- if ((error = dtrace_unregister(sdt_id)) != 0)
- return (error);
+ /* De-register this DTrace provider. */
+ for (prov = sdt_providers; prov->sdtp_name != NULL; prov++) {
+ if ((error = dtrace_unregister(prov->sdtp_id)) != 0) {
+ return (error);
+ }
+ else {
+ prov->sdtp_id = 0;
+ }
+ }
+
+ /* Free the probe table. */
+ free(sdt_probetab, M_SDT);
+ sdt_probetab = NULL;
+ sdt_probetab_mask = 0;
destroy_dev(sdt_cdev);
@@ -229,6 +455,7 @@
break;
}
+
return (error);
}
More information about the p4-projects
mailing list