Arbutils/src/Exception.hpp

157 lines
6.2 KiB
C++

#ifndef ARBUTILS_EXCEPTION_HPP
#define ARBUTILS_EXCEPTION_HPP
#include <exception>
#include <sstream>
#include <string>
#include <utility>
#if !WINDOWS
#if PRETTYTRACES
#define BACKWARD_HAS_DW 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);
#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 override {
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;
bool foundExceptionClass = false;
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);
if (foundExceptionClass) {
framesAppended++;
}
} else {
AppendSourceStack(ss, trace.source, foundExceptionClass, snippetFactory);
if (foundExceptionClass) {
framesAppended++;
}
}
for (auto& t : trace.inliners) {
AppendSourceStack(ss, t, foundExceptionClass, snippetFactory);
if (foundExceptionClass) {
framesAppended++;
if (framesAppended >= depth)
break;
}
}
}
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() > 70) {
function = function.substr(0, 67);
function += "...";
}
ss << objectName;
if (include_addr) {
ss << "[" << trace.addr << "]";
}
ss << " " << function << std::endl;
}
static void AppendSourceStack(std::stringstream& ss, const backward::ResolvedTrace::SourceLoc& source,
bool& foundExceptionClass, backward::SnippetFactory& snippetFactory) {
auto fileName = (strrchr(source.filename.c_str(), '/') ? strrchr(source.filename.c_str(), '/') + 1
: source.filename.c_str());
if (strcmp(fileName, "Exception.hpp") == 0) {
foundExceptionClass = true;
return;
} else if (!foundExceptionClass) {
return;
}
auto snippet = snippetFactory.get_snippet(source.filename, source.line, 1)[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);
if (snippet.length() > 70) {
snippet = snippet.substr(0, 67);
snippet += "...";
}
ss << fileName << "[" << source.line << "] " << snippet << std::endl;
}
#endif
};
}
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define THROW(message) \
std::stringstream ___ss; \
___ss << "[" << __FILENAME__ << ":" << __LINE__ << "] " << message; \
throw ArbUt::Exception(___ss.str());
#define NOT_REACHABLE THROW("Not reachable");
#define NOT_IMPLEMENTED THROW("Not implemented");
#endif // ARBUTILS_EXCEPTION_HPP