207 lines
11 KiB
C
207 lines
11 KiB
C
/*****************************************************************************\
|
|
* *
|
|
* 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 <stdio.h> /* 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) */
|
|
|