From 78d1105c574e7590d9aec53de32d0ff434f23692 Mon Sep 17 00:00:00 2001 From: Ao Li Date: Thu, 27 Jun 2024 17:31:41 -0400 Subject: [PATCH] update. --- .../cmu/pasta/fray/core/GlobalContext.kt | 8 +--- .../pasta/fray/core/command/Configuration.kt | 6 ++- .../cmu/pasta/fray/core/command/Executor.kt | 6 ++- junit-analyzer/build.gradle.kts | 3 ++ .../fray/junit/JunitRunnerTransformer.kt | 3 +- ...yingEngineExecutionListenerInstrumenter.kt | 45 +++++++++++++++++++ .../kotlin/cmu/pasta/fray/junit/Recorder.kt | 36 ++++++++++++++- 7 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/OutcomeDelayingEngineExecutionListenerInstrumenter.kt diff --git a/core/src/main/kotlin/cmu/pasta/fray/core/GlobalContext.kt b/core/src/main/kotlin/cmu/pasta/fray/core/GlobalContext.kt index 657a9951..cdccb470 100644 --- a/core/src/main/kotlin/cmu/pasta/fray/core/GlobalContext.kt +++ b/core/src/main/kotlin/cmu/pasta/fray/core/GlobalContext.kt @@ -206,7 +206,6 @@ object GlobalContext { context.pendingOperation = ParkBlocking() scheduleNextOperation(true) - context.checkInterrupt() // Well, unpark is signaled everywhere. We cannot really rely on it to // block the thread. LockSupport.unpark(t) @@ -216,14 +215,11 @@ object GlobalContext { val t = Thread.currentThread() val context = registeredThreads[t.id]!! - if (!context.unparkSignaled) { + if (!context.unparkSignaled && !context.interruptSignaled) { context.pendingOperation = ParkBlocking() context.state = ThreadState.Paused scheduleNextOperation(true) - if (context.unparkSignaled) { - context.checkInterrupt() - } - } else { + } else if (context.unparkSignaled) { context.unparkSignaled = false } } diff --git a/core/src/main/kotlin/cmu/pasta/fray/core/command/Configuration.kt b/core/src/main/kotlin/cmu/pasta/fray/core/command/Configuration.kt index bb4f8ba4..aeca52ad 100644 --- a/core/src/main/kotlin/cmu/pasta/fray/core/command/Configuration.kt +++ b/core/src/main/kotlin/cmu/pasta/fray/core/command/Configuration.kt @@ -31,7 +31,7 @@ data class ExecutionInfo( sealed class ExecutionConfig(name: String) : OptionGroup(name) { open fun getExecutionInfo(): ExecutionInfo { return ExecutionInfo( - MethodExecutor("", "", emptyList(), emptyList()), false, false, false, 10000000) + MethodExecutor("", "", emptyList(), emptyList(), emptyMap()), false, false, false, 10000000) } } @@ -50,10 +50,12 @@ class CliExecutionConfig : ExecutionConfig("cli") { val ignoreUnhandledExceptions by option("-e", "--ignore-unhandled-exceptions").flag() val interleaveMemoryOps by option("-m", "--memory").flag() val maxScheduledStep by option("-s", "--max-scheduled-step").int().default(10000) + val properties by option("-D", help = "System properties").pair().multiple() override fun getExecutionInfo(): ExecutionInfo { + val propertyMap = properties.toMap() return ExecutionInfo( - MethodExecutor(clazz, method, targetArgs, classpaths), + MethodExecutor(clazz, method, targetArgs, classpaths, propertyMap), ignoreUnhandledExceptions, timedOpAsYield, interleaveMemoryOps, diff --git a/core/src/main/kotlin/cmu/pasta/fray/core/command/Executor.kt b/core/src/main/kotlin/cmu/pasta/fray/core/command/Executor.kt index 3fd0b56a..5af993eb 100644 --- a/core/src/main/kotlin/cmu/pasta/fray/core/command/Executor.kt +++ b/core/src/main/kotlin/cmu/pasta/fray/core/command/Executor.kt @@ -16,9 +16,13 @@ data class MethodExecutor( val clazz: String, val method: String, val args: List, - val classpaths: List + val classpaths: List, + val properties: Map ) : Executor { override fun execute() { + for (property in properties) { + System.setProperty(property.key, property.value) + } val classLoader = URLClassLoader( classpaths.map { it -> URI("file://$it").toURL() }.toTypedArray(), diff --git a/junit-analyzer/build.gradle.kts b/junit-analyzer/build.gradle.kts index 7d38b4c1..1ecfa8f2 100644 --- a/junit-analyzer/build.gradle.kts +++ b/junit-analyzer/build.gradle.kts @@ -10,6 +10,9 @@ repositories { dependencies { compileOnly("junit:junit:4.13.2") + compileOnly("org.junit.platform:junit-platform-engine:1.10.2") + compileOnly("org.junit.platform:junit-platform-launcher:1.10.2") + compileOnly("org.junit.platform:junit-platform-console-standalone:1.10.2") compileOnly(project(":runtime")) implementation(project(":instrumentation")) implementation(kotlin("stdlib-jdk8")) diff --git a/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/JunitRunnerTransformer.kt b/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/JunitRunnerTransformer.kt index ca6690bb..f426ed9b 100644 --- a/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/JunitRunnerTransformer.kt +++ b/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/JunitRunnerTransformer.kt @@ -16,7 +16,8 @@ class JunitRunnerTransformer : ClassFileTransformer { ): ByteArray { val classReader = ClassReader(classfileBuffer) val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) - val cv: ClassVisitor = JunitInstrumenter(classWriter) + var cv: ClassVisitor = JunitInstrumenter(classWriter) + cv = OutcomeDelayingEngineExecutionListenerInstrumenter(cv) classReader.accept(cv, ClassReader.EXPAND_FRAMES) return classWriter.toByteArray() } diff --git a/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/OutcomeDelayingEngineExecutionListenerInstrumenter.kt b/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/OutcomeDelayingEngineExecutionListenerInstrumenter.kt new file mode 100644 index 00000000..fbca3430 --- /dev/null +++ b/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/OutcomeDelayingEngineExecutionListenerInstrumenter.kt @@ -0,0 +1,45 @@ +package cmu.pasta.fray.junit + +import cmu.pasta.fray.instrumentation.visitors.ClassVisitorBase +import cmu.pasta.fray.instrumentation.visitors.Utils +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Type +import org.objectweb.asm.commons.AdviceAdapter + +class OutcomeDelayingEngineExecutionListenerInstrumenter(cv: ClassVisitor) : + ClassVisitorBase( + cv, "org.junit.platform.launcher.core.OutcomeDelayingEngineExecutionListener") { + override fun instrumentMethod( + mv: MethodVisitor, + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array? + ): MethodVisitor { + if (name == "executionStarted") { + return object : AdviceAdapter(ASM9, mv, access, name, descriptor) { + override fun onMethodEnter() { + loadArgs() + invokeStatic( + Type.getObjectType(Recorder::class.java.name.replace(".", "/")), + Utils.kFunctionToASMMethod(Recorder::executionStarted), + ) + } + } + } + if (name == "executionFinished") { + return object : AdviceAdapter(ASM9, mv, access, name, descriptor) { + override fun onMethodEnter() { + loadArgs() + invokeStatic( + Type.getObjectType(Recorder::class.java.name.replace(".", "/")), + Utils.kFunctionToASMMethod(Recorder::executionFinished), + ) + } + } + } + return mv + } +} diff --git a/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/Recorder.kt b/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/Recorder.kt index 713db77c..872cb4c2 100644 --- a/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/Recorder.kt +++ b/junit-analyzer/src/main/kotlin/cmu/pasta/fray/junit/Recorder.kt @@ -4,6 +4,10 @@ import cmu.pasta.fray.runtime.Runtime import java.io.File import java.lang.instrument.Instrumentation import junit.framework.TestCase +import org.junit.platform.engine.TestDescriptor +import org.junit.platform.engine.TestExecutionResult +import org.junit.platform.engine.support.descriptor.ClassSource +import org.junit.vintage.engine.descriptor.VintageTestDescriptor object Recorder { var newThreadSpawned = false @@ -18,6 +22,36 @@ object Recorder { newThreadSpawned = false } + @JvmStatic + fun executionStarted(descriptor: TestDescriptor) { + newThreadSpawned = false + } + + @JvmStatic + fun executionFinished(descriptor: TestDescriptor, result: TestExecutionResult) { + if (newThreadSpawned) { + if (descriptor is VintageTestDescriptor) { + val methodName = descriptor.displayName + if (descriptor.parent.isPresent) { + val parent = descriptor.parent.get() + if (parent.source.isPresent) { + val source = parent.source.get() + if (source is ClassSource) { + source.className + f.appendText("${source.className}#${methodName}\n") + } + } + } + } + } + newThreadSpawned = false + } + + @JvmStatic + fun execution(descriptor: TestDescriptor) { + newThreadSpawned = false + } + @JvmStatic fun testEnd(testCase: TestCase) { if (newThreadSpawned) { @@ -27,7 +61,7 @@ object Recorder { } fun init() { - f.deleteOnExit() + f.delete() f.createNewFile() } }