Why an infinite loop doesn't result in an error in model checking with Promela and Spin? - model-checking

If I write the following code in Promela and run it in Spin in verifier mode it ends with 0 errors. It does report that toogle and init had unreached states, but those seem to be only warnings.
byte x = 0; byte y = 0;
active proctype toggle() {
do
:: x == 1 -> x = 0
:: x == 0 -> x = 1
od
}
init {
(y == 1);
}
I was confused by this because I thought this would give me a 'invalid end state' error. If I change the body of the toogle proctype with a simple skip statement it does error out as I expected.
Why is this? Is there a way to force the simulator to report the infinite loop as an error?
Regarding the 'unreached in proctype' messages, adding an end label to the do loop doesn't seem to do anything.
I am running spin 6.5.0 and ran the following commands:
spin.exe -a test.pml
gcc -o pan pan.c
pan.exe
These are the outputs, for reference.
With do loop:
pan.exe
(Spin Version 6.5.0 -- 1 July 2019)
+ Partial Order Reduction
Full statespace search for:
never claim - (none specified)
assertion violations +
acceptance cycles - (not selected)
invalid end states +
State-vector 20 byte, depth reached 3, errors: 0
4 states, stored
1 states, matched
5 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.292 actual memory usage for states
64.000 memory used for hash table (-w24)
0.343 memory used for DFS stack (-m10000)
64.539 total actual memory usage
unreached in proctype toggle
..\test2.pml:7, state 8, "-end-"
(1 of 8 states)
unreached in init
..\test2.pml:10, state 2, "-end-"
(1 of 2 states)
pan: elapsed time 0.013 seconds
pan: rate 307.69231 states/second
With skip:
pan.exe
pan:1: invalid end state (at depth 0)
pan: wrote ..\test2.pml.trail
(Spin Version 6.5.0 -- 1 July 2019)
Warning: Search not completed
+ Partial Order Reduction
Full statespace search for:
never claim - (none specified)
assertion violations +
acceptance cycles - (not selected)
invalid end states +
State-vector 20 byte, depth reached 1, errors: 1
2 states, stored
0 states, matched
2 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.293 actual memory usage for states
64.000 memory used for hash table (-w24)
0.343 memory used for DFS stack (-m10000)
64.539 total actual memory usage
pan: elapsed time 0.015 seconds
pan: rate 133.33333 states/second

