PERFORCE change 103140 for review

John Baldwin jhb at FreeBSD.org
Thu Aug 3 21:36:29 UTC 2006


http://perforce.freebsd.org/chv.cgi?CH=103140

Change 103140 by jhb at jhb_mutex on 2006/08/03 21:35:31

	- Rename 'show threadchain' to 'show lockchain' and 'show lockchain'
	  to 'show locktree'.
	- Add a new 'show sleepchain' that attempts to do 'show lockchain' for
	  sleepable locks (i.e. sx and lockmgr).  I've had much help tracking
	  down vnode deadlocks at work using a gdb version of this that just
	  grokked lockmgr locks.

Affected files ...

.. //depot/projects/smpng/sys/kern/kern_lock.c#48 edit
.. //depot/projects/smpng/sys/kern/kern_sx.c#36 edit
.. //depot/projects/smpng/sys/kern/subr_turnstile.c#31 edit
.. //depot/projects/smpng/sys/sys/lockmgr.h#16 edit
.. //depot/projects/smpng/sys/sys/sx.h#20 edit

Differences ...

==== //depot/projects/smpng/sys/kern/kern_lock.c#48 (text+ko) ====

@@ -43,6 +43,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD: src/sys/kern/kern_lock.c,v 1.97 2006/07/14 17:55:36 pjd Exp $");
 
+#include "opt_ddb.h"
+
 #include <sys/param.h>
 #include <sys/kdb.h>
 #include <sys/kernel.h>
@@ -56,6 +58,10 @@
 #include <sys/stack.h>
 #endif
 
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
 /*
  * Locking primitives implementation.
  * Locks provide shared/exclusive sychronization.
@@ -581,3 +587,33 @@
 	stack_print(&lkp->lk_stack);
 #endif
 }
+
+#ifdef DDB
+/*
+ * Check to see if a thread that is blocked on a sleep queue is actually
+ * blocked on a 'struct lock'.  If so, output some details and return true.
+ * If the lock has an exclusive owner, return that in *ownerp.
+ */
+int
+lockmgr_chain(struct thread *td, struct thread **ownerp)
+{
+	struct lock *lkp;
+
+	lkp = td->td_wchan;
+
+	/* Simple test to see if wchan points to a lockmgr lock. */
+	if (lkp->lk_wmesg != td->td_wmesg)
+		return (0);
+
+	/* Ok, we think we have a lockmgr lock, so output some details. */
+	db_printf("blocked on \"%s\" ", lkp->lk_wmesg);
+	if (lkp->lk_sharecount) {
+		db_printf("SHARED (count %d)\n", lkp->lk_sharecount);
+		*ownerp = NULL;
+	} else {
+		db_printf("EXCL (count %d)\n", lkp->lk_exclusivecount);
+		*ownerp = lkp->lk_lockholder;
+	}
+	return (1);
+}
+#endif

==== //depot/projects/smpng/sys/kern/kern_sx.c#36 (text+ko) ====

@@ -48,9 +48,9 @@
 #include <sys/proc.h>
 #include <sys/sx.h>
 
+#ifdef DDB
 #include <ddb/ddb.h>
 
-#ifdef DDB
 static void	db_show_sx(struct lock_object *lock);
 #endif
 
@@ -395,4 +395,57 @@
 	db_printf(" waiters: %d shared, %d exclusive\n", sx->sx_shrd_wcnt,
 	    sx->sx_excl_wcnt);
 }
+
+/*
+ * Check to see if a thread that is blocked on a sleep queue is actually
+ * blocked on an sx lock.  If so, output some details and return true.
+ * If the lock has an exclusive owner, return that in *ownerp.
+ */
+int
+sx_chain(struct thread *td, struct thread **ownerp)
+{
+	struct sx *sx;
+	struct cv *cv;
+
+	/*
+	 * First, see if it looks like td is blocked on a condition
+	 * variable.
+	 */
+	cv = td->td_wchan;
+	if (cv->cv_description != td->td_wmesg)
+		return (0);
+
+	/*
+	 * Ok, see if it looks like td is blocked on the exclusive
+	 * condition variable.
+	 */
+	sx = (struct sx *)((char *)cv - offsetof(struct sx, sx_excl_cv));
+	if (LOCK_CLASS(&sx->sx_object) == &lock_class_sx &&
+	    sx->sx_excl_wcnt > 0)
+		goto ok;
+
+	/*
+	 * Second, see if it looks like td is blocked on the shared
+	 * condvar.
+	 */
+	sx = (struct sx *)((char *)cv - offsetof(struct sx, sx_shrd_cv));
+	if (LOCK_CLASS(&sx->sx_object) == &lock_class_sx &&
+	    sx->sx_shrd_wcnt > 0)
+		goto ok;
+
+	/* Doesn't seem to be an sx lock. */
+	return (0);
+
+ok:
+	/* We think we have an sx lock, so output some details. */
+	db_printf("blocked on sx \"%s\" ", td->td_wmesg);
+	if (sx->sx_cnt >= 0) {
+		db_printf("SLOCK (count %d)\n", sx->sx_cnt);
+		*ownerp = NULL;
+	} else {
+		db_printf("XLOCK\n");
+		*ownerp = sx->sx_xholder;
+	}
+	return (1);
+}
 #endif

