[Coverage]Enable code coverage for linux_x64 and mingw_x64

This commit is contained in:
Sergey Bogolepov
2019-09-30 14:36:53 +03:00
committed by Sergey Bogolepov
parent 4782c2adeb
commit 8622eb3be4
7 changed files with 78 additions and 31 deletions
+1 -2
View File
@@ -3,10 +3,9 @@ Kotlin/Native has a code coverage support that is based on Clang's
[Source-based Code Coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html).
**Please note**:
1. Coverage support is in it's very early days and is in active development. Known issues and restrictions are:
1. Coverage support is in it's very early days and is in active development. Known issues are:
* Coverage information may be inaccurate.
* Line execution counts may be wrong.
* Only macOS and iOS simulator binaries are supported.
2. Most of described functionality will be incorporated into Gradle plugin.
@@ -79,6 +79,8 @@ internal class Linker(val context: Context) {
executable = dylibPath.absolutePath
}
val needsProfileLibrary = context.coverage.enabled
try {
File(executable).delete()
linker.linkCommands(objectFiles = objectFiles, executable = executable,
@@ -87,7 +89,8 @@ internal class Linker(val context: Context) {
BitcodeEmbedding.getLinkerOptions(context.config) +
libraryProvidedLinkerFlags + frameworkLinkerArgs,
optimize = optimize, debug = debug, kind = linkerOutput,
outputDsymBundle = context.config.outputFile + ".dSYM").forEach {
outputDsymBundle = context.config.outputFile + ".dSYM",
needsProfileLibrary = needsProfileLibrary).forEach {
it.logWith(context::log)
it.execute()
}
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.supportsCodeCoverage
import org.jetbrains.kotlin.resolve.descriptorUtil.module
/**
@@ -43,14 +43,14 @@ internal class CoverageManager(val context: Context) {
init {
if (enabled && !checkRestrictions()) {
context.reportCompilationError("Coverage is only supported for macOS and iOS simulator binaries for now.")
context.reportCompilationError("Coverage is not supported for ${context.config.target}.")
}
}
private fun checkRestrictions(): Boolean {
val isKindAllowed = context.config.produce.involvesBitcodeGeneration
val target = context.config.target
val isTargetAllowed = target == KonanTarget.MACOS_X64 || target == KonanTarget.IOS_X64
val isTargetAllowed = target.supportsCodeCoverage()
return isKindAllowed && isTargetAllowed
}
+2 -2
View File
@@ -3756,7 +3756,7 @@ KotlinNativeTestKt.createTest(project, 'harmonyRegexTest', KonanGTest) { task ->
standaloneTest("coverage_basic_program") {
disabled = PlatformInfo.getTarget(project).name != "ios_x64" && PlatformInfo.getTarget(project).name != "macos_x64"
disabled = !UtilsKt.testTargetSupportsCodeCoverage(project)
description = "Test that `-Xcoverage` generates correct __llvm_coverage information"
@@ -3783,7 +3783,7 @@ standaloneTest("coverage_basic_program") {
standaloneTest("coverage_basic_library") {
disabled = PlatformInfo.getTarget(project).name != "ios_x64" && PlatformInfo.getTarget(project).name != "macos_x64"
disabled = !UtilsKt.testTargetSupportsCodeCoverage(project)
description = "Test that `-Xlibrary-to-cover` generates correct __llvm_coverage information"
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin
import org.gradle.api.Project
import org.gradle.api.Task
import org.jetbrains.kotlin.konan.target.AppleConfigurables
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.PlatformManager
import org.jetbrains.kotlin.konan.target.*
import java.io.FileInputStream
import java.io.IOException
import java.io.File
@@ -54,6 +51,9 @@ val Project.globalTestArgs: List<String>
fun Project.platformManager() = findProperty("platformManager") as PlatformManager
fun Project.testTarget() = findProperty("target") as KonanTarget
fun Project.testTargetSupportsCodeCoverage(): Boolean =
this.testTarget.supportsCodeCoverage()
/**
* Ad-hoc signing of the specified path.
*/
@@ -0,0 +1,7 @@
package org.jetbrains.kotlin.konan.target
fun KonanTarget.supportsCodeCoverage(): Boolean =
this == KonanTarget.MINGW_X64 ||
this == KonanTarget.LINUX_X64 ||
this == KonanTarget.MACOS_X64 ||
this == KonanTarget.IOS_X64
@@ -75,10 +75,12 @@ abstract class LinkerFlags(val configurables: Configurables)
open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor.
// TODO: Number of arguments is quite big. Better to pass args via object.
abstract fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
kind: LinkerOutputKind, outputDsymBundle: String): List<Command>
kind: LinkerOutputKind, outputDsymBundle: String,
needsProfileLibrary: Boolean): List<Command>
abstract fun filterStaticLibraries(binaries: List<String>): List<String>
@@ -87,6 +89,16 @@ abstract class LinkerFlags(val configurables: Configurables)
// Let's just pass them as absolute paths.
return libraries
}
protected open fun provideCompilerRtLibrary(libraryName: String): String? {
System.err.println("Can't provide $libraryName.")
return null
}
// Code coverage requires this library.
protected val profileLibrary: String? by lazy {
provideCompilerRtLibrary("profile")
}
}
open class AndroidLinker(targetProperties: AndroidConfigurables)
@@ -107,7 +119,8 @@ open class AndroidLinker(targetProperties: AndroidConfigurables)
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
kind: LinkerOutputKind, outputDsymBundle: String,
needsProfileLibrary: Boolean): List<Command> {
if (kind == LinkerOutputKind.STATIC_LIBRARY)
return staticGnuArCommands(ar, executable, objectFiles, libraries)
@@ -153,7 +166,7 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
get() = this == KonanTarget.TVOS_X64 || this == KonanTarget.IOS_X64 ||
this == KonanTarget.WATCHOS_X86 || this == KonanTarget.WATCHOS_X64
private fun provideCompilerRtLibrary(libraryName: String): String? {
override fun provideCompilerRtLibrary(libraryName: String): String? {
val prefix = when (target.family) {
Family.IOS -> "ios"
Family.WATCHOS -> "watchos"
@@ -173,15 +186,6 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
return if (dir != null) "$dir/lib/darwin/libclang_rt.$mangledLibraryName$prefix$suffix.a" else null
}
private val compilerRtLibrary: String? by lazy {
provideCompilerRtLibrary("")
}
// Code coverage requires this library.
private val profileLibrary: String? by lazy {
provideCompilerRtLibrary("profile")
}
private val osVersionMinFlags: List<String> by lazy {
listOf(osVersionMinFlagLd, osVersionMin + ".0")
}
@@ -191,7 +195,8 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean, kind: LinkerOutputKind,
outputDsymBundle: String): List<Command> {
outputDsymBundle: String,
needsProfileLibrary: Boolean): List<Command> {
if (kind == LinkerOutputKind.STATIC_LIBRARY)
return listOf(Command(libtool).apply {
+"-static"
@@ -214,7 +219,7 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
if (dynamic) +linkerDynamicFlags
+linkerKonanFlags
if (compilerRtLibrary != null) +compilerRtLibrary!!
if (profileLibrary != null) +profileLibrary!!
if (needsProfileLibrary) +profileLibrary!!
+libraries
+linkerArgs
+rpath(dynamic)
@@ -231,6 +236,10 @@ open class MacOSBasedLinker(targetProperties: AppleConfigurables)
return result
}
private val compilerRtLibrary: String? by lazy {
provideCompilerRtLibrary("")
}
private fun rpath(dynamic: Boolean): List<String> = listOfNotNull(
when (target.family) {
Family.OSX -> "@executable_path/../Frameworks"
@@ -295,12 +304,22 @@ open class LinuxBasedLinker(targetProperties: LinuxBasedConfigurables)
private val linker = "$absoluteLlvmHome/bin/ld.lld"
private val specificLibs = abiSpecificLibraries.map { "-L${absoluteTargetSysRoot}/$it" }
override fun provideCompilerRtLibrary(libraryName: String): String? {
val targetSuffix = when (target) {
KonanTarget.LINUX_X64 -> "x86_64"
else -> error("$target is not supported.")
}
val dir = File("$absoluteLlvmHome/lib/clang/").listFiles.firstOrNull()?.absolutePath
return if (dir != null) "$dir/lib/linux/libclang_rt.$libraryName-$targetSuffix.a" else null
}
override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isUnixStaticLib }
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
kind: LinkerOutputKind, outputDsymBundle: String,
needsProfileLibrary: Boolean): List<Command> {
if (kind == LinkerOutputKind.STATIC_LIBRARY)
return staticGnuArCommands(ar, executable, objectFiles, libraries)
@@ -331,6 +350,9 @@ open class LinuxBasedLinker(targetProperties: LinuxBasedConfigurables)
if (!debug) +linkerNoDebugFlags
if (dynamic) +linkerDynamicFlags
+objectFiles
// See explanation about `-u__llvm_profile_runtime` here:
// https://github.com/llvm/llvm-project/blob/21e270a479a24738d641e641115bce6af6ed360a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp#L930
if (needsProfileLibrary) +listOf("-u__llvm_profile_runtime", profileLibrary!!)
+linkerKonanFlags
+listOf("-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed",
"-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed")
@@ -352,10 +374,20 @@ open class MingwLinker(targetProperties: MingwConfigurables)
override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isWindowsStaticLib || it.isUnixStaticLib }
override fun provideCompilerRtLibrary(libraryName: String): String? {
val targetSuffix = when (target) {
KonanTarget.MINGW_X64 -> "x86_64"
else -> error("$target is not supported.")
}
val dir = File("$absoluteLlvmHome/lib/clang/").listFiles.firstOrNull()?.absolutePath
return if (dir != null) "$dir/lib/windows/libclang_rt.$libraryName-$targetSuffix.a" else null
}
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
kind: LinkerOutputKind, outputDsymBundle: String,
needsProfileLibrary: Boolean): List<Command> {
if (kind == LinkerOutputKind.STATIC_LIBRARY)
return staticGnuArCommands(ar, executable, objectFiles, libraries)
@@ -366,10 +398,14 @@ open class MingwLinker(targetProperties: MingwConfigurables)
}.apply {
+listOf("-o", executable)
+objectFiles
if (optimize) +linkerOptimizationFlags
// --gc-sections flag may affect profiling.
// See https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#drawbacks-and-limitations.
// TODO: switching to lld may help.
if (optimize && !needsProfileLibrary) +linkerOptimizationFlags
if (!debug) +linkerNoDebugFlags
if (dynamic) +linkerDynamicFlags
+libraries
if (needsProfileLibrary) +profileLibrary!!
+linkerArgs
+linkerKonanFlags
})
@@ -386,7 +422,8 @@ open class WasmLinker(targetProperties: WasmConfigurables)
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
kind: LinkerOutputKind, outputDsymBundle: String,
needsProfileLibrary: Boolean): List<Command> {
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind")
// TODO(horsh): maybe rethink it.
@@ -433,7 +470,8 @@ open class ZephyrLinker(targetProperties: ZephyrConfigurables)
override fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
libraries: List<String>, linkerArgs: List<String>,
optimize: Boolean, debug: Boolean,
kind: LinkerOutputKind, outputDsymBundle: String): List<Command> {
kind: LinkerOutputKind, outputDsymBundle: String,
needsProfileLibrary: Boolean): List<Command> {
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind: $kind")
return listOf(Command(linker).apply {
+listOf("-r", "--gc-sections", "--entry", "main")