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";