/* cstest.c -- context switch test
*
* This program checks the MPD context switch routines in various ways.
* The expected output appears in cstest.stdout.
* Note that a "stack underflow" exit IS expected at the end.
*/
#include <ctype.h>
#include <stdio.h>
#include <math.h>
#include "../arch.h"
#include "../gen.h"
#define STACK_SIZE 16000 /* stack size; must be a multiple of 16 */
#define BOUNDARY 256 /* boundary size; must be multiple of 16 */
#define N_RECURSE 10 /* number of recursive calls */
#define N_STACK 5 /* number of stacks (also see code) */
#define CK_BEFORE 0xB4B4B4B4 /* check value for area below the stack */
#define CK_AFTER 0xAFAFAFAF /* check value for area above the stack */
extern void mpd_build_context ();
extern void mpd_chg_context ();
struct envelope *spawn ();
double mul3 ();
void dotest(), ckenv(), err(), dstack(), dump();
void foo(), bar(), baz(), boo(), stir();
void mpd_check_stk(), mpd_stk_corrupted();
void mpd_stk_overflow(), mpd_stk_underflow();
/* enclose the stacks in a larger structure, to check for errors */
struct envelope {
long before[BOUNDARY]; /* for boundary checks before */
char stack[STACK_SIZE]; /* actual stack - must be mod-8 aligned */
long after[BOUNDARY]; /* for boundary checks after */
};
struct envelope *env[N_STACK]; /* four stacks in envelopes */
char *s1, *s2, *s3, *s4, *s5; /* actual context pointers */
int recursion_depth = 0; /* current recursion depth */
int expect_error = 0; /* is error expected? */
/* main program. */
main ()
{
setbuf (stdout, (char *) NULL); /* unbuffer stdout & stderr */
setbuf (stderr, (char *) NULL);
fprintf (stderr, "running context switch test, arch = %s\n", ARCH);
dotest ((char *) NULL);
err ("dotest() returned to main()");
/*NOTREACHED*/
}
/* create four contexts, then jump to the first. */
void
dotest (context)
char *context; /* caller's context */
{
static char *p[] = { "one", "two", "three", "four" };
env[0] = spawn(foo, "foo", (long)p[0], (long)0x111,(long)0x222,(long)0x333);
env[1] = spawn(bar, "bar", (long)p[1], (long)p[2], (long)0x303,(long)0x404);
env[2] = spawn(baz, "baz", (long)p[0], (long)p[1], (long)p[2], (long)0x300);
env[3] = spawn(boo, "boo", (long)p[0], (long)p[1], (long)p[2], (long)p[3]);
env[4] = spawn(stir,"stir",(long)0x501,(long)0x502,(long)0x503,(long)0x504);
s1 = env[0]->stack;
s2 = env[1]->stack;
s3 = env[2]->stack;
s4 = env[3]->stack;
s5 = env[4]->stack;
ckenv ();
mpd_chg_context (s4, context); /* should not return */
err ("s4 returned to dotest()");
}
/* create a stack and spawn a process. */
struct envelope *
spawn (pc, name, arg1, arg2, arg3, arg4)
void (*pc) ();
char *name;
long arg1, arg2, arg3, arg4;
{
int i;
struct envelope *r;
printf ("creating stack for %s\n", name);
r = (struct envelope *) malloc (sizeof (struct envelope));
if (!r)
err ("can't get memory for new stack");
for (i = 0; i < BOUNDARY; i++) {
r->before[i] = CK_BEFORE;
r->after[i] = CK_AFTER;
}
for (i = 0; i < STACK_SIZE; i++)
r->stack[i] = rand ();
mpd_build_context (pc, r->stack, STACK_SIZE, arg1, arg2, arg3, arg4);
return r;
}
/* four coroutines for checking context switching. */
/* foo is in context s1 */
void
foo (a)
char *a;
{
printf (" 5. foo1(%s)\n", a);
mpd_check_stk (s1);
ckenv ();
mpd_chg_context (s2, s1);
printf (" 8. foo2(%s)\n", a);
mpd_check_stk (s1);
ckenv ();
mpd_chg_context (s3, s1);
printf ("XX. foom!!!!!!!\n");
exit (1);
}
/* bar is in context s2 */
void
bar (a, b)
char *a, *b;
{
double d;
printf (" 6. bar1(%s,%s)\n", a, b);
mpd_check_stk (s2);
ckenv ();
mpd_chg_context (s3, s2);
printf ("10. bar2(%s,%s)\n", a, b);
mpd_check_stk (s2);
puts ("===== checking floating point =====");
d = mul3 (mul3 (2., 3., 5., s2), mul3 (7., 11., 13., s2),
mul3 (17., 19., 23., s2), s2);
if (d != 223092870.)
printf ("oops -- product returned was %.20g\n", d);
mpd_check_stk (s2);
if (recursion_depth++ < N_RECURSE) {
puts ("====== recursing ======");
dotest (s2);
err ("dotest() returned to bar()");
}
puts ("====== finishing up ======");
printf ("now expect underflow error:\n");
expect_error = 1;
return; /* should underflow and be caught */
}
/* baz is in context s3 */
void
baz (a, b, c)
char *a, *b, *c;
{
printf (" 7. baz1(%s,%s,%s)\n", a, b, c);
mpd_check_stk (s3);
ckenv ();
mpd_chg_context (s1, s3);
printf (" 9. baz2(%s,%s,%s)\n", a, b, c);
mpd_check_stk (s3);
ckenv ();
mpd_chg_context (s2, s3);
printf ("XX. bazzzzzzzt!!!!!\n");
exit (1);
}
/* boo is in context s4 */
void
boo (a, b, c, d)
char *a, *b, *c, *d;
{
printf (" 1. boo1(%s,%s,%s,%s)\n", a, b, c, d);
mpd_check_stk (s4);
ckenv ();
mpd_chg_context (s4, s4);
printf (" 2. boo2(%s,%s,%s,%s)\n", a, b, c, d);
mpd_check_stk (s4);
ckenv ();
mpd_chg_context (s4, s4);
printf (" 3. boo3(%s,%s,%s,%s)\n", a, b, c, d);
mpd_check_stk (s4);
ckenv ();
mpd_chg_context (s4, s4);
printf (" 4. boo4(%s,%s,%s,%s)\n", a, b, c, d);
mpd_check_stk (s4);
ckenv ();
mpd_chg_context (s1, s4);
printf ("XX. boom!!!!!!!\n");
exit (1);
}
/* stir is in context s5 */
void
stir () /* stir floating point registers, then switch to stack 2 */
{
double x = 1.7, y = 3.2, z = 2.4;
for (;;) {
printf ("stirring...");
x = sin ((y = cos (x + y + .4)) - (z = cos (x + z + .6)));
mpd_chg_context (s2, s5);
}
}
/* multiply three doubles, with context switching */
double mul3 (x, y, z, ctx)
double x, y, z;
char *ctx; /* context */
{
printf ("mul3...");
mpd_chg_context (s5, ctx); /* stir up the f.p. registers */
printf ("returning\n");
return x * y * z;
}
/* check stack envelopes */
void
ckenv ()
{
int i, j;
int nerrs = 0;
struct envelope *e;
for (i = 1; i <= N_STACK; i++) {
e = env[i-1];
for (j = 0; j < BOUNDARY; j++)
if (e->before[j] != CK_BEFORE) {
printf ("error -- memory corrupted below stack %d\n", i);
nerrs++;
break;
}
for (j = 0; j < BOUNDARY; j++)
if (e->after[j] != CK_AFTER) {
printf ("error -- memory corrupted above stack %d\n", i);
nerrs++;
break;
}
}
if (nerrs)
err ("aborting due to memory corruption");
}
/* stack error handlers */
void mpd_stk_corrupted () { err ("csw: stack corrupted"); }
void mpd_stk_overflow () { err ("csw: stack overflow"); }
void mpd_stk_underflow () { err ("csw: stack underflow"); }
/* announce error, and exit. */
void
err (s)
char *s;
{
puts (s);
if (expect_error)
exit (0);
else
exit (1);
}
/* dump stack s; this may help with debugging. */
/* NOT TESTED ON 64-BIT ARCHITECTURES. */
void
dstack (s, label)
char *s, *label;
{
dump (s, 0, 7, label);
dump (s, STACK_SIZE - 100, STACK_SIZE - 1, "");
}
/* dump(addr,m,n,label) -- dump addr+m through addr+n, with label. */
void
dump (addr, m, n, label)
char *addr, *label;
int m, n;
{
char c, *p = addr + m, *q = addr + n; int i;
if (!label)
label = "";
printf ("%s ---------------------------------------\n", label);
while (p <= q) {
printf ("\t%08X (%2d) %08X ", (int) p, p - addr, i = * (int *) p);
c = toascii (*p++); putchar (isprint (c) ? c : '.');
c = toascii (*p++); putchar (isprint (c) ? c : '.');
c = toascii (*p++); putchar (isprint (c) ? c : '.');
c = toascii (*p++); putchar (isprint (c) ? c : '.');
if (i != 0 && abs (i) <= 10000)
printf ("%6d\n", i);
else
putchar ('\n');
}
}
syntax highlighted by Code2HTML, v. 0.9.1