490 likes | 708 Views
An Overview of PROMELA. A protocol Validation Language. A notation for the specification and verification of procedure rules. A partial description of a protocol called validation model . The language: PROMELA
E N D
A protocol Validation Language • A notation for the specification and verification of procedure rules. • A partial description of a protocol called validation model. • The language: PROMELA • Model: succinctly as possible to study protocol structure and to verify completeness and logical consistency.
Goal • Abstract from other issues of protocol design, such as message format. • A validation model defines the interactions of processes in a distributed system. • It does not resolve implementation details • It does not say how a message is to be transmitted or stored. • Concentrate on • the design of a complete and consistent set of rules to govern the interactions in a distributed system
Processes, Channels, Variables A validation model is defined in terms of: • processes • message channels • state variables • Each object can be translated into a finite state machine • Processes are global objects
Variables, Statements • Variables and channels represent data that can be either global or local to a process. • Excitability of Statements
Conditions == Statements • Execution of a statement is conditional on it executability. • Statement: executable or blocked. • Excitability: basic means of synchronization. • A process can wait for an event to happen by waiting for a statement to become executable
No Busy Wait • In C: • while(a != b) skip • Becomes in PROMELA: • (a == b) • If the condition does not hold, execution blocks until it does.
Variables and Data Types • A variable can be one of the following six predefined data types: • bit, bool, byte, short, int, chan. • The sixth type specifies message channels - an object that can store a number of values, grouped in user-defined structures.
The declarations: bool flag; int state; byte msg; • define variables that can store integer values in three different ranges.
Arrays • byte state[N] • state[0] = state[3] + 5 * state[3*2/n] • Where n is a constant or variable defined elsewhere.
Process Types • A process must have a name. • All types of processes that can be instantiated are defined in proctype declarations. • proctype A() {byte state; state=3} • PROMELA defines two statement separators: • · an arrow -> • · a semicolon ; • The two separators are equivalent.
The Initial Process • A proctype definition only declares process behavior, it does not execute it. • Initially, only one process is executed: a process of type init which must be declared explicitly. • The smallest possible PROMELA specification is: • init { skip } • skip is a null statement.
init • The initial process can initialize global variables, create message channels, and instantiate processes. • init { run A(); run B() } • Processes A and B will run concurrently with init. • The run statement is executable and returns a positive result only if the process can effectively be instantiated.
pid • It is non-executable and returns zero if this cannot be done. • The value returned by run is a run-time process number, or pid. • I = run A() && (run B() || run C() )
run A • run can pass parameter values to the process: • proctype A(byte state;short set) • {(state==1)->state=set} • init { run A(1, 3) }
run • An executing process disappear when it terminates Not before all the processes it instantiated have terminated first. • byte state = 1; • proctype A() { (state == 1) -> state = state + 1 } • proctype B() { (state == 1) -> state = state - 1 } • init { run A(); run B() }
Termination • If one process terminates before the other starts, the latter will block forever. • If both pass the condition checks at the same time, the value of state may be 0, 1, or 2.
#define true 1 • #define false 0 • #define Aturn 1 • #define Bturn 0 • bool x, y, t; • proctype A(){ x = true; t = Bturn; • (y == false || t == Aturn); • /* critical section */ • x = false;} • proctype B(){ y = true; t = Aturn; • (x == false || t == Bturn); • /* critical section */ • y = false;} • init { run A(); run B() }
Atomic Sequences • A sequence of statements in parentheses prefixed with atomic indicates that the sequence be executed as one indivisible unit. • byte state = 1; • proctype A() {atomic{(state==1)-> state = state + 1 }} • proctype B() {atomic{(state==1)-> state = state - 1 }} • init { run A(); run B() }
In this case the final value of state is either 0 or 2. • Atomic sequences can be an important tool in reducing the complexity of a validation model.
proctype nr(short pid, a, b) • { int res; • atomic {res = (a*a+b)/2*a; • printf(“result %d: %d\n”, pid, res) } • } • init {run nr(1,1,1);run nr(1,2,2); run nr(1,3,2) }
The use of atomic sequences reduces the complexity of the outcome due to the avoidance of interleaved behavior.
Message Channels • Used to transfer data from one process to another. • chan a, b; chan c[3]; • or • chan c[3] = [4] of { byte } • Initializes an array of 3 channels, each with a capacity of 4 message slots, each slot consisting of one message field of type byte.
chan qname = [16] of { byte, int, chan, byte } • Channel qname can store up to 16 messages each of which has 4 fields as above.
The statement • qname!expr • sends the value expression expr to the channel qname. • It appends expr to the tail of the implied queue. • qname?msg • retrieves a message from the head of channel qname and stores it in msg.
Channels are FIFO Multiple values are specified: • qname!expr1,expr2,expr3 • If more parameters are sent than the channel can store, the redundant parameters are lost. • If fewer parameters are sent than the channel can store, the value of the remaining parameters is undefined.
By convention, the first message field is often used to specify the message type. • qname!expr1(expr2,expr3) • The send operation is executable only when the channel addressed is not full. • The receive operation is executable only when the channel is non-empty.
Some of the arguments in the receive can be constant: • qname?cons1,var2,cons2 • In this case, the receive is executable only when the value of all message fields specified as constants match the value of the corresponding fields in the message at the head of the channel.
proctype A(chan q1) • { chan q2; • q1?q2; • q2!123; } • proctype B(chan qforb) • { int x; • qforb?x; • printf(“x = %d\n”, x) } • Init { • chan qname[2] = [1] of { chan }; • chan qforb = [1] of { int }; • run A(qname[0]);run B(qforb); • qname!qforb; }
The channel qforb is not declared as an array and therefore the send and the end of the initial process do not need an index. • The value printed by B will be 123. • Send and receive may have side-effects: • (qname?var == 0) • or • (a > b && qname!123) • are invalid in PROMELA.
qname?[ack,var] • is evaluated as condition and can be combined with other Boolean expressions. • It returns a positive result if the corresponding receive statement • qname?ack,var • would be executable.
Rendezvous Communication • So far we have discussed asynchronous communications. • Using a channel size of zero: • chan port = [0] of { byte } • defines a rendezvous port that can pass only a single-byte messages.
#define msgtype 33 • chan name = [0] of { byte, byte }; • proctype A() • { name!msgtype(124); • name!msgtype(121); • } • proctype B() • { byte state; • name?msgtype(state) • } • init • { atomic { run A(); run B() } • }
The two processes synchronously execute a handshake on message msgtype and transfer of 124 to variable state. • The second statement in process A is non-executable.
Control Flow • We have seen three types of control flows: • · concatenation of statements within a process • · parallel execution of processes • · atomic sequences • Three other control flow constructs in PROMELA: • · case selection • · repetition • · unconditional jumps
Case Selection • Using the relative values of two variables a and b to choose between two options. • if • :: ( a != b ) -> option1 • :: ( a == b ) -> option2 • fi • The first statement is called a guard. • If all guards are non-executable, the process blocks until at least one can be selected.
#define a 1 • #define b 2 • chan ch = [1] of { byte }; • proctype A() { ch!a } • proctype B() { ch!b } • proctype C() • { if • :: ch?a • :: ch?b • fi • } • init {atomic{ run A(); run B(); run C() }
Repetition • byte count; • proctype counter() • { do • ::count = count + 1 • ::count = count - 1 • ::(count == 0)-> break • od • }
Only one selection can be selected for execution at a time. • After the options completes, the execution of the structure is repeated. • The normal way to terminate the repetition is with a break.
Jumps • proctype Euclid( int x, y) • { do • ::(x > y) -> x = x - y • ::(x < y) -> y = y - x • ::(x == y)-> goto done • od; • done: • skip • }
The goto jumps to a label named done. • A label can only appear before a statement.
Examples • A filter that receives messages from a channel and divides them over two channels large and small depending on the value attached.
#define N 128 • #define size 16 • chan in = [size] of { short }; • chan large = [size] of { short }; • chan small = [size] of { short }; • proctype split() • { short cargo; • do • :: in?cargo -> • if • ::(cargo >= N)-> large!cargo • ::(cargo < N)-> small!cargo • fi • od • } • init { run split() }
proctype merge() • { short cargo; • do • :: if • :: large?cargo • :: small?cargo • fi; • in!cargo • od • } • Init { • in!345; in!12; in!6777; in!32; in!0; • run split(); run merge() • }
Modeling Procedures and Recursion • Procedures can be modeled as processes. • The return value can be passed back to the calling process via a global variable or via a message.
proctype fact(int n; chan p) • { int result; • if • ::(n <= 1) -> p!1 • ::(n >= 2)-> • chan child = [1] of { int }; • run fact(n-1, child); • child?result; • p!n*result • fi • } • init • { int result; • chan child = [1] of { int }; • run fact(7, child); • child?result; • printf(“result: %d\n”, result)}
Ackermann’s function • ack(0,b) = b + 1 • ack(a,0) = ack(a-1, 1) • ack(a,b) = ack(a-1, ack(a, b-1)) • The PROMELA version is:
proctype ack(short a, b; chan ch1) • { chan ch2 = [1] of { short }; short ans; • if • :: (a == 0) -> ans = b + 1 • :: (a != 0) -> • if • ::(b==0)->run ack(a-1, 1,ch2) • ::(b!=0)->run ack(a, b-1,ch2) ch2?ans; run ack(a-1, ans, ch2) • fi; • ch2?ans • fi; • ch1!ans • } • init • { chan ch = [1] of { short }; short ans; • run ack(3, 3, ch); • ch?ans; • printf(“ack(3,3) = %d\n”, ans); • assert(0) /* a forced stop */ • }
It takes 2433 process instantiations to produce the answer. • The answer is 61.