670 likes | 806 Vues
This paper presents a framework for enhancing Java PathFinder (JPF) to accommodate the Java Memory Model (JMM) within model checking. It discusses the definitions and nuances of memory models, particularly sequentially consistent (SC) and relaxed memory models. The authors detail the implementation algorithm and their experiences with JPF in executing JMM-aware analyses. The study aims to bridge the complexity of the JMM with reliable software verification tools. It illustrates how extending JPF enhances the understanding and validation of concurrent Java programs against JMM constraints.
E N D
Java PathRelaxer: Extending JPF for JMM-Aware Model Checking Huafeng Jin, Tuba Yavuz-Kahveci, and Beverly Sanders Computer and Information Science and Engineering University of Florida
Contents • Memory Model • The Java Memory Model • Algorithm • Implementation • Experience • Conclusion
Contents • Memory Model • The Java Memory Model • Algorithm • Implementation • Experience • Conclusion
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 • 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. JPF assumes SC memory model
Example Intially, x = 0, done = false SCMM r == 1
Example Intially, x = 0, done = false SCMM r == 1 JMM r == 0 ˅ r == 1
Java’s String class • Data race is benign in both SC MM and JMM public final class String{ private final char value[]; private final int offset; private final int count; private inthash; //default 0 … public inthashCode(){ inth = 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; } }
Another Version • Benign in SC MM but not benign in JMM public final class String{ private final char value[]; private final int offset; private final int count; private inthash; //default 0 … public inthashCode(){ inth = 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; } }
Another Version • Benign in SC MM but not benign in JMM public final class String{ private final char value[]; private final int offset; private final int count; private inthash; //default 0 … public inthashCode(){ inth = 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; } } • Return hash code or0
Extending JPF • JPF: generates executions under SC memory model. • JPR: generates executions under an overapproximation of JMM.
Contents • Memory Model • The Java Memory Model • Algorithm • Implementation • Experience • Conclusion
Overview of JMM • 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
JMM Action • 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 Execution • Execution E <A, P, ≤po, ≤so, W, V>
Synchronizes-with Order ≤sw • A partial order over actions with regard to ≤so
Happens-before Order ≤hb • A partial order over actions by taking transitive closure of ≤po and ≤sw Initially, x == 0 ⋀ done == false, done is volatile ≤sw ≤sw ≤po ≤sw ≤po
Happens-before Order ≤hb • A partial order over actions by taking transitive closure of ≤po and ≤sw Initially, x == 0 ⋀ done == false, done is volatile ≤sw ≤sw ≤po ≤hb ≤sw ≤po
Data Race • In an execution Thread-1: … Write … Thread-2: … Read … ≤hb ≤hb x
Data Race Free • 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.
Well-formed Execution • For all reads r of variable v, it cannot be • r ≤hbW(r) • W(r) ≤hbw ≤hbr (w writes to v)
Example • r can only be 1, not 0 Initially, x == 0⋀ done == false, done is volatile ≤sw ≤sw ≤po If read x = 0, then there is an interleaving write x = 1. ≤sw ≤po
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) 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: E Justify ∅ C1 C2 Ci-1 → E1 → E2 → … → Ei
Out-of-thin-air Value • 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
Contents • Memory Model • The Java Memory Model • Algorithm • Implementation • Experience • Conclusion
JPR Overview • Fixed-point semantics • Overapproximation of JMM • WriteSet JPF WriteSet Write: addvalues to Read: Pick value from
Structure of JPR JPR Driver WriteSetold Iterative calls WriteSetnew Events JPF JMMListener Bytecode of the target program
Metadata • JPF’s state representation is extended with the following metadata:
Example Initially, x == y == 0, x and y are non-volatile. Under JMM, r1 == 1 ⋀ r2 == 1 is possible.
1st iteration init: x = 0, y = 0 WS(x) = {<init, 0>}, WS(y) = {<init, 0>} IS = ∅ init GWS = ∅
1st iteration init: x = 0, y = 0 A1; r1 = x; WS(x) = {<init, 0>}, WS(y) = {<init, 0>} IS = ∅ R(A1) = <init, 0>, legal past read init A1
1st iteration init: x = 0, y = 0 A1; r1 = x; A2: y = 1; WS(x) = {<init, 0>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0> init A1 A2
1st iteration init: x = 0, y = 0 A1; r1 = x; A2: y = 1; B1: r2 = y; WS(x) = {<init, 0>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0> A1 A2 init B1
1st iteration init: x = 0, y = 0 A1; r1 = x; A2: y = 1; A1 A2 init B1: r2 = y; B1 0 WS(x) = {<init, 0>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0>, R(B1) = <init, 0>legal past read
1st iteration init: x = 0, y = 0 A1; r1 = x; A2: y = 1; A1 A2 init B1: r2 = y; B1 B2 0 WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0>, R(B1) = <init, 0> B2: x = 1; r1 = 0, r2 = 0
1st iteration init: x = 0, y = 0 A1; r1 = x; A1 A2 A2: y = 1; init B1 B1: r2 = y; WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0>, R(B1) = <A2, 1>legal past read 0 1 B2: x = 1;
1st iteration init: x = 0, y = 0 A1; r1 = x; A2: y = 1; A1 A2 init B1: r2 = y; B1 B2 0 1 WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0>, R(B1) = <A2, 1> B2: x = 1; r1 = 0, r2 = 1
1st iteration init: x = 0, y = 0 A1; r1 = x; A2: y = 1; B1: r2 = y; B1: r2 = y; WS(x) = {<init, 0>}, WS(y) = {<init, 0>} IS = ∅, R(A1) = <init, 0>, R(B1) = 0legal past read 0 1 A1 init B2: x = 1; B1
1st iteration init: x = 0, y = 0 A1; r1 = x; A1 A2 A2: y = 1; B1: r2 = y; init A2: y = 1; B1: r2 = y; B1 WS(x) = {<init, 0>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0>, R(B1) = <init, 0> 0 1 B2: x = 1;
1st iteration init: x = 0, y = 0 A1; r1 = x; A1 A2 A2: y = 1; B1: r2 = y; init A2: y = 1; B1 B2 B1: r2 = y; WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0>, R(B1) = <init, 0> B2: x = 1; 0 1 B2: x = 1; r1 = 0, r2 = 0
1st iteration init: x = 0, y = 0 A1; r1 = x; A2: y = 1; A1 A2 B1: r2 = y; A2: y = 1; init B1: r2 = y; B2: x = 1; B1 B2 B2: x = 1; 0 1 A2: y = 1; r1 = 0, r2 = 0 WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅, R(A1) = <init, 0>, R(B1) = <init, 0> B2: x = 1;
1st iteration init: x = 0, y = 0 A1; r1 = x; B1: r2 = y; A2: y = 1; WS(x) = {<init, 0>}, WS(y) = {<init, 0>} IS = ∅ R(B1) = <init, 0>legal past read B1: r2 = y; A2: y = 1; B1: r2 = y; B2: x = 1; B2: x = 1; 0 1 A2: y = 1; init B1 B2: x = 1;
1st iteration init: x = 0, y = 0 A1; r1 = x; B1: r2 = y; A2: y = 1; A1; r1 = x; B1: r2 = y; A2: y = 1; B1: r2 = y; WS(x) = {<init, 0>}, WS(y) = {<init, 0>} IS = ∅ R(B1) = <init, 0>, R(A1) = <init, 0> legal past read B2: x = 1; B2: x = 1; 0 1 A2: y = 1; A1 init B2: x = 1; B1
1st iteration init: x = 0, y = 0 A1; r1 = x; B1: r2 = y; A2: y = 1; A1; r1 = x; B1: r2 = y; A2: y = 1; A2: y = 1; B1: r2 = y; B2: x = 1; B2: x = 1; B2: x = 1; 0 1 A2: y = 1; B2: x = 1; A2: y = 1; B2: x = 1; r1 = 0, r2 = 0
1st iteration init: x = 0, y = 0 A1; r1 = x; B1: r2 = y; A2: y = 1; A1; r1 = x; B2: x = 1; B1: r2 = y; A2: y = 1; A2: y = 1; A1; r1 = x; B1: r2 = y; B2: x = 1; B2: x = 1; B2: x = 1; 0 1 0 1 A2: y = 1; B2: x = 1; A2: y = 1; B2: x = 1; A2: y = 1; 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 r1 = 0, r2 = 1
2nd iteration init: x = 0, y = 0 WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅ GWS(x) = {<T, 0>, <B2, 1>} GWS(y) = {<T, 0>, <A2, 1>} init
2nd iteration init: x = 0, y = 0 A1: r1 = x; WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>} IS = ∅ init A1
2nd iteration init: x = 0, y = 0 A1: r1 = x; 0 1 init A1 WS(x) = {<init, 0>, <B2, 1>}, WS(y) = {<init, 0>, <A2, 1>} IS = {<B2, 1>}, R(A1) = <B2, 1>potential future read …