1 /******************************************************************************\ 2 * Copyright (c) 2016, Robert van Engelen, Genivia Inc. All rights reserved. * 3 * * 4 * Redistribution and use in source and binary forms, with or without * 5 * modification, are permitted provided that the following conditions are met: * 6 * * 7 * (1) Redistributions of source code must retain the above copyright notice, * 8 * this list of conditions and the following disclaimer. * 9 * * 10 * (2) Redistributions in binary form must reproduce the above copyright * 11 * notice, this list of conditions and the following disclaimer in the * 12 * documentation and/or other materials provided with the distribution. * 13 * * 14 * (3) The name of the author may not be used to endorse or promote products * 15 * derived from this software without specific prior written permission. * 16 * * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * 18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * 20 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * 26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * 27 \******************************************************************************/ 28 29 /** 30 @file debug.h 31 @brief RE/flex debug logs and assertions 32 @author Robert van Engelen - engelen@genivia.com 33 @copyright (c) 2016-2020, Robert van Engelen, Genivia Inc. All rights reserved. 34 @copyright (c) BSD-3 License - see LICENSE.txt 35 36 Exploiting macro magic to simplify debug logging. 37 38 Usage 39 ----- 40 41 Enable macro DEBUG to debug the compiled source code: 42 43 | Source files compiled with | DBGLOG(...) entry added to | 44 | ----------------------------- | ----------------------------- | 45 | `c++ -DDEBUG` | `DEBUG.log` | 46 | `c++ -DDEBUG=TEST` | `TEST.log` | 47 | `c++ -DDEBUG= ` | `stderr` | 48 49 `DBGLOG(format, ...)` creates a timestamped log entry with a printf-formatted 50 message. The log entry is added to a log file or sent to `stderr` as specified: 51 52 `DBGLOGN(format, ...)` creates a log entry without a timestamp. 53 54 `DBGLOGA(format, ...)` appends the formatted string to the previous log entry. 55 56 `DBGCHK(condition)` calls `assert(condition)` when compiled in DEBUG mode. 57 58 The utility macro `DBGSTR(const char *s)` returns string `s` or `"(null)"` when 59 `s == NULL`. 60 61 @note to temporarily enable debugging a specific block of code without globally 62 debugging all code, use a leading underscore, e.g. `_DBGLOG(format, ...)`. 63 This appends the debugging information to `DEBUG.log`. 64 65 @warning Be careful to revert these statements by removing the leading 66 underscore for production-quality code. 67 68 Example 69 ------- 70 71 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} 72 #include <reflex/debug.h> 73 74 int main(int argc, char *argv[]) 75 { 76 FILE *fd; 77 DBGLOG("Program start"); 78 if ((fd = fopen("foo.bar", "r")) == NULL) 79 { 80 DBGLOG("Error %d: %s ", errno, DBGSTR(strerror(errno))); 81 for (int i = 1; i < argc; ++i) 82 DBGLOGA(" %s", argv[1]); 83 } 84 else 85 { 86 DBGCHK(fd != NULL); 87 // OK, so go ahead to read foo.bar ... 88 // ... 89 fclose(fd); 90 } 91 DBGLOG("Program end"); 92 } 93 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 94 95 Compiled with `-DDEBUG` this example logs the following messages in `DEBUG.log`: 96 97 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.txt} 98 140201/225654.692194 example.cpp:11 Program has started 99 140201/225654.692564 example.cpp:15 Error 2: No such file or directory 100 140201/225654.692577 example.cpp:17 Program ended 101 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 102 103 The first column records the date (140201 is February 1, 2014) and the time 104 (225654 is 10:56PM + 54 seconds) with microsecond fraction. The second column 105 records the source code file name and the line number of the `DBGLOG` command. 106 The third column shows the printf-formatted message. 107 108 The `DEBUG.log` file is created in the current directory when it does not 109 already exist. 110 111 Techniques used: 112 113 - Variadic macros with `__VA_ARGS__`. 114 - Standard predefined macros `__FILE__` and `__LINE__`. 115 - Macro "stringification": expand content of macro `DEBUG` as a string in a 116 macro body. 117 - `#if DEBUG + 0` to test whether macro `DEBUG` is set to a value, since 118 `DEBUG` is 1 when set without a value (for example at the command line). 119 - `"" __VA_ARGS__` forces `__VA_ARGS__` to start with a literal format string 120 (printf security advisory). 121 */ 122 123 #ifndef REFLEX_DEBUG_H 124 #define REFLEX_DEBUG_H 125 126 #include <cassert> 127 #include <cstdio> 128 129 /// If ASSERT not defined, make ASSERT a no-op 130 #ifndef ASSERT 131 #define ASSERT(c) 132 #endif 133 134 #undef DBGLOG 135 #undef DBGLOGN 136 #undef DBGLOGA 137 138 extern FILE *REFLEX_DBGFD_; 139 140 extern "C" void REFLEX_DBGOUT_(const char *log, const char *file, int line); 141 142 #define DBGXIFY(S) DBGIFY_(S) 143 #define DBGIFY_(S) #S 144 #if DEBUG + 0 145 # define DBGFILE "DEBUG.log" 146 #else 147 # define DBGFILE DBGXIFY(DEBUG) ".log" 148 #endif 149 #define DBGSTR(S) (S?S:"(NULL)") 150 #define _DBGLOG(...) \ 151 ( REFLEX_DBGOUT_(DBGFILE, __FILE__, __LINE__), ::fprintf(REFLEX_DBGFD_, "" __VA_ARGS__), ::fflush(REFLEX_DBGFD_)) 152 #define _DBGLOGN(...) \ 153 ( ::fprintf(REFLEX_DBGFD_, "\n " __VA_ARGS__), ::fflush(REFLEX_DBGFD_) ) 154 #define _DBGLOGA(...) \ 155 ( ::fprintf(REFLEX_DBGFD_, "" __VA_ARGS__), ::fflush(REFLEX_DBGFD_) ) 156 157 #ifdef DEBUG 158 159 #define DBGCHK(c) assert(c) 160 161 #define DBGLOG _DBGLOG 162 #define DBGLOGN _DBGLOGN 163 #define DBGLOGA _DBGLOGA 164 165 #else 166 167 #define DBGCHK(c) (void)0 168 169 #define DBGLOG(...) (void)0 170 #define DBGLOGN(...) (void)0 171 #define DBGLOGA(...) (void)0 172 173 #endif 174 175 #endif 176