Java Path Finder
December 17, 2011 Leave a comment
Java Path Finder(JPF) is a model checker for Java programs and can check concurrent programs for deadlocks and data races which are more serious in muti-core environments. Java threads used by concurrent programs are context switched by the OS scheduler at run-time when we ‘yield’ a thread or due to some other reason. Even though it might be possible to test these scenarios extensively by profiling a few possible combinations of thread interleavings, it is quite tedious. So JPF tests this type of code by exploring all the code using algorithms to find errors.
JPF has quite a number of plug points and gov.nasa.jpf.jvm.VMListener is one such interface. I have implemented it to get data about the number of BLOCKED threads that the System Under Test(SUT) has and the monitor that they are blocked at. The data is still not clear but it is reasonable. I am not able to get clear data showing how many threads are blocked for a particular monitor to be released at a point in time. I think the code has to be refined.
JPF can be downloaded from http://babelfish.arc.nasa.gov/trac/jpf/ by using a mercurial eclipse plugin and it is quite easy to build and use.
I implemented gov.nasa.jpf.jvm.VMListener and added the classpath entry pointing to the class file to jpf-core.native_classpath
gov.nasa.jpf.jvm.VMListener based on JPF source code
public class LockContentionListener implements VMListener{ @Override public void threadStarted(JVM vm) { printLocks( vm ); } /** * Thread and lock status. * @param vm */ private void printLocks( JVM jvm ){ StringBuffer locksAndThreads = new StringBuffer(); for ( ThreadInfo tf : jvm.getThreadList().getThreads()) { ElementInfo ei = tf.getLockObject(); if (ei != null) { if (tf.getState() == ThreadInfo.State.WAITING || tf.getState() == ThreadInfo.State.BLOCKED ) { locksAndThreads.append( "\n"); locksAndThreads.append(tf.getStateDescription()); locksAndThreads.append(ei); locksAndThreads.append(" \n Lock Count (" + ei.getMonitor().getNumberOfBlockedThreads()+ ")"); LinkedList locks = tf.getLockedObjects(); if (locks.isEmpty()) { locksAndThreads.append(" call stack:"); for (StackFrame frame : tf){ if (!frame.isDirectCallFrame()) { locksAndThreads.append("\tat \n "); locksAndThreads.append(frame.getStackTraceInfo()); } } } System.out.println( "Locks owned by Threads[ " + locksAndThreads + " \n ]"); } } } }
SUT
public class LockContention{ private static final int NUM_THREADS = 2; public static void main( String... argv ) throws InterruptedException { Thread[] readThreads = new Thread[ NUM_THREADS ]; Locker locker = new Locker(); for( int i = 0 ; i < readThreads.length; i ++ ){ readThreads[ i ] = new Thread( new ReadLockContention( locker ) ){ public String getName(){ return "LockContention (Reader)[ " + getId() + " ]"; } }; } Thread[] writeThreads = new Thread[ NUM_THREADS ]; for( int i = 0 ; i < writeThreads.length; i ++ ){ writeThreads[ i ] = new Thread( new WriteLockContention( locker ) ){ public String getName(){ return "LockContention (Writer)[" + getId() + " ]"; } }; } for( Thread t : readThreads ){ t.start(); } for( Thread t : writeThreads ){ t.start(); } for( Thread t : readThreads ){ t.join(); } for( Thread t : writeThreads ){ t.join(); } } } class ReadLockContention implements Runnable{ private Locker locker; public ReadLockContention( Locker locker ){ this.locker = locker; } @Override public void run() { locker.contendForReadLock(); } } class WriteLockContention implements Runnable{ private Locker locker; public WriteLockContention( Locker locker ){ this.locker = locker; } @Override public void run() { locker.contendForWriteLock(); } } class Locker{ private final String lock = new String(); /** * Consider this as a read lock */ void contendForReadLock(){ synchronized( lock ){ try { Thread.sleep(50000); } catch (InterruptedException e) { System.out.println( "InterruptedException" ); } } } /** * Consider this as a write lock */ void contendForWriteLock(){ synchronized( lock ){ } } }
Output
The command
jpf +listener+=LockContentionListener LockContention.jpf
produces
Threads blocked on Locks[
thread id=2,name=Thread-2,status=BLOCKED,priority=5,lockCount=0,suspendCount=0java.lang.String@137
Lock Count (1) call stack: at
Locker.contendForReadLock(LockContention.java:97) at
ReadLockContention.run(LockContention.java:67)
]
Threads blocked on Locks[
thread id=1,name=Thread-1,status=BLOCKED,priority=5,lockCount=0,suspendCount=0java.lang.String@137
Lock Count (1) call stack: at
Locker.contendForReadLock(LockContention.java:97) at
ReadLockContention.run(LockContention.java:67)
]
The monitor is shown in bold. I want to know how many threads are blocked for a lock to be released.
It seems that these messages indicate that two threads(Thread-1,Thread-2) are blocked on the same monitor but I think this has to be investigated further. There is no reply to my forum question about this.