mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-16 15:11:52 +00:00
Merge print_lockchain and print_sleepchain
When debugging a deadlock, it is useful to follow the full chain of locks as far as possible. Reviewed by: jhb Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D12115
This commit is contained in:
parent
2624320fcc
commit
0c1d923efb
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=322836
@ -1096,7 +1096,7 @@ DB_SHOW_COMMAND(turnstile, db_show_turnstile)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Show all the threads a particular thread is waiting on based on
|
* Show all the threads a particular thread is waiting on based on
|
||||||
* non-sleepable and non-spin locks.
|
* non-spin locks.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
print_lockchain(struct thread *td, const char *prefix)
|
print_lockchain(struct thread *td, const char *prefix)
|
||||||
@ -1104,10 +1104,11 @@ print_lockchain(struct thread *td, const char *prefix)
|
|||||||
struct lock_object *lock;
|
struct lock_object *lock;
|
||||||
struct lock_class *class;
|
struct lock_class *class;
|
||||||
struct turnstile *ts;
|
struct turnstile *ts;
|
||||||
|
struct thread *owner;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Follow the chain. We keep walking as long as the thread is
|
* Follow the chain. We keep walking as long as the thread is
|
||||||
* blocked on a turnstile that has an owner.
|
* blocked on a lock that has an owner.
|
||||||
*/
|
*/
|
||||||
while (!db_pager_quit) {
|
while (!db_pager_quit) {
|
||||||
db_printf("%sthread %d (pid %d, %s) ", prefix, td->td_tid,
|
db_printf("%sthread %d (pid %d, %s) ", prefix, td->td_tid,
|
||||||
@ -1136,6 +1137,17 @@ print_lockchain(struct thread *td, const char *prefix)
|
|||||||
return;
|
return;
|
||||||
td = ts->ts_owner;
|
td = ts->ts_owner;
|
||||||
break;
|
break;
|
||||||
|
} else if (TD_ON_SLEEPQ(td)) {
|
||||||
|
if (!lockmgr_chain(td, &owner) &&
|
||||||
|
!sx_chain(td, &owner)) {
|
||||||
|
db_printf("sleeping on %p \"%s\"\n",
|
||||||
|
td->td_wchan, td->td_wmesg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (owner == NULL)
|
||||||
|
return;
|
||||||
|
td = owner;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
db_printf("inhibited\n");
|
db_printf("inhibited\n");
|
||||||
return;
|
return;
|
||||||
@ -1158,6 +1170,7 @@ DB_SHOW_COMMAND(lockchain, db_show_lockchain)
|
|||||||
|
|
||||||
print_lockchain(td, "");
|
print_lockchain(td, "");
|
||||||
}
|
}
|
||||||
|
DB_SHOW_ALIAS(sleepchain, db_show_lockchain);
|
||||||
|
|
||||||
DB_SHOW_ALL_COMMAND(chains, db_show_allchains)
|
DB_SHOW_ALL_COMMAND(chains, db_show_allchains)
|
||||||
{
|
{
|
||||||
@ -1168,14 +1181,11 @@ DB_SHOW_ALL_COMMAND(chains, db_show_allchains)
|
|||||||
i = 1;
|
i = 1;
|
||||||
FOREACH_PROC_IN_SYSTEM(p) {
|
FOREACH_PROC_IN_SYSTEM(p) {
|
||||||
FOREACH_THREAD_IN_PROC(p, td) {
|
FOREACH_THREAD_IN_PROC(p, td) {
|
||||||
if (TD_ON_LOCK(td) && LIST_EMPTY(&td->td_contested)) {
|
if ((TD_ON_LOCK(td) && LIST_EMPTY(&td->td_contested))
|
||||||
|
|| (TD_IS_INHIBITED(td) && TD_ON_SLEEPQ(td))) {
|
||||||
db_printf("chain %d:\n", i++);
|
db_printf("chain %d:\n", i++);
|
||||||
print_lockchain(td, " ");
|
print_lockchain(td, " ");
|
||||||
}
|
}
|
||||||
if (TD_IS_INHIBITED(td) && TD_ON_SLEEPQ(td)) {
|
|
||||||
db_printf("chain %d:\n", i++);
|
|
||||||
print_sleepchain(td, " ");
|
|
||||||
}
|
|
||||||
if (db_pager_quit)
|
if (db_pager_quit)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1183,70 +1193,6 @@ DB_SHOW_ALL_COMMAND(chains, db_show_allchains)
|
|||||||
}
|
}
|
||||||
DB_SHOW_ALIAS(allchains, db_show_allchains)
|
DB_SHOW_ALIAS(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);
|
|
||||||
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 print_waiters(struct turnstile *ts, int indent);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Loading…
Reference in New Issue
Block a user