Add a new command, show all locks, which shows information of active locks

The command shows information of all active (i.e., being held) locks that are
tracked through either of LWPs or CPUs by the LOCKDEBUG facility.  The /t
modifier additionally shows a backtrace for each LWP additionally.  This
feature is useful for debugging especially to analyze deadlocks.

The command is useful only if LOCKDEBUG is enabled.
This commit is contained in:
ozaki-r 2018-03-16 04:44:51 +00:00
parent 1d919413b1
commit e611636f76
4 changed files with 119 additions and 6 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: ddb.4,v 1.175 2018/03/16 04:37:55 ozaki-r Exp $
.\" $NetBSD: ddb.4,v 1.176 2018/03/16 04:44:51 ozaki-r Exp $
.\"
.\" Copyright (c) 1997 - 2009 The NetBSD Foundation, Inc.
.\" All rights reserved.
@ -556,6 +556,13 @@ Display information about callouts in the system.
See
.Xr callout 9
for more information on callouts.
.It Ic show all locks Ns Op Cm /t
Display details information about all active locks.
If
.Cm /t
is specified, stack traces of LWPs holding locks are printed additionally.
This command is useful only if a kernel is compiled with
.Cd options LOCKDEBUG .
.It Ic show all pages
Display basic information about all physical pages managed by the VM system.
For more detailed information about a single page, use

View File

