Application of Design for Verification with Concurrency Controllers to Aysu Betin-Can,

advertisement
Application of Design for Verification
with Concurrency Controllers to
Air traffic Control Software
Aysu Betin-Can, Tevfik Bultan
Mikael Lindvall, Benjamin Lux, Stefan Topp
Department of Computer Science
University of California, Santa Barbara
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 lock
synchronized(o) { ... }
• Coordination using condition variables
– Objects can be used as condition variables
synchronized (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
Concurrency Controller Pattern
• 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
ThreadA
Shared
Controller
ThreadB
Shared
SharedStub
+a()
+b()
+a()
+b()
Helper
classes
Action
+blocking()
+nonblocking()
-GuardedExecute
used at runtime
used at interface verification
used both times
Controller
-var1
-var2
+action1()
+action2()
int
ControllerStateMachine
+action1()
+action2()
GuardedCommand
StateMachine
GuardedCommand
+guard()
+update()
Reader-Writer Controller
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();}
}
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)).u
pdate();
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;
r_enter
reading final static int idle=0,reading=1,
writing=2;
public RWStateMachine(){ ...
r_exit
stateTable.insert("w_enter",idle,writing);
idle
}
w_exit
public void w_enter(){
stateTable.transition("w_enter");
writing }
w_enter
...
}
Verification Framework
Behavior
Specification
in Action
Language
Controllers
and
Interfaces
Action Language
Verifier
Behavior Translator
Counting Abstractor
Data Stubs
Behavior Verification
Notification-Optimizer
Error Trace
Verified
Optimized
Java Code
Interface Verification
Java PathFinder
Thread Isolation
Program with
Stubs
Rest of the
Program
Error Trace
Verified
Modular Verification
• 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.
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
Tactical Separation Assisted Flight Environment (TSAFE)
User
Radar feed
<<TCP/IP>>
Feed Parser
Server
Client
Flight
Database
EventThread
<<RMI>>
Graphical
Client
Computation
21,057 lines of code with 87 classes
Timer
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
Controller
Time(sec)
Memory (MB) P-Time (sec)
P-Memory (MB)
RW
0.17
1.03
8.10
12.05
Mutex
0.01
0.23
0.98
0.03
Barrier
0.01
0.64
0.01
0.50
BB-RW
0.13
6.76
0.63
10.80
BB-Mutex
0.63
1.99
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
Thread
Time (sec)
Memory (MB)
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
Concurrency Controller
RW-8
Time (sec)
0.34
Memory (MB)
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
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]
Related Work
• Behavioral and stateful interfaces
– Interface compatibility checking [CAHJM02]
– Extended type systems with stateful interfaces and
interface checking as a part of type checking
[DF01,DF04]
– Interface discovery and synthesis by analyzing existing
code [WML02],[ACMN05]
Download