NIPS VM added to the SPOT distribution.

2008-05-29  Guillaume SADEGH  <sadegh@lrde.epita.fr>

	* iface/nips/nips.cc, iface/nips/nips.hh, iface/nips/common.cc,
	iface/nips/common.hh, iface/nips/Makefile.am: TGBA implementation
	with the NIPS library.
	* iface/nips/emptiness_check.cc: Emptiness check on a Promela
	interface.
	* iface/nips/dottynips.cc: Dot printer on the NIPS interface.
	* iface/nips/compile.sh: Add. Wrapper around nips compiler to
	compile Promela to NIPS bytecode.
	* iface/nips/nips_vm,iface/nips/nips_vm/bytecode.h,
	iface/nips/nips_vm/ChangeLog, iface/nips/nips_vm/COPYING,
	iface/nips/nips_vm/hashtab.c, iface/nips/nips_vm/hashtab.h,
	iface/nips/nips_vm/INSTALL, iface/nips/nips_vm/instr.c,
	iface/nips/nips_vm/instr.h, iface/nips/nips_vm/instr_step.c,
	iface/nips/nips_vm/instr_step.h,
	iface/nips/nips_vm/instr_tools.c,
	iface/nips/nips_vm/instr_tools.h,
	iface/nips/nips_vm/instr_wrap.c,
	iface/nips/nips_vm/instr_wrap.h,
	iface/nips/nips_vm/interactive.c,
	iface/nips/nips_vm/interactive.h, iface/nips/nips_vm/main.c,
	iface/nips/nips_vm/Makefile, iface/nips/nips_vm/Makefile.am,
	iface/nips/nips_vm/nips_asm_help.pl,
	iface/nips/nips_vm/nips_asm_instr.pl,
	iface/nips/nips_vm/nips_asm.pl,
	iface/nips/nips_vm/nips_disasm.pl, iface/nips/nips_vm/nipsvm.c,
	iface/nips/nips_vm/nipsvm.h, iface/nips/nips_vm/README,
	iface/nips/nips_vm/rt_err.c, iface/nips/nips_vm/rt_err.h,
	iface/nips/nips_vm/search.c, iface/nips/nips_vm/search.h,
	iface/nips/nips_vm/split.c, iface/nips/nips_vm/split.h,
	iface/nips/nips_vm/state.c, iface/nips/nips_vm/state.h,
	iface/nips/nips_vm/state_inline.h,
	iface/nips/nips_vm/state_parts.c,
	iface/nips/nips_vm/state_parts.h, iface/nips/nips_vm/timeval.h,
	iface/nips/nips_vm/tools.h: NIPS VM added to the SPOT
	distribution.
	* configure.ac, iface/Makefile.am: Build system updated for the
	NIPS front-end.
This commit is contained in:
Guillaume Sadegh 2008-04-02 17:43:54 +02:00
parent 543190f2bc
commit bc5f13bb4e
57 changed files with 11464 additions and 3 deletions

View file

@ -1,3 +1,45 @@
2008-05-29 Guillaume SADEGH <sadegh@lrde.epita.fr>
* iface/nips/nips.cc, iface/nips/nips.hh, iface/nips/common.cc,
iface/nips/common.hh, iface/nips/Makefile.am: TGBA implementation
with the NIPS library.
* iface/nips/emptiness_check.cc: Emptiness check on a Promela
interface.
* iface/nips/dottynips.cc: Dot printer on the NIPS interface.
* iface/nips/compile.sh: Add. Wrapper around nips compiler to
compile Promela to NIPS bytecode.
* iface/nips/nips_vm,iface/nips/nips_vm/bytecode.h,
iface/nips/nips_vm/ChangeLog, iface/nips/nips_vm/COPYING,
iface/nips/nips_vm/hashtab.c, iface/nips/nips_vm/hashtab.h,
iface/nips/nips_vm/INSTALL, iface/nips/nips_vm/instr.c,
iface/nips/nips_vm/instr.h, iface/nips/nips_vm/instr_step.c,
iface/nips/nips_vm/instr_step.h,
iface/nips/nips_vm/instr_tools.c,
iface/nips/nips_vm/instr_tools.h,
iface/nips/nips_vm/instr_wrap.c,
iface/nips/nips_vm/instr_wrap.h,
iface/nips/nips_vm/interactive.c,
iface/nips/nips_vm/interactive.h, iface/nips/nips_vm/main.c,
iface/nips/nips_vm/Makefile, iface/nips/nips_vm/Makefile.am,
iface/nips/nips_vm/nips_asm_help.pl,
iface/nips/nips_vm/nips_asm_instr.pl,
iface/nips/nips_vm/nips_asm.pl,
iface/nips/nips_vm/nips_disasm.pl, iface/nips/nips_vm/nipsvm.c,
iface/nips/nips_vm/nipsvm.h, iface/nips/nips_vm/README,
iface/nips/nips_vm/rt_err.c, iface/nips/nips_vm/rt_err.h,
iface/nips/nips_vm/search.c, iface/nips/nips_vm/search.h,
iface/nips/nips_vm/split.c, iface/nips/nips_vm/split.h,
iface/nips/nips_vm/state.c, iface/nips/nips_vm/state.h,
iface/nips/nips_vm/state_inline.h,
iface/nips/nips_vm/state_parts.c,
iface/nips/nips_vm/state_parts.h, iface/nips/nips_vm/timeval.h,
iface/nips/nips_vm/tools.h: NIPS VM added to the SPOT
distribution.
* configure.ac, iface/Makefile.am: Build system updated for the
NIPS front-end.
* src/Makefile.am (_.cc): Fix for `make tags`.
2008-04-16 Damien Lefortier <dam@lrde.epita.fr> 2008-04-16 Damien Lefortier <dam@lrde.epita.fr>
* configure.ac, Makefile.am: Add src/eltltest/ support. * configure.ac, Makefile.am: Add src/eltltest/ support.

View file

