View
218
Download
3
Embed Size (px)
Citation preview
Application of Design for Verification with Concurrency Controllers to
Air traffic Control Software
Aysu Betin-Can, Tevfik Bultan
Department of Computer Science
University of California, Santa Barbara
Mikael Lindvall, Benjamin Lux, Stefan Topp
Fraunhofer Center for Experimental Software Engineering, Maryland
Concurrent Programming in Java
• Java uses a variant of monitor programming
• Synchronization using locks– Each object has a locksynchronized(o) { ... }
• Coordination using condition variables– Objects can be used as condition variablessynchronized (condVar){ while (!condExp) wait(condVar); ... notifyAll(condVar); }
Dangers in Java Concurrency
• Nested locks
synchronized m(other) { other.m(); }• Missed notification
notify(condVar);• Forgotten condition check
if(!condExp) wait(condVar);• Dependency among multiple condition variables can be
very complicated– Conservative notification and condition check:
Inefficient– Optimizing the notification and condition checks:
Error prone
What will I talk about today?
• A design for verification approach for eliminating concurrency errors in Java programs
• Application of this design for verification approach to a safety critical air traffic control software component
• Experiments demonstrating the effectiveness of the presented design for verification approach
Model Checking Software
• Scalability of software model checking depends on– Extracting compact models from programs
• This typically requires a reverse engineering step based on user guidance and/or static analysis techniques to – Rediscover some information about the software that
may be known at design time
• Alternative approach: Design for verification – Structure software in ways that facilitate verification– Document the design decisions that can be useful for
verification– Improve the scalability of verification using this
information
A Design for Verification Approach
We have been investigating a design for verification approach based on the following principles:
1. Using design patterns that facilitate automated verification
2. Use of stateful, behavioral interfaces which isolate the behavior and enable modular verification
3. An assume-guarantee style modular verification strategy which separates verification of the behavior from the verification of the conformance to the interface specifications
4. A general model checking technique for interface verification
5. Domain specific and specialized verification techniques for behavior verification
• A verifiable behavioral design pattern for concurrent programs
– Defines customized synchronization policies
– Avoids usage of error-prone Java synchronization primitives: synchronize, wait, notify, notifyAll
– Separates controller behavior from the threads that use the controller
• Supports modular verification and model extraction
Concurrency Controller Pattern
ControllerShared
Concurrency Controller Pattern
ThreadA
ThreadB
StateMachine
Controller
-var1-var2
+action1()+action2()
Action
+blocking()+nonblocking()-GuardedExecute
SharedStub
+a()+b()
Shared
+a()+b()
GuardedCommand
+guard()+update()
int GuardedCommand
ControllerStateMachine
+action1()+action2()
used at runtimeused at interface verificationused both times
Helperclasses
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
Action Class: Used As Is
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; }}
Controller Interfaces
• 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"); } ...}
writing
reading
idle
r_enter
r_exit
w_exit
w_enter
Verification Framework
Action LanguageVerifier
Java PathFinder
Behavior Specification in Action Language
Optimized Java Code
Program with Stubs
Controllers andInterfaces
Behavior Translator
Notification-OptimizerData Stubs
Rest of the Program
Thread Isolation
Error Trace Verified
VerifiedError Trace
Behavior Verification
Interface Verification
Counting Abstractor
• Utilizes behavior and interface decoupling in the pattern
• Behavior verification– Verify the controller properties (e.g. safety, liveness)– Assume that the user threads adhere to the controller
interface
• Interface verification – Check that each user thread obeys the interface:
• A thread is correct with respect to an interface if all the call sequences generated by the thread can also be generated by the finite state machine defining the interface.
Modular Verification
Behavior Verification
• Analyzing properties (specified in CTL) of the synchronization policy encapsulated with a concurrency controller and its interface– Assume threads obey the controller interfaces
• Behavior verification with Action Language Verifier– Infinite state symbolic model checker– Suitable for specifications with unbounded variables and
parameterized constants– We wrote a translator which translates controller classes
to Action Language
Behavior Verification for Arbitrary Number of Threads
• Counting abstraction– Create an integer variable for each interface state– Each variable counts the number of threads in a
particular interface state– Automatically generate updates and guards for these
variables based on the interface specification
• Counting abstraction is automated
Three Types of Controller Properties
• Properties that only refer to controller variables
AG(busy => nR=0)
AG(busy => AF(!busy))
• Properties that refer to interface states
AG(pc=WRITING => AF(pc=IDLE)
AG(pc=READING => nR > 0)
• Properties for arbitrary number of threads
AG(WRITING > 0 => AF(IDLE > 0)
AG(READING = nR)
Interface Verification
• Checks if all the threads invoke controller methods in the order specified in the interfaces
• Checks if the threads access shared data only at the correct interface states
• Interface verification with Java PathFinder– Verify Java implementations of threads– Correctness criteria are specified as assertions
• Look for assertion violations• Assertions are in the StateMachine and SharedStub
– Performance improvement with thread Isolation
Thread Isolation: Part 1
• Interaction among threads
• Threads can interact with each other in only two ways:– invoking controller actions– Invoking shared data methods
• To isolate the threads– Replace concurrency controllers with controller interface
state machines– Replace shared data with shared stubs
Thread Isolation: Part 2
• Interaction among a thread and its environment
• Modeling thread’s call to its environment with stubs– File I/O, updating GUI components, socket operations,
RMI call to another program• Replace with pre-written or generated stubs
• Modeling the environment’s influence on threads with drivers– Thread initialization, RMI events, GUI events
• Enclose with drivers that generate all possible events that influence controller access
Automated Airspace Concept
• Automated Airspace Concept by NASA researchers automates the decision making in air traffic control
• The most important challenge is achieving high dependability
• Automated Airspace Concept includes a failsafe short term conflict detection component– Dependability of this component is even more important
than the dependability of the rest of the system– It should be a smaller, isolated component compared to
the rest of the system so that it can be verified
Tactical Separation Assisted Flight Environment (TSAFE)
• TSAFE is an implementation of this failsafe short term conflict detection component– It is developed at MIT based on the design of the NASA
researchers
• It is responsible for detecting conflicts in flight plans of the aircraft within 1 minute from the current time
• Functionality:
1. Display aircraft position
2. Display aircraft planned route
3. Display aircraft future projected route trajectory
4. Show conformance problems
Server
Computation
FlightDatabase
GraphicalClient
Client
<<RMI>>
21,057 lines of code with 87 classes
Radar feed<<TCP/IP>>
User
EventThread
Feed Parser
Timer
Tactical Separation Assisted Flight Environment (TSAFE)
Reengineering TSAFE
• Found all the synchronization statements in the code
(synchronize, wait, notify, notifyAll)
• Identified 6 shared objects protected by these synchronization statements
• Used 2 instances of a reader-writer controller and 3 instances of a mutex controller for synchronization
• In the reengineered TSAFE code the synchronization statements appear only in the Action helper class provided by the concurrency controller pattern
Behavior Verification Performance
RW 0.17 1.03
Mutex 0.01 0.23
Barrier 0.01 0.64
BB-RW 0.13 6.76
BB-Mutex 0.63 1.99
Controller Time(sec) Memory (MB) P-Time (sec) P-Memory (MB)
8.10 12.05
0.98 0.03
0.01 0.50
0.63 10.80
2.05 6.47
P denotes parameterized verification for arbitrary number of threads
Interface Verification Performance
Thread Time (sec) Memory (MB)
TServer-Main 67.72 17.08
TServer-RMI 91.79 20.31
TServer-Event 6.57 10.95
TServer-Feed 123.12 83.49
TClient-Main 2.00 2.32
TClient-RMI 17.06 40.96
TClient-Event 663.21 33.09
Fault Categories
• Concurrency controller faults– initialization faults (2) – guard faults (2)– update faults (6)– blocking/nonblocking faults (4)
• Interface faults– modified-call faults (8)– conditional-call faults
• conditions based on existing program variables (13)• conditions on new variables declared during fault
seeding (5)
Effectiveness in Finding Faults
• Created 40 faulty versions of TSAFE
• Each version had at most one interface fault and at most one behavior fault – 14 behavior and 26 interface faults
• Among 14 behavior faults ALV identified 12 of them– 2 uncaught faults were spurious
• Among 26 interface faults JPF identified 21 of them– 2 of the uncaught faults were spurious– 3 of the uncaught faults were real faults that were not
caught by JPF
Falsification Performance
TServer-RMI 29.43 24.74
TServer-Event 6.88 9.56
TServer-Feed 18.51 94.72
TClient-RMI 10.12 42.64
TClient-Event 15.63 12.20
Thread Time (sec) Memory (MB)
RW-8 0.34 3.26
RW-16 1.61 10.04
RW-P 1.51 5.03
Mutex-8 0.02 0.19
Mutex-16 0.04 0.54
Mutex-p 0.12 0.70
Concurrency Controller Time (sec) Memory (MB)
Conclusions
• ALV performance– Cost of parameterized verification was somewhere
between concrete instances with 8 and 16 threads– Falsification performance was better than verification
• Completeness of the controller properties– Effectiveness of behavior verification by ALV critically
depends on the completeness of the specified properties
• Concrete vs. parameterized behavior verification– When no faults are found, the result obtained with
parameterized verification is much stronger – However for falsification we observed that concrete
instances were as effective as parameterized instances
Conclusions
• JPF performance– Typically falsification performance is better than
verification performance– In some cases faults caused execution of new code
causing the falsification performance to be worse than verification performance
• Thread isolation– Automatic environment generation for threads result in
too much non-determinism and JPF runs out of memory– Dependency analysis was crucial for mitigating this
• Deep faults were difficult to catch using JPF– Three uncaught faults were created to test this
Conclusions
• Unknown shared objects– The presented approach does not handle this problem– Using escape analysis may help
• We could not find a scalable and precise escape analysis tool
• Environment generation – This is the crucial problem in scalability of the interface
verification– Using a design for verification approach for environment
generation may help
Related Work
• Design for Verification– [MP03] Mehlitz et al. promote using design patterns and
exploiting their properties for automated verification– [SBK01] Sharygina et al. focus on verification of UML
models– Design for verification has been used in hardware design
and embedded systems [SF03], [GSB02], [SBBCM01]
• Assume-guarantee style modular verification for software– [PDH99], [CCGJV03]
Related Work
• Design Patterns for multi-threaded systems– [SSRB00], [Lea99], [Gra02],[SPM96]
• Verification and synthesizing monitors– [DDHM02], [YKB02], [MK99]
• Environment extraction/generation– [PDH99],[TD03], [TDP03]