1 using model checking to validate style-specific architectural refactoring patterns zoë stephenson...

36
1 Using Model Checking to Validate Style-Specific Architectural Refactoring Patterns Zoë Stephenson John McDermid

Upload: adrian-todd

Post on 14-Dec-2015

230 views

Category:

Documents


0 download

TRANSCRIPT

1

Using Model Checking to Validate Style-Specific Architectural

Refactoring Patterns

Zoë StephensonJohn McDermid

2

Overview

• Motivation• Description of HADES• Underlying semantics• Refactoring• Translation for model checking• Analysis of refactoring patterns• Conclusions / further work

3

Motivation

• Researching into families of dependable control systems– Families are related applications that exhibit small, well-defined

variations

• Control systems traditionally defined through control diagrams– Simple hierarchical structure, based on physical concepts– Not specifically tailored for software

• “How do we get control diagrams that are suitable for software families?”– Use software architecture techniques

4

Software Architecture

• To help with product families– Interface defined by data items communicated

• Same paradigm as control law diagrams• Component boundary is individual data areas

– Composition by matching interface data areas

• To help with dependability– Static hierarchy of packages and components

• Also provides containment of elements that vary

– Low-level component can communicate outside of high-level interfaces

• e.g. to communicate fault information appropriately

• Need to be able to guarantee that communication is possible when components are combined

5

Software Architecture

• Looked at several architectural styles– Wright, Darwin, HRT-HOOD, Mascot/RTN...

• Looked at several other ideas– UML, Simulink

• No existing style matched well enough to have the type of composition needed and also show whether the architecture works when composed

• Would need to make modifications– No confidence that the type of composition does allow for a

guarantee that the architecture works• Invented new, simple ADL to experiment with ideas

– Results provide confidence needed to apply ideas to existing, mature ADL

– Everything must be understandable by the control engineers too

6

HADES

• Hierarchical Architecture for Dependable Embedded Systems

• Based on ideas from HRT-HOOD and MASCOT/RTN– Network of activities and data areas– Cyclic behaviour

• Collect inputs, process, produce outputs, loop

• Adapted to include new interface concept– Interfaces defined in terms of communication with data areas– Low-level interfaces can operate outside of communications

defined at higher level

7

HADESP

I: Signal

O: Signal

D: Pool

A

B

Integer

Integer

Integer

Activity

Data Area

Communication

Initiation

8

Signal Protocol

– Single value– May initiate up to all but one of its connections

• Outgoing connections triggered by incoming connections

– Data value is used immediately rather than stored– Represents procedure call or function call

communication

D: SignalA B D: SignalA B

9

Pool Protocol

– Single value– May not initiate connections

• Separate incoming connections for reading and writing

– Most recently written data value is retained and transmitted when requested

– Represents single shared variable communication

D: PoolA B

10

Semantics

• Communications define:– Data flow direction– Initiation responsibility (source or sink)– Termination (immediate or blocking)

• To model arbitrary networks of activities and data areas, we use transition-constrained finite state machines– One machine per activity and data area– Constraints to represent the communication structure

• Constraints also represent parent-child relationships

– View of semantics that is familiar to engineers

11

State Machine Semantics

I: Signal

A

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

W

WIn

CIn

Proc

Susp

Cout

WRel

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

R;M:releasedQ;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

R;M:released

12

Problematic Structure

P

D1

D2

A

B

Integer

Integer

13

D1: Signal

B

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

W

WIn

CIn

Proc

Susp

Cout

WRel

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

A

W

WIn

CIn

Proc

Susp

Cout

WRel

D2: Signal

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

P

W

WIn

CIn

Proc

Susp

Cout

WRel

W

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

R; M: released

W

R; M: released

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

W

R; M: released

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

D1: Signal

B

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

W

WIn

CIn

Proc

Susp

Cout

WRel

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

A

W

WIn

CIn

Proc

Susp

Cout

WRel

D2: Signal

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

P

W

WIn

CIn

Proc

Susp

Cout

WRel

W

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

R; M: released

W

R; M: released

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

W

R; M: released

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

State Machine View

Constraint 1

Constraint 2

14

Solutions

• More complex semantics– Allow outputs at different times– Reduces claim to ‘simplicity’

• Fewer constraints– Reduces usefulness of synchronisation model

• Change the architecture, preserve intent– What change needs to be made?

• Analysis of the whole model will only show whether a problem exists, not necessarily where or how to fix it

