[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
This commit is contained in:
Pavel Kunyavskiy
2021-10-07 16:18:59 +03:00
committed by Space
parent 2fe9420ca1
commit 0764cef82f
4 changed files with 85 additions and 49 deletions
@@ -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<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(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<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(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<SymbolTable>(hModule);
}
return theExeSymbolTable->functionAddressToSymbol(address, resultBuffer, resultBufferSize);
return theExeSymbolTable->functionAddressToSymbol(address, resultBuffer, resultBufferSize, resultOffset);
}
#elif __has_include("dlfcn.h")
#include <dlfcn.h>
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<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(info.dli_saddr);
} else if (info.dli_fname) {
result = info.dli_fname;
resultOffset = reinterpret_cast<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(info.dli_fbase);
} else if (0 < konan::snprintf(symbuf, sizeof(symbuf), "%p", info.dli_saddr)) {
result = symbuf;
resultOffset = reinterpret_cast<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(info.dli_saddr);
} else {
resultOffset = reinterpret_cast<ptrdiff_t>(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;
}
@@ -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"
@@ -110,6 +110,53 @@ NO_INLINE KStdVector<void*> kotlin::GetCurrentStackTrace(int extraSkipFrames) no
#endif // !KONAN_NO_BACKTRACE
}
#if ! KONAN_NO_BACKTRACE
#include <cstdarg>
#include <cstring>
#include "cpp_support/Span.hpp"
#include "Format.h"
#if __has_include("dlfcn.h")
#include <dlfcn.h>
#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<char> buffer{buf, size};
const char* image = "???";
char symbol[512];
strcpy(symbol, "0x0");
ptrdiff_t symbol_offset = reinterpret_cast<ptrdiff_t>(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<KStdString> kotlin::GetStackTraceStrings(void* const* stackTrace, size_t stackTraceSize) noexcept {
#if KONAN_NO_BACKTRACE
KStdVector<KStdString> strings;
@@ -119,36 +166,12 @@ KStdVector<KStdString> kotlin::GetStackTraceStrings(void* const* stackTrace, siz
KStdVector<KStdString> strings;
strings.reserve(stackTraceSize);
if (stackTraceSize > 0) {
#ifndef USE_GCC_UNWIND
char** symbols = backtrace_symbols(stackTrace, static_cast<int>(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<uintptr_t>(address) == 1) continue;
int frames_or_overflow = getSourceInfo(address, buffer, std::size(buffer));
int frames = std::min<int>(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<KStdString> 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:<unknown>)", strings.size(), address, symbol, inline_tag,
sourceInfo.getFileName().c_str());
snprintf_with_addr(
line, sizeof(line) - 1, strings.size(), address, !is_last, "(%s:<unknown>)",
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
@@ -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";