Fix Windows targets has bad addresses in stacktrace

^KT-49240 Fixed

Merge-request: KT-MR-6883
Merged-by: Vladimir Sukharev <Vladimir.Sukharev@jetbrains.com>
This commit is contained in:
Vladimir Sukharev
2022-08-24 09:05:06 +00:00
committed by Space
parent 18e61315f4
commit 7f1ab29668
8 changed files with 119 additions and 21 deletions
@@ -267,14 +267,22 @@ class SymbolTable {
return (const void*)(imageBase + sectionHeader->VirtualAddress + symbol->Value);
}
// Finds symbol having nearest smaller address
IMAGE_SYMBOL* findFunctionSymbol(const void* address) {
IMAGE_SYMBOL* result = nullptr;
auto addressPtr = reinterpret_cast<uintptr_t>(address);
auto resultPtr = reinterpret_cast<uintptr_t>(nullptr);
for (DWORD i = 0; i < numberOfSymbols; ++i) {
IMAGE_SYMBOL* symbol = &symbols[i];
if (symbol->Type == 0x20 && address == getSymbolAddress(symbol)) {
return symbol;
if (symbol->Type == 0x20) {
auto symbolPtr = reinterpret_cast<uintptr_t>(getSymbolAddress(symbol));
if(resultPtr < symbolPtr && symbolPtr <= addressPtr) {
resultPtr = symbolPtr;
result = symbol;
}
}
}
return nullptr;
return result;
}
public:
@@ -10,6 +10,15 @@
#elif USE_GCC_UNWIND
// GCC unwinder for backtrace.
#include <unwind.h>
#if __MINGW64__
#error // GCC unwinder in MinGW64/libgcc has a bugfix only in version 12. With previous libgcc versions, use RTL unwinder instead.
#endif
#elif USE_WINAPI_UNWIND
// Use RtlCaptureContext, RtlLookupFunctionEntry, RtlVirtualUnwind
#include <windows.h>
#include <winnt.h>
#else
// Glibc backtrace() function.
#include <execinfo.h>
@@ -46,11 +55,7 @@ struct Backtrace {
};
_Unwind_Ptr getUnwindPtr(_Unwind_Context* context) {
#if (__MINGW32__ || __MINGW64__)
return _Unwind_GetRegionStart(context);
#else
return _Unwind_GetIP(context);
#endif
}
_Unwind_Reason_Code depthCountCallback(struct _Unwind_Context* context, void* arg) {
@@ -76,6 +81,39 @@ _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {
return _URC_NO_REASON;
}
#elif USE_WINAPI_UNWIND
// winAPIUnwind() does:
// - if `result` is not empty -> stores IPs of stacktrace(ignoring first `skipCount` entries) into `result`, and returns amount of stored IPs
// - if `result` is empty -> returns depth of stacktrace(ignoring first `skipCount` entries)
NO_INLINE size_t winAPIUnwind(size_t skipCount, std_support::span<void*> result)
{
size_t resultSize = result.size();
bool doStoreIPs = resultSize > 0;
size_t currentSize = 0;
CONTEXT context = {};
context.ContextFlags = CONTEXT_ALL;
RtlCaptureContext (&context);
do {
DWORD64 imageBase = 0;
UNWIND_HISTORY_TABLE historyTable = {};
PRUNTIME_FUNCTION FunctionEntry = RtlLookupFunctionEntry (context.Rip, &imageBase, &historyTable);
if (!FunctionEntry)
break;
PVOID handlerData = nullptr;
ULONG64 establisherFramePointers[2] = { 0, 0 };
RtlVirtualUnwind (UNW_FLAG_NHANDLER, imageBase, context.Rip, FunctionEntry, &context, &handlerData, establisherFramePointers, nullptr);
if (skipCount > 0) {
skipCount--;
} else {
if(doStoreIPs)
result[currentSize] = reinterpret_cast<void*>(context.Rip);
++currentSize;
}
} while (context.Rip != 0 && (currentSize < resultSize || !doStoreIPs));
return currentSize;
}
#endif
THREAD_LOCAL_VARIABLE bool disallowSourceInfo = false;
@@ -96,13 +134,8 @@ NO_INLINE std_support::vector<void*> kotlin::internal::GetCurrentStackTrace(size
return {};
#else
#if (__MINGW32__ || __MINGW64__)
// Skip GetCurrentStackTrace, _Unwind_Backtrace + anything asked by the caller.
const size_t kSkipFrames = 2 + skipFrames;
#else
// Skip GetCurrentStackTrace + anything asked by the caller.
const size_t kSkipFrames = 1 + skipFrames;
#endif
std_support::vector<void*> result;
#if USE_GCC_UNWIND
@@ -115,6 +148,13 @@ NO_INLINE std_support::vector<void*> kotlin::internal::GetCurrentStackTrace(size
_Unwind_Backtrace(unwindCallback, static_cast<void*>(&traceHolder));
RuntimeAssert(result.size() == traceHolder.currentSize, "Expected and collected sizes of the stacktrace differ");
return result;
#elif USE_WINAPI_UNWIND
size_t depth = winAPIUnwind(kSkipFrames, std_support::span<void*>());
if (depth <= 0) return {};
result.resize(depth);
winAPIUnwind(kSkipFrames, std_support::span<void*>(result.data(), result.size()));
return result;
#else
// Take into account this function and StackTrace::current.
@@ -139,18 +179,15 @@ NO_INLINE size_t kotlin::internal::GetCurrentStackTrace(size_t skipFrames, std_s
return {};
#else
#if (__MINGW32__ || __MINGW64__)
// Skip GetCurrentStackTrace, _Unwind_Backtrace + anything asked by the caller.
const size_t kSkipFrames = 2 + skipFrames;
#else
// Skip GetCurrentStackTrace + anything asked by the caller.
const size_t kSkipFrames = 1 + skipFrames;
#endif
#if USE_GCC_UNWIND
Backtrace traceHolder(kSkipFrames, buffer);
_Unwind_Backtrace(unwindCallback, static_cast<void*>(&traceHolder));
return traceHolder.currentSize;
#elif USE_WINAPI_UNWIND
return winAPIUnwind(kSkipFrames, buffer);
#else
// Take into account this function and StackTrace::current.
constexpr size_t maxSize = GetMaxStackTraceDepth<StackTraceCapacityKind::kFixed>() + 2;
@@ -144,7 +144,7 @@ TEST(StackTraceTest, DeepStackTrace) {
auto stackTrace = GetDeepStackTrace(knownStackDepth);
auto symbolicStackTrace = GetStackTraceStrings(stackTrace.data());
#if USE_GCC_UNWIND
#if USE_GCC_UNWIND || USE_WINAPI_UNWIND
EXPECT_GE(stackTrace.size(), knownStackDepth);
size_t lastKnownIndex = knownStackDepth - 1;
#else
@@ -177,7 +177,7 @@ TEST(StackTraceTest, StackAllocatedDeepTraceWithEnoughCapacity) {
constexpr size_t capacity = 150;
auto stackTrace = GetDeepStackTrace<capacity>(knownStackDepth);
#if USE_GCC_UNWIND
#if USE_GCC_UNWIND || USE_WINAPI_UNWIND
EXPECT_GE(stackTrace.size(), knownStackDepth);
#else
// For platforms where the libc unwind is used (e.g. MacOS) the size of a collected trace is limited (see StackTrace::maxDepth).