/***** spin: run.c *****/
/* Copyright (c) 1991-2000 by Lucent Technologies - Bell Laboratories */
/* All Rights Reserved. This software is for educational purposes only. */
/* Permission is given to distribute this code provided that this intro- */
/* ductory message is not removed and no monies are exchanged. */
/* No guarantee is expressed or implied by the distribution of this code. */
/* Software written by Gerard J. Holzmann as part of the book: */
/* `Design and Validation of Computer Protocols,' ISBN 0-13-539925-4, */
/* Prentice Hall, Englewood Cliffs, NJ, 07632. */
/* Send bug-reports and/or questions to: gerard@research.bell-labs.com */
#include <stdlib.h>
#include "spin.h"
#ifdef PC
#include "y_tab.h"
#else
#include "y.tab.h"
#endif
extern RunList *X, *run;
extern Symbol *Fname;
extern Element *LastStep;
extern int Rvous, lineno, Tval, interactive, MadeChoice;
extern int TstOnly, verbose, s_trail, xspin, jumpsteps, depth;
extern int nproc, nstop, no_print, like_java;
static long Seed = 1;
static int E_Check = 0, Escape_Check = 0;
static int eval_sync(Element *);
static int pc_enabled(Lextok *n);
extern void sr_buf(int, int);
void
Srand(unsigned int s)
{ Seed = s;
}
long
Rand(void)
{ /* CACM 31(10), Oct 1988 */
Seed = 16807*(Seed%127773) - 2836*(Seed/127773);
if (Seed <= 0) Seed += 2147483647;
return Seed;
}
Element *
rev_escape(SeqList *e)
{ Element *r;
if (!e)
return (Element *) 0;
if (r = rev_escape(e->nxt)) /* reversed order */
return r;
return eval_sub(e->this->frst);
}
Element *
eval_sub(Element *e)
{ Element *f, *g;
SeqList *z;
int i, j, k;
if (!e->n)
return ZE;
#ifdef DEBUG
printf("\n\teval_sub(%d %s: line %d) ",
e->Seqno, e->esc?"+esc":"", e->n?e->n->ln:0);
comment(stdout, e->n, 0);
printf("\n");
#endif
if (e->n->ntyp == GOTO)
{ if (Rvous) return ZE;
LastStep = e; f = get_lab(e->n, 1);
cross_dsteps(e->n, f->n);
return f;
}
if (e->n->ntyp == UNLESS)
{ /* escapes were distributed into sequence */
return eval_sub(e->sub->this->frst);
} else if (e->sub) /* true for IF, DO, and UNLESS */
{ Element *has_else = ZE;
Element *bas_else = ZE;
int nr_else, nr_choices = 0;
if (interactive
&& !MadeChoice && !E_Check
&& !Escape_Check
&& !(e->status&(D_ATOM))
&& depth >= jumpsteps)
{ printf("Select stmnt (");
whoruns(0); printf(")\n");
if (nproc-nstop > 1)
printf("\tchoice 0: other process\n");
}
for (z = e->sub, j=0; z; z = z->nxt)
{ j++;
if (interactive
&& !MadeChoice && !E_Check
&& !Escape_Check
&& !(e->status&(D_ATOM))
&& depth >= jumpsteps
&& z->this->frst
&& (xspin || (verbose&32) || Enabled0(z->this->frst)))
{ if (z->this->frst->n->ntyp == ELSE)
{ has_else = (Rvous)?ZE:z->this->frst->nxt;
nr_else = j;
continue;
}
printf("\tchoice %d: ", j);
#if 0
if (z->this->frst->n)
printf("line %d, ", z->this->frst->n->ln);
#endif
if (!Enabled0(z->this->frst))
printf("unexecutable, ");
else
nr_choices++;
comment(stdout, z->this->frst->n, 0);
printf("\n");
} }
if (nr_choices == 0 && has_else)
printf("\tchoice %d: (else)\n", nr_else);
if (interactive && depth >= jumpsteps
&& !Escape_Check
&& !(e->status&(D_ATOM))
&& !E_Check)
{ if (!MadeChoice)
{ char buf[256];
if (xspin)
printf("Make Selection %d\n\n", j);
else
printf("Select [0-%d]: ", j);
fflush(stdout);
scanf("%s", buf);
if (isdigit(buf[0]))
k = atoi(buf);
else
{ if (buf[0] == 'q')
alldone(0);
k = -1;
}
} else
{ k = MadeChoice;
MadeChoice = 0;
}
if (k < 1 || k > j)
{ if (k != 0) printf("\tchoice outside range\n");
return ZE;
}
k--;
} else
{ if (e->n && e->n->indstep >= 0)
k = 0; /* select 1st executable guard */
else
k = Rand()%j; /* nondeterminism */
}
has_else = ZE;
bas_else = ZE;
for (i = 0, z = e->sub; i < j+k; i++)
{ if (z->this->frst
&& z->this->frst->n->ntyp == ELSE)
{ bas_else = z->this->frst;
has_else = (Rvous)?ZE:bas_else->nxt;
if (!interactive || depth < jumpsteps
|| Escape_Check
|| (e->status&(D_ATOM)))
{ z = (z->nxt)?z->nxt:e->sub;
continue;
}
}
if (i >= k)
{ if (f = eval_sub(z->this->frst))
return f;
else if (interactive && depth >= jumpsteps
&& !(e->status&(D_ATOM)))
{ if (!E_Check && !Escape_Check)
printf("\tunexecutable\n");
return ZE;
} }
z = (z->nxt)?z->nxt:e->sub;
}
LastStep = bas_else;
return has_else;
} else
{ if (e->n->ntyp == ATOMIC
|| e->n->ntyp == D_STEP)
{ f = e->n->sl->this->frst;
g = e->n->sl->this->last;
g->nxt = e->nxt;
if (!(g = eval_sub(f))) /* atomic guard */
return ZE;
return g;
} else if (e->n->ntyp == NON_ATOMIC)
{ f = e->n->sl->this->frst;
g = e->n->sl->this->last;
g->nxt = e->nxt; /* close it */
return eval_sub(f);
} else if (e->n->ntyp == '.')
{ if (!Rvous) return e->nxt;
return eval_sub(e->nxt);
} else
{ SeqList *x;
if (!(e->status & (D_ATOM))
&& e->esc && verbose&32)
{ printf("Stmnt [");
comment(stdout, e->n, 0);
printf("] has escape(s): ");
for (x = e->esc; x; x = x->nxt)
{ printf("[");
g = x->this->frst;
if (g->n->ntyp == ATOMIC
|| g->n->ntyp == NON_ATOMIC)
g = g->n->sl->this->frst;
comment(stdout, g->n, 0);
printf("] ");
}
printf("\n");
}
if (!(e->status & D_ATOM)) /* escapes don't reach inside d_steps */
{ Escape_Check++;
if (like_java)
{ if (g = rev_escape(e->esc))
{ if (verbose&4)
printf("\tEscape taken\n");
Escape_Check--;
return g;
}
} else
{ for (x = e->esc; x; x = x->nxt)
{ if (g = eval_sub(x->this->frst))
{ if (verbose&4)
printf("\tEscape taken\n");
Escape_Check--;
return g;
} } }
Escape_Check--;
}
switch (e->n->ntyp) {
case TIMEOUT: case RUN:
case PRINT:
case ASGN: case ASSERT:
case 's': case 'r': case 'c':
/* toplevel statements only */
LastStep = e;
default:
break;
}
if (Rvous)
{
return (eval_sync(e))?e->nxt:ZE;
}
return (eval(e->n))?e->nxt:ZE;
}
}
return ZE; /* not reached */
}
static int
eval_sync(Element *e)
{ /* allow only synchronous receives
and related node types */
Lextok *now = (e)?e->n:ZN;
if (!now
|| now->ntyp != 'r'
|| now->val >= 2 /* no rv with a poll */
|| !q_is_sync(now))
{
return 0;
}
LastStep = e;
return eval(now);
}
static int
assign(Lextok *now)
{ int t;
if (TstOnly) return 1;
switch (now->rgt->ntyp) {
case FULL: case NFULL:
case EMPTY: case NEMPTY:
case RUN: case LEN:
t = BYTE;
break;
default:
t = Sym_typ(now->rgt);
break;
}
typ_ck(Sym_typ(now->lft), t, "assignment");
return setval(now->lft, eval(now->rgt));
}
static int
nonprogress(void) /* np_ */
{ RunList *r;
for (r = run; r; r = r->nxt)
{ if (has_lab(r->pc, 4)) /* 4=progress */
return 0;
}
return 1;
}
int
eval(Lextok *now)
{
if (now) {
lineno = now->ln;
Fname = now->fn;
#ifdef DEBUG
printf("eval ");
comment(stdout, now, 0);
printf("\n");
#endif
switch (now->ntyp) {
case CONST: return now->val;
case '!': return !eval(now->lft);
case UMIN: return -eval(now->lft);
case '~': return ~eval(now->lft);
case '/': return (eval(now->lft) / eval(now->rgt));
case '*': return (eval(now->lft) * eval(now->rgt));
case '-': return (eval(now->lft) - eval(now->rgt));
case '+': return (eval(now->lft) + eval(now->rgt));
case '%': return (eval(now->lft) % eval(now->rgt));
case LT: return (eval(now->lft) < eval(now->rgt));
case GT: return (eval(now->lft) > eval(now->rgt));
case '&': return (eval(now->lft) & eval(now->rgt));
case '^': return (eval(now->lft) ^ eval(now->rgt));
case '|': return (eval(now->lft) | eval(now->rgt));
case LE: return (eval(now->lft) <= eval(now->rgt));
case GE: return (eval(now->lft) >= eval(now->rgt));
case NE: return (eval(now->lft) != eval(now->rgt));
case EQ: return (eval(now->lft) == eval(now->rgt));
case OR: return (eval(now->lft) || eval(now->rgt));
case AND: return (eval(now->lft) && eval(now->rgt));
case LSHIFT: return (eval(now->lft) << eval(now->rgt));
case RSHIFT: return (eval(now->lft) >> eval(now->rgt));
case '?': return (eval(now->lft) ? eval(now->rgt->lft)
: eval(now->rgt->rgt));
case 'p': return remotevar(now); /* only _p allowed */
case 'q': return remotelab(now);
case 'R': return qrecv(now, 0); /* test only */
case LEN: return qlen(now);
case FULL: return (qfull(now));
case EMPTY: return (qlen(now)==0);
case NFULL: return (!qfull(now));
case NEMPTY: return (qlen(now)>0);
case ENABLED: if (s_trail) return 1;
return pc_enabled(now->lft);
case EVAL: return eval(now->lft);
case PC_VAL: return pc_value(now->lft);
case NONPROGRESS: return nonprogress();
case NAME: return getval(now);
case TIMEOUT: return Tval;
case RUN: return TstOnly?1:enable(now);
case 's': return qsend(now); /* send */
case 'r': return qrecv(now, 1); /* receive or poll */
case 'c': return eval(now->lft); /* condition */
case PRINT: return TstOnly?1:interprint(stdout, now);
case ASGN: return assign(now);
case ASSERT: if (TstOnly || eval(now->lft)) return 1;
non_fatal("assertion violated", (char *) 0);
printf("spin: text of failed assertion: assert(");
comment(stdout, now->lft, 0);
printf(")\n");
if (s_trail && !xspin) return 1;
wrapup(1); /* doesn't return */
case IF: case DO: case BREAK: case UNLESS: /* compound */
case '.': return 1; /* return label for compound */
case '@': return 0; /* stop state */
case ELSE: return 1; /* only hit here in guided trails */
default : printf("spin: bad node type %d (run)\n", now->ntyp);
if (s_trail) printf("spin: trail file doesn't match spec?\n");
fatal("aborting", 0);
}}
return 0;
}
int
interprint(FILE *fd, Lextok *n)
{ Lextok *tmp = n->lft;
char c, *s = n->sym->name;
int i, j; char lbuf[16];
extern char Buf[];
char tBuf[1024];
Buf[0] = '\0';
if (!no_print)
if (!s_trail || depth >= jumpsteps) {
for (i = 0; i < (int) strlen(s); i++)
switch (s[i]) {
case '\"': break; /* ignore */
case '\\':
switch(s[++i]) {
case 't': strcat(Buf, "\t"); break;
case 'n': strcat(Buf, "\n"); break;
default: goto onechar;
}
break;
case '%':
if ((c = s[++i]) == '%')
{ strcat(Buf, "%"); /* literal */
break;
}
if (!tmp)
{ non_fatal("too few print args %s", s);
break;
}
j = eval(tmp->lft);
tmp = tmp->rgt;
switch(c) {
case 'c': sprintf(lbuf, "%c", j); break;
case 'd': sprintf(lbuf, "%d", j); break;
case 'e': strcpy(tBuf, Buf); /* event name */
Buf[0] = '\0';
sr_buf(j, 1);
strcpy(lbuf, Buf);
strcpy(Buf, tBuf);
break;
case 'o': sprintf(lbuf, "%o", j); break;
case 'u': sprintf(lbuf, "%u", (unsigned) j); break;
case 'x': sprintf(lbuf, "%x", j); break;
default: non_fatal("bad print cmd: '%s'", &s[i-1]);
lbuf[0] = '\0'; break;
}
goto append;
default:
onechar: lbuf[0] = s[i]; lbuf[1] = '\0';
append: strcat(Buf, lbuf);
break;
}
dotag(fd, Buf);
}
if (strlen(Buf) > 1024) fatal("printf string too long", 0);
return 1;
}
static int
Enabled1(Lextok *n)
{ int i; int v = verbose;
if (n)
switch (n->ntyp) {
case 'c':
if (has_typ(n->lft, RUN))
return 1; /* conservative */
/* else fall through */
default: /* side-effect free */
verbose = 0;
E_Check++;
i = eval(n);
E_Check--;
verbose = v;
return i;
case PRINT: case ASGN: case ASSERT:
return 1;
case 's':
if (q_is_sync(n))
{ if (Rvous) return 0;
TstOnly = 1; verbose = 0;
E_Check++;
i = eval(n);
E_Check--;
TstOnly = 0; verbose = v;
return i;
}
return (!qfull(n));
case 'r':
if (q_is_sync(n))
return 0; /* it's never a user-choice */
n->ntyp = 'R'; verbose = 0;
E_Check++;
i = eval(n);
E_Check--;
n->ntyp = 'r'; verbose = v;
return i;
}
return 0;
}
int
Enabled0(Element *e)
{ SeqList *z;
if (!e || !e->n)
return 0;
switch (e->n->ntyp) {
case '@':
return X->pid == (nproc-nstop-1);
case '.':
return 1;
case GOTO:
if (Rvous) return 0;
return 1;
case UNLESS:
return Enabled0(e->sub->this->frst);
case ATOMIC:
case D_STEP:
case NON_ATOMIC:
return Enabled0(e->n->sl->this->frst);
}
if (e->sub) /* true for IF, DO, and UNLESS */
{ for (z = e->sub; z; z = z->nxt)
if (Enabled0(z->this->frst))
return 1;
return 0;
}
for (z = e->esc; z; z = z->nxt)
{ if (Enabled0(z->this->frst))
return 1;
}
#if 0
printf("enabled1 ");
comment(stdout, e->n, 0);
printf(" ==> %s\n", Enabled1(e->n)?"yes":"nope");
#endif
return Enabled1(e->n);
}
int
pc_enabled(Lextok *n)
{ int i = nproc - nstop;
int pid = eval(n);
int result = 0;
RunList *Y, *oX;
for (Y = run; Y; Y = Y->nxt)
if (--i == pid)
{ oX = X; X = Y;
result = Enabled0(Y->pc);
X = oX;
break;
}
return result;
}
|