• We choose to change the architecture

15

Altered Structure

P

D1

D2

A1

B

Integer

Integer

A2

DInt

Integer

16

D1: Signal

B

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

W

WIn

CIn

Proc

Susp

Cout

WRel

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

A2

W

WIn

CIn

Proc

Susp

Cout

WRel

D2: Signal

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

P

W

WIn

CIn

Proc

Susp

Cout

WRel

W

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

R; M: released

W

R; M: released

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

W

R; M: released

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

A1

W

WIn

CIn

Proc

Susp

Cout

WRel

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

DInt: Signal

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

W

R; M: released

D1: Signal

B

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

W

WIn

CIn

Proc

Susp

Cout

WRel

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

A2

W

WIn

CIn

Proc

Susp

Cout

WRel

D2: Signal

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

P

W

WIn

CIn

Proc

Susp

Cout

WRel

W

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

R; M: released

W

R; M: released

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

W

R; M: released

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

A1

W

WIn

CIn

Proc

Susp

Cout

WRel

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

DInt: Signal

Integer

W

WIn

Rx

Str

Ret

Tx

WRel

W

R; M: released

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

W

R; M: released

Altered State Machine View

17

Refactoring Patterns

• Refactoring:– Preserve intended behaviour– Eliminate unintended consequences

• Four refactoring patterns identified– Function call / return– Submerge bidirectional interaction– Feedback loop push pool– Feedback loop pull pool

18

Refactoring Patterns

19

Refactoring Patterns

20

Validation

• Validation criteria:– Before refactoring, a problem exists– After refactoring, the problem is removed– After refactoring, the intended behaviour of the pre-

refactoring structure is preserved

• Initial validation performed by visualising structures in a domain-specific simulator– Especially useful for demonstrating intended

behaviour

• We used model checking to ensure coverage– Reused framework from the simulator tool

21

Model Checking StrategyDraw Tool Model

Translate

XML Model

Visualise

Validation

Translate

SPIN/NuSMV Model

Check

ValidationExisting tools

New tools

22

Example Input

• Automatically translated into XML

23

Example Visualised

24

Example State Machine View

A1

W

WIn

CIn

Proc

Susp

Cout

WRel

W

InputData: Pool

W

WIn

Rx

Str

Ret

Tx

WRel

Q:requested

I:connected_in

S:supplied

N:requested

O:connected_out

D:delivered

W

M:releasedC:cachedR:released

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

R; M: released

OutputData: Signal

W

WIn

Cx

Str

Ret

Tx

WRel

W

Q;N:requested

I:connected_in

S:supplied

C:cached

O:connected_out

D:delivered

R; M: released

25

Translation

• Assumption– Model is correct if each component’s “W” state is always

reachable via the other states

• Three steps– Represent state of each component (data area and activity)– Represent the rules for updating the state of each component– Compose the rules into a complete model

• Different underlying models in SPIN and NuSMV– Interleaved vs. synchronous– Impacts on the representation of rules for updating state

26

NuSMV Strategy

sharedstateVAR state for each componentSPEC at least one state change

component(shared)TRANS case constraint & state = X : next(state) = Y; 1 : next(state)=state;SPEC initial ‘W’ state always reachable

mainVAR instantiates shared stateand components

• Rules localised to components

27

NuSMV ExampleMODULE sharedstate

VAR

odsstate : { W, WIn, Rx, Str, Ret, Tx, WRel};

astate : { W, WIn, CIn, Proc, Susp, COut, WRel};

idpstate : { W, WIn, Rx, Str, Ret, Tx, WRel};

SPEC

AG (odsstate = W & astate = W & idpstate = W) -> AX ! (odsstate = W & astate = W & idpstate = W)

MODULE odscomponent(shared)

ASSIGN

init(shared.odsstate) := W;

TRANS

case

shared.odsstate = Ret : next(shared.odsstate) = Tx;

shared.astate = Proc & shared.odsstate = W & shared.odsstate = W : next(shared.odsstate) = WIn;

shared.odsstate = WRel & shared.odsstate = WRel : next(shared.odsstate) = W;

shared.odsstate = Str : next(shared.odsstate) = Ret;

shared.odsstate = WIn & shared.astate = Susp : next(shared.odsstate) = Rx;

shared.odsstate = WRel & shared.odsstate = WRel : next(shared.odsstate) = W;

shared.odsstate = Tx : next(shared.odsstate) = WRel;

