540 likes | 677 Views
Guided Test Visualization : Making Sense of Errors in Concurrent Programs . Saint Wesonga & Neha Rungta & Eric Mercer Brigham Young University & NASA AMES, USA. State of the Art. Stress Testing Dynamic Analysis Runtime monitoring Static Analysis Model Checking Symbolic Execution
E N D
Guided Test Visualization:Making Sense of Errors in Concurrent Programs Saint Wesonga & NehaRungta & Eric Mercer Brigham Young University & NASA AMES, USA
State of the Art • Stress Testing • Dynamic Analysis • Runtime monitoring • Static Analysis • Model Checking • Symbolic Execution • Software Model Checking Guided-test [SPIN 2009] Slice And Dice [ICSE-NIER 2010]
Belief Guided by belief Sea of choices Guided-test and Slice-and-dice
Guided-test wants to find real errors Find an error or you do not have much to say
Our Solution Possible errors locations from imprecise analysis tools Add inter-thread dependenceinformation Guided Symbolic Execution Sound errordetection Generate backward slicesfrom possible error locations Heuristics Runtime Environment
Input Possible errors locations from imprecise analysis tools Add inter-thread dependenceinformation Guided Symbolic Execution Sound errordetection Generate backward slicesfrom possible error locations Heuristics Runtime Environment
Small example Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Small Example Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Small Example Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Small Example Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Small Example Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Small Example Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Small Example Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Could it really be? Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } Is it possible to arrive at this location on some execution?
Initial Abstraction to Follow (not explore) Possible errors locations from imprecise analysis tools Add inter-thread dependenceinformation Guided Symbolic Execution Sound errordetection Generate backward slicesto possible error locations Heuristics Runtime Environment
Slice along sequential execution Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Target Locations & Start of Programs Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } Exception
Call Sites and Start Locs of the callee Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart check(elem) Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } A.checklstart Exception
Conditional Statements Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart check(elem) Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } A.checklstart if(elem.e > 9) Exception
Data Definitions Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart check(elem) Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } A.checklstart if(elem.e > 9) Exception
Synchronization Operations Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart lock(elem) check(elem) Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } unlock(elem) A.checklstart if(elem.e > 9) Exception
Abstract System Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart lock(elem) check(elem) unlock(elem) Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } A.checklstart if(elem.e > 9) Exception
Abstract Trace Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart lock(elem) check(elem) Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } unlock(elem) A.checklstart if(elem.e > 9) Exception
Abstract Trace Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart lock(elem) One trace of many we could have selected: fair coin or farm in parallel check(elem) A.checklstart Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } if(elem.e > 9) Exception
Our Solution Possible errors locations from imprecise analysis tools Add inter-thread dependenceinformation Guided Symbolic Execution Sound errordetection Generate backward slicesto possible error locations Heuristics Runtime Environment
Map Concrete state to a location Goal is to match a concrete state to the next program location in our trace Follow the trace! Thread-0StackLocalVars Thread-1StackLocalVars Thread-nStackLocalVars HeapGlobalVars s1 Data Input Variables Symbolic Representations ϕpath Constraint Extract current program location of themost recently executed thread l1
Guidance Strategy l1 s0 s3 s1 s2 l2 ln
Guidance Strategy l1 s0 s3 s1 s2 l2 Flip a fair coin and move forward ln
Guidance Strategy l1 s0 s3 s1 s2 l2 s4 s5 s6 Distance heuristic to choose nearest to next location on the current trace ln
Our Solution Possible errors locations from imprecise analysis tools Add inter-thread dependenceinformation (refine) Guided Symbolic Execution Sound errordetection Generate backward slicesto possible error locations Heuristics Runtime Environment
Followed trace to predicate… A.runlstart lock(elem) check(elem) A.checklstart False in all successors (zut!) if(elem.e > 9) Exception
Where else is elem.e defined? Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } Object Element { inte; Element() { e = 1; } void reset() { e = 11; } }
Redefine a global variable B.runlstart A.runlstart lock(elem) check(elem) unlock(elem) A.checklstart if(elem.e > 9) e := 11 Exception
Generate a new backward slice Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } B.runlstart if(x > 18) lock(elem) elem.reset() Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } Element.resetlstart unlock(elem) e := 11
Updated Abstract System B.runlstart A.runlstart if(x > 18) lock(elem) lock(elem) check(elem) elem.reset() unlock(elem) A.checklstart Element.resetlstart unlock(elem) if(elem.e > 9) e := 11 Exception
Update Abstract Trace A.runlstart B.runlstart lock(elem) if(x > 18) check(elem) lock(elem) elem.reset() A.checklstart Element.resetlstart if(elem.e > 9) Exception e := 11
Check Lock Dependencies A.runlstart B.runlstart lock(elem) if(x > 18) check(elem) lock(elem) elem.reset() A.checklstart Element.resetlstart if(elem.e > 9) Exception e := 11
Update Abstract Trace A.runlstart B.runlstart lock(elem) if(x > 18) Thread B { void run (Element elem) { intx; // input variable if( x > 18) { lock(elem) elem.reset(); unlock(elem) } } } check(elem) lock(elem) elem.reset() A.checklstart Element.resetlstart if(elem.e > 9) Exception unlock(elem) e := 11
Update Abstract State A.runlstart B.runlstart lock(elem) if(x > 18) check(elem) lock(elem) unlock(elem) elem.reset() A.checklstart Element.resetlstart if(elem.e > 9) Restart with this new trace from the initial state Exception e := 11
Slice and dice wants to find real errors and prove correct… Don’t find an error yet have much to say
Abstract Trace Thread A { void run (Element elem) { lock(elem) check(elem) unlock(elem) } void check(Elementelem) { if(elem.e > 9) Throw exception } } A.runlstart lock(elem) check(elem) Object Element { inte; Element() { e = 1; } void reset() { e = 11; } } unlock(elem) A.checklstart if(elem.e > 9) Exception
Slice and Dice • Keep entire abstraction • Schedule only on node in the abstraction A.runlstart lock(elem) check(elem) unlock(elem) A.checklstart if(elem.e > 9) Exception
When it is time to refine… A.runlstart B.runlstart lock(elem) if(x > 18) check(elem) lock(elem) unlock(elem) elem.reset() A.checklstart Element.resetlstart if(elem.e > 9) …rather than create a single trace… Exception e := 11
Keep the entire abstraction B.runlstart A.runlstart if(x > 18) lock(elem) lock(elem) check(elem) elem.reset() unlock(elem) A.checklstart Element.resetlstart unlock(elem) if(elem.e > 9) e := 11 Exception
Belief Guided by belief Sea of choices Guided-test and Slice-and-dice
jpf-guided-test Verification & Validation Lab Brigham Young University Provo, UT 84606, USA http://vv.cs.byu.edu/ VV Lab, Brigham Young University, USA
Experimental Results • A new GuidedSymbolic extension in Java Pathfinder • Uses the engine from the symbolic extension (Pasareanuet. al. ISSTA 2008) • Dynamic partial order reduction turned on • Abstraction, refinement and heuristic computation all performed on Java bytecode • Libraries are part of the multi-threaded system
Multi-threaded Programs • Reorder BenchmarkSLOC: 44, Reachability • Airline BenchmarkSLOC: 31, Reachability • VecDeadlock Real JDK 1.4 concurrent librarySLOC: 7267, Deadlock in Vector class • VecDeadlock Real JDK 1.4 concurrent librarySLOC: 7169, Deadlock in Vector class • VecRace Real JDK 1.4 concurrent librarySLOC: 7151, Race in ArrayList class