/*****************************************************************************\ * * * Filename: debugm.h * * * * Description: Debug macros * * * * Notes: Macros inspired by my Tcl/Bash/Batch debugging libraries. * * The idea is to output the function call stack, indenting * * subroutines proportionally to the call depth. * * To ease reading the output, it must look like real C code.* * * * DEBUG_GLOBALS Define global variables used by macros below: * * int iDebug = FALSE; Global variable enabling debug output if TRUE.* * int iIndent = 0; Global variable controlling debug indentation.* * * * DEBUG_PRINTF((format, ...)) Print a debug string if debug is on. * * The double parenthesis are necessary * * because C90 does not support macros * * with variable list of arguments. * * DEBUG_ENTER((format, ...)) Print a function name and arguments. * * Increase indentation of further calls.* * It's the caller's responsibility to * * format the routine name and arguments.* * DEBUG_LEAVE((format, ...)) Print a function return value. * * Decrease indentation of further calls.* * It's the caller's responsibility to * * format the return instruction & value.* * * * Any call to DEBUG_ENTER MUST be matched by one call to * * DEBUG_LEAVE or RETURN_... when the function returns. * * * * * History: * * 2012-01-16 JFL jf.larvoire@hp.com created this file. * * 2012-02-03 JFL Renamed DEBUG_IF_IS_ON DEBUG_CODE_IF_ON. * * Renamed file from debug.h to debugm.h because of a file * * name collision with another library on my PC. * * 2014-02-10 JFL Added macros for an extra debug mode. * * 2014-07-02 JFL renamed macro RETURN() as RETURN_CONST(), and defined * * new macro RETURN() to return nothing. * * Idem for RETURN_COMMENT() as RETURN_CONST_COMMENT(). * * 2016-09-09 JFL Flush every DEBUG_PRINTF output, to make sure to see * * every debug string printed before a program crash. * * 2016-09-13 JFL Added macros DEBUG_WSTR2NEWUTF8() and DEBUG_FREEUTF8(). * * 2016-10-04 JFL Added macros DEBUG_OFF(), DEBUG_MORE(), DEBUG_LESS(). * * Allow using DEBUG_ON()/MORE()/LESS()/OFF() in release mode. * * * © Copyright 2016 Hewlett Packard Enterprise Development LP * * Licensed under the Apache 2.0 license - www.apache.org/licenses/LICENSE-2.0 * \*****************************************************************************/ #ifndef _DEBUGM_H #define _DEBUGM_H 1 #include /* Macros use printf */ #ifdef _MSC_VER #pragma warning(disable:4127) /* Avoid warnings on while(0) below */ #endif #define DEBUG_DO(code) do {code} while (0) #define DEBUG_DO_NOTHING() do {} while (0) /* Conditional compilation based on Microsoft's standard _DEBUG definition */ #if defined(_DEBUG) #define DEBUG_VERSION " Debug" #define DEBUG_GLOBALS \ int iDebug = 0; /* Global variable enabling debug output if TRUE. */ \ int iIndent = 0; /* Global variable controlling debug indentation. */ extern int iDebug; /* Global variable enabling of disabling debug messages */ #define DEBUG_ON() iDebug = 1 /* Turn debug mode on */ #define DEBUG_MORE() iDebug += 1 /* Increase the debug level */ #define DEBUG_LESS() iDebug -= 1 /* Decrease the debug level */ #define DEBUG_OFF() iDebug = 0 /* Turn debug mode off */ #define DEBUG_IS_ON() (iDebug > 0) /* Check if the debug mode is enabled */ #define XDEBUG_ON() iDebug = 2 /* Turn extra debug mode on. Same as calling DEBUG_MORE() twice. */ #define XDEBUG_IS_ON() (iDebug > 1) /* Check if the extra debug mode is enabled */ #define DEBUG_CODE(code) code /* Code included in the _DEBUG version only */ #define DEBUG_CODE_IF_ON(code) DEBUG_CODE(if (DEBUG_IS_ON()) {code}) /* Debug code executed if debug mode is on */ #define XDEBUG_CODE_IF_ON(code) DEBUG_CODE(if (XDEBUG_IS_ON()) {code}) /* Debug code executed if extra debug mode is on */ extern int iIndent; /* Debug messages indentation */ #define DEBUG_INDENT_STEP 2 /* How many spaces to add for each indentation level */ #define DEBUG_PRINT_INDENT() printf("%*s", iIndent, "") /* Debug code, conditionally printing a string based on global variable 'debug' */ /* The enter and leave variants print, then respectively increase or decrease indentation, to make recursive calls easier to review. */ #define DEBUG_FPRINTF(args) DEBUG_DO(if (DEBUG_IS_ON()) {DEBUG_PRINT_INDENT(); fprintf args;}) #define DEBUG_PRINTF(args) DEBUG_DO(if (DEBUG_IS_ON()) {DEBUG_PRINT_INDENT(); printf args; fflush(stdout);}) #define XDEBUG_PRINTF(args) DEBUG_DO(if (XDEBUG_IS_ON()) {DEBUG_PRINT_INDENT(); printf args; fflush(stdout);}) #define DEBUG_ENTER(args) DEBUG_DO(DEBUG_PRINTF(args); iIndent += DEBUG_INDENT_STEP;) #define DEBUG_LEAVE(args) DEBUG_DO(DEBUG_PRINTF(args); iIndent -= DEBUG_INDENT_STEP;) #define DEBUG_RETURN_INT(i, comment) DEBUG_DO(int DEBUG_i = (i); \ DEBUG_LEAVE(("return %d; // " comment "\n", DEBUG_i)); return DEBUG_i;) /* print return instruction and decrease indent */ #define RETURN() DEBUG_DO(DEBUG_LEAVE(("return;\n")); return;) #define RETURN_CONST(value) DEBUG_DO(DEBUG_LEAVE(("return %s;\n", #value)); return value;) #define RETURN_INT(i) DEBUG_DO(int DEBUG_i = (i); \ DEBUG_LEAVE(("return %d;\n", DEBUG_i)); return DEBUG_i;) #define RETURN_STRING(s) DEBUG_DO(char *DEBUG_s = (s); \ DEBUG_LEAVE(("return \"%s\";\n", DEBUG_s)); return DEBUG_s;) #define RETURN_CHAR(c) DEBUG_DO(char DEBUG_c = (c); \ DEBUG_LEAVE(("return '%c';\n", DEBUG_c)); return DEBUG_c;) #define RETURN_BOOL(b) DEBUG_DO(int DEBUG_b = (b); \ DEBUG_LEAVE(("return %s;\n", DEBUG_b ? "TRUE" : "FALSE")); return DEBUG_b;) #define RETURN_COMMENT(args) DEBUG_DO(DEBUG_LEAVE(("return; // ")); \ if (DEBUG_IS_ON()) printf args; return;) #define RETURN_CONST_COMMENT(value, args) DEBUG_DO(DEBUG_LEAVE(("return %s; // ", #value)); \ if (DEBUG_IS_ON()) printf args; return value;) #define RETURN_INT_COMMENT(i, args) DEBUG_DO(int DEBUG_i = (i); \ DEBUG_LEAVE(("return %d; // ", DEBUG_i)); if (DEBUG_IS_ON()) printf args; return DEBUG_i;) #define RETURN_BOOL_COMMENT(b, args) DEBUG_DO(int DEBUG_b = (b); \ DEBUG_LEAVE(("return %s; // ", DEBUG_b ? "TRUE" : "FALSE")); if (DEBUG_IS_ON()) printf args; return DEBUG_b;) #else /* !defined(_DEBUG) */ #define DEBUG_VERSION "" /* Non debug version: Simply don't say it */ #define DEBUG_GLOBALS #define DEBUG_ON() (void)0 #define DEBUG_MORE() (void)0 #define DEBUG_LESS() (void)0 #define DEBUG_OFF() (void)0 #define DEBUG_IS_ON() 0 #define XDEBUG_IS_ON() 0 #define DEBUG_CODE(code) /* Code included in _DEBUG version only */ #define DEBUG_CODE_IF_ON(code) /* Code included in _DEBUG version only */ #define XDEBUG_CODE_IF_ON(code) /* Code included in _DEBUG version only */ #define DEBUG_PRINT_INDENT() DEBUG_DO_NOTHING() /* Print call-depth spaces */ #define DEBUG_FPRINTF(args) DEBUG_DO_NOTHING() /* Print a debug string to a stream */ #define DEBUG_PRINTF(args) DEBUG_DO_NOTHING() /* Print a debug string to stdout */ #define XDEBUG_PRINTF(args) DEBUG_DO_NOTHING() /* Print an extra debug string to stdout */ #define DEBUG_ENTER(args) DEBUG_DO_NOTHING() /* Print and increase indent */ #define DEBUG_LEAVE(args) DEBUG_DO_NOTHING() /* Print and decrease indent */ #define DEBUG_RETURN_INT(i, comment) return(i) /* print return instruction and decrease indent */ #define RETURN() return #define RETURN_CONST(value) return(value) #define RETURN_INT(i) return(i) #define RETURN_STRING(s) return(s) #define RETURN_CHAR(c) return(c) #define RETURN_BOOL(b) return(b) #define RETURN_COMMENT(args) return #define RETURN_CONST_COMMENT(value, args) return(value) #define RETURN_INT_COMMENT(i, args) return(i) #define RETURN_BOOL_COMMENT(b, args) return(b) #endif /* defined(_DEBUG) */ #define STRINGIZE(s) #s /* Convert a macro name to a string */ #define VALUEIZE(s) STRINGIZE(s) /* Convert a macro value to a string */ #define MACRODEF(s) "#define " #s " " STRINGIZE(s) /* Display a macro name and value. */ #define DEBUG_PRINT_MACRO(name) DEBUG_DO( \ const char *pszName = #name; /* Don't use STRINGIZE because we're already inside a macro */ \ const char *pszValue = STRINGIZE(name); /* Don't use VALUEIZE because we're already inside a macro */ \ DEBUG_PRINT_INDENT(); \ if (strcmp(pszName, pszValue)) { \ printf("#define %s %s\n", pszName, pszValue); \ } else { /* Not 100% certain, but most likely. */ \ printf("#undef %s\n", pszName); \ } \ ) #ifdef _WIN32 /* Helper macros for displaying Unicode strings */ #define DEBUG_WSTR2UTF8(from, to, toSize) DEBUG_CODE( \ WideCharToMultiByte(CP_UTF8, 0, from, lstrlenW(from)+1, to, toSize, NULL, NULL); \ ) /* Dynamically allocate a new buffer, then convert a Unicode string to UTF-8 */ /* The dynamic allocation is useful in modules using lots of UTF-16 pathnames. This avoids having many local buffers of length UTF8_PATH_MAX, which may make the stack grow too large and overflow. */ #define DEBUG_WSTR2NEWUTF8(pwStr, pUtf8) \ DEBUG_CODE( \ do { \ int nUtf8 = (int)lstrlenW(pwStr) * 2 + 1; \ pUtf8 = malloc(nUtf8); \ DEBUG_WSTR2UTF8(pwStr, pUtf8, nUtf8); \ } while (0); \ ) /* DEBUG_FREE(pUtf8) MUST be used to free the UTF-8 string after use, else there will be a memory leak */ #define DEBUG_FREEUTF8(pUtf8) DEBUG_CODE(free(pUtf8)) #endif /* defined(_WIN32) */ #endif /* !defined(_DEBUGM_H) */