summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_turnstile.c
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2006-04-25 20:28:17 +0000
committerjhb <jhb@FreeBSD.org>2006-04-25 20:28:17 +0000
commit0b071af54713f86b1c58a4860f759f358c7cb8c6 (patch)
tree65dbc74230acd8c9fe633aa1be6061800a2a9e77 /sys/kern/subr_turnstile.c
parent9a8083cd6c7387ec362c8e5c7b7810bf7c642554 (diff)
downloadFreeBSD-src-0b071af54713f86b1c58a4860f759f358c7cb8c6.zip
FreeBSD-src-0b071af54713f86b1c58a4860f759f358c7cb8c6.tar.gz
Add some new commands to hopefully make it easier to diagnose lock-related
problems in ddb: - "show threadchain [thread]" will start with the specified thread (or the current kdb thread by default) and show it's state. If it is blocked on a lock, it will find the owner of the lock and show its state, etc. - "show allchains" will find all of the threads that are blocked on a lock (but do not have any threads blocked on a lock they hold) and show the resulting thread chain. - "show lockchain <lock>" takes a pointer to a lock_object (such as a mutex or rwlock). If there is a turnstile for that lock, then it will display all the threads blocked on the lock. In addition, for each thread blocked on the lock, it will display any contested locks they hold, and recurse on those locks to show any threads blocked on those locks, etc.
Diffstat (limited to 'sys/kern/subr_turnstile.c')
-rw-r--r--sys/kern/subr_turnstile.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/sys/kern/subr_turnstile.c b/sys/kern/subr_turnstile.c
index 16562f8..2c3e34b 100644
--- a/sys/kern/subr_turnstile.c
+++ b/sys/kern/subr_turnstile.c
@@ -76,6 +76,7 @@ __FBSDID("$FreeBSD$");
#include <sys/turnstile.h>
#ifdef DDB
+#include <sys/kdb.h>
#include <ddb/ddb.h>
#endif
@@ -1033,4 +1034,141 @@ found:
print_queue(&ts->ts_pending, "Pending Threads", "\t");
}
+
+static void
+print_threadchain(struct thread *td, const char *prefix)
+{
+ struct lock_object *lock;
+ struct lock_class *class;
+ struct turnstile *ts;
+
+ /*
+ * Follow the chain. We keep walking as long as the thread is
+ * blocked on a turnstile that has an owner.
+ */
+ for (;;) {
+ 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_LOCK(td)) {
+ ts = td->td_blocked;
+ lock = ts->ts_lockobj;
+ class = LOCK_CLASS(lock);
+ db_printf("blocked on lock %p (%s) \"%s\"\n",
+ lock, class->lc_name, lock->lo_name);
+ if (ts->ts_owner == NULL)
+ return;
+ td = ts->ts_owner;
+ break;
+ }
+ db_printf("inhibited\n");
+ return;
+ default:
+ db_printf("??? (%#x)\n", td->td_state);
+ return;
+ }
+ }
+}
+
+DB_SHOW_COMMAND(threadchain, db_show_threadchain)
+{
+ struct thread *td;
+
+ /* Figure out which thread to start with. */
+ if (have_addr)
+ td = db_lookup_thread(addr, TRUE);
+ else
+ td = kdb_thread;
+
+ print_threadchain(td, "");
+}
+
+DB_SHOW_COMMAND(allchains, db_show_allchains)
+{
+ struct thread *td;
+ struct proc *p;
+ int i;
+
+ i = 1;
+ LIST_FOREACH(p, &allproc, p_list) {
+ 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, " ");
+ }
+ }
+ }
+}
+
+static void print_waiters(struct turnstile *ts, int indent);
+
+static void
+print_waiter(struct thread *td, int indent)
+{
+ struct turnstile *ts;
+ int i;
+
+ for (i = 0; i < indent; i++)
+ db_printf(" ");
+ print_thread(td, "thread ");
+ LIST_FOREACH(ts, &td->td_contested, ts_link)
+ print_waiters(ts, indent + 1);
+}
+
+static void
+print_waiters(struct turnstile *ts, int indent)
+{
+ struct lock_object *lock;
+ struct lock_class *class;
+ struct thread *td;
+ int i;
+
+ lock = ts->ts_lockobj;
+ class = LOCK_CLASS(lock);
+ for (i = 0; i < indent; i++)
+ db_printf(" ");
+ db_printf("lock %p (%s) \"%s\"\n", lock, class->lc_name, lock->lo_name);
+ TAILQ_FOREACH(td, &ts->ts_blocked[TS_EXCLUSIVE_QUEUE], td_lockq)
+ print_waiter(td, indent + 1);
+ TAILQ_FOREACH(td, &ts->ts_blocked[TS_SHARED_QUEUE], td_lockq)
+ print_waiter(td, indent + 1);
+ TAILQ_FOREACH(td, &ts->ts_pending, td_lockq)
+ print_waiter(td, indent + 1);
+}
+
+DB_SHOW_COMMAND(lockchain, db_show_lockchain)
+{
+ struct lock_object *lock;
+ struct lock_class *class;
+ struct turnstile_chain *tc;
+ struct turnstile *ts;
+
+ if (!have_addr)
+ return;
+ lock = (struct lock_object *)addr;
+ tc = TC_LOOKUP(lock);
+ LIST_FOREACH(ts, &tc->tc_turnstiles, ts_hash)
+ if (ts->ts_lockobj == lock)
+ break;
+ if (ts == NULL) {
+ class = LOCK_CLASS(lock);
+ db_printf("lock %p (%s) \"%s\"\n", lock, class->lc_name,
+ lock->lo_name);
+ } else
+ print_waiters(ts, 0);
+}
#endif
OpenPOWER on IntegriCloud