Arbutils/src/Exception.hpp

163 lines
6.0 KiB
C++

#ifndef ARBUTILS_EXCEPTION_HPP
#define ARBUTILS_EXCEPTION_HPP
#if !WINDOWS
#if PRETTYTRACES
#define BACKWARD_HAS_BFD 1
#endif
#include "../extern/backward.hpp"
#endif
namespace ArbUt {
/// @brief Implementation of std::logic_error that gives a stack trace when thrown.
class Exception : std::logic_error {
#if !WINDOWS
backward::StackTrace _stack;
#endif
public:
/// @brief Throw an exception with specified message.
explicit Exception(const std::string& msg) : std::logic_error(msg) {
#if !WINDOWS
_stack.load_here(32);
#if DEBUG
_stack.skip_n_firsts(2);
#else
_stack.skip_n_firsts(0);
#endif
#endif
}
/// @brief Copy operator.
Exception(const Exception& e) noexcept
: std::logic_error(e.what())
#if !WINDOWS
,
_stack(e._stack)
#endif
{
}
/// @brief Returns the error message of the exception.
[[nodiscard]] const char* what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW final {
return logic_error::what();
}
/// @brief Returns the stacktrace of the exception.
/// @param depth The max number of stacks it should retrieve.
/// @param include_addr Whether or not the address should be shown if the line information could not be
/// retrieved.
[[nodiscard]] std::string GetStacktrace([[maybe_unused]] size_t depth = 6,
[[maybe_unused]] bool include_addr = true) const {
#if !WINDOWS
return BuildStacktraceFromStack(_stack, depth, include_addr);
#else
return "Stack trace not available on Windows.";
#endif
}
#if !WINDOWS
/// @brief Builds a string from a given stack.
/// @param stack The stack to build the string from.
/// @param depth The max number of stacks it should retrieve.
/// @param include_addr Whether or not the address should be shown if the line information could not be
/// retrieved.
static std::string BuildStacktraceFromStack(const backward::StackTrace& stack, size_t depth = 6,
bool include_addr = true) {
if (stack.size() == 0) {
return "No stack trace could be retrieved.";
}
backward::TraceResolver tr;
backward::SnippetFactory snippetFactory;
tr.load_stacktrace(stack);
std::stringstream ss;
ss << "Stacktrace with depth " << depth << ": " << std::endl;
size_t framesAppended = 0;
for (size_t i = 0; i < stack.size() && framesAppended < depth; ++i) {
backward::ResolvedTrace trace = tr.resolve(stack[i]);
if (trace.source.filename.empty()) {
AppendNoSourceStack(ss, trace, include_addr);
framesAppended++;
} else if (i != 0) {
AppendSourceStack(ss, trace.source, snippetFactory);
framesAppended++;
}
for (auto& t : trace.inliners) {
AppendSourceStack(ss, t, snippetFactory);
framesAppended++;
if (framesAppended >= depth)
break;
}
}
ss << "--- End of Stack ---";
return ss.str();
}
private:
static void AppendNoSourceStack(std::stringstream& ss, const backward::ResolvedTrace& trace,
bool include_addr) {
auto objectName =
(strrchr(trace.object_filename.c_str(), '/') ? strrchr(trace.object_filename.c_str(), '/') + 1
: trace.object_filename.c_str());
auto function = trace.object_function;
if (function.length() == 0) {
function = "[No function name]";
}
if (strcmp(objectName, "") == 0) {
objectName = "[No object name]";
}
ss << objectName;
if (include_addr) {
ss << "[" << trace.addr << "]";
}
ss << " " << function << std::endl;
}
static void AppendSourceStack(std::stringstream& ss, const backward::ResolvedTrace::SourceLoc& source,
backward::SnippetFactory& snippetFactory) {
auto fileName = (strrchr(source.filename.c_str(), '/') ? strrchr(source.filename.c_str(), '/') + 1
: source.filename.c_str());
auto snippetSearch = snippetFactory.get_snippet(source.filename, source.line, 1);
if (snippetSearch.empty()) {
ss << fileName << "[" << source.line << "]" << std::endl;
return;
}
auto snippet = snippetSearch[0].second;
size_t p = snippet.find_first_not_of(" \t");
snippet.erase(0, p);
p = snippet.find_last_not_of(" \t");
if (std::string::npos != p)
snippet.erase(p + 1);
ss << fileName << "[" << source.line << "] " << snippet << std::endl;
}
#endif
};
}
#if !__cpp_consteval
#define consteval constexpr
#endif
static consteval const char* file_name(const char* path) {
const char* file = path;
while (*path != 0) {
if (*path++ == '/') {
file = path;
}
}
return file;
}
#define THROW(message) \
std::stringstream ___ss; \
___ss << "[" << file_name(__FILE__) << ":" << __LINE__ << "] " << message; \
throw ArbUt::Exception(___ss.str());
#define NOT_REACHABLE THROW("Not reachable");
#define NOT_IMPLEMENTED THROW("Not implemented");
#endif // ARBUTILS_EXCEPTION_HPP