#ifndef lib_dbg_h #define lib_dbg_h /* Cesar project {{{ * * Copyright (C) 2007 Spidcom * * <<>> * * }}} */ /** * \file lib/dbg.h * \brief Debug functions. * \ingroup lib */ #include "config/debug.h" #include "lib/blame.h" /** Shortcut for CONFIG_DEBUG */ #define DEBUG CONFIG_DEBUG /** Shortcut for CONFIG_DEBUG_MORE. * If activated, costly debugs are activated. */ #define DEBUG_MORE CONFIG_DEBUG_MORE /** Blaming cannot be terse. */ #if CONFIG_DEBUG_TERSE && CONFIG_BLAME # error "CONFIG_DEBUG_TERSE and CONFIG_BLAME are not compatibles" #endif #if DEBUG && CONFIG_DEBUG_FATAL_CATCH # include "lib/try.h" #endif #include /** * Check that the provided expression is true. * \param expr the assumed truth * * \warning \a expr is not evaluated when asserts are disabled. */ #define dbg_assert(expr) \ dbg_assert_ (expr, #expr) /** * Check that the provided pointer is fine. * \param ptr the pointer to check * * \warning \a ptr is not evaluated when asserts are disabled. */ #define dbg_assert_ptr(ptr) \ dbg_assert_ptr_ (ptr, #ptr) /** * Check that the provided expression is true and provide more information on * failure. * \param expr the assumed truth * \param fmt printf-like format string * \param rest printf-like arguments * * \warning \a expr is not evaluated when asserts are disabled. */ #define dbg_assert_print(expr, fmt, rest...) \ dbg_assert_print_ (expr, fmt, ## rest) /** * Check that the provided expression is true and print additional errno * information on failure. * \param expr the assumed truth * * \warning \a expr is not evaluated when asserts are disabled. */ #define dbg_assert_perror(expr) \ dbg_assert_perror_ (expr, #expr) /** * Use this macro in a forbidden default clause in switches. */ #define dbg_assert_default() \ dbg_assert_default_ () /** * Check that the provided expression is true, evaluate the expression even * when asserts are disabled. * \param expr the assumed truth * * \warning \a expr is always evaluated. */ #define dbg_check(expr) \ dbg_check_ (expr, #expr) /** * Check that the provided expression is true, evaluate the expression even * when asserts are disabled and provide more information on failure. * \param expr the assumed truth * \param fmt printf-like format string * \param rest printf-like arguments * * \warning \a expr is always evaluated. */ #define dbg_check_print(expr, fmt, rest...) \ dbg_check_print_ (expr, fmt, ## rest) /** * Check that the provided expression is true and print additional errno * information on failure, evaluate the expression even when asserts are * disabled. * \param expr the assumed truth * * \warning \a expr is always evaluated. */ #define dbg_check_perror(expr) \ dbg_check_perror_ (expr, #expr) /** * Claim that the provided expression is true, check is conditioned by * CONFIG_DEBUG_CLAIM. * \param expr the assumed truth * * \warning \a expr is not evaluated when asserts are disabled. */ #define dbg_claim(expr) \ dbg_claim_ (expr, #expr) /** * Check that the provided pointer is fine, check is conditioned by * CONFIG_DEBUG_CLAIM. * \param ptr the pointer to check * * \warning \a ptr is not evaluated when asserts are disabled. */ #define dbg_claim_ptr(ptr) \ dbg_claim_ptr_ (ptr, #ptr) /** * Check that the provided expression is true, blame the caller. * \param expr the assumed truth * * Faulty function is taken from _fl_ (see blame.h). * * \warning \a expr is not evaluated when asserts are disabled. */ #define dbg_blame(expr) \ dbg_blame_ (expr, #expr) /** * Check that the provided pointer is fine, blame the caller. * \param ptr the pointer to check * * Faulty function is taken from _fl_ (see blame.h). * * \warning \a ptr is not evaluated when asserts are disabled. */ #define dbg_blame_ptr(ptr) \ dbg_blame_ptr_ (ptr, #ptr) /** * Check that the provided expression is true and provide more information on * failure, blame the caller. * \param expr the assumed truth * \param fmt printf-like format string * \param rest printf-like arguments * * Faulty function is taken from _fl_ (see blame.h). * * \warning \a expr is not evaluated when asserts are disabled. */ #define dbg_blame_print(expr, fmt, rest...) \ dbg_blame_print_ (expr, fmt, ## rest) /** * Use this macro in a forbidden default clause in switches, blame the caller. * * Faulty function is taken from _fl_ (see blame.h). */ #define dbg_blame_default() \ dbg_blame_default_ () /* Asserts definitions. */ #if DEBUG # define DBG_ASSERT_FMT_ "%s:%d: assertion failure in %s: " # define dbg_assert_ dbg_assert__ # define dbg_assert_ptr_ dbg_assert_ptr__ # define dbg_assert_print_ dbg_assert_print__ # define dbg_assert_perror_ dbg_assert_perror__ # define dbg_assert_default_ dbg_assert_default__ # define dbg_check_ dbg_assert__ # define dbg_check_print_ dbg_assert_print__ # define dbg_check_perror_ dbg_assert_perror__ # if CONFIG_DEBUG_CLAIM # define dbg_claim_ dbg_assert__ # define dbg_claim_ptr_ dbg_assert_ptr__ # else # define dbg_claim_ dbg_void__ # define dbg_claim_ptr_ dbg_void__ # endif # if CONFIG_BLAME # define dbg_blame_ dbg_blame__ # define dbg_blame_ptr_ dbg_blame_ptr__ # define dbg_blame_print_ dbg_blame_print__ # define dbg_blame_default_ dbg_blame_default__ # else # define dbg_blame_ dbg_assert__ # define dbg_blame_ptr_ dbg_assert_ptr__ # define dbg_blame_print_ dbg_assert_print__ # define dbg_blame_default_ dbg_assert_default__ # endif # if !CONFIG_DEBUG_TERSE # define dbg_assert_fail_ dbg_assert_fail # define dbg_assert_print_fail_(fmt, file, line, function, rest...) \ dbg_assert_print_fail (DBG_ASSERT_FMT_ fmt, file, line, \ function, ## rest) # else # define dbg_assert_fail_(message, file, line, function) \ dbg_assert_fail_terse (file ":" #line ": assertion failure: " message) # define dbg_assert_print_fail_(fmt, file, line, function, rest...) \ dbg_assert_fail_terse (file ":" #line ": assertion failure: " fmt) # endif # define dbg_void__(args...) ((void) 0) # define dbg_assert__(expr, exprs) \ ((void) ((expr) ? 0 : (dbg_assert_fail_ (exprs, __FILE__, __LINE__, \ __PRETTY_FUNCTION__), 0))) # define dbg_assert_ptr__(ptr, ptrs) \ ((void) ((ptr && ptr != INVALID_PTR) ? 0 : \ (dbg_assert_fail_ ("@" ptrs, __FILE__, __LINE__, \ __PRETTY_FUNCTION__), 0))) # define dbg_assert_print__(expr, fmt, rest...) \ ((void) ((expr) ? 0 : (dbg_assert_print_fail_ (fmt, __FILE__, __LINE__, \ __PRETTY_FUNCTION__ \ , ##rest)), 0)) # define dbg_assert_perror__(expr, exprs) \ ((void) ((expr) ? 0 : (dbg_assert_perror_fail (exprs, __FILE__, __LINE__, \ __PRETTY_FUNCTION__), 0))) # define dbg_assert_default__() \ dbg_assert_fail_ ("unexpected default case", __FILE__, __LINE__, \ __PRETTY_FUNCTION__) # define dbg_blame__(expr, exprs) \ ((void) ((expr) ? 0 : (dbg_assert_fail_ (exprs, _fl_, \ __PRETTY_FUNCTION__), 0))) # define dbg_blame_ptr__(ptr, ptrs) \ ((void) ((ptr && ptr != INVALID_PTR) ? 0 : \ (dbg_assert_fail_ ("@" ptrs, _fl_, __PRETTY_FUNCTION__), 0))) # define dbg_blame_print__(expr, fmt, rest...) \ ((void) ((expr) ? 0 : (dbg_assert_print_fail_ (fmt, _fl_, \ __PRETTY_FUNCTION__ \ , ##rest), 0))) # define dbg_blame_default__() \ dbg_assert_fail_ ("unexpected default case", _fl_, \ __PRETTY_FUNCTION__) #else /* !DEBUG */ # define dbg_assert_ dbg_void__ # define dbg_assert_ptr_ dbg_void__ # define dbg_assert_print_ dbg_void_print__ # define dbg_assert_perror_ dbg_void__ # define dbg_assert_default_ dbg_fatal_blind # define dbg_check_ dbg_eval__ # define dbg_check_print_ dbg_eval_print__ # define dbg_check_perror_ dbg_eval__ # define dbg_claim_ dbg_void__ # define dbg_claim_ptr_ dbg_void__ # define dbg_blame_ dbg_void__ # define dbg_blame_ptr_ dbg_void__ # define dbg_blame_print_ dbg_void_print__ # define dbg_blame_default_ dbg_fatal_blind # define dbg_void__(args...) ((void) 0) # define dbg_eval__(expr, exprs) ((void) (expr, 0)) # define dbg_void_print__(expr, fmt, rest...) ((void) 0) # define dbg_eval_print__(expr, fmt, rest...) ((void) (expr, 0)) #endif /* !DEBUG */ /** * Invalidate a pointer. * \param ptr pointer to invalidate * * The pointer is set to an invalid value. This is better than set to NULL, * because one could test a pointer for this special NULL value and be very * disappointed when compiling without debug. */ #define dbg_invalid_ptr(ptr) dbg_invalid_ptr_ (ptr) /** * Compile something only when debug is activated. * \param smth the thing conditionally compiled */ #define dbg_do(smth) dbg_do_ (smth) #if DEBUG # define dbg_invalid_ptr_(ptr) (ptr) = INVALID_PTR # define dbg_do_(smth) smth #else /* !DEBUG */ # define dbg_invalid_ptr_(ptr) ((void) 0) # define dbg_do_(smth) #endif /* !DEBUG */ /** * Begin a try block to catch fatal errors. * * dbg_fatal_try_begin, dbg_fatal_try_catch and dbg_fatal_try_end define a * try-catch sequence to catch fatal errors: * * \code * dbg_fatal_try_begin * { * ...code which could fail... * } * dbg_fatal_try_catch (const char *fatal_message) * { * ...code to handle failure... * } * dbg_fatal_try_end; * \endcode * * This is implemented using setjmp, please see setjmp documentation for * quirks about this. */ #define dbg_fatal_try_begin dbg_fatal_try_begin_ /** End a try block to catch fatal errors. */ #define dbg_fatal_try_end dbg_fatal_try_end_ /** Catch fatal errors. */ #define dbg_fatal_try_catch(vardecl) dbg_fatal_try_catch_(vardecl) /** Use in place of dbg_fatal_try_catch when not interested by the message. */ #define dbg_fatal_try_catch_void() dbg_fatal_try_catch_void_ #if DEBUG && CONFIG_DEBUG_FATAL_CATCH extern int dbg_fatal_try_level_; /** Fatal error message buffer. */ extern char dbg_fatal_text_[]; # define dbg_fatal_try_begin_ \ { \ dbg_fatal_try_level_++; \ try_begin \ { #define dbg_fatal_try_catch_(vardecl) \ } \ try_catch (TRY_CODE_FATAL) \ { \ vardecl = dbg_fatal_text_; \ { #define dbg_fatal_try_catch_void_ \ } \ try_catch (TRY_CODE_FATAL) \ { \ { # define dbg_fatal_try_end_ \ } \ } \ try_always \ { \ dbg_fatal_try_level_--; \ } \ try_end; \ } #else /* !(DEBUG && CONFIG_DEBUG_FATAL_CATCH) */ # define dbg_fatal_try_begin_ \ { \ if (1) \ { #define dbg_fatal_try_catch_(vardecl) \ } \ else \ { \ vardecl = NULL; \ { #define dbg_fatal_try_catch_void_ \ } \ else \ { \ { # define dbg_fatal_try_end_ \ } \ } \ } #endif /* !(DEBUG && CONFIG_DEBUG_FATAL_CATCH) */ BEGIN_DECLS /** * Called on assertion failure. * \param assertion assertion text * \param file source file name * \param line source file line * \param function source function name */ void dbg_assert_fail (const char *assertion, const char *file, uint line, const char *function) __attribute__ ((__noreturn__)); /** * Called on assertion failure, terse version. * \param message message text */ void dbg_assert_fail_terse (const char *message) __attribute__ ((__noreturn__)); /** * Called on assertion failure with more information on failure. * \param fmt printf-like format string */ void dbg_assert_print_fail (const char *fmt, ...) __attribute__ ((__noreturn__, format (printf, 1, 2))); /** * Called on errno assertion failure. * \param assertion assertion text * \param file source file name * \param line source file line * \param function source function name * * \todo implement */ void dbg_assert_perror_fail (const char *assertion, const char *file, uint line, const char *function) __attribute__ ((__noreturn__)); /** * Stop the program with a fatal error, but no other indication. */ void dbg_fatal_blind (void) __attribute__ ((__noreturn__)); extern void (*dbg_fatal_cb[4]) (void); /** * Stop the program with a fatal error. * \param fmt printf-like format string */ void dbg_fatal (const char *fmt, ...) __attribute__ ((__noreturn__, format (printf, 1, 2))); /** * Stop the program with a fatal error, variable arguments version. * \param fmt printf-like format string * \param ap argument list pointer */ void dbg_vfatal (const char *fmt, va_list ap) __attribute__ ((__noreturn__)); END_DECLS #endif /* lib_dbg_h */