PASSERT() - asserting for panics

John Baldwin jhb at FreeBSD.org
Wed Sep 10 20:13:22 UTC 2008


So one of the things I like to do is use kernel modules that do regression 
tests.  One of the things I want to test is that certain invalid operations 
will cause a specific panic.  In the past I've done nefarious things 
like '#define panic printf' at the top of kern_rwlock.c and such. :)  
However, this is not suitable for more widespread use.  The other approach of 
having the tests panic and verifying the panics that way is tedious.  So what 
I came up with is a way to assert that a given panic will be triggered by a 
chunk of code, and if that panic happens, the kernel doesn't actually panic.  
The way I implemented this was by having the actual test do a setjmp() and if 
the "expected" panic triggers, then panic() does a longjmp() before setting 
panicstr, etc.  A simple example of using it would be:

	PASSERT("foo", panic("foo"));

The patch is at http://www.FreeBSD.org/~jhb/patches/passert.patch and below:

--- //depot/projects/smpng/sys/kern/kern_shutdown.c	2008/03/18 12:54:14
+++ //depot/user/jhb/lock/kern/kern_shutdown.c	2008/09/10 17:40:44
@@ -531,6 +531,17 @@
 	if (panicstr)
 		bootopt |= RB_NOSYNC;
 	else {
+#ifdef INVARIANT_SUPPORT
+		if (td->td_expected_panic != NULL &&
+		    strcmp(td->td_expected_panic, fmt) == 0) {
+			va_start(ap, fmt);
+			printf("expected panic: ");
+			vprintf(fmt, ap);
+			printf("\n");
+			va_end(ap);
+			longjmp(td->td_panic_buf, 1);
+		}
+#endif
 		panicstr = fmt;
 		newpanic = 1;
 	}
--- //depot/projects/smpng/sys/kern/kern_thread.c	2008/08/25 16:33:41
+++ //depot/user/jhb/lock/kern/kern_thread.c	2008/09/09 18:31:16
@@ -35,6 +35,7 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
+#include <sys/malloc.h>
 #include <sys/mutex.h>
 #include <sys/proc.h>
 #include <sys/resourcevar.h>
@@ -48,6 +49,7 @@
 #include <sys/umtx.h>
 #include <sys/cpuset.h>
 
+#include <machine/setjmp.h>
 #include <security/audit/audit.h>
 
 #include <vm/vm.h>
@@ -161,6 +163,9 @@
 	td->td_sched = (struct td_sched *)&td[1];
 	umtx_thread_init(td);
 	td->td_kstack = 0;
+#ifdef INVARIANT_SUPPORT
+	td->td_panic_buf = malloc(sizeof(struct _jmp_buf), M_SUBPROC, M_WAITOK);
+#endif
 	return (0);
 }
 
@@ -178,6 +183,9 @@
 	sleepq_free(td->td_sleepqueue);
 	umtx_thread_fini(td);
 	seltdfini(td);
+#ifdef INVARIANT_SUPPORT
+	free(td->td_panic_buf, M_SUBPROC);
+#endif
 }
 
 /*
--- //depot/projects/smpng/sys/sys/proc.h	2008/08/25 16:33:41
+++ //depot/user/jhb/lock/sys/proc.h	2008/09/09 18:31:16
@@ -166,6 +166,7 @@
 struct kdtrace_proc;
 struct kdtrace_thread;
 struct cpuset;
+struct _jmp_buf;
 
 /*
  * Kernel runnable context (thread).
@@ -273,6 +274,8 @@
 	struct lpohead	td_lprof[2];	/* (a) lock profiling objects. */
 	struct kdtrace_thread	*td_dtrace; /* (*) DTrace-specific data. */
 	int		td_errno;	/* Error returned by last syscall. */
+	struct _jmp_buf *td_panic_buf;	/* (k) Jump buffer for PASSERT(). */
+	const char	*td_expected_panic; /* (k) Expected panic. */
 };
 
 struct mtx *thread_lock_block(struct thread *);
--- //depot/projects/smpng/sys/sys/systm.h	2008/07/25 18:20:23
+++ //depot/user/jhb/lock/sys/systm.h	2008/09/09 18:31:16
@@ -71,9 +71,25 @@
 	if (__predict_false(!(exp)))					\
 		panic msg;						\
 } while (0)
+
+#define	PASSERT(panicstr, code) do {					\
+	switch (setjmp(curthread->td_panic_buf)) {			\
+	case 0:								\
+		curthread->td_expected_panic = (panicstr);		\
+		code;							\
+		panic("Expected panic '%s' did not trigger",		\
+		    (panicstr));					\
+	case 1:								\
+		curthread->td_expected_panic = NULL;			\
+		break;							\
+	default:							\
+		panic("Unexpected return value from setjmp()");		\
+	}								\
+} while (0)
+
 #define	VNASSERT(exp, vp, msg) do {					\
 	if (__predict_false(!(exp))) {					\
 		vn_printf(vp, "VNASSERT failed\n");			\
 		panic msg;						\
 	}								\
 } while (0)
@@ -81,6 +97,9 @@
 #define	KASSERT(exp,msg) do { \
 } while (0)
 
+#define	PASSERT(panicstr, code) do {					\
+} while (0)
+
 #define	VNASSERT(exp, vp, msg) do { \
 } while (0)
 #endif

Is this too evil for the tree?

-- 
John Baldwin


More information about the freebsd-arch mailing list