Java PathRelaxer: Extending JPF for JMM-Aware Model Checking
Huafeng Jin, Tuba Yavuz-Kahveci, and Beverly SandersComputer and Information Science and Engineering
University of Florida
Contents
Memory ModelThe Java Memory ModelAlgorithm ImplementationExperienceConclusion
Contents
Memory ModelThe Java Memory ModelAlgorithm ImplementationExperienceConclusion
Specifies “which value each read of a memory location may return”.
Sequentially consistent (SC) memory model Memory actions must execute one at a time in
a single total single order Read always see the value of the most recent
write to that memory location. Relaxed memory models
PSO, TSO, Java Memory Model (JMM), etc.
Memory Model
Specifies “which value each read of a memory location may return”.
Sequentially consistent (SC) memory model Memory actions must execute one at a time in
a single total single order Read always see the value of the most recent
write to that memory location. Relaxed memory models
PSO, TSO, Java Memory Model (JMM), etc.
Memory Model
JPF assumes SC memory model
Example
Intially, x = 0, done = false
SCMM r == 1
Thread-1 Thread-2x = 1;
done = true;
while (!done){/*spin*/}
r = x;
Example
Intially, x = 0, done = false
Thread-1 Thread-2x = 1;
done = true;
while (!done){/*spin*/}
r = x;
SCMM r == 1
JMM r == 0 ˅ r == 1
Java’s String class
public final class String{ private final char value[]; private final int offset; private final int count; private int hash; //default 0 … public int hashCode(){ int h = hash, len = count; //read of hash if (h == 0 && len > 0){
… /*calculate hash code locally and assign to h*/hash = h; //write of hash
} return h; }}
Data race is benign in both SC MM and JMM
Another Version
public final class String{ private final char value[]; private final int offset; private final int count; private int hash; //default 0 … public int hashCode(){ int h = hash, len = count; //read of hash if (h == 0 && len > 0){
… /*calculate hash code locally and assign to h*/hash = h; //write of hash
} h = hash; //read of hash return h; }}
Benign in SC MM but not benign in JMM
Another Version
public final class String{ private final char value[]; private final int offset; private final int count; private int hash; //default 0 … public int hashCode(){ int h = hash, len = count; //read of hash if (h == 0 && len > 0){
… /*calculate hash code locally and assign to h*/hash = h; //write of hash
} h = hash; //read of hash return h; }}
Benign in SC MM but not benign in JMM
Return hash code
or 0
JPF: generates executions under SC memory model.
JPR: generates executions under an overapproximation of JMM.
Extending JPF
Contents
Memory ModelThe Java Memory ModelAlgorithm ImplementationExperienceConclusion
SC memory model: Read sees most recent write to that location.
Java memory model: Read sees any write (past/future) to that
location provided the execution is Well-formed Meets causality constraints
Overview of JMM
Action (memory related) <t, k, v, u>
Non-synchronization actions: non-volatile write, non-volatile read
Synchronization actions: volatile write, volatile read, lock, unlock, thread start, thread join, …
JMM Action
t Thread ID
k Action kind (volatile read/write, non-volatile read/write, lock/unlock, thread start, thread join …)
v Variable/monitor
u Unique action ID
Execution E <A, P, ≤po, ≤so, W, V>
JMM Execution
A Finite set of actionsP Program
≤po Program order, a partial order over A based on each thread’s sequence.
≤so Synchronization order, a total order over all the synchronization actions in A
W Write-seen function, maps each read action to the write action it sees
V Value-written function, maps each write action to the value it writes
A partial order over actions with regard to ≤so
Synchronizes-with Order ≤sw
unlock(x)
≤sw
subsequent lock(x)volatile write(x) subsequent volatile read(x)start thread t 1st action of thread tWrite of default value 1st action in each thread
A partial order over actions by taking transitive closure of ≤po and ≤sw
Initially, x == 0 done == false, done is volatile⋀
Happens-before Order ≤hb
Thread-1 Thread-2x = 1;
done = true
while (!done){/*spin*/}
r = x;
≤po
≤po
≤sw
≤sw
≤sw
Thread-1 Thread-2x = 1;
done = true
while (!done){/*spin*/}
r = x;
A partial order over actions by taking transitive closure of ≤po and ≤sw
Initially, x == 0 done == false, done is volatile⋀
Happens-before Order ≤hb
≤po
≤po
≤sw
≤sw
≤sw
≤hb
In an execution
Data Race
Thread-1: …
Write
…
Thread-2: …
Read
…
x
≤hb
≤hb
A program: If all the SC executions are free of
data races, it is Data-Race-Free program (DRF).
DRF Guarantee: Any legal execution of DRF program is SC.
Data Race Free
For all reads r of variable v, it cannot be
r ≤hb W(r)
W(r) ≤hb w ≤hb r (w writes to v)
Well-formed Execution
r can only be 1, not 0Initially, x == 0 done == false, done is volatile⋀
Example
Thread-1 Thread-2x = 1;
done = true
while(!done) {/*spin*/}
r = x;
≤po
≤po
≤sw
≤sw
≤swIf read x = 0, then there is an interleaving writex = 1.
An execution E <A, P, ≤po, ≤so, W, V> with ≤hb is legal if there is a finite sequence of set of actions Ci and well-formed executions Ei < Ai, Pi, ≤poi, ≤soi, Wi, Vi > with ≤hbi and ≤swi such that C0 = , ∅ Ci ⊆ Ci-1 for all i > 0, ∪ Ci = A, and for each i > 0 the following rules are satisfied:
Causality Rules (complicated)
An execution E <A, P, ≤po, ≤so, W, V> with ≤hb is legal if there is a finite sequence of set of actions Ci and well-formed executions Ei < Ai, Pi, ≤poi, ≤soi, Wi, Vi > with ≤hbi and ≤swi such that C0 = , ∅ Ci ⊆ Ci-1 for all i > 0, ∪ Ci = A, and for each i > 0 the following rules are satisfied:
Causality Rules (complicated)
E
→ E1 → E2 → … → Ei
∅ C1 C2 Ci-1
Justify
Causality Rules: Rules out out-of-thin-air values
Example: Initially, x == y == 0, x and y are non-volatile
r1 == r2 == 42 is out-of-thin-air value
Out-of-thin-air Value
Thread-1 Thread-2r1 = x; r2 = y;
y = r1; x = r2;
Contents
Memory ModelThe Java Memory ModelAlgorithm ImplementationExperienceConclusion
Fixed-point semantics
Overapproximation of JMM WriteSet
JPR Overview
WriteSetWrite: add values to Read: Pick value from
JPF
Structure of JPR
JPR Driver
JPF JMMListener
WriteSetold
WriteSetnew
Events
Iterative calls
Bytecode of the target program
JPF’s state representation is extended with the following metadata:
Metadata
WriteSet MemLoc → 2Aid × Val Collect write values
ActionSet 2Action Current set of actions
HBSet 2Aid × Aid Collect ≤hb relations
ImposeSet 2Aid × Val Rule out some out-of-thin-air values
Read Aid → Aid × Val Record W(r) and V(W(r))
Write Aid → Val Record V(w)
Initially, x == y == 0, x and y are non-volatile.
Under JMM, r1 == 1 r2 == 1 is possible. ⋀
Example
Thread-1 Thread-2r1 = x; r2 = y;
y = 1; x = 1;
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>}, WS(y) = {<init, 0>}IS = ∅
1st iteration
GWS = ∅init
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>}, WS(y) = {<init, 0>}IS = ∅R(A1) = <init, 0>, legal past read
A1; r1 = x;
1st iteration
init A1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>
A1; r1 = x;
A2: y = 1;
1st iteration
init A1 A2
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
1st iteration
init
A1 A2
B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>, R(B1) = <init, 0> legal past read
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
1st iteration
init
A1 A2
B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>, R(B1) = <init, 0>
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
r1 = 0, r2 = 0
1st iteration
init
A1 A2
B1 B2
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>, R(B1) = <A2, 1> legal past read
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
1st iteration
init
A1 A2
B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
r1 = 0, r2 = 1
1
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>, R(B1) = <A2, 1>
1st iteration
init
A1 A2
B1 B2
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
B1: r2 = y;
WS(x) = {<init, 0>}, WS(y) = {<init, 0>}IS = , ∅R(A1) = <init, 0>, R(B1) = 0 legal past read
1st iteration
init
A1
B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
B1: r2 = y;
A2: y = 1;
WS(x) = {<init, 0>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>, R(B1) = <init, 0>
1st iteration
init
A1 A2
B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
B1: r2 = y;
A2: y = 1;
B2: x = 1; WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>, R(B1) = <init, 0>
r1 = 0, r2 = 0
1st iteration
init
A1 A2
B1 B2
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
B1: r2 = y;
A2: y = 1;
B2: x = 1;
B2: x = 1;
A2: y = 1;
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = , ∅R(A1) = <init, 0>, R(B1) = <init, 0>
r1 = 0, r2 = 0
1st iteration
init
A1 A2
B1 B2
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
B1: r2 = y;
A2: y = 1;
B2: x = 1;
B2: x = 1;
A2: y = 1;
B1: r2 = y;
WS(x) = {<init, 0>}, WS(y) = {<init, 0>}IS = ∅R(B1) = <init, 0> legal past read
1st iteration
init B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
B1: r2 = y;
A2: y = 1;
B2: x = 1;
B2: x = 1;
A2: y = 1;
B1: r2 = y;
A1; r1 = x;
WS(x) = {<init, 0>}, WS(y) = {<init, 0>}IS = ∅R(B1) = <init, 0>, R(A1) = <init, 0> legal past read
1st iteration
init
A1
B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
B1: r2 = y;
A2: y = 1;
B2: x = 1;
B2: x = 1;
A2: y = 1;
B1: r2 = y;
A1; r1 = x;
A2: y = 1;
B2: x = 1;
B2: x = 1;
A2: y = 1;
r1 = 0, r2 = 0
1st iteration
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
A1; r1 = x;
A2: y = 1;
B1: r2 = y;
0
B2: x = 1;
1
B1: r2 = y;
A2: y = 1;
B2: x = 1;
B2: x = 1;
A2: y = 1;
B1: r2 = y;
A1; r1 = x;
A2: y = 1;
B2: x = 1;
B2: x = 1;
A2: y = 1;
B2: x = 1;
A1; r1 = x;
0
A2: y = 1;
1
r1 = 0, r2 = 1
1st iteration
r1 = 0, r2 = 1
The WriteSet collected after 1st iteration is
GWS(x) = {<init, 0>, <B2, 1>}GWS(y) = {<init, 0>, <A2, 1>}
It is passed to the 2nd iteration
init: x = 0, y = 0;
Thread-1 Thread-2A1: r1 = x; B1: r2 = y;
A2: y = 1; B2: x = 1;
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = ∅
2nd iteration
GWS(x) = {<T, 0>, <B2, 1>}GWS(y) = {<T, 0>, <A2, 1>}
init
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = ∅
2nd iteration
A1: r1 = x;
init A1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = {<B2, 1>}, R(A1) = <B2, 1> potential future read
2nd iteration
A1: r1 = x;
0 1
…
init A1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = {<B2, 1>}, R(A1) = <B2, 1>
2nd iteration
A1: r1 = x;
0 1
A2: y = 1;
…
init A1 A2
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = {<B2, 1>}, R(A1) = <B2, 1>
2nd iteration
A1: r1 = x;
0 1
A2: y = 1;
B1: r2 = y;…
init
A1 A2
B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = {<B2, 1>}, R(A1) = <B2, 1>, R(B1) = <A2, 1>
2nd iteration
A1: r1 = x;
0 1
A2: y = 1;
B1: r2 = y;
0 1
…
…
init
A1 A2
B1
init: x = 0, y = 0; Thread-1 Thread-2A1: r1 = x; B1: r2 = y; A2: y = 1; B2: x = 1;
init: x = 0, y = 0
WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>}IS = {<B2, 1>}, justifiedR(A1) = <B2, 1>, R(B1) = <A2, 1>
2nd iteration
A1: r1 = x;
0 1
A2: y = 1;
B1: r2 = y;
0 1
B2: x = 1;
…
… r1 = 1, r2 = 1
init
A1 A2
B1 B2
3rd iteration generates the same global WriteSet as 2nd iteration, so a fixed-point is reached.
Possible outcomes running JPR: r1 == 0 r2 == 0⋀ r1 == 0 r2 == 1⋀ r1 == 1 r2 == 0⋀ r1 == 1 r2 == 1⋀
Example
Contents
Memory ModelThe Java Memory ModelAlgorithm ImplementationExperienceConclusion
JRF (Java Racefinder) is a JPF extension used to precisely detect data races.
Kyunghee Kim, Eric Mercer, Neha Rungta, Tuba Yavuz-Kahveci, Beverly Sanders
http://babelfish.arc.nasa.gov/trac/jpf/wiki/projects/jpf-racefinder
Working with JRF
Data Race Free (DRF) Guarantee
For DRF programs, model checking under SC memory model is enough. JPF is sufficient, no need to run JPR.
Working with JRF
Working with JRF
JRF
JPF
JPR
DRF? Y
DRF? N
Contents
Memory ModelThe Java Memory ModelAlgorithm ImplementationExperienceConclusion
Group 1 tc1 – tc20 from JMM causality test cases
http://www.cs.umd.edu/~pugh/java/memoryModel/unified Proposal/testcases.html
Group 2 Benign data races (hash code, is prime)
Group 3 Harmful data races (dcl, peterson, dekker)
Testing Suites
Experiment ResultsTe
st C
ases
Time (milliseconds)
Experiment ResultsTe
st C
ases
Time (milliseconds)
JPR takes much longer time than JPF: Iterations Data choice generators
Experiment ResultsTe
st C
ases
Number of states
Contents
Memory ModelThe Java Memory ModelAlgorithm ImplementationExperienceConclusion
JPR: Applies a fixed-point based semantic Adds non-SC behaviors into JPF Generates an overapproximiation of JMM
Conclusion
Thank You!
Top Related