[K/N] Support stacktrace using libbacktrace
This commit is contained in:
@@ -35,7 +35,8 @@ bitcode {
|
||||
"${target}Libbacktrace",
|
||||
"${target}Launcher",
|
||||
"${target}Debug",
|
||||
"${target}Release",
|
||||
"${target}SourceInfoCoreSymbolication",
|
||||
"${target}SourceInfoLibbacktrace",
|
||||
"${target}Strict",
|
||||
"${target}Relaxed",
|
||||
"${target}ProfileRuntime",
|
||||
@@ -118,9 +119,14 @@ bitcode {
|
||||
includeRuntime()
|
||||
}
|
||||
|
||||
create("release") {
|
||||
create("source_info_core_symbolication", file("src/source_info/core_symbolication")) {
|
||||
includeRuntime()
|
||||
}
|
||||
create("source_info_libbacktrace", file("src/source_info/libbacktrace")) {
|
||||
includeRuntime()
|
||||
headersDirs += files("src/libbacktrace/c/include")
|
||||
onlyIf { targetSupportsLibBacktrace(target) }
|
||||
}
|
||||
|
||||
create("strict") {
|
||||
includeRuntime()
|
||||
@@ -183,7 +189,6 @@ targetList.forEach { targetName ->
|
||||
"${targetName}Runtime",
|
||||
"${targetName}LegacyMemoryManager",
|
||||
"${targetName}Strict",
|
||||
"${targetName}Release",
|
||||
"${targetName}StdAlloc",
|
||||
"${targetName}Objc"
|
||||
)
|
||||
@@ -199,7 +204,6 @@ targetList.forEach { targetName ->
|
||||
"${targetName}Runtime",
|
||||
"${targetName}LegacyMemoryManager",
|
||||
"${targetName}Strict",
|
||||
"${targetName}Release",
|
||||
"${targetName}Mimalloc",
|
||||
"${targetName}OptAlloc",
|
||||
"${targetName}Objc"
|
||||
@@ -217,7 +221,6 @@ targetList.forEach { targetName ->
|
||||
"${targetName}ExperimentalMemoryManagerStms",
|
||||
"${targetName}CommonGc",
|
||||
"${targetName}SameThreadMsGc",
|
||||
"${targetName}Release",
|
||||
"${targetName}Mimalloc",
|
||||
"${targetName}OptAlloc",
|
||||
"${targetName}Objc"
|
||||
@@ -236,7 +239,6 @@ targetList.forEach { targetName ->
|
||||
"${targetName}ExperimentalMemoryManagerStms",
|
||||
"${targetName}CommonGc",
|
||||
"${targetName}SameThreadMsGc",
|
||||
"${targetName}Release",
|
||||
"${targetName}StdAlloc",
|
||||
"${targetName}Objc"
|
||||
)
|
||||
@@ -254,7 +256,6 @@ targetList.forEach { targetName ->
|
||||
"${targetName}ExperimentalMemoryManagerNoop",
|
||||
"${targetName}CommonGc",
|
||||
"${targetName}NoopGc",
|
||||
"${targetName}Release",
|
||||
"${targetName}Mimalloc",
|
||||
"${targetName}OptAlloc",
|
||||
"${targetName}Objc"
|
||||
@@ -273,7 +274,6 @@ targetList.forEach { targetName ->
|
||||
"${targetName}ExperimentalMemoryManagerNoop",
|
||||
"${targetName}CommonGc",
|
||||
"${targetName}NoopGc",
|
||||
"${targetName}Release",
|
||||
"${targetName}StdAlloc",
|
||||
"${targetName}Objc"
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "CompilerConstants.hpp"
|
||||
|
||||
#include "Common.h"
|
||||
#include "SourceInfo.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
@@ -14,6 +15,7 @@ RUNTIME_WEAK int32_t Kotlin_destroyRuntimeMode = 1;
|
||||
RUNTIME_WEAK int32_t Kotiln_gcAggressive = 0;
|
||||
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
|
||||
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
|
||||
RUNTIME_WEAK const Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function = nullptr;
|
||||
|
||||
ALWAYS_INLINE compiler::DestroyRuntimeMode compiler::destroyRuntimeMode() noexcept {
|
||||
return static_cast<compiler::DestroyRuntimeMode>(Kotlin_destroyRuntimeMode);
|
||||
|
||||
@@ -28,6 +28,9 @@ using string_view = std::experimental::string_view;
|
||||
extern "C" const int32_t KonanNeedDebugInfo;
|
||||
extern "C" const int32_t Kotlin_runtimeAssertsMode;
|
||||
extern "C" const char* const Kotlin_runtimeLogs;
|
||||
class SourceInfo;
|
||||
using Kotlin_getSourceInfo_FunctionType = int(*)(void * /*addr*/, SourceInfo* /*result*/, int /*result_size*/);
|
||||
extern "C" const Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function;
|
||||
|
||||
namespace kotlin {
|
||||
namespace compiler {
|
||||
@@ -71,6 +74,14 @@ ALWAYS_INLINE inline std::string_view runtimeLogs() noexcept {
|
||||
|
||||
bool freezingEnabled() noexcept;
|
||||
|
||||
ALWAYS_INLINE inline int getSourceInfo(void* addr, SourceInfo *result, int result_size) {
|
||||
if (Kotlin_getSourceInfo_Function == nullptr) {
|
||||
return 0;
|
||||
} else {
|
||||
return Kotlin_getSourceInfo_Function(addr, result, result_size);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace kotlin
|
||||
|
||||
|
||||
@@ -17,22 +17,15 @@
|
||||
#ifndef RUNTIME_SOURCEINFO_H
|
||||
#define RUNTIME_SOURCEINFO_H
|
||||
|
||||
struct SourceInfo {
|
||||
const char* fileName;
|
||||
int lineNumber;
|
||||
int column;
|
||||
#include <string>
|
||||
|
||||
class SourceInfo {
|
||||
std::string fileName;
|
||||
public:
|
||||
int lineNumber = -1;
|
||||
int column = -1;
|
||||
std::string& getFileName() { return fileName; }
|
||||
void setFilename(const char *newFileName) { fileName = newFileName ?: ""; }
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// returns number of filled elements in buffer
|
||||
// there can be several frames because of inlining
|
||||
int Kotlin_getSourceInfo(void* addr, SourceInfo *result, int result_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // RUNTIME_SOURCEINFO_H
|
||||
|
||||
@@ -69,9 +69,9 @@ _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {
|
||||
|
||||
THREAD_LOCAL_VARIABLE bool disallowSourceInfo = false;
|
||||
|
||||
#if !KONAN_NO_BACKTRACE && !USE_GCC_UNWIND
|
||||
#if !KONAN_NO_BACKTRACE
|
||||
int getSourceInfo(void* symbol, SourceInfo *result, int result_len) {
|
||||
return disallowSourceInfo ? 0 : Kotlin_getSourceInfo(symbol, result, result_len);
|
||||
return disallowSourceInfo ? 0 : compiler::getSourceInfo(symbol, result, result_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -118,61 +118,76 @@ KStdVector<KStdString> kotlin::GetStackTraceStrings(void* const* stackTrace, siz
|
||||
#else
|
||||
KStdVector<KStdString> strings;
|
||||
strings.reserve(stackTraceSize);
|
||||
#if USE_GCC_UNWIND
|
||||
for (size_t index = 0; index < stackTraceSize; ++index) {
|
||||
KNativePtr address = stackTrace[index];
|
||||
char symbol[512];
|
||||
if (!AddressToSymbol(address, symbol, sizeof(symbol))) {
|
||||
// Make empty string:
|
||||
symbol[0] = '\0';
|
||||
}
|
||||
char line[512];
|
||||
konan::snprintf(line, sizeof(line) - 1, "%s (%p)", symbol, (void*)(intptr_t)address);
|
||||
strings.push_back(line);
|
||||
}
|
||||
#else
|
||||
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];
|
||||
SourceInfo buffer[10];
|
||||
int frames = getSourceInfo(address, buffer, std::size(buffer));
|
||||
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];
|
||||
// returned symbol starts with frame number
|
||||
// we need to override it, because of inlining, so let's just drop first token
|
||||
while (*symbol != '\0' && *symbol != ' ') symbol++;
|
||||
while (*symbol == ' ') symbol++;
|
||||
// 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++) {
|
||||
auto &sourceInfo = buffer[frame];
|
||||
if (sourceInfo.fileName != nullptr) {
|
||||
const char* inline_tag = (frame == frames - 1) ? "" : "[inlined] ";
|
||||
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);
|
||||
strings.push_back(line);
|
||||
}
|
||||
const char* inline_tag = is_last ? "" : "[inlined] ";
|
||||
if (sourceInfo.lineNumber != -1) {
|
||||
konan::snprintf(
|
||||
line, sizeof(line) - 1, "%-4zd %s %s(%s:%d:%d)", strings.size(), symbol, inline_tag,
|
||||
sourceInfo.fileName, sourceInfo.lineNumber, buffer[frame].column);
|
||||
if (sourceInfo.column != -1) {
|
||||
konan::snprintf(
|
||||
line, sizeof(line) - 1, "%-4zd %-18p %s %s(%s:%d:%d)", strings.size(), address, symbol, inline_tag,
|
||||
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,
|
||||
sourceInfo.getFileName().c_str(), sourceInfo.lineNumber);
|
||||
}
|
||||
} else {
|
||||
konan::snprintf(line, sizeof(line) - 1, "%-4zd %s %s(%s:<unknown>)", strings.size(), symbol, inline_tag,
|
||||
sourceInfo.fileName);
|
||||
konan::snprintf(line, sizeof(line) - 1, "%-4zd %-18p %s %s(%s:<unknown>)", strings.size(), address, symbol, inline_tag,
|
||||
sourceInfo.getFileName().c_str());
|
||||
}
|
||||
isSomethingPrinted = true;
|
||||
strings.push_back(line);
|
||||
}
|
||||
}
|
||||
if (!isSomethingPrinted) {
|
||||
konan::snprintf(line, sizeof(line) - 1, "%-4zd %s", strings.size(), symbol);
|
||||
konan::snprintf(line, sizeof(line) - 1, "%-4zd %-18p %s", strings.size(), address, symbol);
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SourceInfo.h"
|
||||
|
||||
int Kotlin_getSourceInfo(void* addr, SourceInfo *result, int result_size) {
|
||||
return 0;
|
||||
}
|
||||
+6
-24
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
#include "SourceInfo.h"
|
||||
@@ -127,9 +116,9 @@ typedef struct {
|
||||
int end;
|
||||
} SymbolSourceInfoLimits;
|
||||
|
||||
extern "C" int Kotlin_getSourceInfo(void* addr, SourceInfo *result_buffer, int result_size) {
|
||||
extern "C" int Kotlin_getSourceInfo_core_symbolication(void* addr, SourceInfo *result_buffer, int result_size) {
|
||||
if (result_size == 0) return 0;
|
||||
__block SourceInfo result = { .fileName = nullptr, .lineNumber = -1, .column = -1 };
|
||||
__block SourceInfo result;
|
||||
__block bool continueUpdateResult = true;
|
||||
__block SymbolSourceInfoLimits limits = {.start = -1, .end = -1};
|
||||
|
||||
@@ -168,7 +157,7 @@ extern "C" int Kotlin_getSourceInfo(void* addr, SourceInfo *result_buffer, int r
|
||||
});
|
||||
|
||||
SYM_LOG("limits: {%s %d..%d}\n", limits.fileName, limits.start, limits.end);
|
||||
result.fileName = limits.fileName;
|
||||
result.setFilename(limits.fileName);
|
||||
|
||||
CSSymbolForeachSourceInfo(symbol,
|
||||
^(CSSourceInfoRef ref) {
|
||||
@@ -208,11 +197,4 @@ extern "C" int Kotlin_getSourceInfo(void* addr, SourceInfo *result_buffer, int r
|
||||
result_buffer[0] = result;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else // KONAN_CORE_SYMBOLICATION
|
||||
|
||||
extern "C" int Kotlin_getSourceInfo(void* addr, SourceInfo *result, int result_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // KONAN_CORE_SYMBOLICATION
|
||||
#endif // KONAN_CORE_SYMBOLICATION
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
#include "SourceInfo.h"
|
||||
#include "backtrace.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
extern "C" int Kotlin_getSourceInfo_libbacktrace(void* addr, SourceInfo *result, int result_size) {
|
||||
if (result_size == 0) return 0;
|
||||
/**
|
||||
* This is hack for better traces.
|
||||
* backtrace function returns address after call instruction, and address detection need call instruction itself
|
||||
* For honest solution, we should distinguish backtrace symbols got from signal handlers frames, ordinary frames,
|
||||
* and addresses got from somewhere else. But for now, we assume all addresses are ordinary backtrace frames.
|
||||
*/
|
||||
addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) - 1);
|
||||
auto ignore_error = [](void*, const char*, int){};
|
||||
static auto state = backtrace_create_state(nullptr, 1, ignore_error, nullptr);
|
||||
if (!state) return 0;
|
||||
struct callback_arg_t {
|
||||
SourceInfo *result;
|
||||
int result_ptr;
|
||||
int result_size;
|
||||
int total_count;
|
||||
} callback_arg;
|
||||
callback_arg.result = result;
|
||||
callback_arg.result_ptr = 0;
|
||||
callback_arg.result_size = result_size;
|
||||
callback_arg.total_count = 0;
|
||||
auto process_line = [](void *data, uintptr_t pc, const char *filename, int lineno, int column, const char *function) -> int {
|
||||
auto &callback_arg = *static_cast<callback_arg_t*>(data);
|
||||
// Non-inlined frame would be last one, it's better to have it, then intermediate ones
|
||||
if (callback_arg.result_ptr == callback_arg.result_size) {
|
||||
callback_arg.result_ptr--;
|
||||
}
|
||||
auto &info = callback_arg.result[callback_arg.result_ptr];
|
||||
info.setFilename(filename);
|
||||
info.lineNumber = lineno;
|
||||
info.column = column;
|
||||
callback_arg.result_ptr++;
|
||||
callback_arg.total_count++;
|
||||
// Let's stop at least at some point
|
||||
// Probably, this can happen only if debug info is corrupted
|
||||
return callback_arg.total_count > callback_arg.result_size * 10;
|
||||
};
|
||||
backtrace_pcinfo(state, reinterpret_cast<uintptr_t>(addr), process_line, ignore_error, &callback_arg);
|
||||
return callback_arg.total_count;
|
||||
}
|
||||
Reference in New Issue
Block a user