700 likes | 804 Views
Patrick Lam, Viktor Kuncak, Martin Rinard MIT CSAIL Massachusetts Institute of Technology Cambridge, MA 02139. Crosscutting Techniques in Program Specification and Analysis. Goal. Analyze Program to Verify Data Structure Consistency Properties Local Properties (o.next.prev = o)
E N D
Patrick Lam, Viktor Kuncak, Martin Rinard MIT CSAIL Massachusetts Institute of Technology Cambridge, MA 02139 Crosscutting Techniques in Program Specification and Analysis
Goal Analyze Program to Verify Data Structure Consistency Properties • Local Properties (o.next.prev = o) • Global Properties (no object in list & array)
Verifying “o.next.prev == o” proc add(o) proc delete(o) Must ensure that add() and delete() preserve the invariant.
Soundness Problem add(o) Seems like must also analyze the caller.
Encapsulate the Data Structure add(o) Seems like must also analyze the caller.
Modules Encapsulate Fields prev prev prev prev impl module List { format Item { next : Item; prev : Item; } … proc add(o:Item) { … } } impl module M { … } Key Idea: Only the List module may read and write to the next and prev fields. next next next next
Modules Encapsulate Fields prev prev prev prev impl module List { format Item { next : Item; prev : Item; } proc add(o:Item) { … } } impl module M { … } Key Idea: Only the List module may read and write to the next and prev fields. next next next next
Formats are Crosscutting Declarations procs here may access only next and prev impl module List { format Item { next : Item; prev : Item; } … } impl module Array { format Item { key : int; } … } impl module M { format Item {} … } Formats allow Hob to soundly analyze each module’s data structures in isolation, even though different modules share objects. procs here may access only key procs here may access no fields
Global Data Structure Consistency Properties Consider a process scheduler with a set of Running processes and a set of Idle processes. Running: all processes in running list Idle: all processes in idle array Necessary invariant P: RunningIdle = Running Idle
Where must Hob check that P holds? requires P ensures P Process Scheduler
What else must hold there? PQ P Q Q
Problem: Specification Aggregation! P Q R Q R Q P Q P Q R Q Q R • Specifications become unwieldy • Explicit callee preconditions break modularity
Beating Specification Aggregation These invariants crosscut the specifications for the entire program, and would need to be included in all potential callers. Scopes enable developers to factor out these invariants and state the invariant once and for all.
Our Solution to Specification Aggregation The developer: • Specifies a consistency property • Identifies a region where property may be temporarily violated during updates The Hob analysis system: • Verifies the property in the program’s initial state and upon on exit from the region; and • May soundly assume the property holds upon entry to the region and outside the region
Scopes in Example Scheduler Module suspend(p) • RunningIdle = may be violated anywhere within Scheduler, Idle Process, or Running Process modules • Scheduler must coordinate operations on Idle Process and Running Process Modules • Otherwise property may become permanently violated resume(p) add(p) ins(p) empty() rem(p) del() Idle Process Module Running Process Module
Example Scope scope C { invariant RunningIdle = ; modules scheduler, idle, running; export scheduler; } • Property holds except within modules in scope • Sets of property included in modules in scope • Outside scope, may only invoke procedures in exported modules
Scopes and Analysis System conjoins property to preconditions and postconditions of exported modules Analysis verifies that 1) property holds initially and 2) procedures preserve property Scheduler Module suspend(p) RunningIdle = resume(p) add(p) ins(p) empty() rem(p) del()
Multiple Orthogonal Scopes Scopes are independent of each other. scope W scope A scope B
Non-reentrant Outcalls If a call is non-reentrant, we check the invariant at callsite iff it is labelled as a public invariant. Other modules may then use public invariants to prove conditions as desired. Scope C Module M2 Module M1
Reentrant Outcalls Outcalls from scopes may also be reentrant! We verify all invariants at reentrant outcall sites. Scope C Module M2 Module M1
Benefits of Scopes Specifications may omit many properties that are locally irrelevant (aiding concision). In particular, can omit crosscutting callee preconditions at caller sites (aiding modularity).
Another Form of Specification Aggregation Consider an array-based data structure. Must allocate the array before calling data structure operations! specvar ready : bool; proc init() ensures ready'; proc add(p) requires ready ... ; s c
Another Form of Specification Aggregation Consider an array-based data structure. Must allocate the array before calling data structure operations! specvar ready : bool; proc init() modifies ready ensures ready'; proc add(p) requires ready ... ; s c explicit initialization constraint
Another Form of Specification Aggregation spec module Array • proc init() • modifies ready ensures ready'; • proc add(p) • requires ready & • ensures • proc remove(p) • requires ready & • ensures • proc isEmpty() • requires ready & • ensures
Applying Defaults Hob automatically conjoins defaults to appropriate ensures and requires clauses:
Applying Defaults Hob automatically conjoins defaults to appropriate ensures and requires clauses:
Extending the Applicability of Defaults Developers may specify default parameters: specvar Runnable : Item set; default pRunnable(p) = (card(p) = 1) & (p in Runnable) proc isEmpty(); proc add(p:Item); pRunnable not applied pRunnable applied
Applying Defaults at Crosscuts Developers may also specify a crosscut where a default is to apply: default pRunnable(p) : pre(all(scope C)) = (card(p) = 1) & (p in Runnable)
Default Pointcut Language P ::= P1 – P2 | P1 & P2 | P1|P2 | not P | pre S | post S | prepost S S ::= S1 – S2 | S1 & S2 | S1|S2 | not S | proc pn(tn1, …, tnk) returns tnr | exports (module ms) | exports (scope ss) | local (module ms) | local (scope ss) | all (module ms) | all (scope ss) | all
Defaults Improve Specifications • Convert errors of omission (i.e. missing clauses) into errors of commission. • Allow developers to write more concise specifications, focussing on locally important properties.
Hob Framework & Benchmarks • Implemented Hob System components: • Interpreter • Compiler to Java • Analysis framework • Pluggable analyses • Set/flag analysis • PALE analysis interface • Array analysis (VCs discharged via Isabelle) • Modules and programs • Data structures • Minesweeper, Water • HTTP server (http://hob.csail.mit.edu)
Benchmark 1: Minesweeper 750 lines of code, 236 lines of specification Full graphical interface (model/view/controller) Data structure consistency properties Board cell state correlations Correlations between state and actions
Minesweeper Scopes and Defaults default G = Board.gameOver | disjoint(Board.MinedCells, Board.ExposedCells) Note that G is maintained in the Board client, not in the Board itself. invariant (Board.ExposedCells = ExposedSet.Content) & (Board.HiddenCells = HiddenList.Content) This scope invariant ensures that the program state is self-consistent.
Benchmark 2: Water Time step computation, simulates liquid water Computation consists of sequence of steps Predict, correct, boundary box enforcement Inter and intra molecular force calculations 2000 lines of code, 500 lines of specification Typestate properties State correlations – simulation, molecules, atoms
Scopes and Defaults in Water Scope invariant for Ensemble: invariant (Ensemble.Init => Simparm.ParmsLoaded) & (Simparm.ParmsLoaded => Simparm.Init); defaults: mInit(m) = card(m) = 1 & (m in Init); (and similar defaults) I = Simparm.Init;
Scopes and Defaults Shrink Specifications Water: Without scopes and defaults: 24820 bytes With scopes and defaults: 20433 bytes Reduction: 21% Minesweeper: Without scopes and defaults: 12274 bytes With scopes and defaults: 12146 bytes Reduction: 1%
Scopes and Defaults Improve Specification Quality • By using scopes and defaults, developers: • avoid dependencies on callee modules (modularity) • may specify modules more concisely (readability) • are less likely to inadvertently omit needed clauses from specifications (completeness) • reduce the burden on the Hob system for verifying data structure consistency properties (scalability)
Relation to Aspect-Oriented Programming • Formats crosscut object declarations • Scopes and defaults crosscut specifications • Eliminate duplication • Simplify the specification • All constructs enhance modularity • Formats enhance modularity of the program • Scopes and defaults enhance modularity of the specification
Related Work AspectJ Intertype Declarations Crosscutting Module Systems • Decal’s Virtual Source Files (Janzen & Volder) • Hyperslices (Ossher & Tarr) Verifying Specifications for Aspect-Oriented Systems (Fisler and Krishnamurthi): finite-state properties of collaboration-based software designs Program checking tools: • ESC/Java (JML) • Spec#/Boogie
Conclusions Introduced three crosscutting constructs useful for implementing and specifying software systems. Formats allow Hob to locally reason about implementation properties. Scopes and defaults improve the quality of specifications and combat the specification aggregation problem.
Context Software Systems Composed of modules, with: • Encapsulated data structures • Exported procedures • Code
Issues • Scalability • Powerful analyses can verify complex data structure consistency properties • Very detailed model of data structures • Unthinkable to analyze complete program • Diversity • Different analyses for different data structures • New analyses for new data structures • Need for multiple targeted local analyses that interoperate to share information
Verifying “o.next.prev == o” module M add(o) module List Modules M and List share object o. impl module List { format Item { next : Item; prev : Item; } proc add(o:Item) { … } }
Where to verify consistency properties? module M add(o) module List impl module List { format Item { next : Item; prev : Item; } proc add(o:Item) { … } } Do we have to analyze module M as well?
Verifying Data Structure Consistency abstract set interface proc add() requires p not in S modifies S ensures S’ = S {p} acyclic root.next* acyclic root.next* abstraction function proc add() implementation concrete state
What Happens Next? invariant translated interface proc add() requires p not in root<next*> ensures root<next*>’ = root<next*> {p} frame acyclic root.next* analysis plugin proc add() implementation