Skip to content

Commit dbbee86

Browse files
committed
Add semaphore test.
1 parent 7274e60 commit dbbee86

File tree

25 files changed

+512
-69
lines changed

25 files changed

+512
-69
lines changed

core/src/main/kotlin/cmu/pasta/sfuzz/core/GlobalContext.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmu.pasta.sfuzz.core
33
import cmu.pasta.sfuzz.core.concurrency.locks.LockManager
44
import cmu.pasta.sfuzz.core.concurrency.SFuzzThread
55
import cmu.pasta.sfuzz.core.concurrency.SynchronizationManager
6+
import cmu.pasta.sfuzz.core.concurrency.locks.SemaphoreManager
67
import cmu.pasta.sfuzz.core.concurrency.operations.*
78
import cmu.pasta.sfuzz.core.logger.LoggerBase
89
import cmu.pasta.sfuzz.core.runtime.AnalysisResult
@@ -15,6 +16,7 @@ import cmu.pasta.sfuzz.runtime.MemoryOpType
1516
import cmu.pasta.sfuzz.runtime.Runtime
1617
import cmu.pasta.sfuzz.runtime.TargetTerminateException
1718
import java.util.concurrent.Executors
19+
import java.util.concurrent.Semaphore
1820
import java.util.concurrent.locks.Condition
1921
import java.util.concurrent.locks.ReentrantLock
2022
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock
@@ -29,6 +31,7 @@ object GlobalContext {
2931
var scheduler: Scheduler = FifoScheduler()
3032
var config: Configuration? = null
3133
private val lockManager = LockManager()
34+
private val semaphoreManager = SemaphoreManager()
3235
private val volatileManager = VolatileManager()
3336
val syncManager = SynchronizationManager()
3437
val loggers = mutableListOf<LoggerBase>()
@@ -393,7 +396,7 @@ object GlobalContext {
393396
fun log(format: String, vararg args: Any) {
394397
val tid = Thread.currentThread().id
395398
val context = registeredThreads[tid]!!
396-
val data = "[${context.index}]: ${String.format(format, args)}"
399+
val data = "[${context.index}]: ${String.format(format, args)}\n"
397400
for (logger in loggers) {
398401
logger.applicationEvent(data)
399402
}
@@ -449,6 +452,26 @@ object GlobalContext {
449452
lockManager.registerNewCondition(condition, lock)
450453
}
451454

455+
fun semaphoreInit(sem: Semaphore) {
456+
semaphoreManager.init(sem)
457+
}
458+
459+
fun semaphoreAcquire(sem: Semaphore, permits: Int, shouldBlock: Boolean): Boolean {
460+
return semaphoreManager.acquire(sem, permits, shouldBlock)
461+
}
462+
463+
fun semaphoreRelease(sem: Semaphore, permits: Int) {
464+
semaphoreManager.release(sem, permits)
465+
}
466+
467+
fun semaphoreDrainPermits(sem: Semaphore): Int {
468+
return semaphoreManager.drainPermits(sem)
469+
}
470+
471+
fun semaphoreReducePermits(sem: Semaphore, permits: Int) {
472+
semaphoreManager.reducePermits(sem, permits)
473+
}
474+
452475
fun fieldOperation(obj: Any?, owner: String, name: String, type: MemoryOpType) {
453476
if (!volatileManager.isVolatile(owner, name)) return
454477
val objIds = mutableListOf<Int>()

core/src/main/kotlin/cmu/pasta/sfuzz/core/RuntimeDelegate.kt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import cmu.pasta.sfuzz.core.concurrency.SFuzzThread
44
import cmu.pasta.sfuzz.runtime.Delegate
55
import cmu.pasta.sfuzz.runtime.MemoryOpType
66
import cmu.pasta.sfuzz.runtime.TargetTerminateException
7+
import java.util.concurrent.Semaphore
78
import java.util.concurrent.locks.Condition
89
import java.util.concurrent.locks.ReentrantLock
910
import java.util.concurrent.locks.ReentrantReadWriteLock
@@ -278,6 +279,68 @@ class RuntimeDelegate: Delegate() {
278279
entered.set(false)
279280
}
280281

282+
override fun onSemaphoreInit(sem: Semaphore) {
283+
if (checkEntered()) return
284+
GlobalContext.semaphoreInit(sem)
285+
entered.set(false)
286+
}
287+
288+
override fun onSemaphoreAcquire(sem: Semaphore, permits: Int) {
289+
if (checkEntered()) {
290+
skipFunctionEntered.set(1 + skipFunctionEntered.get())
291+
return
292+
}
293+
GlobalContext.semaphoreAcquire(sem, permits, true)
294+
entered.set(false)
295+
skipFunctionEntered.set(skipFunctionEntered.get() + 1)
296+
}
297+
298+
override fun onSemaphoreAcquireDone() {
299+
skipFunctionEntered.set(skipFunctionEntered.get() - 1)
300+
}
301+
302+
override fun onSemaphoreRelease(sem: Semaphore, permits: Int) {
303+
if (checkEntered()) {
304+
skipFunctionEntered.set(1 + skipFunctionEntered.get())
305+
return
306+
}
307+
GlobalContext.semaphoreRelease(sem, permits)
308+
entered.set(false)
309+
skipFunctionEntered.set(skipFunctionEntered.get() + 1)
310+
}
311+
312+
override fun onSemaphoreReleaseDone() {
313+
skipFunctionEntered.set(skipFunctionEntered.get() - 1)
314+
}
315+
316+
override fun onSemaphoreDrainPermits(sem: Semaphore) {
317+
if (checkEntered()) {
318+
skipFunctionEntered.set(1 + skipFunctionEntered.get())
319+
return
320+
}
321+
GlobalContext.semaphoreDrainPermits(sem)
322+
entered.set(false)
323+
skipFunctionEntered.set(skipFunctionEntered.get() + 1)
324+
}
325+
326+
override fun onSemaphoreDrainPermitsDone() {
327+
skipFunctionEntered.set(skipFunctionEntered.get() - 1)
328+
}
329+
330+
override fun onSemaphoreReducePermits(sem: Semaphore, permits: Int) {
331+
if (checkEntered()) {
332+
skipFunctionEntered.set(1 + skipFunctionEntered.get())
333+
return
334+
}
335+
GlobalContext.semaphoreReducePermits(sem, permits)
336+
entered.set(false)
337+
skipFunctionEntered.set(skipFunctionEntered.get() + 1)
338+
}
339+
340+
override fun onSemaphoreReducePermitsDone() {
341+
skipFunctionEntered.set(skipFunctionEntered.get() - 1)
342+
}
343+
281344
override fun start() {
282345
// For the first thread, it is not registered.
283346
// Therefor we cannot call `checkEntered` here.

core/src/main/kotlin/cmu/pasta/sfuzz/core/concurrency/locks/LockManager.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock
99
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock
1010

1111
class LockManager {
12-
val lockContextManager = LockContextManager()
13-
12+
val lockContextManager = ReferencedContextManager<LockContext> { ReentrantLockContext() }
1413
val waitingThreads = mutableMapOf<Int, MutableList<Long>>()
15-
1614
val conditionToLock = mutableMapOf<Condition, Lock>()
1715
val lockToConditions = mutableMapOf<Lock, MutableList<Condition>>()
1816

@@ -22,12 +20,10 @@ class LockManager {
2220

2321
fun reentrantReadWriteLockInit(readLock: ReadLock, writeLock: WriteLock) {
2422
val context = ReentrantReadWriteLockContext()
25-
lockContextManager.addLockContext(readLock, context)
26-
lockContextManager.addLockContext(writeLock, context)
23+
lockContextManager.addContext(readLock, context)
24+
lockContextManager.addContext(writeLock, context)
2725
}
2826

29-
// fun addLockContext(lock)
30-
3127
/**
3228
* Return true if [lock] is acquired by the current thread.
3329
*/
@@ -92,6 +88,6 @@ class LockManager {
9288
assert(waitingThreads.isEmpty())
9389
conditionToLock.clear()
9490
lockToConditions.clear()
95-
// lockContextManager.done()
91+
lockContextManager.done()
9692
}
9793
}

core/src/main/kotlin/cmu/pasta/sfuzz/core/concurrency/locks/LockContextManager.kt renamed to core/src/main/kotlin/cmu/pasta/sfuzz/core/concurrency/locks/ReferencedContextManager.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,34 @@ import java.lang.ref.ReferenceQueue
77
class IdentityPhantomReference<T>(referent: T, queue: ReferenceQueue<in T>) : PhantomReference<T>(referent, queue) {
88
val id = System.identityHashCode(referent)
99
}
10-
class LockContextManager {
10+
class ReferencedContextManager<T>(val contextProducer: (Any) -> T) {
1111
val queue = ReferenceQueue<Any>()
12-
val lockMap = mutableMapOf<Int, LockContext>()
13-
fun getLockContext(lock: Any): LockContext {
12+
val lockMap = mutableMapOf<Int, T>()
13+
fun getLockContext(lock: Any): T {
1414
val id = System.identityHashCode(lock)
1515
if (!lockMap.containsKey(id)) {
16-
lockMap[id] = ReentrantLockContext()
16+
lockMap[id] = contextProducer(lock)
1717
IdentityPhantomReference(lock, queue)
1818
gc()
1919
}
2020
return lockMap[id]!!
2121
}
2222

23-
fun addLockContext(lock: Any, context: LockContext) {
23+
fun addContext(lock: Any, context: T) {
2424
val id = System.identityHashCode(lock)
2525
lockMap[id] = context
2626
IdentityPhantomReference(lock, queue)
2727
gc()
2828
}
2929

3030
fun done() {
31-
lockMap.clear()
31+
gc()
3232
}
3333

3434
fun gc() {
3535
var ref = queue.poll()
3636
while (ref != null) {
3737
val id = (ref as IdentityPhantomReference<*>).id
38-
assert(lockMap[id]!!.isEmpty())
3938
lockMap.remove(id)
4039
ref = queue.poll()
4140
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package cmu.pasta.sfuzz.core.concurrency.locks
2+
3+
import cmu.pasta.sfuzz.core.GlobalContext
4+
import cmu.pasta.sfuzz.core.ThreadState
5+
import cmu.pasta.sfuzz.core.concurrency.operations.ThreadResumeOperation
6+
7+
class SemaphoreContext(var totalPermits: Int) {
8+
private val lockWaiters = mutableMapOf<Long, Int>()
9+
10+
fun acquire(permits: Int, shouldBlock: Boolean): Boolean {
11+
if (totalPermits > permits) {
12+
totalPermits -= permits
13+
return true
14+
} else if (shouldBlock) {
15+
lockWaiters[Thread.currentThread().id] = permits
16+
}
17+
return false
18+
}
19+
20+
fun drainPermits(): Int {
21+
val permits = totalPermits
22+
totalPermits = 0
23+
return permits
24+
}
25+
26+
fun release(permits: Int) {
27+
totalPermits += permits
28+
if (totalPermits > 0) {
29+
val it = lockWaiters.iterator()
30+
while (it.hasNext()) {
31+
val (tid, p) = it.next()
32+
if (totalPermits >= p) {
33+
GlobalContext.registeredThreads[tid]!!.pendingOperation = ThreadResumeOperation()
34+
GlobalContext.registeredThreads[tid]!!.state = ThreadState.Enabled
35+
lockWaiters.remove(tid)
36+
}
37+
}
38+
}
39+
}
40+
41+
fun reducePermits(permits: Int) {
42+
totalPermits -= permits
43+
}
44+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package cmu.pasta.sfuzz.core.concurrency.locks
2+
3+
import java.util.concurrent.Semaphore
4+
5+
class SemaphoreManager {
6+
val lockContextManager = ReferencedContextManager {it ->
7+
if (it is Semaphore) {
8+
SemaphoreContext(it.availablePermits())
9+
} else {
10+
throw IllegalArgumentException("SemaphoreManager can only manage Semaphore objects")
11+
}
12+
}
13+
14+
fun init(sem: Semaphore) {
15+
val context = SemaphoreContext(sem.availablePermits())
16+
lockContextManager.addContext(sem, context)
17+
}
18+
19+
fun acquire(sem: Semaphore, permits: Int, shouldBlock: Boolean): Boolean {
20+
return lockContextManager.getLockContext(sem).acquire(permits, shouldBlock)
21+
}
22+
23+
fun release(sem: Semaphore, permits: Int) {
24+
lockContextManager.getLockContext(sem).release(permits)
25+
}
26+
27+
fun drainPermits(sem: Semaphore): Int {
28+
return lockContextManager.getLockContext(sem).drainPermits()
29+
}
30+
31+
fun reducePermits(sem: Semaphore, permits: Int) {
32+
lockContextManager.getLockContext(sem).reducePermits(permits)
33+
}
34+
}

instrumentation/src/main/kotlin/cmu/pasta/sfuzz/instrumentation/JDKInstrumenter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ fun instrumentClass(path:String, inputStream: InputStream): ByteArray {
3131
cv = UnsafeInstrumenter(cv)
3232
cv = ClassloaderInstrumenter(cv)
3333
cv = ObjectInstrumenter(cv)
34+
cv = SemaphoreInstrumenter(cv)
3435
// MonitorInstrumenter should come second because ObjectInstrumenter will insert more
3536
// monitors.
3637
cv = MonitorInstrumenter(cv)
3738
// SynchronizedMethodEmbeddingInstrumenter should come before MonitorInstrumenter because
3839
// it inlines monitors for synchronized methods.
3940
// cv = SynchronizedMethodInstrumenter(cv)
4041
classReader.accept(cv, ClassReader.EXPAND_FRAMES)
41-
var out = classWriter.toByteArray()
42+
val out = classWriter.toByteArray()
4243
File("/tmp/out/jdk/${path.replace("/", ".").removePrefix(".")}").writeBytes(out)
4344
// File("/tmp/${path.replace("/", ".").removePrefix(".")}").writeBytes(out)
4445
return out

instrumentation/src/main/kotlin/cmu/pasta/sfuzz/instrumentation/visitors/ClassloaderInstrumenter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class ClassloaderInstrumenter(cv: ClassVisitor): ClassVisitor(ASM9, cv) {
3030
val mv = super.visitMethod(access, name, descriptor, signature, exceptions)
3131
if ((name == "loadClass" && descriptor == "(Ljava/lang/String;)Ljava/lang/Class;" && className == "java/lang/ClassLoader") ||
3232
(name == "makeImpl" && className == "java/lang/invoke/MethodType")) {
33-
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onLoadClass, false), Runtime::onLoadClassDone, access, name, descriptor, false)
33+
val eMv = MethodEnterVisitor(mv, Runtime::onLoadClass, access, name, descriptor, false, false)
34+
return MethodExitVisitor(eMv, Runtime::onLoadClassDone, access, name, descriptor, false, false)
3435
}
3536
return mv
3637
}

instrumentation/src/main/kotlin/cmu/pasta/sfuzz/instrumentation/visitors/ConditionInstrumenter.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ class ConditionInstrumenter(cv:ClassVisitor): ClassVisitorBase(cv, ConditionObje
1616
exceptions: Array<out String>?
1717
): MethodVisitor {
1818
if (name.startsWith("await")) {
19-
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onConditionAwait, true),
20-
Runtime::onConditionAwaitDone, access, name, descriptor, true)
19+
val eMv = MethodEnterVisitor(mv, Runtime::onConditionAwait, access, name, descriptor, true, false)
20+
return MethodExitVisitor(eMv, Runtime::onConditionAwaitDone, access, name, descriptor, true, false)
2121
}
2222
if (name == "signal") {
23-
return MethodEnterVisitor(mv, Runtime::onConditionSignal, true)
23+
return MethodEnterVisitor(mv, Runtime::onConditionSignal, access, name, descriptor, true, false)
2424
}
2525
if (name == "signalAll") {
26-
return MethodEnterVisitor(mv, Runtime::onConditionSignalAll, true)
26+
return MethodEnterVisitor(mv, Runtime::onConditionSignalAll, access, name, descriptor, true, false)
2727
}
2828
return mv
2929
}

instrumentation/src/main/kotlin/cmu/pasta/sfuzz/instrumentation/visitors/LockInstrumenter.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ class LockInstrumenter(cv:ClassVisitor): ClassVisitorBase(cv, ReentrantLock::cla
2222
exceptions: Array<out String>?
2323
): MethodVisitor {
2424
if (name == "tryLock") {
25-
return MethodEnterVisitor(mv, Runtime::onLockTryLock, true)
25+
return MethodEnterVisitor(mv, Runtime::onLockTryLock, access, name, descriptor, true, false)
2626
}
2727
if (name == "lock" || name == "lockInterruptibly") {
28-
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onLockLock, true),
29-
Runtime::onLockLockDone, access, name, descriptor, true)
28+
val eMv = MethodEnterVisitor(mv, Runtime::onLockLock, access, name, descriptor, true, false)
29+
return MethodExitVisitor(eMv, Runtime::onLockLockDone, access, name, descriptor, true, false)
3030
}
3131
if (name == "unlock") {
32-
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onLockUnlock, true),
33-
Runtime::onLockUnlockDone, access, name, descriptor, true)
32+
val eMv = MethodEnterVisitor(mv, Runtime::onLockUnlock, access, name, descriptor, true, false)
33+
return MethodExitVisitor(eMv, Runtime::onLockUnlockDone, access, name, descriptor, true, false)
3434
}
3535
if (name == "newCondition") {
3636
return NewConditionVisitor(mv, Runtime::onLockNewCondition, access, name, descriptor)

instrumentation/src/main/kotlin/cmu/pasta/sfuzz/instrumentation/visitors/LockSupportInstrumenter.kt

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,12 @@ class LockSupportInstrumenter(cv: ClassVisitor): ClassVisitorBase(cv, LockSuppor
1717
): MethodVisitor {
1818
var mv = super.instrumentMethod(mv, access, name, descriptor, signature, exceptions)
1919
if (name.startsWith("park")) {
20-
return MethodExitVisitor(MethodEnterVisitor(mv, Runtime::onThreadPark, false), Runtime::onThreadParkDone, access, name, descriptor, false)
20+
val eMv = MethodEnterVisitor(mv, Runtime::onThreadPark, access, name, descriptor, false, false)
21+
return MethodExitVisitor(eMv, Runtime::onThreadParkDone, access, name, descriptor, false, false)
2122
}
2223
if (name.startsWith("unpark")) {
23-
return object: AdviceAdapter(ASM9, mv, access, name, descriptor) {
24-
override fun onMethodEnter() {
25-
loadArg(0)
26-
mv.visitMethodInsn(INVOKESTATIC,
27-
Runtime::class.java.name.replace('.', '/'),
28-
Runtime::onThreadUnpark.name,
29-
Utils.kFunctionToJvmMethodDescriptor(Runtime::onThreadUnpark),
30-
false)
31-
super.onMethodEnter()
32-
}
33-
34-
override fun onMethodExit(opcode: Int) {
35-
loadArg(0)
36-
mv.visitMethodInsn(INVOKESTATIC,
37-
Runtime::class.java.name.replace('.', '/'),
38-
Runtime::onThreadUnparkDone.name,
39-
Utils.kFunctionToJvmMethodDescriptor(Runtime::onThreadUnparkDone),
40-
false)
41-
super.onMethodExit(opcode)
42-
}
43-
}
24+
val eMv = MethodEnterVisitor(mv, Runtime::onThreadUnpark, access, name, descriptor, false, true)
25+
return MethodExitVisitor(eMv, Runtime::onThreadUnparkDone, access, name, descriptor, false, true)
4426
}
4527
return mv
4628
}

0 commit comments

Comments
 (0)