diff options
author | sson <sson@FreeBSD.org> | 2009-05-26 20:28:22 +0000 |
---|---|---|
committer | sson <sson@FreeBSD.org> | 2009-05-26 20:28:22 +0000 |
commit | c0d5996eb6885340017fa87726a787b38dfa689f (patch) | |
tree | b7873baadff878509f8c7b930ddeeaa84074e546 /sys/cddl | |
parent | 8dd898d24297058d22cfd3efb4da99431e21f707 (diff) | |
download | FreeBSD-src-c0d5996eb6885340017fa87726a787b38dfa689f.zip FreeBSD-src-c0d5996eb6885340017fa87726a787b38dfa689f.tar.gz |
Add the OpenSolaris dtrace lockstat provider. The lockstat provider
adds probes for mutexes, reader/writer and shared/exclusive locks to
gather contention statistics and other locking information for
dtrace scripts, the lockstat(1M) command and other potential
consumers.
Reviewed by: attilio jhb jb
Approved by: gnn (mentor)
Diffstat (limited to 'sys/cddl')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c | 110 | ||||
-rw-r--r-- | sys/cddl/dev/lockstat/lockstat.c | 327 |
2 files changed, 385 insertions, 52 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c index 693fbbc..2e0990e 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c +++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c @@ -122,6 +122,7 @@ #include <sys/sysctl.h> #include <sys/lock.h> #include <sys/mutex.h> +#include <sys/rwlock.h> #include <sys/sx.h> #include <sys/dtrace_bsd.h> #include <netinet/in.h> @@ -3168,14 +3169,11 @@ dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs, uintptr_t rw; } r; #else + struct thread *lowner; union { - struct mtx *mi; - uintptr_t mx; - } m; - union { - struct sx *si; - uintptr_t sx; - } s; + struct lock_object *li; + uintptr_t lx; + } l; #endif switch (subr) { @@ -3272,75 +3270,83 @@ dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs, break; #else - /* - * XXX - The following code works because mutex, rwlocks, & sxlocks - * all have similar data structures in FreeBSD. This may not be - * good if someone changes one of the lock data structures. - * Ideally, it would be nice if all these shared a common lock - * object. - */ case DIF_SUBR_MUTEX_OWNED: - /* XXX - need to use dtrace_canload() and dtrace_loadptr() */ - m.mx = tupregs[0].dttk_value; - -#ifdef DOODAD - if (LO_CLASSINDEX(&(m.mi->lock_object)) < 2) { - regs[rd] = !(m.mi->mtx_lock & MTX_UNOWNED); - } else { - regs[rd] = !(m.mi->mtx_lock & SX_UNLOCKED); + if (!dtrace_canload(tupregs[0].dttk_value, + sizeof (struct lock_object), mstate, vstate)) { + regs[rd] = 0; + break; } -#endif + l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); + regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); break; case DIF_SUBR_MUTEX_OWNER: - /* XXX - need to use dtrace_canload() and dtrace_loadptr() */ - m.mx = tupregs[0].dttk_value; - - if (LO_CLASSINDEX(&(m.mi->lock_object)) < 2) { - regs[rd] = m.mi->mtx_lock & ~MTX_FLAGMASK; - } else { - if (!(m.mi->mtx_lock & SX_LOCK_SHARED)) - regs[rd] = SX_OWNER(m.mi->mtx_lock); - else - regs[rd] = 0; + if (!dtrace_canload(tupregs[0].dttk_value, + sizeof (struct lock_object), mstate, vstate)) { + regs[rd] = 0; + break; } + l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); + LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); + regs[rd] = (uintptr_t)lowner; break; case DIF_SUBR_MUTEX_TYPE_ADAPTIVE: - /* XXX - need to use dtrace_canload() and dtrace_loadptr() */ - m.mx = tupregs[0].dttk_value; - - regs[rd] = (LO_CLASSINDEX(&(m.mi->lock_object)) != 0); + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct mtx), + mstate, vstate)) { + regs[rd] = 0; + break; + } + l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); + /* XXX - should be only LC_SLEEPABLE? */ + regs[rd] = (LOCK_CLASS(l.li)->lc_flags & + (LC_SLEEPLOCK | LC_SLEEPABLE)) != 0; break; case DIF_SUBR_MUTEX_TYPE_SPIN: - /* XXX - need to use dtrace_canload() and dtrace_loadptr() */ - m.mx = tupregs[0].dttk_value; - - regs[rd] = (LO_CLASSINDEX(&(m.mi->lock_object)) == 0); + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct mtx), + mstate, vstate)) { + regs[rd] = 0; + break; + } + l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); + regs[rd] = (LOCK_CLASS(l.li)->lc_flags & LC_SPINLOCK) != 0; break; case DIF_SUBR_RW_READ_HELD: case DIF_SUBR_SX_SHARED_HELD: - /* XXX - need to use dtrace_canload() and dtrace_loadptr() */ - s.sx = tupregs[0].dttk_value; - regs[rd] = ((s.si->sx_lock & SX_LOCK_SHARED) && - (SX_OWNER(s.si->sx_lock) >> SX_SHARERS_SHIFT) != 0); + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), + mstate, vstate)) { + regs[rd] = 0; + break; + } + l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); + regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner) && + lowner == NULL; break; case DIF_SUBR_RW_WRITE_HELD: case DIF_SUBR_SX_EXCLUSIVE_HELD: - /* XXX - need to use dtrace_canload() and dtrace_loadptr() */ - s.sx = tupregs[0].dttk_value; - regs[rd] = (SX_OWNER(s.si->sx_lock) == (uintptr_t) curthread); + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), + mstate, vstate)) { + regs[rd] = 0; + break; + } + l.lx = dtrace_loadptr(tupregs[0].dttk_value); + LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); + regs[rd] = (lowner == curthread); break; case DIF_SUBR_RW_ISWRITER: case DIF_SUBR_SX_ISEXCLUSIVE: - /* XXX - need to use dtrace_canload() and dtrace_loadptr() */ - s.sx = tupregs[0].dttk_value; - regs[rd] = ((s.si->sx_lock & SX_LOCK_EXCLUSIVE_WAITERS) || - !(s.si->sx_lock & SX_LOCK_SHARED)); + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), + mstate, vstate)) { + regs[rd] = 0; + break; + } + l.lx = dtrace_loadptr(tupregs[0].dttk_value); + regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner) && + lowner != NULL; break; #endif /* ! defined(sun) */ diff --git a/sys/cddl/dev/lockstat/lockstat.c b/sys/cddl/dev/lockstat/lockstat.c new file mode 100644 index 0000000..d4de759 --- /dev/null +++ b/sys/cddl/dev/lockstat/lockstat.c @@ -0,0 +1,327 @@ +/* + * 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 (c) 2008-2009 Stacey Son <sson@FreeBSD.org> + * + * $FreeBSD$ + * + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include "opt_kdtrace.h" + +#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/lockstat.h> + +#if defined(__i386__) || defined(__amd64__) +#define LOCKSTAT_AFRAMES 1 +#else +#error "architecture not supported" +#endif + +static d_open_t lockstat_open; +static void lockstat_provide(void *, dtrace_probedesc_t *); +static void lockstat_destroy(void *, dtrace_id_t, void *); +static void lockstat_enable(void *, dtrace_id_t, void *); +static void lockstat_disable(void *, dtrace_id_t, void *); +static void lockstat_load(void *); +static int lockstat_unload(void); + + +typedef struct lockstat_probe { + char *lsp_func; + char *lsp_name; + int lsp_probe; + dtrace_id_t lsp_id; +#ifdef __FreeBSD__ + int lsp_frame; +#endif +} lockstat_probe_t; + +#ifdef __FreeBSD__ +lockstat_probe_t lockstat_probes[] = +{ + /* Spin Locks */ + { LS_MTX_SPIN_LOCK, LSS_ACQUIRE, LS_MTX_SPIN_LOCK_ACQUIRE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_MTX_SPIN_LOCK, LSS_SPIN, LS_MTX_SPIN_LOCK_SPIN, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_MTX_SPIN_UNLOCK, LSS_RELEASE, LS_MTX_SPIN_UNLOCK_RELEASE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + /* Adaptive Locks */ + { LS_MTX_LOCK, LSA_ACQUIRE, LS_MTX_LOCK_ACQUIRE, + DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, + { LS_MTX_LOCK, LSA_BLOCK, LS_MTX_LOCK_BLOCK, + DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, + { LS_MTX_LOCK, LSA_SPIN, LS_MTX_LOCK_SPIN, + DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, + { LS_MTX_UNLOCK, LSA_RELEASE, LS_MTX_UNLOCK_RELEASE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_MTX_TRYLOCK, LSA_ACQUIRE, LS_MTX_TRYLOCK_ACQUIRE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + /* Reader/Writer Locks */ + { LS_RW_RLOCK, LSR_ACQUIRE, LS_RW_RLOCK_ACQUIRE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_RLOCK, LSR_BLOCK, LS_RW_RLOCK_BLOCK, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_RLOCK, LSR_SPIN, LS_RW_RLOCK_SPIN, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_RUNLOCK, LSR_RELEASE, LS_RW_RUNLOCK_RELEASE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_WLOCK, LSR_ACQUIRE, LS_RW_WLOCK_ACQUIRE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_WLOCK, LSR_BLOCK, LS_RW_WLOCK_BLOCK, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_WLOCK, LSR_SPIN, LS_RW_WLOCK_SPIN, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_WUNLOCK, LSR_RELEASE, LS_RW_WUNLOCK_RELEASE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_TRYUPGRADE, LSR_UPGRADE, LS_RW_TRYUPGRADE_UPGRADE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_RW_DOWNGRADE, LSR_DOWNGRADE, LS_RW_DOWNGRADE_DOWNGRADE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + /* Shared/Exclusive Locks */ + { LS_SX_SLOCK, LSX_ACQUIRE, LS_SX_SLOCK_ACQUIRE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_SLOCK, LSX_BLOCK, LS_SX_SLOCK_BLOCK, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_SLOCK, LSX_SPIN, LS_SX_SLOCK_SPIN, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_SUNLOCK, LSX_RELEASE, LS_SX_SUNLOCK_RELEASE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_XLOCK, LSX_ACQUIRE, LS_SX_XLOCK_ACQUIRE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_XLOCK, LSX_BLOCK, LS_SX_XLOCK_BLOCK, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_XLOCK, LSX_SPIN, LS_SX_XLOCK_SPIN, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_XUNLOCK, LSX_RELEASE, LS_SX_XUNLOCK_RELEASE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_TRYUPGRADE, LSX_UPGRADE, LS_SX_TRYUPGRADE_UPGRADE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { LS_SX_DOWNGRADE, LSX_DOWNGRADE, LS_SX_DOWNGRADE_DOWNGRADE, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + /* Thread Locks */ + { LS_THREAD_LOCK, LST_SPIN, LS_THREAD_LOCK_SPIN, + DTRACE_IDNONE, LOCKSTAT_AFRAMES }, + { NULL } +}; +#else +#error "OS not supported" +#endif + + +static struct cdevsw lockstat_cdevsw = { + .d_version = D_VERSION, + .d_open = lockstat_open, + .d_name = "lockstat", +}; + +static struct cdev *lockstat_cdev; +static dtrace_provider_id_t lockstat_id; + +/*ARGSUSED*/ +static void +lockstat_enable(void *arg, dtrace_id_t id, void *parg) +{ + lockstat_probe_t *probe = parg; + + ASSERT(!lockstat_probemap[probe->lsp_probe]); + + lockstat_probemap[probe->lsp_probe] = id; +#ifdef DOODAD + membar_producer(); +#endif + + lockstat_probe_func = dtrace_probe; +#ifdef DOODAD + membar_producer(); + + lockstat_hot_patch(); + membar_producer(); +#endif +} + +/*ARGSUSED*/ +static void +lockstat_disable(void *arg, dtrace_id_t id, void *parg) +{ + lockstat_probe_t *probe = parg; + int i; + + ASSERT(lockstat_probemap[probe->lsp_probe]); + + lockstat_probemap[probe->lsp_probe] = 0; +#ifdef DOODAD + lockstat_hot_patch(); + membar_producer(); +#endif + + /* + * See if we have any probes left enabled. + */ + for (i = 0; i < LS_NPROBES; i++) { + if (lockstat_probemap[i]) { + /* + * This probe is still enabled. We don't need to deal + * with waiting for all threads to be out of the + * lockstat critical sections; just return. + */ + return; + } + } + +} + +/*ARGSUSED*/ +static int +lockstat_open(struct cdev *dev __unused, int oflags __unused, + int devtype __unused, struct thread *td __unused) +{ + return (0); +} + +/*ARGSUSED*/ +static void +lockstat_provide(void *arg, dtrace_probedesc_t *desc) +{ + int i = 0; + + for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) { + lockstat_probe_t *probe = &lockstat_probes[i]; + + if (dtrace_probe_lookup(lockstat_id, "kernel", + probe->lsp_func, probe->lsp_name) != 0) + continue; + + ASSERT(!probe->lsp_id); +#ifdef __FreeBSD__ + probe->lsp_id = dtrace_probe_create(lockstat_id, + "kernel", probe->lsp_func, probe->lsp_name, + probe->lsp_frame, probe); +#else + probe->lsp_id = dtrace_probe_create(lockstat_id, + "kernel", probe->lsp_func, probe->lsp_name, + LOCKSTAT_AFRAMES, probe); +#endif + } +} + +/*ARGSUSED*/ +static void +lockstat_destroy(void *arg, dtrace_id_t id, void *parg) +{ + lockstat_probe_t *probe = parg; + + ASSERT(!lockstat_probemap[probe->lsp_probe]); + probe->lsp_id = 0; +} + +static dtrace_pattr_t lockstat_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_UNKNOWN }, +{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, +{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, +}; + +static dtrace_pops_t lockstat_pops = { + lockstat_provide, + NULL, + lockstat_enable, + lockstat_disable, + NULL, + NULL, + NULL, + NULL, + NULL, + lockstat_destroy +}; + +static void +lockstat_load(void *dummy) +{ + /* Create the /dev/dtrace/lockstat entry. */ + lockstat_cdev = make_dev(&lockstat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, + "dtrace/lockstat"); + + if (dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER, + NULL, &lockstat_pops, NULL, &lockstat_id) != 0) + return; +} + +static int +lockstat_unload() +{ + int error = 0; + + if ((error = dtrace_unregister(lockstat_id)) != 0) + return (error); + + destroy_dev(lockstat_cdev); + + return (error); +} + +/* ARGSUSED */ +static int +lockstat_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); +} + +SYSINIT(lockstat_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_load, NULL); +SYSUNINIT(lockstat_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_unload, NULL); + +DEV_MODULE(lockstat, lockstat_modevent, NULL); +MODULE_VERSION(lockstat, 1); +MODULE_DEPEND(lockstat, dtrace, 1, 1, 1); +MODULE_DEPEND(lockstat, opensolaris, 1, 1, 1); |