From owner-p4-projects@FreeBSD.ORG Wed Jan 16 00:53:57 2008 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 4F91116A421; Wed, 16 Jan 2008 00:53:57 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 14CFA16A418 for ; Wed, 16 Jan 2008 00:53:57 +0000 (UTC) (envelope-from jb@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id EB21613C4CC for ; Wed, 16 Jan 2008 00:53:56 +0000 (UTC) (envelope-from jb@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id m0G0ruSe047171 for ; Wed, 16 Jan 2008 00:53:56 GMT (envelope-from jb@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id m0G0rug7047168 for perforce@freebsd.org; Wed, 16 Jan 2008 00:53:56 GMT (envelope-from jb@freebsd.org) Date: Wed, 16 Jan 2008 00:53:56 GMT Message-Id: <200801160053.m0G0rug7047168@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to jb@freebsd.org using -f From: John Birrell To: Perforce Change Reviews Cc: Subject: PERFORCE change 133377 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 16 Jan 2008 00:53:57 -0000 http://perforce.freebsd.org/chv.cgi?CH=133377 Change 133377 by jb@jb_freebsd1 on 2008/01/16 00:53:50 A big change to the design of the Statically Defined Probe (sdt) provider. This utilises the SYSINIT/SYSINIT to keep a list of sdt providers and sub-lists of sdt probes, all statically defined and just linked via queue(3) so that they can be recursed. Since the sdt.h header is included thoroughout the kernel source where static probes are defined, it has to be BSD licensed. This means a complete departure from the sdt design in Solaris. Readers are encouraged to compare the unenabled probe overhead in this implementation with that in the Solaris design. :-) Note that the presence of the KDTRACE_HOOKS kernel option causes the kern_sdt.c file to be compiled in, so that the SYSINIT/SYSUNINIT routines are called even if the DTrace modules aren't loaded. This is necessary so that if the DTrace modules are loaded later, they have an up-to-date set of data to use. The overhead is minimal. Affected files ... .. //depot/projects/dtrace/src/sys/cddl/dev/sdt/sdt.c#8 edit .. //depot/projects/dtrace/src/sys/kern/kern_sdt.c#5 add .. //depot/projects/dtrace/src/sys/sys/sdt.h#10 add Differences ... ==== //depot/projects/dtrace/src/sys/cddl/dev/sdt/sdt.c#8 (text+ko) ==== @@ -18,67 +18,39 @@ * * CDDL HEADER END * - * Portions Copyright 2006 John Birrell jb@freebsd.org + * Portions Copyright 2006-2008 John Birrell jb@freebsd.org * * $FreeBSD$ * */ -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ +#ifndef KDTRACE_HOOKS +#define KDTRACE_HOOKS +#endif #include #include #include -#include #include -#include -#include -#include -#include #include -#include -#include #include +#include #include -#include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include -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 */ +#define SDT_ADDR2NDX(addr) (((uintptr_t)(addr)) >> 4) static d_open_t sdt_open; static int sdt_unload(void); -static void sdt_provide_module(void *, modctl_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 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, @@ -86,339 +58,127 @@ .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_provide_module, sdt_enable, sdt_disable, - sdt_suspend, - sdt_resume, - sdt_getargdesc, + NULL, + NULL, + NULL, NULL, NULL, sdt_destroy }; static struct cdev *sdt_cdev; -static sdt_probe_t **sdt_probetab; -static int sdt_probetab_size; -static int sdt_probetab_mask; -static int sdt_verbose = 0; static int -sdt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval) +sdt_probe_callback(struct sdt_probe *probe, void *arg __unused) { - struct pcpu *cpu = pcpu_find(curcpu); - uintptr_t stack0, stack1, stack2, stack3, stack4; - sdt_probe_t *sdt = sdt_probetab[SDT_ADDR2NDX(addr)]; + 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)); - 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); - cpu->pc_dtrace_caller = addr; - stack0 = stack[i++]; - stack1 = stack[i++]; - stack2 = stack[i++]; - stack3 = stack[i++]; - stack4 = stack[i++]; - DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | - CPU_DTRACE_BADADDR); + if (dtrace_probe_lookup(prov->id, mod, func, name) != 0) + return (0); - dtrace_probe(sdt->sdp_id, stack0, stack1, - stack2, stack3, stack4); - return (DTRACE_INVOP_NOP); - } - } + (void) dtrace_probe_create(prov->id, probe->mod, probe->func, + probe->name, 0, probe); - return (0); + return (0); } static int -sdt_create_module_probes(linker_file_t lf, char *modname) -{ - 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); - - for (; sdpd != NULL; sdpd = sdpd->sdpd_next) { - char *name = sdpd->sdpd_name, *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; - } - } - - while (*name != '\0') { - if (*name <= '9' && *name>='0') - name++; - else - 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; - - /* - * We have our provider. Now create the probe. - */ - if ((id = dtrace_probe_lookup(prov->sdtp_id, modname, - NULL, 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, NULL, nname, 3, sdp); - - lf->sdt_nprobes++; - } - - sdp->sdp_hashnext = - sdt_probetab[SDT_ADDR2NDX(sdpd->sdpd_offset)]; - sdt_probetab[SDT_ADDR2NDX(sdpd->sdpd_offset)] = sdp; - - sdp->sdp_patchval = SDT_PATCHVAL; - sdp->sdp_patchpoint = (uint8_t *)sdpd->sdpd_offset; - sdp->sdp_savedval = *sdp->sdp_patchpoint; - } - return (0); +sdt_provider_entry(struct sdt_provider *prov, void *arg) +{ + return (sdt_probe_listall(prov, sdt_probe_callback, NULL)); } -/*ARGSUSED*/ static void -sdt_provide_module(void *arg, modctl_t *lf) +sdt_provide_probes(void *arg, dtrace_probedesc_t *desc) { - char modname[MAXPATHLEN]; - size_t len; + if (desc != NULL) + return; - strlcpy(modname, lf->filename, sizeof(modname)); - len = strlen(modname); - if (len > 3 && strcmp(modname + len - 3, ".ko") == 0) - modname[len - 3] = '\0'; - - sdt_create_module_probes(lf, modname); + (void) sdt_provider_listall(sdt_provider_entry, NULL); } -/* ARGSUSED */ static void sdt_destroy(void *arg, dtrace_id_t id, void *parg) { - sdt_probe_t *sdt = parg, *next, *hash, *last; - modctl_t *ctl = sdt->sdp_ctl; - int ndx; - - if (ctl->loadcnt == sdt->sdp_loadcnt) - ctl->sdt_nprobes--; - - do { - /* - * 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; - } - - free(sdt->sdp_name, M_SDT); - next = sdt->sdp_next; - free(sdt, M_SDT); - - sdt = next; - } while (sdt != NULL); + /* Nothing to do here. */ } -/* ARGSUSED */ static void sdt_enable(void *arg, dtrace_id_t id, void *parg) { - sdt_probe_t *sdt = parg; - modctl_t *ctl = sdt->sdp_ctl; + struct sdt_probe *probe = parg; - ctl->nenabled++; - - /* - * 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. - */ - 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; - } - - for (; sdt != NULL; sdt = sdt->sdp_next) { - if (sdt_verbose) - printf("sdt_enable %s\n",sdt->sdp_name); - *sdt->sdp_patchpoint = sdt->sdp_patchval; - } + probe->id = id; } -/* ARGSUSED */ static void sdt_disable(void *arg, dtrace_id_t id, void *parg) { - sdt_probe_t *sdt = parg; - modctl_t *ctl = sdt->sdp_ctl; + struct sdt_probe *probe = parg; - 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; + probe->id = 0; } -/*ARGSUSED*/ -static void -sdt_suspend(void *arg, dtrace_id_t id, void *parg) +static int +sdt_provider_reg_callback(struct sdt_provider *prov, void *arg __unused) { - 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; + return (dtrace_register(prov->name, &sdt_attr, DTRACE_PRIV_USER, + NULL, &sdt_pops, NULL, (dtrace_provider_id_t *) &prov->id)); } -/*ARGSUSED*/ static void -sdt_resume(void *arg, dtrace_id_t id, void *parg) +sdt_load(void *dummy) { - sdt_probe_t *sdt = parg; - modctl_t *ctl = sdt->sdp_ctl; + /* Create the /dev/dtrace/sdt entry. */ + sdt_cdev = make_dev(&sdt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, + "dtrace/sdt"); - ASSERT(ctl->nenabled > 0); + sdt_probe_func = dtrace_probe; - if ((ctl->loadcnt != sdt->sdp_loadcnt)) - return; - - for (; sdt != NULL; sdt = sdt->sdp_next) - *sdt->sdp_patchpoint = sdt->sdp_patchval; + (void) sdt_provider_listall(sdt_provider_reg_callback, NULL); } -static void -sdt_load(void *dummy) +static int +sdt_provider_unreg_callback(struct sdt_provider *prov, void *arg __unused) { - 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); - - 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); - } - } + return (dtrace_unregister(prov->id)); } - static int sdt_unload() { int error = 0; - sdt_provider_t *prov; - - /* De-register the invalid opcode handler. */ - dtrace_invop_remove(sdt_invop); + + sdt_probe_func = sdt_probe_stub; - /* 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; - } - } + (void) sdt_provider_listall(sdt_provider_unreg_callback, NULL); - /* Free the probe table. */ - free(sdt_probetab, M_SDT); - sdt_probetab = NULL; - sdt_probetab_mask = 0; - destroy_dev(sdt_cdev); return (error); @@ -432,13 +192,9 @@ switch (type) { case MOD_LOAD: - /* Create the /dev/dtrace/sdt entry. */ - sdt_cdev = make_dev(&sdt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, - "dtrace/sdt"); break; case MOD_UNLOAD: - error = sdt_unload(); break; case MOD_SHUTDOWN: @@ -460,8 +216,10 @@ return (0); } -SYSINIT(sdt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_load, NULL) +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);