@ -78,6 +78,8 @@ AC_CONFIG_FILES([
iface/Makefile iface/Makefile
iface/gspn/Makefile iface/gspn/Makefile
iface/gspn/defs iface/gspn/defs
iface/nips/Makefile
iface/nips/nips_vm/Makefile
src/Makefile src/Makefile
src/eltlast/Makefile src/eltlast/Makefile
src/eltlenv/Makefile src/eltlenv/Makefile

View file

@ -1,4 +1,4 @@
## Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6), ## Copyright (C) 2003, 2008 Laboratoire d'Informatique de Paris 6 (LIP6),
## département Systèmes Répartis Coopératifs (SRC), Université Pierre ## département Systèmes Répartis Coopératifs (SRC), Université Pierre
## et Marie Curie. ## et Marie Curie.
## ##
@ -19,6 +19,8 @@
## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
## 02111-1307, USA. ## 02111-1307, USA.
SUBDIRS = nips
if WITH_GSPN if WITH_GSPN
SUBDIRS = gspn SUBDIRS += gspn
endif endif

2
iface/nips/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.lo
TAGS

46
iface/nips/Makefile.am Normal file
View file

@ -0,0 +1,46 @@
## Copyright (C) 2008 Laboratoire d'Informatique de Paris 6 (LIP6),
## département Systèmes Répartis Coopératifs (SRC), Université Pierre
## et Marie Curie.
##
## This file is part of Spot, a model checking library.
##
## Spot is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## Spot is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
## License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Spot; see the file COPYING. If not, write to the Free
## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
## 02111-1307, USA.
AM_CPPFLAGS = -I$(top_srcdir)/src $(BUDDY_CPPFLAGS) -I$(srcdir)/nips_vm
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
nipsdir = $(pkgincludedir)/nips
nips_HEADERS = \
common.hh \
nips.hh
lib_LTLIBRARIES = libspotnips.la
libspotnips_la_LIBADD = $(top_builddir)/src/libspot.la
libspotnips_la_SOURCES = \
common.cc \
nips.cc
noinst_PROGRAMS = \
dottynips
dottynips_SOURCES = dottynips.cc
dottynips_LDADD = libspotnips.la $(builddir)/nips_vm/libnipsvm.la
empt_check_SOURCES = emptiness_check.cc
empt_check_LDADD = libspotnips.la $(builddir)/nips_vm/libnipsvm.la
SUBDIRS = nips_vm

38
iface/nips/common.cc Normal file
View file

@ -0,0 +1,38 @@
// Copyright (C) 2008 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#include "common.hh"
#include <ostream>
namespace spot
{
std::ostream&
operator<<(std::ostream& os, const nips_exception& e)
{
if (e.get_err_defined())
os << e.get_where() << " exited with " << e.get_err();
else
os << e.get_where() << " with no exit value";
return os;
}
}

72
iface/nips/common.hh Normal file
View file

@ -0,0 +1,72 @@
// Copyright (C) 2008 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#ifndef SPOT_IFACE_NIPS_COMMON_HH
# define SPOT_IFACE_NIPS_COMMON_HH
# include <string>
# include <iosfwd>
namespace spot
{
/// An exception used to forward NIPS errors.
class nips_exception
{
public:
nips_exception(const std::string& where, int err)
: err_(err), where_(where), err_defined_(true)
{
}
nips_exception(const std::string& where)
: err_(-1), where_(where), err_defined_(false)
{
}
int
get_err() const
{
return err_;
}
std::string
get_where() const
{
return where_;
}
int
get_err_defined() const
{
return err_defined_;
}
private:
int err_;
std::string where_;
bool err_defined_;
};
std::ostream& operator<<(std::ostream& os, const nips_exception& e);
}
#endif // SPOT_IFACE_NIPS_COMMON_HH

35
iface/nips/compile.sh Executable file
View file

@ -0,0 +1,35 @@
#! /bin/bash
NIPS_COMPILER=~/lrde/vmssg/nips_c/CodeGen
NIPS_ASSEMBLER=~/lrde/vmssg/nips_vm/nips_asm.pl
THISDIR=`pwd`
if [ ! -f "$NIPS_COMPILER" ]; then
echo "You have to specify the path of your NIPS compiler (CodeGen)"
exit 3
fi
if [ ! -f "$NIPS_ASSEMBLER" ]; then
echo "You have to specify the path of your NIPS assembler (nips_asm.pl)"
exit 3
fi
if [ $# -ne 1 ]; then
echo "usage : $0 promela_model"
exit 1
fi
FILE="$(cd `dirname $1`; pwd)/`basename $1`"
TMP_FILE="/tmp/`basename $1`"
cpp "$FILE" | sed 's/^#.*$//' > "$TMP_FILE"
cd `dirname $NIPS_COMPILER`
./`basename $NIPS_COMPILER` "$TMP_FILE"
cd `dirname $NIPS_ASSEMBLER`
./`basename $NIPS_ASSEMBLER` "$TMP_FILE.s"
mv "$TMP_FILE.b" "$FILE.b"
echo "$FILE".b

48
iface/nips/dottynips.cc Normal file
View file

@ -0,0 +1,48 @@
// Copyright (C) 2008 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#include "nips.hh"
#include "tgbaalgos/dotty.hh"
int
main(int argc, char **argv)
try
{
if (argc < 2)
{
std::cerr << "usage: " << argv[0] << " promela_bytecode" << std::endl;
exit(1);
}
spot::bdd_dict* dict = new spot::bdd_dict();
spot::nips_interface nips(dict, argv[1]);
spot::tgba* a = nips.automaton();
spot::dotty_reachable(std::cout, a);
delete a;
delete dict;
}
catch (spot::nips_exception& e)
{
std::cerr << e << std::endl;
throw;
}

View file

@ -0,0 +1,129 @@
// Copyright (C) 2008 Laboratoire
// d'Informatique de Paris 6 (LIP6), département Systèmes Répartis
// Coopératifs (SRC), Université Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#include <iostream>
#include <cassert>
#include <cstring>
#include "common.hh"
#include "nips.hh"
#include "tgbaalgos/gtec/gtec.hh"
#include "tgbaalgos/gtec/ce.hh"
#include "tgbaalgos/projrun.hh"
void
display_stats(const spot::unsigned_statistics* s)
{
assert(s);
spot::unsigned_statistics::stats_map::const_iterator i;
for (i = s->stats.begin(); i != s->stats.end(); ++i)
std::cout << i->first << " = " << (s->*i->second)() << std::endl;
}
int
main(int argc, char **argv)
try
{
// enum {Couvreur, Couvreur2} check = Couvreur;
bool compute_counter_example = true;
if (argc < 2)
{
std::cerr << "usage: " << argv[0] << "[OPTIONS...] promela_bytecode"
<< std::endl
<< "with OPTIONS :" << std::endl
<< " -c compute an example" << std::endl
<< " (instead of just checking for emptiness)" << std::endl
<< " -eALGO use ALGO emptiness-check (default)" << std::endl
<< "Where ALGO should be one of:" << std::endl
<< " Cou99(OPTIONS) (the default)" << std::endl
<< " CVWY90(OPTIONS)" << std::endl
<< " GV04(OPTIONS)" << std::endl
<< " SE05(OPTIONS)" << std::endl
<< " Tau03(OPTIONS)" << std::endl
<< " Tau03_opt(OPTIONS)" << std::endl;
exit(2);
}
int arg_index = 1;
spot::emptiness_check_instantiator* echeck_inst = 0;
const char* echeck_algo = "Cou99";
for (; arg_index < argc - 1; ++arg_index)
{
if (!strcmp(argv[arg_index], "-c"))
compute_counter_example = false;
else if (!strncmp(argv[arg_index], "-e", 2))
{
echeck_algo = 2 + argv[arg_index];
if (!*echeck_algo)
echeck_algo = "Cou99";
}
}
const char* err;
echeck_inst =
spot::emptiness_check_instantiator::construct(echeck_algo, &err);
if (!echeck_inst)
{
std::cerr << "Failed to parse argument of -e near `"
<< err << "'" << std::endl;
exit(2);
}
spot::bdd_dict* dict = new spot::bdd_dict();
spot::nips_interface nips(dict, argv[arg_index]);
spot::tgba* a = nips.automaton();
spot::emptiness_check* ec = echeck_inst->instantiate(a);
spot::emptiness_check_result* res = ec->check();
if (res)
{
if (compute_counter_example)
{
spot::tgba_run* run = res->accepting_run();
spot::print_tgba_run(std::cout, a, run);
std::cout << "non empty" << std::endl;
ec->print_stats(std::cout);
delete run;
}
else
{
std::cout << "non empty" << std::endl;
ec->print_stats(std::cout);
}
delete res;
}
else
{
std::cout << "empty" << std::endl;
ec->print_stats(std::cout);
}
std::cout << std::endl;
delete ec;
}
catch (spot::nips_exception& e)
{
std::cerr << e << std::endl;
throw;
}

423
iface/nips/nips.cc Normal file
View file

@ -0,0 +1,423 @@
// Copyright (C) 2008 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#include <cassert>
#include "misc/hashfunc.hh"
#include "nips.hh"
namespace spot
{
namespace
{
// Callback for errors
//////////////////////////////////////////////////////////////////////
// Callback for error which continues on assertions
nipsvm_status_t
search_error_callback(nipsvm_errorcode_t err, nipsvm_pid_t pid,
nipsvm_pc_t pc, void *)
{
char str[256];
nipsvm_errorstring (str, sizeof str, err, pid, pc);
std::cerr << "RUNTIME ERROR (" << err << "): " << str << std::endl;
// Continue on assertions
if (err == 9)
return IC_CONTINUE;
throw nips_exception(std::string(str), static_cast<int>(err));
return IC_STOP;
}
// Callback for error which fails on assertions
nipsvm_status_t
search_error_callback_assert(nipsvm_errorcode_t err, nipsvm_pid_t pid,
nipsvm_pc_t pc, void *)
{
char str[256];
nipsvm_errorstring (str, sizeof str, err, pid, pc);
std::cerr << "RUNTIME ERROR (" << err << "): " << str << std::endl;
throw nips_exception(std::string(str), static_cast<int>(err));
return IC_STOP;
}
// state_nips
//////////////////////////////////////////////////////////////////////
class state_nips: public state
{
public:
state_nips(nipsvm_state_t* s)
{
state_nips_init(s);
nips_state_ = s;
}
state_nips(nipsvm_state_t* s, nipsvm_state_t* nips_state)
{
state_nips_init(s);
nips_state_ = nips_state;
}
void state_nips_init(nipsvm_state_t* s)
{
ref_ = new unsigned(1);
unsigned long size = nipsvm_state_size(s);
unsigned long size_buf = size;
char* state_as_char = new char[size];
state_ = reinterpret_cast<nipsvm_state_t*>(state_as_char);
nipsvm_state_copy(size, s, &state_as_char, &size_buf);
}
state_nips(const state* other)
: ref_(new unsigned(1))
{
const state_nips* o = dynamic_cast<const state_nips*>(other);
assert(o);
ref_ = o->ref_;
++(*ref_);
state_ = o->state_;
}
virtual
~state_nips()
{
--(*ref_);
if (*ref_ == 0)
{
delete[] state_;
delete ref_;
}
}
/// Lazy computation for the hash.
void
hash_comp()
{
size_t size = nipsvm_state_size(get_state());
hash_ = 0;
size_t* state = reinterpret_cast<size_t*>(get_state());
size_t full_size = (size - (size % sizeof (size_t))) / sizeof (size_t);
unsigned i;
for (i = 0; i < full_size; ++i)
hash_ ^= wang32_hash(state[i]);
// Hash on the remainder.
unsigned remainder = 0;
char* state_in_char = reinterpret_cast<char*>(state);
size_t init_pos = i * sizeof (size_t);
unsigned j;
for (j = 0; j < (size % sizeof (size_t)); ++j)
remainder = remainder * 0x100 + state_in_char[init_pos + j];
for (; j < sizeof (size_t); ++j)
remainder *= 0x100;
hash_ ^= remainder;
}
virtual int
compare(const state* other) const
{
const state_nips* o = dynamic_cast<const state_nips*>(other);
assert(o);
return reinterpret_cast<char*>(o->get_state())
- reinterpret_cast<char*>(get_state());
}
virtual size_t
hash() const
{
return reinterpret_cast<char*>(get_state()) - static_cast<char*>(0);
}
virtual state_nips* clone() const
{
return new state_nips(get_state());
}
nipsvm_state_t*
get_state() const
{
return state_;
}
nipsvm_state_t*
get_nips_state() const
{
return nips_state_;
}
private:
unsigned* ref_;
nipsvm_state_t* state_;
nipsvm_state_t* nips_state_;
}; // state_nips
// Callback for successors
//////////////////////////////////////////////////////////////////////
nipsvm_status_t
successor_state_callback(size_t, nipsvm_state_t *succ,
nipsvm_transition_information_t *,
void *context)
{
std::list<state_nips*> *succ_list =
reinterpret_cast<std::list<state_nips*>*>(context);
succ_list->push_back(new state_nips(succ));
return IC_CONTINUE;
}
// tgba_succ_iterator_nips
//////////////////////////////////////////////////////////////////////
class tgba_succ_iterator_nips : public tgba_succ_iterator
{
public:
typedef std::list<state_nips*> s_list;
tgba_succ_iterator_nips(const state_nips* parent);
~tgba_succ_iterator_nips();
virtual void first();
virtual void next();
virtual bool done() const;
virtual state* current_state() const;
virtual bdd current_condition() const;
virtual bdd current_acceptance_conditions() const;
s_list* succ_list_get() const;
private:
const state_nips* parent_;
bool has_acceptance_condition_;
bdd acceptance_condition_;
s_list* succ_list_;
s_list::iterator iterator_;
};
tgba_succ_iterator_nips::tgba_succ_iterator_nips(const state_nips* parent)
: parent_(parent), has_acceptance_condition_(false),
acceptance_condition_(bddfalse), succ_list_(new s_list)
{
}
tgba_succ_iterator_nips::~tgba_succ_iterator_nips()
{
// s_list::iterator it = succ_list_->begin();
// for (; it != succ_list_->end(); ++it)
// delete *it;
delete succ_list_;
}
void
tgba_succ_iterator_nips::first()
{
iterator_ = succ_list_->begin();
}
void
tgba_succ_iterator_nips::next()
{
++iterator_;
}
bool
tgba_succ_iterator_nips::done() const
{
return iterator_ == succ_list_->end();
}
state*
tgba_succ_iterator_nips::current_state() const
{
assert(!done());
return *iterator_;
}
bdd
tgba_succ_iterator_nips::current_condition() const
{
return (nipsvm_state_monitor_acc_or_term(parent_->get_state()) ?
bddtrue : bddfalse);
}
bdd
tgba_succ_iterator_nips::current_acceptance_conditions() const
{
return (nipsvm_state_monitor_acc_or_term(parent_->get_state()) ?
bddtrue : bddfalse);
}
tgba_succ_iterator_nips::s_list*
tgba_succ_iterator_nips::succ_list_get() const
{
return succ_list_;
}
// tgba_nips
//////////////////////////////////////////////////////////////////////
class tgba_nips: public tgba
{
public:
tgba_nips(bdd_dict* dict, nipsvm_t* nipsvm);
tgba_nips(const tgba_nips& other);
tgba_nips& operator=(const tgba_nips& other);
virtual ~tgba_nips();
virtual state* get_init_state() const;
virtual tgba_succ_iterator*
succ_iter(const state* local_state,
const state* global_state,
const tgba* global_automaton) const;
virtual bdd_dict* get_dict() const;
virtual std::string format_state(const state* state) const;
virtual bdd all_acceptance_conditions() const;
virtual bdd neg_acceptance_conditions() const;
protected:
virtual bdd compute_support_conditions(const spot::state* state) const;
virtual bdd compute_support_variables(const spot::state* state) const;
private:
bdd_dict* dict_;
nipsvm_t* nipsvm_;
}; //tgba_nips
tgba_nips::tgba_nips(bdd_dict* dict, nipsvm_t* nipsvm)
: dict_(dict), nipsvm_(nipsvm)
{
}
tgba_nips::tgba_nips(const tgba_nips& other)
: tgba()
{
dict_ = other.dict_;
nipsvm_ = other.nipsvm_;
}
tgba_nips&
tgba_nips::operator=(const tgba_nips& other)
{
if (&other != this)
{
dict_ = other.dict_;
nipsvm_ = other.nipsvm_;
}
return *this;
}
tgba_nips::~tgba_nips()
{
}
state*
tgba_nips::get_init_state() const
{
return new state_nips(nipsvm_initial_state(nipsvm_));
}
tgba_succ_iterator*
tgba_nips::succ_iter(const state* local_state,
const state*,
const tgba*) const
{
const state_nips* state =
reinterpret_cast<const state_nips*>(local_state);
tgba_succ_iterator_nips* iter = new tgba_succ_iterator_nips(state);
nipsvm_scheduler_iter(nipsvm_, state->get_state(),
static_cast<void *>(iter->succ_list_get()));
return iter;
}
bdd_dict* tgba_nips::get_dict() const
{
return dict_;
}
std::string
tgba_nips::format_state(const state* state) const
{
const state_nips* s = dynamic_cast<const state_nips*>(state);
unsigned size = global_state_to_str(s->get_state(), 0, 0, 0);
char buf[size];
global_state_to_str(s->get_state(), 0, buf, size);
return std::string(buf);
}
bdd
tgba_nips::all_acceptance_conditions() const
{
return bddtrue;
}
bdd
tgba_nips::neg_acceptance_conditions() const
{
return bddtrue;
}
bdd
tgba_nips::compute_support_conditions(const spot::state*) const
{
return bddtrue;
}
bdd
tgba_nips::compute_support_variables(const spot::state*) const
{
return bddtrue;
}
} // anonymous
// nips_interface
//////////////////////////////////////////////////////////////////////
nips_interface::nips_interface(bdd_dict* dict,
const std::string& filename)
: dict_(dict)
{
bytecode_ = bytecode_load_from_file(filename.c_str(), 0);
if (bytecode_ == 0)
throw nips_exception("bytecode_load_from_file()");
int res = nipsvm_init(&nipsvm_, bytecode_, successor_state_callback,
search_error_callback);
if (res != 0)
throw nips_exception("nipsvm_init()", res);
}
nips_interface::~nips_interface()
{
nipsvm_finalize(&nipsvm_);
bytecode_unload(bytecode_);
}
tgba* nips_interface::automaton()
{
return new tgba_nips(dict_, &nipsvm_);
}
}

65
iface/nips/nips.hh Normal file
View file

@ -0,0 +1,65 @@
// Copyright (C) 2008 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#ifndef SPOT_IFACE_NIPS_NIPS_HH
# define SPOT_IFACE_NIPS_NIPS_HH
// Do not include nipsvm.h here, or it will polute the user's
// namespace with internal C symbols.
# include <string>
# include "tgba/tgba.hh"
# include "common.hh"
// Damn, nipsvm.h is include, to fix.
# include "nipsvm.h"
namespace spot
{
/// \brief An interface to provide a PROMELA front-end.
///
/// This interface let to use a Promela model as a Büchi automata.
/// It uses the NIPS library, which provied a virtual machine for
/// the state-space exploration of a Promela model, therefore, models
/// must be compiled with the NIPS compiler
/// (http://wwwhome.cs.utwente.nl/~michaelw/nips/).
///
/// With this interface, properties to check aren't defined with the Spot LTL
/// representation, but in defining correctness claims (a monitor) in the
/// Promela model (see chapter 4, The Spin Model Checker: Primer and
/// reference manual, Gerard J.Holzmann).
class nips_interface
{
public:
nips_interface(bdd_dict* dict, const std::string& filename);
~nips_interface();
tgba* automaton();
private:
bdd_dict* dict_;
nipsvm_t nipsvm_;
nipsvm_bytecode_t* bytecode_;
};
}
#endif // SPOT_IFACE_NIPS_NIPS_HH

43
iface/nips/nips_vm/BUGS Normal file
View file

@ -0,0 +1,43 @@
REPORTING BUGS
==============
Please report any bugs encountered to
Michael.Weber@cwi.nl
Please include enough information in a bug report that someone reading
it can reproduce the problem.
KNOWN ISSUES
============
[The gaps in the number sequence belong to old bug descriptions which
have gone away.]
#1:
The VM is relatively slow on Intel Xeon CPUs (compared to AMD64).
Probable reasons:
* Cache effects; Xeon has only 512kB cache
* Pipeline stalls; Xeon has a very deep pipeline, such that
mispredicted branches are very expensive. The inner loop of the
VM does a lot of unpredictable branching...
#2: 2006-02-22
Processes do not die in stack order. No idea why this is needed at
all.
Also, if within the last step(sic!) a process-local variable is set
and then the process dies, the never claim might not see it. This
is a more serious issue. Ways around it: put "l: goto l;" at the
end of such a process.
http://spinroot.com/spin/Man/_pid.html:
"Process instantiation numbers begin at zero for the first process
created and count up for every new process added.
[...]
When a process terminates, it can only die and make its _pid number
available for the creation of another process, if and when it has
the highest _pid number in the system. This means that processes can
only die in the reverse order of their creation (in stack order)."

674
iface/nips/nips_vm/COPYING Normal file
View file

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View file

@ -0,0 +1,231 @@
2007-03-27 Michael Weber <michaelw@foldr.org>
* Released version 1.2.7
2007-03-26 Michael Weber <michaelw@foldr.org>
* nipsvm.c (simple_error_cb): added, fixes segfault in
error callback which received the wrong context;
reported by <timo.lindfors@iki.fi>
2007-03-10 Michael Weber <michaelw@foldr.org>
* Released version 1.2.6
* search.c (search): silenced warning on MacOS X
* instr_step.c (instr_succ): code cleanup:
p_monitor
* Makefile (LDFLAGS_LIB): MacOS X support
2006-07-06 Michael Weber <michaelw@foldr.org>
* nips_disasm.pl: emit !modflags only of non-empty
* nips_asm.pl: if set, use NIPS_HOME to locate *.pl files
2006-06-27 Michael Weber <michaelw@foldr.org>
* main.c: adjusted version
* search.c: initialize n_atomic_steps to zero (lost in last commit)
2006-06-06 Michael Weber <michaelw@foldr.org>
* Released version 1.2.5
* search.c: count and print number of atomic steps.
2006-05-19 Michael Weber <michaelw@foldr.org>
* nipsvm.c (nipsvm_default_error_cb): removed extra comment
* tests/spin/pftp: commented out unused variable
* bytecode.h, bytecode.c (bytecode_str_inf): added interface to
query structure information.
TODO: use binary search to find record.
TODO: adapt interface for bytecode_src_loc
2006-03-14 Michael Weber <michaelw@foldr.org>
* instr.c (instr_load_special_timeout): renamed to instr_lds_timeout
(instr_load_special_pid): renamed to instr_lds_pid
(instr_load_special_nrpr): renamed to instr_lds_nrpr
(instr_load_special_last): renamed to instr_lds_last
(instr_load_special_np): renamed to instr_lds_np
(instr_lrun): fixed storage size bug
(instr_run_intern): fixed storage size bug
2006-03-13 Michael Weber <michaelw@foldr.org>
* Makefile (MACHINE): set MACHINE if compiler target
architecture cannot be worked out from ``gcc -dumpmachine'', e.g.
``make MACHINE=x86_64-linux''
(CFLAGS): use -m64 for x86_64
(CFLAGS_LIB): use -fPIC by default on x86, except for win32
2006-03-11 Michael Weber <michaelw@foldr.org>
* state.c (channel_to_str): more unsignedness fixes
* instr.c (instr_chnew): more unsignedness fixes
* hashtab.c (hashtab_get_pos): more unsignedness fixes
* Makefile: changed way to detect mingw32 compiler to make it work
with ming32 cross compiler, use -DSMALLSTACK by default in windows
(TARGET): set TARGET to cross compile, e.g.:
``make TARGET=i586-mingw32msvc all''
* search.c: use #ifdef WIN32 ... #endif for windows specific
sections
* timeval.h: shift platform specifics here, just include if needed
* instr_step.c: conditionalize on SMALLSTACK
2006-03-09 Michael Weber <michaelw@foldr.org>
* hashtab.c (hashtab_hash): fixed signedness oversight
2006-02-23 Michael Weber <michaelw@foldr.org>
* BUGS: Added Bug#2.
* instr_step.c (instr_succ): extension of finite paths only if
monitor is present.
* state_inline.h (global_state_compare): add type cast so that
C++ compilers don't choke. Note, that this function is
deprecated, still.
* nipsvm.h: comment cosmetics
2006-02-17 Michael Weber <michaelw@foldr.org>
* nips_disasm.pl: very simple bytecode-to-C stub generator.
The generated function must be inserted manually into the VM
currently. Also, giving such a specialized VM a different
bytecode file than was used for generating the C stub results
in hilarious behavior. Not recommended.
Speedup: yes, but not very large. Much is eaten by complicated
PC handling. Possible optimizations: move instruction decoding
out of execution loop (requires quite some surgery)
* nips_asm_instr.pl (get_instructions): tag instructions whether
they change control-flow or yield errors.
(instruction_cfun): bytecode to C-function name mapping
* instr.c (instr_ldc): renamed from instr_load_constant to match
functions names generated by disassembler script
(instr_trunc): renamed from instr_truncate
(instr_exec): replaced occurrences of renamed functions
2006-02-15 Michael Weber <michaelw@foldr.org>
* nips_asm.pl: collate flags for the same address instead of
emitting them individually. Happens, if the compiler generates
several labels for the same code address each of which has flags
set. I am not sure that was a real problem for the current VM
implementation.
2006-02-11 Michael Weber <michaelw@foldr.org>
* state.c (global_state_to_str): output flags (accepting,
terminated) if monitor is present
2006-02-06 Michael Weber <michaelw@foldr.org>
* search.c (search_succ_cb): fixed type of succ_sz (size_t)
* state.c (global_state_to_str): fixed format arg mismatch for
64-bit systems.
2006-02-03 Michael Weber <michaelw@foldr.org>
* Released version 1.2.3
2006-02-02 Michael Weber <michaelw@foldr.org>
* search.c (t_search_context, search, search_succ_cb): count
transitions
* nipsvm.h (nipsvm_state_monitor_accepting)
(nipsvm_state_monitor_terminated)
(nipsvm_state_monitor_acc_or_term): added
* state.c, state.h (global_state_monitor_accepting)
(global_state_monitor_terminated)
(global_state_monitor_acc_or_term): deprecated
2006-02-01 Michael Weber <michaelw@foldr.org>
* hashtab.c: faster hash collision resolution using (very simple)
double hashing, speeding up almost-filled hash table insertions.
I measured a speedup of 1.35 when filling hash table to 93%.
API CHANGES AHEAD
Old API functions are still there, but will be be phased out
eventually.
* nipsvm.c, nipsvm.h : beginnings of a new interface: nipsvm_*
equivalents of the most used global_* methods
* simpler and less fragile callback interface, all extra
information is stashed away in
nipsvm_transition_information_t (can be extended without visibly
changing the API)
* converted some of the internals to the new API (work in
progress)
* some state handling functions have grown an extra size_t
argument, instead of calculating the state size themselves
* search.c (search): added initial state to hash table to
eliminate off-by-one difference between visited states and
states in table
* Makefile: added -g option by default, removed rt_err.o from
libnips_vm (not needed)
2005-10-11, 2005-10-12
----------------------
added range errors in assembler
changed maximum size of global variables to 65535
- access to global variables 256..65535 not possible using LDVA / STVA
use LDC <address> ; LDV / STV instead
added instructions
- GLOBSZX <size of global variables (0..65535)>
set size of global variables using 2 byte constant
- LDB [L|G]
load bit from local / global memory
stack:<addr>:<bit> ---> stack:<value>
only last 3 bits of <bit> are used
- STB [L|G]
store bit into local / global memory
stack:<value>:<addr>:<bit> ---> stack
<value> must be 0 or 1 (other values are treated as 1)
- LDV / STV [L|G] [2u|2s|4] LE
load / store variable in little endian format
works as version without LE at end (but in little endian format)
- BGET /BSET r_i, b for bit access to registers
behaviour is as FGET b / FSET b (but for r_i instead of r_F)
1.2.2 2005-09-30
----------------
tried to fix unalign errors on alpha
tried to fix left unalign errors on alpha
fixed byte order bug in LVAR instructions (remote ref)
added global_state_initial_size
added string output of states
1.2.1 2005-09-21
----------------
now forcing cycle in case of blocking system
- needed in most LTL model checkers to recognize
invalid states in which system blocks
- added INSTR_SUCC_CB_FLAG_SYS_BLOCK flag (successor callback)
1.2 2005-09-06
--------------
first public release

237
iface/nips/nips_vm/INSTALL Normal file
View file

@ -0,0 +1,237 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
6. Often, you can also type `make uninstall' to remove the installed
files again.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View file

@ -0,0 +1,41 @@
nipsvmdir = $(srcdir)
nipsvm_HEADERS = \
bytecode.h \
hashtab.h \
instr.h \
instr_step.h \
instr_tools.h \
instr_wrap.h \
interactive.h \
nipsvm.h \
rt_err.h \
search.h \
split.h \
state.h \
state_inline.h \
state_parts.h \
timeval.h \
tools.h
lib_LTLIBRARIES = libnipsvm.la
libnipsvm_la_SOURCES = \
bytecode.c \
hashtab.c \
instr.c \
instr_step.c \
instr_tools.c \
instr_wrap.c \
interactive.c \
nipsvm.c \
rt_err.c \
search.c \
split.c \
state.c \
state_parts.c
noinst_PROGRAMS = nipsvm
nipsvm_SOURCES = main.c
nipsvm_LDADD = libnipsvm.la

75
iface/nips/nips_vm/README Normal file
View file

@ -0,0 +1,75 @@
NIPS VM - New Implementation of Promela Semantics Virtual Machine
Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
Lehrstuhl fuer Informatik II, RWTH Aachen
Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
very short description
======================
NIPS VM is a non-deterministic virtual machine
to be used for state space generation in model checkers.
For more information, see:
Stefan Schuermans, Ein Compiler und eine Virtuelle Maschine
zur Zustandsraumgenerierung, Dimplomarbeit, Lehrstuhl II fuer
Informatik RWTH Aachen, Oktober 2005
(available only in German)
For a description of the assembler and bytecode format, see:
assembler_format.txt
bytecode_format.txt
dependencies
============
GNU make 3.80
GNU C Compiler: gcc 3.3
C runtime library: libc 2.3 (at least libc 2.1)
perl 5.8.4
(NIPS C 1.2+)
versions given are known to work - other may work, too
getting started
===============
compilation
-----------
make
running an example
------------------
cd tests
make
make example
running some tests
------------------
make benchmark
cd ..
assembling own files
--------------------
./nips_asm.pl mycode.s
running interactive / random simulations
----------------------------------------
./nips_vm [-R] mycode.b
generating full state space with breadth first / depth first search
-------------------------------------------------------------------
./nips_vm {-B|-D 30} mycode.b
other features
--------------
./nips_vm --help
benchmarking against SPIN
-------------------------
# ensure that NIPS C is installed in ../nips_c/
cd tests/spin
./Benchmark

View file

@ -0,0 +1,12 @@
KILL
----
set process <pid> to terminated
stack before: [...] <pid>
stack after: [...]
POPX
----
discard topmost value from stack
stack before: [...] <value>
stack after: [...]

View file

@ -0,0 +1,91 @@
NIPS - New Implementation of Promela Semantics
Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
Lehrstuhl fuer Informatik II, RWTH Aachen
Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
specification of the assembler file format
The entire format is line based. A line can contain a label, an instruction,
a label and an instruction or a directive. An additional comment separated by
";" is allowed at the end of every line.
begin of new module
-------------------
"!module" <name>
with <name> being enclosed in "
instruction lines
-----------------
3 formats are allowed
<label>:
<instruction> <parameter 1> ... <parameter n>
<label>: <instruction> <parameter 1> ... <parameter n>
with <label> = "[A-Za-z][A-Za-Z0-9_]*"
For a list of instructions with their parameters, see "nips_asm_instr.pl".
The parameters may be fixed text or be of one of the following types:
!const?: a constant value, i.e. a number
!reg: some register "r0" ... "r7"
!addr: a relative address, i.e. a label name
!address: an absolute address, i.e. a label name
flags (of bytecode addresses)
-----------------------------
"!flags" "progress"? "accept"?
"!flags_addr" <address> "progress"? "accept"?
with <address> of format "0x[0-9A-Fa-f]+"
marks the current / the specified address with the specified flags
string (for the string table)
-----------------------------
"!string" <index> <text>
<index> is used in the PRINT* commands.
<text> must be enclosed in ".
source location information
---------------------------
"!srcloc" <line> <column>
"!srcloc_addr" <address> <line> <column>
with <address> of format "0x[0-9A-Fa-f]+"
with <line> and <column> being numbers
specifies the location in the source code
corresponding to the current / the specified address
structure information
---------------------
"!strinf" "(begin|end)" <type> <name>?
"!strinf_addr" <address> "(begin|end)" <type> <name>?
with <address> of format "0x[0-9A-Fa-f]+"
with <type> and <name> of format "[A-Za-z]+"
specifies the begin / end of some structure of <type> in the source code
at the current / the specified address

View file

@ -0,0 +1,610 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "bytecode.h"
#include "tools.h"
#ifdef EPROTO
# define E_BC_INV_FMT EPROTO
#else
# define E_BC_INV_FMT EINVAL
#endif
// load bytecode module from file - check module name
// returns -1 of error, 0 if not equal, 1 if equal
static int bytecode_load_from_file_check_module( FILE *file, const char *module )
{
// read size of module name
uint16_t mod_name_sz;
if( fread( &mod_name_sz, 1, sizeof( mod_name_sz ), file ) != sizeof( mod_name_sz ) )
{
errno = E_BC_INV_FMT;
return -1;
}
mod_name_sz = be2h_16( mod_name_sz );
if( mod_name_sz == 0 )
{
errno = E_BC_INV_FMT;
return -1;
}
// read module name
char mod_name[mod_name_sz];
if( fread( mod_name, 1, mod_name_sz, file ) != mod_name_sz )
{
errno = E_BC_INV_FMT;
return -1;
}
mod_name[mod_name_sz - 1] = 0; // terminate string (should already be terminated there)
// compare module name (NULL matches any name)
return module == NULL || strcmp( mod_name, module ) == 0;
}
// load bytecode module from file - read a string
static char * bytecode_load_from_file_string( FILE *file )
{
// read size of string
uint16_t str_sz;
if( fread( &str_sz, 1, sizeof( str_sz ), file ) != sizeof( str_sz ) )
{
errno = E_BC_INV_FMT;
return NULL;
}
str_sz = be2h_16( str_sz );
if( str_sz == 0 )
{
errno = E_BC_INV_FMT;
return NULL;
}
// allocate memory
char *str = (char *)malloc( str_sz );
if( str == NULL )
{
errno = ENOMEM;
return NULL;
}
// read string
if( fread( str, 1, str_sz, file ) != str_sz )
{
free( str );
errno = E_BC_INV_FMT;
return NULL;
}
str[str_sz - 1] = 0; // terminate string (should already be terminated there)
return str;
}
// load bytecode module from file - process module
static st_bytecode * bytecode_load_from_file_module( FILE *file )
{
// read number of parts
uint16_t part_cnt;
if( fread( &part_cnt, 1, sizeof( part_cnt ), file ) != sizeof( part_cnt ) )
{
errno = E_BC_INV_FMT;
return NULL;
}
part_cnt = be2h_16( part_cnt );
// allocate structure
st_bytecode *bytecode = (st_bytecode *)malloc( sizeof( st_bytecode ) );
if( bytecode == NULL )
{
errno = ENOMEM;
return NULL;
}
// initialize structure
bytecode->modflags = 0;
bytecode->size = 0;
bytecode->ptr = NULL;
bytecode->flag_cnt = 0;
bytecode->flags = NULL;
bytecode->string_cnt = 0;
bytecode->strings = NULL;
bytecode->src_loc_cnt = 0;
bytecode->src_locs = NULL;
bytecode->str_inf_cnt = 0;
bytecode->str_infs = NULL;
uint16_t part_no;
for( part_no = 0; part_no < part_cnt; part_no++ )
{
// read part type and part size
char part_type[4];
uint32_t part_sz;
if( fread( &part_type, 1, sizeof( part_type ), file ) != sizeof( part_type ) ||
fread( &part_sz, 1, sizeof( part_sz ), file ) != sizeof( part_sz ) )
{
errno = E_BC_INV_FMT;
break;
}
part_sz = be2h_32( part_sz );
// module flags
if( memcmp( part_type, "modf", 4 ) == 0 )
{
uint32_t modflags;
if( fread( &modflags, 1, sizeof( modflags ), file ) != sizeof( modflags ) )
{
errno = E_BC_INV_FMT;
break;
}
bytecode->modflags |= be2h_32( modflags );
}
// bytecode
else if( memcmp( part_type, "bc ", 4 ) == 0 )
{
// two bytecodes in one module are invalid
if( bytecode->ptr != NULL )
{
errno = E_BC_INV_FMT;
break;
}
// allocate memory
bytecode->size = part_sz;
bytecode->ptr = (uint8_t *)malloc( bytecode->size == 0 ? 1 : bytecode->size );
if( bytecode->ptr == NULL )
{
errno = ENOMEM;
break;
}
// read bytecode
if( fread( bytecode->ptr, 1, bytecode->size, file ) != bytecode->size )
{
errno = E_BC_INV_FMT;
break;
}
}
// flag table
else if( memcmp( part_type, "flag", 4 ) == 0 )
{
// two flag tables in one module are invalid
if( bytecode->flags != NULL )
{
errno = E_BC_INV_FMT;
break;
}
// read flag count
uint16_t flag_cnt;
if( fread( &flag_cnt, 1, sizeof( flag_cnt ), file ) != sizeof( flag_cnt ) )
{
errno = E_BC_INV_FMT;
break;
}
bytecode->flag_cnt = be2h_16( flag_cnt );
// allocate memory
bytecode->flags = (st_bc_flag *)malloc( bytecode->flag_cnt == 0 ? 1 : bytecode->flag_cnt * sizeof( st_bc_flag ) );
if( bytecode->flags == NULL )
{
errno = ENOMEM;
break;
}
// read flag entries
uint16_t i;
for( i = 0; i < bytecode->flag_cnt; i++ )
{
uint32_t addr, flags;
if( fread( &addr, 1, sizeof( addr ), file ) != sizeof( addr ) ||
fread( &flags, 1, sizeof( flags ), file ) != sizeof( flags ) )
{
errno = E_BC_INV_FMT;
break;
}
bytecode->flags[i].addr = be2h_32( addr );
bytecode->flags[i].flags = be2h_32( flags );
}
if( i < bytecode->flag_cnt )
break;
}
// string table
else if( memcmp( part_type, "str ", 4 ) == 0 )
{
// two string tables in one module are invalid
if( bytecode->strings != NULL )
{
errno = E_BC_INV_FMT;
break;
}
// read string count
uint16_t string_cnt;
if( fread( &string_cnt, 1, sizeof( string_cnt ), file ) != sizeof( string_cnt ) )
{
errno = E_BC_INV_FMT;
break;
}
bytecode->string_cnt = be2h_16( string_cnt );
// allocate memory
bytecode->strings = (char **)malloc( bytecode->string_cnt == 0 ? 1 : bytecode->string_cnt * sizeof( char * ) );
if( bytecode->strings == NULL )
{
errno = ENOMEM;
break;
}
uint16_t i;
for( i = 0; i < bytecode->string_cnt; i++ )
bytecode->strings[i] = NULL;
// read strings
for( i = 0; i < bytecode->string_cnt; i++ )
{
bytecode->strings[i] = bytecode_load_from_file_string( file );
if( bytecode->strings[i] == NULL )
break;
}
if( i < bytecode->string_cnt )
break;
}
// source location table
else if( memcmp( part_type, "sloc", 4 ) == 0 )
{
// two source location tables in one module are invalid
if( bytecode->src_locs != NULL )
{
errno = E_BC_INV_FMT;
break;
}
// read source location count
uint16_t src_loc_cnt;
if( fread( &src_loc_cnt, 1, sizeof( src_loc_cnt ), file ) != sizeof( src_loc_cnt ) )
{
errno = E_BC_INV_FMT;
break;
}
bytecode->src_loc_cnt = be2h_16( src_loc_cnt );
// allocate memory
bytecode->src_locs = (st_src_loc *)malloc( bytecode->src_loc_cnt == 0 ? 1 : bytecode->src_loc_cnt * sizeof( st_src_loc ) );
if( bytecode->src_locs == NULL )
{
errno = ENOMEM;
break;
}
// read source locations
uint16_t i;
for( i = 0; i < bytecode->src_loc_cnt; i++ )
{
uint32_t addr, line, col;
if( fread( &addr, 1, sizeof( addr ), file ) != sizeof( addr ) ||
fread( &line, 1, sizeof( line ), file ) != sizeof( line ) ||
fread( &col, 1, sizeof( col ), file ) != sizeof( col ) )
{
errno = E_BC_INV_FMT;
break;
}
bytecode->src_locs[i].addr = be2h_32( addr );
bytecode->src_locs[i].line = be2h_32( line );
bytecode->src_locs[i].col = be2h_32( col );
}
if( i < bytecode->src_loc_cnt )
break;
}
// structure information table
else if( memcmp( part_type, "stin", 4 ) == 0 )
{
// two source location tables in one module are invalid
if( bytecode->str_infs != NULL )
{
errno = E_BC_INV_FMT;
break;
}
// read structure information count
uint16_t str_inf_cnt;
if( fread( &str_inf_cnt, 1, sizeof( str_inf_cnt ), file ) != sizeof( str_inf_cnt ) )
{
errno = E_BC_INV_FMT;
break;
}
bytecode->str_inf_cnt = be2h_16( str_inf_cnt );
// allocate memory
bytecode->str_infs = (st_str_inf *)malloc( bytecode->str_inf_cnt == 0 ? 1 : bytecode->str_inf_cnt * sizeof( st_str_inf ) );
if( bytecode->src_locs == NULL )
{
errno = ENOMEM;
break;
}
// read structure information
uint16_t i;
for( i = 0; i < bytecode->str_inf_cnt; i++ )
{
uint32_t addr;
uint8_t code;
if ( fread( &addr, 1, sizeof( addr ), file ) != sizeof( addr ) ||
fread( &code, 1, sizeof( code ), file ) != sizeof( code ))
{
errno = E_BC_INV_FMT;
break;
}
// read type
char *type = bytecode_load_from_file_string( file );
if( type == NULL )
{
errno = E_BC_INV_FMT;
break;
}
// read name
char *name = bytecode_load_from_file_string( file );
if( name == NULL )
{
errno = E_BC_INV_FMT;
break;
}
bytecode->str_infs[i].addr = be2h_32( addr );
bytecode->str_infs[i].code = code;
bytecode->str_infs[i].type = type;
bytecode->str_infs[i].name = name;
}
if( i < bytecode->str_inf_cnt )
break;
}
// unknown part
else
fseek( file, part_sz, SEEK_CUR );
} // for( part_no ...
// no bytecode found
if( bytecode->ptr == NULL )
{
errno = ESRCH;
// mark as error
part_no = 0;
part_cnt = 0;
}
// some error occured in loop
if( part_no < part_cnt || part_cnt == 0 )
{
// cleanup and return NULL (errno is already set)
if( bytecode->ptr != NULL )
free( bytecode->ptr );
if( bytecode->flags != NULL )
free( bytecode->flags );
if( bytecode->strings != NULL )
{
uint16_t i;
for( i = 0; i < bytecode->string_cnt; i++ )
if( bytecode->strings[i] != NULL )
free( bytecode->strings[i] );
free( bytecode->strings );
}
if( bytecode->src_locs != NULL )
free( bytecode->src_locs );
free( bytecode );
return NULL;
}
return bytecode;
}
// load bytecode module from file - process sections
static st_bytecode * bytecode_load_from_file_sections( FILE *file, const char *module )
{
// get number of sections in file
uint16_t sec_cnt;
if( fread( &sec_cnt, 1, sizeof( sec_cnt ), file ) != sizeof( sec_cnt ) )
{
errno = E_BC_INV_FMT;
return NULL;
}
sec_cnt = be2h_16( sec_cnt );
// read sections
uint16_t sec_no;
for( sec_no = 0; sec_no < sec_cnt; sec_no++ )
{
// read section type and section size
char sec_type[4];
uint32_t sec_sz;
if( fread( &sec_type, 1, sizeof( sec_type ), file ) != sizeof( sec_type ) ||
fread( &sec_sz, 1, sizeof( sec_sz ), file ) != sizeof( sec_sz ) )
{
errno = E_BC_INV_FMT;
return NULL;
}
sec_sz = be2h_32( sec_sz );
// module section
if( memcmp( sec_type, "mod ", 4 ) == 0 )
{
// remember position in file
long pos = ftell( file );
// check if module name matches
int result = bytecode_load_from_file_check_module( file, module );
if( result < 0 ) // error
return NULL;
if( result > 0 ) // module name matches
return bytecode_load_from_file_module( file );
// skip module
fseek( file, pos + sec_sz, SEEK_SET );
}
// unknown section
else
fseek( file, sec_sz, SEEK_CUR );
} // for( sec_no ...
errno = ESRCH;
return NULL;
}
// load bytecode module from file
// if module == NULL the first module is loaded
// returns NULL on error (errno ist set in this case)
st_bytecode * bytecode_load_from_file( const char *filename, const char *module ) // extern
{
// open bytecode file for reading
FILE *file = fopen( filename, "rb" );
if( file == NULL )
return NULL;
// magic to check at beginning of bytecode
static char bc_magic[8] = "NIPS v2 ";
// read magic
char magic[sizeof( bc_magic )];
if( fread( magic, 1, sizeof( magic ), file ) != sizeof( magic ) )
{
fclose( file );
return NULL;
}
// check magic
if( memcmp( bc_magic, magic, sizeof( bc_magic ) ) != 0 )
{
fclose( file );
errno = E_BC_INV_FMT;
return NULL;
}
// process sections
st_bytecode *bytecode = bytecode_load_from_file_sections( file, module );
fclose( file );
return bytecode;
}
// unload bytecode
void bytecode_unload( st_bytecode *bytecode ) // extern
{
free( bytecode->ptr );
uint16_t i;
if( bytecode->flags != NULL )
free( bytecode->flags );
if( bytecode->strings != NULL )
{
for( i = 0; i < bytecode->string_cnt; i++ )
free( bytecode->strings[i] );
free( bytecode->strings );
}
if( bytecode->src_locs != NULL )
free( bytecode->src_locs );
if (bytecode->str_infs != NULL)
free(bytecode->str_infs);
free( bytecode );
}
// check if a monitor process exists in bytecode
int bytecode_monitor_present( st_bytecode *bytecode ) // extern
{
return (bytecode->modflags & BC_MODFLAG_MONITOR) != 0;
}
// get flags at address (e.g. program counter)
uint32_t bytecode_flags( st_bytecode *bytecode, uint32_t addr ) // extern
{
int start, end, i;
start = 0;
end = bytecode->flag_cnt - 1;
while( start <= end )
{
i = (start + end) / 2;
if( addr < bytecode->flags[i].addr )
end = i - 1;
else if( addr > bytecode->flags[i].addr )
start = i + 1;
else
return bytecode->flags[i].flags;
}
return 0;
}
// get source location from address (e.g. program counter)
// TODO: change to same interface as bytecode_str_inf
st_src_loc bytecode_src_loc( st_bytecode *bytecode, uint32_t addr ) // extern
{
int start, end, i;
if( bytecode->src_loc_cnt < 1 )
return (st_src_loc){ .addr = addr, .line = -1, .col = -1 };
start = 0;
end = bytecode->src_loc_cnt - 1;
for( ; ; )
{
i = (start + end) / 2;
if( addr < bytecode->src_locs[i].addr )
{
if( i - 1 < start )
return bytecode->src_locs[i > 0 ? i - 1 : 0];
end = i - 1;
}
else if( addr > bytecode->src_locs[i].addr )
{
if( i + 1 > end )
return bytecode->src_locs[end];
start = i + 1;
}
else
return bytecode->src_locs[i];
}
}
// get structure information from address (e.g. program counter), NULL if not found
// *start denotes where to start search, and if found, is modified to
// the next valid start position. Multiple records can be retrieved like this:
// unsigned int seq = 0;
// while ((s_inf = bytecode_str_inf (bc, addr, &seq)) != NULL) {
// /* stuff with *s_inf */
// }
// ASSUMES str_infs sorted by addr field
// TODO: replace linear search with binary search
extern st_str_inf *
bytecode_str_inf( st_bytecode *bytecode, uint32_t addr, unsigned int *start)
{
int i;
for(i = *start; i < bytecode->str_inf_cnt; ++i)
{
if (bytecode->str_infs[i].addr == addr) {
*start = i+1;
return &bytecode->str_infs[i];
} else if (bytecode->str_infs[i].addr > addr) {
break;
}
}
return NULL;
}

View file

@ -0,0 +1,93 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_bytecode
#define INC_bytecode
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
// module flags
#define BC_MODFLAG_MONITOR 0x00000001 // if a monitor process exists in bytecode
// type for flags at some address
#define BC_FLAG_PROGRESS 0x00000001
#define BC_FLAG_ACCEPT 0x00000002
typedef struct t_bc_flag
{
uint32_t addr, flags;
} st_bc_flag;
// type for a source location (mapping of address to line and column in source)
typedef struct t_src_loc
{
uint32_t addr, line, col;
} st_src_loc;
// type for a structure inforamtion
typedef struct t_str_inf
{
uint32_t addr;
uint8_t code;
char *type, *name;
} st_str_inf;
// type for pointer to bytecode and its size
typedef struct t_bytecode
{
uint32_t modflags; // module flags
uint32_t size; // size of bytecode
uint8_t *ptr; // pointer to bytecode
uint16_t flag_cnt; // number of flag entries in table
st_bc_flag *flags; // flag table
uint16_t string_cnt; // number of strings in string table
char **strings; // string table
uint16_t src_loc_cnt; // number of source locations in table
st_src_loc *src_locs; // source location table
uint16_t str_inf_cnt; // number of structure information in table
st_str_inf *str_infs; // structure information table
} st_bytecode;
// load bytecode module from file
// if module == NULL the first module is loaded
// returns NULL on error (errno ist set in this case)
extern st_bytecode * bytecode_load_from_file( const char *filename, const char *module );
// unload bytecode
extern void bytecode_unload( st_bytecode *bytecode );
// check if a monitor process exists in bytecode
extern int bytecode_monitor_present( st_bytecode *bytecode );
// get flags at address (e.g. program counter)
extern uint32_t bytecode_flags( st_bytecode *bytecode, uint32_t addr );
// get source location from address (e.g. program counter)
extern st_src_loc bytecode_src_loc( st_bytecode *bytecode, uint32_t addr );
// get structure information from address (e.g. program counter)
extern st_str_inf *bytecode_str_inf( st_bytecode *bytecode, uint32_t addr, unsigned int *start );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_bytecode

View file

@ -0,0 +1,103 @@
NIPS - New Implementation of Promela Semantics
Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
Lehrstuhl fuer Informatik II, RWTH Aachen
Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
specification of the bytecode file format
bytecode file:
<file header>
<section 0>
...
<section sec_cnt-1>
file header:
"NIPS v1a"
sec_cnt: uint16_t, big-endian, number of sections in file
section:
sec_type: 4 uint8_t, ASCII, type of section
sec_sz: uint32_t, big-endian, size of section's content
section content: sec_sz uint8_t
section: module
sec_type: "mod "
sec_sz: uint32_t, big-endian, size of module section's content
<string module_name>
part_cnt: uint16_t, big-endian, number of parts in module
<part 0>
...
<part part_cnt-1>
part
part_type: 4 uint8_t, ASCII, type of part
part_sz: uint32_t, big-endian, size of part's content
part content: part_sz uint8_t
part: module flags
part_type: "modf"
part_sz: uint32_t, big-endian, always 4
mod_flags: uint32_t, big-endian, some flags describing properties of the module
0x00000001 = monitor exists
part: bytecode
part_type: "bc "
part_sz: uint32_t, big-endian, size of bytecode
bytecode: part_sz uint8_t, the bytecode
part: flag table
part_type: "flag"
part_sz: uint32_t, big-endian, size of entire flag table
flag_cnt: uint16_t, big-endian, number of flag entries in this table
the entries in this table have to be sorted ascending by their addresses
<flag 0>
...
<flag flag_cnt-1>
flag:
addr: uint32_t, big-endian, address for which flags are given
flags: uint32_t, the flags for this address
0x00000001 = progress state
0x00000002 = accept state
part: string table
part_type: "str "
part_sz: uint32_t, big-endian, size of entire string table
str_cnt: uint16_t, big-endian, number of strings in this table
<string 0>
...
<string str_cnt-1>
string:
str_sz: uint16_t, big-endian, size of string (including terminating zero)
str: str_sz uint8_t, ASCII, zero-terminated string
part: source location table
part_type: "sloc"
part_sz: uint32_t, big-endian, size of entire source location table
sloc_cnt: uint16_t, big-endian, number of source locations in this table
the entries in this table have to be sorted ascending by their addresses
<srcloc 0>
...
<srcloc sloc_cnt-1>
srcloc:
addr: uint32_t, big-endian, address whose source location is to be specified
line: uint32_t, big-endian, line number
col: uint32_t, big-endian, column number
part: structure information table
part_type: "stin"
part_sz: uint32_t, big-endian, size of entire structure information table
stin_cnt: uint16_t, big-endian, number of structure information entries in this table
the entries in this table have to be sorted ascending by their addresses
<strinf 0>
...
<strinf stin_cnt-1>
strinf:
addr: uint32_t, big-endian, address for which some structure information is given
code: uint8_t, 0x00 = start of some structure, 0x01 = end of some structure, 0x02 = middle in some structure
<string type>
<string name>

View file

@ -0,0 +1,322 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include "state.h"
#include "tools.h"
#include "hashtab.h"
#define HASH_MIN_ENTRIES 65536 // 64k entries (because of calculation of hash_rest)
// type for hash values
typedef unsigned long t_hash;
typedef st_global_state_header *t_hashtab_value;
typedef struct t_hashtab_bucket
{
t_hashtab_value value;
unsigned short hash_rest; // part of hash value that is lost in "entry = hash_value % entries"
// ---> "hash_rest = hash_value / entries"
} PACKED st_hashtab_bucket;
// hashtable pointer type
typedef struct t_hashtab_header
{
unsigned long entries; // number of entries in the hash table
unsigned long retries; // maximum allowed number of retries by re-hashing
unsigned long memory_size; // size of entire hash table in bytes
unsigned int max_retry_count; // maximum number of retries needed so far
unsigned long conflict_count; // number of conflicts found so far
st_hashtab_bucket *bucket; // entries of hashtable
} st_hashtab_header;
// HASH FUNCTION TAKEN FROM http://burtleburtle.net/bob/hash/doobs.html - begin
typedef uint32_t ub4; /* unsigned 4-byte quantities */
typedef uint8_t ub1; /* unsigned 1-byte quantities */
#define hashsize(n) ((ub4)1<<(n))
#define hashmask(n) (hashsize(n)-1)
/*
* --------------------------------------------------------------------
* mix -- mix 3 32-bit values reversibly.
* For every delta with one or two bits set, and the deltas of all three
* high bits or all three low bits, whether the original value of a,b,c
* is almost all zero or is uniformly distributed,
* * If mix() is run forward or backward, at least 32 bits in a,b,c
* have at least 1/4 probability of changing.
* * If mix() is run forward, every bit of c will change between 1/3 and
* 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
* mix() was built out of 36 single-cycle latency instructions in a
* structure that could supported 2x parallelism, like so:
* a -= b;
* a -= c; x = (c>>13);
* b -= c; a ^= x;
* b -= a; x = (a<<8);
* c -= a; b ^= x;
* c -= b; x = (b>>13);
* ...
* Unfortunately, superscalar Pentiums and Sparcs can't take advantage
* of that parallelism. They've also turned some of those single-cycle
* latency instructions into multi-cycle latency instructions. Still,
* this is the fastest good hash I could find. There were about 2^^68
* to choose from. I only looked at a billion or so.
* --------------------------------------------------------------------
*/
#define mix(a,b,c) \
{ \
a -= b; a -= c; a ^= (c>>13); \
b -= c; b -= a; b ^= (a<<8); \
c -= a; c -= b; c ^= (b>>13); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<16); \
c -= a; c -= b; c ^= (b>>5); \
a -= b; a -= c; a ^= (c>>3); \
b -= c; b -= a; b ^= (a<<10); \
c -= a; c -= b; c ^= (b>>15); \
}
/*
* --------------------------------------------------------------------
* hash() -- hash a variable-length key into a 32-bit value
* k : the key (the unaligned variable-length array of bytes)
* len : the length of the key, counting by bytes
* initval : can be any 4-byte value
* Returns a 32-bit value. Every bit of the key affects every bit of
* the return value. Every 1-bit and 2-bit delta achieves avalanche.
* About 6*len+35 instructions.
*
* The best hash table sizes are powers of 2. There is no need to do
* mod a prime (mod is sooo slow!). If you need less than 32 bits,
* use a bitmask. For example, if you need only 10 bits, do
* h = (h & hashmask(10));
* In which case, the hash table should have hashsize(10) elements.
*
* If you are hashing n strings (ub1 **)k, do it like this:
* for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
*
* By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
* code any way you wish, private, educational, or commercial. It's free.
*
* See http://burtleburtle.net/bob/hash/evahash.html
* Use for hash table lookup, or anything where one collision in 2^^32 is
* acceptable. Do NOT use for cryptographic purposes.
* --------------------------------------------------------------------
*/
static inline ub4 hash( k, length, initval)
register ub1 *k; /* the key */
register ub4 length; /* the length of the key */
register ub4 initval; /* the previous hash, or an arbitrary value */
{
register ub4 a,b,c,len;
/* Set up the internal state */
len = length;
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
c = initval; /* the previous hash value */
/*---------------------------------------- handle most of the key */
while (len >= 12)
{
a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
mix(a,b,c);
k += 12; len -= 12;
}
/*------------------------------------- handle the last 11 bytes */
c += length;
switch(len) /* all the case statements fall through */
{
case 11: c+=((ub4)k[10]<<24);
case 10: c+=((ub4)k[9]<<16);
case 9 : c+=((ub4)k[8]<<8);
/* the first byte of c is reserved for the length */
case 8 : b+=((ub4)k[7]<<24);
case 7 : b+=((ub4)k[6]<<16);
case 6 : b+=((ub4)k[5]<<8);
case 5 : b+=k[4];
case 4 : a+=((ub4)k[3]<<24);
case 3 : a+=((ub4)k[2]<<16);
case 2 : a+=((ub4)k[1]<<8);
case 1 : a+=k[0];
/* case 0: nothing left to add */
}
mix(a,b,c);
/*-------------------------------------------- report the result */
return c;
}
// HASH FUNCTION TAKEN FROM http://burtleburtle.net/bob/hash/doobs.html - end
// a hash function for binary data
static t_hash hashtab_hash( unsigned char * p_data, unsigned long size, unsigned long initval )
{
return hash( p_data, size, initval );
}
// create a new hash table
t_hashtab hashtab_new( unsigned long entries, unsigned long retries ) // extern
{
unsigned long size;
t_hashtab hashtab;
char * ptr;
// correct parameters
entries = max( entries, HASH_MIN_ENTRIES );
retries = max( retries, 1 );
// calculate size
size = sizeof( st_hashtab_header ); // header
size += entries * sizeof( st_hashtab_bucket ); // entries
// allocate memory
hashtab = (t_hashtab)calloc( 1, size );
if( hashtab == NULL )
return NULL;
// initialize hashtab header
hashtab->entries = entries;
hashtab->retries = retries;
hashtab->memory_size = size;
// initialize hashtable
ptr = (char *)hashtab + sizeof( st_hashtab_header ); // pointer to array with entries
hashtab->bucket = (st_hashtab_bucket *)ptr;
return hashtab;
}
// free a hash table
void hashtab_free( t_hashtab hashtab ) // extern
{
free( hashtab );
}
// get index for/of an entry in a hash table
// - returns: 1 if state can be inserted into hash table
// 0 if state is already in hash table
// -1 if state did not fit into hash table (hash conflict that could not be resolved)
// - fills *p_pos with pointer to position for/of entry (not for return value -1)
extern int
hashtab_get_pos (t_hashtab hashtab, size_t size, nipsvm_state_t *state,
nipsvm_state_t ***p_pos)
{
unsigned long entry, i;
t_hash hash, k;
// get hashtable entry
hash = hashtab_hash( (unsigned char *)state, size, 0 );
k = 1 + (hash % (hashtab->entries - 1)); // naive double hashing
entry = hash % hashtab->entries;
for( i = 0; i < hashtab->retries; i++ )
{
if( hashtab->bucket[entry].value == NULL )
{
hashtab->bucket[entry].hash_rest = hash / hashtab->entries;
*p_pos = &hashtab->bucket[entry].value; // return pointer to position
return 1; // can be inserted into hash table
}
else if( hashtab->bucket[entry].hash_rest != hash / hashtab->entries )
{
// no match, try next
}
else if( nipsvm_state_compare( hashtab->bucket[entry].value, state, size ) == 0 )
{
*p_pos = &hashtab->bucket[entry].value; // return pointer to position
return 0;
}
hashtab->conflict_count++;
hashtab->max_retry_count = max( hashtab->max_retry_count, i + 1 );
entry = (entry + k) % hashtab->entries; // considers indexes h, h + k, ..., h + i*k (mod entries)
} // for( i ...
// no more space ---> unresolvable hash conflict
*p_pos = NULL;
return -1;
}
// put a state into a hash table
// - only the pointer is stored
// - returns: 1 if state was added to hash table
// 0 if state was already in hash table
// -1 if state did not fit into hash table (hash conflict that could not be resolved)
extern int
hashtab_insert( t_hashtab hashtab, size_t size, nipsvm_state_t *state )
{
unsigned long ret_val;
t_hashtab_value* pos;
// get indices where to place entry
ret_val = hashtab_get_pos( hashtab, size, state, &pos );
// put pointer to state into hash table if it is a new one
if( ret_val > 0 )
*pos = state;
return ret_val;
}
// output statistical information about hashtable
void hashtab_print_statistics( FILE * stream, t_hashtab hashtab )
{
unsigned int i;
unsigned int entries = 0;
// count entries in hash table
for( i = 0; i < hashtab->entries; i++ )
if( hashtab->bucket[i].value != NULL )
entries++;
double fill_ratio = 100.0 * (double)entries / (double)hashtab->entries;
fprintf( stream, "hashtable statistics:\n"
" memory usage: %0.2fMB\n", hashtab->memory_size / 1048576.0 );
fprintf( stream, " buckets used/available: %u/%lu (%0.1f%%)\n", entries, hashtab->entries, fill_ratio );
fprintf( stream, " max. no. of retries: %u\n", hashtab->max_retry_count );
fprintf( stream, " conflicts (resolved): %lu\n", hashtab->conflict_count );
}
// output statistical information about hashtable
extern void
table_statistics (t_hashtab table, table_statistics_t *stats)
{
unsigned int i;
unsigned int n_entries = 0;
assert (table != NULL);
assert (stats != NULL);
// count entries in hash table
for( i = 0; i < table->entries; i++ ) {
if (table->bucket[i].value != NULL) { n_entries++; }
}
memset (stats, sizeof stats, 0);
stats->memory_size = table->memory_size;
stats->entries_used = n_entries;
stats->entries_available = table->entries;
stats->conflicts = table->conflict_count;
stats->max_retries = table->max_retry_count;
}

View file

@ -0,0 +1,70 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_hashtab
#define INC_hashtab
#include "nipsvm.h"
#ifdef __cplusplus
extern "C"
{
#endif
// hashtable pointer type
typedef struct t_hashtab_header *t_hashtab;
typedef struct table_statistics_t table_statistics_t;
struct table_statistics_t {
size_t memory_size;
unsigned long entries_used;
unsigned long entries_available;
unsigned long conflicts;
unsigned long max_retries;
};
// create a new hash table
extern t_hashtab hashtab_new( unsigned long entries, unsigned long retries );
// free a hash table
extern void hashtab_free( t_hashtab hashtab );
// get index for/of an entry in a hash table
// - returns: 1 if state can be inserted into hash table
// 0 if state is already in hash table
// -1 if state did not fit into hash table (hash conflict that could not be resolved)
// - fills *p_pos with pointer to position for/of entry (not for return value -1)
extern int
hashtab_get_pos (t_hashtab hashtab, size_t size, nipsvm_state_t *state, nipsvm_state_t ***p_pos);
// put a state into a hash table
// - only the pointer is stored
// - returns: 1 if state was added to hash table
// 0 if state was already in hash table
// -1 if state did not fit into hash table (hash conflict that could not be resolved)
extern int
hashtab_insert (t_hashtab hashtab, size_t size, nipsvm_state_t *state);
// retrieve statistical information about hashtable
extern void
table_statistics (t_hashtab hashtab, table_statistics_t *stats);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_hashtab

2151
iface/nips/nips_vm/instr.c Normal file

File diff suppressed because it is too large Load diff

233
iface/nips/nips_vm/instr.h Normal file
View file

@ -0,0 +1,233 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_instr
#define INC_instr
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include "bytecode.h"
#include "state.h"
#include "tools.h"
// return value of instruction callbacks
typedef enum t_ic_status
{
IC_CONTINUE, // continue (e.g. generate further successor states)
IC_STOP, // stop processing immediately and return
} et_ic_status;
// run time errors
typedef enum t_instr_err
{
IE_BYTECODE, // bytecode segmentation fault
IE_INVOP, // invalid opcode
IE_LOCAL, // invalid local memory access
IE_GLOBAL, // invalid global memory access
IE_STACKOV, // stack overflow
IE_STACKUN, // stack underflow
IE_DIV0, // division by zero
IE_OV, // overflow error (TRUNC instruction, must be enabled in instr.c)
IE_INDEX, // invalid array index
IE_ASSERT, // assertion violated
IE_INV_PROC_ID, // invalid process id
IE_NO_PROC, // no process with specified process id
IE_INV_CHAN_ID, // invalid channel id
IE_NO_CHAN, // no channel with specified channel id
IE_INV_CHAN_TYPE, // invalid channel type
IE_CHAN_OV, // channel overflow
IE_CHAN_UN, // channel underflow
IE_CHAN_EMPTY, // channel is empty, but message is needed here
IE_PROC_CNT, // too many processes
IE_CHAN_CNT, // too many channels
IE_PATH_CNT, // too many parallel execution paths
IE_INVIS_CNT, // too many invisible states
IE_SUCC_CNT, // too many possible successor states
IE_STATE_MEM, // out of temporary state memory
IE_NO_ACT_PROC, // no active process (this is most likely an internal error) (no pid or pc is passed to callback)
IE_EN_PROC_CNT, // too many enabled processes (no pid or pc is passed to callback)
IE_INVIS_MEM, // out of memory for invisible states
} et_instr_err;
// output of print instructions is stored in state buffer during instruction
// execution as linked list (pointing towards first output)
// an entry of the linked list of print instruction output
// - must be packed because of improper alignment in state buffer
typedef struct t_instr_output
{
struct t_instr_output *p_prev; // pointer to previous output or NULL
int8_t is_str; // if this is a string output or not (a value output)
union
{
struct // string output
{
uint16_t str; // number of string to print
} str;
struct // value output
{
uint8_t fmt; // format to print value in
t_val value; // value to print
} value;
} data;
} PACKED st_instr_output;
// output during successor state generation
typedef struct t_instr_succ_output
{
st_instr_output *p_out_sys1st; // output of system during 1st part of sync. comm.
st_instr_output *p_out_sys; // output of system
st_instr_output *p_out_monitor; // output of monitor
} st_instr_succ_output;
// types of callback functions
// report single single successor state to caller
// - memory pointed to by succ must not be modified or accessed outside of callback
#define INSTR_SUCC_CB_FLAG_SYNC 0x00000001 // sync. comm. took place
#define INSTR_SUCC_CB_FLAG_TIMEOUT 0x00000002 // timeout occured
#define INSTR_SUCC_CB_FLAG_SYS_BLOCK 0x00000004 // system is blocked
#define INSTR_SUCC_CB_FLAG_MONITOR_EXIST 0x00000010 // monitor process exists
#define INSTR_SUCC_CB_FLAG_MONITOR_EXEC 0x00000020 // monitor process executed
#define INSTR_SUCC_CB_FLAG_MONITOR_ACCEPT 0x00000040 // monitor process is in accepting state
#define INSTR_SUCC_CB_FLAG_MONITOR_TERM 0x00000080 // monitor process is terminated
typedef et_ic_status (*instr_succ_callback_t)( st_global_state_header *succ, // the successor state
uint8_t label1st, // label of STEP command of 1st part of sync. comm. (unused if _SYNC flag is not set)
uint8_t label, // label of STEP command (of 2nd part of sync. comm. if _SYNC flag is set)
t_flag_reg flag_reg_1st, // flag register value returned by 1st part of sync. comm. (unused if _SYNC flag is not set)
t_flag_reg flag_reg, // flag register value returned (by 2nd part of sync. comm. if _SYNC flag is set)
unsigned int succ_cb_flags, // flags with boolean information
st_instr_succ_output *p_succ_out, // output during successor state generation
void *priv_context );
// report run time error to caller
typedef et_ic_status (*instr_err_callback_t)( et_instr_err err, // error code
t_pid pid, // pid of process causing error
t_pc pc, // program counter when error occured (might point into middle of instruction)
void *priv_context );
// context for successor state generation
typedef struct t_instr_succ_context
{
// maximum values (i.e. buffer sizes) used during successor state generation
uint8_t stack_max; // number of entries in the stack during execution of a step
// type is uint8_t because stack pointer is an uint8_t
// error IE_STACK_OV will occur if too small
unsigned int state_mem; // size of memory for temporary states within a step
// error IE_STATE_MEM will occur if too small
unsigned int enab_state_mem; // size of memory for temporary states within a step in ENAB instruction
// this can be smaller as above because execution stops after first state
// error IE_STATE_MEM will occur if too small
unsigned int invis_mem; // size of memory for invisible states stored for further processing
// error IE_INVIS_MEM will occur if too small
unsigned int path_max; // maximum number of states on stack during execution of a step
// maximum number of possible nondeterministic paths within a step
// e.g. maximum number of options in Promela's "do" statement
// error IE_PATH_CNT will happen if too small
unsigned int invis_max; // maximum number of invisible states that can be stored
// error IE_INVIS_CNT will happen if too small
unsigned int succ_max; // maximum number of possible successor states during execution of a step
// maximum number of executable nondeterministic paths within a step
// e.g. maximum number of executable options in Promela's "do" statement
// error IE_SUCC_CNT will happen if too small
// bytecode to execute
st_bytecode *bytecode;
// pointers to callback functions
instr_succ_callback_t succ_cb;
instr_err_callback_t err_cb;
// private context of caller
// - will be passed to every callback function being called
void *priv_context;
} st_instr_succ_context;
// context for execution of an instruction
#define INSTR_CONTEXT_MODE_INVALID 0 // context not initialized
#define INSTR_CONTEXT_MODE_ACTIVE 1 // a process is active, p_glob, p_gvar, gvar_sz, p_proc, p_lvar, lvar_sz, p_stack are valid
#define INSTR_CONTEXT_MODE_COMPLETED 2 // a step has been completed, p_glob is valid, invisible, label and flag_reg are set
#define INSTR_CONTEXT_MODE_STOP 3 // error callback requested stop - context not initialized
typedef struct t_instr_context_state
{
st_global_state_header *p_glob; // pointer to global state
st_instr_output *p_output; // pointer to last output list entry or NULL if none
unsigned int max_step_cnt; // maximum step_cnt in context
// - if step_cnt in context is greater than this, ignore this state
// - used in ELSE and UNLESS to ignore second path if some step has already been completed
} st_instr_context_state;
typedef struct t_instr_context
{
// current state
int mode; // current mode of context
int8_t invisible; // if the completed step is invisible
uint8_t label; // label of completed step
t_flag_reg flag_reg; // the flag register value before the step was completed
st_global_state_header *p_glob; // the global state the instruction shall be executed on
char *p_gvar; // the global variables within *p_glob
t_val gvar_sz; // the size of the global variables *p_gvar already converted to t_val
st_process_active_header *p_proc; // the active process within *p_glob
char *p_lvar; // the local variables within *p_proc
t_val lvar_sz; // the size of the local variables *p_lvar already converted to t_val
ua_t_val *p_stack; // the stack of the active process *p_proc
st_instr_output *p_output; // pointer to last output list entry or NULL if none
// additional states to process (must always be valid)
st_instr_context_state *p_states; // pointer to array with additonal states (stack)
unsigned int state_cnt_max; // maximum number of entries that fit into array
unsigned int state_cnt; // number of entries currently in array
// buffer with memory for new temporary states (must always be valid)
char **pp_buf;
unsigned long *p_buf_len;
// counter of completed steps
unsigned int step_cnt;
// other stuff
t_flag_reg init_flag_reg; // initial value of flag register (needed for ENAB)
int timeout; // boolean flag - Promela's timeout variable
t_pid last_pid; // pid of last process that executed a step (or 0 if not available)
st_instr_succ_context *p_succ_ctx; // context for successor state generation
} st_instr_context;
// type for a temporary successor
typedef struct t_instr_tmp_succ
{
st_global_state_header *p_glob; // the pointer to the state
int8_t invisible; // if the completed step is invisible
int8_t sync_comm; // if synchronous communication is taking place in the completed step
uint8_t label; // the label the step was completed with
t_flag_reg flag_reg; // value of flag register at end of step
st_instr_output *p_output; // output list at end of step
} st_instr_tmp_succ;
// execute instructions in executions paths
// - must be called with an active state
// - stops when all paths have reached inactive state
extern et_ic_status instr_exec_paths( st_instr_context *p_ctx,
int stop_on_first, // boolean flag if to stop when first temp. state is found
st_instr_tmp_succ *p_tmp_succs, // temp. states are returned here
unsigned int tmp_succ_cnt_max, // max. number of temp. states
unsigned int *p_tmp_succ_cnt // number of temp. states is returned here
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_instr

View file

@ -0,0 +1,674 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "bytecode.h"
#include "instr.h"
#include "instr_step.h"
#include "state.h"
#include "tools.h"
// #define DEBUG_INVIS // print invisible states
// default maximum values (i.e. buffer sizes) for successor state generation
#define INSTR_DEF_STACK_MAX 64 // number of entries in the stack during execution of a step
// type is uint8_t because stack pointer is an uint8_t
#ifdef SMALLSTACK // (reduce sizes for windows, because default stack size is only 1MB)
# warning Building with reduced stack size demands.
# define INSTR_DEF_STATE_MEM 65536 // size of memory for temporary states within a step
# define INSTR_DEF_ENAB_STATE_MEM 8192 // size of memory for temporary states within a step in ENAB instruction
# define INSTR_DEF_INVIS_MEM 8192 // size of memory to store invisible states for further execution
#else
# define INSTR_DEF_STATE_MEM 262144 // size of memory for temporary states within a step
# define INSTR_DEF_ENAB_STATE_MEM 32768 // size of memory for temporary states within a step in ENAB instruction
# define INSTR_DEF_INVIS_MEM 32768 // size of memory to store invisible states for further execution
#endif
#define INSTR_DEF_PATH_MAX 256 // maximum number of states on stack during execution of a step
// maximum number of possible nondeterministic paths within a step
#define INSTR_DEF_INVIS_MAX 256 // maximum number of invisible states that can be stored
#define INSTR_DEF_SUCC_MAX 256 // maximum number of possible successor states during execution of a step
// maximum number of executable nondeterministic paths within a step
// type for numbers of successor states (of system and total)
typedef struct t_instr_succ_cnts
{
unsigned int sys; // successor count of system
unsigned int total; // total successor cnt
} st_instr_succ_cnts;
// type to save parameters of 1st part while executing 2nd part of sync. comm.
typedef struct t_instr_prm_save_sync
{
uint8_t label; // label returned by 1st part
t_flag_reg flag_reg; // flag register value returned by 1st part
st_instr_output *p_output; // output done by 1st part
} st_instr_prm_save_sync;
// type to save parameters of system while executing monitor process
typedef struct t_instr_prm_save_monitor
{
st_instr_prm_save_sync * p_prm_1st; // saved settings from 1st part of sync. comm.
uint8_t label; // label returned by step of normal system
t_flag_reg flag_reg; // flag register value returned by normal system
st_instr_output *p_output; // output done by normal system
} st_instr_prm_save_monitor;
// type for internal successor callback function
typedef et_ic_status (*instr_int_succ_cb_t)( st_instr_tmp_succ * p_succ, // successor state (with label, flag_reg, ...)
t_pid pid, // pid of process that executed
t_flag_reg flag_reg, // initial value for flag register of process
int timeout, // boolean flag if timeout
unsigned int succ_cb_flags, // flags accumulated so far
st_instr_succ_context *p_succ_ctx, // context for successor state generation
st_instr_succ_cnts *p_succ_cnts, // successor state statistics
void *p_int_ctx ); // private context
// execute a step in a process
// - callbacks in context are called for every event (e.g. errors)
// - returns instruction callback status
static et_ic_status instr_proc_step( st_global_state_header *p_glob, // state to start with
st_process_header *p_proc, // process to execute
t_flag_reg flag_reg, // initial value for flag register of process
int timeout, // boolean flag if timeout
t_pid monitor_last, // pid of process that did last step (0 if not executing monitor)
unsigned int succ_cb_flags, // flags already accumulated for successor callback
instr_int_succ_cb_t int_succ_cb, // internal successor callback function
void *p_int_ctx, // private context for int_succ_cb
char **pp_invis_buf, unsigned long *p_invis_buf_len, // buffer for invisible states
st_instr_tmp_succ **pp_invis_tmp_succ, unsigned int *p_invis_tmp_succ_cnt, // array for invisible states
st_instr_succ_context *p_succ_ctx, st_instr_succ_cnts *p_succ_cnts,
unsigned int *p_state_cnt ) // number of resulting states
{
// buffer for temporary states
char tmp_buf[p_succ_ctx->state_mem];
char *p_tmp_buf = tmp_buf;
unsigned long tmp_buf_len = sizeof( tmp_buf );
// activate process
st_process_active_header *p_proc_act;
st_global_state_header *p_glob_act = global_state_copy_activate( p_glob, p_proc,
p_succ_ctx->stack_max, flag_reg,
&p_tmp_buf, &tmp_buf_len,
&p_proc_act );
if( p_glob_act == NULL )
return p_succ_ctx->err_cb( IE_STATE_MEM, be2h_pid( p_proc->pid ), be2h_pc( p_proc->pc ),
p_succ_ctx->priv_context );
// reset monitor accept flag in process header
p_proc_act->proc.flags &= ~PROCESS_FLAGS_MONITOR_ACCEPT;
// set up context to execute instructions
st_instr_context ctx;
st_instr_context_state states[p_succ_ctx->path_max]; // create stack of states to process
states[0].p_glob = p_glob_act;
states[0].p_output = NULL;
states[0].max_step_cnt = (unsigned int)-1; // process this state in any case
ctx.p_states = states;
ctx.state_cnt_max = count( states );
ctx.state_cnt = 1;
ctx.step_cnt = 0; // no step completed yet
ctx.pp_buf = &p_tmp_buf; // fill in pointers for buffer with memory for new temporary states
ctx.p_buf_len = &tmp_buf_len;
ctx.init_flag_reg = flag_reg;
ctx.timeout = timeout;
ctx.last_pid = monitor_last;
ctx.p_succ_ctx = p_succ_ctx;
// execute instructions until all paths are done
st_instr_tmp_succ tmp_succs[p_succ_ctx->succ_max];
unsigned int tmp_succ_cnt = 0;
if( instr_exec_paths( &ctx, 0, // do not stop on first state found
tmp_succs, count( tmp_succs ), &tmp_succ_cnt ) == IC_STOP )
return IC_STOP;
// process all completed states
unsigned int i;
for( i = 0; i < tmp_succ_cnt; i++ )
{
(*p_state_cnt)++;
// invisible state
if( tmp_succs[i].invisible && // state marked as invisible
! tmp_succs[i].sync_comm && // no synchronous communication in progress (sync. comm. forces visibility)
tmp_succs[i].p_output == NULL ) // no output done (outout forces visibility)
{
// put invisible state into buffer for invisible states
st_global_state_header * p_glb = global_state_copy( tmp_succs[i].p_glob, // copy state
pp_invis_buf, p_invis_buf_len );
if( p_glb == NULL )
return p_succ_ctx->err_cb( IE_INVIS_MEM, be2h_pid( p_proc->pid ), be2h_pc( p_proc->pc ),
p_succ_ctx->priv_context );
if( *p_invis_tmp_succ_cnt == 0 ) // put copy of state into array
return p_succ_ctx->err_cb( IE_INVIS_CNT, be2h_pid( p_proc->pid ), be2h_pc( p_proc->pc ),
p_succ_ctx->priv_context );
**pp_invis_tmp_succ = tmp_succs[i];
(**pp_invis_tmp_succ).p_glob = p_glb;
(*pp_invis_tmp_succ)++;
(*p_invis_tmp_succ_cnt)--;
}
// visible state
else
{
// report state to caller
if( int_succ_cb( &tmp_succs[i], // successor state
be2h_pid( p_proc->pid ), // pid of process that executed
flag_reg, // initial value for flag register of process
timeout, // boolean flag if timeout
succ_cb_flags, // flags accumulated so far
p_succ_ctx, p_succ_cnts,
p_int_ctx ) == IC_STOP )
return IC_STOP;
}
} // for( i ...
return IC_CONTINUE;
}
// execute steps in a process
// - callbacks in context are called for every event (e.g. errors)
// - continues execution for invisible states are reached
// - returns instruction callback status
static et_ic_status instr_proc_steps( st_global_state_header *p_glob, // state to start with
st_process_header *p_proc, // process to execute
t_flag_reg flag_reg, // initial value for flag register of process
int timeout, // boolean flag if timeout
t_pid monitor_last, // pid of process that did last step (0 if not executing monitor)
unsigned int succ_cb_flags, // flags already accumulated for successor callback
instr_int_succ_cb_t int_succ_cb, // internal successor callback function
void *p_int_ctx, // private context for int_succ_cb
st_instr_succ_context *p_succ_ctx, st_instr_succ_cnts *p_succ_cnts )
{
// buffers for invisible states
char invis_bufs[2][p_succ_ctx->invis_mem];
st_instr_tmp_succ invis_tmp_succs[2][p_succ_ctx->invis_max];
int invis_buf_idx = 0; // active buffer
// save pid of process to execute
t_pid exec_pid = be2h_pid( p_proc->pid );
// initialize buffer for invisible states
char *p_invis_buf = invis_bufs[invis_buf_idx];
unsigned long invis_buf_len = sizeof( invis_bufs[invis_buf_idx] );
st_instr_tmp_succ *p_invis_tmp_succs = invis_tmp_succs[invis_buf_idx];
unsigned int invis_tmp_succ_cnt = count( invis_tmp_succs[invis_buf_idx] );
// process initial state
unsigned int state_cnt = 0;
if( instr_proc_step( p_glob, p_proc,
flag_reg, timeout, monitor_last,
succ_cb_flags, int_succ_cb, p_int_ctx,
&p_invis_buf, &invis_buf_len,
&p_invis_tmp_succs, &invis_tmp_succ_cnt,
p_succ_ctx, p_succ_cnts, &state_cnt ) == IC_STOP )
return IC_STOP;
// as long as there are states in buffer
for( ; ; )
{
// get pointer to and number of states in buffer
st_instr_tmp_succ *p_tmp_succs = invis_tmp_succs[invis_buf_idx];
unsigned int tmp_succ_cnt = count( invis_tmp_succs[invis_buf_idx] ) - invis_tmp_succ_cnt;
// no more states ---> exit loop
if( tmp_succ_cnt == 0 )
break;
// initialize buffer for new invisible states to use other buffer
p_invis_buf = invis_bufs[1 - invis_buf_idx];
invis_buf_len = sizeof( invis_bufs[1 - invis_buf_idx] );
p_invis_tmp_succs = invis_tmp_succs[1 - invis_buf_idx];
invis_tmp_succ_cnt = count( invis_tmp_succs[1 - invis_buf_idx] );
// process all states in buffer
for( ; tmp_succ_cnt > 0 ; tmp_succ_cnt--, p_tmp_succs++ )
{
#ifdef DEBUG_INVIS
printf( "DEBUG (invisible state): " );
global_state_print( p_tmp_succs->p_glob );
#endif
// get process to execute
st_process_header *p_prc = global_state_get_process( p_tmp_succs->p_glob, exec_pid );
if( p_prc != NULL )
{
unsigned int state_cnt = 0;
// if this process became the monitor or ceased to be the monitor
// while executing an invisible step
// then the next step is not executable according to the formal model
// so force this behaviour in this implementation
if( (p_glob->monitor_pid == p_proc->pid) == (p_tmp_succs->p_glob->monitor_pid == p_prc->pid) )
{
// process state
if( instr_proc_step( p_tmp_succs->p_glob, p_prc,
flag_reg, timeout, monitor_last,
succ_cb_flags, int_succ_cb, p_int_ctx,
&p_invis_buf, &invis_buf_len,
&p_invis_tmp_succs, &invis_tmp_succ_cnt,
p_succ_ctx, p_succ_cnts, &state_cnt ) == IC_STOP )
return IC_STOP;
}
// no resulting states -> report original state to caller
if( state_cnt == 0 )
{
if( int_succ_cb( p_tmp_succs, // successor state
exec_pid, // pid of process that executed
flag_reg, // initial value for flag register of process
timeout, // boolean flag if timeout
succ_cb_flags, // flags accumulated so far
p_succ_ctx, p_succ_cnts,
p_int_ctx ) == IC_STOP )
return IC_STOP;
}
}
} // for( ; tmp_succ_cnt > 0; ...
// swap buffers
invis_buf_idx = 1 - invis_buf_idx;
} // for( ; ; )
return IC_CONTINUE;
}
// execute a system step (and monitor step afterwards)
// - callbacks in context are called for every event (e.g. a successor state)
// - returns instruction callback status
static et_ic_status instr_sys_step( st_global_state_header *p_glob, // state to start with
t_flag_reg flag_reg, // initial value for flag register
unsigned int succ_cb_flags, // flags already accumulated for successor callback
instr_int_succ_cb_t int_succ_cb, // internal successor callback function
void *p_int_ctx, // private context for int_succ_cb
st_instr_succ_context *p_succ_ctx, st_instr_succ_cnts *p_succ_cnts )
{
// get enabled processes
st_process_header *procs[PID_MAX]; // there cannot be more than PID_MAX processes
unsigned int proc_cnt = global_state_get_enabled_processes( p_glob, procs, count( procs ) );
if( proc_cnt == (unsigned int)-1 ) // too many enabled processes
return p_succ_ctx->err_cb( IE_EN_PROC_CNT, 0, 0, p_succ_ctx->priv_context );
// execute a step in every enabled process / in the process running exclusively
unsigned int sys_succ_cnt_start = p_succ_cnts->sys;
int timeout;
for( timeout = 0; timeout <= 1; timeout++ ) // first try timeout = 0, then try timeout = 1
{
unsigned int i;
// a process is running exclusively (and it is not the monitor)
if( p_glob->excl_pid != h2be_pid( 0 ) && p_glob->excl_pid != p_glob->monitor_pid )
{
for( i = 0; i < proc_cnt; i++ ) // search this process
if( procs[i]->pid == p_glob->excl_pid )
break;
if( i < proc_cnt ) // found this process
{
if( instr_proc_steps( p_glob, procs[i],
flag_reg, timeout,
0, // executing normal system (not monitor)
succ_cb_flags, int_succ_cb, p_int_ctx,
p_succ_ctx, p_succ_cnts ) == IC_STOP ) // execute this process
return IC_STOP; // some callback requested stop
if( p_succ_cnts->sys > sys_succ_cnt_start ) // process can do a step
return IC_CONTINUE; // ignore other processes
}
}
// execute every enabled process (except monitor)
for( i = 0; i < proc_cnt; i++ )
if( procs[i]->pid != p_glob->monitor_pid )
if( instr_proc_steps( p_glob, procs[i],
flag_reg, timeout,
0, // executing normal system (not monitor)
succ_cb_flags, int_succ_cb, p_int_ctx,
p_succ_ctx, p_succ_cnts ) == IC_STOP )
return IC_STOP; // some callback requested stop
if( p_succ_cnts->sys > sys_succ_cnt_start ) // at least one successor state
return IC_CONTINUE; // do not try again with timeout set
// set timeout flag
succ_cb_flags |= INSTR_SUCC_CB_FLAG_TIMEOUT;
}
return IC_CONTINUE;
}
// execute a monitor step
// - callbacks in context are called for every event (e.g. a successor state)
// - returns instruction callback status
static et_ic_status instr_monitor_step( st_global_state_header *p_glob, // state to start with
uint8_t last_pid, // pid of process that executed last step
unsigned int succ_cb_flags, // flags already accumulated for successor callback
instr_int_succ_cb_t int_succ_cb, // internal successor callback function
void *p_int_ctx, // private context for int_succ_cb
st_instr_succ_context *p_succ_ctx, st_instr_succ_cnts *p_succ_cnts )
{
// get monitor process if available
t_pid monitor_pid = be2h_pid( p_glob->monitor_pid );
st_process_header *p_monitor = NULL;
if( monitor_pid != 0 )
p_monitor = global_state_get_process( p_glob, monitor_pid );
// monitor process is not available or terminated
if( p_monitor == NULL ||
(p_monitor->flags & PROCESS_FLAGS_MODE) == PROCESS_FLAGS_MODE_TERMINATED )
return IC_CONTINUE;
// monitor process exists
succ_cb_flags |= INSTR_SUCC_CB_FLAG_MONITOR_EXIST;
// execute a step in the monitor process
unsigned int succ_cnt_start = p_succ_cnts->total;
int timeout;
for( timeout = 0; timeout <= 1; timeout++ ) // first try timeout = 0, then try timeout = 1
{
// execute monitor process
if( instr_proc_steps( p_glob, p_monitor,
0, timeout, // monitor starts always with flag_reg = 0
last_pid, // executing monitor
succ_cb_flags, int_succ_cb, p_int_ctx,
p_succ_ctx, p_succ_cnts ) == IC_STOP )
return IC_STOP; // some callback requested stop
if( p_succ_cnts->total > succ_cnt_start ) // at least one successor state
return IC_CONTINUE; // do not try again with timeout set
}
// when we get here, there was no successor state with timeout = 0 and none with timeout = 1
// ---> monitor process is blocked
// ---> no successor state
return IC_CONTINUE;
}
// internal successor callback function for monitor successor states
et_ic_status instr_monitor_succ( st_instr_tmp_succ * p_succ, // successor state (with label, flag_reg, ...)
t_pid pid, // pid of process that executed
t_flag_reg flag_reg, // initial value for flag register of process
int timeout, // boolean flag if timeout
unsigned int succ_cb_flags, // flags accumulated so far
st_instr_succ_context *p_succ_ctx, // context for successor state generation
st_instr_succ_cnts *p_succ_cnts, // successor state statistics
void *vp_prm_sys ) // private context: saved settings of system
{
st_instr_prm_save_monitor *p_prm_sys = (st_instr_prm_save_monitor *)vp_prm_sys;
// synchronous communication is still going on
if( p_succ->sync_comm )
// synchronous communication in monitor is not allowed -> invalid state
return IC_CONTINUE;
// monitor executed a step
succ_cb_flags |= INSTR_SUCC_CB_FLAG_MONITOR_EXEC;
// set flags if monitor is in accepting state
st_process_header *p_monitor = global_state_get_process( p_succ->p_glob, pid );
if( p_monitor != NULL &&
bytecode_flags( p_succ_ctx->bytecode, be2h_pc( p_monitor->pc ) ) & BC_FLAG_ACCEPT )
{
p_monitor->flags |= PROCESS_FLAGS_MONITOR_ACCEPT; // set flag in process header of monitor process
succ_cb_flags |= INSTR_SUCC_CB_FLAG_MONITOR_ACCEPT; // set flag for successor callback
}
// set flag if monitor is terminated
if( p_monitor == NULL ||
(p_monitor->flags & PROCESS_FLAGS_MODE) == PROCESS_FLAGS_MODE_TERMINATED )
succ_cb_flags |= INSTR_SUCC_CB_FLAG_MONITOR_TERM; // set flag for successor callback
// count state and report it to user
p_succ_cnts->total++;
st_instr_succ_output succ_out = // assemble structure with output
{
.p_out_sys1st = p_prm_sys->p_prm_1st->p_output, // output of system during 1st part of sync. comm.
.p_out_sys = p_prm_sys->p_output, // output of system
.p_out_monitor = p_succ->p_output // output of monitor
};
return p_succ_ctx->succ_cb( p_succ->p_glob, // report resulting state
p_prm_sys->p_prm_1st->label, p_prm_sys->label, // pass on saved settings of system
p_prm_sys->p_prm_1st->flag_reg, p_prm_sys->flag_reg,
succ_cb_flags, // pass on calulated flags
&succ_out,
p_succ_ctx->priv_context );
// keep compiler happy
flag_reg = 0;
timeout = 0;
}
// execute monitor on system successor state
static et_ic_status instr_exec_monitor( st_instr_tmp_succ *p_sys_succ, // successor state of system (with label, flag_reg, ...)
st_instr_prm_save_sync *p_prm_1st, // saved parameters of 1st part of sync. comm. of system
uint8_t last_pid, // pid of process that executed last step
unsigned int succ_cb_flags, // flags already accumulated for successor callback
st_instr_succ_context *p_succ_ctx, st_instr_succ_cnts *p_succ_cnts )
{
// count system's successor state
// - monitor is executed on every successor state of system
// - so system's successor states can be counted here
p_succ_cnts->sys++;
// save parameters returned by normal system
st_instr_prm_save_monitor prm_sys =
{
.p_prm_1st = p_prm_1st, // saved settings from 1st part of sync. comm.
.label = p_sys_succ->label, // label returned by normal system
.flag_reg = p_sys_succ->flag_reg, // flag register value returned by normal system
.p_output = p_sys_succ->p_output, // output done by normal system
};
// there is a monitor process (or at least a pid of some former monitor process)
if( p_sys_succ->p_glob->monitor_pid != h2be_pid( 0 ) )
{
// execute a monitor step
if( instr_monitor_step( p_sys_succ->p_glob, last_pid, succ_cb_flags,
instr_monitor_succ, (void *)&prm_sys, // monitor callback with saved settings of system as context
p_succ_ctx, p_succ_cnts ) == IC_STOP )
return IC_STOP; // some callback requested stop
return IC_CONTINUE;
}
// when we get here, there is no monitor process
// count original system successor state and report it to user
p_succ_cnts->total++;
st_instr_succ_output succ_out = // assemble structure with output
{
.p_out_sys1st = p_prm_1st->p_output, // output of system during 1st part of sync. comm.
.p_out_sys = p_sys_succ->p_output, // output of system
.p_out_monitor = NULL // no output of monitor
};
return p_succ_ctx->succ_cb( p_sys_succ->p_glob, // report system state
p_prm_1st->label, p_sys_succ->label, // pass on settings of system
p_prm_1st->flag_reg, p_sys_succ->flag_reg,
succ_cb_flags, // pass on flags of system
&succ_out,
p_succ_ctx->priv_context );
}
// internal successor callback function for 2nd part of synchronous communication
et_ic_status instr_sync_succ( st_instr_tmp_succ * p_succ, // successor state (with label, flag_reg, ...)
t_pid pid, // pid of process that executed
t_flag_reg flag_reg, // initial value for flag register of process
int timeout, // boolean flag if timeout
unsigned int succ_cb_flags, // flags accumulated so far
st_instr_succ_context *p_succ_ctx, // context for successor state generation
st_instr_succ_cnts *p_succ_cnts, // successor state statistics
void *vp_prm_1st ) // private context: saved settings of 1st part of sync. comm.
{
st_instr_prm_save_sync *p_prm_1st = (st_instr_prm_save_sync *)vp_prm_1st;
// synchronous communication is still going on
if( p_succ->sync_comm )
// this is not a valid state
return IC_CONTINUE;
// execute monitor
return instr_exec_monitor( p_succ, // start from system successor state
p_prm_1st, // with saved setting from 1st part of sync. comm.
pid, // tell monitor process which process did the last step
succ_cb_flags, // pass on flags
p_succ_ctx, p_succ_cnts );
// keep compiler happy
flag_reg = 0;
timeout = 0;
}
// internal successor callback function for system successor states
et_ic_status instr_sys_succ( st_instr_tmp_succ * p_succ, // successor state (with label, flag_reg, ...)
t_pid pid, // pid of process that executed
t_flag_reg flag_reg, // initial value for flag register of process
int timeout, // boolean flag if timeout
unsigned int succ_cb_flags, // flags accumulated so far
st_instr_succ_context *p_succ_ctx, // context for successor state generation
st_instr_succ_cnts *p_succ_cnts, // successor state statistics
void *p_unused ) // private context
{
// no synchronous communication is going on
if( ! p_succ->sync_comm )
{
// create some dummy parameters to pass on
st_instr_prm_save_sync dummy_1st = { .label = 0, .flag_reg = 0, .p_output = NULL };
// execute monitor
return instr_exec_monitor( p_succ, // start from system successor state
&dummy_1st, // with saved setting from 1st part of sync. comm.
pid, // tell monitor process which process did the last step
succ_cb_flags, // pass on flags
p_succ_ctx, p_succ_cnts );
}
// save parameters of 1st part for 2nd part
st_instr_prm_save_sync prm_1st =
{
.label = p_succ->label, // label returned by 1st part
.flag_reg = p_succ->flag_reg, // flag register value returned by 1st part
.p_output = p_succ->p_output, // output done by 1st part
};
// get enabled processes
st_process_header *procs[PID_MAX]; // there cannot be more than PID_MAX processes
unsigned int proc_cnt = global_state_get_enabled_processes( p_succ->p_glob, procs, count( procs ) );
if( proc_cnt == (unsigned int)-1 ) // too many enabled processes
return p_succ_ctx->err_cb( IE_EN_PROC_CNT, 0, 0, p_succ_ctx->priv_context );
// let possible receivers receive message synchronously
unsigned int i;
for( i = 0; i < proc_cnt; i++ ) // execute every enabled process
if( procs[i]->pid != p_succ->p_glob->monitor_pid && // except monitor and sender
procs[i]->pid != h2be_pid( pid ) )
if( instr_proc_steps( p_succ->p_glob, procs[i],
flag_reg, timeout, // start with same flag_reg value and timeout setting as 1st part
0, // executing normal system (not monitor)
succ_cb_flags | INSTR_SUCC_CB_FLAG_SYNC, // add flag for synchronous communication
instr_sync_succ, (void *)&prm_1st, // sync. comm. callback with saved settings of 1st part as context
p_succ_ctx, p_succ_cnts ) == IC_STOP )
return IC_STOP; // some callback requested stop
return IC_CONTINUE;
// keep compiler happy
p_unused = NULL;
}
// *** functions visible from outside ***
// initialize context with default maximum values
// only fills in maximum values and bytecode
// does not touch pointers to callback functions
void instr_succ_context_default( st_instr_succ_context *succ_ctx, st_bytecode *bytecode ) // extern
{
succ_ctx->stack_max = INSTR_DEF_STACK_MAX;
succ_ctx->state_mem = INSTR_DEF_STATE_MEM;
succ_ctx->enab_state_mem = INSTR_DEF_ENAB_STATE_MEM;
succ_ctx->invis_mem = INSTR_DEF_INVIS_MEM;
succ_ctx->path_max = INSTR_DEF_PATH_MAX;
succ_ctx->invis_max = INSTR_DEF_INVIS_MAX;
succ_ctx->succ_max = INSTR_DEF_SUCC_MAX;
succ_ctx->bytecode = bytecode;
}
// generate successor states
// callbacks in context are called for every event (e.g. a successor state)
// returns number of successor states that were reported to successor callback
unsigned int instr_succ( st_global_state_header *p_glob, // state to start with
t_flag_reg flag_reg, // value to put into flag register
st_instr_succ_context *succ_ctx ) // extern
{
// get monitor process in original state
t_pid monitor_pid = be2h_pid( p_glob->monitor_pid );
st_process_header *p_monitor = NULL;
// there is a monitor process (or at least a pid of some former monitor process)
if( monitor_pid != 0 )
{
// get monitor process
p_monitor = global_state_get_process( p_glob, monitor_pid );
// monitor not available or terminated
if( p_monitor == NULL ||
(p_monitor->flags & PROCESS_FLAGS_MODE) == PROCESS_FLAGS_MODE_TERMINATED )
{
// report original state as successor state to user
st_instr_succ_output succ_out = // structure with output: none
{
.p_out_sys1st = NULL, // output of system during 1st part of sync. comm.
.p_out_sys = NULL, // output of system
.p_out_monitor = NULL // output of monitor
};
succ_ctx->succ_cb( p_glob, // report original state
0, 0, 0, 0,
INSTR_SUCC_CB_FLAG_MONITOR_EXIST | INSTR_SUCC_CB_FLAG_MONITOR_TERM,
&succ_out,
succ_ctx->priv_context );
return 1;
}
}
// execute a step in system (and monitor step afterwards)
st_instr_succ_cnts succ_cnts = { .sys = 0, .total = 0 };
instr_sys_step( p_glob, flag_reg,
0, // no successor callback flags yet
instr_sys_succ, NULL, // system successor callback and context
succ_ctx, &succ_cnts );
// done if at least one successor state of system
if( succ_cnts.sys > 0 )
return succ_cnts.total;
// when we get here, the system is blocked
// extension of finite paths only if monitor is present.
if (monitor_pid == 0)
return 0;
// report original state as successor state to user
st_instr_succ_output succ_out = // structure with output: none
{
.p_out_sys1st = NULL, // output of system during 1st part of sync. comm.
.p_out_sys = NULL, // output of system
.p_out_monitor = NULL // output of monitor
};
succ_ctx->succ_cb( p_glob, // report original state
0, 0, 0, 0,
INSTR_SUCC_CB_FLAG_SYS_BLOCK |
(p_monitor != NULL ? INSTR_SUCC_CB_FLAG_MONITOR_EXIST : 0) |
(p_monitor != NULL && (p_monitor->flags & PROCESS_FLAGS_MONITOR_ACCEPT) ? INSTR_SUCC_CB_FLAG_MONITOR_ACCEPT : 0),
&succ_out,
succ_ctx->priv_context );
return 1;
}

View file

@ -0,0 +1,44 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_instr_step
#define INC_instr_step
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include "bytecode.h"
#include "instr.h"
#include "state.h"
// initialize context with default maximum values
// only fills in maximum values and bytecode
// does not touch pointers to callback functions
extern void instr_succ_context_default( st_instr_succ_context *succ_ctx, st_bytecode *bytecode );
// generate successor states
// callbacks in context are called for every event (e.g. a successor state)
// returns number of successor states that were reported to successor callback
extern unsigned int instr_succ( st_global_state_header *p_glob, // state to start with
t_flag_reg flag_reg, // value to put into flag register
st_instr_succ_context *succ_ctx );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_instr_step

View file

@ -0,0 +1,168 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "bytecode.h"
#include "instr.h"
#include "instr_tools.h"
#include "state.h"
#include "nipsvm.h"
// convert runtime error to string
int
nipsvm_errorstring( char *str, size_t size, nipsvm_errorcode_t err,
nipsvm_pid_t pid, nipsvm_pc_t pc ) // extern
{
switch( err )
{
case IE_BYTECODE: return snprintf( str, size, "bytecode segmentation fault (pid=%d, pc=0x%08X)", pid, pc );
case IE_INVOP: return snprintf( str, size, "invalid opcode (pid=%d, pc=0x%08X)", pid, pc );
case IE_GLOBAL: return snprintf( str, size, "invalid global memory access (pid=%d, pc=0x%08X)", pid, pc );
case IE_LOCAL: return snprintf( str, size, "invalid local memory access (pid=%d, pc=0x%08X)", pid, pc );
case IE_STACKOV: return snprintf( str, size, "stack overflow (pid=%d, pc=0x%08X)", pid, pc );
case IE_STACKUN: return snprintf( str, size, "stack underflow (pid=%d, pc=0x%08X)", pid, pc );
case IE_DIV0: return snprintf( str, size, "division by zero (pid=%d, pc=0x%08X)", pid, pc );
case IE_OV: return snprintf( str, size, "overflow error (pid=%d, pc=0x%08X)", pid, pc );
case IE_INDEX: return snprintf( str, size, "invalid array index (pid=%d, pc=0x%08X)", pid, pc );
case IE_ASSERT: return snprintf( str, size, "assertion violated (pid=%d, pc=0x%08X)", pid, pc );
case IE_INV_PROC_ID: return snprintf( str, size, "invalid process id (pid=%d, pc=0x%08X)", pid, pc );
case IE_NO_PROC: return snprintf( str, size, "no process with specified process id (pid=%d, pc=0x%08X)", pid, pc );
case IE_INV_CHAN_ID: return snprintf( str, size, "invalid channel id (pid=%d, pc=0x%08X)", pid, pc );
case IE_NO_CHAN: return snprintf( str, size, "no channel with specified channel id (pid=%d, pc=0x%08X)", pid, pc );
case IE_INV_CHAN_TYPE: return snprintf( str, size, "invalid channel type (pid=%d, pc=0x%08X)", pid, pc );
case IE_CHAN_OV: return snprintf( str, size, "channel overflow (pid=%d, pc=0x%08X)", pid, pc );
case IE_CHAN_UN: return snprintf( str, size, "channel underflow (pid=%d, pc=0x%08X)", pid, pc );
case IE_CHAN_EMPTY: return snprintf( str, size, "channel is empty, but message is needed here (pid=%d, pc=0x%08X)", pid, pc );
case IE_PROC_CNT: return snprintf( str, size, "too many processes (pid=%d, pc=0x%08X)", pid, pc );
case IE_CHAN_CNT: return snprintf( str, size, "too many channels (pid=%d, pc=0x%08X)", pid, pc );
case IE_PATH_CNT: return snprintf( str, size, "too many parallel execution paths (pid=%d, pc=0x%08X)", pid, pc );
case IE_SUCC_CNT: return snprintf( str, size, "too many possible successor states (pid=%d, pc=0x%08X)", pid, pc );
case IE_STATE_MEM: return snprintf( str, size, "out of temporary state memory (pid=%d, pc=0x%08X)", pid, pc );
case IE_NO_ACT_PROC: return snprintf( str, size, "INTERNAL ERROR: no active process" ); // no pid or pc here
case IE_EN_PROC_CNT: return snprintf( str, size, "too many enabled processes" ); // no pid or pc here
case IE_INVIS_MEM: return snprintf( str, size, "out of memory for invisible states (pid=%d, pc=0x%08X)", pid, pc );
default: return snprintf( str, size, "unknown error (pid=%d, pc=0x%08X)", pid, pc );
}
}
// convert output entries (recursively) to string (without terminating 0)
// *pp_buf: pointer into buffer, advanced by this function
// *p_buf_len: remaining length of buffer, decreased by this function
// returns -1 on error, 0 on success
static int instr_output_entry_to_str( st_bytecode *bytecode, st_instr_output *p_output,
char **p_buf, unsigned long *p_buf_len )
{
char *str, buf[32];
// nothing to print
if( p_output == NULL )
return 0;
// process previous entries
if( instr_output_entry_to_str( bytecode, p_output->p_prev, p_buf, p_buf_len ) < 0 )
return -1;
// string
if( p_output->is_str )
{
if( p_output->data.str.str < bytecode->string_cnt )
str = bytecode->strings[p_output->data.str.str];
else
sprintf( str = buf, "STRING(%d)", p_output->data.str.str );
}
// value
else
{
switch( p_output->data.value.fmt ) // print formatted value
{
case 'c': case 'C': sprintf( str = buf, "%c", p_output->data.value.value ); break;
case 'd': case 'D': sprintf( str = buf, "%d", p_output->data.value.value ); break;
case 'e': case 'E': if( p_output->data.value.value < bytecode->string_cnt )
str = bytecode->strings[p_output->data.value.value];
else
sprintf( str = buf, "STRING(%d)", p_output->data.value.value );
break;
case 'o': case 'O': sprintf( str = buf, "%o", p_output->data.value.value ); break;
case 'u': case 'U': sprintf( str = buf, "%u", p_output->data.value.value ); break;
case 'x': sprintf( str = buf, "%x", p_output->data.value.value ); break;
case 'X': sprintf( str = buf, "%X", p_output->data.value.value ); break;
default: sprintf( str = buf, "%d", p_output->data.value.value ); // default is decimal
}
}
// copy string into buffer
unsigned int len = strlen( str );
if( *p_buf_len < strlen( str ) )
return -1;
memcpy( *p_buf, str, len );
*p_buf += len;
*p_buf_len -= len;
// success
return 0;
}
// convert output list to string
// *pp_buf: pointer into buffer, advanced by this function
// *p_buf_len: remaining length of buffer, decreased by this function
// returns pointer to 0-terminated string in buffer or NULL in case of error
char * instr_output_to_str( st_bytecode *bytecode, st_instr_output *p_output,
char **p_buf, unsigned long *p_buf_len ) // extern
{
// remember start of string
char *start = *p_buf;
// convert output list
if( instr_output_entry_to_str( bytecode, p_output, p_buf, p_buf_len ) < 0 )
return NULL;
// add terminating 0
if( *p_buf_len < 1 )
return NULL;
**p_buf = 0;
(*p_buf)++;
(*p_buf_len)--;
return start;
}
// convert output during successor state generation to string
// *pp_buf: pointer into buffer, advanced by this function
// *p_buf_len: remaining length of buffer, decreased by this function
// returns pointer to 0-terminated string in buffer or NULL in case of error
char * instr_succ_output_to_str( st_bytecode *bytecode, st_instr_succ_output *p_succ_out,
char **p_buf, unsigned long *p_buf_len ) // extern
{
// remember start of string
char *start = *p_buf;
// convert output of system during 1st part of sync. comm.
if( instr_output_entry_to_str( bytecode, p_succ_out->p_out_sys1st, p_buf, p_buf_len ) < 0 )
return NULL;
// convert output of system
if( instr_output_entry_to_str( bytecode, p_succ_out->p_out_sys, p_buf, p_buf_len ) < 0 )
return NULL;
// convert output of monitor
if( instr_output_entry_to_str( bytecode, p_succ_out->p_out_monitor, p_buf, p_buf_len ) < 0 )
return NULL;
// add terminating 0
if( *p_buf_len < 1 )
return NULL;
**p_buf = 0;
(*p_buf)++;
(*p_buf_len)--;
return start;
}

View file

@ -0,0 +1,45 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_instr_tools
#define INC_instr_tools
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include "bytecode.h"
#include "instr.h"
#include "state.h"
// convert output list to string
// *pp_buf: pointer into buffer, advanced by this function
// *p_buf_len: remaining length of buffer, decreased by this function
// returns pointer to 0-terminated string in buffer or NULL in case of error
extern char * instr_output_to_str( st_bytecode *bytecode, st_instr_output *p_output,
char **p_buf, unsigned long *p_buf_len );
// convert output during successor state generation to string
// *pp_buf: pointer into buffer, advanced by this function
// *p_buf_len: remaining length of buffer, decreased by this function
// returns pointer to 0-terminated string in buffer or NULL in case of error
extern char * instr_succ_output_to_str( st_bytecode *bytecode, st_instr_succ_output *p_succ_out,
char **p_buf, unsigned long *p_buf_len );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_instr_tools

View file

@ -0,0 +1,215 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "bytecode.h"
#include "instr.h"
#include "instr_step.h"
#include "instr_tools.h"
#include "instr_wrap.h"
#include "state.h"
#include "nipsvm.h"
// *** helper functions ***
// private context for instr_succ_buf
typedef struct t_instr_succ_buf_ctx
{
unsigned long succ_cnt;
st_global_state_header **p_succ;
unsigned long succ_max;
char **pp_buf;
unsigned long *p_buf_len;
} st_instr_succ_buf_ctx;
// successor callback function for instr_succ_buf
et_ic_status instr_succ_buf_succ_cb( st_global_state_header *succ,
uint8_t label_1st, uint8_t label,
t_flag_reg flag_reg_1st, t_flag_reg flag_reg,
unsigned int succ_cb_flags,
st_instr_succ_output *p_succ_out,
void *priv_context )
{
st_instr_succ_buf_ctx *p_ctx = (st_instr_succ_buf_ctx *)priv_context;
// copy successor state into buffer and save pointer to it
if( p_ctx->succ_cnt >= p_ctx->succ_max )
{
fprintf( stderr, "RUNTIME ERROR: too many successor states (try \"-s\")\n" );
return IC_STOP;
}
p_ctx->p_succ[p_ctx->succ_cnt] = global_state_copy( succ, p_ctx->pp_buf, p_ctx->p_buf_len );
if( p_ctx->p_succ[p_ctx->succ_cnt] == NULL )
{
fprintf( stderr, "RUNTIME ERROR: out of state memory (try \"-b\")\n" );
return IC_STOP;
}
p_ctx->succ_cnt++;
// go on finding successor states
return IC_CONTINUE;
// keep compiler happy
label_1st = 0;
label = 0;
flag_reg_1st = 0;
flag_reg = 0;
succ_cb_flags = 0;
p_succ_out = NULL;
}
// private context for instr_succ_buf_ex
typedef struct t_instr_succ_buf_ex_ctx
{
unsigned long succ_cnt;
st_instr_succ_buf_ex_state *succ;
unsigned long succ_max;
char **pp_buf;
unsigned long *p_buf_len;
int process_output;
st_bytecode *bytecode;
} st_instr_succ_buf_ex_ctx;
// successor callback function for instr_succ_buf_ex
et_ic_status instr_succ_buf_ex_succ_cb( st_global_state_header *succ,
uint8_t label_1st, uint8_t label,
t_flag_reg flag_reg_1st, t_flag_reg flag_reg,
unsigned int succ_cb_flags,
st_instr_succ_output *p_succ_out,
void *priv_context )
{
st_instr_succ_buf_ex_ctx *p_ctx = (st_instr_succ_buf_ex_ctx *)priv_context;
// copy successor state into buffer and save pointer to it
if( p_ctx->succ_cnt >= p_ctx->succ_max )
{
fprintf( stderr, "RUNTIME ERROR: too many successor states (try \"-s\")\n" );
return IC_STOP;
}
p_ctx->succ[p_ctx->succ_cnt].p_state = global_state_copy( succ, p_ctx->pp_buf, p_ctx->p_buf_len ); // save successor state
if( p_ctx->succ[p_ctx->succ_cnt].p_state == NULL )
{
fprintf( stderr, "RUNTIME ERROR: out of state memory (try \"-b\")\n" );
return IC_STOP;
}
p_ctx->succ[p_ctx->succ_cnt].label_1st = label_1st; // save additional information
p_ctx->succ[p_ctx->succ_cnt].label = label;
p_ctx->succ[p_ctx->succ_cnt].flag_reg_1st = flag_reg_1st;
p_ctx->succ[p_ctx->succ_cnt].flag_reg = flag_reg;
p_ctx->succ[p_ctx->succ_cnt].succ_cb_flags = succ_cb_flags;
if( p_ctx->process_output ) // save output string
{
p_ctx->succ[p_ctx->succ_cnt].p_output = instr_succ_output_to_str( p_ctx->bytecode, p_succ_out,
p_ctx->pp_buf, p_ctx->p_buf_len );
if( p_ctx->succ[p_ctx->succ_cnt].p_output == NULL )
{
fprintf( stderr, "RUNTIME ERROR: out of state memory (try \"-b\")\n" );
return IC_STOP;
}
}
else // do not save output string
p_ctx->succ[p_ctx->succ_cnt].p_output = NULL;
p_ctx->succ_cnt++;
// go on finding successor states
return IC_CONTINUE;
}
// *** functions visible from outside ***
// initialize successor state generation context for instr_succ_buf
// must be called before instr_succ_buf (only 1 time, not every time)
void instr_succ_buf_prepare( st_instr_succ_context *succ_ctx,
st_bytecode *bytecode ) // extern
{
// set default maximum values and bytecode
instr_succ_context_default( succ_ctx, bytecode );
// set callbacks
succ_ctx->succ_cb = instr_succ_buf_succ_cb;
succ_ctx->err_cb = nipsvm_default_error_cb;
}
// generate successor states in a buffer
// pointers to states are put into array pointed to by p_succ
// *pp_buf points to memory area of length *p_len to use for successor states
// returns number of successor states
unsigned long instr_succ_buf( st_instr_succ_context *succ_ctx, // must be initialized with instr_succ_buf_prepare
st_global_state_header *p_glob, // state to start with
st_global_state_header **p_succ, unsigned long succ_max,
char **pp_buf, unsigned long *p_buf_len ) // extern
{
// set up private context
st_instr_succ_buf_ctx ctx =
{
.succ_cnt = 0,
.p_succ = p_succ,
.succ_max = succ_max,
.pp_buf = pp_buf,
.p_buf_len = p_buf_len
};
succ_ctx->priv_context = &ctx; // put it into successor state generation context
// generate successor states - using normal successor function
instr_succ( p_glob, 0, succ_ctx );
// return number of successor states
return ctx.succ_cnt;
}
// initialize successor state generation context for instr_succ_buf_ex
// must be called before instr_succ_buf_ex (only 1 time, not every time)
void instr_succ_buf_ex_prepare( st_instr_succ_context *succ_ctx,
st_bytecode *bytecode ) // extern
{
// set default maximum values and bytecode
instr_succ_context_default( succ_ctx, bytecode );
// set callbacks
succ_ctx->succ_cb = instr_succ_buf_ex_succ_cb;
succ_ctx->err_cb = nipsvm_default_error_cb;
}
// generate successor states and extended information in a buffer
// state structure array succ is filled with results
// *pp_buf points to memory area of length *p_len to use for successor states and output strings
// returns number of successor states
unsigned long instr_succ_buf_ex( st_instr_succ_context *succ_ctx, // must be initialized with instr_succ_buf_ex_prepare
st_global_state_header *p_glob, // state to start with
t_flag_reg flag_reg, // value to put into flag register
int process_output, // if to process output (from print instructions)
st_instr_succ_buf_ex_state *succ, unsigned long succ_max,
char **pp_buf, unsigned long *p_buf_len ) // extern
{
// set up private context
st_instr_succ_buf_ex_ctx ctx =
{
.succ_cnt = 0,
.succ = succ,
.succ_max = succ_max,
.pp_buf = pp_buf,
.p_buf_len = p_buf_len,
.process_output = process_output,
.bytecode = succ_ctx->bytecode
};
succ_ctx->priv_context = &ctx; // put it into successor state generation context
// generate successor states - using normal successor function
instr_succ( p_glob, flag_reg, succ_ctx );
// return number of successor states
return ctx.succ_cnt;
}

View file

@ -0,0 +1,77 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_instr_wrap
#define INC_instr_wrap
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include "bytecode.h"
#include "instr.h"
#include "state.h"
// successor state structure for instr_succ_buf_ex
typedef struct t_instr_succ_buf_ex_state
{
st_global_state_header *p_state; // the successor state
uint8_t label_1st; // label of STEP command of 1st part of sync. comm. (unused if ! sync)
uint8_t label; // label of STEP command (of 2nd part of sync. comm. if sync)
t_flag_reg flag_reg_1st; // flag register value returned by 1st part of sync. comm. (unused if ! sync)
t_flag_reg flag_reg; // flag register value returned (by 2nd part of sync. comm. if sync)
unsigned int succ_cb_flags; // boolean flags (if sync. comm. took place, if timeout occured, ...)
char *p_output; // pointer to 0-terminated output string (if process_output in instr_succ_buf_ex call was != 0)
} st_instr_succ_buf_ex_state;
// initialize successor state generation context for instr_succ_buf
// must be called before instr_succ_buf (only 1 time, not every time)
extern void instr_succ_buf_prepare( st_instr_succ_context *succ_ctx,
st_bytecode *bytecode );
// generate successor states in a buffer
// pointers to states are put into array pointed to by p_succ
// *pp_buf points to memory area of length *p_len to use for successor states
// returns number of successor states
extern unsigned long instr_succ_buf( st_instr_succ_context *succ_ctx, // must be initialized with instr_succ_buf_prepare
st_global_state_header *p_glob, // state to start with
st_global_state_header **p_succ, unsigned long succ_max,
char **pp_buf, unsigned long *p_buf_len );
// initialize successor state generation context for instr_succ_buf_ex
// must be called before instr_succ_buf_ex (only 1 time, not every time)
extern void instr_succ_buf_ex_prepare( st_instr_succ_context *succ_ctx,
st_bytecode *bytecode );
// generate successor states and extended information in a buffer
// state structure array succ is filled with results
// *pp_buf points to memory area of length *p_len to use for successor states and output strings
// returns number of successor states
extern unsigned long instr_succ_buf_ex( st_instr_succ_context *succ_ctx, // must be initialized with instr_succ_buf_ex_prepare
st_global_state_header *p_glob, // state to start with
t_flag_reg flag_reg, // value to put into flag register
int process_output, // if to process output (from print instructions)
st_instr_succ_buf_ex_state *succ, unsigned long succ_max,
char **pp_buf, unsigned long *p_buf_len );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_instr_wrap

View file

@ -0,0 +1,207 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include "bytecode.h"
//#include "instr.h"
//#include "instr_step.h"
#include "instr_wrap.h"
#include "rt_err.h"
#include "split.h"
//#include "state.h"
//#include "tools.h"
#include "interactive.h"
// do an interactive simulation stating from the initial state
// random: boolean flag if to replace interactivity by randomness
// rnd_quiet: if to suppress state output during random simulation
// print_hex: boolean flag if to print current states in hexadecimal
// split: boolean flag if to split up and reassemble states
// buffer_len: size of state buffer
// states_max: number of state pointer buffer entries
// ini_state: state to start simulation at (or NULL to use default)
void interactive_simulate( nipsvm_bytecode_t *bytecode,
int random, int rnd_quiet, int print_hex, int split,
unsigned long buffer_len, unsigned int states_max,
nipsvm_state_t *ini_state ) // extern
{
char *p_buffer, *p_buf;
nipsvm_state_t *p_cur;
t_flag_reg flag_reg;
st_instr_succ_buf_ex_state *states;
unsigned long buf_len, cnt, i, j, sel, len;
st_instr_succ_context succ_ctx;
// does monitor process exist?
if( bytecode_monitor_present( bytecode ) )
printf( "monitor process exists\n\n" );
// allocate buffer for states
p_buffer = (char *)malloc( buffer_len );
if( p_buffer == NULL )
rt_err( "out of memory (state buffer for interactive/random simulation)" );
// allocate buffer for successor state pointers
states = (st_instr_succ_buf_ex_state *)malloc( states_max * sizeof( st_instr_succ_buf_ex_state ) );
if( states == NULL )
rt_err( "out of memory (successor state pointer buffer for interactive/random simulation)" );
// get initial state
p_buf = p_buffer;
buf_len = buffer_len;
if( ini_state != NULL )
p_cur = global_state_copy( ini_state, &p_buf, &buf_len );
else
p_cur = global_state_initial( &p_buf, &buf_len );
if( p_cur == NULL )
rt_err( "state buffer too small for initial state" );
flag_reg = 0;
// initialize context for instr_succ_buf_ex
instr_succ_buf_ex_prepare( &succ_ctx, bytecode );
// scheduler loop
for( ; ; )
{
// show current state
if( ! random || ! rnd_quiet )
{
printf( "=== current state ===\n" );
global_state_print( p_cur );
printf( "ext info: flags=0x%X\n", flag_reg );
printf( "\n" );
// print current state in hexadecimal
if( print_hex )
{
len = nipsvm_state_size( p_cur );
for( i = 0, j = 0; i < len; )
{
printf( "%04lX:", i );
for( j = 0; i < len && j < 0x10; i++, j++ )
printf( " %02X", ((unsigned char *)p_cur)[i] );
printf( "\n" );
}
printf( "\n" );
}
}
// split up and reassemble
if( split )
{
if( ! split_test( p_cur, stdout ) )
{
fprintf( stderr, "split test failed\n" );
break;
}
printf( "\n" );
}
// generate successor states
cnt = instr_succ_buf_ex( &succ_ctx, p_cur, flag_reg, 1 /* process output */,
states, states_max, &p_buf, &buf_len );
if( random )
{
// select a random state
if( cnt == 0 )
sel = 1;
else
{
sel = rand( ) % cnt;
if( states[sel].p_output[0] != 0 ) // show output
{
if( rnd_quiet )
printf( "%s", states[sel].p_output );
else
printf( "output: \"%s\"\n\n", states[sel].p_output );
}
}
}
else
{
// show successor states
printf( "\n=== %ld successor state%s ===\n", cnt, cnt == 1 ? "" : "s" );
for( i = 0; i < cnt; i++ )
{
printf( "--- successor state %ld ---\n", i + 1 );
global_state_print( states[i].p_state );
printf( "ext info: " ); // show extended information
if( states[i].succ_cb_flags & INSTR_SUCC_CB_FLAG_SYNC )
printf( "SYNC, label=%u flags=0x%X, label=%u flags=0x%X",
states[i].label_1st, states[i].flag_reg_1st,
states[i].label, states[i].flag_reg );
else
printf( "label=%u flags=0x%X",
states[i].label, states[i].flag_reg );
if( states[i].succ_cb_flags & INSTR_SUCC_CB_FLAG_TIMEOUT )
printf( ", TIMEOUT" );
if( states[i].succ_cb_flags & INSTR_SUCC_CB_FLAG_SYS_BLOCK )
printf( ", SYS_BLOCK" );
printf( "\n" );
if( states[i].succ_cb_flags & INSTR_SUCC_CB_FLAG_MONITOR_EXIST )
{
printf( "monitor: exists" );
if( states[i].succ_cb_flags & INSTR_SUCC_CB_FLAG_MONITOR_EXEC )
printf( ", executed" );
if( states[i].succ_cb_flags & INSTR_SUCC_CB_FLAG_MONITOR_ACCEPT )
printf( ", ACCEPT" );
if( states[i].succ_cb_flags & INSTR_SUCC_CB_FLAG_MONITOR_TERM )
printf( ", TERM" );
printf( "\n" );
}
if( states[i].p_output[0] != 0 ) // show output
printf( "output: \"%s\"\n", states[i].p_output );
}
printf( "\n" );
// let user select a state
if( cnt == 0 )
printf( "enter something to quit: " );
else if( cnt == 1 )
printf( "enter state number (1) or anything else to quit: " );
else
printf( "enter state number (1..%ld) or anything else to quit: ", cnt );
fflush( stdout );
scanf( "%lu", &sel );
printf( "\n" );
sel--; // convert to 0 based index
}
// end requested
if( sel >= cnt )
break;
// system blocked in random mode -> end
if( random && states[sel].succ_cb_flags & INSTR_SUCC_CB_FLAG_SYS_BLOCK )
break;
// keep flag register of selected state (just for testing)
flag_reg = states[sel].flag_reg;
// move selected state to beginning of buffer and use it as current state
len = nipsvm_state_size( states[sel].p_state );
memmove( p_buffer, states[sel].p_state, len );
p_buf = p_buffer + len;
buf_len = buffer_len - len;
p_cur = (nipsvm_state_t *)p_buffer;
} // for ( ; ; )
// free malloc-ed memory buffers
free( p_buffer );
free( states );
}

View file

@ -0,0 +1,36 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_interactive
#define INC_interactive
#include "nipsvm.h"
#ifdef __cplusplus
extern "C"
{
#endif
// do an interactive simulation stating from the initial state
// random: boolean flag if to replace interactivity by randomness
// rnd_quiet: if to suppress state output during random simulation
// print_hex: boolean flag if to print current states in hexadecimal
// split: boolean flag if to split up and reassemble states
// buffer_len: size of state buffer
// states_max: number of state pointer buffer entries
// ini_state: state to start simulation at (or NULL to use default)
extern void interactive_simulate( st_bytecode *bytecode, int random, int rnd_quiet, int print_hex, int split,
unsigned long buffer_len, unsigned int states_max,
st_global_state_header *ini_state );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_interactive

367
iface/nips/nips_vm/main.c Normal file
View file

@ -0,0 +1,367 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005,2006: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "bytecode.h"
#include "interactive.h"
#include "search.h"
#include "state.h"
// minimal, default, maximal size of state buffer (in MB)
#define MINIMAL_BUFFER_LEN_M 1 // 1MB
#define DEFAULT_BUFFER_LEN_M 256 // 256MB
#define MAXIMAL_BUFFER_LEN_M 8192 // 8GB
// minimal, default, maximal hash table parameters (in k entries, in retries)
#define MINIMAL_HASH_ENTRIES_K 64 // 64k entries (may not be smaller than 64k because of hashtab internals)
#define DEFAULT_HASH_ENTRIES_K 4096 // 4M entries
#define MAXIMAL_HASH_ENTRIES_K 131072 // 128M entries
#define MINIMAL_HASH_RETRIES 2 // 2 retries
#define DEFAULT_HASH_RETRIES 500 // 500 retries
#define MAXIMAL_HASH_RETRIES 1000 // 1000 retries (insane)
// minimal, default, maximal size of state pointer buffer entries (in k entries)
#define MINIMAL_STATES_MAX_K 1 // 1k entries
#define DEFAULT_STATES_MAX_K 8 // 8k entries
#define MAXIMAL_STATES_MAX_K 1024 // 1M entries
// maximum values are here to ensure that user does not enter utopical values
// - like 1e9 MB for state buffer size
// - would result in overflow during multiplication (while converting from MB to bytes)
// the maximum values can be increased on 64 bit systems
// and slightly increased on 32 bit systems with 36 bit addresses
// read state from hex string
// returns boolean result (if successful)
static int read_hex( char *str, char *state, int max_size )
{
unsigned int val;
for( ; *str != 0; )
{
if( sscanf( str, "%2X", &val ) != 1 )
return 0;
str += 2;
if( max_size <= 0 )
return 0;
*state = (char)(unsigned char)val;
state++;
max_size--;
}
return 1;
}
int main( int argc, char **argv )
{
char *bytecode_file = "", *bytecode_module = NULL, *graph_file = NULL, *ptr;
unsigned int depth = 0;
unsigned int lookup_addr = 0;
unsigned long buffer_len = DEFAULT_BUFFER_LEN_M * 1048576;
unsigned long hash_entries = DEFAULT_HASH_ENTRIES_K * 1024;
unsigned long hash_retries = DEFAULT_HASH_RETRIES;
unsigned long states_max = DEFAULT_STATES_MAX_K * 1024;
unsigned long val;
int have_bytecode_file, have_bytecode_module, breadth_first, have_depth, have_lookup;
int random, rnd_quiet, print_hex, split, i, arg_err, graph;
st_bytecode *bytecode;
FILE *graph_out = NULL;
char initial_state[1024];
st_global_state_header *p_ini_state = NULL;
printf( "NIPS VM version 1.2.6 date 2007-03-10\n"
"Copyright (C) 2005,2007: Stefan Schuermans <stefan@schuermans.info>\n"
" Michael Weber <Michael.Weber@cwi.nl>\n"
" Lehrstuhl fuer Informatik II, RWTH Aachen\n"
"Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html\n\n" );
// initialize pseudo random number generator
srand( time( NULL ) );
// parse parameters
arg_err = 0;
have_bytecode_file = 0;
have_bytecode_module = 0;
breadth_first = 0;
have_depth = 0;
have_lookup = 0;
random = 0;
rnd_quiet = 0;
print_hex = 0;
split = 0;
graph = 0;
for( i = 1; i < argc && ! arg_err; i++ )
{
// breadth first search
if( strcmp( argv[i], "-B" ) == 0 )
{
breadth_first = 1;
}
// depth first search
else if( strcmp( argv[i], "-D" ) == 0 )
{
if( i + 1 < argc )
{
i++;
depth = (unsigned int)strtoul( argv[i], NULL, 0 );
have_depth = 1;
}
else
arg_err = 1;
}
// print states in hexadecimal
else if( strcmp( argv[i], "-H" ) == 0 )
print_hex = 1;
// initial state
else if( strcmp( argv[i], "-I" ) == 0 )
{
if( i + 1 < argc )
{
i++;
if( read_hex( argv[i], initial_state, sizeof( initial_state ) ) )
p_ini_state = (st_global_state_header *)initial_state;
else
arg_err = 1;
}
else
arg_err = 1;
}
// lookup address
else if( strcmp( argv[i], "-L" ) == 0 )
{
if( i + 1 < argc )
{
i++;
lookup_addr = (unsigned int)strtoul( argv[i], NULL, 0 );
have_lookup = 1;
}
else
arg_err = 1;
}
// random simulation
else if( strcmp( argv[i], "-R" ) == 0 )
random = 1, rnd_quiet = 0;
// quiet random simulation
else if( strcmp( argv[i], "-Rq" ) == 0 )
random = 1, rnd_quiet = 1;
// split up and reassemble states
else if( strcmp( argv[i], "-S" ) == 0 )
split = 1;
// buffer size
else if( strcmp( argv[i], "-b" ) == 0 )
{
if( i + 1 < argc )
{
i++;
val = strtoul( argv[i], NULL, 0 );
if( val >= MINIMAL_BUFFER_LEN_M && val <= MAXIMAL_BUFFER_LEN_M )
buffer_len = val * 1048576;
else
arg_err = 1;
}
else
arg_err = 1;
}
// state graph output
else if( strcmp( argv[i], "-g" ) == 0 )
{
graph = 1;
}
// state graph output to file
else if( strcmp( argv[i], "-gf" ) == 0 )
{
if( i + 1 < argc )
{
i++;
graph = 1;
graph_file = argv[i];
}
else
arg_err = 1;
}
// hash table parameters
else if( strcmp( argv[i], "-h" ) == 0 )
{
if( i + 2 < argc )
{
i++;
val = strtoul( argv[i], NULL, 0 );
if( val >= MINIMAL_HASH_ENTRIES_K && val <= MAXIMAL_HASH_ENTRIES_K )
hash_entries = val * 1024;
else
arg_err = 1;
i++;
val = strtoul( argv[i], NULL, 0 );
if( val >= MINIMAL_HASH_RETRIES && val <= MAXIMAL_HASH_RETRIES )
hash_retries = val;
else
arg_err = 1;
}
else
arg_err = 1;
}
// state pointer buffer entries
else if( strcmp( argv[i], "-s" ) == 0 )
{
if( i + 1 < argc )
{
i++;
val = strtoul( argv[i], NULL, 0 );
if( val >= MINIMAL_STATES_MAX_K && val <= MAXIMAL_STATES_MAX_K )
states_max = val * 1024;
else
arg_err = 1;
}
else
arg_err = 1;
}
// unknown option
else if( argv[i][0] == '-' )
arg_err = 1;
// bytecode file
else if( ! have_bytecode_file )
{
bytecode_file = argv[i];
have_bytecode_file = 1;
}
// bytecode module
else if( ! have_bytecode_module )
{
bytecode_module = argv[i];
have_bytecode_module = 1;
}
// unknown argument
else
arg_err = 1;
}
if( ! have_bytecode_file ) // no bytecode file -> ...
arg_err = 1; // ... -> argument error
// print usage in case of argument error
if( arg_err )
{
printf( "usage:\n"
" %s [options] <bytecode file> [<bytecode module>]\n\n"
"options:\n"
" -B perform breadth-first search\n"
" -D <depth> perform depth-first search up to specified depth\n"
" -H print states in hexadecimal\n"
" -I <hex-state> use supplied initial state (format: \"[0-9A-F]*\")\n"
" -L <address> lookup source code location to address\n"
" -R perform random simulation\n"
" -Rq perform quiet random simulation\n"
" -S split up and reassemble states (not for -B or -D)\n"
" -b <buffer-size> set buffer size to use\n"
" (in MB, valid: %u..%u, default: %u)\n"
" -g output state graph in graphviz format (-B, -D)\n"
" -gf <file> output state graph in graphviz format (-B, -D)\n"
" -h <entries> <retries> set hash table parameters\n"
" (in k entries, valid: %u..%u, default: %u,\n"
" in retries, valid: %u..%u, default: %u)\n"
" -s <state-count> set number of state pointer buffer entries\n"
" (in k entries, valid: %u..%u, default: %u)\n\n",
argv[0],
MINIMAL_BUFFER_LEN_M, MAXIMAL_BUFFER_LEN_M, DEFAULT_BUFFER_LEN_M,
MINIMAL_HASH_ENTRIES_K, MAXIMAL_HASH_ENTRIES_K, DEFAULT_HASH_ENTRIES_K,
MINIMAL_HASH_RETRIES, MAXIMAL_HASH_RETRIES, DEFAULT_HASH_RETRIES,
MINIMAL_STATES_MAX_K, MAXIMAL_STATES_MAX_K, DEFAULT_STATES_MAX_K );
return -1;
}
// load bytecode
bytecode = bytecode_load_from_file( bytecode_file, bytecode_module );
if( bytecode == NULL )
{
if( have_bytecode_module )
fprintf( stderr, "could not load bytecode module \"%s\" from file \"%s\": %s\n\n",
bytecode_module, bytecode_file, strerror( errno ) );
else
fprintf( stderr, "could not load bytecode from file \"%s\": %s\n\n",
bytecode_file, strerror( errno ) );
return -1;
}
// state graph output - generate filename
i = 1;
if( graph && graph_file == NULL )
{
if( have_bytecode_module )
i = strlen( bytecode_file ) + strlen( bytecode_module ) + 16;
else
i = strlen( bytecode_file ) + 16;
}
char graph_file_buf[i]; // allocate buffer for filename
if( graph && graph_file == NULL )
{
if( have_bytecode_module )
sprintf( graph_file_buf, "%s_%s.dot", bytecode_file, bytecode_module );
else
sprintf( graph_file_buf, "%s.dot", bytecode_file );
graph_file = graph_file_buf;
}
// state graph output - open file
if( graph )
{
graph_out = fopen( graph_file, "wt" );
if( graph_out == NULL )
fprintf( stderr, "could not open graph output file \"%s\" (%s) -> no graph output\n", graph_file, strerror( errno ) );
}
// state graph output - print header
if( graph_out != NULL )
{
fprintf( graph_out, "digraph \"" );
for( ptr = bytecode_file; *ptr != 0; ptr++ )
if( *ptr < ' ' || *ptr == '\'' || *ptr == '\"' || *ptr == '\\' )
fputc( '_', graph_out );
else
fputc( *ptr, graph_out );
if( have_bytecode_module )
{
fputc( '_', graph_out );
for( ptr = bytecode_module; *ptr != 0; ptr++ )
if( *ptr < ' ' || *ptr == '\'' || *ptr == '\"' || *ptr == '\\' )
fputc( '_', graph_out );
else
fputc( *ptr, graph_out );
}
fprintf( graph_out, "\" {\n" );
}
// lookup address
if( have_lookup )
{
st_src_loc loc = bytecode_src_loc( bytecode, lookup_addr );
printf( "looked up address 0x%08X: line %d col %d\n\n", loc.addr, loc.line, loc.col );
}
// depth-first/breadth-first search
else if( breadth_first || have_depth )
search( bytecode, breadth_first, depth, buffer_len, hash_entries, hash_retries,
graph_out, print_hex, p_ini_state );
// interactive/random simulation
else
interactive_simulate( bytecode, random, rnd_quiet, print_hex, split,
buffer_len, states_max, p_ini_state );
// state graph output - finish and close file
if( graph_out != NULL )
{
fprintf( graph_out, "}\n" );
fclose( graph_out );
}
printf( "\n" );
// unload bytecode
bytecode_unload( bytecode );
return 0;
}

588
iface/nips/nips_vm/nips_asm.pl Executable file
View file

@ -0,0 +1,588 @@
#! /usr/bin/perl
# NIPS Asm - New Implementation of Promela Semantics Assembler
# Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
# Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
# Lehrstuhl fuer Informatik II, RWTH Aachen
# Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
use strict;
# add directory of script to include directory
my $incdir=$0;
$incdir =~ s/[^\/]*$//;
$incdir = "./" if( $incdir eq "" );
push @INC, $incdir;
my $libprefix = $ENV{'NIPS_HOME'} || ".";
require $libprefix."/nips_asm_help.pl";
require $libprefix."/nips_asm_instr.pl";
my @instructions = get_instructions( );
print <<EOF;
NIPS Asm - New Implementation of Promela Semantics Assembler
Copyright (C) 2005: Stefan Schuermans <stefan\@schuermans.info>
Michael Weber <michaelw\@i2.informatik.rwth-aachen.de>
Lehrstuhl fuer Informatik II, RWTH Aachen
Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
EOF
# parse parameters
die "usage: perl nips_asm.pl <input.s> [<output.b> [<output.l>]]" if( @ARGV < 1 || @ARGV > 3 );
my ( $file_asm, $file_byte, $file_list ) = ( @ARGV[0], @ARGV[0], @ARGV[0] );
$file_byte =~ s/(\.s)?$/.b/;
$file_byte = @ARGV[1] if( @ARGV >= 2 );
$file_list =~ s/(\.s)?$/.l/;
$file_list = @ARGV[2] if( @ARGV >= 3 );
# parse input
print "parsing input file \"$file_asm\"...\n";
open ASM, "<".$file_asm or die "could not open input file \"$file_asm\"";
my ( $line_no, $line, $module );
my $modflags = 0;
my $addr = 0;
my @bytecodes = ( );
my @flags = ( );
my @strings = ( );
my @srclocs = ( );
my @strinfs = ( );
$module = "";
my @modules = ( );
for( $line_no = 1; $line = <ASM>; $line_no++ )
{
# remove newline, whitespace, comment
chomp $line;
chomp $line;
$line =~ s/\t/ /g;
$line =~ s/^ *([^;]*)(;.*)?$/\1/;
# get label and split words
$line =~ /^(([A-Za-z][A-Za-z0-9_]*): *)?(.*)$/;
my $label = $2;
my @words = split /[ ,]+/, $3;
# get string possibly contained in line
my $str = undef;
$str = eval( $1 ) if( $line =~ /^[^"]*("([^"]|\\.)*").*$/ );
# empty line
if( @words <= 0 || (@words == 1 && @words[0] eq "") )
{
# save empty bytecode for this line if there is a label
push @bytecodes, [ $addr, $label, [ ], [ ] ] if( $label ne "" );
next;
}
# start of new module
if( @words[0] eq "!module" )
{
die "\"!module\" needs a string in line $line_no" if( $str eq undef );
push @modules, [ $module, $modflags, [ @bytecodes ], [ @strings ] ] if( @bytecodes > 0 or @strings > 0 );
$module = $str;
$modflags = 0;
$addr = 0;
@bytecodes = ( );
@flags = ( );
@strings = ( );
@srclocs = ( );
@strinfs = ( );
next;
}
# module flags
if( @words[0] eq "!modflags" )
{
my $flag;
foreach $flag (@words)
{
if( $flag eq "monitor" ) { $modflags |= 0x00000001; }
}
next;
}
# flags for address
if( @words[0] eq "!flags" || @words[0] eq "!flags_addr" )
{
my $ad = $addr;
my $i = 1;
if( @words[0] eq "!flags_addr" )
{
if( @words[1] =~ /^0*[xX]/ )
{ $ad = hex( @words[1] ); }
else
{ $ad = int( @words[1] ); }
$i = 2;
}
my $fl = 0;
for( ; $i < @words; $i++ )
{
if( @words[$i] eq "progress" )
{ $fl |= 0x00000001; }
elsif( @words[$i] eq "accept" )
{ $fl |= 0x00000002; }
else
{ die "unknown flag \"@words[$i]\" in line $line_no"; }
}
if( @flags > 0 && @{@flags[@flags-1]}[0] > $ad ) {
die "flags out of order in line $line_no"
} elsif (@flags > 0 && @{@flags[@flags-1]}[0] == $ad) {
# add more flags
@{@flags[@flags-1]}[1] |= $fl;
} else {
push @flags, [ $ad, $fl ] if( $fl != 0 );
}
next;
}
# string to put into string table
if( @words[0] eq "!string" )
{
die "\"!string\" needs a number in line $line_no" if( @words[1] !~ /^[0-9]+$/ );
die "\"!string\" needs a string in line $line_no" if( $str eq undef );
my $i = int( @words[1] );
die "duplicate definition of string $i in line $line_no" if( $i < @strings and @strings[$i] ne undef );
my $j;
for( $j = @strings; $j < $i; $j++ )
{ @strings[$j] = undef; }
@strings[$i] = $str;
next;
}
# source location
if( @words[0] eq "!srcloc" || @words[0] eq "!srcloc_addr" )
{
my $ad = $addr;
my $i = 1;
if( @words[0] eq "!srcloc_addr" )
{
if( @words[1] =~ /^0*[xX]/ )
{ $ad = hex( @words[1] ); }
else
{ $ad = int( @words[1] ); }
$i = 2;
}
my $line = int( @words[$i] );
my $col = int( @words[$i+1] );
die "source location out of order in line $line_no" if( @srclocs > 0 && @{@srclocs[@srclocs-1]}[0] > $ad );
push @srclocs, [ $ad, $line, $col ];
next;
}
# structure information
if( @words[0] eq "!strinf" || @words[0] eq "!strinf_addr" )
{
my $ad = $addr;
my $i = 1;
if( @words[0] eq "!strinf_addr" )
{
if( @words[1] =~ /^0*[xX]/ )
{ $ad = hex( @words[1] ); }
else
{ $ad = int( @words[1] ); }
$i = 2;
}
my $code = 0xFF;
$code = 0x00 if( @words[$i] eq "begin" );
$code = 0x01 if( @words[$i] eq "end" );
$code = 0x02 if( @words[$i] eq "middle" );
my $type = @words[$i+1] . "";
die "invalid type \"$type\" in structure information in line $line_no" if( $type !~ /^[A-Za-z0-9_]+$/ );
my $name = @words[$i+2] . "";
die "invalid name \"$name\" in structure information in line $line_no" if( $name !~ /^[A-Za-z0-9_.]*$/ );
die "structure information out of order in line $line_no" if( @strinfs > 0 && @{@strinfs[@strinfs-1]}[0] > $ad );
push @strinfs, [ $ad, $code, $type, $name ];
next;
}
# find instruction in table
my $ok = 0;
my ( $opcode, $params );
for (@instructions)
{
( $opcode, $params ) = @{$_};
if( @words == @{$params} )
{
my $i;
for( $i = 0; $i < @{$params}; $i++ )
{
my $param = @{$params}[$i];
my $word = @words[$i];
last if( $param !~ /^\!/ and $param ne $word );
}
if( $i >= @{$params} )
{
$ok = 1;
last;
}
}
}
die "invalid instruction \"@words\" in line $line_no" if( ! $ok );
# process parameters and generate bytecode
my @bytecode = ( $opcode );
my $i;
for( $i = 0; $i < @{$params}; $i++ )
{
# byte constant
if( @{$params}[$i] eq "!const1" )
{
die "invalid constant \"".@words[$i]."\" in line $line_no" if( @words[$i] !~ /^-?[0-9]+$/ );
my $val = int( @words[$i] );
die "1-byte constant \"".@words[$i]."\" in line $line_no is out of range" if( $val > 0xFF or $val < -0x80 );
push @bytecode, $val & 0xFF;
}
# word constant
elsif( @{$params}[$i] eq "!const2" )
{
die "invalid constant \"".@words[$i]."\" in line $line_no" if( @words[$i] !~ /^-?[0-9]+$/ );
my $val = int( @words[$i] );
die "2-byte constant \"".@words[$i]."\" in line $line_no is out of range" if( $val > 0xFFFF or $val < -0x8000 );
push @bytecode, ($val >> 8) & 0xFF;
push @bytecode, $val & 0xFF;
}
# double-word constant
elsif( @{$params}[$i] eq "!const4" )
{
die "invalid constant \"".@words[$i]."\" in line $line_no" if( @words[$i] !~ /^-?[0-9]+$/ );
my $val = int( @words[$i] );
push @bytecode, ($val >> 24) & 0xFF;
push @bytecode, ($val >> 16) & 0xFF;
push @bytecode, ($val >> 8) & 0xFF;
push @bytecode, $val & 0xFF;
}
# register
elsif( @{$params}[$i] eq "!reg" )
{
die "invalid register \"".@words[$i]."\" in line $line_no" if( @words[$i] !~ /^r([0-7])$/ );
push @bytecode, int( $1 );
}
# relative address given by label
elsif( @{$params}[$i] eq "!addr" )
{
die "invalid label \"".@words[$i]."\" in line $line_no" if( @words[$i] !~ /^[A-Za-z][A-Za-z0-9_]*$/ );
push @bytecode, "addr1:".@words[$i]; # relative address of label takes 2 bytes
push @bytecode, "addr0:".@words[$i];
}
# absolute address given by label
elsif( @{$params}[$i] eq "!address" )
{
die "invalid label \"".@words[$i]."\" in line $line_no" if( @words[$i] !~ /^[A-Za-z][A-Za-z0-9_]*$/ );
push @bytecode, "address3:".@words[$i]; # absolute address of label takes 4 bytes
push @bytecode, "address2:".@words[$i];
push @bytecode, "address1:".@words[$i];
push @bytecode, "address0:".@words[$i];
}
# other parmeter type
elsif( @{$params}[$i] =~ /^\!/ )
{
die "internal error: unknown parmeter type \"".@{$params}[$i]."\"\n";
}
}
# save bytecode of this line
push @bytecodes, [ $addr, $label, [ @bytecode ], [ @words ] ];
$addr += @bytecode
}
push @modules, [ $module, $modflags, [ @bytecodes ], [ @flags ], [ @strings ], [ @srclocs], [ @strinfs ] ] if( @bytecodes > 0 or @strings > 0 );
die "no code found" if( @modules <= 0 );
# convert labels into addresses
print "converting labels to addresses...\n";
for (@modules)
{
my ($module, $modflags, $bytecodes, $flags, $strings, $srclocs, $strinfs) = @{$_};
for (@{$bytecodes})
{
my ( $addr, $label, $bc, $w ) = @{$_};
for (@{$bc})
{
# byte in bytecode is a label
if( $_ =~ /^addr(ess)?([0123]):([A-Za-z][A-Za-z0-9_]*)$/ )
{
my $rel = $1 eq "";
my $byte = $2;
my $lab = $3;
# find declaration of this label
my $ok = 0;
my $ad = "";
for (@{$bytecodes})
{
my ( $addr, $label, $bc, $w ) = @{$_};
if( $label eq $lab )
{
$ad = $addr;
$ok = 1;
last;
}
}
die "label \"$lab\" is not declared in module \"$module\"" if( ! $ok );
# convert address into relative one
if( $rel )
{
$ad -= $addr + @{$bc};
die "destination label \"".$lab."\" in module \"$module\" is out of range" if( $ad > 0x7FFF or $ad < -0x8000 );
}
# put right byte address into bytecode
$_ = ($ad >> ($byte * 8)) & 0xFF;
}
}
# update this line
$_ = [ $addr, $label, $bc, $w ];
}
}
# output bytecode and listing
print "writing output files \"$file_byte\" and \"$file_list\"...\n";
open BYTE, ">".$file_byte or die "could not open bytecode output file \"$file_byte\"";
binmode BYTE;
open LIST, ">".$file_list or die "could not open list output file \"$file_list\"";
# file header
print BYTE "NIPS v2 ";
wr_word( \*BYTE, @modules + 0 );
print LIST "# this code was assembled according to \"NIPS v2 \"\n\n";
# modules
for (@modules)
{
my ($module, $modflags, $bytecodes, $flags, $strings, $srclocs, $strinfs) = @{$_};
# module header
# sec_type
print BYTE "mod ";
# sec_sz (0 for now)
my $sec_sz_pos = wr_size_tmp( \*BYTE );
print LIST "!module \"" . escape_str( $module ) . "\"\n\n";
# module_name
wr_string( \*BYTE, $module );
# part_cnt
wr_word( \*BYTE, 6 );
# module flags
print BYTE "modf";
wr_dword( \*BYTE, 4 );
wr_dword( \*BYTE, $modflags );
print LIST "!modflags";
print LIST " monitor" if( $modflags & 0x00000001 );
print LIST "\n\n";
# code
# part_type
print BYTE "bc ";
# part_sz (0 for now)
my $part_sz_pos = wr_size_tmp( \*BYTE );
for (@{$bytecodes})
{
my ( $addr, $label, $bc, $w ) = @{$_};
# byte code
wr_byte( \*BYTE, $_ ) for (@{$bc});
# hex dump of bytecode
printf LIST "0x%08X:", $addr;
printf LIST " 0x%02X", $_ for (@{$bc});
# indentation
my $i;
for( $i = 0; $i < 48 - 7 - 5 * @{$bc}; $i++ ) { print LIST " "; }
# source code
print LIST " #";
print LIST " $label:" if( $label ne "" );
print LIST " $_" for (@{$w});
print LIST "\n";
}
print LIST "\n";
# part_sz
wr_size_fillin( \*BYTE, $part_sz_pos );
# flag table
# part_type
print BYTE "flag";
# part_sz
$part_sz_pos = wr_size_tmp( \*BYTE );
# flag_cnt
wr_word( \*BYTE, @{$flags} + 0 );
my $i;
for( $i = 0; $i < @{$flags}; $i++ )
{
my ($addr, $fl) = @{@{$flags}[$i]};
# flag
wr_dword( \*BYTE, $addr );
wr_dword( \*BYTE, $fl );
printf LIST "!flags_addr 0x%08X", $addr;
print LIST " progress" if( $fl & 0x00000001 );
print LIST " accept" if( $fl & 0x00000002 );
print LIST "\n";
}
print LIST "\n";
# part_sz
wr_size_fillin( \*BYTE, $part_sz_pos );
# string table
# part_type
print BYTE "str ";
# part_sz
$part_sz_pos = wr_size_tmp( \*BYTE );
# str_cnt
wr_word( \*BYTE, @{$strings} + 0 );
for( $i = 0; $i < @{$strings}; $i++ )
{
my $str = @{$strings}[$i];
if( $str ne undef )
{
wr_string( \*BYTE, $str );
print LIST "!string $i \"" . escape_str( $str ) . "\"\n";
}
else
{
# empty string
wr_string( \*BYTE, "" );
}
}
print LIST "\n";
# part_sz
wr_size_fillin( \*BYTE, $part_sz_pos );
# source location table
# part_type
print BYTE "sloc";
# part_sz
$part_sz_pos = wr_size_tmp( \*BYTE );
# sloc_cnt
wr_word( \*BYTE, @{$srclocs} + 0 );
for( $i = 0; $i < @{$srclocs}; $i++ )
{
my ($addr, $line, $col) = @{@{$srclocs}[$i]};
# srcloc
wr_dword( \*BYTE, $addr );
wr_dword( \*BYTE, $line );
wr_dword( \*BYTE, $col );
printf LIST "!srcloc_addr 0x%08X %d %d\n", $addr, $line, $col;
}
print LIST "\n";
# part_sz
wr_size_fillin( \*BYTE, $part_sz_pos );
# structure information table
# part_type
print BYTE "stin";
# part_sz
$part_sz_pos = wr_size_tmp( \*BYTE );
# stin_cnt
wr_word( \*BYTE, @{$strinfs} + 0 );
for( $i = 0; $i < @{$strinfs}; $i++ )
{
my ($addr, $code, $type, $name) = @{@{$strinfs}[$i]};
# strinf
wr_dword( \*BYTE, $addr );
wr_byte( \*BYTE, $code );
wr_string( \*BYTE, $type );
wr_string( \*BYTE, $name );
printf LIST "!strinf_addr 0x%08X", $addr;
if( $code == 0x00 )
{ print LIST " begin "; }
elsif( $code == 0x01 )
{ print LIST " end "; }
elsif( $code == 0x02 )
{ print LIST " middle "; }
else
{ print LIST " unknown "; }
print LIST $type;
print LIST " $name" if( $name ne "" );
print LIST "\n";
}
print LIST "\n";
# part_sz
wr_size_fillin( \*BYTE, $part_sz_pos );
# end of section
# sec_sz
wr_size_fillin( \*BYTE, $sec_sz_pos );
}
print "done...\n\n";

View file

@ -0,0 +1,148 @@
# NIPS Asm - New Implementation of Promela Semantics Assembler
# Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
# Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
# Lehrstuhl fuer Informatik II, RWTH Aachen
# Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
use strict;
# escape a string
sub escape_str
{
my $str = shift;
$str =~ s/\0/\\0/g;
$str =~ s/\r/\\r/g;
$str =~ s/\n/\\n/g;
$str =~ s/\t/\\t/g;
$str =~ s/"/\\"/g;
$str =~ s/'/\\'/g;
my $i;
for( $i = 1; $i < 32; $i++ )
{
my $c = pack( "C", $i );
my $h = sprintf( "%02X", $i );
$str =~ s/$c/\\x$h/g;
}
return $str;
}
# convert a byte, a word, a dword to binary
sub byte2bin
{
my $value = shift;
return pack( "C", $value & 0xFF );
}
sub word2bin
{
my $value = shift;
return pack( "CC", ($value >> 8) & 0xFF,
$value & 0xFF );
}
sub dword2bin
{
my $value = shift;
return pack( "CCCC", ($value >> 24) & 0xFF,
($value >> 16) & 0xFF,
($value >> 8) & 0xFF,
$value & 0xFF );
}
# convert a byte, a word, a dword from binary
sub bin2byte
{
my @data = unpack( "C", shift );
return @data[0];
}
sub bin2word
{
my @data = unpack( "CC", shift );
return @data[0] << 8 | @data[1];
}
sub bin2dword
{
my @data = unpack( "CCCC", shift );
return @data[0] << 24 | @data[1] << 16 | @data[2] << 8 | @data[3];
}
# write a byte, a word, a dword to a binary file
sub wr_byte
{
my $filehandle = shift;
my $byte = shift;
print $filehandle byte2bin( $byte );
}
sub wr_word
{
my $filehandle = shift;
my $word = shift;
print $filehandle word2bin( $word );
}
sub wr_dword
{
my $filehandle = shift;
my $dword = shift;
print $filehandle dword2bin( $dword );
}
# read a byte, a word, a dword from a binary file
sub rd_byte
{
my $filehandle = shift;
my $data;
read $filehandle, $data, 1;
return bin2byte( $data );
}
sub rd_word
{
my $filehandle = shift;
my $data;
read $filehandle, $data, 2;
return bin2word( $data );
}
sub rd_dword
{
my $filehandle = shift;
my $data;
read $filehandle, $data, 4;
return bin2dword( $data );
}
# write a string to a binary file
sub wr_string
{
my $filehandle = shift;
my $str = shift;
wr_word( $filehandle, length( $str ) + 1 );
print $filehandle $str . "\0";
}
# read a string from a binary file
sub rd_string
{
my $filehandle = shift;
my $str_sz = rd_word( $filehandle );
my $str;
read $filehandle, $str, $str_sz;
$str =~ s/\0.*$//;
return $str;
}
# write size to binary file
sub wr_size_tmp
{
my $filehandle = shift;
my $pos = tell $filehandle;
wr_dword( $filehandle, 0 );
return $pos;
}
sub wr_size_fillin
{
my $filehandle = shift;
my $sz_pos = shift;
my $pos = tell $filehandle;
seek $filehandle, $sz_pos, 0;
wr_dword( $filehandle, $pos - $sz_pos - 4 );
seek $filehandle, $pos, 0;
}
1;

View file

@ -0,0 +1,187 @@
# NIPS (Dis)Asm - New Implementation of Promela Semantics (Dis-)Assembler
# Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
# Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
# Lehrstuhl fuer Informatik II, RWTH Aachen
# Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
use strict;
# instructions
# - array with fixed words (variable number of strings)
# - e.g. instruction name
# - e.g. instruction variant
# - opcode
# - array with parameters
# - const1, const2, const4: constants with 1, 2, 4 bytes
# - addr: relative address expressed by a label
# - address: absolute address
# - reg: a register r0 .. r7
sub get_instructions
{
my ($cflow, $error) = (1, 1);
my @instructions = (
[ 0x00, [ "NOP" ] ],
[ 0x01, [ "LDC", "!const4" ] ],
[ 0x02, [ "LDV", "L", "1u" ] ],
[ 0x03, [ "LDV", "L", "1s" ] ],
[ 0x04, [ "LDV", "L", "2u" ] ],
[ 0x05, [ "LDV", "L", "2s" ] ],
[ 0x06, [ "LDV", "L", "4" ] ],
[ 0x07, [ "LDV", "G", "1u" ] ],
[ 0x08, [ "LDV", "G", "1s" ] ],
[ 0x09, [ "LDV", "G", "2u" ] ],
[ 0x0A, [ "LDV", "G", "2s" ] ],
[ 0x0B, [ "LDV", "G", "4" ] ],
[ 0x0C, [ "STV", "L", "1u" ] ],
[ 0x0D, [ "STV", "L", "1s" ] ],
[ 0x0E, [ "STV", "L", "2u" ] ],
[ 0x0F, [ "STV", "L", "2s" ] ],
[ 0x10, [ "STV", "L", "4" ] ],
[ 0x11, [ "STV", "G", "1u" ] ],
[ 0x12, [ "STV", "G", "1s" ] ],
[ 0x13, [ "STV", "G", "2u" ] ],
[ 0x14, [ "STV", "G", "2s" ] ],
[ 0x15, [ "STV", "G", "4" ] ],
[ 0x16, [ "TRUNC", "!const1" ] ],
[ 0x18, [ "LDS", "timeout" ] ],
[ 0x19, [ "LDS", "pid" ] ],
[ 0x1A, [ "LDS", "nrpr" ] ],
[ 0x1B, [ "LDS", "last" ] ],
[ 0x1C, [ "LDS", "np" ] ],
[ 0x20, [ "ADD" ] ],
[ 0x21, [ "SUB" ] ],
[ 0x22, [ "MUL" ] ],
[ 0x23, [ "DIV" ] ],
[ 0x24, [ "MOD" ] ],
[ 0x25, [ "NEG" ] ],
[ 0x26, [ "NOT" ] ],
[ 0x27, [ "AND" ] ],
[ 0x28, [ "OR" ] ],
[ 0x29, [ "XOR" ] ],
[ 0x2A, [ "SHL" ] ],
[ 0x2B, [ "SHR" ] ],
[ 0x2C, [ "EQ" ] ],
[ 0x2D, [ "NEQ" ] ],
[ 0x2E, [ "LT" ] ],
[ 0x2F, [ "LTE" ] ],
[ 0x30, [ "GT" ] ],
[ 0x31, [ "GTE" ] ],
[ 0x32, [ "BNOT" ] ],
[ 0x33, [ "BAND" ] ],
[ 0x34, [ "BOR" ] ],
[ 0x40, [ "ICHK", "!const1" ], $error ],
[ 0x41, [ "BCHK" ], $error ],
[ 0x48, [ "JMP", "!addr" ], $cflow ],
[ 0x49, [ "JMPZ", "!addr" ], $cflow ],
[ 0x4A, [ "JMPNZ", "!addr" ], $cflow ],
[ 0x4B, [ "LJMP", "!address" ], $cflow ],
[ 0x50, [ "TOP", "!reg" ] ],
[ 0x51, [ "POP", "!reg" ] ],
[ 0x52, [ "PUSH", "!reg" ] ],
[ 0x53, [ "POPX" ] ],
[ 0x54, [ "INC", "!reg" ] ],
[ 0x55, [ "DEC", "!reg" ] ],
[ 0x56, [ "LOOP", "!reg", "!addr" ], $cflow ],
[ 0x58, [ "CALL", "!addr" ], $cflow ],
[ 0x59, [ "RET" ], $cflow ],
[ 0x5A, [ "LCALL", "!address" ], $cflow ],
[ 0x60, [ "CHNEW", "!const1", "!const1" ] ],
[ 0x61, [ "CHMAX" ] ],
[ 0x62, [ "CHLEN" ] ],
[ 0x63, [ "CHFREE" ] ],
[ 0x64, [ "CHADD" ] ],
[ 0x65, [ "CHSET" ] ],
[ 0x66, [ "CHGET" ] ],
[ 0x67, [ "CHDEL" ] ],
[ 0x68, [ "CHSORT" ] ],
[ 0x6B, [ "CHROT" ] ],
[ 0x6C, [ "CHSETO", "!const1" ] ],
[ 0x6D, [ "CHGETO", "!const1" ] ],
[ 0x70, [ "NDET", "!addr" ] ],
[ 0x72, [ "ELSE", "!addr" ] ],
[ 0x73, [ "UNLESS", "!addr" ] ],
[ 0x74, [ "NEX" ], $cflow ],
[ 0x75, [ "NEXZ" ], $cflow ],
[ 0x76, [ "NEXNZ" ], $cflow ],
[ 0x78, [ "STEP", "N", "!const1" ], $cflow ],
[ 0x79, [ "STEP", "A", "!const1" ], $cflow ],
[ 0x7A, [ "STEP", "I", "!const1" ], $cflow ],
[ 0x7B, [ "STEP", "T", "!const1" ], $cflow ],
[ 0x80, [ "RUN", "!const1", "!const1", "!addr" ] ],
[ 0x81, [ "LRUN", "!const1", "!const1", "!address" ] ],
[ 0x84, [ "GLOBSZ", "!const1" ] ],
[ 0x85, [ "LOCSZ", "!const1" ] ],
[ 0x86, [ "GLOBSZX", "!const2" ] ],
[ 0x88, [ "FCLR" ] ],
[ 0x89, [ "FGET", "!const1" ] ],
[ 0x8A, [ "FSET", "!const1" ] ],
[ 0x8C, [ "BGET", "!reg", "!const1" ] ],
[ 0x8D, [ "BSET", "!reg", "!const1" ] ],
[ 0x90, [ "PRINTS", "!const2" ] ],
[ 0x91, [ "PRINTV", "!const1" ] ],
[ 0x92, [ "LDVA", "L", "1u", "!const1" ] ],
[ 0x93, [ "LDVA", "L", "1s", "!const1" ] ],
[ 0x94, [ "LDVA", "L", "2u", "!const1" ] ],
[ 0x95, [ "LDVA", "L", "2s", "!const1" ] ],
[ 0x96, [ "LDVA", "L", "4", "!const1" ] ],
[ 0x97, [ "LDVA", "G", "1u", "!const1" ] ],
[ 0x98, [ "LDVA", "G", "1s", "!const1" ] ],
[ 0x99, [ "LDVA", "G", "2u", "!const1" ] ],
[ 0x9A, [ "LDVA", "G", "2s", "!const1" ] ],
[ 0x9B, [ "LDVA", "G", "4", "!const1" ] ],
[ 0x9C, [ "STVA", "L", "1u", "!const1" ] ],
[ 0x9D, [ "STVA", "L", "1s", "!const1" ] ],
[ 0x9E, [ "STVA", "L", "2u", "!const1" ] ],
[ 0x9F, [ "STVA", "L", "2s", "!const1" ] ],
[ 0xA0, [ "STVA", "L", "4", "!const1" ] ],
[ 0xA1, [ "STVA", "G", "1u", "!const1" ] ],
[ 0xA2, [ "STVA", "G", "1s", "!const1" ] ],
[ 0xA3, [ "STVA", "G", "2u", "!const1" ] ],
[ 0xA4, [ "STVA", "G", "2s", "!const1" ] ],
[ 0xA5, [ "STVA", "G", "4", "!const1" ] ],
[ 0xB0, [ "LDA", "!address" ] ],
[ 0xB4, [ "PCVAL" ] ],
[ 0xB8, [ "LVAR", "1u" ] ],
[ 0xB9, [ "LVAR", "1s" ] ],
[ 0xBA, [ "LVAR", "2u" ] ],
[ 0xBB, [ "LVAR", "2s" ] ],
[ 0xBC, [ "LVAR", "4" ] ],
[ 0xBE, [ "ENAB" ] ],
[ 0xC0, [ "MONITOR" ] ],
[ 0xC4, [ "KILL" ] ],
[ 0xD0, [ "LDB", "L" ] ],
[ 0xD1, [ "LDB", "G" ] ],
[ 0xD2, [ "STB", "L" ] ],
[ 0xD3, [ "STB", "G" ] ],
[ 0xD4, [ "LDV", "L", "2u", "LE" ] ],
[ 0xD5, [ "LDV", "L", "2s", "LE" ] ],
[ 0xD6, [ "LDV", "L", "4", "LE" ] ],
[ 0xD7, [ "LDV", "G", "2u", "LE" ] ],
[ 0xD8, [ "LDV", "G", "2s", "LE" ] ],
[ 0xD9, [ "LDV", "G", "4", "LE" ] ],
[ 0xDA, [ "STV", "L", "2u", "LE" ] ],
[ 0xDB, [ "STV", "L", "2s", "LE" ] ],
[ 0xDC, [ "STV", "L", "4", "LE" ] ],
[ 0xDD, [ "STV", "G", "2u", "LE" ] ],
[ 0xDE, [ "STV", "G", "2s", "LE" ] ],
[ 0xDF, [ "STV", "G", "4", "LE" ] ],
);
return @instructions;
}
sub instruction_cfun {
my ($op, $params) = @_;
my $name = "instr";
for (@{$params}) {
next if /^!/;
if (/^[0-9]/) {
$name .= lc($_);
next;
}
$name .= "_".lc($_);
}
return $name;
}
1;

513
iface/nips/nips_vm/nips_disasm.pl Executable file
View file

@ -0,0 +1,513 @@
#! /usr/bin/perl
# NIPS DisAsm - New Implementation of Promela Semantics (Dis-)Assembler
# Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
# Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
# Lehrstuhl fuer Informatik II, RWTH Aachen
# Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
use strict;
# add directory of script to include directory
my $incdir=$0;
$incdir =~ s/[^\/]*$//;
$incdir = "./" if( $incdir eq "" );
push @INC, $incdir;
require "nips_asm_help.pl";
require "nips_asm_instr.pl";
my @instructions = get_instructions( );
print <<EOF;
NIPS DisAsm - New Implementation of Promela Semantics Dis-Assembler
Copyright (C) 2005: Stefan Schuermans <stefan\@schuermans.info>
Michael Weber <michaelw\@i2.informatik.rwth-aachen.de>
Lehrstuhl fuer Informatik II, RWTH Aachen
Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
EOF
# parse parameters
die "usage: perl nips_disasm.pl <input.b> [<output.d.s>]" if( @ARGV < 1 || @ARGV > 2 );
my ( $file_byte, $file_asm, $file_c ) = ( @ARGV[0], @ARGV[0], @ARGV[0] );
$file_asm =~ s/(\.b)?$/.d.s/;
$file_asm = @ARGV[1] if( @ARGV >= 2 );
$file_c =~ s/(\.b)?$/.c_stub/;
# read input
print "reading input file \"$file_byte\"...\n";
open BYTE, "<".$file_byte or die "could not open input file \"$file_byte\"";
binmode BYTE;
my $m;
read BYTE, $m, 8;
die "input file \"$file_byte\" is not a valid bytecode file" if( $m ne "NIPS v2 " );
my $sec_cnt = rd_word( \*BYTE );
my @modules = ( );
my $sec_no;
for( $sec_no = 0; $sec_no < $sec_cnt; $sec_no++ )
{
# section header
my $sec_type;
read BYTE, $sec_type, 4;
my $sec_sz = rd_dword( \*BYTE );
my $sec_end = (tell BYTE) + $sec_sz;
# module
if( $sec_type eq "mod " )
{
# module name
my $mod_name = rd_string( \*BYTE );
my $modflags = 0;
my $bytecode = "";
my $flags = [];
my $strings = [];
my $srclocs = [];
my $strinfs = [];
my $part_cnt = rd_word( \*BYTE );
my $part_no;
for( $part_no = 0; $part_no < $part_cnt; $part_no++ )
{
# part header
my $part_type;
read BYTE, $part_type, 4;
my $part_sz = rd_dword( \*BYTE );
my $part_end = (tell BYTE) + $part_sz;
# module flags
if( $part_type eq "modf" )
{
$modflags |= rd_dword( \*BYTE );
}
# bytecode
elsif( $part_type eq "bc " )
{
read BYTE, $bytecode, $part_sz;
}
# flag table
elsif( $part_type eq "flag" )
{
my $flag_cnt = rd_word( \*BYTE );
my $flag_no;
$flags = [];
for( $flag_no = 0; $flag_no < $flag_cnt; $flag_no++ )
{
# flag
my $addr = rd_dword( \*BYTE );
my $fl = rd_dword( \*BYTE );
push @{$flags}, [ $addr, $fl ];
}
}
# string table
elsif( $part_type eq "str " )
{
my $str_cnt = rd_word( \*BYTE );
my $str_no;
$strings = [];
for( $str_no = 0; $str_no < $str_cnt; $str_no++ )
{
push @{$strings}, (rd_string( \*BYTE ));
}
}
# source location table
elsif( $part_type eq "sloc" )
{
my $sloc_cnt = rd_word( \*BYTE );
my $sloc_no;
$srclocs = [];
for( $sloc_no = 0; $sloc_no < $sloc_cnt; $sloc_no++ )
{
# source location
my $addr = rd_dword( \*BYTE );
my $line = rd_dword( \*BYTE );
my $col = rd_dword( \*BYTE );
push @{$srclocs}, [ $addr, $line, $col ];
}
}
# structure information table
elsif( $part_type eq "stin" )
{
my $stin_cnt = rd_word( \*BYTE );
my $stin_no;
$strinfs = [];
for( $stin_no = 0; $stin_no < $stin_cnt; $stin_no++ )
{
# structure information
my $addr = rd_dword( \*BYTE );
my $code = rd_byte( \*BYTE );
my $type = rd_string( \*BYTE );
my $name = rd_string( \*BYTE );
push @{$strinfs}, [ $addr, $code, $type, $name ];
}
}
# unknown part
else
{
seek BYTE, $part_sz, 1;
}
die "part $part_no of section $sec_no of input file \"$file_byte\" is corrupt" if( $part_end != (tell BYTE) );
}
push @modules, [ $mod_name, $modflags, $bytecode, $flags, $strings, $srclocs, $strinfs ];
}
# unknown section
else
{
seek BYTE, $sec_sz, 1;
}
die "section $sec_no of input file \"$file_byte\" is corrupt" if( $sec_end != (tell BYTE) );
}
my $mod;
for $mod (@modules)
{
my ($module, $modflags, $bytecode, $flags, $strings, $srclocs, $strinfs) = @{$mod};
my ( $op );
my $addr = 0;
my @bytecodes = ( );
my $pos = 0;
my $len = length( $bytecode );
while( $pos < $len )
{
$op = unpack( "C", substr( $bytecode, $pos, 1 ) );
$pos += 1;
# find instruction in table
my $ok = 0;
my ( $opcode, $params, $cflow );
for (@instructions)
{
( $opcode, $params, $cflow ) = @{$_};
if( $opcode == $op )
{
$ok = 1;
last;
}
}
die "invalid opcode ".sprintf("0x%02X",$op)." at address ".sprintf("0x%08X",$addr) if( ! $ok );
# get parameters
my @bytecode = ( $opcode );
my @parameters = ( );
my $val;
my @values;
my $addr_tmp = $addr + 1;
for (@{$params} )
{
# byte constant
if( $_ eq "!const1" )
{
die "truncated instruction at address ".sprintf("0x%08X",$addr_tmp) if( $pos + 1 > $len );
@values = unpack( "C", substr( $bytecode, $pos, 1 ) );
$pos += 1;
push @bytecode, @values;
$val = @values[0];
$val -= 256 if( $val >= 128 );
push @parameters, $val;
$addr_tmp += 1;
}
# word constant
elsif( $_ eq "!const2" )
{
die "truncated instruction at address ".sprintf("0x%08X",$addr_tmp) if( $pos + 2 > $len );
@values = unpack( "CC", substr( $bytecode, $pos, 2 ) );
$pos += 2;
push @bytecode, @values;
$val = @values[0] << 8 | @values[1];
$val -= 65536 if( $val >= 32768 );
push @parameters, $val;
$addr_tmp += 2;
}
# double-word constant
elsif( $_ eq "!const4" )
{
die "truncated instruction at address ".sprintf("0x%08X",$addr_tmp) if( $pos + 4 > $len );
@values = unpack( "CCCC", substr( $bytecode, $pos, 4 ) );
$pos += 4;
push @bytecode, @values;
$val = @values[0] << 24 | @values[1] << 16 | @values[2] << 8 | @values[3];
$val -= 4294967296 if( $val >= 2147483648 );
push @parameters, $val;
$addr_tmp += 4;
}
# register
elsif( $_ eq "!reg" )
{
die "truncated instruction at address ".sprintf("0x%08X",$addr_tmp) if( $pos + 1 > $len );
@values = unpack( "C", substr( $bytecode, $pos, 1 ) );
$pos += 1;
die "invalid register number ".@values[0]." at address ".sprintf("0x%08X",$addr_tmp) if( @values[0] >= 8 );
push @bytecode, @values;
push @parameters, "r".@values[0];
$addr_tmp += 1;
}
# relative address given by label
elsif( $_ eq "!addr" )
{
die "truncated instruction at address ".sprintf("0x%08X",$addr_tmp) if( $pos + 2 > $len );
@values = unpack( "CC", substr( $bytecode, $pos, 2 ) );
$pos += 2;
push @bytecode, @values;
$val = (@values[0] << 8 | @values[1]);
$val -= 65536 if( $val >= 32768 );
push @parameters, "addr:".$val;
$addr_tmp += 2;
}
# absolute address given by label
elsif( $_ eq "!address" )
{
die "truncated instruction at address ".sprintf("0x%08X",$addr_tmp) if( $pos + 4 > $len );
@values = unpack( "CCCC", substr( $bytecode, $pos, 4 ) );
$pos += 4;
push @bytecode, @values;
$val = (@values[0] << 24 | @values[1] << 16 | @values[2] << 8 | @values[3]);
push @parameters, "address:".$val;
$addr_tmp += 4;
}
# other parmeter type
elsif( $_ =~ /^\!/ )
{
die "internal error: unknown parmeter type \"$_\"\n";
}
# fixed word
else
{
push @parameters, $_;
}
}
# save bytecode
push @bytecodes, [ $addr, "", [ @bytecode ], [ @parameters ], [ @{$params} ], $cflow ];
$addr = $addr_tmp;
}
$mod = [$module, $modflags, [@bytecodes], $flags, $strings, $srclocs, $strinfs];
}
# convert addresses into labels
print "converting addresses to labels...\n";
for $mod (@modules)
{
my ($module, $modflags, $bytecodes, $flags, $strings, $srclocs, $strinfs) = @{$mod};
for (@{$bytecodes})
{
my ( $addr, $label, $bc, $w, $params, $cflow ) = @{$_};
for (@{$w})
{
# word is an address
if( $_ =~ /^addr(ess)?:(-?[0-9]+)$/ )
{
my $rel = $1 eq "";
my $ad = $2;
$ad += $addr + @{$bc} if( $rel ); # convert to absolute address
# find this address
my $wo = "";
for (@{$bytecodes})
{
my ( $addr, $label, $bc, $w, $params, $cflow ) = @{$_};
if( $ad >= $addr && $ad < $addr + @{$bc} )
{
$label = "L_" . sprintf( "%08X", $addr );
$_ = [ $addr, $label, $bc, $w, $params, $cflow ];
$wo = $label;
$wo .= "+" . ($ad - $addr) if( $ad != $addr );
last;
}
}
# update this word
$wo = sprintf( "0x%08X", $ad ) if( $wo eq "" );
$_ = $wo;
}
}
# update this line
$_ = [ $addr, @{$_}[1], $bc, $w, $params, $cflow ]; # keep label (might have been updated in inner loop
}
}
# output disassembled code
print "writing output file \"$file_asm\"...\n";
open ASM, ">".$file_asm or die "could not open output file \"$file_asm\"";
print "writing output file \"$file_c\"...\n";
open CCODE, ">".$file_c or die "could not open output file \"$file_c\"";
print CCODE <<INITIAL_CCODE;
static inline void instr_exec( st_instr_context *p_ctx )
{
t_pc pc_h = be2h_pc( p_ctx->p_proc->proc.pc );
#define INC_PC() p_ctx->p_proc->proc.pc = h2be_pc( pc_h = be2h_pc( p_ctx->p_proc->proc.pc ) + 1 )
// select instruction (and advance program counter)
#ifdef DEBUG_INSTR
printf( "DEBUG (before instr): " ); global_state_print( p_ctx->p_glob );
#endif
switch(pc_h) {
INITIAL_CCODE
for $mod (@modules)
{
my ($module, $modflags, $bytecodes, $flags, $strings, $srclocs, $strinfs) = @{$mod};
# module name
print ASM "!module \"" . escape_str( $module ) . "\"\n\n";
# module flags
print ASM "!modflags monitor\n\n" if( $modflags & 0x00000001 );
# byte code
my $old_cflow = 1;
for (@{$bytecodes})
{
my ( $addr, $label, $bc, $w, $params, $cflow ) = @{$_};
# source code
if( $label ne "" )
{
print ASM "$label:";
my $i;
for( $i = 0; $i < 11 - length( $label ) - 1; $i++ ) { print ASM " "; }
}
else
{
print ASM " ";
}
print ASM " $_" for (@{$w});
print ASM "\n";
printf CCODE " case 0x%x:\n", $addr if $old_cflow or @{$_}[1] ne "";
print CCODE "\tINC_PC();\t\t".instruction_cfun(0, $params)."(p_ctx);\n";
print CCODE "\tbreak;\n" if $cflow;
$old_cflow = $cflow;
}
print ASM "\n";
# flag table
my $i;
for( $i = 0; $i < @{$flags}; $i++ )
{
my ($addr, $fl) = @{@{$flags}[$i]};
printf ASM "!flags_addr 0x%08X", $addr;
print ASM " progress" if( $fl & 0x00000001 );
print ASM " accept" if( $fl & 0x00000002 );
print ASM "\n";
}
print ASM "\n";
# string table
for( $i = 0; $i < @{$strings}; $i++ )
{
my $str = @{$strings}[$i];
print ASM "!string $i \"" . escape_str( $str ) . "\"\n" if( $str ne undef );
}
print ASM "\n";
# source location table
for( $i = 0; $i < @{$srclocs}; $i++ )
{
my ($addr, $line, $col) = @{@{$srclocs}[$i]};
printf ASM "!srcloc_addr 0x%08X %d %d\n", $addr, $line, $col;
}
print ASM "\n";
# structure information table
for( $i = 0; $i < @{$strinfs}; $i++ )
{
my ($addr, $code, $type, $name) = @{@{$strinfs}[$i]};
printf ASM "!strinf_addr 0x%08X", $addr;
if( $code == 0x00 )
{ print ASM " begin "; }
elsif( $code == 0x01 )
{ print ASM " end "; }
elsif( $code == 0x02 )
{ print ASM " middle "; }
else
{ print ASM " unknown "; }
print ASM $type;
print ASM " $name" if( $name ne "" );
print ASM "\n";
}
print ASM "\n";
}
print CCODE <<FINAL_CCODE;
default:
instr_err( p_ctx, IE_BYTECODE );
}
}
FINAL_CCODE
print "done...\n\n";

104
iface/nips/nips_vm/nipsvm.c Normal file
View file

@ -0,0 +1,104 @@
#include <assert.h>
#include "bytecode.h"
#include "instr_step.h"
#include "nipsvm.h"
struct nipsvm_transition_information_t {
uint8_t label[2];
t_flag_reg flags[2];
unsigned int succ_cb_flags;
st_instr_succ_output *p_succ_out;
};
static et_ic_status
simple_scheduler_cb( st_global_state_header *succ,
uint8_t label_1st, uint8_t label,
t_flag_reg flag_reg_1st, t_flag_reg flag_reg,
unsigned int succ_cb_flags,
st_instr_succ_output *p_succ_out,
void *priv_context )
{
nipsvm_t *vm = (nipsvm_t *)priv_context;
nipsvm_transition_information_t transition_info = {
.label = { label_1st, label },
.flags = { flag_reg_1st, flag_reg },
.succ_cb_flags = succ_cb_flags,
.p_succ_out = p_succ_out,
};
return (*vm->callback)(nipsvm_state_size (succ), succ,
&transition_info, vm->callback_context);
}
static et_ic_status
simple_error_cb (et_instr_err err, t_pid pid, t_pc pc, void *priv_context)
{
nipsvm_t *vm = (nipsvm_t *)priv_context;
return (*vm->error_callback)(err, pid, pc, vm->callback_context);
}
extern int
nipsvm_module_init() { return 0; }
extern int
nipsvm_init (nipsvm_t *vm,
nipsvm_bytecode_t *bytecode,
nipsvm_scheduler_callback_t *s_callback,
nipsvm_error_callback_t *e_callback)
{
assert (vm != NULL);
vm->callback = s_callback;
vm->error_callback = e_callback != NULL ? e_callback : nipsvm_default_error_cb;
instr_succ_context_default (&vm->insn_context, bytecode);
vm->insn_context.priv_context = vm;
vm->insn_context.succ_cb = &simple_scheduler_cb;
vm->insn_context.err_cb = &simple_error_cb;
return 0;
}
extern void *
nipsvm_finalize (nipsvm_t *vm)
{
return vm;
}
extern unsigned int
nipsvm_scheduler_iter (nipsvm_t *vm,
nipsvm_state_t *state,
void *callback_context)
{
assert (vm != NULL);
vm->callback_context = callback_context;
return instr_succ (state, 0, &vm->insn_context);
}
extern nipsvm_state_t *
nipsvm_initial_state (nipsvm_t *vm)
{
assert (vm != NULL);
static char initial_state_buf[NIPSVM_INITIAL_STATE_SIZE];
char *buf = initial_state_buf;
unsigned long sz = sizeof initial_state_buf;
return global_state_initial (&buf, &sz);
}
// standard runtime error callback function
extern nipsvm_status_t
nipsvm_default_error_cb (nipsvm_errorcode_t err, nipsvm_pid_t pid,
nipsvm_pc_t pc, void *priv_context)
{
char str[256];
// print runtime error to stderr
nipsvm_errorstring (str, sizeof str, err, pid, pc);
fprintf (stderr, "RUNTIME ERROR: %s\n", str);
// do not try to find successor states
return IC_STOP;
// keep compiler happy
priv_context = NULL;
}

124
iface/nips/nips_vm/nipsvm.h Normal file
View file

@ -0,0 +1,124 @@
#ifndef NIPSVM_H
#define NIPSVM_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <stddef.h>
#include "state.h"
#include "instr.h"
typedef struct nipsvm_t nipsvm_t;
//typedef st_global_state_header nipsvm_state_t; // state.h
typedef struct nipsvm_transition_information_t nipsvm_transition_information_t;
typedef st_bytecode nipsvm_bytecode_t;
typedef et_ic_status nipsvm_status_t;
typedef et_instr_err nipsvm_errorcode_t;
typedef t_pid nipsvm_pid_t;
typedef t_pc nipsvm_pc_t;
typedef nipsvm_status_t (nipsvm_scheduler_callback_t)(size_t, nipsvm_state_t *,
nipsvm_transition_information_t *,
void *context);
typedef nipsvm_status_t (nipsvm_error_callback_t)(nipsvm_errorcode_t, nipsvm_pid_t,
nipsvm_pc_t, void *context);
/* must be called once before using any other function of this
* module.
* Idempotent.
*/
extern int
nipsvm_module_init();
/* Initialize given VM structure (allocated elsewhere).
* Returns 0 on success.
*
* Usage pattern:
* { nipsvm_t vm;
* if (nipsvm_init (&vm, ...) != 0) error();
* ...
* nipsvm_finalize (&vm); }
*/
extern int
nipsvm_init (nipsvm_t *, nipsvm_bytecode_t *,
nipsvm_scheduler_callback_t *, nipsvm_error_callback_t *);
extern void *
nipsvm_finalize (nipsvm_t *);
/* returned initial state is transient, must be copied by caller */
extern nipsvm_state_t *
nipsvm_initial_state (nipsvm_t *);
/* calculates successor states of ``state'' and calls callback
* previously registered in ``vm'' for each.
* ``context'' is given to the callback verbatim.
*/
extern unsigned int
nipsvm_scheduler_iter (nipsvm_t *vm, nipsvm_state_t *state, void *context);
static inline size_t
nipsvm_state_size (nipsvm_state_t *state) { return global_state_size (state); }
// Copy VM state into given buffer
// *pp_buf points to memory area of length *p_len to use for new state
// Returns NULL in case of error
extern nipsvm_state_t *
nipsvm_state_copy (size_t sz, nipsvm_state_t *p_glob, char **pp_buf,
unsigned long *p_buf_len);
// Compare two states
// Returns 0 if equal, -1 or 1 if not equal
static inline int
nipsvm_state_compare (nipsvm_state_t *p_glob1, nipsvm_state_t *p_glob2, size_t size)
{
return memcmp (p_glob1, p_glob2, size);
}
// Check if the monitor process is in an accepting state
// Returns 0 if no monitor exists or it is not in an accepting state
// Returns 1 if a monitor exists and is in an accpeting state
extern int
nipsvm_state_monitor_accepting (nipsvm_state_t *);
// Check if the monitor process is terminated
// Returns 0 if no monitor exists or it is not terminated
// Returns 1 if a monitor exists and is in terminated
extern int
nipsvm_state_monitor_terminated (nipsvm_state_t *);
// Check if the monitor process is in an accepting state or terminated
// (This is the equivalent function of SPIN's accepting states in the never claim.)
// Returns 0 if no monitor exists or it is not in an accepting state and not terminated
// Returns 1 if a monitor exists and is in an accpeting state or terminated
extern int
nipsvm_state_monitor_acc_or_term (nipsvm_state_t *state);
extern int
nipsvm_errorstring (char *str, size_t size, nipsvm_errorcode_t err,
nipsvm_pid_t pid, nipsvm_pc_t pc);
extern nipsvm_error_callback_t nipsvm_default_error_cb;
/* Internal */
struct nipsvm_t {
nipsvm_scheduler_callback_t *callback;
nipsvm_error_callback_t *error_callback;
void *callback_context;
st_instr_succ_context insn_context;
};
static inline size_t
sizeof_nipsvm_t() { return sizeof (nipsvm_t); }
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View file

@ -0,0 +1,19 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdlib.h>
#include "rt_err.h"
// generate a runtime error and abort program
void rt_err( char *err_msg ) // extern
{
fprintf( stderr, "RUNTIME ERROR: %s\n", err_msg );
exit( -1 );
}

View file

@ -0,0 +1,27 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_rt_err
#define INC_rt_err
#ifdef __cplusplus
extern "C"
{
#endif
// generate a runtime error and abort program
extern void rt_err( char *err_msg );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_rt_err

346
iface/nips/nips_vm/search.c Normal file
View file

@ -0,0 +1,346 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "timeval.h"
#include "rt_err.h"
#include "hashtab.h"
#include "search.h"
// context for depth-first search
typedef struct t_search_context
{
unsigned int depth_max; // maximum depth that may be reached (only for depth-first search)
unsigned int depth_reached; // maximum depth that was reached (only for depth-first search)
unsigned long state_cnt; // number of states visited
unsigned long transition_cnt; // number of transitions
unsigned long n_atomic_steps; // number of atomic steps
unsigned int max_state_size; // maximum state size seen
char *p_buf; // pointer to current position in state buffer
unsigned long buf_len; // number of bytes left in buffer
t_hashtab hashtab; // hash table to find duplicate states
int error; // set in case of an error
nipsvm_t nipsvm; // context for successor state generation
FILE *graph_out; // stream to output state graph to
nipsvm_state_t *graph_out_pred; // pointer to predecessor state (for graph output)
int print_hex; // if to print states in hex
} st_search_context;
// print a state in hex
static void search_print_hex( nipsvm_state_t *state )
{
int size = nipsvm_state_size( state );
int i;
for( i = 0; i < size; i++ )
printf( "%02X", (unsigned char)((char *)state)[i] );
}
// successor callback function
nipsvm_status_t
search_succ_cb (size_t succ_sz, nipsvm_state_t *succ,
nipsvm_transition_information_t *t_info,
void *priv_context)
{
st_search_context *ctx = (st_search_context *)priv_context;
nipsvm_state_t **pos, *state;
int result;
++ctx->transition_cnt;
if (succ->excl_pid != 0) {
++ctx->n_atomic_steps;
}
// output edge of graph
if( ctx->graph_out != NULL )
{
fprintf( ctx->graph_out, "\"" );
global_state_print_ex( ctx->graph_out, ctx->graph_out_pred, 1 );
fprintf( ctx->graph_out, "\" -> \"" );
global_state_print_ex( ctx->graph_out, succ, 1 );
fprintf( ctx->graph_out, "\";\n" );
}
//print state in hex
if( ctx->print_hex )
{
printf( " " );
search_print_hex( succ );
}
// get position for/of state in hash table
result = hashtab_get_pos( ctx->hashtab, succ_sz, succ, &pos );
if( result < 0 )
{
fprintf( stderr, "RUNTIME ERROR: unresolvable hash conflict (try \"-h\")\n" );
ctx->error = 1;
return IC_STOP;
}
// state already in hash table
if( result == 0 )
return IC_CONTINUE;
// copy successor state into buffer
state = nipsvm_state_copy( succ_sz, succ, &ctx->p_buf, &ctx->buf_len );
if( state == NULL )
{
fprintf( stderr, "RUNTIME ERROR: out of state memory (try \"-b\")\n" );
ctx->error = 1;
return IC_STOP;
}
// put state into hash table
*pos = state;
// go on finding successor states
return IC_CONTINUE;
// keep compiler happy
t_info = NULL;
}
// runtime error callback function
nipsvm_status_t
search_err_cb (nipsvm_errorcode_t err, nipsvm_pid_t pid, nipsvm_pc_t pc, void *priv_context) // extern
{
st_search_context *ctx = (st_search_context *)priv_context;
char str[256];
// print runtime error to stderr
nipsvm_errorstring( str, sizeof str, err, pid, pc );
fprintf( stderr, "RUNTIME ERROR: %s\n", str );
// remember error and stop
ctx->error = 1;
return IC_STOP;
}
// depth-first search in state space
static void
search_internal_dfs_to_depth (nipsvm_state_t *state,
unsigned int depth_cur,
st_search_context *ctx)
{
char *start, *end;
unsigned int state_size;
// check maximum depth
if( depth_cur > ctx->depth_max )
return;
// update reached depth
if( depth_cur > ctx->depth_reached )
ctx->depth_reached = depth_cur;
// count states
ctx->state_cnt++;
// generate successor states
start = ctx->p_buf; // remember pointer to start of successor states
if( ctx->print_hex )
{
search_print_hex( state );
printf( " ->" );
}
nipsvm_scheduler_iter (&ctx->nipsvm, state, ctx);
if( ctx->print_hex )
printf( "\n" );
end = ctx->p_buf; // remember pointer behind end of successor states
// continue with depth first search
while( start < end && ! ctx->error ) // abort on error
{
ctx->graph_out_pred = (nipsvm_state_t *)start;
search_internal_dfs_to_depth( (nipsvm_state_t *)start, depth_cur + 1, ctx );
state_size = nipsvm_state_size( (nipsvm_state_t *)start );
start += state_size;
// remember maximum state size
if( state_size > ctx->max_state_size )
ctx->max_state_size = state_size;
}
}
// breadth-first search in state space
static void
search_internal_bfs (nipsvm_state_t *state, st_search_context *ctx)
{
size_t state_size;
// as long as there is a state
while( (char *)state < ctx->p_buf && ! ctx->error ) // abort on error
{
// count states
ctx->state_cnt++;
// generate successor states
ctx->graph_out_pred = state;
if( ctx->print_hex )
{
search_print_hex( state );
printf( " ->" );
}
nipsvm_scheduler_iter (&ctx->nipsvm, state, ctx);
if( ctx->print_hex )
printf( "\n" );
// state is processed (jump over it)
state_size = nipsvm_state_size (state);
state = (nipsvm_state_t *)((char*)state + state_size);
// remember maximum state size
if( state_size > ctx->max_state_size )
ctx->max_state_size = state_size;
}
}
// do a depth-first or breadth-first search in state space up to specified depth
// bfs: boolean flag if to do a breadth-first seach (depth_max is ignored in this case)
// buffer_len: size of state buffer
// hash_entries, hash_retries: parameters of hash table to finf duplicate states
// graph_out: if != NULL state graph will be output to this tream in graphviz dot format
// print_hex: if to print states in hex
// ini_state: state to start simulation at (or NULL to use default)
void
search (nipsvm_bytecode_t *bytecode, int bfs, unsigned int depth_max, unsigned long buffer_len,
unsigned long hash_entries, unsigned long hash_retries, FILE *graph_out, int print_hex,
nipsvm_state_t *ini_state) // extern
{
char *p_buffer, *p_buf;
t_hashtab hashtab;
unsigned long buf_len;
nipsvm_state_t *state;
st_search_context ctx;
struct timeval time_start, time_end, time_diff;
double us_state;
// allocate buffer for states
p_buffer = (char *)malloc( buffer_len );
if( p_buffer == NULL )
rt_err( "out of memory (state buffer for search)" );
p_buf = p_buffer;
buf_len = buffer_len;
// allocate hash table
hashtab = hashtab_new( hash_entries, hash_retries );
if( hashtab == NULL )
rt_err( "out of memory (hash table)" );
// tell user to wait
if( bfs )
printf( "doing breadth-first search, please wait...\n" );
else
printf( "doing depth-first search up to depth %u, please wait...\n", depth_max );
// remember start time
gettimeofday( &time_start, NULL );
// set up context for successor state generation
nipsvm_init (&ctx.nipsvm, bytecode, &search_succ_cb, &search_err_cb);
// get initial state
if( ini_state == NULL ) {
ini_state = nipsvm_initial_state (&ctx.nipsvm);
}
// insert initial state into table
{
size_t sz = nipsvm_state_size (ini_state);
state = nipsvm_state_copy (sz, ini_state, &p_buf, &buf_len);
hashtab_insert (hashtab, sz, ini_state);
}
// set up rest of context for search
ctx.depth_max = depth_max;
ctx.depth_reached = 0;
ctx.state_cnt = 0;
ctx.transition_cnt = 0;
ctx.n_atomic_steps = 0;
ctx.max_state_size = 0;
ctx.p_buf = p_buf;
ctx.buf_len = buf_len;
ctx.hashtab = hashtab;
ctx.error = 0;
ctx.graph_out = graph_out;
ctx.graph_out_pred = state;
ctx.print_hex = print_hex;
// start search
if( bfs )
search_internal_bfs( state, &ctx );
else
search_internal_dfs_to_depth( state, 0, &ctx );
nipsvm_finalize (&ctx.nipsvm);
// update pointer and length of state buffer
p_buf = ctx.p_buf;
buf_len = ctx.buf_len;
// remember end time
gettimeofday( &time_end, NULL );
// get time difference
time_diff.tv_usec = time_end.tv_usec - time_start.tv_usec;
time_diff.tv_sec = time_end.tv_sec - time_start.tv_sec;
if( time_diff.tv_usec < 0 )
{
time_diff.tv_usec += 1000000;
time_diff.tv_sec--;
}
// get time per state (in microseconds)
us_state = ((double)time_diff.tv_sec * 1000000.0 + (double)time_diff.tv_usec)
/ (double)ctx.state_cnt;
// print statistics
table_statistics_t t_stats;
table_statistics (hashtab, &t_stats);
unsigned long used_buffer_space = buffer_len - buf_len;
double table_fill_ratio = 100.0 * (double)t_stats.entries_used / (double)t_stats.entries_available;
double state_memory_fill_ratio = 100.0 * (double)used_buffer_space / (double)buffer_len;
printf( "\n" );
if( bfs )
printf( "breadth-first search %s:\n", ctx.error ? "aborted" : "completed" );
else
printf( "depth-first search up to depth %u %s:\n", depth_max, ctx.error ? "aborted" : "completed" );
printf( " states visited: %lu\n", ctx.state_cnt );
printf( " transitions visited: %lu\n", ctx.transition_cnt );
printf( " atomic steps: %lu\n", ctx.n_atomic_steps );
printf( " maximum state size: %u\n", ctx.max_state_size );
if( ! bfs )
printf( " reached depth: %u\n", ctx.depth_reached );
printf( " total time: %lu.%06lu s\n"
" time per state: %f us\n", time_diff.tv_sec,
(unsigned long)time_diff.tv_usec, us_state );
printf( "\n" );
printf( "state memory statistics:\n" );
printf( " total: %0.2fMB\n", buffer_len / 1048576.0 );
printf( " used: %0.2fMB (%0.1f%%)\n", used_buffer_space / 1048576.0,
state_memory_fill_ratio );
printf( "\n" );
printf( "state table statistics:\n"
" memory usage: %0.2fMB\n", t_stats.memory_size / 1048576.0 );
printf( " buckets used/available: %lu/%lu (%0.1f%%)\n", t_stats.entries_used,
t_stats.entries_available, table_fill_ratio );
printf( " max. no. of retries: %lu\n", t_stats.max_retries );
printf( " conflicts (resolved): %lu\n", t_stats.conflicts );
// free state buffer and hash table
free( p_buffer );
hashtab_free( hashtab );
}

View file

@ -0,0 +1,37 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_search
#define INC_search
#include "nipsvm.h"
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
// do a depth-first or breadth-first search in state space up to specified depth
// bfs: boolean flag if to do a breadth-first seach (depth_max is ignored in this case)
// buffer_len: size of state buffer
// hash_entries, hash_retries: parameters of hash table to finf duplicate states
// graph_out: if != NULL state graph will be output to this tream in graphviz dot format
// print_hex: if to print states in hex
// ini_state: state to start simulation at (or NULL to use default)
extern void search( nipsvm_bytecode_t *bytecode, int bfs, unsigned int depth_max, unsigned long buffer_len,
unsigned long hash_entries, unsigned long hash_retries, FILE *graph_out, int print_hex,
st_global_state_header *ini_state );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_search

105
iface/nips/nips_vm/split.c Normal file
View file

@ -0,0 +1,105 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <string.h>
#include "split.h"
#include "state_parts.h"
#include "tools.h"
#include "nipsvm.h"
// out context for splitting up the state and reassembling ist
typedef struct t_split_ctx
{
char *parts[512];
unsigned int part_cnt;
char *p_buf;
unsigned int buf_len;
unsigned int parts_size;
unsigned int restore_cnt;
} st_split_ctx;
// callback to save a part
void part_cb( char *p_part, unsigned int part_size, void *vp_ctx )
{
st_split_ctx *p_ctx = (st_split_ctx *)vp_ctx;
if( p_ctx->part_cnt < count( p_ctx->parts ) && // free pointer storage
part_size <= p_ctx->buf_len ) // free buffer space
{
memcpy( p_ctx->p_buf, p_part, part_size ); // copy part
p_ctx->parts[p_ctx->part_cnt] = p_ctx->p_buf; // save pointer
p_ctx->part_cnt++; // count pointers
p_ctx->p_buf += part_size; // reduce buffer space
p_ctx->buf_len -= part_size;
p_ctx->parts_size += part_size; // count total size
}
}
// callbacks to deliver parts for restoring state
unsigned int restore_cb( char *p_buf, unsigned int buf_len, void *vp_ctx, unsigned int (*part_size)( char * ) )
{
st_split_ctx *p_ctx = (st_split_ctx *)vp_ctx;
if( p_ctx->restore_cnt >= p_ctx->part_cnt ) // no more parts
return 0;
unsigned int sz = part_size( p_ctx->parts[p_ctx->restore_cnt] ); // get size
if( buf_len < sz ) // no more buffer space
return 0;
memcpy( p_buf, p_ctx->parts[p_ctx->restore_cnt], sz ); // copy part
p_ctx->restore_cnt++; // next part
return sz;
}
unsigned int glob_cb( char *p_buf, unsigned int buf_len, void *vp_ctx )
{
return restore_cb( p_buf, buf_len, vp_ctx, glob_part_size );
}
unsigned int proc_cb( char *p_buf, unsigned int buf_len, void *vp_ctx )
{
return restore_cb( p_buf, buf_len, vp_ctx, proc_part_size );
}
unsigned int chan_cb( char *p_buf, unsigned int buf_len, void *vp_ctx )
{
return restore_cb( p_buf, buf_len, vp_ctx, chan_part_size );
}
// split a state and recreate it from its parts - then compare it
// - used to test state_parts
// - outputs some info if stream != NULL
// - returns boolean flag if success
int split_test( st_global_state_header * p_glob, FILE * stream ) // extern
{
char buffer[65536]; // some memory to store the parts of the state
st_split_ctx ctx;
ctx.part_cnt = 0;
ctx.p_buf = buffer;
ctx.buf_len = sizeof( buffer );
ctx.parts_size = 0;
ctx.restore_cnt = 0;
// split up state
state_parts( p_glob, part_cb, part_cb, part_cb, &ctx );
if( stream != NULL )
fprintf( stream, "splitted state into %d parts with total size %d\n",
ctx.part_cnt, ctx.parts_size );
// restore state
char restore[65536];
st_global_state_header *p_restored = state_restore( restore, sizeof( restore ), glob_cb, proc_cb, chan_cb, &ctx );
if( p_restored == NULL )
return 0;
// compare
int ok = nipsvm_state_compare( p_glob, p_restored, nipsvm_state_size (p_glob) ) == 0;
if( stream != NULL )
fprintf( stream, "comparing restored state with original one: %s\n",
ok ? "identical" : "DIFFERENT" );
return ok;
}

View file

@ -0,0 +1,33 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_split
#define INC_split
#ifdef __cplusplus
extern "C"
{
#endif
#include "state.h"
// split a state and recreate it from its parts - then compare it
// - used to test state_parts
// - outputs some info if stream != NULL
// - returns boolean flag if success
extern int split_test( st_global_state_header * p_glob, FILE * stream );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_split

646
iface/nips/nips_vm/state.c Normal file
View file

@ -0,0 +1,646 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "tools.h"
#include "state.h"
#include "nipsvm.h"
#define STATE_OUT_DOTTY_LINE_END "\\l\\"
// import functions as "extern" for unoptimized compilation
#define STATE_INLINE extern
#include "state_inline.h"
#undef STATE_INLINE
// *** helper functions ***
// allocate memory of size sz in supplied buffer
// *pp_buf points to memory area of length *p_len to use for allocation
// NULL is returned in case of insufficient memory
static inline char * state_alloc( unsigned int sz, char **pp_buf, unsigned long *p_buf_len )
{
if( *p_buf_len < sz ) return NULL;
char *p_new = *pp_buf;
*pp_buf += sz;
*p_buf_len -= sz;
memset( p_new, 0, sz ); // needed because of gaps in structures caused by alignment
return p_new;
}
// *** functions visible from outside ***
// generate initial state
// *pp_buf points to memory area of length *p_len to use for new state
// NULL is returned in case of error
st_global_state_header * global_state_initial( char **pp_buf, unsigned long *p_buf_len ) // extern
{
st_global_state_header *p_glob = (st_global_state_header *)state_alloc( global_state_initial_size, pp_buf, p_buf_len );
if( p_glob == NULL ) return NULL;
p_glob->gvar_sz = h2be_16( 0 ); // global header
p_glob->proc_cnt = 1;
p_glob->excl_pid = h2be_pid( 0 );
p_glob->monitor_pid = h2be_pid( 0 );
p_glob->chan_cnt = 0;
st_process_header *p_proc = (st_process_header *)((char *)p_glob + sizeof( st_global_state_header )); // process header
p_proc->pid = h2be_pid( 1 );
p_proc->flags = 0;
p_proc->lvar_sz = 0;
p_proc->pc = h2be_pc( 0 );
return p_glob;
}
nipsvm_state_t *
nipsvm_state_copy (size_t sz, nipsvm_state_t *p_glob, char **pp_buf,
unsigned long *p_buf_len)
{
nipsvm_state_t *p_glob_new = (nipsvm_state_t *)state_alloc (sz, pp_buf, p_buf_len);
if( p_glob_new == NULL ) return NULL;
memcpy (p_glob_new, p_glob, sz); // simply copy
return p_glob_new;
}
/* DEPRECATED */
st_global_state_header * global_state_copy( st_global_state_header *p_glob,
char **pp_buf, unsigned long *p_buf_len ) // extern
{
size_t sz = nipsvm_state_size( p_glob );
return nipsvm_state_copy (sz, p_glob, pp_buf, p_buf_len);
}
// get copy of global state with resized global variables
// *pp_buf points to memory area of length *p_len to use for new state
st_global_state_header * global_state_copy_gvar_sz( st_global_state_header *p_glob,
uint16_t gvar_sz,
char **pp_buf, unsigned long *p_buf_len ) // extern
{
unsigned int sz = nipsvm_state_size( p_glob ) - be2h_16( p_glob->gvar_sz ) + gvar_sz; // allocate
st_global_state_header *p_glob_new = (st_global_state_header *)state_alloc( sz, pp_buf, p_buf_len );
if( p_glob_new == NULL ) return NULL;
char *src = (char *)p_glob;
char *dest = (char *)p_glob_new;
memcpy( dest, src, sizeof( st_global_state_header ) ); // header
p_glob_new->gvar_sz = h2be_16( gvar_sz );
dest += sizeof( st_global_state_header );
src += sizeof( st_global_state_header );
sz -= sizeof( st_global_state_header );
memset( dest, 0, gvar_sz ); // variables
memcpy( dest, src, min( gvar_sz, be2h_16( p_glob->gvar_sz ) ) );
dest += gvar_sz;
src += be2h_16( p_glob->gvar_sz );
sz -= gvar_sz;
memcpy( dest, src, sz ); // rest of state
return p_glob_new;
}
// get copy of global state with resized local variables
// *pp_buf points to memory area of length *p_len to use for new state
st_global_state_header * global_state_copy_lvar_sz( st_global_state_header *p_glob,
st_process_header *p_proc, uint8_t lvar_sz,
char **pp_buf, unsigned long *p_buf_len ) // extern
{
size_t sz = nipsvm_state_size( p_glob ) - p_proc->lvar_sz + lvar_sz; // allocate
st_global_state_header *p_glob_new = (st_global_state_header *)state_alloc( sz, pp_buf, p_buf_len );
if( p_glob_new == NULL ) return NULL;
char *src = (char *)p_glob;
char *dest = (char *)p_glob_new;
unsigned int len = (char *)p_proc - (char *)p_glob; // size before process
st_process_header *p_proc_new = (st_process_header *)((char*)p_glob_new + len); // pointer to process in new state
if( p_proc->flags & PROCESS_FLAGS_ACTIVE ) // size of process header
len += sizeof( st_process_active_header );
else
len += sizeof( st_process_header );
memcpy( dest, src, len ); // part before process and process header
p_proc_new->lvar_sz = lvar_sz;
dest += len;
src += len;
sz -= len;
memset( dest, 0, lvar_sz ); // variables
memcpy( dest, src, min( lvar_sz, p_proc->lvar_sz ) );
dest += lvar_sz;
src += p_proc->lvar_sz;
sz -= lvar_sz;
memcpy( dest, src, sz ); // rest of state
return p_glob_new;
}
// get copy of global state with selected process activated
// *pp_buf points to memory area of length *p_len to use for new state
// *pp_proc is filled with the pointer to the activated process
// NULL is returned in case of error
st_global_state_header * global_state_copy_activate( st_global_state_header *p_glob, st_process_header *p_proc,
uint8_t stack_max, t_flag_reg flag_reg,
char **pp_buf, unsigned long *p_buf_len,
st_process_active_header **pp_proc ) // extern
{
size_t sz = nipsvm_state_size( p_glob ) // allocate
- sizeof( st_process_header )
+ sizeof( st_process_active_header )
+ stack_max * sizeof( t_val );
st_global_state_header *p_glob_new = (st_global_state_header *)state_alloc( sz, pp_buf, p_buf_len );
if( p_glob_new == NULL ) return NULL;
char *src = (char *)p_glob;
char *dest = (char *)p_glob_new;
unsigned int len = (char *)p_proc - (char *)p_glob; // part before process
memcpy( dest, src, len );
dest += len;
src += len;
sz -= len;
*pp_proc = (st_process_active_header *)dest; // return pointer to activated process
memcpy( dest, src, sizeof( st_process_header ) ); // process header
memset( ((st_process_active_header *)dest)->registers, 0, sizeof( ((st_process_active_header *)dest)->registers ) );
((st_process_active_header *)dest)->flag_reg = flag_reg;
((st_process_active_header *)dest)->proc.flags |= PROCESS_FLAGS_ACTIVE;
((st_process_active_header *)dest)->stack_cur = 0;
((st_process_active_header *)dest)->stack_max = stack_max;
dest += sizeof( st_process_active_header );
src += sizeof( st_process_header );
sz -= sizeof( st_process_active_header );
memcpy( dest, src, p_proc->lvar_sz ); // local variables
dest += p_proc->lvar_sz;
src += p_proc->lvar_sz;
sz -= p_proc->lvar_sz;
len = stack_max * sizeof( t_val ); // stack
memset( dest, 0, len );
dest += len;
sz -= len;
memcpy( dest, src, sz ); // rest of state
return p_glob_new;
}
// get copy of global state with an additional process
// *pp_buf points to memory area of length *p_len to use for new state
// *pp_proc is filled with the pointer to the new process
// NULL is returned in case of error
st_global_state_header * global_state_copy_new_process( st_global_state_header *p_glob,
t_pid new_pid, uint8_t lvar_sz,
char **pp_buf, unsigned long *p_buf_len,
st_process_header **pp_proc ) // extern
{
size_t sz = nipsvm_state_size( p_glob ) // allocate
+ sizeof( st_process_header )
+ lvar_sz;
st_global_state_header *p_glob_new = (st_global_state_header *)state_alloc( sz, pp_buf, p_buf_len );
if( p_glob_new == NULL ) return NULL;
char *src = (char *)p_glob;
char *dest = (char *)p_glob_new;
unsigned int len = (char *)global_state_get_channels( p_glob ) - (char *)p_glob; // part to end of last process
memcpy( dest, src, len );
p_glob_new->proc_cnt++;
dest += len;
src += len;
sz -= len;
*pp_proc = (st_process_header *)dest; // new process
(*pp_proc)->pid = h2be_pid( new_pid );
(*pp_proc)->flags = 0;
(*pp_proc)->lvar_sz = lvar_sz;
(*pp_proc)->pc = h2be_pc( 0 );
dest += sizeof( st_process_header );
sz -= sizeof( st_process_header );
memset( dest, 0, lvar_sz );
dest += lvar_sz;
sz -= lvar_sz;
memcpy( dest, src, sz ); // rest of state
return p_glob_new;
}
// get copy of global state with an additional channel
// *pp_buf points to memory area of length *p_len to use for new state
// the new channel is inserted at the right place according to its channel id
// *pp_chan is filled with the pointer to the new process
// NULL is returned in case of error
st_global_state_header * global_state_copy_new_channel( st_global_state_header *p_glob,
t_chid new_chid, uint8_t max_len, uint8_t type_len, uint8_t msg_len,
char **pp_buf, unsigned long *p_buf_len,
st_channel_header **pp_chan ) // extern
{
size_t len = nipsvm_state_size( p_glob );
char * ptr = global_state_get_channels( p_glob ); // find place to insert new channel
int i;
for( i = 0; i < (int)p_glob->chan_cnt; i++ )
{
if( be2h_chid( ((st_channel_header *)ptr)->chid ) == new_chid ) // duplicate chid -> error
return NULL;
if( be2h_chid( ((st_channel_header *)ptr)->chid ) > new_chid ) // place found
break;
ptr += channel_size( (st_channel_header *)ptr );
}
unsigned int len_before = ptr - (char *)p_glob; // get size of part before new channel
unsigned int len_chan = sizeof( st_channel_header ) // size of channel
+ type_len
+ max( 1, max_len ) * msg_len;
unsigned int sz = len + len_chan;
st_global_state_header *p_glob_new = (st_global_state_header *)state_alloc( sz, pp_buf, p_buf_len ); // allocate new state
if( p_glob_new == NULL ) return NULL;
char *src = (char *)p_glob;
char *dest = (char *)p_glob_new;
memcpy( dest, src, len_before ); // first part of old state
p_glob_new->chan_cnt++;
dest += len_before;
src += len_before;
sz -= len_before;
*pp_chan = (st_channel_header *)dest; // new channel
(*pp_chan)->chid = h2be_chid( new_chid );
(*pp_chan)->max_len = max_len;
(*pp_chan)->cur_len = 0;
(*pp_chan)->msg_len = msg_len;
(*pp_chan)->type_len = type_len;
dest += sizeof( st_channel_header );
sz -= sizeof( st_channel_header );
len_chan -= sizeof( st_channel_header );
memset( dest, 0, len_chan ); // types and content of new channel
dest += len_chan;
sz -= len_chan;
memcpy( dest, src, sz ); // rest of state
return p_glob_new;
}
// deactivate selected process in global state
void global_state_deactivate( st_global_state_header *p_glob, st_process_active_header *p_proc ) // extern
{
size_t sz = nipsvm_state_size( p_glob )
+ sizeof( st_process_header )
- sizeof( st_process_active_header )
- p_proc->stack_max * sizeof( t_val );
char *src = (char *)p_glob; // no new allocation must be done
char *dest = src; // because state becomes always smaller here
unsigned int len = (char *)p_proc - (char *)p_glob; // part before process
dest += len;
src += len;
sz -= len;
((st_process_header *)dest)->flags &= ~PROCESS_FLAGS_ACTIVE; // process header
dest += sizeof( st_process_header );
src += sizeof( st_process_active_header );
sz -= sizeof( st_process_header );
uint8_t lvar_sz = p_proc->proc.lvar_sz; // get size of local variables and stack
uint8_t stack_max = p_proc->stack_max;
memmove( dest, src, lvar_sz ); // local variables
dest += lvar_sz;
src += lvar_sz;
sz -= lvar_sz;
src += stack_max * sizeof( t_val ); // stack
memmove( dest, src, sz ); // rest of state
}
// remove selected process in global state
void global_state_remove( st_global_state_header *p_glob, st_process_header *p_proc ) // extern
{
unsigned int proc_sz = process_size( p_proc );
size_t sz = nipsvm_state_size( p_glob ) - proc_sz;
char *src = (char *)p_glob; // no new allocation must be done
char *dest = src; // because state becomes always smaller here
p_glob->proc_cnt--; // one process less
unsigned int len = (char *)p_proc - (char *)p_glob; // part before process
dest += len;
src += len;
sz -= len;
src += proc_sz; // remove process
memmove( dest, src, sz ); // rest of state
}
// count enabled processes (i.e. processes that are not yet terminated)
// returns number of processes
unsigned int global_state_count_enabled_processes( st_global_state_header *p_glob ) // extern
{
char *ptr = global_state_get_processes( p_glob );
unsigned int i, cnt = 0;
for( i = 0; i < p_glob->proc_cnt; i++ )
{
if( (((st_process_header *)ptr)->flags & PROCESS_FLAGS_MODE) != PROCESS_FLAGS_MODE_TERMINATED )
cnt++;
ptr += process_size( (st_process_header *)ptr );
}
return cnt;
}
// get enabled processes (i.e. processes that may be activated)
// returns number of processes or (unsigned int)-1 if there are too many enabled processes
unsigned int global_state_get_enabled_processes( st_global_state_header *p_glob, st_process_header **p_procs, unsigned int proc_max ) // extern
{
char *ptr = global_state_get_processes( p_glob );
unsigned int i, cnt = 0;
for( i = 0; i < p_glob->proc_cnt; i++ )
{
if( (((st_process_header *)ptr)->flags & PROCESS_FLAGS_MODE) != PROCESS_FLAGS_MODE_TERMINATED )
{
if( cnt >= proc_max ) return (unsigned int)-1;
p_procs[cnt++] = (st_process_header *)ptr;
}
ptr += process_size( (st_process_header *)ptr );
}
return cnt;
}
extern int
nipsvm_state_monitor_accepting (nipsvm_state_t *p_glob)
{
st_process_header *p_monitor = global_state_get_process( p_glob, be2h_pid( p_glob->monitor_pid ) );
if (p_monitor == NULL)
return 0;
return (p_monitor->flags & PROCESS_FLAGS_MONITOR_ACCEPT) != 0; // force result 0 or 1
}
extern int
nipsvm_state_monitor_terminated (nipsvm_state_t *p_glob)
{
// check that there is a monitor process (or at least a pid of some former monitor process)
t_pid monitor_pid = be2h_pid( p_glob->monitor_pid );
if( monitor_pid == 0 ) // no monitor
return 0;
// get monitor process
st_process_header *p_monitor = global_state_get_process( p_glob, monitor_pid );
// return if monitor not available or terminated
return p_monitor == NULL
|| (p_monitor->flags & PROCESS_FLAGS_MODE) == PROCESS_FLAGS_MODE_TERMINATED;
}
extern int
nipsvm_state_monitor_acc_or_term (nipsvm_state_t *p_glob)
{
// check that there is a monitor process (or at least a pid of some former monitor process)
t_pid monitor_pid = be2h_pid( p_glob->monitor_pid );
if( monitor_pid == 0 ) // no monitor
return 0;
// get monitor process
st_process_header *p_monitor = global_state_get_process( p_glob, monitor_pid );
return p_monitor == NULL || // not availabale -> terminated
(p_monitor->flags & PROCESS_FLAGS_MODE) == PROCESS_FLAGS_MODE_TERMINATED || // terminated
(p_monitor->flags & PROCESS_FLAGS_MONITOR_ACCEPT) != 0; // accepting state
}
/* DEPRECATED */
int global_state_monitor_accepting( st_global_state_header *p_glob )
{
return nipsvm_state_monitor_accepting (p_glob);
}
/* DEPRECATED */
int global_state_monitor_terminated( st_global_state_header *p_glob )
{
return nipsvm_state_monitor_terminated (p_glob);
}
/* DEPRECATED */
int global_state_monitor_acc_or_term( st_global_state_header *p_glob )
{
return nipsvm_state_monitor_acc_or_term (p_glob);
}
// macro to shorten output to a string
// - p_buf: pointer to buffer to print string to
// - size: total size of buffer
// - pos: "unsigned int" variable containing current position in buffer
#define to_str( p_buf, size, pos, ... ) \
{ int cnt = snprintf( (p_buf) + pos, pos > (size) ? 0 : (size) - pos, __VA_ARGS__ ); \
pos += cnt < 0 ? 0 : cnt; }
// output a process to a string
// - possibly to output in graphviz dot format
// - puts up to size charactes into *p_buf (including temintaing 0 character)
// - returns number of characters needed (e.g. size or more if string is truncated)
unsigned int process_to_str( st_process_header *p_proc, int dot_fmt, char *p_buf, unsigned int size ) // extern
{
unsigned int pos = 0;
int i;
char *ptr;
ua_t_val *val_ptr;
// dotty format?
char line_end[] = STATE_OUT_DOTTY_LINE_END;
if( ! dot_fmt )
line_end[0] = 0;
// header
to_str( p_buf, size, pos, " %sprocess pid=%u", p_proc->flags & PROCESS_FLAGS_ACTIVE ? "active " : "", be2h_pid( p_proc->pid ) );
switch( p_proc->flags & PROCESS_FLAGS_MODE )
{
case PROCESS_FLAGS_MODE_NORMAL: to_str( p_buf, size, pos, " mode=normal" ); break;
case PROCESS_FLAGS_MODE_ATOMIC: to_str( p_buf, size, pos, " mode=normal" ); break;
case PROCESS_FLAGS_MODE_INVISIBLE: to_str( p_buf, size, pos, " mode=invisible" ); break;
case PROCESS_FLAGS_MODE_TERMINATED: to_str( p_buf, size, pos, " mode=terminated" ); break;
default: to_str( p_buf, size, pos, " mode=?" );
}
to_str( p_buf, size, pos, " pc=0x%08X", be2h_pc( p_proc->pc ) );
to_str( p_buf, size, pos, " (size=%u)%s\n", process_size( p_proc ), line_end );
if( p_proc->flags & PROCESS_FLAGS_ACTIVE ) // process is active
{
to_str( p_buf, size, pos, " registers:" );
for( i = 0; i < 8; i++ )
to_str( p_buf, size, pos, " r%i=%d", i, ((st_process_active_header *)p_proc)->registers[i] );
to_str( p_buf, size, pos, "%s\n", line_end );
to_str( p_buf, size, pos, " flag register:" );
for( i = FLAG_REG_FLAG_CNT - 1; i >= 0; i-- )
to_str( p_buf, size, pos, "%s%d", (i & 7) == 7 ? " " : "",
((st_process_active_header *)p_proc)->flag_reg & 1 << i ? 1 : 0 );
to_str( p_buf, size, pos, "%s\n", line_end );
ptr = (char *)p_proc + sizeof( st_process_active_header );
}
else // process is not active
ptr = (char *)p_proc + sizeof( st_process_header );
// variables
to_str( p_buf, size, pos, " variables:" );
for( i = 0; i < (int)p_proc->lvar_sz; i++ )
to_str( p_buf, size, pos, " 0x%02X", (unsigned char)*(ptr++) );
to_str( p_buf, size, pos, "%s\n", line_end );
if( p_proc->flags & PROCESS_FLAGS_ACTIVE ) // process is active
{
// stack
to_str( p_buf, size, pos, " stack (max=%d):", ((st_process_active_header *)p_proc)->stack_max );
val_ptr = (ua_t_val *)ptr;
for( i = 0; i < (int)((st_process_active_header *)p_proc)->stack_cur; i++ )
to_str( p_buf, size, pos, " %d", (val_ptr++)->val );
to_str( p_buf, size, pos, "%s\n", line_end );
}
return pos;
}
// output a channel to a string
// - possibly to output in graphviz dot format
// - puts up to size charactes into *p_buf (including temintaing 0 character)
// - returns number of characters needed (e.g. size or more if string is truncated)
unsigned int channel_to_str( st_channel_header *p_chan, int dot_fmt, char *p_buf, unsigned int size ) // extern
{
unsigned int pos = 0;
int i, j;
// dotty format?
char line_end[] = STATE_OUT_DOTTY_LINE_END;
if( ! dot_fmt )
line_end[0] = 0;
// header
t_chid chid = be2h_chid( p_chan->chid );
to_str( p_buf, size, pos, " channel chid=0x%04X=%u-%u", chid, chid >> 8, chid & 0xFF );
to_str( p_buf, size, pos, " max_len=%u", p_chan->max_len );
to_str( p_buf, size, pos, " (size=%u)%s\n", channel_size( p_chan ), line_end );
// type
to_str( p_buf, size, pos, " type:" );
int8_t *p_type = (int8_t *)((char *)p_chan + sizeof( st_channel_header ));
for( i = 0; i < (int)p_chan->type_len; i++ )
to_str( p_buf, size, pos, " %d", p_type[i] );
to_str( p_buf, size, pos, "%s\n", line_end );
// messages
to_str( p_buf, size, pos, " messages:""%s\n", line_end );
char *ptr = (char *)p_type + p_chan->type_len;
for( i = 0; i < (int)p_chan->cur_len; i++ )
{
to_str( p_buf, size, pos, " %u) ", i + 1 );
for( j = 0; j < (int)p_chan->type_len; j++ )
{
if( p_type[j] < 0 ) // signed
{
if( p_type[j] >= -7 )
{ to_str( p_buf, size, pos, " %d", *(int8_t *)ptr ); ptr++; }
else if( p_type[j] >= -15 )
{ to_str( p_buf, size, pos, " %d", be2h_16( (int16_t)ua_rd_16( ptr ) ) ); ptr += 2; }
else
{ to_str( p_buf, size, pos, " %d", be2h_32( (int32_t)ua_rd_32( ptr ) ) ); ptr += 4; }
}
else // unsigned
{
if( p_type[j] <= 8 )
{ to_str( p_buf, size, pos, " %u", *(uint8_t *)ptr ); ptr++; }
else if( p_type[j] <= 16 )
{ to_str( p_buf, size, pos, " %u", be2h_16( (uint16_t)ua_rd_16( ptr ) ) ); ptr += 2; }
else
{ to_str( p_buf, size, pos, " %u", be2h_32( (uint32_t)ua_rd_32( ptr ) ) ); ptr += 4; }
}
}
to_str( p_buf, size, pos, "%s\n", line_end );
}
return pos;
}
// output a global state to a string
// - possibly to output in graphviz dot format
// - puts up to size charactes into *p_buf (including temintaing 0 character)
// - returns number of characters needed (e.g. size or more if string is truncated)
unsigned int global_state_to_str( st_global_state_header *p_glob, int dot_fmt, char *p_buf, unsigned int size ) // extern
{
unsigned int pos = 0;
int i;
// dotty format?
char line_end[] = STATE_OUT_DOTTY_LINE_END;
if( ! dot_fmt )
line_end[0] = 0;
// header
t_pid monitor_pid = be2h_pid( p_glob->monitor_pid );
to_str( p_buf, size, pos, "global state excl_pid=%u monitor_pid=%u",
be2h_pid( p_glob->excl_pid ),
monitor_pid );
to_str( p_buf, size, pos, " (size=%lu)%s\n", (unsigned long)nipsvm_state_size( p_glob ), line_end );
// variables
to_str( p_buf, size, pos, " variables:" );
char *ptr = (char *)p_glob + sizeof( st_global_state_header );
for( i = 0; i < (int)be2h_16( p_glob->gvar_sz ); i++ )
to_str( p_buf, size, pos, " 0x%02X", (unsigned char)*(ptr++) );
to_str( p_buf, size, pos, "%s\n", line_end );
// processes
for( i = 0; i< (int)p_glob->proc_cnt; i++ )
{
int cnt = process_to_str( (st_process_header *)ptr, dot_fmt,
p_buf + pos, pos > size ? 0 : size - pos );
pos += cnt < 0 ? 0 : cnt;
ptr += process_size( (st_process_header *)ptr );
}
// channels
for( i = 0; i< (int)p_glob->chan_cnt; i++ )
{
int cnt = channel_to_str( (st_channel_header *)ptr, dot_fmt,
p_buf + pos, pos > size ? 0 : size - pos );
pos += cnt < 0 ? 0 : cnt;
ptr += channel_size( (st_channel_header *)ptr );
}
// monitor flags
if (monitor_pid != 0) {
to_str (p_buf, size, pos, "Monitor:");
if (nipsvm_state_monitor_acc_or_term (p_glob)) {
to_str (p_buf, size, pos, " ACCEPTING");
}
if (nipsvm_state_monitor_terminated (p_glob)) {
to_str (p_buf, size, pos, " TERMINATED");
}
to_str( p_buf, size, pos, "%s\n", line_end );
}
return pos;
}
// print a process to a stream
// - possibly to output in graphviz dot format
void process_print_ex( FILE *out, st_process_header *p_proc, int dot_fmt ) // extern
{
unsigned int size = process_to_str( p_proc, dot_fmt, NULL, 0 ) + 1; // get needed buffer size
{
char buf[size]; // allocate buffer
process_to_str( p_proc, dot_fmt, buf, size ); // convert to string
fprintf( out, "%s", buf ); // output
}
}
// print a channel to a stream
// - possibly to output in graphviz dot format
void channel_print_ex( FILE *out, st_channel_header *p_chan, int dot_fmt ) // extern
{
unsigned int size = channel_to_str( p_chan, dot_fmt, NULL, 0 ) + 1; // get needed buffer size
{
char buf[size]; // allocate buffer
channel_to_str( p_chan, dot_fmt, buf, size ); // convert to string
fprintf( out, "%s", buf ); // output
}
}
// print a global state to a stream
// - possibly to output in graphviz dot format
void global_state_print_ex( FILE *out, st_global_state_header *p_glob, int dot_fmt ) // extern
{
unsigned int size = global_state_to_str( p_glob, dot_fmt, NULL, 0 ) + 1; // get needed buffer size
{
char buf[size]; // allocate buffer
global_state_to_str( p_glob, dot_fmt, buf, size ); // convert to string
fprintf( out, "%s", buf ); // output
}
}
// print a process to stdout
void process_print( st_process_header *p_proc ) // extern
{
process_print_ex( stdout, p_proc, 0 );
}
// print a channel to stdout
void channel_print( st_channel_header *p_chan ) // extern
{
channel_print_ex( stdout, p_chan, 0 );
}
// print a global state to stdout
void global_state_print( st_global_state_header *p_glob ) // extern
{
global_state_print_ex( stdout, p_glob, 0 );
}

274
iface/nips/nips_vm/state.h Normal file
View file

@ -0,0 +1,274 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_state
#define INC_state
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "tools.h"
// types for process and channel identifiers
typedef uint8_t t_pid;
#define PID_MAX ((t_pid)-1)
#define PID_OK( pid ) ((pid) > 0)
#define h2be_pid( x ) (x)
#define be2h_pid( x ) (x)
typedef uint16_t t_chid; // channel-id = <pid of creating process> <1 byte to make channel-id unique>
#define CHID_MAX ((t_chid)-1)
#define CHID_OK( chid ) ((chid) > 0)
#define h2be_chid( x ) h2be_16( x )
#define be2h_chid( x ) be2h_16( x )
// type for program counter
typedef uint32_t t_pc;
#define PC_MAX ((t_pc)-1)
#define h2be_pc( x ) h2be_32( x )
#define be2h_pc( x ) be2h_32( x )
// type of flag register
typedef uint32_t t_flag_reg;
#define FLAG_REG_FLAG_CNT 32
// type for internal values (type for data stack and normal registers)
typedef int32_t t_val;
typedef uint32_t t_u_val;
// unaligned version of internal value
// - realized as structure to be able to use PACKED
typedef struct { t_val val; } PACKED ua_t_val;
// process
typedef struct t_process_header
{
t_pid pid; // process identifier (never 0)
uint8_t flags; // flags (contain execution mode)
uint8_t lvar_sz; // size of local variables in bytes
t_pc pc; // program counter
} PACKED st_process_header;
// after header follows (if ! (flags & process_flags_active)):
// variables (lvar_sz bytes, multi-byte values in big-endian format),
// process flags
#define PROCESS_FLAGS_MODE 0x03 // execution modes
#define PROCESS_FLAGS_MODE_NORMAL 0x00
#define PROCESS_FLAGS_MODE_ATOMIC 0x01
#define PROCESS_FLAGS_MODE_INVISIBLE 0x02
#define PROCESS_FLAGS_MODE_TERMINATED 0x03
#define PROCESS_FLAGS_MONITOR_ACCEPT 0x10 // if monitor process is in accepting state (only valid for monitor process)
#define PROCESS_FLAGS_ACTIVE 0x80 // if process is currently active (i.e. executing)
// active process
typedef struct t_process_active_header
{
st_process_header proc; // normal process header (with flags & process_flags_active)
t_val registers[8]; // virtual machine registers (host byte order)
t_flag_reg flag_reg; // flag register (host byte order)
uint8_t stack_cur; // current size of stack in entries
uint8_t stack_max; // maximum size of stack in entries
} PACKED st_process_active_header;
// after header follows:
// variables (proc.lvar_sz bytes, multi-byte values in big-endian format),
// stack (stack_max ints in host byte order),
// channel
typedef struct t_channel_header
{
t_chid chid; // channel identifier (never 0)
uint8_t max_len; // maximum number of messages in channel (if 0, real max_len is 1)
uint8_t cur_len; // current number of messages in channel
uint8_t msg_len; // length of a single message in bytes
uint8_t type_len; // number of entries of flat type
} PACKED st_channel_header;
// after header follows:
// flat type of message (type_len bytes),
// - bytes contain number of bits the entries use
// (use -value+1 for negative values,
// round up to 8, 16 or 32 and divide by 8 to get number of bytes)
// messages (max( 1, max_len ) * msg_len bytes)
// (multi-byte values in big-endian format)
// global state
#define PROC_CNT_MAX 255 // maximum number of processes (must fit into uint8_t, see below)
#define CHAN_CNT_MAX 255 // maximum number of channels (must fit into uint8_t, see below)
typedef struct t_global_state_header
{
uint16_t gvar_sz; // size of global variables in bytes (big endian format)
uint8_t proc_cnt; // number of process currently active
t_pid excl_pid; // pid of the process executed exclusively or 0 if none
t_pid monitor_pid; // pid of the monitor process or 0 if none
uint8_t chan_cnt; // number of channels currently existing
} PACKED st_global_state_header;
// after header follows:
// variables (gvar_sz bytes, multi-byte values in big-endian format),
// processes (proc_cnt times a process),
// channels (chan_cnt times a channel)
typedef st_global_state_header nipsvm_state_t;
// inline functions as "extern inline" for optimized compilation
#define STATE_INLINE extern inline
#include "state_inline.h"
#undef STATE_INLINE
// size of initial state
#define NIPSVM_INITIAL_STATE_SIZE (sizeof( st_global_state_header ) \
+ sizeof( st_process_header ))
static const size_t global_state_initial_size = NIPSVM_INITIAL_STATE_SIZE;
// generate initial state
// *pp_buf points to memory area of length *p_len to use for new state
// NULL is returned in case of error
extern st_global_state_header * global_state_initial( char **pp_buf, unsigned long *p_buf_len );
// get a copy of global state
// *pp_buf points to memory area of length *p_len to use for new state
// NULL is returned in case of error
extern st_global_state_header * global_state_copy( st_global_state_header *p_glob,
char **pp_buf, unsigned long *p_buf_len );
// get copy of global state with resized global variables
// *pp_buf points to memory area of length *p_len to use for new state
// NULL is returned in case of error
extern st_global_state_header * global_state_copy_gvar_sz( st_global_state_header *p_glob,
uint16_t gvar_sz,
char **pp_buf, unsigned long *p_buf_len );
// get copy of global state with resized local variables
// *pp_buf points to memory area of length *p_len to use for new state
// NULL is returned in case of error
extern st_global_state_header * global_state_copy_lvar_sz( st_global_state_header *p_glob,
st_process_header *p_proc, uint8_t lvar_sz,
char **pp_buf, unsigned long *p_buf_len );
// get copy of global state with selected process activated
// *pp_buf points to memory area of length *p_len to use for new state
// *pp_proc is filled with the pointer to the activated process
// NULL is returned in case of error
extern st_global_state_header * global_state_copy_activate( st_global_state_header *p_glob, st_process_header *p_proc,
uint8_t stack_max, t_flag_reg flag_reg,
char **pp_buf, unsigned long *p_buf_len,
st_process_active_header **pp_proc );
// get copy of global state with an additional process
// *pp_buf points to memory area of length *p_len to use for new state
// *pp_proc is filled with the pointer to the new process
// NULL is returned in case of error
extern st_global_state_header * global_state_copy_new_process( st_global_state_header *p_glob,
t_pid new_pid, uint8_t lvar_sz,
char **pp_buf, unsigned long *p_buf_len,
st_process_header **pp_proc );
// get copy of global state with an additional channel
// *pp_buf points to memory area of length *p_len to use for new state
// the new channel is inserted at the right place according to its channel id
// *pp_chan is filled with the pointer to the new process
// NULL is returned in case of error
extern st_global_state_header * global_state_copy_new_channel( st_global_state_header *p_glob,
t_chid new_chid, uint8_t max_len, uint8_t type_len, uint8_t msg_len,
char **pp_buf, unsigned long *p_buf_len,
st_channel_header **pp_chan );
// deactivate selected process in global state
extern void global_state_deactivate( st_global_state_header *p_glob, st_process_active_header *p_proc );
// remove selected process in global state
extern void global_state_remove( st_global_state_header *p_glob, st_process_header *p_proc );
// count enabled processes (i.e. processes that are not yet terminated)
// returns number of processes
extern unsigned int global_state_count_enabled_processes( st_global_state_header *p_glob );
// get enabled processes (i.e. processes that may be activated)
// returns number of processes or (unsigned int)-1 if there are too many enabled processes
extern unsigned int global_state_get_enabled_processes( st_global_state_header *p_glob, st_process_header **p_procs, unsigned int proc_max );
/* DEPRECATED */ extern int global_state_monitor_accepting( st_global_state_header *p_glob );
/* DEPRECATED */ extern int global_state_monitor_terminated( st_global_state_header *p_glob );
/* DEPRECATED */ extern int global_state_monitor_acc_or_term( st_global_state_header *p_glob );
// TIP: the following 3 functions can be called with ( ..., NULL, 0 )
// to obtain the needed buffer size
// (add 1 to return value for the terminating 0 character)
// output a process to a string
// - possibly to output in graphviz dot format
// - puts up to size charactes into *p_buf (including temintaing 0 character)
// - returns number of characters needed (e.g. size or more if string is truncated)
extern unsigned int process_to_str( st_process_header *p_proc, int dot_fmt, char *p_buf, unsigned int size );
// output a channel to a string
// - possibly to output in graphviz dot format
// - puts up to size charactes into *p_buf (including temintaing 0 character)
// - returns number of characters needed (e.g. size or more if string is truncated)
extern unsigned int channel_to_str( st_channel_header *p_chan, int dot_fmt, char *p_buf, unsigned int size );
// output a global state to a string
// - possibly to output in graphviz dot format
// - puts up to size charactes into *p_buf (including temintaing 0 character)
// - returns number of characters needed (e.g. size or more if string is truncated)
extern unsigned int global_state_to_str( st_global_state_header *p_glob, int dot_fmt, char *p_buf, unsigned int size );
// print a process to a stream
// - possibly to output in graphviz dot format
extern void process_print_ex( FILE *out, st_process_header *p_proc, int dot_fmt );
// print a channel to a stream
// - possibly to output in graphviz dot format
extern void channel_print_ex( FILE *out, st_channel_header *p_chan, int dot_fmt );
// print a global state to a stream
// - possibly to output in graphviz dot format
extern void global_state_print_ex( FILE *out, st_global_state_header *p_glob, int dot_fmt );
// print a process to stdout
extern void process_print( st_process_header *p_proc );
// print a channel to stdout
extern void channel_print( st_channel_header *p_chan );
// print a global state to stdout
extern void global_state_print( st_global_state_header *p_glob );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_state

View file

@ -0,0 +1,280 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
// DO NOT INCLUDE THIS FILE - IT IS FOR INTERNAL USE ONLY
#ifdef STATE_INLINE
// size of a process
STATE_INLINE unsigned int process_size( st_process_header *p_proc )
{
// active process
if( p_proc->flags & PROCESS_FLAGS_ACTIVE )
{
return sizeof( st_process_active_header ) // header
+ ((st_process_active_header *)p_proc)->proc.lvar_sz // variables
+ ((st_process_active_header *)p_proc)->stack_max * sizeof( t_val ); // stack
}
// normal process
return sizeof( st_process_header ) // header
+ p_proc->lvar_sz; // variables
}
// get pointer to variables
STATE_INLINE char * process_get_variables( st_process_header *p_proc )
{
// active process
if( p_proc->flags & PROCESS_FLAGS_ACTIVE )
{
return (char *)p_proc
+ sizeof( st_process_active_header ); // header
}
// normal process
return (char *)p_proc
+ sizeof( st_process_header ); // header
}
// get pointer to stack
STATE_INLINE ua_t_val * process_active_get_stack( st_process_active_header *p_proc )
{
return (ua_t_val *)((char *)p_proc
+ sizeof( st_process_active_header ) // header
+ p_proc->proc.lvar_sz); // variables
}
// size of a channel
STATE_INLINE unsigned int channel_size( st_channel_header *p_chan )
{
return sizeof( st_channel_header ) // header
+ p_chan->type_len // type specification
+ max( 1, p_chan->max_len ) * p_chan->msg_len; // messages
}
// get pointer to type
STATE_INLINE int8_t * channel_get_type( st_channel_header *p_chan )
{
return (int8_t *)((char *)p_chan + sizeof( st_channel_header )); // header
}
// get pointer to message
STATE_INLINE char * channel_get_msg( st_channel_header *p_chan, uint8_t msg_no )
{
return (char *)p_chan + sizeof( st_channel_header ) // header
+ p_chan->type_len // type specification
+ msg_no * p_chan->msg_len; // messages before msg_no
}
// size of a global state without processes and channels
STATE_INLINE unsigned int global_state_size_noproc_nochan( st_global_state_header *p_glob )
{
return sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
}
// size of a global state
STATE_INLINE size_t global_state_size( st_global_state_header *p_glob )
{
unsigned int sz, i;
sz = sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
for( i = 0; i < p_glob->proc_cnt; i++ ) // processes
sz += process_size( (st_process_header *)((char *)p_glob + sz) );
for( i = 0; i < p_glob->chan_cnt; i++ ) // channels
sz += channel_size( (st_channel_header *)((char *)p_glob + sz) );
return sz;
}
// get pointer to variables
STATE_INLINE char * global_state_get_variables( st_global_state_header *p_glob )
{
return (char *)p_glob
+ sizeof( st_global_state_header ); // header
}
// get pointer to processes
STATE_INLINE char * global_state_get_processes( st_global_state_header *p_glob )
{
char *ptr;
ptr = (char *)p_glob
+ sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
return ptr;
}
// get pointer to active process
STATE_INLINE st_process_active_header * global_state_get_active_process( st_global_state_header *p_glob )
{
char *ptr;
unsigned int i;
ptr = (char *)p_glob
+ sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
for( i = 0; i < p_glob->proc_cnt; i++ ) // search process
{
if( ((st_process_header *)ptr)->flags & PROCESS_FLAGS_ACTIVE ) // active process found
return (st_process_active_header *)ptr;
ptr += process_size( (st_process_header *)ptr ); // next process
}
return NULL; // no active process found
}
// get pointer to process
STATE_INLINE st_process_header * global_state_get_process( st_global_state_header *p_glob, t_pid pid )
{
char *ptr;
unsigned int i;
ptr = (char *)p_glob
+ sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
for( i = 0; i < p_glob->proc_cnt; i++ ) // search process
{
if( ((st_process_header *)ptr)->pid == h2be_pid( pid ) ) // found
return (st_process_header *)ptr;
ptr += process_size( (st_process_header *)ptr ); // next process
}
return NULL; // not found
}
// get maximum process id
STATE_INLINE t_pid global_state_get_max_pid( st_global_state_header *p_glob )
{
char *ptr;
unsigned int i;
t_pid max_pid;
ptr = (char *)p_glob
+ sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
max_pid = 0;
for( i = 0; i < p_glob->proc_cnt; i++ ) // processes
{
max_pid = max( max_pid, be2h_pid( ((st_process_header *)ptr)->pid ) );
ptr += process_size( (st_process_header *)ptr );
}
return max_pid;
}
// get pointer to channels
STATE_INLINE char * global_state_get_channels( st_global_state_header *p_glob )
{
char *ptr;
unsigned int i;
ptr = (char *)p_glob
+ sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
for( i = 0; i < p_glob->proc_cnt; i++ ) // processes
ptr += process_size( (st_process_header *)ptr );
return ptr;
}
// get pointer to channel
STATE_INLINE st_channel_header * global_state_get_channel( st_global_state_header *p_glob, t_chid chid )
{
char *ptr;
unsigned int i;
ptr = (char *)p_glob
+ sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
for( i = 0; i < p_glob->proc_cnt; i++ ) // processes
ptr += process_size( (st_process_header *)ptr );
for( i = 0; i < p_glob->chan_cnt; i++ ) // search channel
{
if( ((st_channel_header *)ptr)->chid == h2be_chid( chid ) ) // found
return (st_channel_header *)ptr;
ptr += channel_size( (st_channel_header *)ptr ); // next channel
}
return NULL; // not found
}
// get new channel id
STATE_INLINE t_chid global_state_get_new_chid( st_global_state_header *p_glob, t_pid pid )
{
char *ptr;
unsigned int i;
t_chid chid_begin, chid, chid_end_max, chid_end;
chid_begin = (t_chid)pid << 8;
ptr = (char *)p_glob
+ sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
for( i = 0; i < p_glob->proc_cnt; i++ ) // processes
ptr += process_size( (st_process_header *)ptr );
chid_end_max = 0;
for( i = 0; i < p_glob->chan_cnt; i++ ) // channels
{
chid = be2h_chid( ((st_channel_header *)ptr)->chid ); // get chid
if( (chid & 0xFF00) == chid_begin ) // compare begin of chid (pid)
chid_end_max = max( chid_end_max, chid & 0x00FF ); // save maximum end part
ptr += channel_size( (st_channel_header *)ptr );
}
chid_end = chid_end_max + 1; // generate new chid
if( chid_end > 0x00FF ) // all end parts used
return 0; // no chid
return chid_begin | chid_end; // return new chid
}
// check if synchronous communication is occuring in at least one channel
STATE_INLINE int global_state_sync_comm( st_global_state_header *p_glob )
{
char *ptr;
unsigned int i;
ptr = (char *)p_glob
+ sizeof( st_global_state_header ) // header
+ be2h_16( p_glob->gvar_sz ); // variables
for( i = 0; i < p_glob->proc_cnt; i++ ) // processes
ptr += process_size( (st_process_header *)ptr );
for( i = 0; i < p_glob->chan_cnt; i++ ) // channels
{
if( ((st_channel_header *)ptr)->cur_len > ((st_channel_header *)ptr)->max_len )
return 1; // found a channel that contains more messages than max_len
// ---> synchronous communication is occuring
ptr += channel_size( (st_channel_header *)ptr );
}
return 0; // all channels do not contain more than max_len messages
// ---> no synchronous communication
}
/* deprecated */ STATE_INLINE int
global_state_compare (void *p_glob1, void *p_glob2)
{
// see nipsvm_state_compare
return memcmp (p_glob1, p_glob2, global_state_size ((st_global_state_header *)p_glob1));
}
#undef STATE_INLINE
#endif // #ifdef STATE_INLINE

View file

@ -0,0 +1,103 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#include "state.h"
#include "state_parts.h"
// get size of a global/process/channel part from pointer
unsigned int glob_part_size( char *p_glob_part ) // extern
{
return global_state_size_noproc_nochan( (st_global_state_header *)p_glob_part );
}
unsigned int proc_part_size( char *p_proc_part ) // extern
{
return process_size( (st_process_header *)p_proc_part );
}
unsigned int chan_part_size( char *p_chan_part ) // extern
{
return channel_size( (st_channel_header *)p_chan_part );
}
// split up a global state into its parts
// - callbacks are called with pointer to the different parts
// - 1x glob_cb, Nx proc_cb, Mx chan_cb
// - memory pointed to is read only and only valid within callback
void state_parts( st_global_state_header *p_glob, // the global state to split up
t_glob_part_cb glob_cb, t_proc_part_cb proc_cb, t_chan_part_cb chan_cb, // the callbacks to call
void *p_ctx ) // extern
{
char *ptr = (char *)p_glob;
unsigned int sz, i;
// report global part to callback
sz = global_state_size_noproc_nochan( (st_global_state_header *)ptr );
glob_cb( ptr, sz, p_ctx );
ptr += sz;
// report process parts to callback
for( i = 0; i < p_glob->proc_cnt; i++ )
{
sz = process_size( (st_process_header *)ptr );
proc_cb( ptr, sz, p_ctx );
ptr += sz;
}
// report channel parts to callback
for( i = 0; i < p_glob->chan_cnt; i++ )
{
sz = channel_size( (st_channel_header *)ptr );
chan_cb( ptr, sz, p_ctx );
ptr += sz;
}
}
// recreate a global state from its parts
// - callbacks are called to retrieve parts of state
// - 1x glob_cb, Nx proc_cb, Mx chan_cb
// - state is restored in buffer pointed to by p_buf of length buf_len
// - pointer to start of buffer is returned, or NULL if buffer too small
st_global_state_header * state_restore( char *p_buf, unsigned int buf_len,
t_glob_restore_cb glob_cb, t_proc_restore_cb proc_cb, t_chan_restore_cb chan_cb, // the callbacks to call
void *p_ctx ) // extern
{
st_global_state_header *p_glob = (st_global_state_header *)p_buf;
unsigned int sz, i;
// get global part
sz = glob_cb( p_buf, buf_len, p_ctx );
if( sz <= 0 || sz >= buf_len )
return NULL;
p_buf += sz;
buf_len -= sz;
// get processes
for( i = 0; i < p_glob->proc_cnt; i++ )
{
sz = proc_cb( p_buf, buf_len, p_ctx );
if( sz <= 0 || sz >= buf_len )
return NULL;
p_buf += sz;
buf_len -= sz;
}
// get channels
for( i = 0; i < p_glob->chan_cnt; i++ )
{
sz = chan_cb( p_buf, buf_len, p_ctx );
if( sz <= 0 || sz >= buf_len )
return NULL;
p_buf += sz;
buf_len -= sz;
}
// return assembled state
return p_glob;
}

View file

@ -0,0 +1,67 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_state_parts
#define INC_state_parts
#ifdef __cplusplus
extern "C"
{
#endif
#include "state.h"
// get size of a global/process/channel part from pointer
extern unsigned int glob_part_size( char *p_glob_part );
extern unsigned int proc_part_size( char *p_proc_part );
extern unsigned int chan_part_size( char *p_chan_part );
// types for callbacks to split up state
// - called for every part of the state
// - part is read only and only valid during callback
typedef void (*t_glob_part_cb)( char *p_glob_part, unsigned int glob_part_size, void *p_ctx );
typedef void (*t_proc_part_cb)( char *p_proc_part, unsigned int proc_part_size, void *p_ctx );
typedef void (*t_chan_part_cb)( char *p_chan_part, unsigned int chan_part_size, void *p_ctx );
// split up a global state into its parts
// - callbacks are called with pointer to the different parts
// - 1x glob_cb, Nx proc_cb, Mx chan_cb
// - memory pointed to is read only and only valid within callback
extern void state_parts( st_global_state_header *p_glob, // the global state to split up
t_glob_part_cb glob_cb, t_proc_part_cb proc_cb, t_chan_part_cb chan_cb, // the callbacks to call
void *p_ctx );
// types for callbacks to restore state
// - must copy the part into the memory pointed to by p_buf of length buf_len
// - must return number of bytes placed into buffer (0 in case of error)
typedef unsigned int (*t_glob_restore_cb)( char *p_buf, unsigned int buf_len, void *p_ctx );
typedef unsigned int (*t_proc_restore_cb)( char *p_buf, unsigned int buf_len, void *p_ctx );
typedef unsigned int (*t_chan_restore_cb)( char *p_buf, unsigned int buf_len, void *p_ctx );
// recreate a global state from its parts
// - callbacks are called to retrieve parts of state
// - 1x glob_cb, Nx proc_cb, Mx chan_cb
// - state is restored in buffer pointed to by p_buf of length buf_len
// - pointer to start of buffer is returned, or NULL if buffer too small
extern st_global_state_header * state_restore( char *p_buf, unsigned int buf_len,
t_glob_restore_cb glob_cb, t_proc_restore_cb proc_cb, t_chan_restore_cb chan_cb, // the callbacks to call
void *p_ctx );
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef INC_state_parts

View file

@ -0,0 +1,76 @@
/*
* timeval.h 1.0 01/12/19
*
* Defines gettimeofday, timeval, etc. for Win32
*
* By Wu Yongwei
*
*/
#ifndef _TIMEVAL_H
#define _TIMEVAL_H
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <sys/time.h>
#include <time.h>
#ifndef __GNUC__
#define EPOCHFILETIME (116444736000000000i64)
#else
#define EPOCHFILETIME (116444736000000000LL)
#endif
//struct timeval {
// long tv_sec; /* seconds */
// long tv_usec; /* microseconds */
//};
struct timezone {
int tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
};
__inline int gettimeofday(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
LARGE_INTEGER li;
__int64 t;
static int tzflag;
if (tv)
{
GetSystemTimeAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
t = li.QuadPart; /* In 100-nanosecond intervals */
t -= EPOCHFILETIME; /* Offset to the Epoch time */
t /= 10; /* In microseconds */
tv->tv_sec = (long)(t / 1000000);
tv->tv_usec = (long)(t % 1000000);
}
if (tz)
{
if (!tzflag)
{
_tzset();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}
#else /* _WIN32 */
#include <sys/time.h>
#include <time.h>
#endif /* _WIN32 */
#endif /* _TIMEVAL_H */

View file

@ -0,0 +1,60 @@
/* NIPS VM - New Implementation of Promela Semantics Virtual Machine
* Copyright (C) 2005: Stefan Schuermans <stefan@schuermans.info>
* Michael Weber <michaelw@i2.informatik.rwth-aachen.de>
* Lehrstuhl fuer Informatik II, RWTH Aachen
* Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
*/
#ifndef INC_tools
#define INC_tools
#include <sys/types.h>
#ifdef WIN32
# include <winsock2.h>
#else
# include <sys/socket.h>
# include <netinet/in.h>
#endif
// minimum and maximum
#ifndef min
# define min( a, b ) ((a) < (b) ? (a) : (b))
#endif
#ifndef max
# define max( a, b ) ((a) > (b) ? (a) : (b))
#endif
// number of entries in an array
#define count( array ) (sizeof( (array) ) / sizeof( (array)[0] ))
// packed (and unaligned) structures
#define PACKED __attribute__((packed))
// types to define macros for unaligned reads and writes
typedef struct { uint16_t u16; } PACKED ua_16;
typedef struct { uint32_t u32; } PACKED ua_32;
// unaligned reads
#define ua_rd_16( ptr ) (((ua_16 *)(ptr))->u16)
#define ua_rd_32( ptr ) (((ua_32 *)(ptr))->u32)
// unaligned writes
#define ua_wr_16( ptr, val ) (((ua_16 *)(ptr))->u16 = (uint16_t)(val))
#define ua_wr_32( ptr, val ) (((ua_32 *)(ptr))->u32 = (uint32_t)(val))
// byte order (big endian)
// we steal this code from netinet/in.h
#define h2be_16( x ) ((uint16_t)htons( (uint16_t)(x) ))
#define h2be_32( x ) ((uint32_t)htonl( (uint32_t)(x) ))
#define be2h_16( x ) ((uint16_t)ntohs( (uint16_t)(x) ))
#define be2h_32( x ) ((uint32_t)ntohl( (uint32_t)(x) ))
// byte oder (little endian)
// use big endian macros and swap bytes
// FIXME: there must be a better way
#define byteswap_16( x ) ((uint16_t)x << 8 | (uint16_t)x >> 8)
#define byteswap_32( x ) ((uint32_t)x << 24 | ((uint32_t)x << 8 & 0x00FF0000) | ((uint32_t)x >> 8 & 0x0000FF00) | (uint32_t)x >> 24)
#define h2le_16( x ) (byteswap_16( h2be_16( x ) ))
#define h2le_32( x ) (byteswap_32( h2be_32( x ) ))
#define le2h_16( x ) (be2h_16( byteswap_16( x ) ))
#define le2h_32( x ) (be2h_32( byteswap_32( x ) ))
#endif // #ifndef INC_tools

View file

@ -47,3 +47,4 @@ libspot_la_LIBADD = \
# Dummy C++ source to cause C++ linking. # Dummy C++ source to cause C++ linking.
nodist_EXTRA_libspot_la_SOURCES = _.cc nodist_EXTRA_libspot_la_SOURCES = _.cc
_.cc:; touch $@