Date: 01 Apr 2002 00:07:18 +0200 From: Dag-Erling Smorgrav <des@ofug.org> To: arch@freebsd.org Subject: mutex profiling Message-ID: <xzp8z88e6i1.fsf@flood.ping.uio.no>
next in thread | raw e-mail | index | archive | help
--=-=-= The attached patch (derived from patches by Eivind) adds code to record statistics about MTX_DEF locks. It's currently i386-only as it uses the TSC to measure the amount of time each mutex is held. Once compiled in and enabled, statistics can be retrieved using sysctl(8). The code records four numbers for each mutex (longest time held in one go, total time held, number of acquisitions, average time held) and returns a top-16 list for each. Each mutex is identified by the place where it was first acquired, so multiple instances of the same type of mutex (e.g. proc lock, filedesc lock) are counted as one. Here's a sample listing (with some annotations): des@des ~% sysctl kern.mtx.trace kern.mtx.trace.enable: 1 kern.mtx.trace.max: 20880774 kern/kern_fork.c:467 12632714 dev/sound/pcm/channel.c:677 10653798 i386/i386/machdep.c:1715 2222570 kern/kern_descrip.c:748 1101574 pci/if_xl.c:1259 812677 vm/uma_core.c:232 723969 dev/random/yarrow.c:265 523384 vm/uma_core.c:1179 500278 kern/kern_lock.c:227 427118 kern/vfs_subr.c:936 357190 kern/vfs_syscalls.c:1692 141822 vm/uma_core.c:985 79442 kern/vfs_lookup.c:149 65260 dev/sound/pcm/sound.c:134 52277 vm/uma_core.c:1301 36987 kern/kern_fork.c:535 kern.mtx.trace.total: 5472784414 i386/i386/machdep.c:1715 # Giant 1155767274 kern/kern_descrip.c:748 # filedesc lock 400100257 kern/kern_fork.c:467 # proc lock 352664142 kern/kern_lock.c:227 229755648 kern/sys_generic.c:800 # select(2) lock 226619967 kern/vfs_syscalls.c:1692 # file lock 116591756 vm/uma_core.c:1179 # zone lock 113001343 kern/vfs_subr.c:1857 # vnode lock 68279100 kern/kern_descrip.c:1108 28700835 pci/if_xl.c:1259 25726912 kern/vfs_lookup.c:149 25351235 kern/vfs_subr.c:1805 24323335 dev/sound/pcm/channel.c:677 21193642 kern/kern_sx.c:147 20097794 kern/vfs_subr.c:1788 18994392 kern/subr_mbuf.c:452 kern.mtx.trace.count: 1780724 kern/kern_fork.c:467 # proc lock 1127992 i386/i386/machdep.c:1715 # Giant 753901 kern/kern_lock.c:227 592171 kern/sys_generic.c:800 # select(2) lock 438879 vm/uma_core.c:1179 # zone lock 419580 kern/kern_descrip.c:748 # filedesc lock 407064 kern/vfs_syscalls.c:1692 258866 kern/vfs_subr.c:1857 104235 kern/kern_sx.c:147 86810 kern/vfs_subr.c:1788 61475 kern/vfs_subr.c:1805 52572 kern/subr_mbuf.c:452 36992 kern/vfs_vnops.c:762 36415 sys/buf.h:278 28884 kern/kern_resource.c:900 28421 kern/kern_descrip.c:1108 kern.mtx.trace.average: 3040416 dev/sound/pcm/channel.c:677 126265 vm/uma_core.c:985 # zone lock 38844 dev/random/yarrow.c:265 11743 pci/if_xl.c:1259 8335 dev/sound/pcm/sound.c:134 6292 kern/kern_fork.c:535 6069 kern/subr_disklabel.c:95 4852 i386/i386/machdep.c:1715 4554 kern/vfs_subr.c:936 3575 vm/uma_core.c:232 3337 vm/uma_core.c:1761 2787 vm/swap_pager.c:306 2754 kern/kern_descrip.c:748 2552 net/bpf.c:254 2402 kern/kern_descrip.c:1108 2321 kern/sys_pipe.c:1302 DES -- Dag-Erling Smorgrav - des@ofug.org --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=mutex_trace.diff Index: sys/_mutex.h =================================================================== RCS file: /home/ncvs/src/sys/sys/_mutex.h,v retrieving revision 1.4 diff -u -r1.4 _mutex.h --- sys/_mutex.h 18 Dec 2001 00:27:18 -0000 1.4 +++ sys/_mutex.h 23 Mar 2002 17:41:05 -0000 @@ -41,6 +41,10 @@ volatile u_int mtx_recurse; /* number of recursive holds */ TAILQ_HEAD(, thread) mtx_blocked; /* threads blocked on this lock */ LIST_ENTRY(mtx) mtx_contested; /* list of all contested locks */ +#ifdef TRACE_MUTEX_TIME + u_int64_t tsc; + char name[64]; +#endif }; #endif /* !_SYS_MUTEX_TYPES_H_ */ Index: kern/kern_mutex.c =================================================================== RCS file: /home/ncvs/src/sys/kern/kern_mutex.c,v retrieving revision 1.83 diff -u -r1.83 kern_mutex.c --- kern/kern_mutex.c 27 Mar 2002 09:23:38 -0000 1.83 +++ kern/kern_mutex.c 31 Mar 2002 21:42:56 -0000 @@ -37,17 +37,18 @@ #include "opt_ddb.h" #include <sys/param.h> +#include <sys/systm.h> #include <sys/bus.h> #include <sys/kernel.h> +#include <sys/ktr.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/mutex.h> #include <sys/proc.h> #include <sys/resourcevar.h> +#include <sys/sbuf.h> #include <sys/sysctl.h> -#include <sys/systm.h> #include <sys/vmmeter.h> -#include <sys/ktr.h> #include <machine/atomic.h> #include <machine/bus.h> @@ -196,6 +197,121 @@ } } +#ifdef TRACE_MUTEX_TIME +#define TRACE_HASH_SIZE 1009 +SYSCTL_NODE(_kern, OID_AUTO, mtx, CTLFLAG_RD, NULL, "mutex manipulation"); +SYSCTL_NODE(_kern_mtx, OID_AUTO, trace, CTLFLAG_RD, NULL, "mutex tracing"); +static int kern_mtx_trace_enable = 0; +SYSCTL_INT(_kern_mtx_trace, OID_AUTO, enable, CTLFLAG_RW, + &kern_mtx_trace_enable, 0, "Enable tracing of mutex holdtime"); +static int kern_mtx_trace_numtraced = 0; +#ifdef DIAGNOSTICS +SYSCTL_INT(_kern_mtx_trace, OID_AUTO, numtraced, CTLFLAG_RD, + &kern_mtx_trace_numtraced, 0, "Number of traced mutices"); +static int kern_mtx_trace_hashsize = TRACE_HASH_SIZE; +SYSCTL_INT(_kern_mtx_trace, OID_AUTO, hashsize, CTLFLAG_RD, + &kern_mtx_trace_hashsize, 0, "Trace hash size"); +static int kern_mtx_trace_collisions = 0; +SYSCTL_INT(_kern_mtx_trace, OID_AUTO, collisions, CTLFLAG_RD, + &kern_mtx_trace_collisions, 0, "Number of hash collisions"); +#endif + +static struct mutex_trace { + char name[64]; +#define TRACE_MAX 0 +#define TRACE_TOT 1 +#define TRACE_CNT 2 +#define TRACE_AVG 3 + u_int64_t tsc[4]; + struct mutex_trace *next; +} *mutex_traces[TRACE_HASH_SIZE]; + +static struct mtx trace_mtx; + +static void +mutex_trace_init(void *arg __unused) +{ + mtx_init(&trace_mtx, "mutex trace lock", MTX_SPIN|MTX_QUIET); +} +SYSINIT(mtxtraceinit, SI_SUB_LOCK, SI_ORDER_ANY, mutex_trace_init, NULL); + +/* + * tracebuf is the pool of trace records we have; + * we cannot (reliably) handle more than this number + * of places in the code where mutexes are aquired. + * + * We use a static allocation to avoid interacting + * with the rest of the system. + */ +#define NUM_TRACE_BUFFERS 1000 +static struct mutex_trace tracebuf[NUM_TRACE_BUFFERS]; +static int first_free_mutex_trace = 0; + +static const char *unknown = "(unknown)"; + +/* Number of locks to show in the sysctl. */ +#define NUM_LOCKS_TO_DUMP 16 + +/* + * Output statistics of mutex holders that keep the mutex the longest. + */ +static int dump_mtx_trace(SYSCTL_HANDLER_ARGS); +SYSCTL_PROC(_kern_mtx_trace, OID_AUTO, max, CTLTYPE_STRING|CTLFLAG_RD, + NULL, TRACE_MAX, dump_mtx_trace, "A", ""); +SYSCTL_PROC(_kern_mtx_trace, OID_AUTO, total, CTLTYPE_STRING|CTLFLAG_RD, + NULL, TRACE_TOT, dump_mtx_trace, "A", ""); +SYSCTL_PROC(_kern_mtx_trace, OID_AUTO, count, CTLTYPE_STRING|CTLFLAG_RD, + NULL, TRACE_CNT, dump_mtx_trace, "A", ""); +SYSCTL_PROC(_kern_mtx_trace, OID_AUTO, average, CTLTYPE_STRING|CTLFLAG_RD, + NULL, TRACE_AVG, dump_mtx_trace, "A", ""); +static int +dump_mtx_trace(SYSCTL_HANDLER_ARGS) +{ + struct sbuf *sb; + struct mutex_trace *mtp; + int traces[NUM_LOCKS_TO_DUMP]; + int error, i, j, k, n; + + if (kern_mtx_trace_numtraced == 0) + return SYSCTL_OUT(req, "No locking recorded", + sizeof("No locking recorded")); + n = arg2; + + /* + * Find the 10 largest + */ + mtx_lock_spin(&trace_mtx); + for (i = 0; i < NUM_LOCKS_TO_DUMP; ++i) + traces[i] = -1; + for (k = 0; k < first_free_mutex_trace; ++k) { + for (i = 0; i < NUM_LOCKS_TO_DUMP; ++i) { + if (traces[i] == -1) { + traces[i] = k; + break; + } + if (tracebuf[k].tsc[n] < tracebuf[traces[i]].tsc[n]) + continue; + for (j = NUM_LOCKS_TO_DUMP - 1; j > i; --j) + traces[j] = traces[j - 1]; + traces[i] = k; + break; + } + } + + /* Now dump the garbage */ + sb = sbuf_new(NULL, NULL, 256, SBUF_AUTOEXTEND); + for (i = 0; i < NUM_LOCKS_TO_DUMP && traces[i] != -1; ++i) { + mtp = &tracebuf[traces[i]]; + sbuf_printf(sb, "%12llu %s\n", mtp->tsc[n], mtp->name); + } + sbuf_finish(sb); + mtx_unlock_spin(&trace_mtx); + error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); + sbuf_delete(sb); + return (error); +} +#endif /* TRACE_MUTEX_TIME */ + /* * Function versions of the inlined __mtx_* macros. These are used by * modules and can also be called from assembly language if needed. @@ -209,6 +325,18 @@ LOCK_LOG_LOCK("LOCK", &m->mtx_object, opts, m->mtx_recurse, file, line); WITNESS_LOCK(&m->mtx_object, opts | LOP_EXCLUSIVE, file, line); +#ifdef TRACE_MUTEX_TIME + m->tsc = kern_mtx_trace_enable ? rdtsc() : 0; + if (m->name[0] == '\0') { + const char *p = file; + + while (p && strncmp(p, "../", 3) == 0) + p += 3; + if (p == NULL || *p == '\0') + p = unknown; + snprintf(m->name, sizeof m->name, "%s:%d", p, line); + } +#endif } void @@ -217,6 +345,53 @@ MPASS(curthread != NULL); mtx_assert(m, MA_OWNED); +#ifdef TRACE_MUTEX_TIME + if (m->tsc != 0 && m->name[0] != '\0') { + struct mutex_trace *mtp; + u_int64_t tsc, mtsc; + volatile u_int hash; + char *p; + + tsc = rdtsc(); + mtsc = m->tsc; + m->tsc = 0; + if (tsc <= mtsc) + goto out; + hash = 0; + for (p = m->name; *p != '\0'; ++p) + hash = ((hash << 1) | *p) % TRACE_HASH_SIZE; + mtx_lock_spin(&trace_mtx); + for (mtp = mutex_traces[hash]; mtp != NULL; mtp = mtp->next) + if (strcmp(mtp->name, m->name) == 0) + break; + if (mtp == NULL) { + /* Just exit if we cannot get a trace buffer */ + if (first_free_mutex_trace >= NUM_TRACE_BUFFERS) + goto unlock; + mtp = &tracebuf[first_free_mutex_trace++]; + strcpy(mtp->name, m->name); +#ifdef DIAGNOSTICS + if (mutex_traces[hash] != NULL) + ++kern_mtx_trace_collisions; +#endif + mtp->next = mutex_traces[hash]; + mutex_traces[hash] = mtp; + ++kern_mtx_trace_numtraced; + } + /* + * Record if the mutex has been held longer now than ever + * before + */ + if ((tsc - mtsc) > mtp->tsc[TRACE_MAX]) + mtp->tsc[TRACE_MAX] = tsc - mtsc; + mtp->tsc[TRACE_TOT] += tsc - mtsc; + mtp->tsc[TRACE_CNT] += 1; + mtp->tsc[TRACE_AVG] = mtp->tsc[TRACE_TOT] / mtp->tsc[TRACE_CNT]; +unlock: + mtx_unlock_spin(&trace_mtx); + } +out: +#endif WITNESS_UNLOCK(&m->mtx_object, opts | LOP_EXCLUSIVE, file, line); LOCK_LOG_LOCK("UNLOCK", &m->mtx_object, opts, m->mtx_recurse, file, line); Index: conf/options.i386 =================================================================== RCS file: /home/ncvs/src/sys/conf/options.i386,v retrieving revision 1.169 diff -u -r1.169 options.i386 --- conf/options.i386 27 Feb 2002 09:51:31 -0000 1.169 +++ conf/options.i386 23 Mar 2002 18:04:45 -0000 @@ -188,6 +188,9 @@ # SMB/CIFS filesystem SMBFS +# Mutex profiling +TRACE_MUTEX_TIME opt_global.h + # ------------------------------- # EOF # ------------------------------- --=-=-=-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-arch" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?xzp8z88e6i1.fsf>