Gradle: Kotlin/Native stack trace parser

#KT-30526
	#KT-30529
This commit is contained in:
Sergey Rostov
2019-04-04 14:49:11 +03:00
parent 0304f090e8
commit 125dc65178
3 changed files with 238 additions and 4 deletions
@@ -0,0 +1,138 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.gradle.targets.native.internal
import org.jetbrains.kotlin.gradle.internal.testing.ParsedStackTrace
data class KotlinNativeStackTrace(
val message: String?,
val stackTrace: List<KotlinNativeStackTraceElement>?
) {
fun toJvm() = ParsedStackTrace(
message,
stackTrace?.map { it.toJvmStackTraceElement() }
)
override fun toString(): String {
return "KotlinNativeStackTrace(\nmessage=\"$message\",\nstacktrace=[\n${stackTrace?.joinToString("\n")}\n])"
}
}
data class KotlinNativeStackTraceElement(
val bin: String?,
val address: String?,
val className: String?,
val methodName: String?,
val signature: String?,
val offset: Int = -1,
val fileName: String?,
val lineNumber: Int = -1,
val columnNumber: Int = -1
) {
fun toJvmStackTraceElement() = StackTraceElement(
className ?: "<global>",
methodName ?: "<unknown>",
fileName,
lineNumber
)
}
fun parseKotlinNativeStackTraceAsJvm(stackTrace: String): ParsedStackTrace? =
parseKotlinNativeStackTrace(stackTrace).toJvm()
fun parseKotlinNativeStackTrace(stackTrace: String): KotlinNativeStackTrace {
val message = StringBuilder()
var firstLines = true
val stack = mutableListOf<KotlinNativeStackTraceElement>()
// see examples in KotlinNativeStackTraceParserKtTest
stackTrace.lines().forEach {
val srcLine = it.trim()
var bin: String? = null
var address: String? = null
var className: String? = null
var methodName: String? = null
var signature: String? = null
var offset: Int = -1
var fileName: String? = null
var lineNumber: Int = -1
var columnNumber: Int = -1
fun parsePos(fileAndPos: String) {
val fileAndPosComponents = fileAndPos.split(":")
fileName = fileAndPosComponents[0]
if (fileAndPosComponents.size > 1) lineNumber = fileAndPosComponents[1].toIntOrNull() ?: -1
if (fileAndPosComponents.size > 2) columnNumber = fileAndPosComponents[2].toIntOrNull() ?: -1
}
// Example with debug info:
// at 15 test.kexe 0x0000000104902e12 kfun:f.q.n<f.q.n>(f.q.n<f.q.n>,f.q.n<f.q.n>) + 50 (/file/name.kt:23:5)
// Without:
// at 15 test.kexe 0x0000000104902e12 kfun:f.q.n<f.q.n>(f.q.n<f.q.n>,f.q.n<f.q.n>) + 50
if (srcLine.startsWith("at ")) {
firstLines = false
val line = srcLine.removePrefix("at ")
val offsetPos = line.indexOf('+')
if (offsetPos > 0) {
val withoutFileAndPos = if (line.indexOf('(', offsetPos) > 0) {
val fileAndPos = line.substringAfterLast("(").removeSuffix(")")
parsePos(fileAndPos)
line.substringBeforeLast("(")
} else line
val components = withoutFileAndPos.split(Regex("\\s+"))
if (components.size > 5) {
// val number = components[0]
bin = components[1]
address = components[2]
var classAndMethod = components[3]
// val plus = components[4]
offset = components[5].toIntOrNull() ?: -1
classAndMethod = classAndMethod.removePrefix("kfun:")
signature = "(" + classAndMethod.substringAfterLast("(")
classAndMethod = classAndMethod.substringBeforeLast("(")
if ("." in classAndMethod) {
methodName = classAndMethod.substringAfterLast(".").trim()
className = classAndMethod.substringBeforeLast(".").trim()
} else {
methodName = classAndMethod.trim()
}
if (methodName.isBlank()) methodName = null
stack.add(
KotlinNativeStackTraceElement(
bin,
address,
className,
methodName,
signature,
offset,
fileName,
lineNumber,
columnNumber
)
)
}
}
} else {
if (firstLines) {
message.appendln(it)
}
}
}
return KotlinNativeStackTrace(
message.toString().trim().let { if (it.isEmpty()) null else it },
if (stack.isEmpty()) null else stack
)
}
@@ -14,10 +14,11 @@ import org.gradle.process.internal.DefaultProcessForkOptions
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.targets.native.internal.parseKotlinNativeStackTraceAsJvm
import org.jetbrains.kotlin.gradle.tasks.KotlinTest
import org.jetbrains.kotlin.gradle.tasks.KotlinTestTask
import org.jetbrains.kotlin.gradle.testing.TestsGrouping
import java.io.File
open class KotlinNativeTest : KotlinTest() {
open class KotlinNativeTestTask : KotlinTestTask() {
@Suppress("LeakingThis")
@Internal
val processOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver)
@@ -47,8 +48,8 @@ open class KotlinNativeTest : KotlinTest() {
val clientSettings = TCServiceMessagesClientSettings(
name,
testNameSuffix = targetName,
prependSuiteName = targetName != null,
testNameSuffix = if (showTestTargetName) targetName else null,
prepandSuiteName = showTestTargetName,
treatFailedTestOutputAsStacktrace = true,
stackTraceParser = ::parseKotlinNativeStackTraceAsJvm
)
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.gradle.targets.native.internal
import org.junit.Assert.assertEquals
import org.junit.Test
class KotlinNativeStackTraceParserKtTest {
@Test
fun testDebug() {
assertEquals(
"""
KotlinNativeStackTrace(
message="kotlin.AssertionError: Expected <7>, actual <42>.",
stacktrace=[
KotlinNativeStackTraceElement(bin=test.kexe, address=0x00000001048f3c86, className=kotlin.Error, methodName=<init>, signature=(kotlin.String?)kotlin.Error, offset=70, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/Exceptions.kt, lineNumber=12, columnNumber=5)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x00000001048f3ada, className=kotlin.AssertionError, methodName=<init>, signature=(kotlin.Any?)kotlin.AssertionError, offset=122, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/Exceptions.kt, lineNumber=128, columnNumber=5)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104987939, className=kotlin.test.DefaultAsserter, methodName=fail, signature=(kotlin.String?)kotlin.Nothing, offset=137, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/DefaultAsserter.kt, lineNumber=16, columnNumber=19)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104987797, className=kotlin.test.Asserter, methodName=assertTrue, signature=(kotlin.Function0<kotlin.String?>;kotlin.Boolean), offset=263, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/Assertions.kt, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x000000010498840c, className=kotlin.test.Asserter, methodName=assertEquals, signature=(kotlin.String?;kotlin.Any?;kotlin.Any?), offset=380, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/Assertions.kt, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x000000010498b904, className=kotlin.test, methodName=assertEquals, signature=(#GENERIC;#GENERIC;kotlin.String?)Generic, offset=196, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/Assertions.kt, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x000000010498b7fe, className=kotlin.test, methodName=assertEquals${'$'}default, signature=(#GENERIC;#GENERIC;kotlin.String?;kotlin.Int)Generic, offset=158, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/Assertions.kt, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x000000010498b65c, className=sample.SampleTests, methodName=testMe, signature=(), offset=140, fileName=/Users/jetbrains/IdeaProjects/mpplib2/src/commonTest/kotlin/sample/SampleTests.kt, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x000000010498b593, className=sample.${'$'}SampleTests${'$'}test${'$'}0.${'$'}testMe${'$'}FUNCTION_REFERENCE${'$'}0, methodName=invoke#internal, signature=(sample.${'$'}SampleTests${'$'}test${'$'}0.${'$'}testMe${'$'}FUNCTION_REFERENCE${'$'}0.invoke#internal, offset=67, fileName=/Users/jetbrains/IdeaProjects/mpplib2/src/commonTest/kotlin/sample/SampleTests.kt, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x000000010498b4fb, className=sample.${'$'}SampleTests${'$'}test${'$'}0.${'$'}testMe${'$'}FUNCTION_REFERENCE${'$'}0, methodName=${'$'}<bridge-UNNN>invoke, signature=(#GENERIC)#internal, offset=75, fileName=/Users/jetbrains/IdeaProjects/mpplib2/src/commonTest/kotlin/sample/SampleTests.kt, lineNumber=10, columnNumber=6)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104985c1c, className=kotlin.native.internal.test.BaseClassSuite.TestCase, methodName=run, signature=(), offset=492, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/generated/_Collections.kt, lineNumber=98, columnNumber=7)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x000000010490649b, className=kotlin.native.internal.test.TestRunner, methodName=run#internal, signature=(kotlin.native.internal.test.TestRunner.run#internal, offset=1467, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/internal/test/TestRunner.kt, lineNumber=198, columnNumber=21)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104905287, className=kotlin.native.internal.test.TestRunner, methodName=runIteration#internal, signature=(kotlin.native.internal.test.TestRunner.runIteration#internal, offset=1863, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/system/Timing.kt, lineNumber=33, columnNumber=12)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x00000001049042d0, className=kotlin.native.internal.test.TestRunner, methodName=run, signature=()kotlin.Int, offset=816, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/internal/test/TestRunner.kt, lineNumber=232, columnNumber=17)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104902eae, className=kotlin.native.internal.test, methodName=testLauncherEntryPoint, signature=(kotlin.Array<kotlin.String>)kotlin.Int, offset=110, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/internal/test/Launcher.kt, lineNumber=19, columnNumber=47)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104902e12, className=kotlin.native.internal.test, methodName=main, signature=(kotlin.Array<kotlin.String>), offset=50, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/internal/test/Launcher.kt, lineNumber=23, columnNumber=5)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104902d67, className=null, methodName=Konan_start, signature=(Konan_start, offset=71, fileName=/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/launcher/kotlin/konan/start.kt, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104902cf1, className=null, methodName=Konan_run_start, signature=(Konan_run_start, offset=113, fileName=null, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=test.kexe, address=0x0000000104902c6b, className=null, methodName=Konan_main, signature=(Konan_main, offset=27, fileName=null, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=libdyld.dylib, address=0x00007fff5ab4fed9, className=null, methodName=start, signature=(start, offset=1, fileName=null, lineNumber=-1, columnNumber=-1)
])
""".trim(),
parseKotlinNativeStackTrace(
"""
kotlin.AssertionError: Expected <7>, actual <42>.
at 0 test.kexe 0x00000001048f3c86 kfun:kotlin.Error.<init>(kotlin.String?)kotlin.Error + 70 (/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/Exceptions.kt:12:5)
at 1 test.kexe 0x00000001048f3ada kfun:kotlin.AssertionError.<init>(kotlin.Any?)kotlin.AssertionError + 122 (/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/Exceptions.kt:128:5)
at 2 test.kexe 0x0000000104987939 kfun:kotlin.test.DefaultAsserter.fail(kotlin.String?)kotlin.Nothing + 137 (/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/DefaultAsserter.kt:16:19)
at 3 test.kexe 0x0000000104987797 kfun:kotlin.test.Asserter.assertTrue(kotlin.Function0<kotlin.String?>;kotlin.Boolean) + 263 (/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/Assertions.kt:<unknown>)
at 4 test.kexe 0x000000010498840c kfun:kotlin.test.Asserter.assertEquals(kotlin.String?;kotlin.Any?;kotlin.Any?) + 380 (/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/Assertions.kt:<unknown>)
at 5 test.kexe 0x000000010498b904 kfun:kotlin.test.assertEquals(#GENERIC;#GENERIC;kotlin.String?)Generic + 196 (/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/Assertions.kt:<unknown>)
at 6 test.kexe 0x000000010498b7fe kfun:kotlin.test.assertEquals${'$'}default(#GENERIC;#GENERIC;kotlin.String?;kotlin.Int)Generic + 158 (/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/kotlin/test/Assertions.kt:<unknown>)
at 7 test.kexe 0x000000010498b65c kfun:sample.SampleTests.testMe() + 140 (/Users/jetbrains/IdeaProjects/mpplib2/src/commonTest/kotlin/sample/SampleTests.kt:<unknown>)
at 8 test.kexe 0x000000010498b593 kfun:sample.${'$'}SampleTests${'$'}test${'$'}0.${'$'}testMe${'$'}FUNCTION_REFERENCE${'$'}0.invoke#internal + 67 (/Users/jetbrains/IdeaProjects/mpplib2/src/commonTest/kotlin/sample/SampleTests.kt:<unknown>)
at 9 test.kexe 0x000000010498b4fb kfun:sample.${'$'}SampleTests${'$'}test${'$'}0.${'$'}testMe${'$'}FUNCTION_REFERENCE${'$'}0.${'$'}<bridge-UNNN>invoke(#GENERIC)#internal + 75 (/Users/jetbrains/IdeaProjects/mpplib2/src/commonTest/kotlin/sample/SampleTests.kt:10:6)
at 10 test.kexe 0x0000000104985c1c kfun:kotlin.native.internal.test.BaseClassSuite.TestCase.run() + 492 (/Users/teamcity/buildAgent/work/4d622a065c544371/backend.native/build/stdlib/generated/_Collections.kt:98:7)
at 11 test.kexe 0x000000010490649b kfun:kotlin.native.internal.test.TestRunner.run#internal + 1467 (/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/internal/test/TestRunner.kt:198:21)
at 12 test.kexe 0x0000000104905287 kfun:kotlin.native.internal.test.TestRunner.runIteration#internal + 1863 (/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/system/Timing.kt:33:12)
at 13 test.kexe 0x00000001049042d0 kfun:kotlin.native.internal.test.TestRunner.run()kotlin.Int + 816 (/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/internal/test/TestRunner.kt:232:17)
at 14 test.kexe 0x0000000104902eae kfun:kotlin.native.internal.test.testLauncherEntryPoint(kotlin.Array<kotlin.String>)kotlin.Int + 110 (/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/internal/test/Launcher.kt:19:47)
at 15 test.kexe 0x0000000104902e12 kfun:kotlin.native.internal.test.main(kotlin.Array<kotlin.String>) + 50 (/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/internal/test/Launcher.kt:23:5)
at 16 test.kexe 0x0000000104902d67 Konan_start + 71 (/Users/teamcity/buildAgent/work/4d622a065c544371/runtime/src/launcher/kotlin/konan/start.kt:<unknown>)
at 17 test.kexe 0x0000000104902cf1 Konan_run_start + 113
at 18 test.kexe 0x0000000104902c6b Konan_main + 27
at 19 libdyld.dylib 0x00007fff5ab4fed9 start + 1
""".trim()
).toString()
)
}
@Test
fun testRelease() {
assertEquals(
"""
KotlinNativeStackTrace(
message="Uncaught Kotlin exception: kotlin.Exception: Foo!",
stacktrace=[
KotlinNativeStackTraceElement(bin=program.kexe, address=0x000000010d6ad726, className=kotlin.Exception, methodName=<init>, signature=(kotlin.String?)kotlin.Exception, offset=70, fileName=null, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=program.kexe, address=0x000000010d6bc7a9, className=org.test.A, methodName=<get-qux>, signature=()ValueType, offset=89, fileName=null, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=program.kexe, address=0x000000010d6bc712, className=org.test.A, methodName=baz, signature=()ValueType, offset=50, fileName=null, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=program.kexe, address=0x000000010d6bc633, className=org.test, methodName=bar, signature=()ValueType, offset=67, fileName=null, lineNumber=-1, columnNumber=-1)
KotlinNativeStackTraceElement(bin=program.kexe, address=0x000000010d6bc5e9, className=org.test, methodName=foo, signature=()ValueType, offset=9, fileName=null, lineNumber=-1, columnNumber=-1)
])
""".trim(),
parseKotlinNativeStackTrace(
"""
Uncaught Kotlin exception: kotlin.Exception: Foo!
at 0 program.kexe 0x000000010d6ad726 kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception + 70
at 1 program.kexe 0x000000010d6bc7a9 kfun:org.test.A.<get-qux>()ValueType + 89
at 2 program.kexe 0x000000010d6bc712 kfun:org.test.A.baz()ValueType + 50
at 3 program.kexe 0x000000010d6bc633 kfun:org.test.bar()ValueType + 67
at 4 program.kexe 0x000000010d6bc5e9 kfun:org.test.foo()ValueType + 9
""".trim()
).toString()
)
}
}