From 0764cef82fc53c124b76f0cddbef3e07e447edd9 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Thu, 7 Oct 2021 16:18:59 +0300 Subject: [PATCH] [K/N] Rewrite stacktrace formatting It was inconsistent between platforms, which leads to strange work of different parsers, including one in gradle plugin. Now it should work more or less like it was on MacOS before recent changes on all platforms. ^KT-49126 --- .../runtime/src/main/cpp/ExecFormat.cpp | 36 +++++-- .../runtime/src/main/cpp/ExecFormat.h | 2 +- .../runtime/src/main/cpp/StackTrace.cpp | 93 +++++++++++-------- .../runtime/src/mm/cpp/CallsChecker.cpp | 3 +- 4 files changed, 85 insertions(+), 49 deletions(-) diff --git a/kotlin-native/runtime/src/main/cpp/ExecFormat.cpp b/kotlin-native/runtime/src/main/cpp/ExecFormat.cpp index 7a2ddcc6617..ffd045919cf 100644 --- a/kotlin-native/runtime/src/main/cpp/ExecFormat.cpp +++ b/kotlin-native/runtime/src/main/cpp/ExecFormat.cpp @@ -99,12 +99,13 @@ void initSymbols() { } } -const char* addressToSymbol(const void* address) { +const char* addressToSymbol(const void* address, ptrdiff_t& resultOffset) { if (address == nullptr) return nullptr; // First, look up in dynamically loaded symbols. Dl_info info; if (dladdr(address, &info) != 0 && info.dli_sname != nullptr) { + resultOffset = reinterpret_cast(address) - reinterpret_cast(info.dli_saddr); return info.dli_sname; } @@ -121,6 +122,7 @@ const char* addressToSymbol(const void* address) { while (begin < end) { // st_value is load address adjusted. if (addressValue >= begin->st_value && addressValue < begin->st_value + begin->st_size) { + resultOffset = addressValue - begin->st_value; return &record.strtab[begin->st_name]; } begin++; @@ -131,8 +133,8 @@ const char* addressToSymbol(const void* address) { } // namespace -extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) { - const char* result = addressToSymbol(address); +extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) { + const char* result = addressToSymbol(address, resultOffset); if (result == nullptr) { return false; } else { @@ -295,11 +297,12 @@ class SymbolTable { } } - bool functionAddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) { + bool functionAddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) { IMAGE_SYMBOL* symbol = findFunctionSymbol(address); if (symbol == nullptr) { return false; } else { + resultOffset = reinterpret_cast(address) - reinterpret_cast(getSymbolAddress(symbol)); getSymbolName(symbol, resultBuffer, resultBufferSize); return true; } @@ -311,7 +314,7 @@ SymbolTable* theExeSymbolTable = nullptr; } // namespace -extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) { +extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) { if (theExeSymbolTable == nullptr) { // Note: do not protecting the lazy initialization by critical sections for simplicity; // this doesn't have any serious consequences. @@ -321,20 +324,33 @@ extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t RuntimeAssert(rv != 0, "GetModuleHandleExW fails"); theExeSymbolTable = konanConstructInstance(hModule); } - return theExeSymbolTable->functionAddressToSymbol(address, resultBuffer, resultBufferSize); + return theExeSymbolTable->functionAddressToSymbol(address, resultBuffer, resultBufferSize, resultOffset); } #elif __has_include("dlfcn.h") #include -extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) { +extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) { if (address == nullptr) return false; - const char *result = nullptr; Dl_info info; - if (dladdr(address, &info) != 0 && info.dli_sname != nullptr) { + if (dladdr(address, &info) == 0) return false; + const char *result = nullptr; + char symbuf[20]; + + if (info.dli_sname) { result = info.dli_sname; + resultOffset = reinterpret_cast(address) - reinterpret_cast(info.dli_saddr); + } else if (info.dli_fname) { + result = info.dli_fname; + resultOffset = reinterpret_cast(address) - reinterpret_cast(info.dli_fbase); + } else if (0 < konan::snprintf(symbuf, sizeof(symbuf), "%p", info.dli_saddr)) { + result = symbuf; + resultOffset = reinterpret_cast(address) - reinterpret_cast(info.dli_saddr); + } else { + resultOffset = reinterpret_cast(address); } + if (result == nullptr) { return false; } else { @@ -346,7 +362,7 @@ extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t #else -extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) { +extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) { return false; } diff --git a/kotlin-native/runtime/src/main/cpp/ExecFormat.h b/kotlin-native/runtime/src/main/cpp/ExecFormat.h index 62890df151e..df3b3249509 100644 --- a/kotlin-native/runtime/src/main/cpp/ExecFormat.h +++ b/kotlin-native/runtime/src/main/cpp/ExecFormat.h @@ -21,7 +21,7 @@ extern "C" { -bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize); +bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset); } // extern "C" diff --git a/kotlin-native/runtime/src/main/cpp/StackTrace.cpp b/kotlin-native/runtime/src/main/cpp/StackTrace.cpp index e2eb84de6f0..f79339b5d99 100644 --- a/kotlin-native/runtime/src/main/cpp/StackTrace.cpp +++ b/kotlin-native/runtime/src/main/cpp/StackTrace.cpp @@ -110,6 +110,53 @@ NO_INLINE KStdVector kotlin::GetCurrentStackTrace(int extraSkipFrames) no #endif // !KONAN_NO_BACKTRACE } +#if ! KONAN_NO_BACKTRACE +#include +#include +#include "cpp_support/Span.hpp" +#include "Format.h" + +#if __has_include("dlfcn.h") +#include +#endif + +__attribute__((format(printf, 6, 7))) +static size_t snprintf_with_addr(char* buf, size_t size, size_t frame, const void* addr, bool is_inline, const char *format, ...) { + std_support::span buffer{buf, size}; + const char* image = "???"; + char symbol[512]; + strcpy(symbol, "0x0"); + ptrdiff_t symbol_offset = reinterpret_cast(addr); + +#if __has_include("dlfcn.h") + Dl_info info; + memset(&info, 0, sizeof(info)); + dladdr(addr, &info); + + if (info.dli_fname) { + const char* tmp = strrchr(info.dli_fname, '/'); + if (tmp == nullptr) { + image = info.dli_fname; + } else { + image = tmp + 1; + } + } +#endif + + AddressToSymbol(addr, symbol, sizeof(symbol), symbol_offset); + + buffer = FormatToSpan(buffer, "%-4zd%-35s %-18p %s + %td ", frame, image, addr, symbol, symbol_offset); + if (is_inline) { + buffer = FormatToSpan(buffer, "[inlined] "); + } + std::va_list args; + va_start(args, format); + buffer = VFormatToSpan(buffer, format, args); + va_end(args); + return size - buffer.size(); +} +#endif // ! KONAN_NO_BACKTRACE + KStdVector kotlin::GetStackTraceStrings(void* const* stackTrace, size_t stackTraceSize) noexcept { #if KONAN_NO_BACKTRACE KStdVector strings; @@ -119,36 +166,12 @@ KStdVector kotlin::GetStackTraceStrings(void* const* stackTrace, siz KStdVector strings; strings.reserve(stackTraceSize); if (stackTraceSize > 0) { -#ifndef USE_GCC_UNWIND - char** symbols = backtrace_symbols(stackTrace, static_cast(stackTraceSize)); - RuntimeCheck(symbols != nullptr, "Not enough memory to retrieve the stacktrace"); -#endif - SourceInfo buffer[10]; // outside of the loop to avoid calling constructors and destructors each time for (size_t index = 0; index < stackTraceSize; ++index) { KNativePtr address = stackTrace[index]; if (!address || reinterpret_cast(address) == 1) continue; int frames_or_overflow = getSourceInfo(address, buffer, std::size(buffer)); int frames = std::min(frames_or_overflow, std::size(buffer)); -#if USE_GCC_UNWIND - char symbol_[512]; - if (!AddressToSymbol(address, symbol_, sizeof(symbol_))) { - // Make empty string: - symbol_[0] = '\0'; - } - const char* symbol = symbol_; -#else - const char* symbol = symbols[index]; - // On MacOs symbol name contain index, executable file containing symbol and address - // but it doesn't contain them on other platforms - // So we skip first three tokens to make things work similar on all platforms - for (int it = 0; it < 3; it++) { - while (*symbol != '\0' && *symbol != ' ') symbol++; - while (*symbol == ' ') symbol++; - } - // probably, this can't happen, but let's print at least something - if (*symbol == '\0') symbol = symbols[index]; -#endif bool isSomethingPrinted = false; char line[1024]; for (int frame = 0; frame < frames; frame++) { @@ -156,37 +179,33 @@ KStdVector kotlin::GetStackTraceStrings(void* const* stackTrace, siz if (!sourceInfo.getFileName().empty()) { bool is_last = frame == frames - 1; if (is_last && frames_or_overflow != frames) { - konan::snprintf(line, sizeof(line) - 1, "%-4zd %-18p %s (some inlined frames skipped)", strings.size(), address, symbol); + snprintf_with_addr(line, sizeof(line) - 1, strings.size(), address, false, "[some inlined frames skipped]"); strings.push_back(line); } - const char* inline_tag = is_last ? "" : "[inlined] "; if (sourceInfo.lineNumber != -1) { if (sourceInfo.column != -1) { - konan::snprintf( - line, sizeof(line) - 1, "%-4zd %-18p %s %s(%s:%d:%d)", strings.size(), address, symbol, inline_tag, + snprintf_with_addr( + line, sizeof(line) - 1, strings.size(), address, !is_last, "(%s:%d:%d)", sourceInfo.getFileName().c_str(), sourceInfo.lineNumber, sourceInfo.column); } else { - konan::snprintf( - line, sizeof(line) - 1, "%-4zd %-18p %s %s(%s:%d)", strings.size(), address, symbol, inline_tag, + snprintf_with_addr( + line, sizeof(line) - 1, strings.size(), address, !is_last, "(%s:%d)", sourceInfo.getFileName().c_str(), sourceInfo.lineNumber); } } else { - konan::snprintf(line, sizeof(line) - 1, "%-4zd %-18p %s %s(%s:)", strings.size(), address, symbol, inline_tag, - sourceInfo.getFileName().c_str()); + snprintf_with_addr( + line, sizeof(line) - 1, strings.size(), address, !is_last, "(%s:)", + sourceInfo.getFileName().c_str()); } isSomethingPrinted = true; strings.push_back(line); } } if (!isSomethingPrinted) { - konan::snprintf(line, sizeof(line) - 1, "%-4zd %-18p %s", strings.size(), address, symbol); + snprintf_with_addr(line, sizeof(line) - 1, strings.size(), address, false, "%s", ""); strings.push_back(line); } } - // Not konan::free. Used to free memory allocated in backtrace_symbols where malloc is used. -#ifndef USE_GCC_UNWIND - free(symbols); -#endif } return strings; #endif // !KONAN_NO_BACKTRACE diff --git a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp index e0d1034e398..644c5cf932b 100644 --- a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp +++ b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp @@ -347,7 +347,8 @@ extern "C" RUNTIME_NOTHROW RUNTIME_NODEBUG void Kotlin_mm_checkStateAtExternalFu char buf[200]; if (callee == nullptr) { - if (AddressToSymbol(calleePtr, buf, sizeof(buf))) { + ptrdiff_t unused; + if (AddressToSymbol(calleePtr, buf, sizeof(buf), unused)) { callee = buf; } else { callee = "unknown function";