shared.odsstate = Rx : next(shared.odsstate) = Str;

shared.astate = Proc & shared.odsstate = W & shared.odsstate = W : next(shared.odsstate) = WIn;

1 : next(shared.odsstate) = shared.odsstate;

esac;

SPEC

AG AF shared.odsstate = W

MODULE acomponent(shared)

ASSIGN

init(shared.astate) := W;

TRANS

case

shared.odsstate = WIn & shared.astate = Susp : next(shared.astate) = COut;

shared.astate = W & shared.idpstate = W & shared.astate = W : next(shared.astate) = WIn;

shared.astate = WRel & shared.astate = WRel : next(shared.astate) = W;

shared.astate = Proc & shared.odsstate = W & shared.odsstate = W : next(shared.astate) = Susp;

shared.astate = WIn & shared.idpstate = Ret : next(shared.astate) = CIn;

shared.astate = WRel & shared.astate = WRel : next(shared.astate) = W;

shared.astate = COut : next(shared.astate) = WRel;

shared.astate = CIn : next(shared.astate) = Proc;

shared.astate = W & shared.idpstate = W & shared.astate = W : next(shared.astate) = WIn;

1 : next(shared.astate) = shared.astate;

esac;

SPEC

AG AF shared.astate = W

MODULE idpcomponent(shared)

ASSIGN

init(shared.idpstate) := W;

TRANS

case

shared.astate = WIn & shared.idpstate = Ret : next(shared.idpstate) = Tx;

shared.astate = W & shared.idpstate = W & shared.astate = W : next(shared.idpstate) = Ret;

shared.idpstate = WRel : next(shared.idpstate) = W;

shared.idpstate = Str & shared.idpstate = Str : next(shared.idpstate) = W;

shared.idpstate = WIn : next(shared.idpstate) = Rx;

shared.idpstate = Str & shared.idpstate = Str : next(shared.idpstate) = W;

shared.idpstate = Tx : next(shared.idpstate) = WRel;

shared.idpstate = Rx : next(shared.idpstate) = Str;

shared.idpstate = W : next(shared.idpstate) = WIn;

1 : next(shared.idpstate) = shared.idpstate;

esac;

SPEC

AG AF shared.idpstate = W

MODULE main

VAR

shared: sharedstate;

theods: odscomponent(shared);

thea: acomponent(shared);

theidp: idpcomponent(shared);

MODULE sharedstate

VAR

odsstate : { W, WIn, Rx, Str, Ret, Tx, WRel};

astate : { W, WIn, CIn, Proc, Susp, COut, WRel};

idpstate : { W, WIn, Rx, Str, Ret, Tx, WRel};

SPEC

AG (odsstate = W & astate = W & idpstate = W) -> AX ! (odsstate = W & astate = W & idpstate = W)

MODULE odscomponent(shared)ASSIGN init(shared.odsstate) := W;TRANS case shared.odsstate = Ret : next(shared.odsstate) = Tx; shared.astate = Proc & shared.odsstate = W & shared.odsstate = W : next(shared.odsstate) = WIn; shared.odsstate = WRel & shared.odsstate = WRel : next(shared.odsstate) = W; shared.odsstate = Str : next(shared.odsstate) = Ret; shared.odsstate = WIn & shared.astate = Susp : next(shared.odsstate) = Rx; shared.odsstate = WRel & shared.odsstate = WRel : next(shared.odsstate) = W; shared.odsstate = Tx : next(shared.odsstate) = WRel; shared.odsstate = Rx : next(shared.odsstate) = Str; shared.astate = Proc & shared.odsstate = W & shared.odsstate = W : next(shared.odsstate) = WIn; 1 : next(shared.odsstate) = shared.odsstate; esac;SPEC AG AF shared.odsstate = W

MODULE mainVAR shared: sharedstate; theods: odscomponent(shared); thea: acomponent(shared); theidp: idpcomponent(shared);

28

NuSMV State Machine View

A1

Proc

OutputData: Signal

W

WIn

Q;N:requested

shared.astate = Proc & shared.odsstate = W & shared.odsstate = W :next(shared.odsstate) = WIn;

29

SPIN Strategy

global datamtype state for each component

constraintproctype constraint() { endconstraint: do :: atomic { if :: constraints -> update :: else -> skip fi } od}

initInstantiate constraints with run

• Rules localised to transition constraints

30

SPIN Example

