From c8cef4346872ae07d9fbda424406c97fa5e73ca5 Mon Sep 17 00:00:00 2001 From: Ao Li Date: Mon, 15 Jul 2024 13:10:19 -0400 Subject: [PATCH] update. --- .../cmu/pasta/fray/core/GlobalContext.kt | 8 +++- .../cmu/pasta/fray/core/RuntimeDelegate.kt | 12 ++++++ .../pasta/fray/core/command/Configuration.kt | 3 ++ .../cmu/pasta/fray/core/scheduler/Choice.kt | 3 +- .../fray/core/scheduler/ReplayScheduler.kt | 18 ++++++-- .../cmu/pasta/fray/core/scheduler/Schedule.kt | 5 ++- .../ApplicationCodeTransformer.kt | 1 + .../visitors/ThreadHashCodeInstrumenter.kt | 43 +++++++++++++++++++ .../pasta/fray/it/IntegrationTestRunner.java | 1 + .../java/cmu/pasta/fray/runtime/Delegate.java | 4 ++ .../java/cmu/pasta/fray/runtime/Runtime.java | 4 ++ 11 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 instrumentation/src/main/kotlin/cmu/pasta/fray/instrumentation/visitors/ThreadHashCodeInstrumenter.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 6f59624..10fafeb 100644 --- a/core/src/main/kotlin/cmu/pasta/fray/core/GlobalContext.kt +++ b/core/src/main/kotlin/cmu/pasta/fray/core/GlobalContext.kt @@ -30,6 +30,7 @@ import java.util.concurrent.locks.LockSupport import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock +import kotlin.system.exitProcess // TODO(aoli): make this a class maybe? @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") @@ -91,6 +92,10 @@ object GlobalContext { for (logger in loggers) { logger.applicationEvent(sw.toString()) } + if (!config!!.exploreMode && config!!.exitWhenBugFound) { + loggers.forEach { it.executionDone(bugFound) } + exitProcess(-1) + } } } @@ -877,7 +882,8 @@ object GlobalContext { index, nextThread.index, enabledOperations.size, - enabledOperations.map { it.index })) + enabledOperations.map { it.index }, + nextThread.pendingOperation.toString())) } } nextThread.state = ThreadState.Running diff --git a/core/src/main/kotlin/cmu/pasta/fray/core/RuntimeDelegate.kt b/core/src/main/kotlin/cmu/pasta/fray/core/RuntimeDelegate.kt index beec296..aa6b877 100644 --- a/core/src/main/kotlin/cmu/pasta/fray/core/RuntimeDelegate.kt +++ b/core/src/main/kotlin/cmu/pasta/fray/core/RuntimeDelegate.kt @@ -741,4 +741,16 @@ class RuntimeDelegate : Delegate() { override fun onNanoTime(): Long { return GlobalContext.nanoTime() } + + override fun onThreadHashCode(t: Any): Int { + if (t is Thread) { + val context = GlobalContext.registeredThreads[t.id] + if (context != null) { + return 0 + } else { + return t.hashCode() + } + } + return t.hashCode() + } } 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 8f94d3f..03509c5 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 @@ -156,6 +156,7 @@ class MainCommand : CliktCommand() { "pct" to PCT()) val noFray by option("--no-fray").flag() val exploreMode by option("--explore").flag() + val exitWhenBugFound by option("--exit-on-bug").flag() val runConfig by option() .groupChoice( @@ -176,6 +177,7 @@ class MainCommand : CliktCommand() { fullSchedule, logger!!.getLogger(report, fullSchedule), exploreMode, + exitWhenBugFound, noFray) } } @@ -188,5 +190,6 @@ data class Configuration( val fullSchedule: Boolean, val logger: LoggerBase, val exploreMode: Boolean, + val exitWhenBugFound: Boolean, val noFray: Boolean, ) {} diff --git a/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/Choice.kt b/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/Choice.kt index 8101372..8fd6541 100644 --- a/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/Choice.kt +++ b/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/Choice.kt @@ -7,5 +7,6 @@ data class Choice( val selected: Int, val threadId: Int, val enabled: Int, - val enabledIds: List + val enabledIds: List, + val operation: String ) diff --git a/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/ReplayScheduler.kt b/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/ReplayScheduler.kt index 7e42722..cbac779 100644 --- a/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/ReplayScheduler.kt +++ b/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/ReplayScheduler.kt @@ -10,6 +10,9 @@ class ReplayScheduler(val schedule: Schedule) : Scheduler { if (threads.size == 1 && !schedule.fullSchedule) { return threads[0] } + if (index == 1710) { + println("???") + } if (index >= schedule.choices.size) { return threads[0] @@ -17,12 +20,19 @@ class ReplayScheduler(val schedule: Schedule) : Scheduler { // scheduler") } val choice = schedule.choices[index] - assert(threads.map { it.index }.toList() == choice.enabledIds) - assert(choice.enabled == threads.size) + if (threads.map { it.index }.toList() != choice.enabledIds) { + // println("?") + } + // assert(threads.map { it.index }.toList() == choice.enabledIds) + // assert(choice.enabled == threads.size) - val selected = threads[choice.selected] - assert(choice.threadId == selected.index) + val selected = threads[choice.selected % threads.size] + // assert(choice.threadId == selected.index) index += 1 + if (selected.pendingOperation.toString().split("@")[0] != choice.operation.split("@")[0]) { + // println("?") + } + // assert(selected.pendingOperation.toString() == choice.operation) return selected } diff --git a/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/Schedule.kt b/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/Schedule.kt index fc4b1dc..6f8c126 100644 --- a/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/Schedule.kt +++ b/core/src/main/kotlin/cmu/pasta/fray/core/scheduler/Schedule.kt @@ -19,8 +19,9 @@ data class Schedule(val choices: MutableList, val fullSchedule: Boolean) } if (line.strip().isEmpty()) continue val parts = line.split(",") - val enabledIds = parts.subList(3, parts.size).map { it.toInt() } - choices.add(Choice(parts[0].toInt(), parts[1].toInt(), parts[2].toInt(), enabledIds)) + val enabledIds = parts.subList(3, parts.size - 1).map { it.toInt() } + choices.add( + Choice(parts[0].toInt(), parts[1].toInt(), parts[2].toInt(), enabledIds, parts.last())) } return Schedule(choices, fullSchedule) } diff --git a/instrumentation/src/main/kotlin/cmu/pasta/fray/instrumentation/ApplicationCodeTransformer.kt b/instrumentation/src/main/kotlin/cmu/pasta/fray/instrumentation/ApplicationCodeTransformer.kt index 371e6f5..8d13f47 100644 --- a/instrumentation/src/main/kotlin/cmu/pasta/fray/instrumentation/ApplicationCodeTransformer.kt +++ b/instrumentation/src/main/kotlin/cmu/pasta/fray/instrumentation/ApplicationCodeTransformer.kt @@ -54,6 +54,7 @@ class ApplicationCodeTransformer : ClassFileTransformer { cv = ClassConstructorInstrumenter(cv) cv = SleepInstrumenter(cv) cv = TimeInstrumenter(cv) + cv = ThreadHashCodeInstrumenter(cv) val classVersionInstrumenter = ClassVersionInstrumenter(cv) cv = ArrayOperationInstrumenter(classVersionInstrumenter) classReader.accept(cv, ClassReader.EXPAND_FRAMES) diff --git a/instrumentation/src/main/kotlin/cmu/pasta/fray/instrumentation/visitors/ThreadHashCodeInstrumenter.kt b/instrumentation/src/main/kotlin/cmu/pasta/fray/instrumentation/visitors/ThreadHashCodeInstrumenter.kt new file mode 100644 index 0000000..3ee5c6f --- /dev/null +++ b/instrumentation/src/main/kotlin/cmu/pasta/fray/instrumentation/visitors/ThreadHashCodeInstrumenter.kt @@ -0,0 +1,43 @@ +package cmu.pasta.fray.instrumentation.visitors + +import cmu.pasta.fray.runtime.Runtime +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes.ASM9 +import org.objectweb.asm.Type +import org.objectweb.asm.commons.AdviceAdapter + +class ThreadHashCodeInstrumenter(cv: ClassVisitor) : ClassVisitor(ASM9, cv) { + override fun visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array? + ): MethodVisitor { + return object : + AdviceAdapter( + ASM9, + super.visitMethod(access, name, descriptor, signature, exceptions), + access, + name, + descriptor) { + override fun visitMethodInsn( + opcodeAndSource: Int, + owner: String, + name: String, + descriptor: String, + isInterface: Boolean + ) { + if (name == "hashCode" && owner == "java/lang/Object") { + invokeStatic( + Type.getObjectType(Runtime::class.java.name.replace(".", "/")), + Utils.kFunctionToASMMethod(Runtime::onThreadHashCode), + ) + } else { + super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface) + } + } + } + } +} diff --git a/integration-tests/src/test/java/cmu/pasta/fray/it/IntegrationTestRunner.java b/integration-tests/src/test/java/cmu/pasta/fray/it/IntegrationTestRunner.java index 8b38843..7eb405e 100644 --- a/integration-tests/src/test/java/cmu/pasta/fray/it/IntegrationTestRunner.java +++ b/integration-tests/src/test/java/cmu/pasta/fray/it/IntegrationTestRunner.java @@ -44,6 +44,7 @@ public String runTest(Function0 exec, Scheduler scheduler, int iter) { true, new JsonLogger("/tmp/report", false), false, + false, false ); TestRunner runner = new TestRunner(config); diff --git a/runtime/src/main/java/cmu/pasta/fray/runtime/Delegate.java b/runtime/src/main/java/cmu/pasta/fray/runtime/Delegate.java index 4110723..0e7a584 100644 --- a/runtime/src/main/java/cmu/pasta/fray/runtime/Delegate.java +++ b/runtime/src/main/java/cmu/pasta/fray/runtime/Delegate.java @@ -268,5 +268,9 @@ public boolean onThreadIsInterrupted(boolean result, Thread t) { public long onNanoTime() { return System.nanoTime(); } + + public int onThreadHashCode(Object t) { + return t.hashCode(); + } } diff --git a/runtime/src/main/java/cmu/pasta/fray/runtime/Runtime.java b/runtime/src/main/java/cmu/pasta/fray/runtime/Runtime.java index 07e895a..53f8469 100644 --- a/runtime/src/main/java/cmu/pasta/fray/runtime/Runtime.java +++ b/runtime/src/main/java/cmu/pasta/fray/runtime/Runtime.java @@ -358,4 +358,8 @@ public static boolean onLockHasQueuedThread(boolean result, Lock l, Thread t) { public static long onNanoTime() { return DELEGATE.onNanoTime(); } + + public static int onThreadHashCode(Object t) { + return DELEGATE.onThreadHashCode(t); + } }