Search code examples
model-checkingspinpromela

Testing Multiple LTL Formulae with SPIN


I have many LTL formulae that I am trying to test on the same .pml file. My issue is that when an error is found in any single ltl formula, the trail file is written (or overwrites) to the same trail file name. I have not been able to find a way to write to an trail file name of my choice. Does anyone know if this option exists?

If not, what is a strategy I could use for testing multiple ltl formulae from the same .pml file simultaneously without overwriting the same trail file every time?

I am aware of the SPIN runtime -x option, but that just prevents a trail file from being overwritten. It does not generate trail files with different names.


Solution

  • AFAIK, there is no such option.


    WORKAROUND

    For linux+bash, you can opt for the following, brutal, approach.

    Define set_trail_name function:

    ~$ function set_trail_name() { sed -i "s/^\\(char \\*TrailFile =\\)\\(.*\\)$/\\1 \"${1}\";/" "${2}"; }
    ~$ export -f set_trail_name
    

    It takes two parameters: your preferred trail_file_nime and the location of pan.c.

    Then use it as follows:

    ~$ spin -a test.pml
    ltl p1: [] (<> (! (q0)))
    ltl p2: [] (<> (q1))
      the model contains 2 never claims: p2, p1
    ...
    
    ~$ set_trail_name my_p1_name pan.c
    ~$ gcc -o pan pan.c
    ~$ ./pan -a -N p1
    pan: ltl formula p1
    pan:1: acceptance cycle (at depth 4)
    pan: wrote my_p1_name.trail
    ...
    
    ~$ ls *.trail
    my_p1_name.trail
    
    ~$ set_trail_name my_p2_name pan.c
    ~$ gcc -o pan pan.c
    ~$ pan -a -N p2
    pan: ltl formula p2
    pan:1: acceptance cycle (at depth 2)
    pan: wrote my_p2_name.trail
    ...
    
    ~$ ls *.trail
    my_p1_name.trail     my_p2_name.trail
    

    WORKAROUND IMPROVEMENT #1

    You can go one step further, e.g.

    #!/bin/bash
    
    function set_trail_name() {
        sed -i "s/^\\(char \\*TrailFile =\\)\\(.*\\)$/\\1 \"${1}\";/" "${2}";
    }
    
    function check_property() {
        set -e
    
        spin -a "${1}" 1>/dev/null
        set_trail_name "${2}" pan.c
        gcc -o pan pan.c
        ./pan -a -N "${2}"
    
        set +e
    }
    
    check_property "${@}"
    

    Which makes it easier to run it:

    ~$ ./run_spin.sh test.pml p1
    pan: ltl formula p1
    pan:1: acceptance cycle (at depth 4)
    pan: wrote p1.trail
    ...
    
    ~$ ~$ ./run_spin.sh test.pml p2
    pan: ltl formula p2
    pan:1: acceptance cycle (at depth 2)
    pan: wrote p2.trail
    

    WORKAROUND IMPROVEMENT #2

    You can even go a couple of steps further, e.g.

    #!/bin/bash
    
    function set_trail_name()
    {
        sed -i "s/^\\(char \\*TrailFile =\\)\\(.*\\)$/\\1 \"${1}\";/" "${2}";
    }
    
    function check_property()
    {
        echo -e "\\n>>> Testing property ${1} ...\\n"
    
        set_trail_name "${1}" pan.c
        gcc -o pan pan.c
        ./pan -a -N "${1}"
    }
    
    function check_properties()
    {
        set -e
    
        spin -a "${1}" 1>/dev/null
        mapfile -t properties < <(gawk 'match($0, /^ltl ([^{]+) .*$/, a) { print a[1] }' "${1}")
    
        for prop in "${properties[@]}"
        do
            check_property "${prop}"
        done
    
        set +e
    }
    
    check_properties "${@}"
    

    Which makes it trivial to run it:

    ~$ ./run_spin.sh test.pml
    
    >>> Testing property p1 ...
    
    pan: ltl formula p1
    pan:1: acceptance cycle (at depth 4)
    pan: wrote p1.trail
    ...
    
    >>> Testing property p2 ...
    
    pan: ltl formula p2
    pan:1: acceptance cycle (at depth 2)
    pan: wrote p2.trail
    ...
    

    NOTES

    You might want to enrich the scripts with

    • cleanup of temporary files, e.g. pan, pan.*, _spin_nvr.tmp
    • analysis of property status (true/false) and printing
    • ...

    Another completely legitimate solution could be to simply rename existing trail files after each call to the Spin model checker.