spot/iface/nips/nips_vm/search.c
Guillaume Sadegh bc5f13bb4e 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.
2008-05-30 13:22:00 +02:00

346 lines
10 KiB
C

/* 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 );
}