Compare commits

..

3 Commits

3 changed files with 144 additions and 3 deletions

View File

@ -5,14 +5,22 @@
#include <string>
#include "../detect.hpp"
#if MIJIN_COMPILER == MIJIN_COMPILER_CLANG || MIJIN_COMPILER == MIJIN_COMPILER_GCC
#if MIJIN_TARGET_OS != MIJIN_OS_WINDOWS && (MIJIN_COMPILER == MIJIN_COMPILER_CLANG || MIJIN_COMPILER == MIJIN_COMPILER_GCC)
#define MIJIN_USE_LIBBACKTRACE 1
#else
#define MIJIN_USE_LIBBACKTRACE 0
#endif
#if MIJIN_USE_LIBBACKTRACE
#include <backtrace.h>
#include <backtrace.h>
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
#include <array>
#include <cstddef>
#include <mutex>
#include <Windows.h>
#include <DbgHelp.h>
#include "../util/winundef.hpp"
#pragma comment(lib, "dbghelp")
#endif
@ -32,11 +40,15 @@ namespace
// internal types
//
#if MIJIN_USE_LIBBACKTRACE
struct BacktraceData
{
std::optional<std::string> error;
std::vector<Stackframe> stackframes;
};
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
HANDLE gProcessHandle = nullptr;
#endif
//
// internal variables
@ -44,6 +56,11 @@ struct BacktraceData
thread_local Optional<Stacktrace> gCurrentExceptionStackTrace;
#if MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
std::mutex gDbgHelpMutex;
bool gDbgHelpInitCalled = false;
#endif
//
// internal functions
//
@ -68,6 +85,49 @@ void backtraceErrorCallback(void* data, const char* msg, int /* errnum */)
}
thread_local backtrace_state* gBacktraceState = nullptr;
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
void cleanupDbgHelp() MIJIN_NOEXCEPT
{
if (!SymCleanup(gProcessHandle))
{
[[maybe_unused]] const DWORD error = GetLastError();
MIJIN_ERROR("Error cleaning up DbgHelp.");
}
}
[[nodiscard]]
bool initDbgHelp() MIJIN_NOEXCEPT
{
if (gDbgHelpInitCalled)
{
return gProcessHandle != nullptr; // if init was successful, process handle is not null
}
gDbgHelpInitCalled = true;
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
HANDLE hCurrentProcess = GetCurrentProcess();
HANDLE hCopy = nullptr;
if (!DuplicateHandle(hCurrentProcess, hCurrentProcess, hCurrentProcess, &hCopy, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
[[maybe_unused]] const DWORD error = GetLastError();
MIJIN_ERROR("Error duplicating process handle.");
return false;
}
if (!SymInitialize(hCopy, nullptr, true))
{
[[maybe_unused]] const DWORD error = GetLastError();
MIJIN_ERROR("Error initializing DbHelp.");
return false;
}
const int result = std::atexit(&cleanupDbgHelp);
MIJIN_ASSERT(result == 0, "Error registering DbgHelp cleanup handler.");
// only copy in the end so we can still figure out if initialization was successful
gProcessHandle = hCopy;
return true;
}
#endif // MIJIN_USE_LIBBACKTRACE
} // namespace
@ -98,7 +158,85 @@ Result<Stacktrace> captureStacktrace(unsigned skipFrames) MIJIN_NOEXCEPT
}
return Stacktrace(std::move(btData.stackframes));
#else // MIJIN_USE_LIBBACKTRACE
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
if (!initDbgHelp())
{
return ResultError("error initializing DbgHelp");
}
const HANDLE hThread = GetCurrentThread();
CONTEXT context;
RtlCaptureContext(&context);
STACKFRAME64 stackFrame = {
.AddrPC = {
.Offset = context.Rip,
.Mode = AddrModeFlat
},
.AddrFrame = {
.Offset = context.Rbp,
.Mode = AddrModeFlat
},
.AddrStack = {
.Offset = context.Rsp,
.Mode = AddrModeFlat
}
};
++skipFrames; // always skip the first frame (the current function)
// for symbol info
DWORD64 displacement64 = 0;
static constexpr std::size_t SYMBOL_BUFFER_SIZE = sizeof(SYMBOL_INFO) + (MAX_SYM_NAME * sizeof(char));
std::array<std::byte, SYMBOL_BUFFER_SIZE> symbolBuffer alignas(SYMBOL_INFO);
SYMBOL_INFO& symbolInfo = *std::bit_cast<SYMBOL_INFO*>(symbolBuffer.data());
symbolInfo.SizeOfStruct = sizeof(SYMBOL_BUFFER_SIZE);
symbolInfo.MaxNameLen = MAX_SYM_NAME;
// for file and line info
DWORD displacement = 0;
IMAGEHLP_LINE64 line64;
line64.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
std::vector<Stackframe> stackframes;
while (StackWalk64(
/* MachineType = */ IMAGE_FILE_MACHINE_AMD64,
/* hProcess = */ gProcessHandle,
/* hThread = */ hThread,
/* StackFrame = */ &stackFrame,
/* ContextRecord = */ &context,
/* ReadMemoryRoutine = */ nullptr,
/* FunctionTableAccessRoutine = */ &SymFunctionTableAccess64,
/* GetModuleBaseRoutine = */ &SymGetModuleBase64,
/* TranslateAddress = */ nullptr
))
{
if (skipFrames > 0)
{
--skipFrames;
continue;
}
Stackframe& frame = stackframes.emplace_back();
const DWORD64 baseAddress = SymGetModuleBase64(gProcessHandle, stackFrame.AddrPC.Offset);
const DWORD64 relativeAddress = stackFrame.AddrPC.Offset - baseAddress;
frame.address = std::bit_cast<void*>(relativeAddress);
if (SymFromAddr(gProcessHandle, stackFrame.AddrPC.Offset, &displacement64, &symbolInfo))
{
frame.function = symbolInfo.Name;
}
if (SymGetLineFromAddr64(gProcessHandle, stackFrame.AddrPC.Offset, &displacement, &line64))
{
frame.filename = line64.FileName;
frame.lineNumber = static_cast<int>(line64.LineNumber);
}
}
return Stacktrace(std::move(stackframes));
#else // MIJIN_USE_LIBBACKTRACE || (MIJIN_TARGET_OS == MIJIN_OS_WINDOWS)
(void) skipFrames;
return ResultError("not implemented");
#endif // MIJIN_USE_LIBBACKTRACE

View File

@ -56,6 +56,7 @@ public:
{
static_assert(always_false_v<char_t>, "Character type not supported.");
}
std::fflush(stream);
}
};

View File

@ -326,6 +326,7 @@ public:
// couldn't allocate the snapshot
return {};
}
::new (snapshotData) StackAllocatorSnapshotData;
StackAllocatorSnapshot snapshot;
snapshot.data = snapshotData;
if (firstChunk_ != prevFirst)
@ -442,6 +443,7 @@ public:
Chunk* snapshotChunk = findChunk(snapshot.data);
MIJIN_ASSERT_FATAL(snapshotChunk != nullptr, "Snapshot not in chunks?");
snapshotChunk->allocated -= calcSnapshotSize(snapshot->numChunks); // note: this might miss the alignment bytes of the snapshot, but that should be fine
snapshot.data->~StackAllocatorSnapshotData();
}
private:
void initAndAddChunk(Chunk* newChunk) noexcept