BLT/src/blt/std/assert.cpp

189 lines
4.7 KiB
C++

/*
* Created by Brett on 23/08/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#include <blt/std/assert.h>
#include <blt/std/utility.h>
#include <blt/std/logging.h>
#include <blt/std/string.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <exception>
#include <cstring>
struct abort_exception final : public std::exception
{
public:
explicit abort_exception(const char* what)
{
auto len = std::strlen(what) + 1;
error = static_cast<char*>(std::malloc(len));
std::memcpy(static_cast<char*>(error), what, len);
}
abort_exception(const abort_exception& copy) = delete;
abort_exception& operator=(const abort_exception& copy) = delete;
[[nodiscard]] const char* what() const noexcept override
{
if (error == nullptr)
return "Abort called";
return error;
}
~abort_exception() override
{
std::free(static_cast<void*>(error));
}
private:
char* error{nullptr};
};
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) && !defined(WIN32)
#define IS_GNU_BACKTRACE
#endif
#ifdef IS_GNU_BACKTRACE
#include <execinfo.h>
#include <cstdlib>
#endif
#if IS_GNU_BACKTRACE
#define BLT_STACK_TRACE(number) void* ptrs[number]; \
int size = backtrace(ptrs, number); \
char** messages = backtrace_symbols(ptrs, size);
#define BLT_FREE_STACK_TRACE() free(messages);
#else
#define BLT_STACK_TRACE(number) void();
#define BLT_FREE_STACK_TRACE() void();
#endif
namespace blt
{
#if IS_GNU_BACKTRACE
static inline std::string _macro_filename(const std::string& path)
{
auto paths = blt::string::split(path, "/");
auto final = paths[paths.size() - 1];
if (final == "/")
return paths[paths.size() - 2];
return final;
}
#endif
void b_throw(const char* what, const char* path, int line)
{
#if IS_GNU_BACKTRACE
BLT_STACK_TRACE(50);
BLT_ERROR("An exception '%s' has occurred in file '%s:%d'", what, path, line);
BLT_ERROR("Stack Trace:");
printStacktrace(messages, size, path, line);
BLT_FREE_STACK_TRACE();
#else
(void) what;
(void) path;
(void) line;
#endif
}
void b_assert_failed(const char* expression, const char* msg, const char* path, int line)
{
#if IS_GNU_BACKTRACE
BLT_STACK_TRACE(50);
BLT_ERROR("The assertion '%s' has failed in file '%s:%d'", expression, path, line);
if (msg != nullptr)
BLT_ERROR(msg);
BLT_ERROR("Stack Trace:");
printStacktrace(messages, size, path, line);
BLT_FREE_STACK_TRACE();
#else
(void) expression;
(void) msg;
(void) path;
(void) line;
#endif
if (msg != nullptr)
throw abort_exception(msg);
throw abort_exception(expression);
}
void printStacktrace(char** messages, int size, const char* path, int line)
{
if (messages == nullptr)
return;
#if IS_GNU_BACKTRACE
for (int i = 1; i < size; i++)
{
int tabs = i - 1;
std::string buffer;
for (int j = 0; j < tabs; j++)
buffer += '\t';
if (i != 1)
buffer += "";
std::string message(messages[i]);
auto f = message.find('(');
auto mes = message.substr(f + 1, message.size());
std::string loc;
buffer += message.substr(0, f);
if (i == 1)
{
loc = '\'';
loc += _macro_filename(path);
loc += ':';
loc += std::to_string(line);
loc += '\'';
} else
loc = demangle(mes.substr(0, mes.find('+')));
if (!loc.empty())
buffer += " in ";
buffer += loc;
BLT_ERROR(buffer);
}
#else
(void) size;
(void) path;
(void) line;
#endif
}
void b_abort(const char* what, const char* path, int line)
{
#if IS_GNU_BACKTRACE
BLT_STACK_TRACE(50);
#endif
BLT_FATAL("----{BLT ABORT}----");
BLT_FATAL("\tWhat: %s", what);
BLT_FATAL("\tCalled from %s:%d", path, line);
#if IS_GNU_BACKTRACE
printStacktrace(messages, size, path, line);
BLT_FREE_STACK_TRACE();
#endif
throw abort_exception(what);
}
}