What is SPIN(Simple Promela Interpreter) Lecture 3 SPIN and Promela A tool for analyzing mels of reactive systems Mels described in Promela Language with concurrent processes, Communication via channels, Analysis by Simulation Mel checking Several optimizations implemented most efcient tool for explicit-state mel checking 1 2 Material About SPIN SPIN Home page http://spinroot.com/spin/ Contains manuals, reference material, and tutorial Books about SPIN G.J. Holzmann SPIN MODEL CHECKER Primer and Reference Manual, G.J. Holzmann Design and Validation of Computer Protocols,, Prentice Hall 1991, older book, available on the Internet Elements of Promela Language for dening nite-state transition systems Data types with precisely dened nite mains Bits, integers, arrays, messages Processes, which can be dynamically created Communication via global variables or communication channels Simple control constructs 3 4 ypical Structure of Promela Mel Basic Variables and ypes Promela mel consists of type declarations channel declarations variable declarations process declarations init process mtype = {msg, ack; chan tos = chan tor = bool flag; proctype Sender () { proctype Receiver () { Basic types Array declaration Array access Records type denition Record declaration Record access Enumeration type f. messages bit [0.. 1] bool [0.. 1] byte [0.. 255] short [-2 15.. 2 15-1] int [-2 31.. 2 31-1] byte anarray[24]; anarray[v] = anarray[3]; typedef Msg{ byte a[3], b; chan p Msg astruct astruct.a[1] mtype = {ack, nak, err, next 5 6 1
Expressions Basic Statements Operators + - * / % ^ > >= < <= ==!=! && & - >> << ++ -- Expressions Assignments No-Op (y == false x > 9) y = 34 % 3 anarray[0] = anarray[3] * anarray[(v+2)/4] skip Conditional expression (v >= 0 -> v : -v) Goto goto label Operations on channel identers len(qid) empty(qid) full(qid) nempty(qid) nfull(qid) 7 printf Print (only in simulation) All basic statements are either executable (enabled) or blocked (disabled) (of course, depending on values of variables, etc.) Expressions are also statements Blocked evaluated to 0, otherwise executable In this way, expressions/statements can be used as guards 8 Compound Statements Compound Statements (ctd.) Sequential composition Selection test == 1 ; state = state + 1 test == 1 -> state = state + 1 :: (a == b) -> state = state + 1 :: (a!= b) -> state = state 1 equivalent Repetition :: (m > n) -> m = m - n :: (m < n) -> n = n m :: (m == n) -> break ; printf ( GCD = %d\n, m) :: (a == b) -> state = state + 1 -> state = state 1 Selection can be nonderministic :: input? offhook :: (a == b) -> b = 3 ; goto onhook :: output! wakeup :: b = 3 /*any statement can be guard*/ 9 10 Processes and process types Name proctype Sender(chan in; chan out) { Local variable declarations bit sndb, rcvb; :: out! data(sndb) -> in? ack(rcvb); formal parameters :: sndb == rcvb > sndb = 1-sndB -> skip Processes dened by proctype denitions A process type may be instantiated several times Each process has its local state (pc, local variables) Processes execute concurrently, by interleaving Process instantiations Processes are created by run statement/expression run returns process id Processes execute their rst statement some time after creation Processes can be statically created at initialization time (no formal parameters allowed) proctype Foo(byte x) { int pid = run Foo(2); run Foo(27) active[3] proctype Bar(){ 11 12 2
Communication Channels Communication Channels (ctd.) Channels used for passing messages Asynchronous (buffered, default is FIFO) Synchronously (rendez-vous) Declaring synchronous (rendez-vous) channel w. capacity 0 chan synch[3] = [0] of {mtype, int chan qid = [4] of {mtype, int, byte chan synch[3] = [0] of {mtype, int name capacity types of messages Sending qid! var1, const, var qid! err(const, var) matched Receiving qid? err(const, var) assigned Non-mying receive qid? [err, const, var] Sorted send/receive (inserts lexicographically, receives any element) qid!! err(const, var) qid?? err(const, var) 13 14 Peterson-Fischer Mutual Exclusion Peterson-Fischer Mutual Exclusion #dene true 1 #dene false 0 #dene turn1 false #dene turn2 true l2: t = turn2; l3: (y2 == false t == turn1) ; atomic{ y1 = false ; goto l1 m2: t = turn1; m3: (y1 == false t == turn2) ; atomic{ y2 = false ; goto m1 run P1() ; run P2() #dene true 1 #dene false 0 #dene turn1 false #dene turn2 true l2: t = turn2; l3: (y2 == false t == turn1) ; assert (mutex <= 1) ; mutex -- ; atomic{ y1 = false ; goto l1 m2: t = turn1; m3: (y1 == false t == turn2) ; assert (mutex <= 1) ; mutex-- ; atomic{ y2 = false ; goto m1 run P1() ; run P2() 15 16 Problem with Atomicity Solution: atomic-construct Only basic statements are atomic Q: What is the nal value of state? byte state = 1; proctype A() { state == 1 -> state++ proctype B() { state == 1 -> state-- run A() ; run B() A process whose control is inside atomic{ executes without being interrupted by other processes NOE: Make sure that such a sequence cannot be blocked inside (after the rst statement). In that case, Promela will suspend the process, and you get unintended semantics. byte state = 1; proctype A() { state == 1 -> state++ proctype B() { state == 1 -> state-- run A() ; run B() 17 18 3
Other use of atomic{ Else and imeout o group complex manipulation into single transition cnt = 0 ; :: (cnt < Max) -> z[cnt] = 3; cnt++ :: (cnt >= Max) -> break else is enabled no other statement in the same process is enabled :: (m > n) -> m = m - n :: (m < n) -> n = n m -> break ; printf ( GCD = %d\n, m) If the manipulation is deterministic and always exits at the end d_step { is more efcient d_step { cnt = 0 ; :: (cnt < Max) -> z[cnt] = 3; cnt++ :: (cnt >= Max) -> break 19 timeout is enabled no other statement in the entire Promela mel is enabled skip is best to use we want to be sure to analyze the effect of possibly premature timeout. :: input? offhook :: input? ringing :: timeout -> output! wakeup Od :: input? offhook :: input? ringing -> output! wakeup 20 Useful Macros Message ransmission Protocol IF without else branch Allows to write FOR loop Allows to write which means #dene IF :: #dene FI IF b -> x++ FI #dene FOR(i,l,h) i = l ; :: i < h -> #dene ROF(i,l,h) ;i++ :: i >= h -> break FOR(i,0,N) run proc(i) ROF(i,0,N) i = 0 ; :: i < N -> run proc(i) :: i >= N -> break mtype = { m0, m1, ack proctype Sender{ Send0: -> /* timeout */ StoR!m0 :: RtoS?ack -> /* rec ack */ SoR!m1; goto Send1 ; Send1: -> /* timeout */ StoR!m1 :: RtoS?ack -> /* rec ack */ StoR!m0; goto Send0 proctype Receiver{ Rec0: :: StoR?any -> skip /* loss */ :: StoR?m0 -> RtoS!ack; goto Rec1 Rec1: :: StoR?any -> skip /* loss */ :: StoR?m1 -> RtoS!ack; goto Rec0 chan StoR = [1] of { mtype ; chan RtoS = [1] of { mtype ; StoR!m0; /* start */ run Sender; run Receiver 21 22 Alternating Bit Protocol (version 1) Alternating Bit Protocol (version 1 altern) mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; r_s? ack(seqno); sbit = 1 sbit; byte recd ; bit rbit, seqno = 0; r_s! ack(seqno); :: seqno == rbit -> rbit = 1 rbit mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; proctype Sender() { r_s? ack(seqno); sbit = 1 sbit; proctype Receiver() { byte recd ; bit rbit, seqno = 0; r_s! ack(seqno); :: seqno == rbit -> rbit = 1 rbit run Sender(); run Receiver() 23 24 4
AB Protocol (version 2, with losses ) AB Protocol (version 3, w. retransmission) mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; r_s? ack(seqno); byte recd ; bit rbit, seqno = 0; :: r_s! ack(seqno) ; :: seqno == rbit -> rbit = 1 - rbit mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; :: s_r! msg(data, sbit) assert(recd == expected) ; 25 26 AB Protocol (version 4, w. checks) AB Protocol (version 5, limiting checks) mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; :: data < 10 -> s_r! msg(data, sbit) assert(recd == expected); mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; :: data < 10 -> s_r! msg(data, sbit) progress: assert(recd == expected) ; 27 28 AB Protocol (version 6, w progress) AB Protocol (version 7, w progress) mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; :: data < 10 -> s_r! msg(data, sbit) :: (1) -> progress1: skip progress: assert(recd == expected) ; :: (1) -> progress2: skip 29 # dene MAXMSG 4 mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; chan source = [0] of {byte; chan sink = [0] of {byte; source?data; :: s_r! msg(data, sbit) :: (1) -> progress1: skip source?data byte recd = 0; sink!recd; progress: rbit = 1 - rbit :: (1) -> progress2: skip 30 5
AB Protocol (version 7, w harness) AB Protocol (version 8, new harness) # dene MAXMSG 4 chan source = [0] of {byte; chan sink = [0] of {byte; active proctype Generator() { byte seed = 0; :: source!seed -> seed = (seed+1)%maxmsg active proctype Checker() { byte expected, inmsg= 0; assert(inmsg == expected); expected = (expected+1)%maxmsg # dene white 0 # dene red 1 # dene blue 2 chan source = [0] of {byte; chan sink = [0] of {byte; active proctype Generator() { :: source!white :: source!red -> break ; :: source!white :: source!blue -> break ; end: :: source!white active proctype Checker() { byte inmsg; :: (inmsg == red) -> break assert(inmsg == white) ; :: (inmsg == blue) -> break assert(inmsg == white) ; end1: assert(inmsg == white) 31 32 AB Protocol (version 9, w acceptance) AB Protocol (version 9, the never claim) #dene p Sender@Slabel #dene q Receiver@Rlabel #dene r Receiver@Rsuccess mtype = { msg, ack ; chan s_r = [2] of {mtype, byte, bit; chan r_s = [2] of {mtype, bit ; source?data; Slabel: skip source?data byte recd = 0; Rsuccess: sink!recd; rbit = 1 - rbit -> Rlabel: skip 33 #dene p Sender@Slabel #dene q Receiver@Rlabel #dene r Receiver@Rsuccess /* * Formula As yped: ([] <> p && [] <> q) -> [] <> r * he Never Claim Below Corresponds * o he Negated Formula!(([] <> p && [] <> q) -> [] <> r) * (formalizing violations of the original) */ never { /*!(([] <> p && [] <> q) -> [] <> r) */ 0_init: :: (! ((r)) && (p) && (q)) -> goto accept_s485 :: (! ((r)) && (p)) -> goto 2_S485 :: (! ((r))) -> goto 0_S485 :: (1) -> goto 0_init ; accept_s485: :: (! ((r))) -> goto 0_S485 ; 2_S485: :: (! ((r)) && (q)) -> goto accept_s485 :: (! ((r))) -> goto 2_S485 ; 0_S485: :: (! ((r)) && (p) && (q)) -> goto accept_s485 :: (! ((r)) && (p)) -> goto 2_S485 :: (! ((r))) -> goto 0_S485 ; 34 Specying invariant properties Checking by monitor Always executable If v <= 2 is false, SPIN exits with error Used to check invariants assert (v <= 2) #dene true 1 #dene false 0 #dene turn1 false #dene turn2 true l2: t = turn2; l3: (y2 == false t == turn1) ; mutex -- ; atomic{ y1 = false ; goto l1 m2: t = turn1; m3: (y1 == false t == turn2) ; mutex-- ; atomic{ y2 = false ; goto m1 active proctype monitor() { assert (mutex <= 1) run P1() ; run P2() 35 36 6
Checking deadlocks Checking progress by progress-labels #dene true 1 #dene false 0 #dene turn1 false #dene turn2 true l2: skip ; l3: (y2 == false) ; mutex -- ; atomic{ y1 = false ; goto l1 m2: skip ; m3: (y1 == false) ; mutex-- ; atomic{ y2 = false ; goto m1 active proctype monitor() { assert (mutex <= 1) run P1() ; run P2() 37 #dene MAX 5 mtype = { mesg, ack, nak, err ; proctype sender(chan in, out) { byte o, s, r; o=max-1; :: o = (o+1)%max; /* next msg */ again: :: out!mesg(o,s) /* send */ :: (1) -> progress1: out!err(0,0) /* distort */ :: (1) -> progress2: skip /* or lose */ ; :: timeout -> goto again :: in?err(0,0) -> goto again :: in?nak(r,0) -> goto again :: in?ack(r,0) -> :: (r == s) -> goto progress :: (r!= s) -> goto again ; progress: s = 1-s /* toggle seqno */ proctype receiver(chan in, out) { byte i; /* actual input */ byte s; /* actual seqno */ byte es; /* expected seqno */ byte ei; /* expected input */ :: in?mesg(i, s) -> :: (s == es) -> assert(i == ei); progress: es = 1 - es; ei = (ei + 1)%MAX; /* send, */ :: out!ack(s,0) /* distort */ :: (1) -> out!err(0,0) :: (s!= es) -> /* send, */ :: out!nak(s,0) /* distort */ :: (1) -> out!err(0,0) :: in?err(0,0) -> out!nak(s,0) 38 Checking progress (ctd.) Progress labels: improved version chan s_r = [1] of { mtype,byte,byte ; chan r_s = [1] of { mtype,byte,byte ; run sender(r_s, s_r); run receiver(s_r, r_s) #dene MAX 5 mtype = { mesg, ack, nak, err ; proctype sender(chan in, out) { byte o, s, r; o=max-1; :: o = (o+1)%max; /* next msg */ again: :: out!mesg(o,s) /* send */ :: (1) -> progress1: out!err(0,0) /* distort */ proctype receiver(chan in, out) { byte i; /* actual input */ byte s; /* actual seqno */ byte es; /* expected seqno */ byte ei; /* expected input */ :: in?mesg(i, s) -> :: (s == es) -> assert(i == ei); progress: es = 1 - es; ei = (ei + 1)%MAX; 39 :: (1) -> progress2: skip /* or lose */ ; :: timeout -> goto again :: in?err(0,0) -> goto again :: in?nak(r,0) -> goto again :: in?ack(r,0) -> :: (r == s) -> goto progress :: (r!= s) -> goto again ; progress: s = 1-s /* toggle seqno */ /* send, */ :: out!ack(s,0) /* distort */ :: (1) -> progress1: out!err(0,0) :: (1) -> progress2: skip :: (s!= es) -> /* send, */ :: out!nak(s,0) /* distort */ :: (1) -> progress3: out!err(0,0) :: (1) -> progress4: skip :: in?err(0,0) -> out!nak(s,0) 40 Automata properties: never claims Automata specications can be given in Promela as Never claims, e.g., ( p -> p ) p p never { :: p -> break ; ::!p -> break Never claims execute in lock-step with the rest of the Promela mel Accept they reach the end Buchi Automata as never claims Accepting states designated by labels acceptxxxxx ( p /\ q) p /\ q q never { :: p &&!q -> break ; accept: ::!q he never claim accepts the Promela mel has cycle with only!q hen SPIN reports a violating cycle. 41 42 7