Indentations break the feed.
[SquirrelJME.git] / nanocoat / lib / base / debug.c
blobab9d99f84f602f4d0981ce18c15fdad22005f1f2
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include <stdarg.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
15 #include "sjme/stdTypes.h"
17 #if defined(SJME_CONFIG_HAS_WINDOWS)
18 #define WIN32_LEAN_AND_MEAN 1
20 #include <windows.h>
21 #include <debugapi.h>
23 #undef WIN32_LEAN_AND_MEAN
24 #endif
26 #include "sjme/debug.h"
27 #include "sjme/alloc.h"
28 #include "sjme/dylib.h"
30 /** Debug buffer size for messages. */
31 #define DEBUG_BUF 512
33 sjme_debug_handlerFunctions* sjme_debug_handlers = NULL;
35 void sjme_debug_abort(void)
37 /* Use specific abort handler? */
38 if (sjme_debug_handlers != NULL && sjme_debug_handlers->abort != NULL)
39 if (sjme_debug_handlers->abort())
40 return;
42 #if defined(SJME_CONFIG_HAS_WINDOWS)
43 /* When running tests without a debugger this will pop up about 1000 */
44 /* dialogs saying the program aborted, so only abort on debugging. */
45 if (!IsDebuggerPresent())
46 return;
47 #endif
49 /* Otherwise use C abort handler. */
50 abort();
53 /**
54 * Potentially debug exits.
56 * @param exitCode The exit code.
57 * @since 2023/12/21
59 static void sjme_debug_exit(int exitCode)
61 /* Use specific exit handler? */
62 if (sjme_debug_handlers != NULL && sjme_debug_handlers->exit != NULL)
63 if (sjme_debug_handlers->exit(exitCode))
64 return;
66 /* Fallback to normal exit. */
67 exit(exitCode);
70 sjme_lpcstr sjme_debug_shortenFile(sjme_lpcstr file)
72 sjme_jint i, n;
74 /* There is nothing to shorten. */
75 if (file == NULL)
76 return NULL;
78 /* Try to find nanocoat in there. */
79 n = strlen(file);
80 for (i = (n - 11 >= 0 ? n - 11 : 0); i >= 0; i--)
82 if (0 == memcmp(&file[i], "/nanocoat/", 10) ||
83 0 == memcmp(&file[i], "\\nanocoat\\", 10))
84 return &file[i + 10];
87 /* Use the full name regardless. */
88 return file;
91 sjme_errorCode sjme_error_fatalR(SJME_DEBUG_DECL_FILE_LINE_FUNC,
92 sjme_attrInValue sjme_errorCode error)
94 #if defined(SJME_CONFIG_DEBUG)
95 sjme_dieR(file, line, func, "FATAL ERROR: %d!", error);
96 #endif
98 return sjme_error_default(error);
101 sjme_errorCode sjme_error_notImplementedR(SJME_DEBUG_DECL_FILE_LINE_FUNC,
102 sjme_attrInValue sjme_intPointer context)
104 #if defined(SJME_CONFIG_DEBUG)
105 sjme_todoR(file, line, func, "NOT IMPLEMENTED: %d %p!",
106 (int)context, (void*)context);
107 #endif
109 return SJME_ERROR_NOT_IMPLEMENTED;
112 sjme_errorCode sjme_error_outOfMemoryR(SJME_DEBUG_DECL_FILE_LINE_FUNC,
113 sjme_attrInNullable sjme_alloc_pool* inPool,
114 sjme_attrInValue sjme_intPointer context)
116 #if defined(SJME_CONFIG_DEBUG)
117 /* Dump entire pool contents. */
118 if (inPool != NULL)
119 sjme_alloc_poolDump(inPool);
121 /* It could be huge... */
122 sjme_todoR(file, line, func, "OUT OF MEMORY %p: %d %p!",
123 inPool, (int)context, (void*)context);
124 #endif
126 return SJME_ERROR_OUT_OF_MEMORY;
129 void sjme_genericMessage(sjme_lpcstr file, int line,
130 sjme_lpcstr func, sjme_lpcstr prefix, sjme_lpcstr format, va_list args)
132 va_list copy;
133 char buf[DEBUG_BUF];
134 char fullBuf[DEBUG_BUF];
135 int hasPrefix;
136 sjme_jboolean handled;
138 /* Need to copy because this works differently on other arches. */
139 va_copy(copy, args);
141 /* Load message buffer. */
142 if (format == NULL)
143 strncpy(buf, "No message", DEBUG_BUF);
144 else
146 memset(buf, 0, sizeof(buf));
147 vsnprintf(buf, DEBUG_BUF - 1, format, copy);
150 /* Cleanup the copy. */
151 va_end(copy);
153 /* Print output message. */
154 hasPrefix = (prefix != NULL && strlen(prefix) > 0);
155 memset(fullBuf, 0, sizeof(fullBuf));
156 if (file != NULL || line > 0 || func != NULL)
157 snprintf(fullBuf, DEBUG_BUF - 1,
158 "%s%s(%s:%d in %s()): %s",
159 prefix, (hasPrefix ? " " : ""),
160 sjme_debug_shortenFile(file), line, func, buf);
161 else
162 snprintf(fullBuf, DEBUG_BUF - 1,
163 "%s%s%s",
164 prefix, (hasPrefix ? " " : ""), buf);
166 /* First try to print to the frontend callback, if any. */
167 handled = SJME_JNI_FALSE;
168 if (sjme_debug_handlers != NULL && sjme_debug_handlers->message != NULL)
169 handled = sjme_debug_handlers->message(
170 fullBuf, buf);
172 if (!handled)
173 fprintf(stderr, "%s\n", fullBuf);
175 /* Make sure it gets written. */
176 fflush(stderr);
179 void sjme_messageR(sjme_lpcstr file, int line,
180 sjme_lpcstr func, sjme_jboolean isBlank, sjme_lpcstr message, ...)
182 va_list list;
184 va_start(list, message);
186 sjme_genericMessage(file, line, func,
187 (isBlank ? "" : "DB"), message,
188 list);
190 va_end(list);
193 void sjme_messageV(SJME_DEBUG_DECL_FILE_LINE_FUNC,
194 sjme_jboolean isBlank,
195 sjme_attrInNullable sjme_attrFormatArg sjme_lpcstr message,
196 va_list args)
198 sjme_genericMessage(file, line, func,
199 (isBlank ? "" : "DB"), message,
200 args);
203 sjme_errorCode sjme_dieR(sjme_lpcstr file, int line,
204 sjme_lpcstr func, sjme_lpcstr message, ...)
206 va_list list;
207 va_list copy;
209 va_start(list, message);
211 sjme_genericMessage(file, line, func, "FATAL", message,
212 list);
214 va_end(list);
216 /* Exit and stop. */
217 sjme_debug_abort();
219 /* Exit after abort happens, it can be ignored in debugging. */
220 sjme_debug_exit(EXIT_FAILURE);
222 /* Never reaches, but returns false naturally. */
223 return SJME_ERROR_UNKNOWN;
226 static void sjme_message_hexDumpChar(sjme_lpstr* w, sjme_lpstr end,
227 sjme_jint c)
229 if ((*w) < end)
230 *((*w)++) = c;
233 static void sjme_message_hexDumpHex(sjme_lpstr* w, sjme_lpstr end,
234 sjme_jint c)
236 if (c < 10)
237 sjme_message_hexDumpChar(w, end, '0' + c);
238 else
239 sjme_message_hexDumpChar(w, end, 'A' + (c - 10));
242 void sjme_message_hexDump(
243 sjme_attrInNullable sjme_buffer inData,
244 sjme_attrInPositive sjme_jint inLen)
246 #define SJME_HEX_LINE 12
247 sjme_jint at, sub, i, c;
248 sjme_cchar buf[DEBUG_BUF];
249 sjme_lpstr w, end;
251 if (inData == NULL || inLen <= 0)
252 return;
254 /* Print all sequences. */
255 for (at = 0; at < inLen; at += SJME_HEX_LINE)
257 /* Clear buffer. */
258 memset(buf, 0, sizeof(buf));
259 w = buf;
260 end = &buf[DEBUG_BUF - 1];
262 /* Print hex first. */
263 for (sub = at, i = 0; sub < inLen && i < SJME_HEX_LINE; sub++, i++)
265 /* Get byte from the input. */
266 c = ((sjme_jubyte*)inData)[sub];
268 /* Write hex. */
269 sjme_message_hexDumpHex(&w, end, (c >> 4) & 0xF);
270 sjme_message_hexDumpHex(&w, end, c & 0xF);
272 /* Space. */
273 sjme_message_hexDumpChar(&w, end, ' ');
276 /* Split to hex. */
277 sjme_message_hexDumpChar(&w, end, '|');
279 /* Then characters. */
280 for (sub = at, i = 0; sub < inLen && i < SJME_HEX_LINE; sub++, i++)
282 /* Get byte from the input. */
283 c = ((sjme_jubyte*)inData)[sub];
285 if (c >= ' ' && c < 0x7F)
286 sjme_message_hexDumpChar(&w, end, c);
287 else
288 sjme_message_hexDumpChar(&w, end, '.');
291 /* End splice. */
292 sjme_message_hexDumpChar(&w, end, '|');
294 /* Send out the buffer. */
295 sjme_messageR(NULL, -1, NULL, SJME_JNI_TRUE,
296 "%s", buf);
299 #undef SJME_HEX_LINE
302 void sjme_todoR(sjme_lpcstr file, int line,
303 sjme_lpcstr func, sjme_lpcstr message, ...)
305 va_list list;
307 va_start(list, message);
309 sjme_genericMessage(file, line, func, "TD TODO HIT", message,
310 list);
312 va_end(list);
314 /* Exit and stop. */
315 sjme_debug_abort();
317 /* Exit after abort happens, it can be ignored in debugging. */
318 sjme_debug_exit(EXIT_FAILURE);