820 likes | 835 Views
Modularity, Interfaces, and Verification. Tevfik Bultan Department of Computer Science University of California, Santa Barbara. Joint Work with My Students. Action Language Verifier (ALV) Tuba Yavuz-Kahveci, University of Florida, Gainesville Web Service Analysis Tool (WSAT)
E N D
Modularity, Interfaces, and Verification Tevfik Bultan Department of Computer Science University of California, Santa Barbara
Joint Work with My Students • Action Language Verifier (ALV) • Tuba Yavuz-Kahveci, University of Florida, Gainesville • Web Service Analysis Tool (WSAT) • Xiang Fu, Hofstra University • (co-advised with Jianwen Su) • Design for verification • Aysu Betin-Can, Middle East Technical University • Interface Grammars • Graham Hughes, PhD candidate http://www.cs.ucsb.edu/~bultan/vlab/publications.html
Outline • Motivation • PART 1: Verification of Synchronization Policies in Concurrent Programs via Interfaces • PART 2: Verification of Conversations among Web Services via Interfaces • PART 3: Modular Verification with Interface Grammars • Conclusions
Model Checking Software • Model checking • An automated software verification technique • Exhaustive exploration of the state space of a program to find bugs • Systematically explore all possible behaviors of a program • look for violations of the properties of interest • assertion violations, deadlock • Software model checkers: Verisoft, Java PathFinder (JPF), SLAM, BLAST, CBMC
Two Challenges in Model Checking • State space explosion • Exponential increase in the state space with increasing number of variables and threads • State space includes everything: threads, variables, control stack, heap • Environment generation • Finding models for parts of software that are • either not available for analysis, or • are outside the scope of the model checker
Modular Verification • Modularity is key to scalability of any verification technique • Moreover, it can help in isolatingthe behavior you wish to focus on, removing the parts that are beyond the scope of your verification technique • Modularity is also a key concept for successful software design • The question is finding effective ways of exploiting the modularity in software during verification
Interfaces for Modularity • How do we do modular verification? • Divide the software to a set of modules • Check each module in isolation • How do we isolate a module during verification/testing? • Provide stubs representing other modules (environment) • How do we get the stubs representing other modules? • Write interfaces • Interfaces specify the behavior of a module from the viewpoint of other modules • Generate stubs from the interfaces
Interfaces and Modularity: Basic Idea • Write interface specifications for the modules • Automatically generate stubs from the interface specifications • Automatically generated stubs provide the environment during modular verification
Three Applications I will talk about three different instantiations of this basic idea: • Verification of synchronization policies in concurrent programs using finite state interfaces • Verification of conversations among web services using finite state interfaces • Verification of sequential interactions using interface grammars
An Infinite State Model Checker Action Language Specification + CTL property Action Language Verifier (ALV) Action Language Parser Composite Symbolic Library Model Checker Omega Library CUDD Package MONA Counter-example Verified BDD Manipulator Automata Manipulator Not sure Presburger Arithmetic Manipulator
module main() integernr; booleanbusy; restrict: nr>=0; initial: nr=0 and !busy; module ReaderWriter() enumeratedstate {idle, reading, writing}; initial: state=idle; r_enter: state=idle and !busy and nr’=nr+1 and state’=reading; r_exit: state=reading and nr’=nr-1 and state’=idle; w_enter: state=idle and !busy and nr=0 busy’ and state’=writing; w_exit: state=writing and !busy’ and state’=idle; ReaderWriter: r_enter | r_exit | w_enter | w_exit; endmodule main: ReaderWriter*(); spec: invariant(busy => nr=0) spec: invariant(busy => eventually(!busy)) endmodule Read-Write Lock in Action Language S : Cartesian product of variable domains defines the set of states I : Predicates defining the initial states R : Atomic actions of a single process R : Transition relation of a process, defined as asynchronous composition of its atomic actions R : Transition relation of main, defined as asynchronous composition of finite but arbitrary number of reader-writer modules
Read-Write Lock in Java class ReadWriteLock {private Object lockObj;private int totalReadLocksGiven;private boolean writeLockIssued;private int threadsWaitingForWriteLock;public ReadWriteLock() { lockObj = new Object(); writeLockIssued = false; } public void getReadLock() {synchronized (lockObj) {while ((writeLockIssued) || (threadsWaitingForWriteLock != 0)) {try { lockObj.wait(); } catch (InterruptedException e) { } } totalReadLocksGiven++; } }public void getWriteLock() {synchronized (lockObj) { threadsWaitingForWriteLock++;while ((totalReadLocksGiven != 0) || (writeLockIssued)) {try { lockObj.wait(); } catch (InterruptedException e) { // } } threadsWaitingForWriteLock--; writeLockIssued = true; } }public void done() {synchronized (lockObj) { //check for errorsif ((totalReadLocksGiven == 0) && (!writeLockIssued)) { System.out.println(" Error: Invalid call to release the lock");return; }if (writeLockIssued) writeLockIssued = false;else totalReadLocksGiven--; lockObj.notifyAll(); } }} How do we translate this to Action Language? Action Language Verifier Verification of Synchronization in Java Programs
A Design for Verification Approach Our design for verification approach is based on the following principles: • Use of design patterns that facilitate automated verification • Use of stateful, behavioral interfaces which isolate the behavior and enable modular verification • An assume-guarantee style modular verification strategy that separates verification of the behavior from the verification of the conformance to the interface specifications • A general model checking technique for interface verification • Domain specific and specialized verification techniques for behavior verification
Concurrency Controller Pattern Controller GuardedCommand StateMachine Shared SharedStub ControllerStateMachine Controller +action1() +action2() +a() +b() +a() +b() -var1 -var2 +action1() +action2() Action +blocking() +nonblocking() -GuardedExecute GuardedCommand +guard() +update() ThreadA Shared ThreadB Helper classes int used at runtime used during interface verification used both times
Concurrency Controller Pattern • Avoids usage of error-prone Java synchronization primitives: synchronize, wait, notify • Separates controller behavior from the threads that use the controller • Supports a modular verification approach that exploits this modularity for scalable verification
class Action{ protected final Object owner; … private boolean GuardedExecute(){ boolean result=false; for(int i=0; i<gcV.size(); i++) try{ if(((GuardedCommand)gcV.get(i)).guard()){ ((GuardedCommand)gcV.get(i)).update(); result=true; break; } }catch(Exception e){} return result; } public void blocking(){ synchronized(owner) { while(!GuardedExecute()) { try{owner.wait();} catch (Exception e){} } owner.notifyAll(); } } public boolean nonblocking(){ synchronized(owner) { boolean result=GuardedExecute(); if (result) owner.notifyAll(); return result; } } } class RWController implements RWInterface{ int nR; boolean busy; final Action act_r_enter, act_r_exit; final Action act_w_enter, act_w_exit; RWController() { ... gcs = new Vector(); gcs.add(new GuardedCommand() { public boolean guard(){ return (nR == 0 && !busy);} public void update(){busy = true;}} ); act_w_enter = new Action(this,gcs); } public void w_enter(){ act_w_enter.blocking();} public boolean w_exit(){ return act_w_exit.nonblocking();} public void r_enter(){ act_r_enter.blocking();} public boolean r_exit(){ return act_r_exit.nonblocking();} } Reader-Writer Controller This helper class is provided. No need to rewrite it!
A controller interface defines the acceptable call sequences for the threads that use the controller Interfaces are specified using finite state machines public class RWStateMachine implements RWInterface{ StateTable stateTable; final static int idle=0,reading=1,writing=2; public RWStateMachine(){ ... stateTable.insert("w_enter",idle,writing); } public void w_enter(){ stateTable.transition("w_enter"); } ... } Controller Interfaces reading r_enter r_exit idle w_exit writing w_enter
Modular Design / Modular Verification Thread Modular Interface Verification Concurrent Program Thread 1 Thread 2 Thread n Thread 1 Thread 2 Thread n Interface Machine Interface Machine Interface Machine Interface Controller Modular Behavior Verification Shared Data Controller Behavior
Verification Framework Behavior Verification Action Language Verifier Controller Behavior Machine Controller Classes Counting Abstraction Concurrent Program Controller Interface Machine Interface Verification Java Path Finder Thread Thread Isolation Thread Thread Class Thread Classes
A Case Study • Tactical Separation Assisted Flight Environment (TSAFE) • 21,057 lines of code with 87 classes • Reengineered TSAFE using 2 instances of a reader-writer controller and 3 instances of a mutex controller • In the reengineered version the synchronization statements appear only in the Action helper class • Created 40 faulty versions of TSAFE by fault seeding • Caught 33 of them using interface and behavior verification • 4 of the uncaught faults were spurious • 3 uncaught faults were real faults and they were interface violations
Behavior Verification Performance P denotes parameterized verification for arbitrary number of threads
Observations • Falsification performance is better than verification performance • Completeness of the controller properties is an issue • For falsification, concrete instances were as effective as parameterized instances • Unknown shared objects: need escape analysis • Thread isolation is challenging • Environment generation is the crucial problem in scalability of the interface verification
Web Services • Loosely coupled, interaction through standardized interfaces • Standardized data transmission via XML • Asynchronous messaging • Platform independent (.NET, J2EE) WSCDL Interaction WSBPEL Composition Service WSDL Implementation Platforms Microsoft .Net, Sun J2EE SOAP Message XML Schema Type XML Data Web Service Standards
Web Service Conversations • A composite web service consists of • a finite set of peers • and a finite set of message classes • The messages among the peers are exchanged using reliable and asynchronous messaging • FIFO and unbounded message queues • A conversation is a sequence of messages generated by the peers during an execution
Checking Conversations confirm query Agent Customer Hotel reserve suggest Agent Customer Hotel !query ?query Input Queue !query ?reserve ?suggest ?query !suggest !confirm !reserve ?suggest ?confirm !suggest ... ? Conversation G(query F(confirm)) This is an undecidable verification problem due to unbounded queues
Synchronizability Analysis • A composite web service issynchronizable, if its conversation setdoes not change • when asynchronous communication is replaced with synchronous communication • If a composite web service is synchronizable we can check its properties about its conversations using synchronous communication semantics • For finite state peers this is a finite state model checking problem
Web Service Analysis Tool (WSAT) Verification Languages WebServices Front End Analysis Back End Intermediate Representation GFSA to Promela (synchronous communication) success BPEL to GFSA SynchronizabilityAnalysis Guarded automata BPEL fail (bottom-up) GFSA to Promela (bounded queue) Promela skip GFSA parser Conversation Protocol Guarded automaton success GFSA to Promela(single process, no communication) Realizability Analysis (top-down) fail
Checking Service Implementations There are some problems: • People write web service implementations using programming languages such as Java, C#, etc. • Synchronizability analysis works on state machine models • How do we generate the state machines from the Java code? Synchronizability Analysis Checking Service Implementations
Design for Verification Approach Use the same principles: • Use of design patterns that facilitate automated verification • Use of stateful, behavioral interfaces which isolate the behavior and enable modular verification • An assume-guarantee style modular verification strategy that separates verification of the behavior from the verification of the conformance to the interface specifications • A general model checking technique for interface verification • Domain specific and specialized verification techniques for behavior verification
Peer Controller Pattern ApplicationThread CommunicationInterface StateMachine Communicator CommunicationController PeerServlet ThreadContainer sessionId used at runtime Red Bordered classes are the ones the user has to implement used during interface verification used both times
Modular Design / Modular Verification Peer Modular Interface Verification Composite Service Peer 1 Peer 2 Peer n Peer 1 Peer 2 Peer n Interface Machine Interface Machine Interface Machine interface interface interface Modular Conversation Verification Conversation Behavior
Verification Framework Thread Promela WSAT Thread Peer State Machines Promela Translation Synchronizability Analysis Conversation Verification Composite Service Spin Peer State Machine Interface Verification Java Path Finder Peer Code
Examples • We used this approach to implement several simple web services • Travel agency • Loan approver • Product ordering • Performance of both interface and behavior verification were reasonable
Interface Verification Interface Verification with JPF for Loan Approver
Behavior Verification • Sample Property: Whenever a request with a small amount is sent, eventually an approval message accepting the loan request will be sent. • Loan Approval system has 154 reachable states • because queue lengths never exceeds 1 • Behavior verification used <1 sec and 1.49 MB • SPIN requires restricted domains • Have to bound the channel sizes bounded message queues • In general there is no guarantee these results will hold for other queue sizes • Using synchronizability analysis we use queues of size 0 and still guarantee that the verification results hold for unbounded queues!
Observations • Once the behavior is isolated (using concurrency controller or peer controller patterns) behavior verification is quite efficient • Interface verification is challenging • It is necessary to find effective behavioral interface specification and verification techniques
Interface Grammars Interface Compiler Interface Grammar Component B Interface Grammar Component B Stub Component A Model Checker Component A
An Example • An interface grammar for transactions • Specifies the appropriate ordering for method calls to a transaction manager • Method calls are the terminal symbols of the interface grammar Start→Base Base→beginTailBase | ε Tail→commit | rollback
An Example • Consider the call sequence begin rollback begin commit • Here is a derivation: Start • Base • beginTail Base • begin rollbackBase • begin rollback beginTail Base • begin rollback begin commitBase • begin rollback begin commit Start→Base Base→beginTailBase | ε Tail→commit | rollback
Another Example • This interface can also be specified as a Finite State Machine (FSM) • However, the following grammar, which specifies nested transactions, cannot be specified as a FSM begin commit rollback Start→Base Base→beginBaseTailBase | ε Tail→commit | rollback
Yet Another Example • Let’s add another method called setrollbackonlywhich forces all the pending transactions to finish with rollback instead of commit • We achieve this by extending the interface grammars with semantic predicates and semantic actions Start→«r:=false; l:=0»Base Base→begin«l:=l+1»BaseTail «l:=l-1; if l=0 then r:=false»Base | setrollbackonly«r:=true»Base | ε Tail→«r=false»commit | rollback
Interface Grammar Translation • Our interface compiler translates interface grammars to executable code: • the generated code is the stub for the component • The generated code is a parser that • parses the incoming method calls • while making sure that the incoming method calls conform to the interface grammar specification
Verification with Interface Grammars Interface Compiler Interface Grammar Top-down parser Program parser stack method invocation (lookahead) Component Stub parse table semantic predicates and semantic actions Model Checker
A Case Study • We wrote an interface grammar for the EJB 3.0 Persistence API • This is an API specification for mapping Java object graphs to a relational database • Hibernate is an implementation of this API • Hibernate distribution contains several example test clients that are designed to fail and test exceptional behavior by violating the interface specification
A Case Study, Continued • We used these simple clients to check the fidelity of the stub generated from our interface specification • We used the JPF software model checker • None of these examples can run under JPF directly • Time taken to develop the interface was dominated by the need to understand EJB Persistence first • about a couple of hours