@ -1,4 +1,4 @@
/* $NetBSD: db_command.c,v 1.150 2018/03/16 04:37:55 ozaki-r Exp $ */
/* $NetBSD: db_command.c,v 1.151 2018/03/16 04:44:51 ozaki-r Exp $ */
/*
* Copyright (c) 1996, 1997, 1998, 1999, 2002, 2009 The NetBSD Foundation, Inc.
@ -60,7 +60,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_command.c,v 1.150 2018/03/16 04:37:55 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: db_command.c,v 1.151 2018/03/16 04:44:51 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_aio.h"
@ -190,6 +190,7 @@ static void db_event_print_cmd(db_expr_t, bool, db_expr_t, const char *);
static void db_fncall(db_expr_t, bool, db_expr_t, const char *);
static void db_help_print_cmd(db_expr_t, bool, db_expr_t, const char *);
static void db_lock_print_cmd(db_expr_t, bool, db_expr_t, const char *);
static void db_show_all_locks(db_expr_t, bool, db_expr_t, const char *);
static void db_show_lockstat(db_expr_t, bool, db_expr_t, const char *);
static void db_mount_print_cmd(db_expr_t, bool, db_expr_t, const char *);
static void db_mbuf_print_cmd(db_expr_t, bool, db_expr_t, const char *);
@ -226,6 +227,8 @@ static const struct db_command db_show_cmds[] = {
0 ,"List all processes.",NULL,NULL) },
{ DDB_ADD_CMD("pools", db_show_all_pools,
0 ,"Show all pools",NULL,NULL) },
{ DDB_ADD_CMD("locks", db_show_all_locks,
0 ,"Show all held locks", "[/t]", NULL) },
#ifdef AIO
/*added from all sub cmds*/
{ DDB_ADD_CMD("aio_jobs", db_show_aio_jobs, 0,
@ -1230,6 +1233,16 @@ db_lock_print_cmd(db_expr_t addr, bool have_addr,
#endif
}
static void
db_show_all_locks(db_expr_t addr, bool have_addr,
db_expr_t count, const char *modif)
{
#ifdef _KERNEL /* XXX CRASH(8) */
lockdebug_show_all_locks(db_printf, modif);
#endif
}
static void
db_show_lockstat(db_expr_t addr, bool have_addr,
db_expr_t count, const char *modif)

View File

@ -1,4 +1,4 @@
/* $NetBSD: subr_lockdebug.c,v 1.61 2018/03/16 04:43:37 ozaki-r Exp $ */
/* $NetBSD: subr_lockdebug.c,v 1.62 2018/03/16 04:44:51 ozaki-r Exp $ */
/*-
* Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: subr_lockdebug.c,v 1.61 2018/03/16 04:43:37 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: subr_lockdebug.c,v 1.62 2018/03/16 04:44:51 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@ -51,6 +51,7 @@ __KERNEL_RCSID(0, "$NetBSD: subr_lockdebug.c,v 1.61 2018/03/16 04:43:37 ozaki-r
#include <sys/atomic.h>
#include <sys/lock.h>
#include <sys/rbtree.h>
#include <sys/ksyms.h>
#include <machine/lock.h>
@ -815,6 +816,9 @@ lockdebug_abort1(const char *func, size_t line, lockdebug_t *ld, int s,
* Handle the DDB 'show lock' command.
*/
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_interface.h>
void
lockdebug_lock_print(void *addr, void (*pr)(const char *, ...))
{
@ -839,6 +843,93 @@ lockdebug_lock_print(void *addr, void (*pr)(const char *, ...))
#endif /* LOCKDEBUG */
}
#ifdef LOCKDEBUG
static void
lockdebug_show_all_locks_lwp(void (*pr)(const char *, ...), bool show_trace)
{
struct proc *p;
LIST_FOREACH(p, &allproc, p_list) {
struct lwp *l;
LIST_FOREACH(l, &p->p_lwps, l_sibling) {
lockdebug_t *ld;
const char *sym;
int i = 0;
if (TAILQ_EMPTY(&l->l_ld_locks))
continue;
(*pr)("Locks held by an LWP (%s):\n",
l->l_name ? l->l_name : p->p_comm);
TAILQ_FOREACH(ld, &l->l_ld_locks, ld_chain) {
ksyms_getname(NULL, &sym,
(vaddr_t)ld->ld_initaddr,
KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY);
(*pr)("Initialized at %s\n", sym);
(*pr)("Lock %d (initialized at %s)\n", i++, sym);
lockdebug_dump(ld, pr);
}
if (show_trace) {
db_stack_trace_print((db_expr_t)(intptr_t)l,
true,
32 /* Limit just in case */,
"a", pr);
}
(*pr)("\n");
}
}
}
static void
lockdebug_show_all_locks_cpu(void (*pr)(const char *, ...), bool show_trace)
{
lockdebug_t *ld;
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
const char *sym;
for (CPU_INFO_FOREACH(cii, ci)) {
int i = 0;
if (TAILQ_EMPTY(&ci->ci_data.cpu_ld_locks))
continue;
(*pr)("Locks held on CPU %u:\n", ci->ci_index);
TAILQ_FOREACH(ld, &ci->ci_data.cpu_ld_locks, ld_chain) {
ksyms_getname(NULL, &sym,
(vaddr_t)ld->ld_initaddr,
KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY);
(*pr)("Lock %d (initialized at %s)\n", i++, sym);
lockdebug_dump(ld, pr);
if (show_trace) {
db_stack_trace_print(
(db_expr_t)(intptr_t)ci->ci_curlwp,
true,
32 /* Limit just in case */,
"a", pr);
}
(*pr)("\n");
}
}
}
#endif /* LOCKDEBUG */
void
lockdebug_show_all_locks(void (*pr)(const char *, ...), const char *modif)
{
#ifdef LOCKDEBUG
bool show_trace = false;
if (modif[0] == 't')
show_trace = true;
(*pr)("[Locks tracked through LWPs]\n");
lockdebug_show_all_locks_lwp(pr, show_trace);
(*pr)("\n");
(*pr)("[Locks tracked through CPUs]\n");
lockdebug_show_all_locks_cpu(pr, show_trace);
(*pr)("\n");
#else
(*pr)("Sorry, kernel not built with the LOCKDEBUG option.\n");
#endif /* LOCKDEBUG */
}
void
lockdebug_show_lockstat(void (*pr)(const char *, ...))
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: lockdebug.h,v 1.17 2018/03/16 04:37:55 ozaki-r Exp $ */
/* $NetBSD: lockdebug.h,v 1.18 2018/03/16 04:44:51 ozaki-r Exp $ */
/*-
* Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
@ -58,6 +58,8 @@ void lockdebug_abort(const char *, size_t, const volatile void *,
void lockdebug_lock_print(void *, void (*)(const char *, ...)
__printflike(1, 2));
void lockdebug_show_all_locks(void (*)(const char *, ...) __printflike(1, 2),
const char *);
void lockdebug_show_lockstat(void (*)(const char *, ...) __printflike(1, 2));
#ifdef LOCKDEBUG