In this example
byte x = 0; byte y = 0;
active proctype toggle() {
do
:: x == 1 -> x = 0
:: x == 0 -> x = 1
od
}
init {
(y == 1);
}
the init process is blocked forever (because y == 1 is always false), but the toggle process can always execute something. Therefore, there is no invalid end state error.
Instead, in this example
byte x = 0; byte y = 0;
active proctype toggle() {
skip;
}
init {
(y == 1);
}
the init process is still blocked forever, but the toggle process can execute its only instruction skip; and then terminate. At this point, none of the remaining processes (i.e. only init) has any instruction it can execute, so Spin terminates with an invalid end state error.
~$ spin -a -search test.pml
pan:1: invalid end state (at depth 0)
pan: wrote test.pml.trail
(Spin Version 6.5.0 -- 17 July 2019)
...
State-vector 20 byte, depth reached 1, errors: 1
...
Is there a way to force the simulator to report the infinite loop as an error?
Yes. There are actually multiple ways.
The simplest approach is to use option -l of Spin:
~$ spin --help
...
-l: search for non-progress cycles
...
With this option, Spin reports any infinite-loop which does not contain any state with a progress label.
This is the output on your original problem:
~$ spin -search -l test.pml
pan:1: non-progress cycle (at depth 2)
pan: wrote test.pml.trail
(Spin Version 6.5.0 -- 17 July 2019)
...
State-vector 28 byte, depth reached 9, errors: 1
...
~$ spin -t test.pml
spin: couldn't find claim 2 (ignored)
<<<<<START OF CYCLE>>>>>
spin: trail ends after 10 steps
#processes: 2
x = 0
y = 0
10: proc 1 (:init::1) test.pml:10 (state 1)
10: proc 0 (toggle:1) test.pml:5 (state 4)
2 processes created
An alternative approach is to use LTL model checking. For instance, you may state that at some point the number of processes (see _nr_pr) that are in execution becomes equal to 0 (or more, if you admit some infinite loops), or check that a particular process terminates correctly using remote references.
Both cases are contained in the following example:
byte x = 0; byte y = 0;
active proctype toggle() {
do
:: x == 1 -> x = 0
:: x == 0 -> x = 1
od;
end:
}
init {
(y == 1);
}
// sooner or later, the process toggle
// with _pid == 0 will reach the end
// state
ltl p1 { <> toggle[0]#end };
// sooner or later, the number of processes
// that are currently running becomes 0,
// (hence, there can be no infinite loops)
ltl p2 { <> (_nr_pr == 0) };
Both the first
~$ spin -a -search -ltl p1 test.pml
~$ spin -t test.pml
ltl p1: <> ((toggle[0]#end))
ltl p2: <> ((_nr_pr==0))
<<<<<START OF CYCLE>>>>>
Never claim moves to line 4 [(!((toggle[0]._p==end)))]
spin: trail ends after 8 steps
#processes: 2
x = 0
y = 0
end = 0
8: proc 1 (:init::1) test.pml:10 (state 1)
8: proc 0 (toggle:1) test.pml:3 (state 5)
8: proc - (p1:1) _spin_nvr.tmp:3 (state 3)
2 processes created
and the second
~$ spin -a -search -ltl p2 test.pml
~$ spin -t test.pml
ltl p1: <> ((toggle[0]#end))
ltl p2: <> ((_nr_pr==0))
<<<<<START OF CYCLE>>>>>
Never claim moves to line 11 [(!((_nr_pr==0)))]
spin: trail ends after 8 steps
#processes: 2
x = 0
y = 0
end = 0
8: proc 1 (:init::1) test.pml:10 (state 1)
8: proc 0 (toggle:1) test.pml:3 (state 5)
8: proc - (p2:1) _spin_nvr.tmp:10 (state 3)
2 processes created
LTL properties are found to be false.
Regarding the 'unreached in proctype' messages, adding an end label to the do loop doesn't seem to do anything.
The end label(s) are used to remove the "invalid end state" error that would be otherwise be found.
For example, modifying your previous example as follows:
byte x = 0; byte y = 0;
active proctype toggle() {
skip;
}
init {
end:
(y == 1);
}
Makes the error go away:
~$ spin -a -search test.pml
(Spin Version 6.5.0 -- 17 July 2019)
...
State-vector 20 byte, depth reached 1, errors: 0
...
One should only ever use an end label when one is willing to guarantee that a process being stuck with no executable instruction is not a symptom of an undesired deadlock situation.

Related

Promela SPIN unreached in proctype error

I'm pretty new to SPIN and Promela and I came across this error when I'm trying to verify the liveness property in my models.
Error code:
unreached in proctype P
(0 of 29 states)
unreached in proctype monitor
mutex_assert.pml:39, state 1, "assert(!((mutex>1)))"
mutex_assert.pml:42, state 2, "-end-"
(2 of 2 states)
unreached in init
(0 of 3 states)
unreached in claim ltl_0
_spin_nvr.tmp:10, state 13, "-end-"
(1 of 13 states)
pan: elapsed time 0 seconds
The code is basically an implementation of Peterson's algorithm and I checked for safety and it seems to be valid. But whenever I try to validate the liveness property using the ltl {[]((wait -> <> (cs)))}, it comes up with the above errors. I'm not sure what they mean so I don't know how to proceed...
My code is as follows:
#define N 3
#define wait (P[1]#WAIT)
#define cs (P[1]#CRITICAL)
int pos[N];
int step[N];
int enter;
byte mutex;
ltl {[]((wait -> <> (cs)))}
proctype P(int i) {
int t;
int k;
WAIT:
for (t : 1 .. (N-1)){
pos[i] = t
step[t] = i
k = 0;
do
:: atomic {(k != i && k < N && (pos[k] < t|| step[t] != i)) -> k++}
:: atomic {k == i -> k++}
:: atomic {k == N -> break}
od;
}
CRITICAL:
atomic {mutex++;
printf("MSC: P(%d) HAS ENTERED THE CRITICAL SECTION.\n", i);
mutex--;}
pos[i] = 0;
}
init {
atomic { run P(0); }
}
General Answer
This is a warning telling you that some states are unreachable due to transitions that are never taken.
In general, this is not an error, but it is a good practice to take a close look to the unreachable states for every routine that you modelled, and check that you expect none of them to be reachable. i.e. in the case the model is not correct wrt. the intended behaviour.
Note. You can use the label end: in front of a particular line of code to mark valid terminating states, so to get rid of those warnings, e.g. when your procedure does not terminate. More info here.
Specific Answer
I can not reproduce your output. In particular, by running
~$ spin -a file.pml
~$ gcc pan.c
~$ ./a.out -a
I get the following output, which is different from yours:
(Spin Version 6.4.3 -- 16 December 2014)
+ Partial Order Reduction
Full statespace search for:
never claim + (ltl_0)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 64 byte, depth reached 47, errors: 0
41 states, stored (58 visited)
18 states, matched
76 transitions (= visited+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.004 equivalent memory usage for states (stored*(State-vector + overhead))
0.288 actual memory usage for states
128.000 memory used for hash table (-w24)
0.534 memory used for DFS stack (-m10000)
128.730 total actual memory usage
unreached in proctype P
(0 of 29 states)
unreached in init
(0 of 3 states)
unreached in claim ltl_0
_spin_nvr.tmp:10, state 13, "-end-"
(1 of 13 states)
pan: elapsed time 0 seconds
In particular, I lack the warnings about the unreached states in the monitor process. As far as I am concerned, juding from the source code, none of the warnings I obtained is problematic.
Either you are using a different version of Spin than me, or you did not include the full source code in your question. In the latter case, could you edit your question and add the code? I'll update my answer afterwards.
EDIT: in the comments, you ask what does the following message mean: "unreached in claim ltl_0 _spin_nvr.tmp:10, state 13, "-end-"".
If you open the file _spin_nvr.tmp, you can see the following piece of Promela code, which corresponds to a Büchi automaton that accepts all and only the execution which violate your ltl property []((wait -> <> (cs))).
never ltl_0 { /* !([] ((! ((P[1]#WAIT))) || (<> ((P[1]#CRITICAL))))) */
T0_init:
do
:: (! ((! ((P[1]#WAIT)))) && ! (((P[1]#CRITICAL)))) -> goto accept_S4
:: (1) -> goto T0_init
od;
accept_S4:
do
:: (! (((P[1]#CRITICAL)))) -> goto accept_S4
od;
}
The message simply warns you that the execution of this code will never reach the last closing bracket } (state "-end-"), meaning that the procedure does never terminate.

Ask for multiple (or all) violation traces in Spin

Is it possible to get multiple (or all) violation traces for a property using Spin?
As an example, I created the Promela model below:
byte mutex = 0;
active proctype A() {
A1: mutex==0; /* Is free? */
A2: mutex++; /* Get mutex */
A3: /* A's critical section */
A4: mutex--; /* Release mutex */
}
active proctype B() {
B1: mutex==0; /* Is free? */
B2: mutex++; /* Get mutex */
B3: /* B's critical section */
B4: mutex--; /* Release mutex */
}
ltl {[] (mutex < 2)}
It has a naive mutex implementation. One could expect that processes A and B would not reach their critical section together and I wrote an LTL expression to check that.
Running
spin -run mutex_example.pml
shows that the property is not valid and running
spin -p -t mutex_example.pml
show the sequence of statements that violate the property.
Never claim moves to line 4 [(1)]
2: proc 1 (B:1) mutex_example.pml:11 (state 1) [((mutex==0))]
4: proc 0 (A:1) mutex_example.pml:4 (state 1) [((mutex==0))]
6: proc 1 (B:1) mutex_example.pml:12 (state 2) [mutex = (mutex+1)]
8: proc 0 (A:1) mutex_example.pml:5 (state 2) [mutex = (mutex+1)]
spin: _spin_nvr.tmp:3, Error: assertion violated
spin: text of failed assertion: assert(!(!((mutex<2))))
Never claim moves to line 3 [assert(!(!((mutex<2))))]
spin: trail ends after 9 steps
#processes: 2
mutex = 2
9: proc 1 (B:1) mutex_example.pml:14 (state 3)
9: proc 0 (A:1) mutex_example.pml:7 (state 3)
9: proc - (ltl_0:1) _spin_nvr.tmp:2 (state 6)
This shows that the sequence of statements (indicated by labels) 'B1' -> 'A1' -> 'B2' -> 'A2' violate the property but there are other interleaving options leading to that (e.g. 'A1' -> 'B1' -> 'B2' -> 'A2').
Can I ask Spin to give me multiple (or all) traces?
I doubt that you can get all violation traces in Spin.
For example, if we consider the following model, then there are infinitely many counter-examples.
byte mutex = 0;
active [2] proctype P() {
do
:: mutex == 0 ->
mutex++;
/* critical section */
mutex--;
od
}
ltl {[] (mutex <= 1)}
What you can do, is to use different search algorithms for your verifier, and this might yield some different counter-examples
-search (or -run) generate a verifier, and compile and run it
options before -search are interpreted by spin to parse the input
options following a -search are used to compile and run the verifier pan
valid options that can follow a -search argument include:
-bfs perform a breadth-first search
-bfspar perform a parallel breadth-first search
-bcs use the bounded-context-switching algorithm
-bitstate or -bit, use bitstate storage
-biterate use bitstate with iterative search refinement (-w18..-w35)
-swarmN,M like -biterate, but running all iterations in parallel
perform N parallel runs and increment -w every M runs
default value for N is 10, default for M is 1
-link file.c link executable pan to file.c
-collapse use collapse state compression
-hc use hash-compact storage
-noclaim ignore all ltl and never claims
-p_permute use process scheduling order permutation
-p_rotateN use process scheduling order rotation by N
-p_reverse use process scheduling order reversal
-ltl p verify the ltl property named p
-safety compile for safety properties only
-i use the dfs iterative shortening algorithm
-a search for acceptance cycles
-l search for non-progress cycles
similarly, a -D... parameter can be specified to modify the compilation
and any valid runtime pan argument can be specified for the verification

Minimum value over entire simulation

In a continuous model, how do I save the minimum value of a variable during the simulation?
When a simulation has finished I want to display a variable T_min with a graphical annotation that shows me the lowest value of a temperature T during the simulation.
For example, if the simulated temperature T was a sine function, the desired result for the value of T_min would be:
In discrete code this would look something like this:
T_min := Modelica.Constants.inf "Start value";
if T < T_min then
T_min := T;
else
T_min := T_min;
end if;
... but I would like a continuous implementation to avoid sampling, high number of events etc.
I'm not sure if Renes solution is optimal. The solution generates many state events generated by the two if conditions. Embedded in the following model:
model globalMinimum2
Real T, T_min;
Boolean is_true;
initial equation
T_min = T;
equation
T =time/10*sin(time);
// if statement ensures that 'T_min' doesn't integrate downwards...
// ... whenever der(T) is negative;
if T < T_min then
der(T_min) = min(0, der(T));
is_true=true;
else
der(T_min) = 0;
is_true=false;
end if;
end globalMinimum2;
The simulation log is the following:
Integration started at T = 0 using integration method DASSL
(DAE multi-step solver (dassl/dasslrt of Petzold modified by Dynasim))
Integration terminated successfully at T = 50
WARNING: You have many state events. It might be due to chattering.
Enable logging of event in Simulation/Setup/Debug/Events during simulation
CPU-time for integration : 0.077 seconds
CPU-time for one GRID interval: 0.154 milli-seconds
Number of result points : 3801
Number of GRID points : 501
Number of (successful) steps : 2519
Number of F-evaluations : 4799
Number of H-evaluations : 18822
Number of Jacobian-evaluations: 2121
Number of (model) time events : 0
Number of (U) time events : 0
Number of state events : 1650
Number of step events : 0
Minimum integration stepsize : 1.44e-005
Maximum integration stepsize : 5.61
Maximum integration order : 3
Perhaps it is better to detect two events as given in the following example:
model unnamed_2
Real T;
Real hold;
Real T_min;
Boolean take_signal;
initial equation
hold=T;
equation
T = time/10*sin(time);
when (T < pre(hold)) then
take_signal = true;
hold = T;
elsewhen (der(T) >=0) then
take_signal = false;
hold = T;
end when;
if (take_signal) then
T_min = T;
else
T_min = hold;
end if;
end unnamed_2;
The simulation log shows that this solutions is more efficient:
Log-file of program ./dymosim
(generated: Tue May 24 14:13:38 2016)
dymosim started
... "dsin.txt" loading (dymosim input file)
... "unnamed_2.mat" creating (simulation result file)
Integration started at T = 0 using integration method DASSL
(DAE multi-step solver (dassl/dasslrt of Petzold modified by Dynasim))
Integration terminated successfully at T = 50
CPU-time for integration : 0.011 seconds
CPU-time for one GRID interval: 0.022 milli-seconds
Number of result points : 549
Number of GRID points : 501
Number of (successful) steps : 398
Number of F-evaluations : 771
Number of H-evaluations : 1238
Number of Jacobian-evaluations: 373
Number of (model) time events : 0
Number of (U) time events : 0
Number of state events : 32
Number of step events : 0
Minimum integration stepsize : 4.65e-006
Maximum integration stepsize : 3.14
Maximum integration order : 1
Calling terminal section
... "dsfinal.txt" creating (final states)
It seems I was able to finde an answer to my own question simply by looking at the figure above
The code is quite simple:
model globalMinimum
Modelica.SIunits.Temperature T, T_min;
initial equation
T_min = T;
equation
// if statement ensures that 'T_min' doesn't integrate downwards...
// ... whenever der(T) is negative;
der(T_min) = if T < T_min then min(0, der(T)) else 0;
end globalMinimum;

Multithreading in SPIN model checker

How do we parametrize the SPIN model with the number of threads?
I am using the standard SPIN model checker. Does it have options to set the number of parallel threads? I checked the reference but didn't find anything useful
You can dynamically spawn a Promela process using the run operator. You can thus iterate as:
#define TRY_COUNT 5
byte index = 0;
do
:: index == TRY_COUNT -> break
:: else -> index++; run theProcess()
od
You can also define a file like:
active [TRY_COUNT] proctype foo () {
printf ("PID: %d\n", _pid);
assert (true)
}
init {
assert (true)
}
and then run a SPIN simulation:
$ spin -DTRY_COUNT=4 bar.pml
PID: 2
PID: 0
PID: 1
PID: 3
5 processes created
or a verification
$ spin -DTRY_COUNT=4 -a bar.pml
$ gcc -o bar pan.c
$ ./bar
hint: this search is more efficient if pan.c is compiled -DSAFETY
(Spin Version 6.3.2 -- 17 May 2014)
+ Partial Order Reduction
...

LTL model checking using Spin and Promela syntax

I'm trying to reproduce ALGOL 60 code written by Dijkstra in the paper titled "Cooperating sequential processes", the code is the first attempt to solve the mutex problem, here is the syntax:
begin integer turn; turn:= 1;
parbegin
process 1: begin Ll: if turn = 2 then goto Ll;
critical section 1;
turn:= 2;
remainder of cycle 1; goto L1
end;
process 2: begin L2: if turn = 1 then goto L2;
critical section 2;
turn:= 1;
remainder of cycle 2; goto L2
end
parend
end
So I tried to reproduce the above code in Promela and here is my code:
#define true 1
#define Aturn true
#define Bturn false
bool turn, status;
active proctype A()
{
L1: (turn == 1);
status = Aturn;
goto L1;
/* critical section */
turn = 1;
}
active proctype B()
{
L2: (turn == 2);
status = Bturn;
goto L2;
/* critical section */
turn = 2;
}
never{ /* ![]p */
if
:: (!status) -> skip
fi;
}
init
{ turn = 1;
run A(); run B();
}
What I'm trying to do is, verify that the fairness property will never hold because the label L1 is running infinitely.
The issue here is that my never claim block is not producing any error, the output I get simply says that my statement was never reached..
here is the actual output from iSpin
spin -a dekker.pml
gcc -DMEMLIM=1024 -O2 -DXUSAFE -DSAFETY -DNOCLAIM -w -o pan pan.c
./pan -m10000
Pid: 46025
(Spin Version 6.2.3 -- 24 October 2012)
+ Partial Order Reduction
Full statespace search for:
never claim - (not selected)
assertion violations +
cycle checks - (disabled by -DSAFETY)
invalid end states +
State-vector 44 byte, depth reached 8, errors: 0
11 states, stored
9 states, matched
20 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.001 equivalent memory usage for states (stored*(State-vector + overhead))
0.291 actual memory usage for states
128.000 memory used for hash table (-w24)
0.534 memory used for DFS stack (-m10000)
128.730 total actual memory usage
unreached in proctype A
dekker.pml:13, state 4, "turn = 1"
dekker.pml:15, state 5, "-end-"
(2 of 5 states)
unreached in proctype B
dekker.pml:20, state 2, "status = 0"
dekker.pml:23, state 4, "turn = 2"
dekker.pml:24, state 5, "-end-"
(3 of 5 states)
unreached in claim never_0
dekker.pml:30, state 5, "-end-"
(1 of 5 states)
unreached in init
(0 of 4 states)
pan: elapsed time 0 seconds
No errors found -- did you verify all claims?
I've read all the documentation of spin on the never{..} block but couldn't find my answer (here is the link), also I've tried using ltl{..} blocks as well (link) but that just gave me syntax error, even though its explicitly mentioned in the documentation that it can be outside the init and proctypes, can someone help me correct this code please?
Thank you
You've redefined 'true' which can't possibly be good. I axed that redefinition and the never claim fails. But, the failure is immaterial to your goal - that initial state of 'status' is 'false' and thus the never claim exits, which is a failure.
Also, it is slightly bad form to assign 1 or 0 to a bool; assign true or false instead - or use bit. Why not follow the Dijkstra code more closely - use an 'int' or 'byte'. It is not as if performance will be an issue in this problem.
You don't need 'active' if you are going to call 'run' - just one or the other.
My translation of 'process 1' would be:
proctype A ()
{
L1: turn !=2 ->
/* critical section */
status = Aturn;
turn = 2
/* remainder of cycle 1 */
goto L1;
}
but I could be wrong on that.

Resources