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.
646 lines
25 KiB
C
646 lines
25 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 <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 );
|
|
}
|