[Native][tests] Track and report memory usage while tests are running
This commit is contained in:
@@ -132,6 +132,9 @@ fun nativeTest(taskName: String, vararg tags: String) = projectTest(
|
|||||||
TestProperty.CACHE_MODE.setUpFromGradleProperty(this)
|
TestProperty.CACHE_MODE.setUpFromGradleProperty(this)
|
||||||
TestProperty.EXECUTION_TIMEOUT.setUpFromGradleProperty(this)
|
TestProperty.EXECUTION_TIMEOUT.setUpFromGradleProperty(this)
|
||||||
|
|
||||||
|
// Pass the current Gradle task name so test can use it in logging.
|
||||||
|
environment("GRADLE_TASK_NAME", path)
|
||||||
|
|
||||||
useJUnitPlatform {
|
useJUnitPlatform {
|
||||||
includeTags(*tags)
|
includeTags(*tags)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -87,7 +87,8 @@ private fun fullPropertyName(shortName: String) = "kotlin.internal.native.test.$
|
|||||||
/*************** Environment variables ***************/
|
/*************** Environment variables ***************/
|
||||||
|
|
||||||
internal enum class EnvironmentVariable {
|
internal enum class EnvironmentVariable {
|
||||||
PROJECT_BUILD_DIR;
|
PROJECT_BUILD_DIR,
|
||||||
|
GRADLE_TASK_NAME;
|
||||||
|
|
||||||
fun readValue(): String = System.getenv(name) ?: fail { "Unspecified $name environment variable" }
|
fun readValue(): String = System.getenv(name) ?: fail { "Unspecified $name environment variable" }
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-8
@@ -43,9 +43,6 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
|
|||||||
override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) {
|
override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) {
|
||||||
val settings = createTestRunSettings()
|
val settings = createTestRunSettings()
|
||||||
|
|
||||||
// Set the essential compiler property.
|
|
||||||
System.setProperty("kotlin.native.home", settings.get<KotlinNativeHome>().dir.path)
|
|
||||||
|
|
||||||
// Inject the required properties to test instance.
|
// Inject the required properties to test instance.
|
||||||
with(settings.get<BlackBoxTestInstances>().enclosingTestInstance) {
|
with(settings.get<BlackBoxTestInstances>().enclosingTestInstance) {
|
||||||
testRunSettings = settings
|
testRunSettings = settings
|
||||||
@@ -58,9 +55,6 @@ class NativeSimpleTestSupport : BeforeEachCallback {
|
|||||||
override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) {
|
override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) {
|
||||||
val settings = createSimpleTestRunSettings()
|
val settings = createSimpleTestRunSettings()
|
||||||
|
|
||||||
// Set the essential compiler property.
|
|
||||||
System.setProperty("kotlin.native.home", settings.get<KotlinNativeHome>().dir.path)
|
|
||||||
|
|
||||||
// Inject the required properties to test instance.
|
// Inject the required properties to test instance.
|
||||||
with(settings.get<SimpleTestInstances>().enclosingTestInstance) {
|
with(settings.get<SimpleTestInstances>().enclosingTestInstance) {
|
||||||
testRunSettings = settings
|
testRunSettings = settings
|
||||||
@@ -70,14 +64,20 @@ class NativeSimpleTestSupport : BeforeEachCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private object NativeTestSupport {
|
private object NativeTestSupport {
|
||||||
private val NAMESPACE = ExtensionContext.Namespace.create(NativeBlackBoxTestSupport::class.java.simpleName)
|
private val NAMESPACE = ExtensionContext.Namespace.create(NativeTestSupport::class.java.simpleName)
|
||||||
|
|
||||||
/*************** Test process settings ***************/
|
/*************** Test process settings ***************/
|
||||||
|
|
||||||
fun ExtensionContext.getOrCreateTestProcessSettings(): TestProcessSettings =
|
fun ExtensionContext.getOrCreateTestProcessSettings(): TestProcessSettings =
|
||||||
root.getStore(NAMESPACE).getOrComputeIfAbsent(TestProcessSettings::class.java.name) {
|
root.getStore(NAMESPACE).getOrComputeIfAbsent(TestProcessSettings::class.java.name) {
|
||||||
|
val nativeHome = computeNativeHome()
|
||||||
|
|
||||||
|
// Apply the necessary process-wide settings:
|
||||||
|
System.setProperty("kotlin.native.home", nativeHome.dir.path) // Set the essential compiler property.
|
||||||
|
setUpMemoryTracking() // Set up memory tracking and reporting.
|
||||||
|
|
||||||
TestProcessSettings(
|
TestProcessSettings(
|
||||||
computeNativeHome(),
|
nativeHome,
|
||||||
computeNativeClassLoader(),
|
computeNativeClassLoader(),
|
||||||
computeBaseDirs()
|
computeBaseDirs()
|
||||||
)
|
)
|
||||||
@@ -103,6 +103,33 @@ private object NativeTestSupport {
|
|||||||
return BaseDirs(testBuildDir)
|
return BaseDirs(testBuildDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ExtensionContext.setUpMemoryTracking() {
|
||||||
|
TestLogger.initialize() // Initialize special logging (directly to Gradle's console).
|
||||||
|
|
||||||
|
val gradleTaskName = EnvironmentVariable.GRADLE_TASK_NAME.readValue()
|
||||||
|
fun Long.toMBs() = (this / 1024 / 1024)
|
||||||
|
|
||||||
|
// Set up memory tracking and reporting:
|
||||||
|
MemoryTracker.startTracking(intervalMillis = 1000) { memoryMark ->
|
||||||
|
TestLogger.log(
|
||||||
|
buildString {
|
||||||
|
append(memoryMark.timestamp).append(' ').append(gradleTaskName)
|
||||||
|
append(" Memory usage (MB): ")
|
||||||
|
append("used=").append(memoryMark.usedMemory.toMBs())
|
||||||
|
append(", free=").append(memoryMark.freeMemory.toMBs())
|
||||||
|
append(", total=").append(memoryMark.totalMemory.toMBs())
|
||||||
|
append(", max=").append(memoryMark.maxMemory.toMBs())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop tracking memory when all tests are finished:
|
||||||
|
root.getStore(NAMESPACE).put(
|
||||||
|
testClassKeyFor<MemoryTracker>(),
|
||||||
|
ExtensionContext.Store.CloseableResource { MemoryTracker.stopTracking() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/*************** Test class settings (common part) ***************/
|
/*************** Test class settings (common part) ***************/
|
||||||
|
|
||||||
private fun ExtensionContext.addCommonTestClassSettingsTo(
|
private fun ExtensionContext.addCommonTestClassSettingsTo(
|
||||||
|
|||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* 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.konan.blackboxtest.support.util
|
||||||
|
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal tool for tracking used memory.
|
||||||
|
*/
|
||||||
|
internal object MemoryTracker {
|
||||||
|
data class MemoryMark(
|
||||||
|
val timestamp: LocalDateTime,
|
||||||
|
val usedMemory: Long,
|
||||||
|
val freeMemory: Long,
|
||||||
|
val totalMemory: Long,
|
||||||
|
val maxMemory: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
private class MemoryTrackerRunner(
|
||||||
|
private val intervalMillis: Long,
|
||||||
|
private val logger: (MemoryMark) -> Unit
|
||||||
|
) : Thread("NativeTestMemoryTrackerRunner") {
|
||||||
|
private val runtime = Runtime.getRuntime()
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
try {
|
||||||
|
while (!interrupted()) {
|
||||||
|
val timestamp = LocalDateTime.now()
|
||||||
|
|
||||||
|
val free = runtime.freeMemory()
|
||||||
|
val total = runtime.totalMemory()
|
||||||
|
val used = total - free
|
||||||
|
val max = runtime.maxMemory()
|
||||||
|
|
||||||
|
logger(
|
||||||
|
MemoryMark(
|
||||||
|
timestamp = timestamp,
|
||||||
|
usedMemory = used,
|
||||||
|
freeMemory = free,
|
||||||
|
totalMemory = total,
|
||||||
|
maxMemory = max
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
sleep(intervalMillis)
|
||||||
|
}
|
||||||
|
} catch (_: InterruptedException) {
|
||||||
|
// do nothing, just leave the loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val activeRunner = AtomicReference<MemoryTrackerRunner>()
|
||||||
|
|
||||||
|
fun startTracking(intervalMillis: Long, logger: (MemoryMark) -> Unit) {
|
||||||
|
val runner = MemoryTrackerRunner(intervalMillis, logger)
|
||||||
|
check(activeRunner.compareAndSet(null, runner)) { "There is another active runner" }
|
||||||
|
runner.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopTracking() {
|
||||||
|
val runner = activeRunner.getAndSet(null) ?: error("No active runner")
|
||||||
|
runner.interrupt()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user