Commit dd4b821d authored by Thibaud Michaud's avatar Thibaud Michaud Committed by Alexandre Duret-Lutz
Browse files

Adding support for promela models via SpinS.

* configure.ac, iface/Makefile.am: Adjust.
* iface/dve2/finite.test, iface/dve2/.gitignore, iface/dve2/Makefile.am,
iface/dve2/README, iface/dve2/beem-peterson.4.dve,
iface/dve2/dve2check.test, iface/dve2/defs.in, iface/dve2/finite.dve,
iface/ltsmin/finite.test, iface/dve2/kripke.test, iface/dve2/dve2.cc,
iface/dve2/dve2.hh, iface/dve2/dve2check.cc: Move to iface/ltsmin.
* iface/ltsmin/.gitignore, iface/ltsmin/Makefile.am,
iface/ltsmin/README, iface/ltsmin/beem-peterson.4.dve,
iface/ltsmin/check.test, iface/ltsmin/defs.in, iface/ltsmin/finite.dve,
iface/ltsmin/finite.test, iface/ltsmin/kripke.test,
iface/ltsmin/ltsmin.cc, iface/ltsmin/ltsmin.hh,
iface/ltsmin/modelcheck.cc: Factorize dve2 and spins interface in
iface/ltsmin/
* iface/ltsmin/elevator2.1.pm, iface/ltsmin/finite.pm: Test promela
models.
* README: Document iface/ltsmin/ directory.
parent 6dd2749c
......@@ -180,7 +180,7 @@ wrap/ Wrappers for other languages.
tests/ Tests for these bindings
ajax/ LTL-to-TGBA translator with web interface, using Ajax.
iface/ Interfaces to other libraries.
dve2/ Interface with DiVinE2.
ltsmin/ Interface with DiVinE2 and SpinS.
Third party software
--------------------
......
......@@ -169,8 +169,8 @@ AC_CONFIG_FILES([
doc/Makefile
doc/tl/Makefile
doc/org/init.el
iface/dve2/defs
iface/dve2/Makefile
iface/ltsmin/defs
iface/ltsmin/Makefile
iface/Makefile
lib/Makefile
src/bin/Makefile
......
......@@ -17,4 +17,4 @@
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
SUBDIRS = dve2
SUBDIRS = ltsmin
*.dve2C
*.dve.cpp
dve2check
defs
*.dir
*.spins
*.c
check
......@@ -18,26 +18,26 @@
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src \
$(BUDDY_CPPFLAGS) -I$(top_srcdir)/ltdl
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
AM_CXXFLAGS = $(WARNING_CXXFLAGS) -lpthread
dve2dir = $(pkgincludedir)/iface/dve2
ltsmindir = $(pkgincludedir)/iface/ltsmin
dve2_HEADERS = dve2.hh
ltsmin_HEADERS = ltsmin.hh
lib_LTLIBRARIES = libspotdve2.la
libspotdve2_la_LIBADD = \
lib_LTLIBRARIES = libspotltsmin.la
libspotltsmin_la_LIBADD = \
$(top_builddir)/src/libspot.la \
$(top_builddir)/ltdl/libltdlc.la
libspotdve2_la_SOURCES = dve2.cc
libspotltsmin_la_SOURCES = ltsmin.cc
noinst_PROGRAMS = dve2check
noinst_PROGRAMS = modelcheck
dve2check_SOURCES = dve2check.cc
dve2check_LDADD = libspotdve2.la
modelcheck_SOURCES = modelcheck.cc
modelcheck_LDADD = libspotltsmin.la
check_SCRIPTS = defs
TESTS = dve2check.test finite.test kripke.test
TESTS = check.test finite.test kripke.test
EXTRA_DIST = $(TESTS) beem-peterson.4.dve finite.dve
kripke.test: $(top_builddir)/src/kripketest/parse_print$(EXEEXT)
......
This directory contains an interface that presents DiVinE models as
kripke* objects for Spot.
This directory contains an interface that presents DiVinE and PROMELA
models as kripke* objects for Spot.
The DiVinE model checker [http://anna.fi.muni.cz/divine/] has a
specification language called DVE that makes it easy to model
......@@ -9,16 +9,12 @@ processes synchonizing through channels
A lot of models can be found in the BEEM database at
http://anna.fi.muni.cz/models/
For efficient generation of the state space of the model, DiVinE
compiles the DVE input into a dynamic library that contains anything
needed to generate the state-space.
The LTSmin people [http://fmt.cs.utwente.nl/tools/ltsmin/] have made a
patched version of DiVinE that compiles a dynamic library with a very
simple C interface (no C++) and extra information about state
variables (name, type, possible values). We are using this interface,
therefore you need to install their version of DiVinE in order to use
Spot's DVE interface.
The LTSmin people [http://fmt.cs.utwente.nl/tools/ltsmin/] patched
DiVinE and SpinJa to compile models as dynamic libraries. This dynamic
library provides a very simple C interface (no C++) and extra
information about state variables (name, type, possible values). We
use this interface so you will need to install their version of these
tools to use Spot with DVE or PROMELA models.
......@@ -47,14 +43,32 @@ model.dve2C (which is a dynamic library).
divine compile --ltsmin model.dve
Installation of SpinS
======================
The extended version of SpinJa is called SpinS and should be included
with LTSmin.
You can download LTSmin from their website:
[http://fmt.cs.utwente.nl/tools/ltsmin/] and install it following the
INSTALL instructions.
To compile a promela model, simply run the following command:
spins model.pm
It should create a dynamic library called model.pm.spins in the
current directory.
Usage with Spot
===============
The function load_dve2() defined in dve2.hh in this directory
will accept "model.dve" or "model.dve2C" as file argument.
In the former case, it will call "divine compile --ltsmin"
if "model.dve2C" does not exist or is older. Then it will
load "model.dve2C" dynamically.
The function load_dve2() defined in dve2.hh in this directory will
accept either a model or its compiled version as file argument. In
the former case, it will call "divine compile --ltsmin model.dve" or
"spins model.pm" depending on the file extension, only if a compiled
model with the corresponding file extension (.dve2C or .spins) does
not exist or is older. Then it will load the compiled model
dynamically.
load_dve2() also requires a set of atomic propositions that should
be observed in the model. These are usually the atomic propositions
......@@ -89,15 +103,15 @@ Usage with Spot
"P_0.j >= 2" Process P_0's variable j is greater or equal to 2.
P_0.j This is equivalent to "P_0.j != 0".
Comparisons operators available are "<", ">", ">=", "<=", "==", and
"!=". The left operant should always be a variable and the right
Comparison operators available are "<", ">", ">=", "<=", "==", and
"!=". The left operand should always be a variable and the right
operand should always be a number, so you cannot write something
like "P_0.j <= P_0.i".
Because the LTL parser knows nothing about the details of the
languages we interface with, every atomic proposition that cannot be
express using only alphanumeric characters (plus `_' and `.') should
be enclosed in double quote.
expressed using only alphanumeric characters (plus `_' and `.')
should be enclosed in double quote.
Caveat: "P_0.j >= 2" and " P_0.j>=2" (watch the spaces!) are
considered to be two distinct atomic propositions with the same
......
#!/bin/sh
# Copyright (C) 2011, 2012 Laboratoire de Recherche et Dveloppement
# Copyright (C) 2011, 2012, 2014 Laboratoire de Recherche et Dveloppement
# de l'Epita (LRDE).
#
# This file is part of Spot, a model checking library.
......@@ -29,32 +29,47 @@ else
exit 77
fi
if ! test -x "$(which spins)"; then
echo "spins not installed."
exit 77
fi
set -e
# Promela
for opt in '' '-z'; do
run 0 ../modelcheck $opt -E $srcdir/elevator2.1.pm \
'!G("req[1]==1" -> (F("p==1" && "cabin_0._pc==2")))'
run 0 ../modelcheck $opt -e $srcdir/elevator2.1.pm \
'F("p==2")'
done
# dve2
for opt in '' '-z'; do
# The three examples from the README.
# (Don't run the first one using "run 0" because it would take too much
# time with valgrind.).
../dve2check $opt -E $srcdir/beem-peterson.4.dve \
../modelcheck $opt -E $srcdir/beem-peterson.4.dve \
'!GF(P_0.CS|P_1.CS|P_2.CS|P_3.CS)' \
| grep -v pages > stdout1
# same formula, different syntax.
../dve2check $opt -E $srcdir/beem-peterson.4.dve \
../modelcheck $opt -E $srcdir/beem-peterson.4.dve \
'!GF("P_0==CS"|"P_1 == CS"|"P_2 ==CS"|"P_3== CS")' \
| grep -v pages > stdout2
cmp stdout1 stdout2
run 0 ../dve2check $opt -e $srcdir/beem-peterson.4.dve \
run 0 ../modelcheck $opt -e $srcdir/beem-peterson.4.dve \
'!G(P_0.wait -> F P_0.CS)'
run 0 ../dve2check $opt -e $srcdir/beem-peterson.4.dve '!G("pos[1] < 3")'
run 0 ../modelcheck $opt -e $srcdir/beem-peterson.4.dve '!G("pos[1] < 3")'
done
# Now check some error messages.
run 1 ../dve2check foo.dve "F(P_0.CS)" 2>stderr
run 1 ../modelcheck foo.dve "F(P_0.CS)" 2>stderr
cat stderr
grep 'Cannot open' stderr
# the dve2C file was generated in the current directory
run 1 ../dve2check beem-peterson.4.dve2C \
run 1 ../modelcheck beem-peterson.4.dve2C \
'Xfoo | P_0.f & X"P_0.k < 2xx" | G"pos[0]"' 2>stderr
cat stderr
grep 'variable `foo' stderr
......
byte req[4];
int t=0;
int p=0;
byte v=0;
active proctype cabin() {
idle: if
:: v>0; goto mov;
fi;
mov: if
:: t==p; goto open;
:: d_step {t<p;p = p-1;} goto mov;
:: d_step {t>p;p = p+1;} goto mov;
fi;
open: if
:: d_step {req[p] = 0;v = 0;} goto idle;
fi;
}
active proctype environment() {
read: if
:: d_step {req[0]==0;req[0] = 1;} goto read;
:: d_step {req[1]==0;req[1] = 1;} goto read;
:: d_step {req[2]==0;req[2] = 1;} goto read;
:: d_step {req[3]==0;req[3] = 1;} goto read;
fi;
}
active proctype controller() {
byte ldir=0;
wait: if
:: d_step {v==0;t = t+(2*ldir)-1;} goto work;
fi;
work: if
:: d_step {t<0 || t==4;ldir = 1-ldir;} goto wait;
:: t>=0 && t<4 && req[t]==1; goto done;
:: d_step {t>=0 && t<4 && req[t]==0;t = t+(2*ldir)-1;} goto work;
fi;
done: if
:: v = 1; goto wait;
fi;
}
active proctype P() {
int a = 0;
int b = 0;
x: if
:: d_step {a < 3 && b < 3; a = a + 1; } goto x;
:: d_step {a < 3 && b < 3; b = b + 1; } goto x;
fi;
}
......@@ -29,29 +29,62 @@ else
exit 77
fi
if ! test -x "$(which spins)"; then
echo "spins not installed."
exit 77
fi
set -e
run 0 ../dve2check -gm $srcdir/finite.dve '"P.a < 10"' > stdout
run 0 ../modelcheck -gm $srcdir/finite.dve '"P.a < 10"' > stdout
test `grep ' -> ' stdout | wc -l` = 25
test `grep 'P.a=' stdout | wc -l` = 15
run 0 ../dve2check -dtrue -gm $srcdir/finite.dve '"P.a < 10"' > stdout2
run 0 ../modelcheck -dtrue -gm $srcdir/finite.dve '"P.a < 10"' > stdout2
cmp stdout stdout2
run 0 ../dve2check -dfalse -gm $srcdir/finite.dve '"P.a < 10"' > stdout
run 0 ../modelcheck -dfalse -gm $srcdir/finite.dve '"P.a < 10"' > stdout
test `grep ' -> ' stdout | wc -l` = 19
test `grep 'P.a=' stdout | wc -l` = 15
# the same with compressed states
run 0 ../dve2check -z -dfalse -gm $srcdir/finite.dve '"P.a < 10"' > stdout
run 0 ../modelcheck -z -dfalse -gm $srcdir/finite.dve '"P.a < 10"' > stdout
test `grep ' -> ' stdout | wc -l` = 19
test `grep 'P.a=' stdout | wc -l` = 15
run 0 ../dve2check -ddead -E $srcdir/finite.dve \
run 0 ../modelcheck -ddead -E $srcdir/finite.dve \
'!(G(dead -> ("P.a==3" | "P.b==3")))'
run 0 ../dve2check -ddead -e $srcdir/finite.dve \
run 0 ../modelcheck -ddead -e $srcdir/finite.dve \
'!(G(dead -> ("P.a==2" | "P.b==3")))'
# This used to segfault because of a bug in
# tgba_product::transition_annotation.
run 0 ../dve2check -gp $srcdir/finite.dve true
run 0 ../modelcheck -gp $srcdir/finite.dve true
# Same tests with finite.pm.
# Compile it beforehand to avoid compiler's output interference.
spins $srcdir/finite.pm
run 0 ../modelcheck -gm $srcdir/finite.pm.spins '"P_0.a < 10"' > stdout
test `grep ' -> ' stdout | wc -l` = 25
test `grep 'P_0.a=' stdout | wc -l` = 15
run 0 ../modelcheck -dtrue -gm $srcdir/finite.pm.spins '"P_0.a < 10"' > stdout2
diff stdout stdout2
run 0 ../modelcheck -dfalse -gm $srcdir/finite.pm.spins '"P_0.a < 10"' > stdout
test `grep ' -> ' stdout | wc -l` = 19
test `grep 'P_0.a=' stdout | wc -l` = 15
# the same with compressed states
run 0 ../modelcheck -z -dfalse -gm $srcdir/finite.pm.spins '"P_0.a < 10"' \
> stdout
test `grep ' -> ' stdout | wc -l` = 19
test `grep 'P_0.a=' stdout | wc -l` = 15
run 0 ../modelcheck -ddead -E $srcdir/finite.pm.spins \
'!(G(dead -> ("P_0.a==3" | "P_0.b==3")))'
run 0 ../modelcheck -ddead -e $srcdir/finite.pm.spins \
'!(G(dead -> ("P_0.a==2" | "P_0.b==3")))'
run 0 ../modelcheck -gp $srcdir/finite.pm.spins true
......@@ -33,10 +33,10 @@ fi
set -e
run 0 ../dve2check -gK ${srcdir}/finite.dve 'F("P.a > 5")' > output
run 0 ../modelcheck -gK ${srcdir}/finite.dve 'F("P.a > 5")' > output
run 0 ${top_builddir}/src/kripketest/parse_print output | tr -d '"' > output2
tr -d '"' < output >outputF
cmp outputF output2
../dve2check -gK $srcdir/beem-peterson.4.dve '!G("pos[1] < 3")' > outputP
../modelcheck -gK $srcdir/beem-peterson.4.dve '!G("pos[1] < 3")' > outputP
${top_builddir}/src/tgbatest/ltl2tgba -e -KPoutputP '!G("pos[1] < 3")'
......@@ -30,21 +30,20 @@
# define WEXITSTATUS(x) ((x) & 0xff)
#endif
#include "dve2.hh"
#include "ltsmin.hh"
#include "misc/hashfunc.hh"
#include "misc/fixpool.hh"
#include "misc/mspool.hh"
#include "misc/intvcomp.hh"
#include "misc/intvcmp2.hh"
namespace spot
{
namespace
{
////////////////////////////////////////////////////////////////////////
// DVE2 --ltsmin interface
// spins interface
typedef struct transition_info {
int* labels; // edge labels, NULL, or pointer to the edge label(s)
......@@ -55,29 +54,27 @@ namespace spot
transition_info_t *transition_info,
int *dst);
struct dve2_interface
struct spins_interface
{
lt_dlhandle handle; // handle to the dynamic library
void (*get_initial_state)(void *to);
int (*have_property)();
int (*get_successors)(void* m, int *in, TransitionCB, void *arg);
int (*get_state_variable_count)();
int (*get_state_size)();
const char* (*get_state_variable_name)(int var);
int (*get_state_variable_type)(int var);
int (*get_state_variable_type_count)();
const char* (*get_state_variable_type_name)(int type);
int (*get_state_variable_type_value_count)(int type);
const char* (*get_state_variable_type_value)(int type, int value);
int (*get_transition_count)();
int (*get_type_count)();
const char* (*get_type_name)(int type);
int (*get_type_value_count)(int type);
const char* (*get_type_value_name)(int type, int value);
};
////////////////////////////////////////////////////////////////////////
// STATE
struct dve2_state: public state
struct spins_state: public state
{
dve2_state(int s, fixed_size_pool* p)
spins_state(int s, fixed_size_pool* p)
: pool(p), size(s), count(1)
{
}
......@@ -89,10 +86,10 @@ namespace spot
hash_value = wang32_hash(hash_value ^ vars[i]);
}
dve2_state* clone() const
spins_state* clone() const
{
++count;
return const_cast<dve2_state*>(this);
return const_cast<spins_state*>(this);
}
void destroy() const
......@@ -111,7 +108,7 @@ namespace spot
{
if (this == other)
return 0;
const dve2_state* o = down_cast<const dve2_state*>(other);
const spins_state* o = down_cast<const spins_state*>(other);
assert(o);
if (hash_value < o->hash_value)
return -1;
......@@ -122,7 +119,7 @@ namespace spot
private:
~dve2_state()
~spins_state()
{
}
......@@ -134,9 +131,9 @@ namespace spot
int vars[0];
};
struct dve2_compressed_state: public state
struct spins_compressed_state: public state
{
dve2_compressed_state(int s, multiple_size_pool* p)
spins_compressed_state(int s, multiple_size_pool* p)
: pool(p), size(s), count(1)
{
}
......@@ -148,10 +145,10 @@ namespace spot
hash_value = wang32_hash(hash_value ^ vars[i]);
}
dve2_compressed_state* clone() const
spins_compressed_state* clone() const
{
++count;
return const_cast<dve2_compressed_state*>(this);
return const_cast<spins_compressed_state*>(this);
}
void destroy() const
......@@ -170,8 +167,8 @@ namespace spot
{
if (this == other)
return 0;
const dve2_compressed_state* o =
down_cast<const dve2_compressed_state*>(other);
const spins_compressed_state* o =
down_cast<const spins_compressed_state*>(other);
assert(o);
if (hash_value < o->hash_value)
return -1;
......@@ -188,7 +185,7 @@ namespace spot
private:
~dve2_compressed_state()
~spins_compressed_state()
{
}
......@@ -223,8 +220,8 @@ namespace spot
{
callback_context* ctx = static_cast<callback_context*>(arg);
fixed_size_pool* p = static_cast<fixed_size_pool*>(ctx->pool);
dve2_state* out =
new(p->allocate()) dve2_state(ctx->state_size, p);
spins_state* out =
new(p->allocate()) spins_state(ctx->state_size, p);
memcpy(out->vars, dst, ctx->state_size * sizeof(int));
out->compute_hash();
ctx->transitions.push_back(out);
......@@ -238,9 +235,9 @@ namespace spot
size_t csize = ctx->state_size * 2;
ctx->compress(dst, ctx->state_size, ctx->compressed, csize);
void* mem = p->allocate(sizeof(dve2_compressed_state)
void* mem = p->allocate(sizeof(spins_compressed_state)
+ sizeof(int) * csize);
dve2_compressed_state* out = new(mem) dve2_compressed_state(csize, p);
spins_compressed_state* out = new(mem) spins_compressed_state(csize, p);
memcpy(out->vars, ctx->compressed, csize * sizeof(int));
out->compute_hash();
ctx->transitions.push_back(out);
......@@ -249,11 +246,11 @@ namespace spot
////////////////////////////////////////////////////////////////////////
// SUCC_ITERATOR
class dve2_succ_iterator: public kripke_succ_iterator
class spins_succ_iterator: public kripke_succ_iterator
{
public:
dve2_succ_iterator(const callback_context* cc,
spins_succ_iterator(const callback_context* cc,
bdd cond)
: kripke_succ_iterator(cond), cc_(cc)
{
......@@ -266,7 +263,7 @@ namespace spot
kripke_succ_iterator::recycle(cond);
}
~dve2_succ_iterator()
~spins_succ_iterator()
{
delete cc_;
}
......@@ -327,14 +324,14 @@ namespace spot
int
convert_aps(const ltl::atomic_prop_set* aps,
const dve2_interface* d,
const spins_interface* d,
bdd_dict_ptr dict,
const ltl::formula* dead,
prop_set& out)
{
int errors = 0;
int state_size = d->get_state_variable_count();
int state_size = d->get_state_size();
typedef std::map<std::string, var_info> val_map_t;
val_map_t val_map;
......@@ -346,14 +343,14 @@ namespace spot
val_map[name] = v;
}
int type_count = d->get_state_variable_type_count();
int type_count = d->get_type_count();
typedef std::map<std::string, int> enum_map_t;
std::vector<enum_map_t> enum_map(type_count);
for (int i = 0; i < type_count; ++i)
{
int enum_count = d->get_state_variable_type_value_count(i);
int enum_count = d->get_type_value_count(i);
for (int j = 0; j < enum_count; ++j)
enum_map[i].emplace(d->get_state_variable_type_value(i, j), j);
enum_map[i].emplace(d->get_type_value_name(i, j), j);
}
for (ltl::atomic_prop_set::const_iterator ap = aps->begin();
......@@ -600,15 +597,15 @@ namespace spot
////////////////////////////////////////////////////////////////////////
// KRIPKE
class dve2_kripke: public kripke
class spins_kripke: public kripke
{
public:
dve2_kripke(const dve2_interface* d, const bdd_dict_ptr& dict,
spins_kripke(const spins_interface* d, const bdd_dict_ptr& dict,