[Native][tests] Track and report memory usage while tests are running

This commit is contained in:
Dmitriy Dolovov
2022-05-24 09:56:33 +03:00
committed by Space
parent 009e7f5c3a
commit 70aaa212a5
4 changed files with 109 additions and 9 deletions
+3
View File
@@ -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)
}
@@ -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" }
}
@@ -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(
@@ -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()
}
}