mtype { W, WIn, CIn, Proc, Susp, COut, WRel, Rx, Str, Ret, Tx };

mtype odsstate = W;

mtype astate = W;

mtype idpstate = W;

proctype odsods_1() {

endodsods_1: do

:: atomic {

if

:: (odsstate == WRel) ->

odsstate = W;

printf("odsstate = W\n");

:: else -> skip

fi

}

od

}

proctype aodsods_1() {

endaodsods_1: do

:: atomic {

if

:: (odsstate == W && astate == Proc) ->

odsstate = WIn;

printf("odsstate = WIn\n");

astate = Susp;

printf("astate = Susp\n");

:: else -> skip

fi

}

od

}

proctype aidpa_1() {

endaidpa_1: do

:: atomic {

if

:: (astate == W && idpstate == W) ->

astate = WIn;

printf("astate = WIn\n");

idpstate = Ret;

printf("idpstate = Ret\n");

:: else -> skip

fi

}

od

}

proctype odsa_1() {

endodsa_1: do

:: atomic {

if

:: (odsstate == WIn && astate == Susp) ->

odsstate = Rx;

printf("odsstate = Rx\n");

astate = COut;

printf("astate = COut\n");

:: else -> skip

fi

}

od

}

proctype idp_1() {

endidp_1: do

:: atomic {

if

:: (idpstate == Rx) ->

idpstate = Str;

printf("idpstate = Str\n");

:: else -> skip

fi

}

od

}

proctype idpidp_1() {

endidpidp_1: do

:: atomic {

if

:: (idpstate == Str) ->

idpstate = W;

printf("idpstate = W\n");

:: else -> skip

fi

}

od

}

proctype a_1() {

enda_1: do

:: atomic {

if

:: (astate == COut) ->

astate = WRel;

printf("astate = WRel\n");

:: else -> skip

fi

}

od

}

proctype ods_1() {

endods_1: do

:: atomic {

if

:: (odsstate == Tx) ->

odsstate = WRel;

printf("odsstate = WRel\n");

:: else -> skip

fi

}

od

}

proctype a_2() {

enda_2: do

:: atomic {

if

:: (astate == CIn) ->

astate = Proc;

printf("astate = Proc\n");

:: else -> skip

fi

}

od

}

proctype idp_2() {

endidp_2: do

:: atomic {

if

:: (idpstate == W) ->

idpstate = WIn;

printf("idpstate = WIn\n");

:: else -> skip

fi

}

od

}

proctype ods_2() {

endods_2: do

:: atomic {

if

:: (odsstate == Str) ->

odsstate = Ret;

printf("odsstate = Ret\n");

:: else -> skip

fi

}

od

}

proctype aidp_1() {

endaidp_1: do

:: atomic {

if

:: (astate == WIn && idpstate == Ret) ->

astate = CIn;

printf("astate = CIn\n");

idpstate = Tx;

printf("idpstate = Tx\n");

:: else -> skip

fi

}

od

}

proctype idp_3() {

endidp_3: do

:: atomic {

if

:: (idpstate == Tx) ->

idpstate = WRel;

printf("idpstate = WRel\n");

:: else -> skip

fi

}

od

}

proctype ods_3() {

endods_3: do

:: atomic {

if

:: (odsstate == Ret) ->

odsstate = Tx;

printf("odsstate = Tx\n");

:: else -> skip

fi

}

od

}

proctype aa_1() {

endaa_1: do

:: atomic {

if

:: (astate == WRel) ->

astate = W;

printf("astate = W\n");

:: else -> skip

fi

}

od

}

proctype idp_4() {

endidp_4: do

:: atomic {

if

:: (idpstate == WIn) ->

idpstate = Rx;

printf("idpstate = Rx\n");

:: else -> skip

fi

}

od

}

proctype idp_5() {

endidp_5: do

:: atomic {

if

:: (idpstate == WRel) ->

idpstate = W;

printf("idpstate = W\n");

:: else -> skip

fi

}

od

}

proctype ods_4() {

endods_4: do

:: atomic {

if

:: (odsstate == Rx) ->

odsstate = Str;

printf("odsstate = Str\n");

:: else -> skip

fi

}

od

}

