Skip to content

Commit

Permalink
Add semaphore test.
Browse files Browse the repository at this point in the history
  • Loading branch information
aoli-al committed Mar 25, 2024
1 parent 7274e60 commit dbbee86
Show file tree
Hide file tree
Showing 25 changed files with 512 additions and 69 deletions.
25 changes: 24 additions & 1 deletion core/src/main/kotlin/cmu/pasta/sfuzz/core/GlobalContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmu.pasta.sfuzz.core
import cmu.pasta.sfuzz.core.concurrency.locks.LockManager
import cmu.pasta.sfuzz.core.concurrency.SFuzzThread
import cmu.pasta.sfuzz.core.concurrency.SynchronizationManager
import cmu.pasta.sfuzz.core.concurrency.locks.SemaphoreManager
import cmu.pasta.sfuzz.core.concurrency.operations.*
import cmu.pasta.sfuzz.core.logger.LoggerBase
import cmu.pasta.sfuzz.core.runtime.AnalysisResult
Expand All @@ -15,6 +16,7 @@ import cmu.pasta.sfuzz.runtime.MemoryOpType
import cmu.pasta.sfuzz.runtime.Runtime
import cmu.pasta.sfuzz.runtime.TargetTerminateException
import java.util.concurrent.Executors
import java.util.concurrent.Semaphore
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock
Expand All @@ -29,6 +31,7 @@ object GlobalContext {
var scheduler: Scheduler = FifoScheduler()
var config: Configuration? = null
private val lockManager = LockManager()
private val semaphoreManager = SemaphoreManager()
private val volatileManager = VolatileManager()
val syncManager = SynchronizationManager()
val loggers = mutableListOf<LoggerBase>()
Expand Down Expand Up @@ -393,7 +396,7 @@ object GlobalContext {
fun log(format: String, vararg args: Any) {
val tid = Thread.currentThread().id
val context = registeredThreads[tid]!!
val data = "[${context.index}]: ${String.format(format, args)}"
val data = "[${context.index}]: ${String.format(format, args)}\n"
for (logger in loggers) {
logger.applicationEvent(data)
}
Expand Down Expand Up @@ -449,6 +452,26 @@ object GlobalContext {
lockManager.registerNewCondition(condition, lock)
}

fun semaphoreInit(sem: Semaphore) {
semaphoreManager.init(sem)
}

fun semaphoreAcquire(sem: Semaphore, permits: Int, shouldBlock: Boolean): Boolean {
return semaphoreManager.acquire(sem, permits, shouldBlock)
}

fun semaphoreRelease(sem: Semaphore, permits: Int) {
semaphoreManager.release(sem, permits)
}

fun semaphoreDrainPermits(sem: Semaphore): Int {
return semaphoreManager.drainPermits(sem)
}

fun semaphoreReducePermits(sem: Semaphore, permits: Int) {
semaphoreManager.reducePermits(sem, permits)
}

fun fieldOperation(obj: Any?, owner: String, name: String, type: MemoryOpType) {
if (!volatileManager.isVolatile(owner, name)) return
val objIds = mutableListOf<Int>()
Expand Down
63 changes: 63 additions & 0 deletions core/src/main/kotlin/cmu/pasta/sfuzz/core/RuntimeDelegate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cmu.pasta.sfuzz.core.concurrency.SFuzzThread
import cmu.pasta.sfuzz.runtime.Delegate
import cmu.pasta.sfuzz.runtime.MemoryOpType
import cmu.pasta.sfuzz.runtime.TargetTerminateException
import java.util.concurrent.Semaphore
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.locks.ReentrantReadWriteLock
Expand Down Expand Up @@ -278,6 +279,68 @@ class RuntimeDelegate: Delegate() {
entered.set(false)
}

override fun onSemaphoreInit(sem: Semaphore) {
if (checkEntered()) return
GlobalContext.semaphoreInit(sem)
entered.set(false)
}

override fun onSemaphoreAcquire(sem: Semaphore, permits: Int) {
if (checkEntered()) {
skipFunctionEntered.set(1 + skipFunctionEntered.get())
return
}
GlobalContext.semaphoreAcquire(sem, permits, true)
entered.set(false)
skipFunctionEntered.set(skipFunctionEntered.get() + 1)
}

override fun onSemaphoreAcquireDone() {
skipFunctionEntered.set(skipFunctionEntered.get() - 1)
}

override fun onSemaphoreRelease(sem: Semaphore, permits: Int) {
if (checkEntered()) {
skipFunctionEntered.set(1 + skipFunctionEntered.get())
return
}
GlobalContext.semaphoreRelease(sem, permits)
entered.set(false)
skipFunctionEntered.set(skipFunctionEntered.get() + 1)
}

override fun onSemaphoreReleaseDone() {
skipFunctionEntered.set(skipFunctionEntered.get() - 1)
}

override fun onSemaphoreDrainPermits(sem: Semaphore) {
if (checkEntered()) {
skipFunctionEntered.set(1 + skipFunctionEntered.get())
return
}
GlobalContext.semaphoreDrainPermits(sem)
entered.set(false)
skipFunctionEntered.set(skipFunctionEntered.get() + 1)
}

override fun onSemaphoreDrainPermitsDone() {
skipFunctionEntered.set(skipFunctionEntered.get() - 1)
}

override fun onSemaphoreReducePermits(sem: Semaphore, permits: Int) {
if (checkEntered()) {
skipFunctionEntered.set(1 + skipFunctionEntered.get())
return
}
GlobalContext.semaphoreReducePermits(sem, permits)
entered.set(false)
skipFunctionEntered.set(skipFunctionEntered.get() + 1)
}

override fun onSemaphoreReducePermitsDone() {
skipFunctionEntered.set(skipFunctionEntered.get() - 1)
}

override fun start() {
// For the first thread, it is not registered.
// Therefor we cannot call `checkEntered` here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock

class LockManager {
val lockContextManager = LockContextManager()

val lockContextManager = ReferencedContextManager<LockContext> { ReentrantLockContext() }
val waitingThreads = mutableMapOf<Int, MutableList<Long>>()

val conditionToLock = mutableMapOf<Condition, Lock>()
val lockToConditions = mutableMapOf<Lock, MutableList<Condition>>()

Expand All @@ -22,12 +20,10 @@ class LockManager {

fun reentrantReadWriteLockInit(readLock: ReadLock, writeLock: WriteLock) {
val context = ReentrantReadWriteLockContext()
lockContextManager.addLockContext(readLock, context)
lockContextManager.addLockContext(writeLock, context)
lockContextManager.addContext(readLock, context)
lockContextManager.addContext(writeLock, context)
}

// fun addLockContext(lock)

/**
* Return true if [lock] is acquired by the current thread.
*/
Expand Down Expand Up @@ -92,6 +88,6 @@ class LockManager {
assert(waitingThreads.isEmpty())
conditionToLock.clear()
lockToConditions.clear()
// lockContextManager.done()
lockContextManager.done()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,34 @@ import java.lang.ref.ReferenceQueue
class IdentityPhantomReference<T>(referent: T, queue: ReferenceQueue<in T>) : PhantomReference<T>(referent, queue) {
val id = System.identityHashCode(referent)
}
class LockContextManager {
class ReferencedContextManager<T>(val contextProducer: (Any) -> T) {
val queue = ReferenceQueue<Any>()
val lockMap = mutableMapOf<Int, LockContext>()
fun getLockContext(lock: Any): LockContext {
val lockMap = mutableMapOf<Int, T>()
fun getLockContext(lock: Any): T {
val id = System.identityHashCode(lock)
if (!lockMap.containsKey(id)) {
lockMap[id] = ReentrantLockContext()
lockMap[id] = contextProducer(lock)
IdentityPhantomReference(lock, queue)
gc()
}
return lockMap[id]!!
}

fun addLockContext(lock: Any, context: LockContext) {
fun addContext(lock: Any, context: T) {
val id = System.identityHashCode(lock)
lockMap[id] = context
IdentityPhantomReference(lock, queue)
gc()
}

fun done() {
lockMap.clear()
gc()
}

fun gc() {
var ref = queue.poll()
while (ref != null) {
val id = (ref as IdentityPhantomReference<*>).id
assert(lockMap[id]!!.isEmpty())
lockMap.remove(id)
ref = queue.poll()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package cmu.pasta.sfuzz.core.concurrency.locks

import cmu.pasta.sfuzz.core.GlobalContext
import cmu.pasta.sfuzz.core.ThreadState
import cmu.pasta.sfuzz.core.concurrency.operations.ThreadResumeOperation

class SemaphoreContext(var totalPermits: Int) {
private val lockWaiters = mutableMapOf<Long, Int>()

fun acquire(permits: Int, shouldBlock: Boolean): Boolean {
if (totalPermits > permits) {
totalPermits -= permits
return true
} else if (shouldBlock) {
lockWaiters[Thread.currentThread().id] = permits
}
return false
}

fun drainPermits(): Int {
val permits = totalPermits
totalPermits = 0
return permits
}

fun release(permits: Int) {
totalPermits += permits
if (totalPermits > 0) {
val it = lockWaiters.iterator()
while (it.hasNext()) {
val (tid, p) = it.next()
if (totalPermits >= p) {
GlobalContext.registeredThreads[tid]!!.pendingOperation = ThreadResumeOperation()
GlobalContext.registeredThreads[tid]!!.state = ThreadState.Enabled
lockWaiters.remove(tid)
}
}
}
}

fun reducePermits(permits: Int) {
totalPermits -= permits
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cmu.pasta.sfuzz.core.concurrency.locks

import java.util.concurrent.Semaphore

class SemaphoreManager {
val lockContextManager = ReferencedContextManager {it ->
if (it is Semaphore) {
SemaphoreContext(it.availablePermits())
} else {
throw IllegalArgumentException("SemaphoreManager can only manage Semaphore objects")
}
}

fun init(sem: Semaphore) {
val context = SemaphoreContext(sem.availablePermits())
lockContextManager.addContext(sem, context)
}

fun acquire(sem: Semaphore, permits: Int, shouldBlock: Boolean): Boolean {
return lockContextManager.getLockContext(sem).acquire(permits, shouldBlock)
}

fun release(sem: Semaphore, permits: Int) {
lockContextManager.getLockContext(sem).release(permits)
}

fun drainPermits(sem: Semaphore): Int {
return lockContextManager.getLockContext(sem).drainPermits()
}

fun reducePermits(sem: Semaphore, permits: Int) {
lockContextManager.getLockContext(sem).reducePermits(permits)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ fun instrumentClass(path:String, inputStream: InputStream): ByteArray {
cv = UnsafeInstrumenter(cv)
cv = ClassloaderInstrumenter(cv)
cv = ObjectInstrumenter(cv)
cv = SemaphoreInstrumenter(cv)
// MonitorInstrumenter should come second because ObjectInstrumenter will insert more
// monitors.
cv = MonitorInstrumenter(cv)
// SynchronizedMethodEmbeddingInstrumenter should come before MonitorInstrumenter because
// it inlines monitors for synchronized methods.
// cv = SynchronizedMethodInstrumenter(cv)
classReader.accept(cv, ClassReader.EXPAND_FRAMES)
var out = classWriter.toByteArray()
val out = classWriter.toByteArray()
File("/tmp/out/jdk/${path.replace("/", ".").removePrefix(".")}").writeBytes(out)
// File("/tmp/${path.replace("/", ".").removePrefix(".")}").writeBytes(out)
return out
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class ClassloaderInstrumenter(cv: ClassVisitor): ClassVisitor(ASM9, cv) {
val mv = super.visitMethod(access, name, descriptor, signature, exceptions)
if ((name == "loadClass" && descriptor == "(Ljava/lang/String;)Ljava/lang/Class;" && className == "java/lang/ClassLoader") ||
(name == "makeImpl" && className == "java/lang/invoke/MethodType")) {
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onLoadClass, false), Runtime::onLoadClassDone, access, name, descriptor, false)
val eMv = MethodEnterVisitor(mv, Runtime::onLoadClass, access, name, descriptor, false, false)
return MethodExitVisitor(eMv, Runtime::onLoadClassDone, access, name, descriptor, false, false)
}
return mv
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ class ConditionInstrumenter(cv:ClassVisitor): ClassVisitorBase(cv, ConditionObje
exceptions: Array<out String>?
): MethodVisitor {
if (name.startsWith("await")) {
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onConditionAwait, true),
Runtime::onConditionAwaitDone, access, name, descriptor, true)
val eMv = MethodEnterVisitor(mv, Runtime::onConditionAwait, access, name, descriptor, true, false)
return MethodExitVisitor(eMv, Runtime::onConditionAwaitDone, access, name, descriptor, true, false)
}
if (name == "signal") {
return MethodEnterVisitor(mv, Runtime::onConditionSignal, true)
return MethodEnterVisitor(mv, Runtime::onConditionSignal, access, name, descriptor, true, false)
}
if (name == "signalAll") {
return MethodEnterVisitor(mv, Runtime::onConditionSignalAll, true)
return MethodEnterVisitor(mv, Runtime::onConditionSignalAll, access, name, descriptor, true, false)
}
return mv
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ class LockInstrumenter(cv:ClassVisitor): ClassVisitorBase(cv, ReentrantLock::cla
exceptions: Array<out String>?
): MethodVisitor {
if (name == "tryLock") {
return MethodEnterVisitor(mv, Runtime::onLockTryLock, true)
return MethodEnterVisitor(mv, Runtime::onLockTryLock, access, name, descriptor, true, false)
}
if (name == "lock" || name == "lockInterruptibly") {
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onLockLock, true),
Runtime::onLockLockDone, access, name, descriptor, true)
val eMv = MethodEnterVisitor(mv, Runtime::onLockLock, access, name, descriptor, true, false)
return MethodExitVisitor(eMv, Runtime::onLockLockDone, access, name, descriptor, true, false)
}
if (name == "unlock") {
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onLockUnlock, true),
Runtime::onLockUnlockDone, access, name, descriptor, true)
val eMv = MethodEnterVisitor(mv, Runtime::onLockUnlock, access, name, descriptor, true, false)
return MethodExitVisitor(eMv, Runtime::onLockUnlockDone, access, name, descriptor, true, false)
}
if (name == "newCondition") {
return NewConditionVisitor(mv, Runtime::onLockNewCondition, access, name, descriptor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,12 @@ class LockSupportInstrumenter(cv: ClassVisitor): ClassVisitorBase(cv, LockSuppor
): MethodVisitor {
var mv = super.instrumentMethod(mv, access, name, descriptor, signature, exceptions)
if (name.startsWith("park")) {
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onThreadPark, false), Runtime::onThreadParkDone, access, name, descriptor, false)
val eMv = MethodEnterVisitor(mv, Runtime::onThreadPark, access, name, descriptor, false, false)
return MethodExitVisitor(eMv, Runtime::onThreadParkDone, access, name, descriptor, false, false)
}
if (name.startsWith("unpark")) {
return object: AdviceAdapter(ASM9, mv, access, name, descriptor) {
override fun onMethodEnter() {
loadArg(0)
mv.visitMethodInsn(INVOKESTATIC,
Runtime::class.java.name.replace('.', '/'),
Runtime::onThreadUnpark.name,
Utils.kFunctionToJvmMethodDescriptor(Runtime::onThreadUnpark),
false)
super.onMethodEnter()
}

override fun onMethodExit(opcode: Int) {
loadArg(0)
mv.visitMethodInsn(INVOKESTATIC,
Runtime::class.java.name.replace('.', '/'),
Runtime::onThreadUnparkDone.name,
Utils.kFunctionToJvmMethodDescriptor(Runtime::onThreadUnparkDone),
false)
super.onMethodExit(opcode)
}
}
val eMv = MethodEnterVisitor(mv, Runtime::onThreadUnpark, access, name, descriptor, false, true)
return MethodExitVisitor(eMv, Runtime::onThreadUnparkDone, access, name, descriptor, false, true)
}
return mv
}
Expand Down
Loading

0 comments on commit dbbee86

Please sign in to comment.