diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 2b39c317e65..3b8a1b0baf5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -496,7 +496,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { fun shouldProfilePhases() = config.configuration.getBoolean(KonanConfigKeys.TIME_PHASES) - fun shouldContainDebugInfo() = config.configuration.getBoolean(KonanConfigKeys.DEBUG) + fun shouldContainDebugInfo() = config.debug fun shouldOptimize() = config.configuration.getBoolean(KonanConfigKeys.OPTIMIZATION) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 372bb64ca10..94b1161b5cc 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -41,6 +41,8 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration val infoArgsOnly = configuration.kotlinSourceRoots.isEmpty() && !linkOnly + val debug: Boolean get() = configuration.getBoolean(KonanConfigKeys.DEBUG) + init { if (!platformManager.isEnabled(target)) { error("Target ${target.visibleName} is not available on the ${HostManager.hostName} host") @@ -106,10 +108,14 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration return resolvedLibraries.filterRoots { (!it.isDefault && !this.purgeUserLibs) || it.isNeededForLink }.getFullList() } - internal val defaultNativeLibraries = - if (produce == CompilerOutputKind.PROGRAM) - File(distribution.defaultNatives(target)).listFiles.map { it.absolutePath } - else emptyList() + internal val defaultNativeLibraries: List = mutableListOf().apply { + add(if (debug) "debug.bc" else "release.bc") + if (produce == CompilerOutputKind.PROGRAM) { + addAll(distribution.launcherFiles) + } + }.map { + File(distribution.defaultNatives(target)).child(it).absolutePath + } internal val nativeLibraries: List = configuration.getList(KonanConfigKeys.NATIVE_LIBRARY_FILES) @@ -117,6 +123,16 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration internal val includeBinaries: List = configuration.getList(KonanConfigKeys.INCLUDED_BINARY_FILES) + internal val defaultSystemLibraries: List = when (target) { + KonanTarget.MACOS_X64, KonanTarget.IOS_X64 -> if (debug) { + listOf(File(distribution.tbdDirectory(target)).child("CoreSymbolication.tbd").absolutePath) + } else { + emptyList() + } + + else -> emptyList() + } + internal val languageVersionSettings = configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)!! diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index 70ee8d57a87..6840fdf24ad 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -23,7 +23,7 @@ internal class LinkStage(val context: Context, val phaser: PhaseManager) { private val linker = platform.linker private val optimize = context.shouldOptimize() - private val debug = config.get(KonanConfigKeys.DEBUG) ?: false + private val debug = context.config.debug private val linkerOutput = when (context.config.produce) { CompilerOutputKind.DYNAMIC, CompilerOutputKind.FRAMEWORK -> LinkerOutputKind.DYNAMIC_LIBRARY CompilerOutputKind.STATIC -> LinkerOutputKind.STATIC_LIBRARY @@ -181,7 +181,8 @@ internal class LinkStage(val context: Context, val phaser: PhaseManager) { try { File(executable).delete() linker.linkCommands(objectFiles = objectFiles, executable = executable, - libraries = linker.targetLibffi + linker.linkStaticLibraries(includedBinaries), + libraries = linker.targetLibffi + linker.linkStaticLibraries(includedBinaries) + + context.config.defaultSystemLibraries, linkerArgs = entryPointSelector + asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) + BitcodeEmbedding.getLinkerOptions(context.config) + diff --git a/konan/targets/ios_x64/tbd/CoreSymbolication.tbd b/konan/targets/ios_x64/tbd/CoreSymbolication.tbd new file mode 100644 index 00000000000..3bfe53b40a7 --- /dev/null +++ b/konan/targets/ios_x64/tbd/CoreSymbolication.tbd @@ -0,0 +1,16 @@ +--- !tapi-tbd-v3 +archs: [ x86_64 ] +uuids: [ 'x86_64: BA456C79-974B-499E-9013-BEDB414FE7B9' ] +platform: ios +install-name: /System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication +exports: + - archs: [ x86_64 ] + symbols: [ _CSIsNull, + _CSSourceInfoGetFilename, + _CSSourceInfoGetPath, + _CSSourceInfoGetLineNumber, + _CSSourceInfoGetColumn, + _CSSymbolOwnerGetSourceInfoWithAddress, + _CSSymbolicatorCreateWithPid, + _CSSymbolicatorGetSymbolOwnerWithAddressAtTime ] +... diff --git a/konan/targets/macos_x64/tbd/CoreSymbolication.tbd b/konan/targets/macos_x64/tbd/CoreSymbolication.tbd new file mode 100644 index 00000000000..ba1c6c9820d --- /dev/null +++ b/konan/targets/macos_x64/tbd/CoreSymbolication.tbd @@ -0,0 +1,16 @@ +--- !tapi-tbd-v3 +archs: [ x86_64 ] +uuids: [ 'x86_64: E996B8F6-929B-4111-A859-EB054C062FE8' ] +platform: macosx +install-name: /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication +exports: + - archs: [ x86_64 ] + symbols: [ _CSIsNull, + _CSSourceInfoGetFilename, + _CSSourceInfoGetPath, + _CSSourceInfoGetLineNumber, + _CSSourceInfoGetColumn, + _CSSymbolOwnerGetSourceInfoWithAddress, + _CSSymbolicatorCreateWithPid, + _CSSymbolicatorGetSymbolOwnerWithAddressAtTime ] +... diff --git a/runtime/build.gradle b/runtime/build.gradle index e6196aabf60..e93af07cce9 100644 --- a/runtime/build.gradle +++ b/runtime/build.gradle @@ -6,30 +6,47 @@ import org.jetbrains.kotlin.CompileCppToBitcode // TODO: consider using some Gradle plugins to build and test +void includeRuntime(CompileCppToBitcode task) { + task.compilerArgs '-I' + project.file('../common/src/hash/headers') + task.compilerArgs '-I' + project.file('src/main/cpp') +} + targetList.each { targetName -> task("${targetName}Runtime", type: CompileCppToBitcode) { name "runtime" srcRoot file('src/main') dependsOn ":common:${targetName}Hash" dependsOn "${targetName}Launcher" + dependsOn "${targetName}Debug" + dependsOn "${targetName}Release" target targetName if (!isWindows()) compilerArgs '-fPIC' - compilerArgs '-I' + project.file('../common/src/hash/headers') - compilerArgs '-I' + file('src/main/cpp') + includeRuntime(delegate) if (rootProject.hasProperty("${targetName}LibffiDir")) compilerArgs '-I' + project.file(rootProject.ext.get("${targetName}LibffiDir") + "/include") linkerArgs project.file("../common/build/$targetName/hash.bc").path } -} -targetList.each { targetName -> task("${targetName}Launcher", type: CompileCppToBitcode) { name "launcher" srcRoot file('src/launcher') target targetName - compilerArgs '-I' + project.file('../common/src/hash/headers') - compilerArgs '-I' + project.file('src/main/cpp') + includeRuntime(delegate) + } + + task ("${targetName}Debug", type: CompileCppToBitcode) { + name "debug" + srcRoot file('src/debug') + target targetName + includeRuntime(delegate) + } + + task ("${targetName}Release", type: CompileCppToBitcode) { + name "release" + srcRoot file('src/release') + target targetName + includeRuntime(delegate) } } diff --git a/runtime/src/debug/cpp/SourceInfo.cpp b/runtime/src/debug/cpp/SourceInfo.cpp new file mode 100644 index 00000000000..fafa8c4d3d4 --- /dev/null +++ b/runtime/src/debug/cpp/SourceInfo.cpp @@ -0,0 +1,103 @@ +/* + * 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" + +#ifdef KONAN_CORE_SYMBOLICATION + +#include +#include +#include + +typedef struct _CSTypeRef { + unsigned long type; + void* contents; +} CSTypeRef; + +typedef CSTypeRef CSSymbolicatorRef; +typedef CSTypeRef CSSymbolOwnerRef; +typedef CSTypeRef CSSymbolRef; +typedef CSTypeRef CSSourceInfoRef; + +typedef struct _CSRange { + unsigned long long location; + unsigned long long length; +} CSRange; + +typedef unsigned long long CSArchitecture; + +#define kCSNow LONG_MAX + +extern "C" { + +CSSymbolicatorRef CSSymbolicatorCreateWithPid(pid_t pid); + +CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithAddressAtTime( + CSSymbolicatorRef symbolicator, + unsigned long long address, + long time +); + +CSSourceInfoRef CSSymbolOwnerGetSourceInfoWithAddress( + CSSymbolOwnerRef owner, + unsigned long long address +); + + +const char* CSSourceInfoGetPath(CSSourceInfoRef info); + +uint32_t CSSourceInfoGetLineNumber(CSSourceInfoRef info); + +uint32_t CSSourceInfoGetColumn(CSSourceInfoRef info); + + +bool CSIsNull(CSTypeRef); + +} // extern "C" + +extern "C" struct SourceInfo Kotlin_getSourceInfo(void* addr) { + SourceInfo result = { .fileName = nullptr, .lineNumber = -1, .column = -1 }; + + static CSSymbolicatorRef symbolicator = CSSymbolicatorCreateWithPid(getpid()); + + if (!CSIsNull(symbolicator)) { + unsigned long long address = static_cast((uintptr_t)addr); + + CSSymbolOwnerRef symbolOwner = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, address, kCSNow); + CSSourceInfoRef sourceInfo = CSSymbolOwnerGetSourceInfoWithAddress(symbolOwner, address); + if (!CSIsNull(sourceInfo)) { + const char* fileName = CSSourceInfoGetPath(sourceInfo); + if (fileName != nullptr) { + result.fileName = fileName; + uint32_t lineNumber = CSSourceInfoGetLineNumber(sourceInfo); + if (lineNumber != 0) { + result.lineNumber = lineNumber; + result.column = CSSourceInfoGetColumn(sourceInfo); + } + } + } + } + + return result; +} + +#else // KONAN_CORE_SYMBOLICATION + +extern "C" struct SourceInfo Kotlin_getSourceInfo(void* addr) { + return (SourceInfo) { .fileName = nullptr, .lineNumber = -1, .column = -1 }; +} + +#endif // KONAN_CORE_SYMBOLICATION \ No newline at end of file diff --git a/runtime/src/main/cpp/Exceptions.cpp b/runtime/src/main/cpp/Exceptions.cpp index 746a6205eab..14508b41e41 100644 --- a/runtime/src/main/cpp/Exceptions.cpp +++ b/runtime/src/main/cpp/Exceptions.cpp @@ -39,6 +39,7 @@ #include "Memory.h" #include "Natives.h" #include "KString.h" +#include "SourceInfo.h" #include "Types.h" #include "Utils.h" @@ -171,7 +172,22 @@ OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace) { RuntimeCheck(symbols != nullptr, "Not enough memory to retrieve the stacktrace"); AutoFree autoFree(symbols); for (int index = 0; index < size; ++index) { - CreateStringFromCString(symbols[index], ArrayAddressOfElementAt(strings->array(), index)); + auto sourceInfo = Kotlin_getSourceInfo(*PrimitiveArrayAddressOfElementAt(stackTrace->array(), index)); + const char* symbol = symbols[index]; + const char* result; + char line[1024]; + if (sourceInfo.fileName != nullptr) { + if (sourceInfo.lineNumber != -1) { + konan::snprintf(line, sizeof(line) - 1, "%s (%s:%d:%d)", + symbol, sourceInfo.fileName, sourceInfo.lineNumber, sourceInfo.column); + } else { + konan::snprintf(line, sizeof(line) - 1, "%s (%s:)", symbol, sourceInfo.fileName); + } + result = line; + } else { + result = symbol; + } + CreateStringFromCString(result, ArrayAddressOfElementAt(strings->array(), index)); } } #endif diff --git a/runtime/src/main/cpp/SourceInfo.h b/runtime/src/main/cpp/SourceInfo.h new file mode 100644 index 00000000000..1a72a831a0d --- /dev/null +++ b/runtime/src/main/cpp/SourceInfo.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +struct SourceInfo { + const char* fileName; + int lineNumber; + int column; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +struct SourceInfo Kotlin_getSourceInfo(void* addr); + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/runtime/src/release/cpp/SourceInfo.cpp b/runtime/src/release/cpp/SourceInfo.cpp new file mode 100644 index 00000000000..be607b4a762 --- /dev/null +++ b/runtime/src/release/cpp/SourceInfo.cpp @@ -0,0 +1,21 @@ +/* + * 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" + +extern "C" struct SourceInfo Kotlin_getSourceInfo(void* addr) { + return (SourceInfo) { .fileName = nullptr, .lineNumber = -1, .column = -1 }; +} \ No newline at end of file diff --git a/samples/symbolication/src/symbolicationMain/kotlin/main.kt b/samples/symbolication/src/symbolicationMain/kotlin/main.kt index 8e1388b7340..9414f5d4339 100644 --- a/samples/symbolication/src/symbolicationMain/kotlin/main.kt +++ b/samples/symbolication/src/symbolicationMain/kotlin/main.kt @@ -44,7 +44,7 @@ fun sourceInfoString(owner: CValue, address: ULong): String? { if (sourceInfo == null) return null val filePath = CSSourceInfoGetPath(sourceInfo)?.toKString() ?: return null val columnNumber = CSSourceInfoGetColumn(sourceInfo) - return "$filePath:${if (maybe) "~" else ""}$lineNumber:$columnNumber" + return "$filePath:${if (lineNumber == 0u) "" else "${if (maybe) "~" else ""}$lineNumber:$columnNumber"}" } fun analyzeTrace(program: String, arch: String, input: Sequence) { @@ -67,7 +67,7 @@ fun analyzeTrace(program: String, arch: String, input: Sequence) { val address = this.location + offsetInFunction val sourceInfo = sourceInfoString(owner, address) if (sourceInfo != null) { - result = matcher.replaceFirst(line, "$atPart$functionName $sourceInfo") + result = matcher.replaceFirst(line, "$atPart$functionName + $offsetInFunction ($sourceInfo)") } } } diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt index d1c1ac57927..747a1ea195b 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt @@ -150,6 +150,7 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con KonanTarget.MACOS_X64 -> listOf("-DKONAN_OSX=1", "-DKONAN_OBJC_INTEROP=1", + "-DKONAN_CORE_SYMBOLICATION=1", "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64 -> @@ -158,6 +159,7 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con KonanTarget.IOS_X64 -> listOf("-DKONAN_OBJC_INTEROP=1", + "-DKONAN_CORE_SYMBOLICATION=1", "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") KonanTarget.ANDROID_ARM32 -> diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Distribution.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Distribution.kt index 241f14a9e98..82dabc936ea 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Distribution.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Distribution.kt @@ -75,9 +75,12 @@ class Distribution( val stdlib = "$klib/common/stdlib" fun defaultNatives(target: KonanTarget) = "$konanHome/konan/targets/${target.visibleName}/native" + fun tbdDirectory(target: KonanTarget) = "$konanHome/konan/targets/${target.visibleName}/tbd" fun runtime(target: KonanTarget) = runtimeFileOverride ?: "$stdlib/targets/${target.visibleName}/native/runtime.bc" + val launcherFiles = listOf("start.bc", "launcher.bc") + val dependenciesDir = DependencyProcessor.defaultDependenciesRoot.absolutePath fun availableSubTarget(genericName: String) =