init {

printf("odsstate = W\n");

printf("astate = W\n");

printf("idpstate = W\n");

run odsods_1();

run aodsods_1();

run aidpa_1();

run odsa_1();

run idp_1();

run idpidp_1();

run a_1();

run ods_1();

run a_2();

run idp_2();

run ods_2();

run aidp_1();

run idp_3();

run ods_3();

run aa_1();

run idp_4();

run idp_5();

run ods_4();

}

mtype { W, WIn, CIn, Proc, Susp, COut, WRel, Rx, Str, Ret, Tx };mtype odsstate = W;mtype astate = W;mtype idpstate = W;proctype aodsods_1() { endaodsods_1: do :: atomic { if :: (odsstate == W && astate == Proc) -> odsstate = WIn; printf("odsstate = WIn\n"); astate = Susp; printf("astate = Susp\n"); :: else -> skip fi } od}

init { printf("odsstate = W\n"); printf("astate = W\n"); printf("idpstate = W\n"); run odsods_1(); run aodsods_1(); run aidpa_1(); run odsa_1(); run idp_1(); run idpidp_1(); run a_1(); run ods_1(); run a_2(); run idp_2(); run ods_2(); run aidp_1(); run idp_3(); run ods_3(); run aa_1(); run idp_4(); run idp_5(); run ods_4();}

31

SPIN State Machine ViewA1

Proc

OutputData: Signal

W

WIn

Q;N:requested

if:: (odsstate == W && astate == Proc) -> odsstate = WIn; printf("odsstate = WIn\n"); astate = Susp; printf("astate = Susp\n");:: else -> skipfi

Susp

C: Computed

32

Results

Model Expect Simulator NuSMV SPIN

Deadlock Livelock Deadlock Livelock Deadlock Livelock Deadlock Livelock

f13 Yes No Yes No Yes No Yes No

f14 No No No No No No No No

f18 Yes No Yes No Yes No Yes No

f19 No No No No No No No No

fpush Yes No Yes No Yes No Yes No

f17a No No Yes No Yes No Yes No

fpull Yes No Yes No Yes No Yes No

f17b No No Yes No Yes No Yes No

x1 Yes No No No No No No No

x2 No No No No No No No No

lraam No No No Yes No Yes - -

f28 No No Yes No Yes No Yes No

lraam-mod No No - - No No No No

Function call pattern came out entirely as expectedSubmerge pattern also came out as expectedFeedback push pattern needed asynchronous terminationFeedback pull pattern had the same problemWe created some further examples to increase confidence in the simulatorThis translation of an RTN structure for a missile launch controller could not complete when analysed in SPIN

The problem was with unconstrained pools on the edge of an architectural model, we introduced ‘source’ and ‘sink’ protocols to solve the problem

This is figure 28 from the original HADES manual, a thrust reverser controller – it also has the synchronous termination problem like the feedback loops

33

Unconstrained Pool Problem

• If only one side of a pool is attached, the machine can just loop round the unattached side

• Invented ‘source’ and ‘sink’ component types that are just one half of a pool

• Possible to convert one sink and one source into a pool when composing

A1

W

WIn

CIn

Proc

Susp

Cout

WRel

W

InputData: Pool

W

WIn

Rx

Str

Ret

Tx

WRel

Q:requested

I:connected_in

S:supplied

N:requested

O:connected_out

D:delivered

W

M:releasedC:cachedR:released

Q;N:requested

I:connected_in

S:supplied

C:computed

O:connected_out

D:delivered

R; M: released

34

Evaluation

• Simulator and model checker results consistent– Model checker identifies livelock and deadlock– Model checker known to exhaustively search for

problems– Simulator gives more immediate feedback to the user

in identifying what to change to fix the problem

• Simulator, SPIN and NuSMV are equally usable for fully-constrained models

• Demonstrates ability to reason about whether components operate correctly when composed

35

Conclusions

• Simple ADL just to check that ideas are sound– Refactoring patterns to match user intent to the

simple synchronisation model– Patterns needed validation

• Partial validation using a visualisation technique• Not necessarily exhaustive

• Validation completed with model checkers– Obtained consistent results– Choice of tool does not affect ability to obtain results

• Other criteria e.g. user experience, tool availability, tool integration would come into play instead

36

Further Work

• Apply ideas to existing ADL– AADL, UML, Simulink…

• Integrate model checkers into visualisation tool– Automatic invocation– Visualisation of results in terms of the architecture

• Examine real time / data storage issues– Bottlenecks– Blocking on queues

• Examine model-based design issues– Integration of previous work

• Activity specifications• Translation to programming language structures (packages)