[K/N] Support stacktrace using libbacktrace
This commit is contained in:
+2
@@ -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>(
|
||||
|
||||
+11
-1
@@ -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")
|
||||
|
||||
+12
@@ -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,
|
||||
}
|
||||
+11
@@ -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)),
|
||||
]
|
||||
}
|
||||
}
|
||||
+21
@@ -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()
|
||||
}
|
||||
+25
@@ -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")
|
||||
|
||||
|
||||
@@ -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