diff --git a/src/main.c b/src/main.c index 6b48e00..8bb8127 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,41 @@ +#include +#include #include -int main() { +#define LOG_LEVEL LOG_LEVEL_DEBUG + +/** + * @brief Log a debug message to inform about beginning exit procedures + * + */ +void notify_exit(void) +{ + DEBUG("Exiting gemstone..."); +} + +/** + * @brief Run compiler setup here + * + */ +void setup(void) +{ + // setup preample + + log_init(); + DEBUG("starting gemstone..."); + + #if LOG_LEVEL <= LOG_LEVEL_DEBUG + atexit(¬ify_exit); + #endif + + // actual setup + + DEBUG("finished starting up gemstone..."); +} + +int main(void) { + setup(); + yyparse(); return 0; -} \ No newline at end of file +} diff --git a/src/sys/log.c b/src/sys/log.c new file mode 100644 index 0000000..e3a7cd6 --- /dev/null +++ b/src/sys/log.c @@ -0,0 +1,113 @@ + +#include +#include +#include +#include + +static struct Logger_t { + FILE** streams; + size_t stream_count; +} GlobalLogger; + +void log_init(void) +{ + log_register_stream(LOG_DEFAULT_STREAM); +} + +void log_register_stream(FILE* restrict stream) +{ + if (stream == NULL) + PANIC("stream to register is NULL"); + + if (GlobalLogger.stream_count == 0) + { + GlobalLogger.streams = (FILE**) malloc(sizeof(FILE*)); + GlobalLogger.stream_count = 1; + + if (GlobalLogger.streams == NULL) + { + PANIC("failed to allocate stream buffer"); + } + } + else + { + GlobalLogger.stream_count++; + size_t bytes = GlobalLogger.stream_count * sizeof(FILE*); + GlobalLogger.streams = (FILE**) realloc(GlobalLogger.streams, bytes); + + if (GlobalLogger.streams == NULL) + { + PANIC("failed to reallocate stream buffer"); + } + } + + GlobalLogger.streams[GlobalLogger.stream_count - 1] = stream; +} + +static void vflogf( + FILE* restrict stream, + const char* restrict level, + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + va_list args) +{ + fprintf(stream, "%s in %s() at %s:%lu: ", level, func, file, line); + vfprintf(stream, format, args); +} + +void __logf( + const char* restrict level, + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...) +{ + va_list args; + va_start(args, format); + + for (size_t i = 0; i < GlobalLogger.stream_count; i++) + { + FILE* stream = GlobalLogger.streams[i]; + + vflogf(stream, level, file, line, func, format, args); + } + + va_end(args); +} + +void __panicf( + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...) +{ + va_list args; + va_start(args, format); + + vflogf(stderr, LOG_STRING_PANIC, file, line, func, format, args); + + va_end(args); + + exit(EXIT_FAILURE); +} + +void __fatalf( + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...) +{ + va_list args; + va_start(args, format); + + vflogf(stderr, LOG_STRING_FATAL, file, line, func, format, args); + + va_end(args); + + abort(); +} diff --git a/src/sys/log.h b/src/sys/log.h new file mode 100644 index 0000000..84d0440 --- /dev/null +++ b/src/sys/log.h @@ -0,0 +1,116 @@ +#ifndef _SYS_ERR_H_ +#define _SYS_ERR_H_ + +#include +#include + +#define LOG_DEFAULT_STREAM stderr + +#define LOG_LEVEL_ERROR 3 +#define LOG_LEVEL_WARNING 2 +#define LOG_LEVEL_INFORMATION 1 +#define LOG_LEVEL_DEBUG 0 + +#define LOG_LEVEL LOG_LEVEL_DEBUG + +#define LOG_STRING_PANIC "Critical" +#define LOG_STRING_FATAL "Fatal" +#define LOG_STRING_ERROR "Error" +#define LOG_STRING_WARNING "Warning" +#define LOG_STRING_INFORMATION "Information" +#define LOG_STRING_DEBUG "Debug" + +/** + * @brief Panic is used in cases where the process is in an unrecoverable state. + * This macro will print debug information to stderr and call abort() to + * performa a ungracefull exit. No clean up possible. + */ +#define PANIC(format, ...) __panicf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__) + +/** + * @brief Panic is used in cases where the process is in an invalid or undefined state. + * This macro will print debug information to stderr and call exit() to + * initiate a gracefull exit, giving the process the opportunity to clean up. + */ +#define FATAL(format, ...) __fatalf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__) + +/* +Standard log macros. These will not terminate the application. +Can be turned off by setting LOG_LEVEL. All logs which have smaller log numbers +will not print. +*/ +#define ERROR(format, ...) __LOG(LOG_STRING_ERROR, LOG_LEVEL_ERROR, format"\n", ##__VA_ARGS__) +#define WARN(format, ...) __LOG(LOG_STRING_WARNING, LOG_LEVEL_WARNING, format"\n", ##__VA_ARGS__) +#define INFO(format, ...) __LOG(LOG_STRING_INFORMATION, LOG_LEVEL_INFORMATION, format"\n", ##__VA_ARGS__) +#define DEBUG(format, ...) __LOG(LOG_STRING_DEBUG, LOG_LEVEL_DEBUG, format"\n", ##__VA_ARGS__) + +#define __LOG(level, priority, format, ...) \ + do { \ + if (LOG_LEVEL <= priority) \ + __logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \ + } while(0) + +/** + * @brief Log a message into all registered streams + * + * @param level of the message + * @param file origin of the message cause + * @param line line in which log call was made + * @param func function the log call was done in + * @param format the format to print following args in + * @param ... + */ +void __logf( + const char* restrict level, + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...); + +/** + * @brief Log a panic message to stderr and perform gracefull crash with exit() denoting a failure + * + * @param file origin of the message cause + * @param line line in which log call was made + * @param func function the log call was done in + * @param format the format to print following args in + * @param ... + */ +void __panicf( + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...); + +/** + * @brief Log a critical message to stderr and perform ungracefull crash with abort() + * + * @param file origin of the message cause + * @param line line in which log call was made + * @param func function the log call was done in + * @param format the format to print following args in + * @param ... + */ +void __fatalf( + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...); + +/** + * @brief Initialize the logger by registering stderr as stream + * + */ +void log_init(void); + +/** + * @brief Register a stream as output source. Must be freed manually at exit if necessary + * + * @param stream + */ +void log_register_stream(FILE* restrict stream); + +#endif /* _SYS_ERR_H_ */