1 /***********************************************************************************************************************************
2 Error Handler
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5 
6 #include <assert.h>
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include "common/error.h"
13 #include "common/stackTrace.h"
14 
15 /***********************************************************************************************************************************
16 Represents an error type
17 ***********************************************************************************************************************************/
18 struct ErrorType
19 {
20     const int code;
21     const char *name;
22     const struct ErrorType *parentType;
23 };
24 
25 // Macro for defining new error types
26 #define ERROR_DEFINE(code, name, parentType)                                                                                       \
27     const ErrorType name = {code, #name, &parentType}
28 
29 // Define test error
30 #ifdef DEBUG
31     ERROR_DEFINE(1, TestError, RuntimeError);
32 #endif
33 
34 // Include error type definitions
35 #include "common/error.auto.c"
36 
37 /***********************************************************************************************************************************
38 Maximum allowed number of nested try blocks
39 ***********************************************************************************************************************************/
40 #define ERROR_TRY_MAX                                             64
41 
42 /***********************************************************************************************************************************
43 States for each try
44 ***********************************************************************************************************************************/
45 typedef enum {errorStateBegin, errorStateTry, errorStateCatch, errorStateFinal, errorStateEnd} ErrorState;
46 
47 /***********************************************************************************************************************************
48 Track error handling
49 ***********************************************************************************************************************************/
50 typedef struct Error
51 {
52     const ErrorType *errorType;                                     // Error type
53     const char *fileName;                                           // Source file where the error occurred
54     const char *functionName;                                       // Function where the error occurred
55     int fileLine;                                                   // Source file line where the error occurred
56     const char *message;                                            // Description of the error
57     const char *stackTrace;                                         // Stack trace
58 } Error;
59 
60 static struct
61 {
62     // Array of jump buffers
63     jmp_buf jumpList[ERROR_TRY_MAX];
64 
65     // Handler list
66     const ErrorHandlerFunction *handlerList;
67     unsigned int handlerTotal;
68 
69     // State of each try
70     int tryTotal;
71 
72     struct
73     {
74         ErrorState state;
75         bool uncaught;
76     } tryList[ERROR_TRY_MAX + 1];
77 
78     // Last error
79     Error error;
80 } errorContext;
81 
82 /***********************************************************************************************************************************
83 Message buffer and buffer size
84 
85 The message buffer is statically allocated so there is some space to store error messages.  Not being able to allocate such a small
86 amount of memory seems pretty unlikely so just keep the code simple and let the loader deal with massively constrained memory
87 situations.
88 
89 The temp buffer is required because the error message being passed might be the error already stored in the message buffer.
90 ***********************************************************************************************************************************/
91 #define ERROR_MESSAGE_BUFFER_SIZE                                   8192
92 
93 static char messageBuffer[ERROR_MESSAGE_BUFFER_SIZE];
94 static char messageBufferTemp[ERROR_MESSAGE_BUFFER_SIZE];
95 static char stackTraceBuffer[ERROR_MESSAGE_BUFFER_SIZE];
96 
97 /**********************************************************************************************************************************/
errorHandlerSet(ErrorHandlerFunction * list,unsigned int total)98 void errorHandlerSet(ErrorHandlerFunction *list, unsigned int total)
99 {
100     assert(total == 0 || list != NULL);
101 
102     errorContext.handlerList = list;
103     errorContext.handlerTotal = total;
104 }
105 
106 /**********************************************************************************************************************************/
107 int
errorTypeCode(const ErrorType * errorType)108 errorTypeCode(const ErrorType *errorType)
109 {
110     return errorType->code;
111 }
112 
113 /**********************************************************************************************************************************/
114 const ErrorType *
errorTypeFromCode(int code)115 errorTypeFromCode(int code)
116 {
117     // Search for error type by code
118     int errorTypeIdx = 0;
119     const ErrorType *result = errorTypeList[errorTypeIdx];
120 
121     while (result != NULL)
122     {
123         if (result->code == code)
124             break;
125 
126         result = errorTypeList[++errorTypeIdx];
127     }
128 
129     // Error if type was not found
130     if (result == NULL)
131         result = &UnknownError;
132 
133     return result;
134 }
135 
136 /**********************************************************************************************************************************/
137 const char *
errorTypeName(const ErrorType * errorType)138 errorTypeName(const ErrorType *errorType)
139 {
140     return errorType->name;
141 }
142 
143 /**********************************************************************************************************************************/
144 const ErrorType *
errorTypeParent(const ErrorType * errorType)145 errorTypeParent(const ErrorType *errorType)
146 {
147     return errorType->parentType;
148 }
149 
150 /**********************************************************************************************************************************/
errorTryDepth(void)151 unsigned int errorTryDepth(void)
152 {
153     return (unsigned int)errorContext.tryTotal;
154 }
155 
156 /**********************************************************************************************************************************/
157 bool
errorTypeExtends(const ErrorType * child,const ErrorType * parent)158 errorTypeExtends(const ErrorType *child, const ErrorType *parent)
159 {
160     const ErrorType *find = child;
161 
162     do
163     {
164         find = errorTypeParent(find);
165 
166         // Parent was found
167         if (find == parent)
168             return true;
169     }
170     while (find != errorTypeParent(find));
171 
172     // Parent was not found
173     return false;
174 }
175 
176 /**********************************************************************************************************************************/
177 const ErrorType *
errorType(void)178 errorType(void)
179 {
180     assert(errorContext.error.errorType != NULL);
181 
182     return errorContext.error.errorType;
183 }
184 
185 /**********************************************************************************************************************************/
186 int
errorCode(void)187 errorCode(void)
188 {
189     return errorTypeCode(errorType());
190 }
191 
192 /**********************************************************************************************************************************/
193 const char *
errorFileName(void)194 errorFileName(void)
195 {
196     assert(errorContext.error.fileName != NULL);
197 
198     return errorContext.error.fileName;
199 }
200 
201 /**********************************************************************************************************************************/
202 const char *
errorFunctionName(void)203 errorFunctionName(void)
204 {
205     assert(errorContext.error.functionName != NULL);
206 
207     return errorContext.error.functionName;
208 }
209 
210 /**********************************************************************************************************************************/
211 int
errorFileLine(void)212 errorFileLine(void)
213 {
214     assert(errorContext.error.fileLine != 0);
215 
216     return errorContext.error.fileLine;
217 }
218 
219 /**********************************************************************************************************************************/
220 const char *
errorMessage(void)221 errorMessage(void)
222 {
223     assert(errorContext.error.message != NULL);
224 
225     return errorContext.error.message;
226 }
227 
228 /**********************************************************************************************************************************/
229 const char *
errorName(void)230 errorName(void)
231 {
232     return errorTypeName(errorType());
233 }
234 
235 /**********************************************************************************************************************************/
236 const char *
errorStackTrace(void)237 errorStackTrace(void)
238 {
239     assert(errorContext.error.stackTrace != NULL);
240 
241     return errorContext.error.stackTrace;
242 }
243 
244 /**********************************************************************************************************************************/
245 bool
errorInstanceOf(const ErrorType * errorTypeTest)246 errorInstanceOf(const ErrorType *errorTypeTest)
247 {
248     return errorType() == errorTypeTest || errorTypeExtends(errorType(), errorTypeTest);
249 }
250 
251 /***********************************************************************************************************************************
252 Return current error context state
253 ***********************************************************************************************************************************/
254 static ErrorState
errorInternalState(void)255 errorInternalState(void)
256 {
257     return errorContext.tryList[errorContext.tryTotal].state;
258 }
259 
260 /**********************************************************************************************************************************/
261 bool
errorInternalStateTry(void)262 errorInternalStateTry(void)
263 {
264     return errorInternalState() == errorStateTry;
265 }
266 
267 /**********************************************************************************************************************************/
268 bool
errorInternalStateCatch(const ErrorType * errorTypeCatch)269 errorInternalStateCatch(const ErrorType *errorTypeCatch)
270 {
271     if (errorInternalState() == errorStateCatch && errorInstanceOf(errorTypeCatch))
272         return errorInternalProcess(true);
273 
274     return false;
275 }
276 
277 /**********************************************************************************************************************************/
278 bool
errorInternalStateFinal(void)279 errorInternalStateFinal(void)
280 {
281     return errorInternalState() == errorStateFinal;
282 }
283 
284 /**********************************************************************************************************************************/
285 jmp_buf *
errorInternalJump(void)286 errorInternalJump(void)
287 {
288     return &errorContext.jumpList[errorContext.tryTotal - 1];
289 }
290 
291 /**********************************************************************************************************************************/
292 bool
errorInternalTry(const char * fileName,const char * functionName,int fileLine)293 errorInternalTry(const char *fileName, const char *functionName, int fileLine)
294 {
295     // If try total has been exceeded then throw an error
296     if (errorContext.tryTotal >= ERROR_TRY_MAX)
297         errorInternalThrowFmt(&AssertError, fileName, functionName, fileLine, "too many nested try blocks");
298 
299     // Increment try total
300     errorContext.tryTotal++;
301 
302     // Setup try
303     errorContext.tryList[errorContext.tryTotal].state = errorStateBegin;
304     errorContext.tryList[errorContext.tryTotal].uncaught = false;
305 
306     // Try setup was successful
307     return true;
308 }
309 
310 /**********************************************************************************************************************************/
311 void
errorInternalPropagate(void)312 errorInternalPropagate(void)
313 {
314     assert(errorContext.error.errorType != NULL);
315 
316     // Mark the error as uncaught
317     errorContext.tryList[errorContext.tryTotal].uncaught = true;
318 
319     // If there is a parent try then jump to it
320     if (errorContext.tryTotal > 0)
321         longjmp(errorContext.jumpList[errorContext.tryTotal - 1], 1);
322 
323     // If there was no try to catch this error then output to stderr
324     fprintf(stderr, "\nUncaught %s: %s\n    thrown at %s:%d\n\n", errorName(), errorMessage(), errorFileName(), errorFileLine());
325     fflush(stderr);
326 
327     // Exit with failure
328     exit(UnhandledError.code);
329 }
330 
331 /**********************************************************************************************************************************/
332 bool
errorInternalProcess(bool catch)333 errorInternalProcess(bool catch)
334 {
335     // If a catch statement then return
336     if (catch)
337     {
338         errorContext.tryList[errorContext.tryTotal].uncaught = false;
339         return true;
340     }
341     // Else if just entering error state clean up the stack
342     else if (errorContext.tryList[errorContext.tryTotal].state == errorStateTry)
343     {
344         for (unsigned int handlerIdx = 0; handlerIdx < errorContext.handlerTotal; handlerIdx++)
345             errorContext.handlerList[handlerIdx](errorTryDepth());
346     }
347 
348     // Any catch blocks have been processed and none of them called RETHROW() so clear the error
349     if (errorContext.tryList[errorContext.tryTotal].state == errorStateCatch &&
350         !errorContext.tryList[errorContext.tryTotal].uncaught)
351     {
352         errorContext.error = (Error){0};
353     }
354 
355     // Increment the state
356     errorContext.tryList[errorContext.tryTotal].state++;
357 
358     // If the error has been caught then increment the state
359     if (errorContext.tryList[errorContext.tryTotal].state == errorStateCatch &&
360         !errorContext.tryList[errorContext.tryTotal].uncaught)
361     {
362         errorContext.tryList[errorContext.tryTotal].state++;
363     }
364 
365     // Return if not done
366     if (errorContext.tryList[errorContext.tryTotal].state < errorStateEnd)
367         return true;
368 
369     // Remove the try
370     errorContext.tryTotal--;
371 
372     // If not caught in the last try then propagate
373     if (errorContext.tryList[errorContext.tryTotal + 1].uncaught)
374         errorInternalPropagate();
375 
376     // Nothing left to process
377     return false;
378 }
379 
380 /**********************************************************************************************************************************/
381 void
errorInternalThrow(const ErrorType * errorType,const char * fileName,const char * functionName,int fileLine,const char * message)382 errorInternalThrow(const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *message)
383 {
384     // Setup error data
385     errorContext.error.errorType = errorType;
386     errorContext.error.fileName = fileName;
387     errorContext.error.functionName = functionName;
388     errorContext.error.fileLine = fileLine;
389 
390     // Assign message to the error
391     strncpy(messageBuffer, message, sizeof(messageBuffer));
392     messageBuffer[sizeof(messageBuffer) - 1] = 0;
393 
394     errorContext.error.message = (const char *)messageBuffer;
395 
396     // Generate the stack trace for the error
397     if (stackTraceToZ(
398             stackTraceBuffer, sizeof(stackTraceBuffer), fileName, functionName, (unsigned int)fileLine) >= sizeof(stackTraceBuffer))
399     {
400         // Indicate that the stack trace was truncated
401     }
402 
403     errorContext.error.stackTrace = (const char *)stackTraceBuffer;
404 
405     // Propagate the error
406     errorInternalPropagate();
407 }
408 
409 void
errorInternalThrowFmt(const ErrorType * errorType,const char * fileName,const char * functionName,int fileLine,const char * format,...)410 errorInternalThrowFmt(
411     const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *format, ...)
412 {
413     // Format message
414     va_list argument;
415     va_start(argument, format);
416     vsnprintf(messageBufferTemp, ERROR_MESSAGE_BUFFER_SIZE - 1, format, argument);
417     va_end(argument);
418 
419     errorInternalThrow(errorType, fileName, functionName, fileLine, messageBufferTemp);
420 }
421 
422 /**********************************************************************************************************************************/
423 void
errorInternalThrowSys(int errNo,const ErrorType * errorType,const char * fileName,const char * functionName,int fileLine,const char * message)424 errorInternalThrowSys(
425     int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *message)
426 {
427     // Format message with system message appended
428     if (errNo == 0)
429     {
430         strncpy(messageBufferTemp, message, ERROR_MESSAGE_BUFFER_SIZE - 1);
431         messageBufferTemp[sizeof(messageBuffer) - 1] = 0;
432     }
433     else
434         snprintf(messageBufferTemp, ERROR_MESSAGE_BUFFER_SIZE - 1, "%s: [%d] %s", message, errNo, strerror(errNo));
435 
436     errorInternalThrow(errorType, fileName, functionName, fileLine, messageBufferTemp);
437 }
438 
439 #ifdef DEBUG_COVERAGE
440 void
errorInternalThrowOnSys(bool error,int errNo,const ErrorType * errorType,const char * fileName,const char * functionName,int fileLine,const char * message)441 errorInternalThrowOnSys(
442     bool error, int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine,
443     const char *message)
444 {
445     if (error)
446         errorInternalThrowSys(errNo, errorType, fileName, functionName, fileLine, message);
447 }
448 #endif
449 
450 void
errorInternalThrowSysFmt(int errNo,const ErrorType * errorType,const char * fileName,const char * functionName,int fileLine,const char * format,...)451 errorInternalThrowSysFmt(
452     int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *format, ...)
453 {
454     // Format message
455     va_list argument;
456     va_start(argument, format);
457     size_t messageSize = (size_t)vsnprintf(messageBufferTemp, ERROR_MESSAGE_BUFFER_SIZE - 1, format, argument);
458     va_end(argument);
459 
460     // Append the system message
461     if (errNo != 0)
462         snprintf(messageBufferTemp + messageSize, ERROR_MESSAGE_BUFFER_SIZE - 1 - messageSize, ": [%d] %s", errNo, strerror(errNo));
463 
464     errorInternalThrow(errorType, fileName, functionName, fileLine, messageBufferTemp);
465 }
466 
467 #ifdef DEBUG_COVERAGE
468 void
errorInternalThrowOnSysFmt(bool error,int errNo,const ErrorType * errorType,const char * fileName,const char * functionName,int fileLine,const char * format,...)469 errorInternalThrowOnSysFmt(
470     bool error, int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine,
471     const char *format, ...)
472 {
473     if (error)
474     {
475         // Format message
476         va_list argument;
477         va_start(argument, format);
478         size_t messageSize = (size_t)vsnprintf(messageBufferTemp, ERROR_MESSAGE_BUFFER_SIZE - 1, format, argument);
479         va_end(argument);
480 
481         // Append the system message
482         if (errNo != 0)
483         {
484             snprintf(
485                 messageBufferTemp + messageSize, ERROR_MESSAGE_BUFFER_SIZE - 1 - messageSize, ": [%d] %s", errNo, strerror(errNo));
486         }
487 
488         errorInternalThrow(errorType, fileName, functionName, fileLine, messageBufferTemp);
489     }
490 }
491 #endif
492