diff --git a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/NativeTest.kt b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/NativeTest.kt index f7ec03824bd..61d6b2f3022 100644 --- a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/NativeTest.kt +++ b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/NativeTest.kt @@ -11,23 +11,148 @@ import javax.inject.Inject import org.gradle.api.* import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.tasks.* +import org.gradle.api.model.ObjectFactory import org.gradle.kotlin.dsl.getByType +import org.gradle.process.ExecOperations +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.ExecClang import org.jetbrains.kotlin.bitcode.CompileToBitcode import org.jetbrains.kotlin.bitcode.CompileToBitcodeExtension import org.jetbrains.kotlin.konan.target.* import java.io.OutputStream -open class CompileNativeTest @Inject constructor( - @InputFile val inputFile: File, - @Input val target: KonanTarget +interface CompileNativeTestParameters : WorkParameters { + var mainFile: File + var inputFiles: List + var llvmLinkOutputFile: File + var compilerOutputFile: File + var targetName: String + var compilerArgs: List + var linkCommands: List> + + var konanHome: File + var llvmDir: File + var experimentalDistribution: Boolean + var isInfoEnabled: Boolean +} + +abstract class CompileNativeTestJob : WorkAction { + @get:Inject + abstract val execOperations: ExecOperations + + @get:Inject + abstract val objects: ObjectFactory + + private fun llvmLink() { + with(parameters) { + val tmpOutput = File.createTempFile("runtimeTests", ".bc").apply { + deleteOnExit() + } + + // The runtime provides our implementations for some standard functions (see StdCppStubs.cpp). + // We need to internalize these symbols to avoid clashes with symbols provided by the C++ stdlib. + // But llvm-link -internalize is kinda broken: it links modules one by one and can't see usages + // of a symbol in subsequent modules. So it will mangle such symbols causing "unresolved symbol" + // errors at the link stage. So we have to run llvm-link twice: the first one links all modules + // except the one containing the entry point to a single *.bc without internalization. The second + // run internalizes this big module and links it with a module containing the entry point. + execOperations.exec { + executable = "$llvmDir/bin/llvm-link" + args = listOf("-o", tmpOutput.absolutePath) + inputFiles.map { it.absolutePath } + } + + execOperations.exec { + executable = "$llvmDir/bin/llvm-link" + args = listOf( + "-o", llvmLinkOutputFile.absolutePath, + mainFile.absolutePath, + tmpOutput.absolutePath, + "-internalize" + ) + } + } + } + + private fun compile() { + with(parameters) { + val platformManager = PlatformManager(buildDistribution(konanHome.absolutePath), experimentalDistribution) + val execClang = ExecClang.create(objects, platformManager, llvmDir) + val target = platformManager.targetByName(targetName) + + if (target.family.isAppleFamily) { + execClang.execToolchainClang(target) { + executable = "clang++" + this.args = compilerArgs + listOf(llvmLinkOutputFile.absolutePath, "-o", compilerOutputFile.absolutePath) + } + } else { + execClang.execBareClang { + executable = "clang++" + this.args = compilerArgs + listOf(llvmLinkOutputFile.absolutePath, "-o", compilerOutputFile.absolutePath) + } + } + } + } + + private fun link() { + with(parameters) { + for (command in linkCommands) { + execOperations.exec { + commandLine(command) + if (!isInfoEnabled && command[0].endsWith("dsymutil")) { + // Suppress dsymutl's warnings. + // See: https://bugs.swift.org/browse/SR-11539. + val nullOutputStream = object: OutputStream() { + override fun write(b: Int) {} + } + errorOutput = nullOutputStream + } + } + } + } + } + + override fun execute() { + llvmLink() + compile() + link() + } +} + +abstract class CompileNativeTest @Inject constructor( + baseName: String, + @Input val target: KonanTarget, + @InputFile val mainFile: File, + private val platformManager: PlatformManager, + private val mimallocEnabled: Boolean, ) : DefaultTask() { + + @SkipWhenEmpty + @InputFiles + val inputFiles: ConfigurableFileCollection = project.files() + @OutputFile - var outputFile = project.buildDir.resolve("bin/test/${target.name}/${inputFile.nameWithoutExtension}.o") + var llvmLinkOutputFile: File = project.buildDir.resolve("bitcode/test/${target.name}/$baseName.bc") + + @OutputFile + var compilerOutputFile: File = project.buildDir.resolve("bin/test/${target.name}/$baseName.o") + + private val executableExtension: String = when (target) { + is KonanTarget.MINGW_X64 -> ".exe" + is KonanTarget.MINGW_X86 -> ".exe" + else -> "" + } + + @OutputFile + var outputFile: File = project.buildDir.resolve("bin/test/${target.name}/$baseName$executableExtension") @Input val clangArgs = mutableListOf() + @Input + val linkerArgs = mutableListOf() + @Input @Optional var sanitizer: SanitizerKind? = null @@ -37,126 +162,14 @@ open class CompileNativeTest @Inject constructor( SanitizerKind.THREAD -> listOf("-fsanitize=thread") } - @TaskAction - fun compile() { - val plugin = project.extensions.getByType() - val args = clangArgs + sanitizerFlags + listOf(inputFile.absolutePath, "-o", outputFile.absolutePath) - if (target.family.isAppleFamily) { - plugin.execToolchainClang(target) { - executable = "clang++" - this.args = args - } - } else { - plugin.execBareClang { - executable = "clang++" - this.args = args - } - } - } -} - -open class LlvmLinkNativeTest @Inject constructor( - baseName: String, - @Input val target: String, - @InputFile val mainFile: File -) : DefaultTask() { - - @SkipWhenEmpty - @InputFiles - val inputFiles: ConfigurableFileCollection = project.files() - - @OutputFile - var outputFile: File = project.buildDir.resolve("bitcode/test/$target/$baseName.bc") - - @TaskAction - fun llvmLink() { - val llvmDir = project.property("llvmDir") - val tmpOutput = File.createTempFile("runtimeTests", ".bc").apply { - deleteOnExit() - } - - // The runtime provides our implementations for some standard functions (see StdCppStubs.cpp). - // We need to internalize these symbols to avoid clashes with symbols provided by the C++ stdlib. - // But llvm-link -internalize is kinda broken: it links modules one by one and can't see usages - // of a symbol in subsequent modules. So it will mangle such symbols causing "unresolved symbol" - // errors at the link stage. So we have to run llvm-link twice: the first one links all modules - // except the one containing the entry point to a single *.bc without internalization. The second - // run internalizes this big module and links it with a module containing the entry point. - project.exec { - executable = "$llvmDir/bin/llvm-link" - args = listOf("-o", tmpOutput.absolutePath) + inputFiles.map { it.absolutePath } - } - - project.exec { - executable = "$llvmDir/bin/llvm-link" - args = listOf( - "-o", outputFile.absolutePath, - mainFile.absolutePath, - tmpOutput.absolutePath, - "-internalize" - ) - } - } -} - -open class LinkNativeTest @Inject constructor( - @InputFiles val inputFiles: List, - @OutputFile val outputFile: File, - @Internal val target: String, - @Internal val linkerArgs: List, - private val platformManager: PlatformManager, - private val mimallocEnabled: Boolean -) : DefaultTask () { - companion object { - fun create( - project: Project, - platformManager: PlatformManager, - taskName: String, - inputFiles: List, - target: String, - outputFile: File, - linkerArgs: List, - mimallocEnabled: Boolean - ): LinkNativeTest = project.tasks.create( - taskName, - LinkNativeTest::class.java, - inputFiles, - outputFile, - target, - linkerArgs, - platformManager, - mimallocEnabled) - - fun create( - project: Project, - platformManager: PlatformManager, - taskName: String, - inputFiles: List, - target: String, - executableName: String, - mimallocEnabled: Boolean, - linkerArgs: List = listOf() - ): LinkNativeTest = create( - project, - platformManager, - taskName, - inputFiles, - target, - project.buildDir.resolve("bin/test/$target/$executableName"), - linkerArgs, mimallocEnabled) - } - - @Input @Optional - var sanitizer: SanitizerKind? = null - @get:Input - val commands: List> + val linkCommands: List> get() { // Getting link commands requires presence of a target toolchain. // Thus we cannot get them at the configuration stage because the toolchain may be not downloaded yet. - val linker = platformManager.platform(platformManager.targetByName(target)).linker + val linker = platformManager.platform(target).linker return linker.finalLinkCommands( - inputFiles.map { it.absolutePath }, + listOf(compilerOutputFile.absolutePath), outputFile.absolutePath, listOf(), linkerArgs, @@ -170,36 +183,79 @@ open class LinkNativeTest @Inject constructor( ).map { it.argsWithExecutable } } + @get:Inject + abstract val workerExecutor: WorkerExecutor + @TaskAction - fun link() { - for (command in commands) { - project.exec { - commandLine(command) - if (!logger.isInfoEnabled() && command[0].endsWith("dsymutil")) { - // Suppress dsymutl's warnings. - // See: https://bugs.swift.org/browse/SR-11539. - val nullOutputStream = object: OutputStream() { - override fun write(b: Int) {} - } - errorOutput = nullOutputStream - } - } + fun compile() { + val workQueue = workerExecutor.noIsolation() + + val parameters = { it: CompileNativeTestParameters -> + it.mainFile = mainFile + it.inputFiles = inputFiles.map { it } + it.llvmLinkOutputFile = llvmLinkOutputFile + it.compilerOutputFile = compilerOutputFile + it.targetName = target.name + it.compilerArgs = clangArgs + sanitizerFlags + it.linkCommands = linkCommands + + it.konanHome = project.project(":kotlin-native").projectDir + it.llvmDir = project.file(project.findProperty("llvmDir")!!) + it.isInfoEnabled = logger.isInfoEnabled() } + + workQueue.submit(CompileNativeTestJob::class.java, parameters) } } -open class ReportWithPrefixes @Inject constructor( - @Input val testName: String, - @InputFile val inputReport: File, - @OutputFile val outputReport: File +open class RunNativeTest @Inject constructor( + @Input val testName: String, + @InputFile val inputFile: File, ) : DefaultTask() { + + @Internal + var workingDir: File = project.buildDir.resolve("testReports/$testName") + + @OutputFile + var outputFile: File = workingDir.resolve("report.xml") + + @OutputFile + var outputFileWithPrefixes: File = workingDir.resolve("report-with-prefixes.xml") + + @Input @Optional + var filter: String? = project.findProperty("gtest_filter") as? String + + @Input @Optional + var sanitizer: SanitizerKind? = null + + @InputFile + var tsanSuppressionsFile = project.file("tsan_suppressions.txt") + @TaskAction - fun process() { + fun run() { + workingDir.mkdirs() + + // Do not run this in workers, because we don't want this task to run in parallel. + project.exec { + executable = inputFile.absolutePath + + if (filter != null) { + args("--gtest_filter=${filter}") + } + args("--gtest_output=xml:${outputFile.absolutePath}") + when (sanitizer) { + SanitizerKind.THREAD -> { + environment("TSAN_OPTIONS", "suppressions=${tsanSuppressionsFile.absolutePath}") + } + else -> {} // no action required + } + } + // TODO: Better to use proper XML parsing. - var contents = inputReport.readText() + var contents = outputFile.readText() contents = contents.replace(" { - val file = project.file("tsan_suppressions.txt") - inputs.file(file) - environment("TSAN_OPTIONS", "suppressions=${file.absolutePath}") - } - else -> {} // no action required - } - - doFirst { - workingDir.mkdirs() - } - - val reportWithPrefixes = workingDir.resolve("report-with-prefixes.xml") - val reportTask = project.tasks.register("${testName}Report", ReportWithPrefixes::class.java, testName, xmlReport, reportWithPrefixes) - finalizedBy(reportTask) - } + return runTask } // TODO: These tests should be created by `CompileToBitcodeExtension` diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadSuspensionTest.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadSuspensionTest.cpp index 2ed2140172f..03125de72ab 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadSuspensionTest.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadSuspensionTest.cpp @@ -25,11 +25,11 @@ using namespace kotlin; namespace { #ifdef KONAN_WINDOWS -constexpr size_t kDefaultIterations = 1000; -constexpr size_t kDefaultReportingStep = 100; +constexpr size_t kDefaultIterations = 20; +constexpr size_t kDefaultReportingStep = 2; #else -constexpr size_t kDefaultIterations = 10000; -constexpr size_t kDefaultReportingStep = 1000; +constexpr size_t kDefaultIterations = 200; +constexpr size_t kDefaultReportingStep = 20; #endif // #ifdef KONAN_WINDOWS std_support::vector collectThreadData() {