mijin2/source/mijin/debug/stacktrace.hpp

86 lines
2.0 KiB
C++

#pragma once
#if !defined(MIJIN_DEBUG_STACKTRACE_HPP_INCLUDED)
#define MIJIN_DEBUG_STACKTRACE_HPP_INCLUDED 1
#include <cmath>
#include <iomanip>
#include <vector>
#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<Stackframe> frames_;
public:
Stacktrace() = default;
Stacktrace(const Stacktrace&) = default;
Stacktrace(Stacktrace&&) = default;
explicit Stacktrace(std::vector<Stackframe> frames) noexcept : frames_(std::move(frames)) {}
Stacktrace& operator=(const Stacktrace&) = default;
Stacktrace& operator=(Stacktrace&&) = default;
[[nodiscard]] const std::vector<Stackframe>& getFrames() const noexcept { return frames_; }
};
//
// public functions
//
[[nodiscard]] Result<Stacktrace> captureStacktrace(unsigned skipFrames = 0) noexcept;
template<typename TStream>
TStream& operator<<(TStream& stream, const Stackframe& stackframe)
{
stream << "[" << stackframe.address << "] " << stackframe.filename << ":" << stackframe.lineNumber << " in " << demangleCPPIdentifier(stackframe.function.c_str());
return stream;
}
template<typename TStream>
TStream& operator<<(TStream& stream, const Stacktrace& stacktrace)
{
const int oldWidth = stream.width();
const std::ios::fmtflags oldFlags = stream.flags();
const int numDigits = static_cast<int>(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)