==== //depot/projects/smpng/sys/kern/subr_turnstile.c#31 (text+ko) ====

@@ -78,6 +78,8 @@
 #ifdef DDB
 #include <sys/kdb.h>
 #include <ddb/ddb.h>
+#include <sys/lockmgr.h>
+#include <sys/sx.h>
 #endif
 
 /*
@@ -1035,8 +1037,12 @@
 	
 }
 
+/*
+ * Show all the threads a particular thread is waiting on based on
+ * non-sleepable and non-spin locks.
+ */
 static void
-print_threadchain(struct thread *td, const char *prefix)
+print_lockchain(struct thread *td, const char *prefix)
 {
 	struct lock_object *lock;
 	struct lock_class *class;
@@ -1084,7 +1090,7 @@
 	}
 }
 
-DB_SHOW_COMMAND(threadchain, db_show_threadchain)
+DB_SHOW_COMMAND(lockchain, db_show_lockchain)
 {
 	struct thread *td;
 
@@ -1094,7 +1100,7 @@
 	else
 		td = kdb_thread;
 
-	print_threadchain(td, "");
+	print_lockchain(td, "");
 }
 
 DB_SHOW_COMMAND(allchains, db_show_allchains)
@@ -1108,7 +1114,7 @@
 		FOREACH_THREAD_IN_PROC(p, td) {
 			if (TD_ON_LOCK(td) && LIST_EMPTY(&td->td_contested)) {
 				db_printf("chain %d:\n", i++);
-				print_threadchain(td, " ");
+				print_lockchain(td, " ");
 			}
 			if (db_pager_quit)
 				return;
@@ -1116,6 +1122,72 @@
 	}
 }
 
+
+/*
+ * Show all the threads a particular thread is waiting on based on
+ * sleepable locks.
+ */
+static void
+print_sleepchain(struct thread *td, const char *prefix)
+{
+	struct thread *owner;
+
+	/*
+	 * Follow the chain.  We keep walking as long as the thread is
+	 * blocked on a sleep lock that has an owner.
+	 */
+	while (!db_pager_quit) {
+		db_printf("%sthread %d (pid %d, %s) ", prefix, td->td_tid,
+		    td->td_proc->p_pid, td->td_name[0] != '\0' ? td->td_name :
+		    td->td_proc->p_comm);
+		switch (td->td_state) {
+		case TDS_INACTIVE:
+			db_printf("is inactive\n");
+			return;
+		case TDS_CAN_RUN:
+			db_printf("can run\n");
+			return;
+		case TDS_RUNQ:
+			db_printf("is on a run queue\n");
+			return;
+		case TDS_RUNNING:
+			db_printf("running on CPU %d\n", td->td_oncpu);
+			return;
+		case TDS_INHIBITED:
+			if (TD_ON_SLEEPQ(td)) {
+				if (lockmgr_chain(td, &owner) ||
+				    sx_chain(td, &owner)) {
+					if (owner == NULL)
+						return;
+					td = owner;
+					break;
+				}
+				db_printf("sleeping on %p \"%s\"\n",
+				    td->td_wchan, td->td_wmesg);
+				return;
+			}
+			db_printf("inhibited\n");
+			return;
+		default:
+			db_printf("??? (%#x)\n", td->td_state);
+			return;
+		}
+	}
+}
+
+DB_SHOW_COMMAND(sleepchain, db_show_sleepchain)
+{
+	struct thread *td;
+
+	/* Figure out which thread to start with. */
+	if (have_addr)
+		td = db_lookup_thread(addr, TRUE);
+	else
+		td = kdb_thread;
+
+	print_sleepchain(td, "");
+}
+
 static void	print_waiters(struct turnstile *ts, int indent);
 	
 static void
@@ -1156,7 +1228,7 @@
 		print_waiter(td, indent + 1);
 }
 
-DB_SHOW_COMMAND(lockchain, db_show_lockchain)
+DB_SHOW_COMMAND(locktree, db_show_locktree)
 {
 	struct lock_object *lock;
 	struct lock_class *class;

==== //depot/projects/smpng/sys/sys/lockmgr.h#16 (text+ko) ====

@@ -203,5 +203,8 @@
 void	lockmgr_printinfo(struct lock *);
 int	lockstatus(struct lock *, struct thread *);
 int	lockcount(struct lock *);
+#ifdef DDB
+int	lockmgr_chain(struct thread *td, struct thread **ownerp);
+#endif
 
 #endif /* !_SYS_LOCKMGR_H_ */

==== //depot/projects/smpng/sys/sys/sx.h#20 (text+ko) ====

@@ -60,6 +60,9 @@
 #ifdef INVARIANT_SUPPORT
 void	_sx_assert(struct sx *sx, int what, const char *file, int line);
 #endif
+#ifdef DDB
+int	sx_chain(struct thread *td, struct thread **ownerp);
+#endif
 
 struct sx_args {
 	struct sx 	*sa_sx;


More information about the p4-projects mailing list