1 /***********************************************************************************************************************************
2 Debug Routines
3 ***********************************************************************************************************************************/
4 #ifndef COMMON_DEBUG_H
5 #define COMMON_DEBUG_H
6 
7 #include "common/assert.h"
8 #include "common/stackTrace.h"
9 #include "common/type/convert.h"
10 #include "common/type/stringZ.h"
11 
12 /***********************************************************************************************************************************
13 Base function debugging macros
14 
15 In debug mode parameters will always be recorded in the stack trace while in production mode they will only be recorded when the log
16 level is set to debug or trace.
17 ***********************************************************************************************************************************/
18 #define FUNCTION_LOG_LEVEL()                                                                                                       \
19     FUNCTION_LOG_logLevel
20 
21 #ifdef DEBUG_TEST_TRACE
22     #define FUNCTION_LOG_BEGIN_BASE(logLevel)                                                                                      \
23         LogLevel FUNCTION_LOG_LEVEL() = STACK_TRACE_PUSH(logLevel);                                                                \
24                                                                                                                                    \
25         {                                                                                                                          \
26             stackTraceParamLog();                                                                                                  \
27             stackTraceTestStop();
28 
29     #define FUNCTION_LOG_END_BASE()                                                                                                \
30             stackTraceTestStart();                                                                                                 \
31             LOG_FMT(FUNCTION_LOG_LEVEL(), 0, "(%s)", stackTraceParam());                                                           \
32         }
33 #else
34     #define FUNCTION_LOG_BEGIN_BASE(logLevel)                                                                                      \
35         LogLevel FUNCTION_LOG_LEVEL() = STACK_TRACE_PUSH(logLevel);                                                                \
36                                                                                                                                    \
37         if (logAny(FUNCTION_LOG_LEVEL()))                                                                                          \
38         {                                                                                                                          \
39             stackTraceParamLog();
40 
41     #define FUNCTION_LOG_END_BASE()                                                                                                \
42             LOG_FMT(FUNCTION_LOG_LEVEL(), 0, "(%s)", stackTraceParam());                                                           \
43         }
44 #endif
45 
46 /***********************************************************************************************************************************
47 General purpose function debugging macros
48 
49 FUNCTION_LOG_VOID() is provided as a shortcut for functions that have no parameters.
50 ***********************************************************************************************************************************/
51 #define FUNCTION_LOG_BEGIN(logLevel)                                                                                               \
52     FUNCTION_LOG_BEGIN_BASE(logLevel)
53 
54 #define FUNCTION_LOG_END()                                                                                                         \
55     FUNCTION_LOG_END_BASE()
56 
57 #define FUNCTION_LOG_VOID(logLevel)                                                                                                \
58     FUNCTION_LOG_BEGIN_BASE(logLevel);                                                                                             \
59     FUNCTION_LOG_END_BASE()
60 
61 #define FUNCTION_LOG_PARAM(typeMacroPrefix, param)                                                                                 \
62     stackTraceParamAdd(FUNCTION_LOG_##typeMacroPrefix##_FORMAT(param, stackTraceParamBuffer(#param), STACK_TRACE_PARAM_MAX))
63 
64 #define FUNCTION_LOG_PARAM_P(typeMacroPrefix, param)                                                                               \
65     do                                                                                                                             \
66     {                                                                                                                              \
67         char *buffer = stackTraceParamBuffer(#param);                                                                              \
68                                                                                                                                    \
69         if (param == NULL)                                                                                                         \
70             stackTraceParamAdd(typeToLog(NULL_Z, buffer, STACK_TRACE_PARAM_MAX));                                                  \
71         else                                                                                                                       \
72         {                                                                                                                          \
73             buffer[0] = '*';                                                                                                       \
74             stackTraceParamAdd(FUNCTION_LOG_##typeMacroPrefix##_FORMAT(*param, buffer + 1, STACK_TRACE_PARAM_MAX - 1) + 1);        \
75         }                                                                                                                          \
76     }                                                                                                                              \
77     while (0)
78 
79 #define FUNCTION_LOG_PARAM_PP(typeMacroPrefix, param)                                                                              \
80     do                                                                                                                             \
81     {                                                                                                                              \
82         char *buffer = stackTraceParamBuffer(#param);                                                                              \
83                                                                                                                                    \
84         if (param == NULL)                                                                                                         \
85             stackTraceParamAdd(typeToLog(NULL_Z, buffer, STACK_TRACE_PARAM_MAX));                                                  \
86         else if (*param == NULL)                                                                                                   \
87             stackTraceParamAdd(typeToLog("*null", buffer, STACK_TRACE_PARAM_MAX));                                                 \
88         else                                                                                                                       \
89         {                                                                                                                          \
90             buffer[0] = '*';                                                                                                       \
91             buffer[1] = '*';                                                                                                       \
92             stackTraceParamAdd(FUNCTION_LOG_##typeMacroPrefix##_FORMAT(**param, buffer + 2, STACK_TRACE_PARAM_MAX - 2) + 2);       \
93         }                                                                                                                          \
94     }                                                                                                                              \
95     while (0)
96 
97 /***********************************************************************************************************************************
98 Functions and macros to render various data types
99 ***********************************************************************************************************************************/
100 // Convert object to a zero-terminated string for logging
101 size_t objToLog(const void *object, const char *objectName, char *buffer, size_t bufferSize);
102 
103 // Convert pointer to a zero-terminated string for logging
104 size_t ptrToLog(const void *pointer, const char *pointerName, char *buffer, size_t bufferSize);
105 
106 // Convert zero-terminated string for logging
107 size_t strzToLog(const char *string, char *buffer, size_t bufferSize);
108 
109 // Convert a type name to a zero-terminated string for logging
110 size_t typeToLog(const char *typeName, char *buffer, size_t bufferSize);
111 
112 #define FUNCTION_LOG_BOOL_TYPE                                                                                                     \
113     bool
114 #define FUNCTION_LOG_BOOL_FORMAT(value, buffer, bufferSize)                                                                        \
115     cvtBoolToZ(value, buffer, bufferSize)
116 
117 #define FUNCTION_LOG_CHAR_TYPE                                                                                                     \
118     char
119 #define FUNCTION_LOG_CHAR_FORMAT(value, buffer, bufferSize)                                                                        \
120     cvtCharToZ(value, buffer, bufferSize)
121 
122 #define FUNCTION_LOG_CHARDATA_TYPE                                                                                                 \
123     char
124 #define FUNCTION_LOG_CHARDATA_FORMAT(value, buffer, bufferSize)                                                                    \
125     typeToLog("(char)", buffer, bufferSize)
126 
127 #define FUNCTION_LOG_CHARPY_TYPE                                                                                                   \
128     char *[]
129 #define FUNCTION_LOG_CHARPY_FORMAT(value, buffer, bufferSize)                                                                      \
130     ptrToLog(value, "char *[]", buffer, bufferSize)
131 
132 #define FUNCTION_LOG_DOUBLE_TYPE                                                                                                   \
133     double
134 #define FUNCTION_LOG_DOUBLE_FORMAT(value, buffer, bufferSize)                                                                      \
135     cvtDoubleToZ(value, buffer, bufferSize)
136 
137 #define FUNCTION_LOG_INT_TYPE                                                                                                      \
138     int
139 #define FUNCTION_LOG_INT_FORMAT(value, buffer, bufferSize)                                                                         \
140     cvtIntToZ(value, buffer, bufferSize)
141 
142 #define FUNCTION_LOG_INT64_TYPE                                                                                                    \
143     int64_t
144 #define FUNCTION_LOG_INT64_FORMAT(value, buffer, bufferSize)                                                                       \
145     cvtInt64ToZ(value, buffer, bufferSize)
146 
147 #define FUNCTION_LOG_ENUM_TYPE                                                                                                     \
148     unsigned int
149 #define FUNCTION_LOG_ENUM_FORMAT(value, buffer, bufferSize)                                                                        \
150     FUNCTION_LOG_UINT_FORMAT(value, buffer, bufferSize)
151 
152 #define FUNCTION_LOG_FUNCTIONP_FORMAT(value, buffer, bufferSize)                                                                   \
153     ptrToLog(value == NULL ? NULL : (void *)1, "function *", buffer, bufferSize)
154 
155 #define FUNCTION_LOG_MODE_TYPE                                                                                                     \
156     mode_t
157 #define FUNCTION_LOG_MODE_FORMAT(value, buffer, bufferSize)                                                                        \
158     cvtModeToZ(value, buffer, bufferSize)
159 
160 #define FUNCTION_LOG_TIMEMSEC_TYPE                                                                                                 \
161     TimeMSec
162 #define FUNCTION_LOG_TIMEMSEC_FORMAT(value, buffer, bufferSize)                                                                    \
163     cvtUInt64ToZ(value, buffer, bufferSize)
164 
165 #define FUNCTION_LOG_UCHARDATA_TYPE                                                                                                \
166     unsigned char
167 #define FUNCTION_LOG_UCHARDATA_FORMAT(value, buffer, bufferSize)                                                                   \
168     typeToLog("(unsigned char)", buffer, bufferSize)
169 
170 #define FUNCTION_LOG_SIZE_TYPE                                                                                                     \
171     size_t
172 #define FUNCTION_LOG_SIZE_FORMAT(value, buffer, bufferSize)                                                                        \
173     cvtSizeToZ(value, buffer, bufferSize)
174 
175 #define FUNCTION_LOG_SSIZE_TYPE                                                                                                    \
176     ssize_t
177 #define FUNCTION_LOG_SSIZE_FORMAT(value, buffer, bufferSize)                                                                       \
178     cvtSSizeToZ(value, buffer, bufferSize)
179 
180 #define FUNCTION_LOG_TIME_TYPE                                                                                                     \
181     time_t
182 #define FUNCTION_LOG_TIME_FORMAT(value, buffer, bufferSize)                                                                        \
183     cvtTimeToZ(value, buffer, bufferSize)
184 
185 #define FUNCTION_LOG_UINT_TYPE                                                                                                     \
186     unsigned int
187 #define FUNCTION_LOG_UINT_FORMAT(value, buffer, bufferSize)                                                                        \
188     cvtUIntToZ(value, buffer, bufferSize)
189 
190 #define FUNCTION_LOG_UINT16_TYPE                                                                                                   \
191     uint16_t
192 #define FUNCTION_LOG_UINT16_FORMAT(value, buffer, bufferSize)                                                                      \
193     FUNCTION_LOG_UINT_FORMAT(value, buffer, bufferSize)
194 
195 #define FUNCTION_LOG_UINT32_TYPE                                                                                                   \
196     uint32_t
197 #define FUNCTION_LOG_UINT32_FORMAT(value, buffer, bufferSize)                                                                      \
198     FUNCTION_LOG_UINT_FORMAT(value, buffer, bufferSize)
199 
200 #define FUNCTION_LOG_UINT64_TYPE                                                                                                   \
201     uint64_t
202 #define FUNCTION_LOG_UINT64_FORMAT(value, buffer, bufferSize)                                                                      \
203     cvtUInt64ToZ(value, buffer, bufferSize)
204 
205 #define FUNCTION_LOG_VOID_TYPE                                                                                                     \
206     void
207 #define FUNCTION_LOG_VOID_FORMAT(value, buffer, bufferSize)                                                                        \
208     typeToLog("void", buffer, bufferSize)
209 
210 #define FUNCTION_LOG_STRINGZ_TYPE                                                                                                  \
211     char *
212 #define FUNCTION_LOG_STRINGZ_FORMAT(value, buffer, bufferSize)                                                                     \
213     strzToLog(value, buffer, bufferSize)
214 
215 /***********************************************************************************************************************************
216 Macros to return function results (or void)
217 ***********************************************************************************************************************************/
218 #define FUNCTION_LOG_RETURN_BASE(typePre, typeMacroPrefix, typePost, result)                                                       \
219     do                                                                                                                             \
220     {                                                                                                                              \
221         typePre FUNCTION_LOG_##typeMacroPrefix##_TYPE typePost FUNCTION_LOG_RETURN_result = result;                                \
222                                                                                                                                    \
223         STACK_TRACE_POP(false);                                                                                                    \
224                                                                                                                                    \
225         IF_LOG_ANY(FUNCTION_LOG_LEVEL())                                                                                           \
226         {                                                                                                                          \
227             char buffer[STACK_TRACE_PARAM_MAX];                                                                                    \
228                                                                                                                                    \
229             FUNCTION_LOG_##typeMacroPrefix##_FORMAT(FUNCTION_LOG_RETURN_result, buffer, sizeof(buffer));                           \
230             LOG_FMT(FUNCTION_LOG_LEVEL(), 0, "=> %s", buffer);                                                                     \
231         }                                                                                                                          \
232                                                                                                                                    \
233         return FUNCTION_LOG_RETURN_result;                                                                                         \
234     }                                                                                                                              \
235     while (0)
236 
237 #define FUNCTION_LOG_RETURN(typeMacroPrefix, result)                                                                               \
238     FUNCTION_LOG_RETURN_BASE(, typeMacroPrefix, , result)
239 
240 #define FUNCTION_LOG_RETURN_P(typeMacroPrefix, result)                                                                             \
241     FUNCTION_LOG_RETURN_BASE(, typeMacroPrefix, *, result)
242 
243 #define FUNCTION_LOG_RETURN_PP(typeMacroPrefix, result)                                                                            \
244     FUNCTION_LOG_RETURN_BASE(, typeMacroPrefix, **, result)
245 
246 #define FUNCTION_LOG_RETURN_CONST(typeMacroPrefix, result)                                                                         \
247     FUNCTION_LOG_RETURN_BASE(const, typeMacroPrefix, , result)
248 
249 #define FUNCTION_LOG_RETURN_CONST_P(typeMacroPrefix, result)                                                                       \
250     FUNCTION_LOG_RETURN_BASE(const, typeMacroPrefix, *, result)
251 
252 #define FUNCTION_LOG_RETURN_CONST_PP(typeMacroPrefix, result)                                                                      \
253     FUNCTION_LOG_RETURN_BASE(const, typeMacroPrefix, **, result)
254 
255 #define FUNCTION_LOG_RETURN_STRUCT(result)                                                                                         \
256     do                                                                                                                             \
257     {                                                                                                                              \
258         STACK_TRACE_POP(false);                                                                                                    \
259                                                                                                                                    \
260         IF_LOG_ANY(FUNCTION_LOG_LEVEL())                                                                                           \
261             LOG_FMT(FUNCTION_LOG_LEVEL(), 0, "=> struct");                                                                         \
262                                                                                                                                    \
263         return result;                                                                                                             \
264     }                                                                                                                              \
265     while (0)
266 
267 #define FUNCTION_LOG_RETURN_VOID()                                                                                                 \
268     do                                                                                                                             \
269     {                                                                                                                              \
270         STACK_TRACE_POP(false);                                                                                                    \
271                                                                                                                                    \
272         LOG(FUNCTION_LOG_LEVEL(), 0, "=> void");                                                                                   \
273     }                                                                                                                              \
274     while (0)
275 
276 /***********************************************************************************************************************************
277 Function Test Macros
278 
279 In debug builds these macros will update the stack trace with function names and parameters but will not log. In production builds
280 all test macros are compiled out (except for return statements).
281 
282 Ignore DEBUG_TEST_TRACE_MACRO if DEBUG is not defined because the underlying functions that support the macros will not be present.
283 ***********************************************************************************************************************************/
284 #ifdef DEBUG
285 #ifdef DEBUG_TEST_TRACE
286     #define DEBUG_TEST_TRACE_MACRO
287 #endif // DEBUG_TEST_TRACE
288 #endif // DEBUG
289 
290 #ifdef DEBUG_TEST_TRACE_MACRO
291     #define FUNCTION_TEST_BEGIN()                                                                                                  \
292         /* Ensure that FUNCTION_LOG_BEGIN() and FUNCTION_TEST_BEGIN() are not both used in a single function by declaring the */   \
293         /* same variable that FUNCTION_LOG_BEGIN() uses to track logging */                                                        \
294         LogLevel FUNCTION_LOG_LEVEL();                                                                                             \
295         (void)FUNCTION_LOG_LEVEL();                                                                                                \
296                                                                                                                                    \
297         /* Ensure that FUNCTION_TEST_RETURN*() is not used with FUNCTION_LOG_BEGIN*() by declaring a variable that will be */      \
298         /* referenced in FUNCTION_TEST_RETURN*() */                                                                                \
299         bool FUNCTION_TEST_BEGIN_exists;                                                                                           \
300                                                                                                                                    \
301         if (stackTraceTest())                                                                                                      \
302         {                                                                                                                          \
303             STACK_TRACE_PUSH(logLevelDebug);                                                                                       \
304             stackTraceParamLog();                                                                                                  \
305             stackTraceTestStop()
306 
307     #define FUNCTION_TEST_PARAM(typeMacroPrefix, param)                                                                            \
308         FUNCTION_LOG_PARAM(typeMacroPrefix, param)
309 
310     #define FUNCTION_TEST_PARAM_P(typeName, param)                                                                                 \
311         FUNCTION_LOG_PARAM_P(typeName, param)
312 
313     #define FUNCTION_TEST_PARAM_PP(typeName, param)                                                                                \
314         FUNCTION_LOG_PARAM_PP(typeName, param)
315 
316     #define FUNCTION_TEST_END()                                                                                                    \
317             /* CHECK for presense of FUNCTION_TEST_BEGIN*() */                                                                     \
318             (void)FUNCTION_TEST_BEGIN_exists;                                                                                      \
319                                                                                                                                    \
320             stackTraceTestStart();                                                                                                 \
321         }
322 
323     #define FUNCTION_TEST_VOID()                                                                                                   \
324         FUNCTION_TEST_BEGIN();                                                                                                     \
325         FUNCTION_TEST_END();
326 
327     #define FUNCTION_TEST_RETURN(result)                                                                                           \
328         do                                                                                                                         \
329         {                                                                                                                          \
330             /* CHECK for presense of FUNCTION_TEST_BEGIN*() */                                                                     \
331             (void)FUNCTION_TEST_BEGIN_exists;                                                                                      \
332                                                                                                                                    \
333             STACK_TRACE_POP(true);                                                                                                 \
334             return result;                                                                                                         \
335         }                                                                                                                          \
336         while (0)
337 
338     #define FUNCTION_TEST_RETURN_VOID()                                                                                            \
339         do                                                                                                                         \
340         {                                                                                                                          \
341             /* CHECK for presense of FUNCTION_TEST_BEGIN*() */                                                                     \
342             (void)FUNCTION_TEST_BEGIN_exists;                                                                                      \
343                                                                                                                                    \
344             STACK_TRACE_POP(true);                                                                                                 \
345         }                                                                                                                          \
346         while (0)
347 #else
348     #define FUNCTION_TEST_BEGIN()
349     #define FUNCTION_TEST_PARAM(typeMacroPrefix, param)
350     #define FUNCTION_TEST_PARAM_P(typeMacroPrefix, param)
351     #define FUNCTION_TEST_PARAM_PP(typeMacroPrefix, param)
352     #define FUNCTION_TEST_END()
353     #define FUNCTION_TEST_VOID()
354     #define FUNCTION_TEST_RETURN(result)                                                                                           \
355         return result
356     #define FUNCTION_TEST_RETURN_VOID()
357 #endif // DEBUG_TEST_TRACE_MACRO
358 
359 #endif
360