Model Checking Java Programs Using Java Pathfinder: Tools
Model Checking Java Programs Using Java Pathfinder: Tools
Model Checking Java Programs Using Java Pathfinder: Tools
Software
be Tools
inserted byfor theTechnology
editor) Transfer manuscript No.
Verify.assert(received[i].attr == i);}
2.3 Property Specifications
JPF allows a programmer to annotate his JAVA program Fig. 5. The JAVA Producer and Consumer classes
with assertions and verify them using the SPIN model
checker. In addition, deadlocks can be identified. An as-
class Verify{
sert statement is expressed as a call to the static method
public static void assert(boolean b){
assert in the Verify class shown in Figure 6. The fact
if (!b)
that it is static means that we can call this method di-
System.out.println("assertion broken");
rectly using the class name Verify as prefix, without }
making an object instance first. The body of this method
is of no real importance for the verification since only
Fig. 6. The JAVA Verify class
the call of this method (and not its definition) will be
translated into a corresponding PROMELA assert state-
ment. A meaningful body, like print!ng an error message
as in this example, can be useful during normal testing One can consider other kinds of Verify methods, in
though, but it will not be translated into PROMELA. general methods corresponding to the operators in LTL,
The first assertion in Figure 5 states that the con- the linear temporal logic of SPIN. Since these methods
sumer receives exactly 6 elements from the buffer. The can be called wherever statements can occur, this kind of
second assertion within the for loop states that the re- logic represents what could be called an embedded tempo-
ceived elements are the correct ones (at least wrt. the ral logic. As an example, one could consider a statement
attr value). of the form: Verify.eventually(count == 6) inserted
#define Index byte
#define Attribute 6
#define MAX_OBJECT 7
#define AttrData 7
typedef Attribute_Class{
int attr;
#define create object(c,i)
};
(c*100 + i)
Attribute_Class Attribute_Obj[MAX_OBJECT];
Index Attribute_Next = O;
#define get_class(x)
(x/100)
typedef AttrData_Class{
int attr;
#define get_index(x)
int data;
(x - ((x/100)*t00))
};
AttrData_Class AttrData_Obj[MAX_OBJECT]; Fig. 8. GenerM object re_rence operations
Index AttrData_Next = O;
#define Buffer_incr_WAITING(obj)
#define AttrData_get_data(obj) Buffer_Obj[get_index(obj)].WAITING++
AttrData_Obj[get_index(obj)].data
#define Buffer_decr_WAITING(obj)
#define AttrData_set_data(obj,value) Buffer_Obj[get_index(obj)].WAITING--
AttrData_Obj[get_index(obj)].data = value
Fig. 13. Operations on WAITING
Fig. 9. Variable accessors _rthe Attribute and AttrData classes
#define Attribute_constr(obj,attr)
ables LOCK, WAITING and WAIT used for managing ob-
Attribute_set_attr(obj,attr);
ject locking and release. The LOCK variable will at any
#define AttrData_constr(oBj,attr,data) time either be null (a negative integer) or it will be the
Attribute_constr(obj,attr); thread id of the thread that currently is executing a syn-
AttrData_set_data(obj,data); chronized method on the object (actually a non-negative
PROMELA process id). Hence, once this field is set to a
Fig. 10. Constructors _rthe Attribute and AttrData classes
proper thread id by a thread that calls a synchronized
method, only the thread with this thread id is allowed to
atomic{
operate on the object. When the call of the synchronized
x = create_object(AttrData,AttrData_Next);
method terminates, the lock is released by setting it to
AttrData_Next++
}; null again. Note that a synchronized method must be
allowed to call other synchronized methods on the same
AttrData_constr(x,2,4);
object without causing blocking.
Fig. 11. W rans|ation ofx = new AttrData(2,4)
The variables WAITING and WAIT are used to man-
age threads that call the wait, notify, and notifyAll
methods on the object. A thread that calls wait() will
mented, and the constructor is applied to the reference first unlock the object, and then try to read a value on
now stored in x.
the zero place channel WAIT. A zero place channel in
PROMELA is used to model rendezvous communication,
3.2 Synchronization on Objects hence some other thread must send a value on this chan-
nel in order for the calling thread to be released. At any
The Buffer class is more complicated because it con- time, all threads that are waiting to get access to the
tains methods, and because these in addition are syn- object are waiting on this channel. Each time a thread
chronized, which means that they must have exclusive calls the wait () method on the object, the WAITING vari-
access to the object when executing. That is, only one able is additionally incremented, and conversely decre-
thread at a time may execute any synchronized method mented when released. Hence, the value of this variable
on the same object. Therefore, the translation must pro- will always be the number of threads waiting on the ob-
vide a locking mechanism with which one can lock an ject. The variable is used when notifyAll is called by a
object to serve a particular thread that calls a synchro- thread, and all waiting threads have to be released: we
nized method on the object. For that purpose some extra need to know how many times the WAIT channel must
variables are inserted into the data area of each object, be signaled. The operations on the WAITING variable are
as shown in Figure 12. In addition to the variables de- shown in Figure 13.
clared in the class (note that JAVA arrays are modeled Locking and unlocking an object is implemented by
directly as PROMELA arrays), it contains the three vari- the operations shown in Figure 14. Locking an object
#define continue 0
#define this _pid
#define Buffer_wait(obj)
#define Buffer_lock(obj)
atomic{ atomic{
Buffer_set_LOCK(obj,null) }
unless{
Fig. 14. Operations _rlocking and unlocking objects d_step{
value of '%his" to the LOCK field, where "this" denotes :: Buffer_any_WAITING(obj) ->
Buffer_get_WAIT(obj)!continue;
the thread id of the current thread ("this" is defined as
Buffer_decr_WAITING(obj)
_pid, which in PROMELA refers to the process id of the
:: else -> skip
currently executing process). Unlocking an object just
fi}
corresponds to setting the LOCK field to null.
Finally, the macros modeling the wait, notify and #define Buffer_notifyAll(obj)
notifyAll methods are shown in Figure 15. The atomic{
Buffer_wait macro models the wait method. Ignore do
for a moment tim unless constructs. When called, it :: Buffer_any_WAITING(obj) ->
if {
int Producer_run_i;
:: Buffer_get_LOCK(obj) == this ->
Buffer_halt_code(obj) ObjRef Producer_run_ad;
fi atomic{
Producer_run_ad =
Buffer_set_halted(obj,true); AttrData_Next++};
Buffer_notifyAll(obj) AttrData_constr(Producer_run_ad,
Producer_run_i,
Fig. 16. Trans]ation ofthe halt method (Producer_run_i * Producer_run_i));
Buffer_put(Producer_get_buffer(obj),
Producer_run_ad);
Producer_run_i = (Producer_run_i + i);
3.3 Methods
:: else -> break
od;
In order to illustrate how methods are modeled, we illus- Buffer_halt(Producer_get_buffer(obj));
trate the simplest one, namely the halt method. A syn- }
chronized method M in a class ¢ is defined in two parts: unless {get_this_EXN > O}
a ¢_M macro dealing with object locking, and a C_M_code
macro containing the actual code of the method. For Fig. 1T. The Producer run method
non-synchronized methods there is no such distinction.
This is shown for the synchronized halt method in Fig-
ure 16. In general, a method call is translated into a call
of the ¢_M macro with parameters corresponding to the
method parameters. In the case of halt, the method is The process takes as parameter its own object ref-
parameterless, hence only tile object reference of the ob- erence. Notice how local variables in called methods are
ject upon which the method is called is given as parame- declared as global variables in the process. For example,
ter. The macro tests whether the object is already locked the variable ex in the put method that binds the caught
by the calling thread, in which case the actual code, exception in the try statement (Figure 2) is declared
C_M_code, is executed. This models the situation where at this point. This is necessary because PROMELA does
this method is called by another synchronized method, not support local variables (nor does it in the new inline
hence the object is already locked. In case the object procedures that PROMELA got recently). This solution
is not locked by the calling thread, an attempt is made of course prevents the translation of recursive methods,
to lock it (Buffer_lock (obj) ), and after successful lock- but it is efficient with respect to verification time since
ing, the code is executed. The try macro models tile try we don't need to maintain a call stack (and represents
construct of JAVA and models the fact that the object what one normally would do in a hand translation). An-
must always be unlocked (Buffer_unlock(obj)) when other possibility is to translate methods into proctypes,
leaving the method, also in the case where an exception as suggested in [15], but experiences during earlier work
is raised during execution of the code. suggested that this would be inefficient, see [9]. The while
loop is translated into PROMELA'S do ... od construct
> 0 is still true) and x gets the value 2 anyway. Hence, another thread, causing that other thread's EXN variable
to be set. The throw macro throws an exception to the
we have to prevent that kind of behavior. Note that the
above PROMELA code needs some modifications to ac- object identified by the free variable obj which is this
tually "work" because if it is the last statement in "P" current thread object. Finally, calling "T. stop()" in a
within the statement "P unless Q" that makes "Q" ex- JAVA program corresponds to throwing the predefined
ecutable, then "0" will actually not get executed. THREAD_DEATH exception object to the T object.
Recall that a JAVA exception is an object of one of We shall now explain how exceptions are caught.
the exception classes, either one of the built in or a user Consider a try statement of the form:
#define try(s)
{ try(Buffer_wait(obj))
unless { {s;exit_to_final;skip}
if
#define exit_to_final
:: catch(exn_InterruptedException,
throw(EXIT)
Buffer_get_ex,skip)
fi};
#define catch(exn_E,x,s)
}
catch_cond(exn_E,x) -> s;exit_to_final
unless {finally(skip)};
Fig. 22. Trans|ation ofthe try statementin the get method #define catch_cond(exn_E,e)
d_step{
(exn_E)
->
try T
e = get_this_EXN;
catch(El el) Cl
set_this_EXN(NO_EXN)
catch(E2 e2) C2
}
catch(En en) Cn
#define finally(s)
finally F final_cond -> s;exit_final;skip
if set_this_EXN(- get_this_EXN)
:: catch(exn_El,el,Cl') fi}
:: catch(exn_E2 & !exn_El,e2,C2')
#define exit_final
set_this_EXN(- get_this_EXN)
catch(exn_En & !exn_El & !exn_E2 &
... _ !exn_En-l,en,Cn') Fig. 23. Operations _rcatching exceptions
fi}
}
unless {finally(F')}
be accessible within the block to be executed. Note also
how the EXN variable is zeroed to the predefined NO_EXN
For example, the try construct in the get method
value in order to avoid outer unless constructs being
in Figure 2 is translated into the PROMELA code in Fig-
triggered, as already discussed earlier on page 10.
ure 22. The general idea behind this translation is that
the inner unless construct catches any exceptions which The finally (s) macro defines when the outer unless
match any one of the exception predicates exn v.1 ... construct should be triggered, namely when f inal_cond
exn_En, and that the outer unless construct models the becomes executable. The f inal_cond macro is executable
finally construct in JAVA: the block F must be exe- whenever there is some exception object reference in the
cuted no matter what as the last thing. For example, EXN variable (it is bigger than 0). Note that we reset this
in the case where for example C l' itself throws a new to 0 (N0_EXN) whenever an exception is caught at an in-
exception, the F still has to be executed before that ex- ner level so that this outer final construct will not get
ception can be thrown further up. Now, let us explain activated. If there is an exception, and this is just a nor-
the macros in Figure 23. real EXIT from one of the blocks T,C1 ..... Cn in the try
The try(s) macro executes s and then throws the statement, this is then forgotten (set_this_EXN (N0_EXN)).
predefined EXIT object (exit_to_final). The EXIT ex- If on the other hand (else) it concerns an exception
ception is then caught by the finally macro (always in- that has not been caught, we need to remember it so
serted at the end) as we shall see. The catch (exn_E, x, s) that it can be re-thrown after the code in the finally
macro tests whether the current value of the EXN variable construct has been executed. We also need to make sure
satisfies the exn_E predicate (catch_cond (exn_E, x)), in that it does not activate unless constructs higher up
which case that branch is executed. during the execution of the finally code. One way to
The catch_cond (exn_E, e) macro in an atomic move do this is to negate it since the unless constructs in the
tests the predicate exn_E (hence is only executable when translation only react on positive values of EXN. When
it is true, see Figure 20 for one such predicate), and then we then leave the finally construct, we negate it back
stores the value of the exception in the local variable e to to its positive value such that it can be "thrown" fi_r-
#define return_with(e)
#define Buffer_get_code(obj)
{ RES[TOP] = e;
throw(RETURN)
return_with(Buffer_get_x);
} #define return_without
unless {return_cond} throw(RETURN)
Main_main_b = every time. JvM 1.1.6 is the newer version with na-
create_object(Buffer,Buffer_Next); tive threads, where JAVA threads are mapped to Solaris
Buffer_Next++}; threads. This version is therefore non-deterministic, po-
Buffer_constr(Main_main_b); tentially yielding different results for different runs of a
atomic{ multi-threaded program.
Main_main_p = Every program has been run several times (from 30
create object(Producer,Producer_Next); to 100), and the numbers indicate the percentage of runs
Producer_Next++);
that have highlighted the error, either via a deadlock, an
Producer_constr(Main_main_p,Main_main_b);
assertion violation, or a thrown NullPointerException.
atomic{
All runs, model ctiecking as well as JvM runs, have
Main_main_c =
been executed on a Sun Ultra Spare 60 with 512 Mb
create_object(Consumer,Consumer_Next);
Consumer_Next++}; of main memory, and with the Solaris operating system
Consumer_constr(Main_main_c,Main_main_b); version 5.5.1.
} unless {get_this_EXN > 0} Running the SPIN model checker Oil the PROMELA
} code generated by JPF typically used less than half a
Fig. 27. The main method second to find an error and explored between 40 and 400
states and a similar number of transitions. In a few cases
"received[i] .attr == ±"). The point here is that all to illustrate the translation done by JPF. However, the
the errors are caught. exercise does show that around half (11) of the errors
Table1.Verification
results
Nr. Line Modification
(changed
to) JPF JVM1.1.6 JVM1.1.3
1 3 protectedint putPtr= 1; A2 100 100
4 9 while (usedSlots == 2) D 65 0
7 13 putPtr = (putPtr + 1) Y. 2; A2 56 0
28 getPtr = (getPtr + 1) Y. 2;
9 14 remove: D 55 100
if (usedSlots == 0) notifyAUO;
10 14 usedSlots++ ; D 35 100
15 if (usedSlots == 0) notifyAll();
14 23 if (halted) { A1 3 0
15 29 if (usedSlots == 0) notifyAll(); D 50 0
16 29 remove: D 44 0
(if usedSlots == 3} notifyAllO;
17 29 usedSlots-- ; D 66 0
30 if (usedSlots == 3) notifyAll();
ing and exception variables, thus keeping these variables guage. Addison Wesley, 1996.
R. Iosif C. Demartini and R. Sisto. Modeling and Vali-
separate from user defined variables. This gave a 50% 2.
dation of Java Multithreading Applications using SPIN.
reduction in the amount of C-code generated by SPIN.
In Proceedings of the 4th SPIN workshop, Paris, France,
We have presented the simpler solution for readability November 1998.
reasons. 3. T. Cattel. Modeling and Verification of sC++ Applica-
We see the current translator as a phase 1 proto- tions. In Proceedings of the Tools and Algorithms ,for the
type experiment (involving 9 man months until now) Construction and Analysis o/Systems, Lisbon, Portugal,
that provides us with useful input to a phase 2, where LNCS 1384., April 1998.
these questions will be raised. What is equally impor- 4. J. C. Corbett. Constructing Compact Models of Concur-
tant is our planned efforts to build an abstraction work- rent Java Programs. In Proceedings o/ the ACM Sigsoft
bench around JAVA, where programs can be reduced in Symposium on So/tware Testing and Analysis, Clearnva-
size before model checking is applied. Techniques such ter Beach, Florida., March 1998.
D. L. Delefs, K. R. M. Leino, G. Nelson, and J. B.
as program slicing, abstract interpretation, and partial 5.
Saxe. Extended Static Checking. Technical Report 159,
evaluation will have a great influence.
Compaq Systems Research Center, Palo Alto, California,
We believe that the kind of technology presented in
USA, 1998.
this paper, already as is, can be very useful for unit test-
J. Gosling, B. Joy, and G. Steele. The Java Language
ing where one focuses on a single or a few classes, just 6.
Specification. Addison Wesley, 1996.
as has demonstrated with the example. This requires 7. K. Havelund. Mechanical Verification of a Garbage Col-
setting up an aggressive environment consisting of a col- lector. In Fourth International Workshop on Formal
lection of threads which will "bombard" the unit with Methods for Parallel Programming : Theory and Appli-
accesses. The technology is probably ready for this al- cations (FMPPTA '99), Lecture Notes in Computer Sci-
ready now, since there will typically not be a big need to ence, April 1999. To appear.
cut down the state space. Finally, we believe that per- 8. K. Havelund, K. G. Larsen, and A. Skou. Formal Ver-
haps the technology can be useful for students learning ification of an Audio/Video Power Controller using the
Real-Time Model Checker UPPAAL. In 5th Int. AMAST
to program in JAVA.
Workshop on Real-Time and Probabilistic Systems, Lec-
Concerning the specification language, our main ap-
ture Notes in Computer Science, May 1999. To appear.
proach has been not to extend the JAVA language but to
9. K. Havelund, M. Lowry, and J. Penix. Formal Analysis
express temporal properties as calls to methods defined of a Space Craft Controller using SPIN. In Proceedings o/
in a special temporal logic class (the Verify class), all of the 4th SPIN workshop, Paris, France, November 1998.
whose methods are static (hence one does not need to in- 10. K. Havelund and T. Pressburger. Translating Java to
stantiate the class to objects before calling the methods). SPIN, a step towards the JavaProver. Technical report,
In addition to the assert method one can for example NASA Ames Research center, Moffett Field, California,
imagine an always method, an eventually method, and USA, May 1997.
basically include all of SPIN's linear temporal logic op- 11. K. Havelund and N. Shankar. Experiments in Theorem
erators as methods, having boolean return types in ad- Proving and Model Checking for Protocol Verification.
In M-C. Gaudel and J. Woodcock, editors, FME'96: In-
dition to boolean argument types, such that they can be
dustrial Benefit and Advances in Formal Methods, vol-
composed. Calls of such methods will then generate LTL
ume 1051 of Lecture Notes in Computer Science, pages
formulae to be verified, referring to the position where
662 681, 1996.
they are called in the code. K. Havelund and N. Shankar. A Mechanized Refinement
12.
Proof for a Garbage Collector. Submitted to the journal:
Acknowledgements. We thank Gerard Holzmann for chang- Formal Aspects of Computing, 1998.
ing SPIN to support the translation of exceptions. We thank 13. K. Havelund and J. Skakkeb_ek. Practical Application
the members of the Automated Software Engineering team of Model Checking in Software Verification. Describes
at NASA Ames Research Center for their comments; and in an application of JPF to a game server. Submitted for
particular Mike Lowry for supporting the project, and John publication., February 1999.
14.K. Havelund,
A. Skou,
K. G.Larsen,
andK. Lund.For-
malModeling
andAnalysisofanAudio/Video
Protocol:
AnIndustrialCase
StudyUsingUPPAAL.In Proc. of
the 18th IEEE Real-Time Systems Symposium, pages 2-
13, Dec 1997. San Francisco, California, USA.
15. G. Holzmann. The Design and Validation of Computer
Protocols. Prentice Hall, 1991.
16. D. Lea. Concurrent Programming in Java. Addison Wes-
ley, 1997.
17. B. PelI, E. Gat, R. Keesing, N. Muscettola, and B. Smith.
Plan Execution for Autonomous Spacecrafts. In Pro-
ceedings of the 1997 International Joint Conference on
Artificial Intelligence, 1997.