From 0369a282c8125e0c418c2f25b21aa1478e429c94 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Thu, 20 Dec 2018 13:47:15 +0300 Subject: [PATCH] Symbolication sample (#2479) --- runtime/src/main/cpp/Porting.cpp | 7 +- runtime/src/main/cpp/Runtime.cpp | 1 + runtime/src/main/kotlin/kotlin/Throwable.kt | 8 +-- samples/settings.gradle | 1 + samples/symbolication/README.md | 4 ++ samples/symbolication/build.gradle | 23 +++++++ samples/symbolication/gradle.properties | 2 + .../cinterop/coreSymbolication.def | 68 +++++++++++++++++++ .../src/symbolicationMain/kotlin/main.kt | 58 ++++++++++++++++ samples/symbolication/test.kt | 22 ++++++ 10 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 samples/symbolication/README.md create mode 100644 samples/symbolication/build.gradle create mode 100644 samples/symbolication/gradle.properties create mode 100644 samples/symbolication/src/nativeInterop/cinterop/coreSymbolication.def create mode 100644 samples/symbolication/src/symbolicationMain/kotlin/main.kt create mode 100644 samples/symbolication/test.kt diff --git a/runtime/src/main/cpp/Porting.cpp b/runtime/src/main/cpp/Porting.cpp index b8b5b241e60..183ddb772a4 100644 --- a/runtime/src/main/cpp/Porting.cpp +++ b/runtime/src/main/cpp/Porting.cpp @@ -112,7 +112,6 @@ extern "C" int rpl_vsnprintf(char *, size_t, const char *, va_list); #define vsnprintf_impl ::vsnprintf #endif - void consolePrintf(const char* format, ...) { char buffer[1024]; va_list args; @@ -122,7 +121,6 @@ void consolePrintf(const char* format, ...) { consoleWriteUtf8(buffer, rv); } - // Thread execution. #if !KONAN_NO_THREADS @@ -325,7 +323,7 @@ void* moreCore(int32_t delta) { return (void *) top; } -// dlmalloc wants to know the page size. +// dlmalloc() wants to know the page size. long getpagesize() { return WASM_PAGESIZE; } @@ -455,4 +453,5 @@ extern "C" { while(1) {} } #endif // KONAN_ZEPHYR -} + +} // extern "C" diff --git a/runtime/src/main/cpp/Runtime.cpp b/runtime/src/main/cpp/Runtime.cpp index 504ffa8c36b..4699de7bfd9 100644 --- a/runtime/src/main/cpp/Runtime.cpp +++ b/runtime/src/main/cpp/Runtime.cpp @@ -85,6 +85,7 @@ RuntimeState* initRuntime() { if (firstRuntime) { isMainThread = 1; konan::consoleInit(); + InitOrDeinitGlobalVariables(INIT_GLOBALS); } InitOrDeinitGlobalVariables(INIT_THREAD_LOCAL_GLOBALS); diff --git a/runtime/src/main/kotlin/kotlin/Throwable.kt b/runtime/src/main/kotlin/kotlin/Throwable.kt index 340bbcb5a84..1f4fb4f19ee 100644 --- a/runtime/src/main/kotlin/kotlin/Throwable.kt +++ b/runtime/src/main/kotlin/kotlin/Throwable.kt @@ -29,13 +29,13 @@ public open class Throwable(open val message: String?, open val cause: Throwable getStackTraceStrings(stackTrace) } - fun getStackTrace(): Array = stackTraceStrings + public fun getStackTrace(): Array = stackTraceStrings - fun printStackTrace() { + public fun printStackTrace() { println(this.toString()) for (element in stackTraceStrings) { - println(" at " + element) + println(" at $element") } this.cause?.printEnclosedStackTrace(this) @@ -48,7 +48,7 @@ public open class Throwable(open val message: String?, open val cause: Throwable this.printStackTrace() } - override fun toString(): String { + override public fun toString(): String { val kClass = this::class val s = kClass.qualifiedName ?: kClass.simpleName ?: "Throwable" return if (message != null) s + ": " + message.toString() else s diff --git a/samples/settings.gradle b/samples/settings.gradle index bc3074fd53e..20a09b39051 100644 --- a/samples/settings.gradle +++ b/samples/settings.gradle @@ -42,6 +42,7 @@ if (MPPTools.isMacos() || MPPTools.isLinux()) { if (MPPTools.isMacos()) { include ':objc' include ':opengl' + include ':symbolication' include ':uikit' } diff --git a/samples/symbolication/README.md b/samples/symbolication/README.md new file mode 100644 index 00000000000..b9c54fb5917 --- /dev/null +++ b/samples/symbolication/README.md @@ -0,0 +1,4 @@ +# Backtrace symbolication tool + +Produces more verbose backtrace symbolication with file name and line number. +Only works on debug .dSYM binaries on macOS at the moment. diff --git a/samples/symbolication/build.gradle b/samples/symbolication/build.gradle new file mode 100644 index 00000000000..2b0f81f6ad9 --- /dev/null +++ b/samples/symbolication/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'kotlin-multiplatform' +} + +// Determine host preset. +def hostPreset = MPPTools.defaultHostPreset(project) + +kotlin { + targets { + fromPreset(hostPreset, 'symbolication') { + compilations.main.outputKinds 'EXECUTABLE' + compilations.main.entryPoint 'symbolication.main' + compilations.main.cinterops { + coreSymbolication { + } + } + } + } +} + +MPPTools.createRunTask(project, 'runProgram', kotlin.targets.symbolication) { + //args "./test.kexe.dSYM/Contents/Resources/DWARF/test.kexe" +} diff --git a/samples/symbolication/gradle.properties b/samples/symbolication/gradle.properties new file mode 100644 index 00000000000..790a07d5124 --- /dev/null +++ b/samples/symbolication/gradle.properties @@ -0,0 +1,2 @@ +kotlin.code.style=official +kotlin.import.noCommonSourceSets=true diff --git a/samples/symbolication/src/nativeInterop/cinterop/coreSymbolication.def b/samples/symbolication/src/nativeInterop/cinterop/coreSymbolication.def new file mode 100644 index 00000000000..fb8b2c70668 --- /dev/null +++ b/samples/symbolication/src/nativeInterop/cinterop/coreSymbolication.def @@ -0,0 +1,68 @@ +package = CoreSymbolication +linkerOpts.osx = -F /System/Library/PrivateFrameworks -framework CoreSymbolication +headerFilter = ** +language=Objective-C +--- +#include +#include + +typedef struct { + unsigned long type; + void* contents; +} CSTypeRef; + +typedef CSTypeRef CSSymbolicatorRef; +typedef CSTypeRef CSSymbolOwnerRef; +typedef CSTypeRef CSSymbolRef; +typedef CSTypeRef CSSourceInfoRef; + +typedef struct { + unsigned long long location; + unsigned long long length; +} CSRange; + +typedef unsigned long long CSArchitecture; + +#define kCSNow LONG_MAX + +cpu_type_t CSArchitectureGetCurrent(); + +CSSymbolicatorRef CSSymbolicatorCreateWithPid(pid_t pid); + +CSSymbolicatorRef CSSymbolicatorCreateWithPathAndArchitecture(const char* path, cpu_type_t type); + +CSArchitecture CSSymbolicatorGetArchitecture(CSSymbolicatorRef symbolicator); + +CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithNameAtTime( + CSSymbolicatorRef symbolicator, const char* name, long time); + +CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithAddressAtTime( + CSSymbolicatorRef symbolicator, unsigned long long address, long time); + +CSSymbolOwnerRef CSSymbolicatorGetSymbolOwner(CSSymbolicatorRef cs); + +CSSymbolRef CSSymbolicatorGetSymbolWithNameAtTime( + CSSymbolicatorRef symbolicator, const char* name, long time); + +const char* CSSymbolOwnerGetName(CSSymbolOwnerRef owner); + +unsigned long long CSSymbolOwnerGetBaseAddress(CSSymbolOwnerRef owner); + +CSSourceInfoRef CSSymbolOwnerGetSourceInfoWithAddress( + CSSymbolOwnerRef owner, unsigned long long address); + +const char* CSSymbolGetName(CSSymbolRef symbol); + +CSRange CSSymbolGetRange(CSSymbolRef symbol); + +const char* CSSourceInfoGetPath(CSSourceInfoRef info); + +const char* CSSourceInfoGetFilename(CSSourceInfoRef info); + +uint32_t CSSourceInfoGetLineNumber(CSSourceInfoRef info); + +CSTypeRef CSRetain(CSTypeRef); + +void CSRelease(CSTypeRef); + +bool CSIsNull(CSTypeRef); diff --git a/samples/symbolication/src/symbolicationMain/kotlin/main.kt b/samples/symbolication/src/symbolicationMain/kotlin/main.kt new file mode 100644 index 00000000000..98a4308d089 --- /dev/null +++ b/samples/symbolication/src/symbolicationMain/kotlin/main.kt @@ -0,0 +1,58 @@ +package symbolication + +import platform.darwin.* + +import kotlinx.cinterop.* +import CoreSymbolication.* + +fun main(args: Array) { + val program = if (args.size > 0 && args[0].isNotEmpty()) + args[0] else throw Error("please specify .dSYM path as the first argument") + val arch = if (args.size > 1 && args[1].isNotEmpty()) args[1] else "current" + val input = generateSequence { readLine() } + analyzeTrace(program, arch, input) +} + +fun archToCpuName(arch: String): cpu_type_t = when (arch) { + "x64", "x86_64" -> CPU_TYPE_X86_64 + "arm32" -> CPU_TYPE_ARM + "arm64" -> CPU_TYPE_ARM64 + "current" -> CSArchitectureGetCurrent() + else -> TODO("unsupported $arch") +} + +fun analyzeTrace(program: String, arch: String, input: Sequence) { + val symbolicator = CSSymbolicatorCreateWithPathAndArchitecture(program, archToCpuName(arch)) + if (CSIsNull(symbolicator)) throw Error("Cannot create \"$arch\" symbolicator for $program") + val owner = CSSymbolicatorGetSymbolOwner(symbolicator) + // Format is like + // at 14 test.kexe 0x0000000106a2e071 Konan_run_start + 113 + val matcher = "(at \\d+\\s+\\S+\\s+0x[0-9a-f]+\\s+)([^+]+)\\+\\s(\\d+)".toRegex() + for (line in input) { + val match = matcher.find(line) + var result = line + if (match != null) { + val atPart = match.groupValues[1] + val functionName = match.groupValues[2].trim() + val offsetInFunction = match.groupValues[3].toUInt() + val symbol = CSSymbolicatorGetSymbolWithNameAtTime(symbolicator, functionName, kCSNow) + if (!CSIsNull(symbol)) { + CSSymbolGetRange(symbol).useContents { + val address = this.location + offsetInFunction + val sourceInfo = CSSymbolOwnerGetSourceInfoWithAddress(owner, address) + if (!CSIsNull(sourceInfo)) { + val filePath = CSSourceInfoGetPath(sourceInfo)?.toKString() + // or val fileName = CSSourceInfoGetFilename(sourceInfo)?.toKString() + val lineNumber = CSSourceInfoGetLineNumber(sourceInfo) + val lineNumberString = if (lineNumber != 0u) lineNumber.toString() else "" + if (filePath != null) + result = matcher.replaceFirst(line, + "$atPart$functionName $filePath:$lineNumberString") + } + } + } + } + println(result) + } + CSRelease(symbolicator) +} diff --git a/samples/symbolication/test.kt b/samples/symbolication/test.kt new file mode 100644 index 00000000000..826d5303408 --- /dev/null +++ b/samples/symbolication/test.kt @@ -0,0 +1,22 @@ +fun f(p: Int) { + if (p == 0) throw Error() + f(p - 1) +} + +fun g1(p: Int) { + if (p == 0) throw Error() + g2(p - 1) +} + +fun g2(p: Int) { + g1(p - 1) +} + +fun main() { + try { + f(10) + } catch (e: Throwable) { + e.printStackTrace() + } + g1(10) +}