diff options
author | jhb <jhb@FreeBSD.org> | 2006-08-15 18:29:01 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2006-08-15 18:29:01 +0000 |
commit | 4e96206d8aa504d0b00ace5c45fcb104888b7dc3 (patch) | |
tree | cdceb81a7ecf499b8d0d51c3f275b40b0a836064 /sys/kern/subr_turnstile.c | |
parent | e092be01212a8a69fe2f62be7d44c909fe7002bb (diff) | |
download | FreeBSD-src-4e96206d8aa504d0b00ace5c45fcb104888b7dc3.zip FreeBSD-src-4e96206d8aa504d0b00ace5c45fcb104888b7dc3.tar.gz |
Add a new 'show sleepchain' ddb command similar to 'show lockchain' except
that it operates on lockmgr and sx locks. This can be useful for tracking
down vnode deadlocks in VFS for example. Note that this command is a bit
more fragile than 'show lockchain' as we have to poke around at the
wait channel of a thread to see if it points to either a struct lock or
a condition variable inside of a struct sx. If td_wchan points to
something unmapped, then this command will terminate early due to a fault,
but no harm will be done.
Diffstat (limited to 'sys/kern/subr_turnstile.c')
-rw-r--r-- | sys/kern/subr_turnstile.c | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/sys/kern/subr_turnstile.c b/sys/kern/subr_turnstile.c index 0866999..ec659ae 100644 --- a/sys/kern/subr_turnstile.c +++ b/sys/kern/subr_turnstile.c @@ -78,6 +78,8 @@ __FBSDID("$FreeBSD$"); #ifdef DDB #include <sys/kdb.h> #include <ddb/ddb.h> +#include <sys/lockmgr.h> +#include <sys/sx.h> #endif /* @@ -1120,6 +1122,71 @@ DB_SHOW_COMMAND(allchains, db_show_allchains) } } +/* + * 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 |