[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 freezing by option<Freezing>()
|
||||||
|
|
||||||
val stripDebugInfoFromNativeLibs by booleanOption()
|
val stripDebugInfoFromNativeLibs by booleanOption()
|
||||||
|
|
||||||
|
val sourceInfoType by option<SourceInfoType>()
|
||||||
}
|
}
|
||||||
|
|
||||||
open class BinaryOption<T : Any>(
|
open class BinaryOption<T : Any>(
|
||||||
|
|||||||
+11
-1
@@ -94,6 +94,11 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
|||||||
else -> freezingMode
|
else -> freezingMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val sourceInfoType: SourceInfoType
|
||||||
|
get() = configuration.get(BinaryOptions.sourceInfoType)
|
||||||
|
?: SourceInfoType.CORESYMBOLICATION.takeIf { debug && target.family.isAppleFamily }
|
||||||
|
?: SourceInfoType.NOOP
|
||||||
|
|
||||||
|
|
||||||
val needVerifyIr: Boolean
|
val needVerifyIr: Boolean
|
||||||
get() = configuration.get(KonanConfigKeys.VERIFY_IR) == true
|
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()
|
private val shouldCoverLibraries = !configuration.getList(KonanConfigKeys.LIBRARIES_TO_COVER).isNullOrEmpty()
|
||||||
|
|
||||||
internal val runtimeNativeLibraries: List<String> = mutableListOf<String>().apply {
|
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") {
|
val useMimalloc = if (configuration.get(KonanConfigKeys.ALLOCATION_MODE) == "mimalloc") {
|
||||||
if (target.supportsMimallocAllocator()) {
|
if (target.supportsMimallocAllocator()) {
|
||||||
true
|
true
|
||||||
@@ -220,6 +225,11 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shouldCoverLibraries || shouldCoverSources) add("profileRuntime.bc")
|
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) {
|
if (useMimalloc) {
|
||||||
add("opt_alloc.bc")
|
add("opt_alloc.bc")
|
||||||
add("mimalloc.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_gcAggressive", Int32(if (context.config.gcAggressive) 1 else 0))
|
||||||
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
|
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
|
||||||
overrideRuntimeGlobal("Kotlin_freezingEnabled", Int32(if (context.config.freezing.enableFreezeAtRuntime) 1 else 0))
|
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") {
|
standaloneTest("check_stacktrace_format") {
|
||||||
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt') || (project.testTarget == 'ios_arm64')
|
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt')
|
||||||
flags = ['-g']
|
flags = ['-g']
|
||||||
source = "runtime/exceptions/check_stacktrace_format.kt"
|
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") {
|
standaloneTest("stack_trace_inline") {
|
||||||
// TODO: Enable after the test has been fixed.
|
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt')
|
||||||
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt') || (project.testTarget == 'ios_arm64')
|
flags = ['-g', '-Xg-generate-debug-trampoline=enable', '-Xbinary=sourceInfoType=coresymbolication']
|
||||||
flags = ['-g', '-Xg-generate-debug-trampoline=enable']
|
|
||||||
source = "runtime/exceptions/stack_trace_inline.kt"
|
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") {
|
standaloneTest("stack_trace_out_of_bounds") {
|
||||||
@@ -2689,9 +2697,32 @@ standaloneTest("stack_trace_out_of_bounds") {
|
|||||||
|
|
||||||
|
|
||||||
standaloneTest("kt-37572") {
|
standaloneTest("kt-37572") {
|
||||||
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt') || (project.testTarget == 'ios_arm64')
|
disabled = !isAppleTarget(project) || project.globalTestArgs.contains('-opt')
|
||||||
flags = ['-g']
|
flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication']
|
||||||
source = "runtime/exceptions/kt-37572.kt"
|
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") {
|
standaloneTest("custom_hook") {
|
||||||
@@ -5420,6 +5451,25 @@ if (isAppleTarget(project)) {
|
|||||||
}
|
}
|
||||||
swiftSources = ['framework/kt43517/']
|
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.text.Regex
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
|
var inlinesCount = 0
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
try {
|
try {
|
||||||
foo()
|
foo()
|
||||||
} catch (tw:Throwable) {
|
} catch (tw:Throwable) {
|
||||||
val stackTrace = tw.getStackTrace()
|
val stackTrace = tw.getStackTrace();
|
||||||
|
inlinesCount = stackTrace.count { it.contains("[inlined]")}
|
||||||
stackTrace.take(6).forEach(::checkFrame)
|
stackTrace.take(6).forEach(::checkFrame)
|
||||||
}
|
}
|
||||||
|
println(inlinesCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
@@ -25,17 +29,22 @@ fun throwException() {
|
|||||||
throw Error()
|
throw Error()
|
||||||
}
|
}
|
||||||
internal val regex = Regex("^(\\d+)\\ +.*/(.*):(\\d+):.*$")
|
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) {
|
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
|
val (pos, file, line) = regex.find(value)!!.destructured
|
||||||
goldValues[pos.toInt()]?.let{
|
goldValues[pos.toInt()]?.let {
|
||||||
assertEquals(it.first, file)
|
assertEquals(it.first, file)
|
||||||
assertEquals(it.second, line.toInt())
|
assertEquals(it.second, line.toInt())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import kotlin.text.Regex
|
import kotlin.text.Regex
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
|
var inlinesCount = 0
|
||||||
|
|
||||||
fun exception() {
|
fun exception() {
|
||||||
error("FAIL!")
|
error("FAIL!")
|
||||||
}
|
}
|
||||||
@@ -11,20 +13,23 @@ fun main() {
|
|||||||
}
|
}
|
||||||
catch (e:Exception) {
|
catch (e:Exception) {
|
||||||
val stackTrace = e.getStackTrace()
|
val stackTrace = e.getStackTrace()
|
||||||
|
inlinesCount = stackTrace.count { it.contains("[inlined]")}
|
||||||
stackTrace.take(6).forEach(::checkFrame)
|
stackTrace.take(6).forEach(::checkFrame)
|
||||||
}
|
}
|
||||||
|
println(inlinesCount)
|
||||||
}
|
}
|
||||||
internal val regex = Regex("^(\\d+)\\ +.*/(.*):(\\d+):.*$")
|
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) {
|
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
|
val (pos, file, line) = regex.find(value)!!.destructured
|
||||||
goldValues[pos.toInt()]?.let{
|
goldValues[pos.toInt()]?.let {
|
||||||
assertEquals(it.first, file)
|
assertEquals(it.first, file)
|
||||||
assertEquals(it.second, line.toInt())
|
assertEquals(it.second, line.toInt())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ object PlatformInfo {
|
|||||||
return platformManager.targetManager(targetName).target
|
return platformManager.targetManager(targetName).target
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun supportsLibBacktrace(project: Project): Boolean {
|
||||||
|
return getTarget(project).supportsLibBacktrace()
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun checkXcodeVersion(project: Project) {
|
fun checkXcodeVersion(project: Project) {
|
||||||
val properties = PropertiesProvider(project)
|
val properties = PropertiesProvider(project)
|
||||||
|
|||||||
@@ -325,6 +325,10 @@ fun compileSwift(project: Project, target: KonanTarget, sources: List<String>, o
|
|||||||
fun targetSupportsMimallocAllocator(targetName: String) =
|
fun targetSupportsMimallocAllocator(targetName: String) =
|
||||||
HostManager().targetByName(targetName).supportsMimallocAllocator()
|
HostManager().targetByName(targetName).supportsMimallocAllocator()
|
||||||
|
|
||||||
|
fun targetSupportsLibBacktrace(targetName: String) =
|
||||||
|
HostManager().targetByName(targetName).supportsLibBacktrace()
|
||||||
|
|
||||||
|
|
||||||
fun Project.mergeManifestsByTargets(source: File, destination: File) {
|
fun Project.mergeManifestsByTargets(source: File, destination: File) {
|
||||||
logger.info("Merging manifests: $source -> $destination")
|
logger.info("Merging manifests: $source -> $destination")
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ bitcode {
|
|||||||
"${target}Libbacktrace",
|
"${target}Libbacktrace",
|
||||||
"${target}Launcher",
|
"${target}Launcher",
|
||||||
"${target}Debug",
|
"${target}Debug",
|
||||||
"${target}Release",
|
"${target}SourceInfoCoreSymbolication",
|
||||||
|
"${target}SourceInfoLibbacktrace",
|
||||||
"${target}Strict",
|
"${target}Strict",
|
||||||
"${target}Relaxed",
|
"${target}Relaxed",
|
||||||
"${target}ProfileRuntime",
|
"${target}ProfileRuntime",
|
||||||
@@ -118,9 +119,14 @@ bitcode {
|
|||||||
includeRuntime()
|
includeRuntime()
|
||||||
}
|
}
|
||||||
|
|
||||||
create("release") {
|
create("source_info_core_symbolication", file("src/source_info/core_symbolication")) {
|
||||||
includeRuntime()
|
includeRuntime()
|
||||||
}
|
}
|
||||||
|
create("source_info_libbacktrace", file("src/source_info/libbacktrace")) {
|
||||||
|
includeRuntime()
|
||||||
|
headersDirs += files("src/libbacktrace/c/include")
|
||||||
|
onlyIf { targetSupportsLibBacktrace(target) }
|
||||||
|
}
|
||||||
|
|
||||||
create("strict") {
|
create("strict") {
|
||||||
includeRuntime()
|
includeRuntime()
|
||||||
@@ -183,7 +189,6 @@ targetList.forEach { targetName ->
|
|||||||
"${targetName}Runtime",
|
"${targetName}Runtime",
|
||||||
"${targetName}LegacyMemoryManager",
|
"${targetName}LegacyMemoryManager",
|
||||||
"${targetName}Strict",
|
"${targetName}Strict",
|
||||||
"${targetName}Release",
|
|
||||||
"${targetName}StdAlloc",
|
"${targetName}StdAlloc",
|
||||||
"${targetName}Objc"
|
"${targetName}Objc"
|
||||||
)
|
)
|
||||||
@@ -199,7 +204,6 @@ targetList.forEach { targetName ->
|
|||||||
"${targetName}Runtime",
|
"${targetName}Runtime",
|
||||||
"${targetName}LegacyMemoryManager",
|
"${targetName}LegacyMemoryManager",
|
||||||
"${targetName}Strict",
|
"${targetName}Strict",
|
||||||
"${targetName}Release",
|
|
||||||
"${targetName}Mimalloc",
|
"${targetName}Mimalloc",
|
||||||
"${targetName}OptAlloc",
|
"${targetName}OptAlloc",
|
||||||
"${targetName}Objc"
|
"${targetName}Objc"
|
||||||
@@ -217,7 +221,6 @@ targetList.forEach { targetName ->
|
|||||||
"${targetName}ExperimentalMemoryManagerStms",
|
"${targetName}ExperimentalMemoryManagerStms",
|
||||||
"${targetName}CommonGc",
|
"${targetName}CommonGc",
|
||||||
"${targetName}SameThreadMsGc",
|
"${targetName}SameThreadMsGc",
|
||||||
"${targetName}Release",
|
|
||||||
"${targetName}Mimalloc",
|
"${targetName}Mimalloc",
|
||||||
"${targetName}OptAlloc",
|
"${targetName}OptAlloc",
|
||||||
"${targetName}Objc"
|
"${targetName}Objc"
|
||||||
@@ -236,7 +239,6 @@ targetList.forEach { targetName ->
|
|||||||
"${targetName}ExperimentalMemoryManagerStms",
|
"${targetName}ExperimentalMemoryManagerStms",
|
||||||
"${targetName}CommonGc",
|
"${targetName}CommonGc",
|
||||||
"${targetName}SameThreadMsGc",
|
"${targetName}SameThreadMsGc",
|
||||||
"${targetName}Release",
|
|
||||||
"${targetName}StdAlloc",
|
"${targetName}StdAlloc",
|
||||||
"${targetName}Objc"
|
"${targetName}Objc"
|
||||||
)
|
)
|
||||||
@@ -254,7 +256,6 @@ targetList.forEach { targetName ->
|
|||||||
"${targetName}ExperimentalMemoryManagerNoop",
|
"${targetName}ExperimentalMemoryManagerNoop",
|
||||||
"${targetName}CommonGc",
|
"${targetName}CommonGc",
|
||||||
"${targetName}NoopGc",
|
"${targetName}NoopGc",
|
||||||
"${targetName}Release",
|
|
||||||
"${targetName}Mimalloc",
|
"${targetName}Mimalloc",
|
||||||
"${targetName}OptAlloc",
|
"${targetName}OptAlloc",
|
||||||
"${targetName}Objc"
|
"${targetName}Objc"
|
||||||
@@ -273,7 +274,6 @@ targetList.forEach { targetName ->
|
|||||||
"${targetName}ExperimentalMemoryManagerNoop",
|
"${targetName}ExperimentalMemoryManagerNoop",
|
||||||
"${targetName}CommonGc",
|
"${targetName}CommonGc",
|
||||||
"${targetName}NoopGc",
|
"${targetName}NoopGc",
|
||||||
"${targetName}Release",
|
|
||||||
"${targetName}StdAlloc",
|
"${targetName}StdAlloc",
|
||||||
"${targetName}Objc"
|
"${targetName}Objc"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "CompilerConstants.hpp"
|
#include "CompilerConstants.hpp"
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include "SourceInfo.h"
|
||||||
|
|
||||||
using namespace kotlin;
|
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 Kotiln_gcAggressive = 0;
|
||||||
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
|
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
|
||||||
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
|
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
|
||||||
|
RUNTIME_WEAK const Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function = nullptr;
|
||||||
|
|
||||||
ALWAYS_INLINE compiler::DestroyRuntimeMode compiler::destroyRuntimeMode() noexcept {
|
ALWAYS_INLINE compiler::DestroyRuntimeMode compiler::destroyRuntimeMode() noexcept {
|
||||||
return static_cast<compiler::DestroyRuntimeMode>(Kotlin_destroyRuntimeMode);
|
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 KonanNeedDebugInfo;
|
||||||
extern "C" const int32_t Kotlin_runtimeAssertsMode;
|
extern "C" const int32_t Kotlin_runtimeAssertsMode;
|
||||||
extern "C" const char* const Kotlin_runtimeLogs;
|
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 kotlin {
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
@@ -71,6 +74,14 @@ ALWAYS_INLINE inline std::string_view runtimeLogs() noexcept {
|
|||||||
|
|
||||||
bool freezingEnabled() 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 compiler
|
||||||
} // namespace kotlin
|
} // namespace kotlin
|
||||||
|
|
||||||
|
|||||||
@@ -17,22 +17,15 @@
|
|||||||
#ifndef RUNTIME_SOURCEINFO_H
|
#ifndef RUNTIME_SOURCEINFO_H
|
||||||
#define RUNTIME_SOURCEINFO_H
|
#define RUNTIME_SOURCEINFO_H
|
||||||
|
|
||||||
struct SourceInfo {
|
#include <string>
|
||||||
const char* fileName;
|
|
||||||
int lineNumber;
|
class SourceInfo {
|
||||||
int column;
|
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
|
#endif // RUNTIME_SOURCEINFO_H
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {
|
|||||||
|
|
||||||
THREAD_LOCAL_VARIABLE bool disallowSourceInfo = false;
|
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) {
|
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
|
#endif
|
||||||
|
|
||||||
@@ -118,61 +118,76 @@ KStdVector<KStdString> kotlin::GetStackTraceStrings(void* const* stackTrace, siz
|
|||||||
#else
|
#else
|
||||||
KStdVector<KStdString> strings;
|
KStdVector<KStdString> strings;
|
||||||
strings.reserve(stackTraceSize);
|
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) {
|
if (stackTraceSize > 0) {
|
||||||
|
#ifndef USE_GCC_UNWIND
|
||||||
char** symbols = backtrace_symbols(stackTrace, static_cast<int>(stackTraceSize));
|
char** symbols = backtrace_symbols(stackTrace, static_cast<int>(stackTraceSize));
|
||||||
RuntimeCheck(symbols != nullptr, "Not enough memory to retrieve the stacktrace");
|
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) {
|
for (size_t index = 0; index < stackTraceSize; ++index) {
|
||||||
KNativePtr address = stackTrace[index];
|
KNativePtr address = stackTrace[index];
|
||||||
SourceInfo buffer[10];
|
if (!address || reinterpret_cast<uintptr_t>(address) == 1) continue;
|
||||||
int frames = getSourceInfo(address, buffer, std::size(buffer));
|
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];
|
const char* symbol = symbols[index];
|
||||||
// returned symbol starts with frame number
|
// On MacOs symbol name contain index, executable file containing symbol and address
|
||||||
// we need to override it, because of inlining, so let's just drop first token
|
// but it doesn't contain them on other platforms
|
||||||
while (*symbol != '\0' && *symbol != ' ') symbol++;
|
// So we skip first three tokens to make things work similar on all platforms
|
||||||
while (*symbol == ' ') symbol++;
|
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
|
// probably, this can't happen, but let's print at least something
|
||||||
if (*symbol == '\0') symbol = symbols[index];
|
if (*symbol == '\0') symbol = symbols[index];
|
||||||
|
#endif
|
||||||
bool isSomethingPrinted = false;
|
bool isSomethingPrinted = false;
|
||||||
char line[1024];
|
char line[1024];
|
||||||
for (int frame = 0; frame < frames; frame++) {
|
for (int frame = 0; frame < frames; frame++) {
|
||||||
auto &sourceInfo = buffer[frame];
|
auto &sourceInfo = buffer[frame];
|
||||||
if (sourceInfo.fileName != nullptr) {
|
if (!sourceInfo.getFileName().empty()) {
|
||||||
const char* inline_tag = (frame == frames - 1) ? "" : "[inlined] ";
|
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) {
|
if (sourceInfo.lineNumber != -1) {
|
||||||
konan::snprintf(
|
if (sourceInfo.column != -1) {
|
||||||
line, sizeof(line) - 1, "%-4zd %s %s(%s:%d:%d)", strings.size(), symbol, inline_tag,
|
konan::snprintf(
|
||||||
sourceInfo.fileName, sourceInfo.lineNumber, buffer[frame].column);
|
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 {
|
} else {
|
||||||
konan::snprintf(line, sizeof(line) - 1, "%-4zd %s %s(%s:<unknown>)", strings.size(), symbol, inline_tag,
|
konan::snprintf(line, sizeof(line) - 1, "%-4zd %-18p %s %s(%s:<unknown>)", strings.size(), address, symbol, inline_tag,
|
||||||
sourceInfo.fileName);
|
sourceInfo.getFileName().c_str());
|
||||||
}
|
}
|
||||||
isSomethingPrinted = true;
|
isSomethingPrinted = true;
|
||||||
strings.push_back(line);
|
strings.push_back(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isSomethingPrinted) {
|
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);
|
strings.push_back(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Not konan::free. Used to free memory allocated in backtrace_symbols where malloc is used.
|
// Not konan::free. Used to free memory allocated in backtrace_symbols where malloc is used.
|
||||||
|
#ifndef USE_GCC_UNWIND
|
||||||
free(symbols);
|
free(symbols);
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
return strings;
|
return strings;
|
||||||
#endif // !KONAN_NO_BACKTRACE
|
#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.
|
* 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.
|
||||||
* 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"
|
#include "SourceInfo.h"
|
||||||
@@ -127,9 +116,9 @@ typedef struct {
|
|||||||
int end;
|
int end;
|
||||||
} SymbolSourceInfoLimits;
|
} 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;
|
if (result_size == 0) return 0;
|
||||||
__block SourceInfo result = { .fileName = nullptr, .lineNumber = -1, .column = -1 };
|
__block SourceInfo result;
|
||||||
__block bool continueUpdateResult = true;
|
__block bool continueUpdateResult = true;
|
||||||
__block SymbolSourceInfoLimits limits = {.start = -1, .end = -1};
|
__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);
|
SYM_LOG("limits: {%s %d..%d}\n", limits.fileName, limits.start, limits.end);
|
||||||
result.fileName = limits.fileName;
|
result.setFilename(limits.fileName);
|
||||||
|
|
||||||
CSSymbolForeachSourceInfo(symbol,
|
CSSymbolForeachSourceInfo(symbol,
|
||||||
^(CSSourceInfoRef ref) {
|
^(CSSourceInfoRef ref) {
|
||||||
@@ -208,11 +197,4 @@ extern "C" int Kotlin_getSourceInfo(void* addr, SourceInfo *result_buffer, int r
|
|||||||
result_buffer[0] = result;
|
result_buffer[0] = result;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#endif // KONAN_CORE_SYMBOLICATION
|
||||||
#else // KONAN_CORE_SYMBOLICATION
|
|
||||||
|
|
||||||
extern "C" int Kotlin_getSourceInfo(void* addr, SourceInfo *result, int result_size) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#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