[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.EXECUTION_TIMEOUT.setUpFromGradleProperty(this)
|
||||
|
||||
// Pass the current Gradle task name so test can use it in logging.
|
||||
environment("GRADLE_TASK_NAME", path)
|
||||
|
||||
useJUnitPlatform {
|
||||
includeTags(*tags)
|
||||
}
|
||||
|
||||
+2
-1
@@ -87,7 +87,8 @@ private fun fullPropertyName(shortName: String) = "kotlin.internal.native.test.$
|
||||
/*************** Environment variables ***************/
|
||||
|
||||
internal enum class EnvironmentVariable {
|
||||
PROJECT_BUILD_DIR;
|
||||
PROJECT_BUILD_DIR,
|
||||
GRADLE_TASK_NAME;
|
||||
|
||||
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) {
|
||||
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.
|
||||
with(settings.get<BlackBoxTestInstances>().enclosingTestInstance) {
|
||||
testRunSettings = settings
|
||||
@@ -58,9 +55,6 @@ class NativeSimpleTestSupport : BeforeEachCallback {
|
||||
override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) {
|
||||
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.
|
||||
with(settings.get<SimpleTestInstances>().enclosingTestInstance) {
|
||||
testRunSettings = settings
|
||||
@@ -70,14 +64,20 @@ class NativeSimpleTestSupport : BeforeEachCallback {
|
||||
}
|
||||
|
||||
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 ***************/
|
||||
|
||||
fun ExtensionContext.getOrCreateTestProcessSettings(): TestProcessSettings =
|
||||
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(
|
||||
computeNativeHome(),
|
||||
nativeHome,
|
||||
computeNativeClassLoader(),
|
||||
computeBaseDirs()
|
||||
)
|
||||
@@ -103,6 +103,33 @@ private object NativeTestSupport {
|
||||
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) ***************/
|
||||
|
||||
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