Skip to content

Commit

Permalink
Windows: Add custom debug printing for your asserts
Browse files Browse the repository at this point in the history
Signed-off-by: Jorgen Lundman <lundman@lundman.net>
  • Loading branch information
lundman committed Sep 2, 2024
1 parent e22f997 commit 8203d5b
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 86 deletions.
312 changes: 227 additions & 85 deletions include/os/windows/spl/sys/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,12 @@ extern void printBuffer(const char *fmt, ...);
DbgBreakPoint(); \
} while (0)

#define SPL_DEBUG_STR " (DEBUG mode)"

#else // DBG

#define SPL_DEBUG_STR ""

#define TraceEvent(x, ...)
#define xprintf(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, \
__VA_ARGS__)
Expand All @@ -149,113 +153,251 @@ extern void printBuffer(const char *fmt, ...);
} while (0)
#endif

#ifdef DBG /* Debugging Disabled */
#define zfs_fallthrough __attribute__((__fallthrough__))

/* Define SPL_DEBUG_STR to make clear which ASSERT definitions are used */
#define SPL_DEBUG_STR " (DEBUG mode)"
/* From here is a copy of FreeBSD's debug.h */

/* ASSERTION that is safe to use within the debug system */
#define __ASSERT(cond) \
do { \
if (unlikely(!(cond))) { \
printk(KERN_EMERG "ASSERTION(" #cond ") failed\n"); \
BUG(); \
} \
/*
* Without this, we see warnings from objtool during normal Linux builds when
* the kernel is built with CONFIG_STACK_VALIDATION=y:
*
* warning: objtool: tsd_create() falls through to next function __list_add()
* warning: objtool: .text: unexpected end of section
*
* Until the toolchain stops doing this, we must only define this attribute on
* spl_panic() when doing static analysis.
*/
#if defined(__COVERITY__) || defined(__clang_analyzer__)
__attribute__((__noreturn__))
#endif
extern void spl_panic(const char *file, const char *func, int line,
const char *fmt, ...);
extern void spl_dumpstack(void);

static inline int
spl_assert(const char *buf, const char *file, const char *func, int line)
{
spl_panic(file, func, line, "%s", buf);
return (0);
}

#define VERIFY(cond) \
(void) (unlikely(!(cond)) && \
spl_assert("VERIFY(" #cond ") failed\n", \
__FILE__, __FUNCTION__, __LINE__))

#define VERIFYF(cond, str, ...) do { \
if (unlikely(!cond)) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY(" #cond ") failed " str "\n", __VA_ARGS__);\
} while (0)

#define ASSERTF(cond, fmt, ...) \
do { \
if (unlikely(!(cond))) \
PANIC("ASSERTION(" #cond ") failed: " fmt, \
__VA_ARGS__); \
#define VERIFY3B(LEFT, OP, RIGHT) do { \
const boolean_t _verify3_left = (boolean_t)(LEFT); \
const boolean_t _verify3_right = (boolean_t)(RIGHT); \
if (unlikely(!(_verify3_left OP _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \
"failed (%d " #OP " %d)\n", \
(boolean_t)_verify3_left, \
(boolean_t)_verify3_right); \
} while (0)

#define ASSERT3B(x, y, z) VERIFY3B(x, y, z)
#define ASSERT3S(x, y, z) VERIFY3S(x, y, z)
#define ASSERT3U(x, y, z) VERIFY3U(x, y, z)
#define ASSERT3P(x, y, z) VERIFY3P(x, y, z)
#define ASSERT0P(x) VERIFY0P(x)
#define ASSERT0(x) VERIFY0(x)
#define VERIFY3S(LEFT, OP, RIGHT) do { \
const int64_t _verify3_left = (int64_t)(LEFT); \
const int64_t _verify3_right = (int64_t)(RIGHT); \
if (unlikely(!(_verify3_left OP _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \
"failed (%lld " #OP " %lld)\n", \
(long long)_verify3_left, \
(long long)_verify3_right); \
} while (0)

#define ASSERTV(x) x
#define VERIFY3U(LEFT, OP, RIGHT) do { \
const uint64_t _verify3_left = (uint64_t)(LEFT); \
const uint64_t _verify3_right = (uint64_t)(RIGHT); \
if (unlikely(!(_verify3_left OP _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \
"failed (%llu " #OP " %llu)\n", \
(unsigned long long)_verify3_left, \
(unsigned long long)_verify3_right); \
} while (0)

#ifndef ZFS_DEBUG
#define ZFS_DEBUG 1
#endif
#define VERIFY3P(LEFT, OP, RIGHT) do { \
const uintptr_t _verify3_left = (uintptr_t)(LEFT); \
const uintptr_t _verify3_right = (uintptr_t)(RIGHT); \
if (unlikely(!(_verify3_left OP _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \
"failed (%px " #OP " %px)\n", \
(void *)_verify3_left, \
(void *)_verify3_right); \
} while (0)

#else /* Debugging Enabled */
#define VERIFY0(RIGHT) do { \
const int64_t _verify0_right = (int64_t)(RIGHT); \
if (unlikely(!(0 == _verify0_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY0(" #RIGHT ") " \
"failed (0 == %lld)\n", \
(long long)_verify0_right); \
} while (0)

/* Define SPL_DEBUG_STR to make clear which ASSERT definitions are used */
#define SPL_DEBUG_STR ""
#define VERIFY0P(RIGHT) do { \
const uintptr_t _verify0_right = (uintptr_t)(RIGHT); \
if (unlikely(!(0 == _verify0_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY0P(" #RIGHT ") " \
"failed (NULL == %px)\n", \
(void *)_verify0_right); \
} while (0)

#define __ASSERT(x) ((void)0)
#define ASSERTF(x, y, z, ...) ((void)0)
#define ASSERTV(x)
/*
* Note that you should not put any operations you want to always happen
* in the print section for ASSERTs unless you only want them to run on
* debug builds!
* e.g. ASSERT3UF(2, <, 3, "%s", foo(x)), foo(x) won't run on non-debug
* builds.
*/

#define VERIFY3BF(LEFT, OP, RIGHT, STR, ...) do { \
const boolean_t _verify3_left = (boolean_t)(LEFT); \
const boolean_t _verify3_right = (boolean_t)(RIGHT); \
if (unlikely(!(_verify3_left OP _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \
"failed (%d " #OP " %d) " STR "\n", \
(boolean_t)(_verify3_left), \
(boolean_t)(_verify3_right), \
__VA_ARGS__); \
} while (0)

#define VERIFY3SF(LEFT, OP, RIGHT, STR, ...) do { \
const int64_t _verify3_left = (int64_t)(LEFT); \
const int64_t _verify3_right = (int64_t)(RIGHT); \
if (unlikely(!(_verify3_left OP _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \
"failed (%lld " #OP " %lld) " STR "\n", \
(long long)(_verify3_left), \
(long long)(_verify3_right), \
__VA_ARGS); \
} while (0)

#define VERIFY3UF(LEFT, OP, RIGHT, STR, ...) do { \
const uint64_t _verify3_left = (uint64_t)(LEFT); \
const uint64_t _verify3_right = (uint64_t)(RIGHT); \
if (unlikely(!(_verify3_left OP _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \
"failed (%llu " #OP " %llu) " STR "\n", \
(unsigned long long)(_verify3_left), \
(unsigned long long)(_verify3_right), \
__VA_ARGS); \
} while (0)

#define VERIFY3PF(LEFT, OP, RIGHT, STR, ...) do { \
const uintptr_t _verify3_left = (uintptr_t)(LEFT); \
const uintptr_t _verify3_right = (uintptr_t)(RIGHT); \
if (unlikely(!(_verify3_left OP _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \
"failed (%px " #OP " %px) " STR "\n", \
(void *) (_verify3_left), \
(void *) (_verify3_right), \
__VA_ARGS__); \
} while (0)

#define ASSERT3B(x, y, z) ((void)0)
#define ASSERT3S(x, y, z) ((void)0)
#define ASSERT3U(x, y, z) ((void)0)
#define ASSERT3P(x, y, z) ((void)0)
#define ASSERT0P(x) ((void)0)
#define ASSERT0(x) ((void)0)

#endif /* DBG */

#define VERIFY3_IMPL(LEFT, OP, RIGHT, TYPE, FMT, CAST) \
do { \
TYPE _verify3_left = (TYPE)(LEFT); \
TYPE _verify3_right = (TYPE)(RIGHT); \
if (!(_verify3_left OP _verify3_right)) \
PANIC("VERIFY3( %s " #OP " %s ) " \
"failed (" FMT " " #OP " " FMT \
")\n", #LEFT, #RIGHT, \
CAST(_verify3_left), CAST(_verify3_right)); \
#define VERIFY0PF(RIGHT, STR, ...) do { \
const uintptr_t _verify3_left = (uintptr_t)(0); \
const uintptr_t _verify3_right = (uintptr_t)(RIGHT); \
if (unlikely(!(_verify3_left == _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY0(0 == " #RIGHT ") " \
"failed (0 == %px) " STR "\n", \
(long long) (_verify3_right), \
__VA_ARGS__); \
} while (0)

#define VERIFY3B(x, y, z) VERIFY3_IMPL(x, y, z, int64_t, "%lld", (boolean_t))
#define VERIFY3S(x, y, z) VERIFY3_IMPL(x, y, z, int64_t, "%lld", (long long))
#define VERIFY3U(x, y, z) VERIFY3_IMPL(x, y, z, uint64_t, "%llu", \
(unsigned long long))
#define VERIFY3P(x, y, z) VERIFY3_IMPL(x, y, z, uintptr_t, "%p", (void *))
#define VERIFY0P(x) VERIFY3_IMPL(0, ==, x, uintptr_t, "%p", (void *))
#define VERIFY0(x) VERIFY3_IMPL(0, ==, x, int64_t, "%lld", (long long))

#define VERIFY(EX) do { \
if (!(EX)) panic("PANIC: %s %s:%d\n", #EX, __FILE__, \
__LINE__); \
#define VERIFY0F(RIGHT, STR, ...) do { \
const int64_t _verify3_left = (int64_t)(0); \
const int64_t _verify3_right = (int64_t)(RIGHT); \
if (unlikely(!(_verify3_left == _verify3_right))) \
spl_panic(__FILE__, __FUNCTION__, __LINE__, \
"VERIFY0(0 == " #RIGHT ") " \
"failed (0 == %lld) " STR "\n", \
(long long) (_verify3_right), \
__VA_ARGS__); \
} while (0)

#define VERIFY_IMPLY(A, B) \
((void)(likely((!(A)) || (B)) || \
spl_assert("(" #A ") implies (" #B ")", \
__FILE__, __FUNCTION__, __LINE__)))

#define VERIFY_EQUIV(A, B) \
((void)(likely(!!(A) == !!(B)) || \
spl_assert("(" #A ") is equivalent to (" #B ")", \
__FILE__, __FUNCTION__, __LINE__)))

/*
* IMPLY and EQUIV are assertions of the form:
*
* if (a) then (b)
* and
* if (a) then (b) *AND* if (b) then (a)
* Debugging disabled (--disable-debug)
*/
#if DEBAG
#define IMPLY(A, B) \
((void)(((!(A)) || (B)) || \
panic("(" #A ") implies (" #B ")", __FILE__, __LINE__)))
#define EQUIV(A, B) \
((void)((!!(A) == !!(B)) || \
panic("(" #A ") is equivalent to (" #B ")", __FILE__, \
__LINE__)))
#else
#define IMPLY(A, B) ((void)0)
#define EQUIV(A, B) ((void)0)
#ifdef _WIN32
#undef ASSERT
#endif

#if !defined(DBG)

#define ASSERT(x) ((void) sizeof ((uintptr_t)(x)))
#define ASSERTV(x)
#define ASSERT3B(x, y, z) \
((void) sizeof ((uintptr_t)(x)), (void) sizeof ((uintptr_t)(z)))
#define ASSERT3S(x, y, z) \
((void) sizeof ((uintptr_t)(x)), (void) sizeof ((uintptr_t)(z)))
#define ASSERT3U(x, y, z) \
((void) sizeof ((uintptr_t)(x)), (void) sizeof ((uintptr_t)(z)))
#define ASSERT3P(x, y, z) \
((void) sizeof ((uintptr_t)(x)), (void) sizeof ((uintptr_t)(z)))
#define ASSERT0(x) ((void) sizeof ((uintptr_t)(x)))
#define ASSERT0P(x) ((void) sizeof ((uintptr_t)(x)))
#define ASSERT3BF(x, y, z, str, ...) ASSERT3B(x, y, z)
#define ASSERT3SF(x, y, z, str, ...) ASSERT3S(x, y, z)
#define ASSERT3UF(x, y, z, str, ...) ASSERT3U(x, y, z)
#define ASSERT3PF(x, y, z, str, ...) ASSERT3P(x, y, z)
#define ASSERT0PF(x, str, ...) ASSERT0P(x)
#define ASSERT0F(x, str, ...) ASSERT0(x)
#define ASSERTF(x, str, ...) ASSERT(x)
#define IMPLY(A, B) \
((void) sizeof ((uintptr_t)(A)), (void) sizeof ((uintptr_t)(B)))
#define EQUIV(A, B) \
((void) sizeof ((uintptr_t)(A)), (void) sizeof ((uintptr_t)(B)))

/*
* Compile-time assertion. The condition 'x' must be constant.
* Debugging enabled (--enable-debug)
*/
#define CTASSERT_GLOBAL(x) _CTASSERT(x, __LINE__)
#define CTASSERT(x) { _CTASSERT(x, __LINE__); }
#define _CTASSERT(x, y) __CTASSERT(x, y)
#define __CTASSERT(x, y) \
typedef char __attribute__((unused)) \
__compile_time_assertion__ ## y[(x) ? 1 : -1]

#define zfs_fallthrough __attribute__((__fallthrough__))
#else

#define ASSERT3B VERIFY3B
#define ASSERT3S VERIFY3S
#define ASSERT3U VERIFY3U
#define ASSERT3P VERIFY3P
#define ASSERT0 VERIFY0
#define ASSERT0P VERIFY0P
#define ASSERT3BF VERIFY3BF
#define ASSERT3SF VERIFY3SF
#define ASSERT3UF VERIFY3UF
#define ASSERT3PF VERIFY3PF
#define ASSERT0PF VERIFY0PF
#define ASSERT0F VERIFY0F
#define ASSERTF VERIFYF
#define ASSERTV(x) x
#define ASSERT VERIFY
#define IMPLY VERIFY_IMPLY
#define EQUIV VERIFY_EQUIV

#endif /* NDEBUG */

#endif /* SPL_DEBUG_H */
2 changes: 1 addition & 1 deletion module/os/windows/spl/spl-err.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ cmn_err(int ce, const char *fmt, ...)
va_end(ap);
} /* cmn_err() */

int
void
spl_panic(const char *file, const char *func, int line, const char *fmt, ...)
{
va_list ap;
Expand Down

0 comments on commit 8203d5b

Please sign in to comment.