[K/N] Support stacktrace using libbacktrace

This commit is contained in:
Pavel Kunyavskiy
2021-09-14 11:09:36 +03:00
committed by Space
parent 4fa5be3e58
commit c704fd6ae7
21 changed files with 350 additions and 123 deletions
@@ -19,6 +19,8 @@ object BinaryOptions : BinaryOptionRegistry() {
val freezing by option<Freezing>()
val stripDebugInfoFromNativeLibs by booleanOption()
val sourceInfoType by option<SourceInfoType>()
}
open class BinaryOption<T : Any>(
@@ -94,6 +94,11 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
else -> freezingMode
}
}
val sourceInfoType: SourceInfoType
get() = configuration.get(BinaryOptions.sourceInfoType)
?: SourceInfoType.CORESYMBOLICATION.takeIf { debug && target.family.isAppleFamily }
?: SourceInfoType.NOOP
val needVerifyIr: Boolean
get() = configuration.get(KonanConfigKeys.VERIFY_IR) == true
@@ -184,7 +189,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
private val shouldCoverLibraries = !configuration.getList(KonanConfigKeys.LIBRARIES_TO_COVER).isNullOrEmpty()
internal val runtimeNativeLibraries: List<String> = mutableListOf<String>().apply {
add(if (debug) "debug.bc" else "release.bc")
if (debug) add("debug.bc")
val useMimalloc = if (configuration.get(KonanConfigKeys.ALLOCATION_MODE) == "mimalloc") {
if (target.supportsMimallocAllocator()) {
true
@@ -220,6 +225,11 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
}
}
if (shouldCoverLibraries || shouldCoverSources) add("profileRuntime.bc")
add("source_info_core_symbolication.bc")
if (target.supportsLibBacktrace()) {
add("source_info_libbacktrace.bc")
add("libbacktrace.bc")
}
if (useMimalloc) {
add("opt_alloc.bc")
add("mimalloc.bc")
@@ -0,0 +1,12 @@
/*
* 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.
*/
package org.jetbrains.kotlin.backend.konan
enum class SourceInfoType {
CORESYMBOLICATION,
LIBBACKTRACE,
NOOP,
}
@@ -2631,6 +2631,17 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
overrideRuntimeGlobal("Kotlin_gcAggressive", Int32(if (context.config.gcAggressive) 1 else 0))
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
overrideRuntimeGlobal("Kotlin_freezingEnabled", Int32(if (context.config.freezing.enableFreezeAtRuntime) 1 else 0))
val getSourceInfoFunctionName = when (context.config.sourceInfoType) {
SourceInfoType.NOOP -> null
SourceInfoType.LIBBACKTRACE -> "Kotlin_getSourceInfo_libbacktrace"
SourceInfoType.CORESYMBOLICATION -> "Kotlin_getSourceInfo_core_symbolication"
}
if (getSourceInfoFunctionName != null) {
val getSourceInfoFunction = LLVMGetNamedFunction(context.llvmModule, getSourceInfoFunctionName)
?: LLVMAddFunction(context.llvmModule, getSourceInfoFunctionName,
functionType(int32Type, false, int8TypePtr, int8TypePtr, int32Type))
overrideRuntimeGlobal("Kotlin_getSourceInfo_Function", constValue(getSourceInfoFunction!!))
}
}
//-------------------------------------------------------------------------//
@@ -2669,16 +2669,24 @@ task extend_exception(type: KonanLocalTest) {
}
standaloneTest("check_stacktrace_format") {
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt') || (project.testTarget == 'ios_arm64')
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt')
flags = ['-g']
source = "runtime/exceptions/check_stacktrace_format.kt"
}
standaloneTest("check_stacktrace_format_coresymbolication") {
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication']
source = "runtime/exceptions/check_stacktrace_format.kt"
}
standaloneTest("stack_trace_inline") {
// TODO: Enable after the test has been fixed.
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt') || (project.testTarget == 'ios_arm64')
flags = ['-g', '-Xg-generate-debug-trampoline=enable']
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xg-generate-debug-trampoline=enable', '-Xbinary=sourceInfoType=coresymbolication']
source = "runtime/exceptions/stack_trace_inline.kt"
// test on simulator is writing Invalid connection: com.apple.coresymbolicationd by unknown reason
// so we need to use custom checker as workaround
outputChecker = { str -> str.split("\n").contains("0") }
}
standaloneTest("stack_trace_out_of_bounds") {
@@ -2689,9 +2697,32 @@ standaloneTest("stack_trace_out_of_bounds") {
standaloneTest("kt-37572") {
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt') || (project.testTarget == 'ios_arm64')
flags = ['-g']
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication']
source = "runtime/exceptions/kt-37572.kt"
// test on simulator is writing Invalid connection: com.apple.coresymbolicationd by unknown reason
// so we need to use custom checker as workaround
outputChecker = { str -> str.split("\n").contains("0") }
}
standaloneTest("check_stacktrace_format_libbacktrace") {
disabled = !supportsLibBacktrace(project)|| project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
source = "runtime/exceptions/check_stacktrace_format.kt"
}
standaloneTest("stack_trace_inline_libbacktrace") {
disabled = !supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
source = "runtime/exceptions/stack_trace_inline.kt"
outputChecker = { str -> str.split("\n").contains("1") }
}
standaloneTest("kt-37572-libbacktrace") {
disabled = !supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
source = "runtime/exceptions/kt-37572.kt"
outputChecker = { str -> str.split("\n").contains("2") }
}
standaloneTest("custom_hook") {
@@ -5420,6 +5451,25 @@ if (isAppleTarget(project)) {
}
swiftSources = ['framework/kt43517/']
}
frameworkTest("testStackTraceFramework") {
enabled = !project.globalTestArgs.contains('-opt')
framework('Stacktrace') {
sources = ['framework/stacktrace']
opts = ['-g']
}
swiftSources = ['framework/stacktrace/']
}
frameworkTest("testStackTraceByLibbacktraceFramework") {
enabled = !project.globalTestArgs.contains('-opt')
framework('StacktraceByLibbacktrace') {
sources = ['framework/stacktraceByLibbacktrace']
opts = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
}
swiftSources = ['framework/stacktraceByLibbacktrace/']
}
}
/**
@@ -0,0 +1,17 @@
/*
* 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.
*/
fun inner1() : Nothing {
throw Exception()
}
fun getStackTrace() : List<String> {
try {
inner1()
} catch (e: Exception) {
return e.getStackTrace().toList()
}
return emptyList()
}
@@ -0,0 +1,23 @@
import Foundation
import Stacktrace
func testStackTrace() throws {
let trace = StacktraceKt.getStackTrace()
try assertTrue(trace[0].contains("Throwable.kt"))
try assertTrue(trace[1].contains("Exceptions.kt"))
try assertTrue(trace[2].contains("stacktrace.kt:7"))
try assertTrue(trace[3].contains("stacktrace.kt:12"))
try assertTrue(trace[5].contains("stacktrace.swift:4"))
try assertTrue(trace[6].contains("main.swift"))
}
class StacktraceTests : TestProvider {
var tests: [TestCase] = []
init() {
providers.append(self)
tests = [
TestCase(name: "Stacktrace", method: withAutorelease(testStackTrace)),
]
}
}
@@ -0,0 +1,21 @@
/*
* 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.
*/
inline fun inner2() : Nothing {
throw Exception()
}
fun inner1() : Nothing {
inner2()
}
fun getStackTrace() : List<String> {
try {
inner1()
} catch (e: Exception) {
return e.getStackTrace().toList()
}
return emptyList()
}
@@ -0,0 +1,25 @@
import Foundation
import StacktraceByLibbacktrace
func testStackTrace() throws {
let trace = StacktraceByLibbacktraceKt.getStackTrace()
try assertTrue(trace[0].contains("Throwable.kt"))
try assertTrue(trace[1].contains("Exceptions.kt"))
try assertTrue(trace[2].contains("stacktraceByLibbacktrace.kt:7"))
try assertTrue(trace[2].contains("[inlined]"))
try assertTrue(trace[3].contains("stacktraceByLibbacktrace.kt:11"))
try assertTrue(trace[4].contains("stacktraceByLibbacktrace.kt:16"))
try assertTrue(trace[6].contains("stacktraceByLibbacktrace.swift:5"))
try assertTrue(trace[7].contains("main.swift:126"))
}
class StacktraceByLibbacktraceTests : TestProvider {
var tests: [TestCase] = []
init() {
providers.append(self)
tests = [
TestCase(name: "Stacktrace", method: withAutorelease(testStackTrace)),
]
}
}
@@ -1,13 +1,17 @@
import kotlin.text.Regex
import kotlin.test.*
var inlinesCount = 0
fun main() {
try {
foo()
} catch (tw:Throwable) {
val stackTrace = tw.getStackTrace()
val stackTrace = tw.getStackTrace();
inlinesCount = stackTrace.count { it.contains("[inlined]")}
stackTrace.take(6).forEach(::checkFrame)
}
println(inlinesCount)
}
fun foo() {
@@ -25,17 +29,22 @@ fun throwException() {
throw Error()
}
internal val regex = Regex("^(\\d+)\\ +.*/(.*):(\\d+):.*$")
internal val goldValues = arrayOf<Pair<String, Int>?>(
null,
null,
"kt-37572.kt" to 25,
"kt-37572.kt" to 16,
"kt-37572.kt" to 6,
"kt-37572.kt" to 4)
internal fun checkFrame(value:String) {
val goldValues = arrayOf<Pair<String, Int>?>(
null,
null,
"kt-37572.kt" to 29,
"kt-37572.kt" to 20,
*(if (inlinesCount != 0) arrayOf(
"kt-37572.kt" to 25,
"kt-37572.kt" to 18,
) else emptyArray()),
"kt-37572.kt" to 8,
"kt-37572.kt" to 6)
val (pos, file, line) = regex.find(value)!!.destructured
goldValues[pos.toInt()]?.let{
goldValues[pos.toInt()]?.let {
assertEquals(it.first, file)
assertEquals(it.second, line.toInt())
}
@@ -1,6 +1,8 @@
import kotlin.text.Regex
import kotlin.test.*
var inlinesCount = 0
fun exception() {
error("FAIL!")
}
@@ -11,20 +13,23 @@ fun main() {
}
catch (e:Exception) {
val stackTrace = e.getStackTrace()
inlinesCount = stackTrace.count { it.contains("[inlined]")}
stackTrace.take(6).forEach(::checkFrame)
}
println(inlinesCount)
}
internal val regex = Regex("^(\\d+)\\ +.*/(.*):(\\d+):.*$")
internal val goldValues = arrayOf<Pair<String, Int>?>(
null,
null,
null,
null,
"stack_trace_inline.kt" to 5,
"stack_trace_inline.kt" to 10)
internal fun checkFrame(value:String) {
val goldValues = arrayOf<Pair<String, Int>?>(
null,
null,
null,
null,
*(if (inlinesCount != 0) arrayOf(null) else emptyArray()),
"stack_trace_inline.kt" to 7,
"stack_trace_inline.kt" to 12)
val (pos, file, line) = regex.find(value)!!.destructured
goldValues[pos.toInt()]?.let{
goldValues[pos.toInt()]?.let {
assertEquals(it.first, file)
assertEquals(it.second, line.toInt())
}
@@ -37,6 +37,11 @@ object PlatformInfo {
return platformManager.targetManager(targetName).target
}
@JvmStatic
fun supportsLibBacktrace(project: Project): Boolean {
return getTarget(project).supportsLibBacktrace()
}
@JvmStatic
fun checkXcodeVersion(project: Project) {
val properties = PropertiesProvider(project)
@@ -325,6 +325,10 @@ fun compileSwift(project: Project, target: KonanTarget, sources: List<String>, o
fun targetSupportsMimallocAllocator(targetName: String) =
HostManager().targetByName(targetName).supportsMimallocAllocator()
fun targetSupportsLibBacktrace(targetName: String) =
HostManager().targetByName(targetName).supportsLibBacktrace()
fun Project.mergeManifestsByTargets(source: File, destination: File) {
logger.info("Merging manifests: $source -> $destination")
+8 -8
View File
@@ -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;
}
@@ -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;
}