diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c index dd4ec77..8de926d 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c +++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c @@ -8098,19 +8098,6 @@ dtrace_probe_description(const dtrace_probe_t *prp, dtrace_probedesc_t *pdp) (void) strncpy(pdp->dtpd_name, prp->dtpr_name, DTRACE_NAMELEN - 1); } -#if !defined(sun) -static int -dtrace_probe_provide_cb(linker_file_t lf, void *arg) -{ - dtrace_provider_t *prv = (dtrace_provider_t *) arg; - - prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, lf); - - return(0); -} -#endif - - /* * Called to indicate that a probe -- or probes -- should be provided by a * specfied provider. If the specified description is NULL, the provider will @@ -8166,8 +8153,6 @@ dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv) } while ((ctl = ctl->mod_next) != &modules); mutex_exit(&mod_lock); -#else - (void) linker_file_foreach(dtrace_probe_provide_cb, prv); #endif } while (all && (prv = prv->dtpv_next) != NULL); } @@ -15152,12 +15137,11 @@ dtrace_helpers_duplicate(proc_t *from, proc_t *to) dtrace_helper_provider_register(to, newhelp, NULL); } -#if defined(sun) /* * DTrace Hook Functions */ static void -dtrace_module_loaded(modctl_t *ctl) +dtrace_module_loaded(struct linker_file *lf) { dtrace_provider_t *prv; @@ -15166,14 +15150,16 @@ dtrace_module_loaded(modctl_t *ctl) mutex_enter(&mod_lock); #endif +#if defined(sun) ASSERT(ctl->mod_busy); +#endif /* * We're going to call each providers per-module provide operation * specifying only this module. */ for (prv = dtrace_provider; prv != NULL; prv = prv->dtpv_next) - prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, ctl); + prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, lf); #if defined(sun) mutex_exit(&mod_lock); @@ -15213,13 +15199,26 @@ dtrace_module_loaded(modctl_t *ctl) delay(1); } -static void -dtrace_module_unloaded(modctl_t *ctl) +static int +dtrace_module_unloaded(struct linker_file *lf) { dtrace_probe_t template, *probe, *first, *next; dtrace_provider_t *prov; +#if !defined(sun) + char modname[DTRACE_MODNAMELEN]; + size_t len; +#endif +#if defined(sun) template.dtpr_mod = ctl->mod_modname; +#else + /* Handle the fact that lf->filename may end in ".ko". */ + strlcpy(modname, lf->filename, sizeof(modname)); + len = strlen(lf->filename); + if (len > 3 && strcmp(modname + len - 3, ".ko") == 0) + modname[len - 3] = '\0'; + template.dtpr_mod = modname; +#endif mutex_enter(&dtrace_provider_lock); #if defined(sun) @@ -15227,6 +15226,15 @@ dtrace_module_unloaded(modctl_t *ctl) #endif mutex_enter(&dtrace_lock); +#if !defined(sun) + if (lf->nenabled > 0) { + /* Don't allow unloads if a probe is enabled. */ + mutex_exit(&dtrace_provider_lock); + mutex_exit(&dtrace_lock); + return (1); + } +#endif + if (dtrace_bymod == NULL) { /* * The DTrace module is loaded (obviously) but not attached; @@ -15237,7 +15245,7 @@ dtrace_module_unloaded(modctl_t *ctl) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_lock); - return; + return (0); } for (probe = first = dtrace_hash_lookup(dtrace_bymod, &template); @@ -15261,10 +15269,10 @@ dtrace_module_unloaded(modctl_t *ctl) */ if (dtrace_err_verbose) { cmn_err(CE_WARN, "unloaded module '%s' had " - "enabled probes", ctl->mod_modname); + "enabled probes", modname); } - return; + return (1); } } @@ -15304,7 +15312,11 @@ dtrace_module_unloaded(modctl_t *ctl) kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1); kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1); kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1); +#if defined(sun) vmem_free(dtrace_arena, (void *)(uintptr_t)probe->dtpr_id, 1); +#else + free_unr(dtrace_arena, probe->dtpr_id); +#endif kmem_free(probe, sizeof (dtrace_probe_t)); } @@ -15313,8 +15325,11 @@ dtrace_module_unloaded(modctl_t *ctl) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); + + return (0); } +#if defined(sun) static void dtrace_suspend(void) { diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h index 4595c2e..c008791 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h @@ -2312,8 +2312,8 @@ extern void dtrace_membar_producer(void); extern void dtrace_membar_consumer(void); extern void (*dtrace_cpu_init)(processorid_t); -extern void (*dtrace_modload)(modctl_t *); -extern void (*dtrace_modunload)(modctl_t *); +extern void (*dtrace_modload)(struct linker_file *); +extern int (*dtrace_modunload)(struct linker_file *); extern void (*dtrace_helpers_cleanup)(void); extern void (*dtrace_helpers_fork)(proc_t *parent, proc_t *child); extern void (*dtrace_cpustart_init)(void); diff --git a/sys/cddl/dev/dtrace/dtrace_load.c b/sys/cddl/dev/dtrace/dtrace_load.c index 672945c..ec28a65 100644 --- a/sys/cddl/dev/dtrace/dtrace_load.c +++ b/sys/cddl/dev/dtrace/dtrace_load.c @@ -56,6 +56,10 @@ dtrace_load(void *dummy) /* Hang our hook for exceptions. */ dtrace_invop_init(); + /* Register callbacks for module load and unload events. */ + dtrace_modload = dtrace_module_loaded; + dtrace_modunload = dtrace_module_unloaded; + /* * Initialise the mutexes without 'witness' because the dtrace * code is mostly written to wait for memory. To have the diff --git a/sys/cddl/dev/dtrace/dtrace_unload.c b/sys/cddl/dev/dtrace/dtrace_unload.c index 2ebb52d..f432a83 100644 --- a/sys/cddl/dev/dtrace/dtrace_unload.c +++ b/sys/cddl/dev/dtrace/dtrace_unload.c @@ -67,6 +67,8 @@ dtrace_unload() } dtrace_provider = NULL; + dtrace_modload = NULL; + dtrace_modunload = NULL; if ((state = dtrace_anon_grab()) != NULL) { /* diff --git a/sys/cddl/dev/fbt/fbt.c b/sys/cddl/dev/fbt/fbt.c index b828163..7b278f7 100644 --- a/sys/cddl/dev/fbt/fbt.c +++ b/sys/cddl/dev/fbt/fbt.c @@ -1335,6 +1335,15 @@ fbt_getargdesc(void *arg __unused, dtrace_id_t id __unused, void *parg, dtrace_a return; } +static int +fbt_linker_file_cb(linker_file_t lf, void *arg) +{ + + fbt_provide_module(arg, lf); + + return (0); +} + static void fbt_load(void *dummy) { @@ -1359,8 +1368,10 @@ fbt_load(void *dummy) if (dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_USER, NULL, &fbt_pops, NULL, &fbt_id) != 0) return; -} + /* Create probes for the kernel and already-loaded modules. */ + linker_file_foreach(fbt_linker_file_cb, NULL); +} static int fbt_unload() diff --git a/sys/cddl/dev/sdt/sdt.c b/sys/cddl/dev/sdt/sdt.c index 96ab550..ac0e99b 100644 --- a/sys/cddl/dev/sdt/sdt.c +++ b/sys/cddl/dev/sdt/sdt.c @@ -19,41 +19,49 @@ * CDDL HEADER END * * Portions Copyright 2006-2008 John Birrell jb@freebsd.org + * Copyright (c) 2013 Mark Johnston . All rights reserved. * * $FreeBSD$ * */ -#ifndef KDTRACE_HOOKS -#define KDTRACE_HOOKS -#endif +#include "opt_kdtrace.h" #include #include #include + #include #include #include -#include #include +#include +#include +#include #include #include - -#include +#include #include -#define SDT_ADDR2NDX(addr) (((uintptr_t)(addr)) >> 4) +#include +#include -static d_open_t sdt_open; -static int sdt_unload(void); +/* DTrace methods. */ 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 d_open_t sdt_open; static void sdt_load(void *); -static int sdt_provider_unreg_callback(struct sdt_provider *prov, - void *arg); +static int sdt_unload(void *); +static void sdt_create_provider(struct sdt_provider *); +static void sdt_create_probe(struct sdt_probe *); +static void sdt_modload(struct linker_file *); +static int sdt_modunload(struct linker_file *); + +static MALLOC_DEFINE(M_SDT, "SDT", "DTrace SDT providers"); static struct cdevsw sdt_cdevsw = { .d_version = D_VERSION, @@ -79,141 +87,253 @@ static dtrace_pops_t sdt_pops = { sdt_getargdesc, NULL, NULL, - sdt_destroy + 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 */ - } +static struct cdev *sdt_cdev; - return (0); -} +static TAILQ_HEAD(, sdt_provider) sdt_prov_list; static void -sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc) +sdt_create_provider(struct sdt_provider *prov) { - struct sdt_probe *probe = parg; + struct sdt_provider *curr, *newprov; - if (desc->dtargd_ndx < probe->n_args) - (void) (sdt_argtype_listall(probe, sdt_argtype_callback, desc)); - else - desc->dtargd_ndx = DTRACE_ARGNONE; + TAILQ_FOREACH(curr, &sdt_prov_list, prov_entry) + if (strcmp(prov->name, curr->name) == 0) { + /* The provider has already been defined. */ + curr->sdt_refs++; + return; + } + + /* + * Make a copy of prov so that we don't lose fields if its module is + * unloaded but the provider isn't destroyed. This could happen with + * a provider that spans multiple modules. + */ + newprov = malloc(sizeof(*newprov), M_SDT, M_WAITOK | M_ZERO); + newprov->name = strdup(prov->name, M_SDT); + prov->sdt_refs = newprov->sdt_refs = 1; + TAILQ_INIT(&newprov->probe_list); + + TAILQ_INSERT_TAIL(&sdt_prov_list, newprov, prov_entry); - return; + (void)dtrace_register(newprov->name, &sdt_attr, DTRACE_PRIV_USER, NULL, + &sdt_pops, NULL, (dtrace_provider_id_t *)&newprov->id); + prov->id = newprov->id; } -static int -sdt_probe_callback(struct sdt_probe *probe, void *arg __unused) +static void +sdt_create_probe(struct sdt_probe *probe) { - struct sdt_provider *prov = probe->prov; - char mod[64]; - char func[64]; - char name[64]; + struct sdt_provider *prov; + char mod[DTRACE_MODNAMELEN]; + char func[DTRACE_FUNCNAMELEN]; + char name[DTRACE_NAMELEN]; + size_t len; + + TAILQ_FOREACH(prov, &sdt_prov_list, prov_entry) + if (strcmp(prov->name, probe->prov->name) == 0) + break; + + KASSERT(prov != NULL, ("probe defined without a provider")); + + /* If no module name was specified, use the module filename. */ + if (*probe->mod == 0) { + len = strlcpy(mod, probe->sdtp_lf->filename, sizeof(mod)); + if (len > 3 && strcmp(mod + len - 3, ".ko") == 0) + mod[len - 3] = '\0'; + } else + strlcpy(mod, probe->mod, sizeof(mod)); /* * 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. + * in the C compiler, 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); + if (dtrace_probe_lookup(prov->id, mod, func, name) != DTRACE_IDNONE) + return; - (void) dtrace_probe_create(prov->id, probe->mod, probe->func, - probe->name, 1, probe); + TAILQ_INSERT_TAIL(&prov->probe_list, probe, probe_entry); - return (0); + (void)dtrace_probe_create(prov->id, mod, func, name, 1, probe); } -static int -sdt_provider_entry(struct sdt_provider *prov, void *arg) +/* Probes are created through the SDT module load/unload hook. */ +static void +sdt_provide_probes(void *arg, dtrace_probedesc_t *desc) { - return (sdt_probe_listall(prov, sdt_probe_callback, NULL)); } static void -sdt_provide_probes(void *arg, dtrace_probedesc_t *desc) +sdt_enable(void *arg __unused, dtrace_id_t id, void *parg) { - if (desc != NULL) - return; + struct sdt_probe *probe = parg; - (void) sdt_provider_listall(sdt_provider_entry, NULL); + probe->id = id; + probe->sdtp_lf->nenabled++; } static void -sdt_destroy(void *arg, dtrace_id_t id, void *parg) +sdt_disable(void *arg __unused, dtrace_id_t id, void *parg) { - /* Nothing to do here. */ + struct sdt_probe *probe = parg; + + probe->id = 0; + probe->sdtp_lf->nenabled--; } static void -sdt_enable(void *arg, dtrace_id_t id, void *parg) +sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc) { + struct sdt_argtype *argtype; struct sdt_probe *probe = parg; - probe->id = id; + if (desc->dtargd_ndx < probe->n_args) { + TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) { + if (desc->dtargd_ndx == argtype->ndx) { + /* XXX */ + desc->dtargd_mapping = desc->dtargd_ndx; + strlcpy(desc->dtargd_native, argtype->type, + sizeof(desc->dtargd_native)); + desc->dtargd_xlate[0] = '\0'; /* XXX */ + } + } + } else + desc->dtargd_ndx = DTRACE_ARGNONE; } static void -sdt_disable(void *arg, dtrace_id_t id, void *parg) +sdt_destroy(void *arg, dtrace_id_t id, void *parg) { - struct sdt_probe *probe = parg; + struct sdt_probe *probe; - probe->id = 0; + probe = parg; + TAILQ_REMOVE(&probe->prov->probe_list, probe, probe_entry); +} + +/* + * Called from the kernel linker when a module is loaded, before + * dtrace_module_loaded() is called. This is done so that it's possible to + * register new providers when modules are loaded. We cannot do this in the + * provide_module method since it's called with the provider lock held + * and dtrace_register() will try to acquire it again. + */ +static void +sdt_modload(struct linker_file *lf) +{ + struct sdt_provider **prov, **begin, **end; + struct sdt_probe **probe, **p_begin, **p_end; + struct sdt_argtype **argtype, **a_begin, **a_end; + + if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end, NULL)) + return; + for (prov = begin; prov < end; prov++) + sdt_create_provider(*prov); + + if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end, + NULL)) + return; + for (probe = p_begin; probe < p_end; probe++) { + (*probe)->sdtp_lf = lf; + sdt_create_probe(*probe); + TAILQ_INIT(&(*probe)->argtype_list); + } + + if (linker_file_lookup_set(lf, "sdt_argtypes_set", &a_begin, &a_end, + NULL)) + return; + for (argtype = a_begin; argtype < a_end; argtype++) { + (*argtype)->probe->n_args++; + TAILQ_INSERT_TAIL(&(*argtype)->probe->argtype_list, *argtype, + argtype_entry); + } } static int -sdt_provider_reg_callback(struct sdt_provider *prov, void *arg __unused) +sdt_modunload(struct linker_file *lf) { - return (dtrace_register(prov->name, &sdt_attr, DTRACE_PRIV_USER, - NULL, &sdt_pops, NULL, (dtrace_provider_id_t *) &prov->id)); + struct sdt_provider *prov, **curr, **begin, **end, *tmp; + + if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end, NULL)) + /* No DTrace providers are declared in this module. */ + return (0); + + /* + * Go through all the providers declared in this module and unregister + * any that aren't declared in another loaded module. + */ + for (curr = begin; curr < end; curr++) { + TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) { + if (strcmp(prov->name, (*curr)->name) == 0) { + if (prov->sdt_refs == 1) { + TAILQ_REMOVE(&sdt_prov_list, prov, + prov_entry); + dtrace_unregister(prov->id); + free(prov->name, M_SDT); + free(prov, M_SDT); + } else + prov->sdt_refs--; + break; + } + } + } + + return (0); +} + +static int +sdt_linker_file_cb(linker_file_t lf, void *arg __unused) +{ + + sdt_modload(lf); + + return (0); } static void -sdt_load(void *dummy) +sdt_load(void *arg __unused) { + + TAILQ_INIT(&sdt_prov_list); + /* 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); -} + dtrace_sdt_modload = sdt_modload; + dtrace_sdt_modunload = sdt_modunload; -static int -sdt_provider_unreg_callback(struct sdt_provider *prov, void *arg __unused) -{ - return (dtrace_unregister(prov->id)); + /* Pick up probes from the kernel and already-loaded modules. */ + linker_file_foreach(sdt_linker_file_cb, NULL); } static int -sdt_unload() +sdt_unload(void *arg __unused) { - int error = 0; + struct sdt_provider *prov, *tmp; + + dtrace_sdt_modload = NULL; + dtrace_sdt_modunload = NULL; sdt_probe_func = sdt_probe_stub; - sdt_deregister_callbacks(); - + TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) { + TAILQ_REMOVE(&sdt_prov_list, prov, prov_entry); + dtrace_unregister(prov->id); + free(prov->name, M_SDT); + free(prov, M_SDT); + } + destroy_dev(sdt_cdev); - return (error); + return (0); } /* ARGSUSED */ @@ -235,7 +355,6 @@ sdt_modevent(module_t mod __unused, int type, void *data __unused) default: error = EOPNOTSUPP; break; - } return (error); @@ -243,8 +362,10 @@ sdt_modevent(module_t mod __unused, int type, void *data __unused) /* ARGSUSED */ static int -sdt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused) +sdt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, + struct thread *td __unused) { + return (0); } diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c index b3ab4df..3ece807 100644 --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -28,8 +28,9 @@ __FBSDID("$FreeBSD$"); #include "opt_ddb.h" -#include "opt_kld.h" #include "opt_hwpmc_hooks.h" +#include "opt_kdtrace.h" +#include "opt_kld.h" #include #include @@ -63,6 +64,14 @@ __FBSDID("$FreeBSD$"); #include #endif +#ifdef KDTRACE_HOOKS +#include +dtrace_modloadevt_t dtrace_modload; +dtrace_modunloadevt_t dtrace_modunload; +dtrace_modloadevt_t dtrace_sdt_modload; +dtrace_modunloadevt_t dtrace_sdt_modunload; +#endif + #ifdef KLD_DEBUG int kld_debug = 0; SYSCTL_INT(_debug, OID_AUTO, kld_debug, CTLFLAG_RW | CTLFLAG_TUN, @@ -582,8 +591,6 @@ linker_make_file(const char *pathname, linker_class_t lc) lf->ndeps = 0; lf->deps = NULL; lf->loadcnt = ++loadcnt; - lf->sdt_probes = NULL; - lf->sdt_nprobes = 0; STAILQ_INIT(&lf->common); TAILQ_INIT(&lf->modules); TAILQ_INSERT_TAIL(&linker_files, lf, link); @@ -1046,6 +1053,13 @@ kern_kldload(struct thread *td, const char *file, int *fileid) lf->userrefs++; if (fileid != NULL) *fileid = lf->id; +#ifdef KDTRACE_HOOKS + /* Make it possible for the SDT provider to register new providers. */ + if (dtrace_sdt_modload != NULL) + dtrace_sdt_modload(lf); + if (dtrace_modload != NULL) + dtrace_modload(lf); +#endif #ifdef HWPMC_HOOKS KLD_DOWNGRADE(); pkm.pm_file = lf->filename; @@ -1101,19 +1115,23 @@ kern_kldunload(struct thread *td, int fileid, int flags) if (lf) { KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); - /* Check if there are DTrace probes enabled on this file. */ - if (lf->nenabled > 0) { - printf("kldunload: attempt to unload file that has" - " DTrace probes enabled\n"); - error = EBUSY; - } else if (lf->userrefs == 0) { + if (lf->userrefs == 0) { /* * XXX: maybe LINKER_UNLOAD_FORCE should override ? */ printf("kldunload: attempt to unload file that was" " loaded by the kernel\n"); error = EBUSY; - } else { + } +#ifdef KDTRACE_HOOKS + else if ((dtrace_modunload && dtrace_modunload(lf) != 0) || + (dtrace_sdt_modunload && dtrace_sdt_modunload(lf) != 0)) { + printf("kldunload: attempt to unload file that has" + " DTrace probes enabled\n"); + error = EBUSY; + } +#endif + else { #ifdef HWPMC_HOOKS /* Save data needed by hwpmc(4) before unloading. */ pkm.pm_address = (uintptr_t) lf->address; diff --git a/sys/kern/kern_sdt.c b/sys/kern/kern_sdt.c index ee4adde..c8e1940 100644 --- a/sys/kern/kern_sdt.c +++ b/sys/kern/kern_sdt.c @@ -23,317 +23,29 @@ * SUCH DAMAGE. * * $FreeBSD$ - * - * Backend for the Statically Defined Tracing (SDT) kernel support. This is - * required to allow a module to load even though DTrace kernel support may - * not be present. A module may be built with SDT probes in it which are - * registered and deregistered via SYSINIT/SYSUNINIT. - * */ #include "opt_kdtrace.h" -#include #include #include -#include -#include -#include -#include -#include #include /* - * This is the list of statically defined tracing providers. - */ -static TAILQ_HEAD(sdt_provider_list_head, sdt_provider) sdt_provider_list; - -/* - * Mutex to serialise access to the SDT provider list. - */ -static struct sx sdt_sx; - -/* - * Hook for the DTrace probe function. The 'sdt' provider will set this - * to dtrace_probe when it loads. + * Hook for the DTrace probe function. The SDT provider will set this to + * dtrace_probe() when it loads. */ sdt_probe_func_t sdt_probe_func = sdt_probe_stub; -static sdt_provider_listall_func_t sdt_provider_register_func = NULL; -static sdt_provider_listall_func_t sdt_provider_deregister_func = NULL; -static sdt_probe_listall_func_t sdt_probe_register_func = NULL; - -static void *sdt_provider_register_arg; -static void *sdt_provider_deregister_arg; -static void *sdt_probe_register_arg; - -static int sdt_provider_listall_locked(sdt_provider_listall_func_t, void *); - /* * This is a stub for probe calls in case kernel DTrace support isn't - * compiled in. It should never get called because there is no DTrace - * support to enable it. + * enabled. It should never get called because there is no DTrace support + * to enable it. */ void sdt_probe_stub(uint32_t id, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4) { - printf("sdt_probe_stub: Why did this get called?\n"); -} - -/* - * Called from SYSINIT to register a provider. - */ -void -sdt_provider_register(void *arg) -{ - struct sdt_provider *prov = arg; - - sx_xlock(&sdt_sx); - - TAILQ_INSERT_TAIL(&sdt_provider_list, prov, prov_entry); - - TAILQ_INIT(&prov->probe_list); - - if (sdt_provider_register_func != NULL) - sdt_provider_register_func(prov, sdt_provider_register_arg); - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSUNINIT to de-register a provider. - */ -void -sdt_provider_deregister(void *arg) -{ - struct sdt_provider *prov = arg; - - sx_xlock(&sdt_sx); - - TAILQ_REMOVE(&sdt_provider_list, prov, prov_entry); - - if (sdt_provider_deregister_func != NULL) - sdt_provider_deregister_func(prov, sdt_provider_deregister_arg); - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSINIT to register a statically defined trace probe. - */ -void -sdt_probe_register(void *arg) -{ - struct sdt_probe *probe = arg; - - /* - * Check the reference structure version. Only version 1 is - * supported at the moment. - */ - if (probe->version != sizeof(struct sdt_probe)) { - printf("%s:%s:%s has version %d when %d required\n", probe->mod, probe->func, probe->name, probe->version, (int) sizeof(struct sdt_probe)); - return; - } - - sx_xlock(&sdt_sx); - - TAILQ_INSERT_TAIL(&probe->prov->probe_list, probe, probe_entry); - - TAILQ_INIT(&probe->argtype_list); - - probe->state = SDT_INIT; - - if (sdt_probe_register_func != NULL) - sdt_probe_register_func(probe, sdt_provider_register_arg); - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSUNINIT to de-register a statically defined trace probe. - */ -void -sdt_probe_deregister(void *arg) -{ - struct sdt_probe *probe = arg; - - sx_xlock(&sdt_sx); - - if (probe->state == SDT_INIT) { - TAILQ_REMOVE(&probe->prov->probe_list, probe, probe_entry); - probe->state = SDT_UNINIT; - } - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSINIT to register a statically defined trace probe argument. - */ -void -sdt_argtype_register(void *arg) -{ - struct sdt_argtype *argtype = arg; - - sx_xlock(&sdt_sx); - - TAILQ_INSERT_TAIL(&argtype->probe->argtype_list, argtype, argtype_entry); - - argtype->probe->n_args++; - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSUNINIT to de-register a statically defined trace probe argument. - */ -void -sdt_argtype_deregister(void *arg) -{ - struct sdt_argtype *argtype = arg; - - sx_xlock(&sdt_sx); - - TAILQ_REMOVE(&argtype->probe->argtype_list, argtype, argtype_entry); - - sx_xunlock(&sdt_sx); -} - -static void -sdt_init(void *arg) -{ - sx_init_flags(&sdt_sx, "Statically Defined Tracing", SX_NOWITNESS); - - TAILQ_INIT(&sdt_provider_list); -} - -SYSINIT(sdt, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_init, NULL); - -static void -sdt_uninit(void *arg) -{ - sx_destroy(&sdt_sx); -} - -SYSUNINIT(sdt, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_uninit, NULL); - -/* - * List statically defined tracing providers. - */ -int -sdt_provider_listall(sdt_provider_listall_func_t callback_func, void *arg) -{ - int error; - - sx_xlock(&sdt_sx); - error = sdt_provider_listall_locked(callback_func, arg); - sx_xunlock(&sdt_sx); - - return (error); -} - -static int -sdt_provider_listall_locked(sdt_provider_listall_func_t callback_func, - void *arg) -{ - int error = 0; - struct sdt_provider *prov; - - sx_assert(&sdt_sx, SX_XLOCKED); - - TAILQ_FOREACH(prov, &sdt_provider_list, prov_entry) { - if ((error = callback_func(prov, arg)) != 0) - break; - } - - return (error); -} - -/* - * List statically defined tracing probes. - */ -int -sdt_probe_listall(struct sdt_provider *prov, - sdt_probe_listall_func_t callback_func,void *arg) -{ - int error = 0; - int locked; - struct sdt_probe *probe; - - locked = sx_xlocked(&sdt_sx); - if (!locked) - sx_xlock(&sdt_sx); - - TAILQ_FOREACH(probe, &prov->probe_list, probe_entry) { - if ((error = callback_func(probe, arg)) != 0) - break; - } - - if (!locked) - sx_xunlock(&sdt_sx); - - return (error); -} - -/* - * List statically defined tracing probe arguments. - */ -int -sdt_argtype_listall(struct sdt_probe *probe, - sdt_argtype_listall_func_t callback_func,void *arg) -{ - int error = 0; - int locked; - struct sdt_argtype *argtype; - - locked = sx_xlocked(&sdt_sx); - if (!locked) - sx_xlock(&sdt_sx); - TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) { - if ((error = callback_func(argtype, arg)) != 0) - break; - } - - if (!locked) - sx_xunlock(&sdt_sx); - - return (error); -} - -void sdt_register_callbacks(sdt_provider_listall_func_t register_prov, - void *reg_prov_arg, sdt_provider_listall_func_t deregister_prov, - void *dereg_prov_arg, sdt_probe_listall_func_t register_probe, - void * reg_probe_arg) -{ - - sx_xlock(&sdt_sx); - sdt_provider_register_func = register_prov; - sdt_provider_deregister_func = deregister_prov; - sdt_probe_register_func = register_probe; - - sdt_provider_register_arg = reg_prov_arg; - sdt_provider_deregister_arg = dereg_prov_arg; - sdt_probe_register_arg = reg_probe_arg; - - sdt_provider_listall_locked(register_prov, reg_prov_arg); - sx_xunlock(&sdt_sx); -} - -void sdt_deregister_callbacks(void) -{ - - sx_xlock(&sdt_sx); - sdt_provider_listall_locked(sdt_provider_deregister_func, - sdt_provider_deregister_arg); - - sdt_provider_register_func = NULL; - sdt_provider_deregister_func = NULL; - sdt_probe_register_func = NULL; - - sdt_provider_register_arg = NULL; - sdt_provider_deregister_arg = NULL; - sdt_probe_register_arg = NULL; - sx_xunlock(&sdt_sx); + printf("sdt_probe_stub: Why did this get called?\n"); } diff --git a/sys/sys/dtrace_bsd.h b/sys/sys/dtrace_bsd.h index 2198b26..c1df5f5 100644 --- a/sys/sys/dtrace_bsd.h +++ b/sys/sys/dtrace_bsd.h @@ -40,6 +40,7 @@ struct vnode; struct reg; struct devstat; struct bio; +struct linker_file; /* * Cyclic clock function type definition used to hook the cyclic @@ -100,6 +101,21 @@ extern dtrace_execexit_func_t dtrace_fasttrap_exit; typedef void (*dtrace_malloc_probe_func_t)(u_int32_t, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); +/* Module load and unload handlers. */ +typedef void (*dtrace_modloadevt_t)(struct linker_file *); +typedef int (*dtrace_modunloadevt_t)(struct linker_file *); +extern dtrace_modloadevt_t dtrace_modload; +extern dtrace_modunloadevt_t dtrace_modunload; +/* + * We have a separate handler for SDT so that the SDT provider can register new + * providers when a module is loaded. This cannot be done from SDT's + * module_provider method because of locking problems associated with calling + * back into the DTrace framework. Similarly, we cannot deregister a provider + * from say, the probe destroy method, so we use our own handler. + */ +extern dtrace_modloadevt_t dtrace_sdt_modload; +extern dtrace_modunloadevt_t dtrace_sdt_modunload; + extern dtrace_malloc_probe_func_t dtrace_malloc_probe; /* dtnfsclient NFSv[34] access cache provider hooks. */ diff --git a/sys/sys/linker.h b/sys/sys/linker.h index 8ea140f..b2942f2 100644 --- a/sys/sys/linker.h +++ b/sys/sys/linker.h @@ -92,10 +92,6 @@ struct linker_file { */ int nenabled; /* number of enabled probes. */ int fbt_nentries; /* number of fbt entries created. */ - void *sdt_probes; - int sdt_nentries; - size_t sdt_nprobes; - size_t sdt_size; }; /* diff --git a/sys/sys/param.h b/sys/sys/param.h index 72bacce..b33049e 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1000035 /* Master, propagated to newvers */ +#define __FreeBSD_version 1000036 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/sys/sys/sdt.h b/sys/sys/sdt.h index 1ea7abf..b4ef59f 100644 --- a/sys/sys/sdt.h +++ b/sys/sys/sdt.h @@ -1,5 +1,6 @@ /*- * Copyright 2006-2008 John Birrell + * Copyright 2013 Mark Johnston * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -77,6 +78,9 @@ #else /* _KERNEL */ +#include +#include + #ifndef KDTRACE_HOOKS #define SDT_PROVIDER_DEFINE(prov) @@ -108,85 +112,24 @@ #else -/* - * This type definition must match that of dtrace_probe. It is defined this - * way to avoid having to rely on CDDL code. - */ -typedef void (*sdt_probe_func_t)(u_int32_t, uintptr_t arg0, uintptr_t arg1, - uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); - -/* - * The hook for the probe function. See kern_sdt.c which defaults this to - * it's own stub. The 'sdt' provider will set it to dtrace_probe when it - * loads. - */ -extern sdt_probe_func_t sdt_probe_func; - -typedef enum { - SDT_UNINIT = 1, - SDT_INIT, -} sdt_state_t; - -struct sdt_probe; -struct sdt_provider; - -struct sdt_argtype { - int ndx; /* Argument index. */ - const char *type; /* Argument type string. */ - TAILQ_ENTRY(sdt_argtype) - argtype_entry; /* Argument type list entry. */ - struct sdt_probe - *probe; /* Ptr to the probe structure. */ -}; - -struct sdt_probe { - int version; /* Set to sizeof(struct sdt_ref). */ - sdt_state_t state; - struct sdt_provider - *prov; /* Ptr to the provider structure. */ - TAILQ_ENTRY(sdt_probe) - probe_entry; /* SDT probe list entry. */ - TAILQ_HEAD(argtype_list_head, sdt_argtype) argtype_list; - const char *mod; - const char *func; - const char *name; - id_t id; /* DTrace probe ID. */ - int n_args; /* Number of arguments. */ -}; - -struct sdt_provider { - const char *name; /* Provider name. */ - TAILQ_ENTRY(sdt_provider) - prov_entry; /* SDT provider list entry. */ - TAILQ_HEAD(probe_list_head, sdt_probe) probe_list; - uintptr_t id; /* DTrace provider ID. */ -}; - #define SDT_PROVIDER_DEFINE(prov) \ struct sdt_provider sdt_provider_##prov[1] = { \ - { #prov, { NULL, NULL }, { NULL, NULL } } \ + { #prov, { NULL, NULL }, { NULL, NULL }, 0, 0 } \ }; \ - SYSINIT(sdt_provider_##prov##_init, SI_SUB_KDTRACE, \ - SI_ORDER_SECOND, sdt_provider_register, \ - sdt_provider_##prov ); \ - SYSUNINIT(sdt_provider_##prov##_uninit, SI_SUB_KDTRACE, \ - SI_ORDER_SECOND, sdt_provider_deregister, \ - sdt_provider_##prov ) + SET_DECLARE(sdt_providers_set, struct sdt_provider); \ + DATA_SET(sdt_providers_set, sdt_provider_##prov); #define SDT_PROVIDER_DECLARE(prov) \ extern struct sdt_provider sdt_provider_##prov[1] #define SDT_PROBE_DEFINE(prov, mod, func, name, sname) \ struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1] = { \ - { sizeof(struct sdt_probe), 0, sdt_provider_##prov, \ - { NULL, NULL }, { NULL, NULL }, #mod, #func, #sname, 0, 0 } \ + { sizeof(struct sdt_probe), sdt_provider_##prov, \ + { NULL, NULL }, { NULL, NULL }, #mod, #func, #sname, 0, 0, \ + NULL } \ }; \ - SYSINIT(sdt_##prov##_##mod##_##func##_##name##_init, SI_SUB_KDTRACE, \ - SI_ORDER_SECOND + 1, sdt_probe_register, \ - sdt_##prov##_##mod##_##func##_##name ); \ - SYSUNINIT(sdt_##prov##_##mod##_##func##_##name##_uninit, \ - SI_SUB_KDTRACE, SI_ORDER_SECOND + 1, sdt_probe_deregister, \ - sdt_##prov##_##mod##_##func##_##name ) + SET_DECLARE(sdt_probes_set, struct sdt_probe); \ + DATA_SET(sdt_probes_set, sdt_##prov##_##mod##_##func##_##name); #define SDT_PROBE_DECLARE(prov, mod, func, name) \ extern struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1] @@ -203,12 +146,8 @@ struct sdt_provider { = { { num, type, { NULL, NULL }, \ sdt_##prov##_##mod##_##func##_##name } \ }; \ - SYSINIT(sdt_##prov##_##mod##_##func##_##name##num##_init, \ - SI_SUB_KDTRACE, SI_ORDER_SECOND + 2, sdt_argtype_register, \ - sdt_##prov##_##mod##_##func##_##name##num ); \ - SYSUNINIT(sdt_##prov##_##mod##_##func##_##name##num##_uninit, \ - SI_SUB_KDTRACE, SI_ORDER_SECOND + 2, sdt_argtype_deregister, \ - sdt_##prov##_##mod##_##func##_##name##num ) + SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); \ + DATA_SET(sdt_argtypes_set, sdt_##prov##_##mod##_##func##_##name##num); #define SDT_PROBE_DEFINE0(prov, mod, func, name, sname) \ SDT_PROBE_DEFINE(prov, mod, func, name, sname) @@ -298,28 +237,59 @@ struct sdt_provider { (uintptr_t)arg6); \ } while (0) -typedef int (*sdt_argtype_listall_func_t)(struct sdt_argtype *, void *); -typedef int (*sdt_probe_listall_func_t)(struct sdt_probe *, void *); -typedef int (*sdt_provider_listall_func_t)(struct sdt_provider *, void *); - -void sdt_argtype_deregister(void *); -void sdt_argtype_register(void *); -void sdt_probe_deregister(void *); -void sdt_probe_register(void *); -void sdt_provider_deregister(void *); -void sdt_provider_register(void *); -void sdt_probe_stub(u_int32_t, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, - uintptr_t arg3, uintptr_t arg4); -int sdt_argtype_listall(struct sdt_probe *, sdt_argtype_listall_func_t, void *); -int sdt_probe_listall(struct sdt_provider *, sdt_probe_listall_func_t, void *); -int sdt_provider_listall(sdt_provider_listall_func_t,void *); - -void sdt_register_callbacks(sdt_provider_listall_func_t, void *, - sdt_provider_listall_func_t, void *, sdt_probe_listall_func_t, void *); -void sdt_deregister_callbacks(void); - #endif /* KDTRACE_HOOKS */ +/* + * This type definition must match that of dtrace_probe. It is defined this + * way to avoid having to rely on CDDL code. + */ +typedef void (*sdt_probe_func_t)(uint32_t, uintptr_t arg0, uintptr_t arg1, + uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); + +/* + * The 'sdt' provider will set it to dtrace_probe when it loads. + */ +extern sdt_probe_func_t sdt_probe_func; + +struct sdt_probe; +struct sdt_provider; +struct linker_file; + +struct sdt_argtype { + int ndx; /* Argument index. */ + const char *type; /* Argument type string. */ + TAILQ_ENTRY(sdt_argtype) + argtype_entry; /* Argument type list entry. */ + struct sdt_probe + *probe; /* Ptr to the probe structure. */ +}; + +struct sdt_probe { + int version; /* Set to sizeof(struct sdt_probe). */ + struct sdt_provider *prov; /* Ptr to the provider structure. */ + TAILQ_ENTRY(sdt_probe) + probe_entry; /* SDT probe list entry. */ + TAILQ_HEAD(argtype_list_head, sdt_argtype) argtype_list; + const char *mod; + const char *func; + const char *name; + id_t id; /* DTrace probe ID. */ + int n_args; /* Number of arguments. */ + struct linker_file *sdtp_lf; /* Module in which we're defined. */ +}; + +struct sdt_provider { + char *name; /* Provider name. */ + TAILQ_ENTRY(sdt_provider) + prov_entry; /* SDT provider list entry. */ + TAILQ_HEAD(probe_list_head, sdt_probe) probe_list; + uintptr_t id; /* DTrace provider ID. */ + int sdt_refs; /* Number of module references. */ +}; + +void sdt_probe_stub(uint32_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); + #endif /* _KERNEL */ #endif /* _SYS_SDT_H */