Library: Added logging and memory modules. The logging module provides a flexible interface for adding and removing logging streams (think stdout, stderr, a file) and supports ANSI colouring and including the date and time of each log call. Its header also provides several definitions for assertions, each corresponding to a specific log level and some other side effects such as debug_break(), returning, exiting or doing nothing. The memory module wraps (_)aligned_alloc and free functions and includes memory tagging by class and location and the ability to fetch a formatted string containing this information. This can allow you to narrow down where memory leaks and double frees occur to particular sections within a module. Memory can be aligned to any, 32, 64 or 4096 byte boundaries using this module.
186 lines
5.4 KiB
C++
186 lines
5.4 KiB
C++
#include <stdarg.h>
|
|
#include <immintrin.h>
|
|
#include <time.h>
|
|
#include "core/basalt_logger.h"
|
|
#include "memory.h"
|
|
|
|
static struct {
|
|
logger_stream_t streams[BASALT_LOGGER_MAX_STREAMS];
|
|
u8 global_mask;
|
|
u8 num_streams;
|
|
u8 initialized;
|
|
u8 _padding[5];// Unused - Signifies how many bytes aren't being used
|
|
} logger_state;
|
|
|
|
void initialize_logger()
|
|
{
|
|
if (logger_state.initialized != 0x00)
|
|
return;
|
|
memset(&logger_state, 0, sizeof(logger_state));
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
logger_state.global_mask = LOG_MASK_ALL;
|
|
#else
|
|
logger_state.global_mask = LOG_MASK_DEFAULT;
|
|
#endif
|
|
logger_state.num_streams = 2;
|
|
logger_add_stream(stdout, LOG_MASK_INFO | LOG_MASK_DEBUGGING, LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT | LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT);
|
|
logger_add_stream(stderr, LOG_MASK_ERRORS, LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT | LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT);
|
|
logger_state.initialized = 0xff;
|
|
}
|
|
|
|
void terminate_logger()
|
|
{
|
|
if (logger_state.initialized != 0xff)
|
|
return;
|
|
for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
|
logger_remove_stream(i);
|
|
memset(&logger_state, 0, sizeof(logger_state));
|
|
logger_state.initialized = 0x00;
|
|
}
|
|
|
|
inline FILE* logger_stream_get_file(logger_stream_t* state)
|
|
{
|
|
return (FILE*)((state->stream & 0xffffffffffff) | ((u64)stdout & 0xffff0000000000));
|
|
}
|
|
|
|
u8 logger_add_stream(FILE* output, LOG_LEVEL mask, LOG_STREAM_FLAGS flags)
|
|
{
|
|
if (logger_state.num_streams == BASALT_LOGGER_MAX_STREAMS)
|
|
return -1;
|
|
if ((((u64)output) & 0xffffffffffff) != 0)
|
|
__builtin_trap();
|
|
for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
|
{
|
|
if ((logger_state.streams[i].flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0)
|
|
{
|
|
logger_state.streams[i].stream = (u64)output;
|
|
logger_state.streams[i].flags = flags;
|
|
logger_state.streams[i].mask = mask;
|
|
logger_state.num_streams++;
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void logger_remove_stream(u8 index)
|
|
{
|
|
if (index >= sizeof(logger_state.streams) / sizeof(logger_state.streams[0]))
|
|
return;
|
|
logger_stream_t* stream = logger_state.streams+index;
|
|
FILE* f = logger_stream_get_file(stream);
|
|
if (f != stdout && f != stderr)
|
|
fclose(f);
|
|
stream->flags = 0;
|
|
stream->mask = 0;
|
|
stream->stream = (u64)nullptr;
|
|
logger_state.num_streams--;
|
|
}
|
|
|
|
u8 logger_find_stream(FILE* target)
|
|
{
|
|
for (size_t i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
|
{
|
|
if (logger_stream_get_file(logger_state.streams + i) == target &&
|
|
(logger_state.streams[i].flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) != 0)
|
|
return i;
|
|
}
|
|
return (u8)(-1);
|
|
}
|
|
|
|
void basalt_log(const LOG_LEVEL level, const char* msg, ...)
|
|
{
|
|
const char log_prefixes[8][6] = { "FATAL", "ERROR", "WARN ", "INFO ", "OK ", "WEIRD", "DEBUG", "TRACE" };
|
|
const char* log_ansi_prefixes[8] = { "\033[38;5;0m\033[48;5;9m", "\033[38;5;9m", "\033[38;5;11m", "\033[38;5;15m", "\033[38;5;10m", "\033[38;5;13m", "\033[38;5;12m", "\033[38;5;6m" };
|
|
if (level == 0)
|
|
return;
|
|
const u8 level = (u8)__builtin_ctz(level);
|
|
if (level > 8)
|
|
{
|
|
debug_break();
|
|
return;
|
|
}
|
|
|
|
if ((level & logger_state.global_mask) == 0)
|
|
return;
|
|
|
|
u8 valid_streams = 0;
|
|
for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
|
{
|
|
logger_stream_t* stream = logger_state.streams + i;
|
|
if ((stream->flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0)
|
|
continue;
|
|
valid_streams++;
|
|
if ((stream->mask & level) == 0)
|
|
continue;
|
|
FILE* out = logger_stream_get_file(stream);
|
|
if (stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT)
|
|
fprintf(out, log_ansi_prefixes[level]);
|
|
if ((stream->flags & LOG_STREAM_FLAG_DISABLE_PREFIXES) == 0)
|
|
{
|
|
if (stream->flags & LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT)
|
|
{
|
|
char time_buffer[32];
|
|
const time_t timer = time(NULL);
|
|
struct tm tm_info;
|
|
errno_t err = localtime_s(&tm_info, &timer);
|
|
if (!err)
|
|
{
|
|
int n_chars = (int)strftime(time_buffer, sizeof(time_buffer), "%d-%m-%y %R:%S", &tm_info);
|
|
fprintf(out, "[%*s %*s]: ", sizeof(log_prefixes[0]), log_prefixes[level], n_chars, time_buffer);
|
|
}
|
|
else
|
|
fprintf(out, "[%*s]: ", sizeof(log_prefixes[0]), log_prefixes[level]);
|
|
}
|
|
else
|
|
fprintf(out, "[%*s]: ", sizeof(log_prefixes[0]), log_prefixes[level]);
|
|
}
|
|
__builtin_va_list argstart;
|
|
va_start(argstart, msg);
|
|
vfprintf(out, msg, argstart);
|
|
va_end(argstart);
|
|
|
|
if ((stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT) != 0)
|
|
fprintf(out, "\033[m");
|
|
}
|
|
}
|
|
|
|
void basalt_write(const LOG_LEVEL level, const char* msg, ...)
|
|
{
|
|
const char log_prefixes[8][6] = { "FATAL", "ERROR", "WARN ", "INFO ", "OK ", "WEIRD", "DEBUG", "TRACE" };
|
|
const char* log_ansi_prefixes[8] = { "\033[38;5;0m\033[48;5;9m", "\033[38;5;9m", "\033[38;5;11m", "\033[38;5;15m", "\033[38;5;10m", "\033[38;5;13m", "\033[38;5;12m", "\033[38;5;6m" };
|
|
if (level == 0)
|
|
return;
|
|
const u8 level = (u8)__builtin_ctz(level);
|
|
if (level > 8)
|
|
{
|
|
debug_break();
|
|
return;
|
|
}
|
|
|
|
if ((level & logger_state.global_mask) == 0)
|
|
return;
|
|
|
|
u8 valid_streams = 0;
|
|
for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
|
{
|
|
logger_stream_t* stream = logger_state.streams + i;
|
|
if ((stream->flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0)
|
|
continue;
|
|
valid_streams++;
|
|
if ((stream->mask & level) == 0)
|
|
continue;
|
|
FILE* out = logger_stream_get_file(stream);
|
|
if (stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT)
|
|
fprintf(out, log_ansi_prefixes[level]);
|
|
|
|
__builtin_va_list argstart;
|
|
va_start(argstart, msg);
|
|
vfprintf(out, msg, argstart);
|
|
va_end(argstart);
|
|
|
|
if ((stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT) != 0)
|
|
fprintf(out, "\033[m");
|
|
}
|
|
}
|