Allow KASSERT to log instead of panic.
This is to allow debug images to be used without taking down the system when non-fatal asserts are hit. The following sysctls are added: debug.kassert.warn_only: 1 = log, 0 = panic debug.kassert.do_ktr: set to a ktr mask for logging via KTR debug.kassert.do_log: 1 = log, 0 = quiet debug.kassert.warnings: stats, number of kasserts hit debug.kassert.log_panic_at: number of kasserts before we actually panic, 0 = never debug.kassert.log_pps_limit: pps limit for log messages debug.kassert.log_mute_at: stop warning after N kasserts, 0 = never stop debug.kassert.kassert: set this sysctl to trigger a kassert Discussed with: scottl, gnn, marcel Sponsored by: iXsystems
This commit is contained in:
parent
b9fab40a3d
commit
3945a96431
@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kerneldump.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/ktr.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/priv.h>
|
||||
@ -150,6 +151,7 @@ static void poweroff_wait(void *, int);
|
||||
static void shutdown_halt(void *junk, int howto);
|
||||
static void shutdown_panic(void *junk, int howto);
|
||||
static void shutdown_reset(void *junk, int howto);
|
||||
static void vpanic(const char *fmt, va_list ap) __dead2;
|
||||
|
||||
/* register various local shutdown events */
|
||||
static void
|
||||
@ -538,6 +540,120 @@ shutdown_reset(void *junk, int howto)
|
||||
/* NOTREACHED */ /* assuming reset worked */
|
||||
}
|
||||
|
||||
#ifdef INVARIANTS
|
||||
static int kassert_warn_only = 0;
|
||||
#ifdef KTR
|
||||
static int kassert_do_ktr = 0;
|
||||
#endif
|
||||
static int kassert_do_log = 1;
|
||||
static int kassert_log_pps_limit = 4;
|
||||
static int kassert_log_mute_at = 0;
|
||||
static int kassert_log_panic_at = 0;
|
||||
static int kassert_warnings = 0;
|
||||
|
||||
SYSCTL_NODE(_debug, OID_AUTO, kassert, CTLFLAG_RW, NULL, "kassert options");
|
||||
|
||||
SYSCTL_INT(_debug_kassert, OID_AUTO, warn_only, CTLFLAG_RW | CTLFLAG_TUN,
|
||||
&kassert_warn_only, 0,
|
||||
"KASSERT triggers a panic (1) or just a warning (0)");
|
||||
TUNABLE_INT("debug.kassert.warn_only", &kassert_warn_only);
|
||||
|
||||
#ifdef KTR
|
||||
SYSCTL_UINT(_debug_kassert, OID_AUTO, do_ktr, CTLFLAG_RW | CTLFLAG_TUN,
|
||||
&kassert_do_ktr, 0,
|
||||
"KASSERT does a KTR, set this to the KTRMASK you want");
|
||||
TUNABLE_INT("debug.kassert.do_ktr", &kassert_do_ktr);
|
||||
#endif
|
||||
|
||||
SYSCTL_INT(_debug_kassert, OID_AUTO, do_log, CTLFLAG_RW | CTLFLAG_TUN,
|
||||
&kassert_do_log, 0, "KASSERT triggers a panic (1) or just a warning (0)");
|
||||
TUNABLE_INT("debug.kassert.do_log", &kassert_do_log);
|
||||
|
||||
SYSCTL_INT(_debug_kassert, OID_AUTO, warnings, CTLFLAG_RW | CTLFLAG_TUN,
|
||||
&kassert_warnings, 0, "number of KASSERTs that have been triggered");
|
||||
TUNABLE_INT("debug.kassert.warnings", &kassert_warnings);
|
||||
|
||||
SYSCTL_INT(_debug_kassert, OID_AUTO, log_panic_at, CTLFLAG_RW | CTLFLAG_TUN,
|
||||
&kassert_log_panic_at, 0, "max number of KASSERTS before we will panic");
|
||||
TUNABLE_INT("debug.kassert.log_panic_at", &kassert_log_panic_at);
|
||||
|
||||
SYSCTL_INT(_debug_kassert, OID_AUTO, log_pps_limit, CTLFLAG_RW | CTLFLAG_TUN,
|
||||
&kassert_log_pps_limit, 0, "limit number of log messages per second");
|
||||
TUNABLE_INT("debug.kassert.log_pps_limit", &kassert_log_pps_limit);
|
||||
|
||||
SYSCTL_INT(_debug_kassert, OID_AUTO, log_mute_at, CTLFLAG_RW | CTLFLAG_TUN,
|
||||
&kassert_log_mute_at, 0, "max number of KASSERTS to log");
|
||||
TUNABLE_INT("debug.kassert.log_mute_at", &kassert_log_mute_at);
|
||||
|
||||
static int kassert_sysctl_kassert(SYSCTL_HANDLER_ARGS);
|
||||
|
||||
SYSCTL_PROC(_debug_kassert, OID_AUTO, kassert,
|
||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE, NULL, 0,
|
||||
kassert_sysctl_kassert, "I", "set to trigger a test kassert");
|
||||
|
||||
static int
|
||||
kassert_sysctl_kassert(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error, i;
|
||||
|
||||
error = sysctl_wire_old_buffer(req, sizeof(int));
|
||||
if (error == 0) {
|
||||
i = 0;
|
||||
error = sysctl_handle_int(oidp, &i, 0, req);
|
||||
}
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
KASSERT(0, ("kassert_sysctl_kassert triggered kassert %d", i));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by KASSERT, this decides if we will panic
|
||||
* or if we will log via printf and/or ktr.
|
||||
*/
|
||||
void
|
||||
kassert_panic(const char *fmt, ...)
|
||||
{
|
||||
static char buf[256];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
(void)vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
/*
|
||||
* panic if we're not just warning, or if we've exceeded
|
||||
* kassert_log_panic_at warnings.
|
||||
*/
|
||||
if (!kassert_warn_only ||
|
||||
(kassert_log_panic_at > 0 &&
|
||||
kassert_warnings >= kassert_log_panic_at)) {
|
||||
va_start(ap, fmt);
|
||||
vpanic(fmt, ap);
|
||||
/* NORETURN */
|
||||
}
|
||||
#ifdef KTR
|
||||
if (kassert_do_ktr)
|
||||
CTR0(ktr_mask, buf);
|
||||
#endif /* KTR */
|
||||
/*
|
||||
* log if we've not yet met the mute limit.
|
||||
*/
|
||||
if (kassert_do_log &&
|
||||
(kassert_log_mute_at == 0 ||
|
||||
kassert_warnings < kassert_log_mute_at)) {
|
||||
static struct timeval lasterr;
|
||||
static int curerr;
|
||||
|
||||
if (ppsratecheck(&lasterr, &curerr, kassert_log_pps_limit)) {
|
||||
printf("KASSERT failed: %s\n", buf);
|
||||
kdb_backtrace();
|
||||
}
|
||||
}
|
||||
atomic_add_int(&kassert_warnings, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Panic is called on unresolvable fatal errors. It prints "panic: mesg",
|
||||
* and then reboots. If we are called twice, then we avoid trying to sync
|
||||
@ -545,13 +661,21 @@ shutdown_reset(void *junk, int howto)
|
||||
*/
|
||||
void
|
||||
panic(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vpanic(fmt, ap);
|
||||
}
|
||||
|
||||
static void
|
||||
vpanic(const char *fmt, va_list ap)
|
||||
{
|
||||
#ifdef SMP
|
||||
cpuset_t other_cpus;
|
||||
#endif
|
||||
struct thread *td = curthread;
|
||||
int bootopt, newpanic;
|
||||
va_list ap;
|
||||
static char buf[256];
|
||||
|
||||
spinlock_enter();
|
||||
@ -587,7 +711,6 @@ panic(const char *fmt, ...)
|
||||
newpanic = 1;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (newpanic) {
|
||||
(void)vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
panicstr = buf;
|
||||
@ -598,7 +721,6 @@ panic(const char *fmt, ...)
|
||||
vprintf(fmt, ap);
|
||||
printf("\n");
|
||||
}
|
||||
va_end(ap);
|
||||
#ifdef SMP
|
||||
printf("cpuid = %d\n", PCPU_GET(cpuid));
|
||||
#endif
|
||||
|
@ -73,14 +73,16 @@ extern int vm_guest; /* Running as virtual machine guest? */
|
||||
enum VM_GUEST { VM_GUEST_NO = 0, VM_GUEST_VM, VM_GUEST_XEN };
|
||||
|
||||
#ifdef INVARIANTS /* The option is always available */
|
||||
void kassert_panic(const char *fmt, ...);
|
||||
|
||||
#define KASSERT(exp,msg) do { \
|
||||
if (__predict_false(!(exp))) \
|
||||
panic msg; \
|
||||
kassert_panic msg; \
|
||||
} while (0)
|
||||
#define VNASSERT(exp, vp, msg) do { \
|
||||
if (__predict_false(!(exp))) { \
|
||||
vn_printf(vp, "VNASSERT failed\n"); \
|
||||
panic msg; \
|
||||
kassert_panic msg; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
|
Loading…
x
Reference in New Issue
Block a user