debugm.h 10.6 KB
Newer Older
1
/*****************************************************************************\
S
Shengliang Guan 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
*                                                                             *
*   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.
*		    							      *
47
*         Copyright 2016 Hewlett Packard Enterprise Development LP          *
S
Shengliang Guan 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
* 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) */