#pragma once #if !defined(MIJIN_DEBUG_STACKTRACE_HPP_INCLUDED) #define MIJIN_DEBUG_STACKTRACE_HPP_INCLUDED 1 #include #include #include #include "./symbol_info.hpp" #include "../types/result.hpp" #include "../util/iterators.hpp" namespace mijin { // // public defines // // // public constants // // // public types // struct Stackframe { void* address; std::string filename; std::string function; int lineNumber; }; class Stacktrace { private: std::vector frames_; public: Stacktrace() = default; Stacktrace(const Stacktrace&) = default; Stacktrace(Stacktrace&&) = default; explicit Stacktrace(std::vector frames) noexcept : frames_(std::move(frames)) {} Stacktrace& operator=(const Stacktrace&) = default; Stacktrace& operator=(Stacktrace&&) = default; [[nodiscard]] const std::vector& getFrames() const noexcept { return frames_; } }; // // public functions // [[nodiscard]] Result captureStacktrace(unsigned skipFrames = 0) noexcept; template TStream& operator<<(TStream& stream, const Stackframe& stackframe) { stream << "[" << stackframe.address << "] " << stackframe.filename << ":" << stackframe.lineNumber << " in " << demangleCPPIdentifier(stackframe.function.c_str()); return stream; } template TStream& operator<<(TStream& stream, const Stacktrace& stacktrace) { const int oldWidth = stream.width(); const std::ios::fmtflags oldFlags = stream.flags(); const int numDigits = static_cast(std::ceil(std::log10(stacktrace.getFrames().size()))); stream << std::left; for (const auto& [idx, frame] : mijin::enumerate(stacktrace.getFrames())) { stream << " #" << std::setw(numDigits) << idx << std::setw(0) << " at " << frame << "\n"; } stream << std::setw(oldWidth); stream.flags(oldFlags); return stream; } } // namespace mijin #endif // !defined(MIJIN_DEBUG_STACKTRACE_HPP_INCLUDED)