318 lines
9.3 KiB
C
318 lines
9.3 KiB
C
/* Test51.c
|
|
*
|
|
* Test getcontext, setcontext, makecontext, and swapcontext system calls.
|
|
*
|
|
* Part of this test is somewhat based on the GNU GCC ucontext test set.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <ucontext.h>
|
|
#include <math.h>
|
|
#include <fenv.h>
|
|
|
|
#include <sys/signal.h>
|
|
|
|
_PROTOTYPE( void do_calcs, (void) );
|
|
_PROTOTYPE( void do_child, (void) );
|
|
_PROTOTYPE( void do_parent, (void) );
|
|
_PROTOTYPE( void err, (int subtest, int error_no) );
|
|
_PROTOTYPE( void func1, (int a, int b, int c, int d, int e, int f, int g,
|
|
int h, int i, int j, int k, int l, int m, int n,
|
|
int o, int p, int q, int r, int s, int t, int u,
|
|
int v, int w, int x, int y, int z, int aa, int bb));
|
|
_PROTOTYPE( void func2, (void) );
|
|
_PROTOTYPE( void just_exit, (void) );
|
|
_PROTOTYPE( void test_brk, (void) );
|
|
_PROTOTYPE( void verify_main_reenter, (void) );
|
|
|
|
#define MAX_ERROR 5
|
|
#define SSIZE 32768
|
|
#define ROUNDS 10
|
|
#define SWAPS 10
|
|
|
|
#include "common.c"
|
|
|
|
int subtest;
|
|
ucontext_t ctx[3];
|
|
int entered_func1, entered_func2, reentered_main, entered_overflow;
|
|
|
|
static char st_stack[SSIZE];
|
|
static volatile int shift, global;
|
|
|
|
void do_calcs(void)
|
|
{
|
|
float a, b, c, d, e;
|
|
float foo, bar;
|
|
int i;
|
|
|
|
a = 1.1;
|
|
b = 2.2;
|
|
c = 3.3;
|
|
d = 4.4;
|
|
e = 5.5;
|
|
|
|
foo = a * b; /* 2.42 */
|
|
bar = c * d; /* 14.52 */
|
|
|
|
i = 0;
|
|
while(i < ROUNDS) {
|
|
foo += c; /* 5.72 */
|
|
foo *= d; /* 25.168 */
|
|
foo /= e; /* 4.5760 */
|
|
bar -= a; /* 13.42 */
|
|
bar *= b; /* 29.524 */
|
|
bar /= e; /* 5.3680 */
|
|
|
|
/* Undo */
|
|
foo *= e;
|
|
foo /= d;
|
|
foo -= c;
|
|
|
|
bar *= e;
|
|
bar /= b;
|
|
bar += a;
|
|
|
|
i++;
|
|
}
|
|
if(fabs(foo - (a * b)) > 0.0001) err(8, 1);
|
|
if(fabs(bar - (c * d)) > 0.0001) err(8, 2);
|
|
}
|
|
|
|
void do_child(void)
|
|
{
|
|
int s;
|
|
s = 1;
|
|
|
|
/* Initialize FPU context and verify it's set to round to nearest. */
|
|
if (fegetround() != FE_TONEAREST) err(9, 1);
|
|
|
|
/* Now we change the rounding to something else, and this should be preserved
|
|
between context swaps. */
|
|
if (fesetround(FE_DOWNWARD) != 0) err(9, 2);
|
|
|
|
while(s < SWAPS) {
|
|
s++;
|
|
if (swapcontext(&ctx[2], &ctx[1]) == -1) err(9, 2);
|
|
do_calcs();
|
|
if (fegetround() != FE_DOWNWARD) err(9, 4);
|
|
}
|
|
quit();
|
|
}
|
|
|
|
void do_parent(void)
|
|
{
|
|
int s;
|
|
s = 1;
|
|
|
|
/* Initialize FPU context and verify it's set to round to nearest. */
|
|
if (fegetround() != FE_TONEAREST) err(10, 1);
|
|
|
|
/* Now we change the rounding to something else, and this should be preserved
|
|
between context swaps. */
|
|
if (fesetround(FE_UPWARD) != 0) err(10, 2);
|
|
|
|
while(s < SWAPS) {
|
|
do_calcs();
|
|
if (fegetround() != FE_UPWARD) err(10, 3);
|
|
s++;
|
|
if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 4);
|
|
}
|
|
/* Returning to main thread through uc_link */
|
|
}
|
|
|
|
void fail(void)
|
|
{
|
|
/* Shouldn't get here */
|
|
err(5, 1);
|
|
}
|
|
|
|
void func1(int a, int b, int c, int d, int e, int f, int g,
|
|
int h, int i, int j, int k, int l, int m, int n,
|
|
int o, int p, int q, int r, int s, int t, int u,
|
|
int v, int w, int x, int y, int z, int aa, int bb)
|
|
{
|
|
if ( a != (0x0000001 << shift) || b != (0x0000004 << shift) ||
|
|
c != (0x0000010 << shift) || d != (0x0000040 << shift) ||
|
|
e != (0x0000100 << shift) || f != (0x0000400 << shift) ||
|
|
g != (0x0001000 << shift) || h != (0x0004000 << shift) ||
|
|
i != (0x0010000 << shift) || j != (0x0040000 << shift) ||
|
|
k != (0x0100000 << shift) || l != (0x0400000 << shift) ||
|
|
m != (0x1000000 << shift) || n != (0x4000000 << shift) ||
|
|
o != (0x0000002 << shift) || p != (0x0000008 << shift) ||
|
|
q != (0x0000020 << shift) || r != (0x0000080 << shift) ||
|
|
s != (0x0000200 << shift) || t != (0x0000800 << shift) ||
|
|
u != (0x0002000 << shift) || v != (0x0008000 << shift) ||
|
|
w != (0x0020000 << shift) || x != (0x0080000 << shift) ||
|
|
y != (0x0200000 << shift) || z != (0x0800000 << shift) ||
|
|
aa != (0x2000000 << shift) || bb != (0x8000000 << shift) ) {
|
|
err(2, 1);
|
|
}
|
|
|
|
if (shift && swapcontext (&ctx[1], &ctx[2]) != 0) err(2, 2);
|
|
shift++;
|
|
entered_func1++;
|
|
}
|
|
|
|
void func2(void)
|
|
{
|
|
if (swapcontext(&ctx[2], &ctx[1]) != 0) err(3, 1);
|
|
entered_func2++;
|
|
}
|
|
|
|
void just_exit(void)
|
|
{
|
|
if (errct == 0) printf("ok\n");
|
|
_exit(1);
|
|
}
|
|
|
|
void test_brk(void)
|
|
{
|
|
char *big_stack;
|
|
|
|
big_stack = malloc(16 * 1024 * 1024); /* 16 MB */
|
|
/* If this fails, it is likely brk system call failed due stack/data segments
|
|
collision detection. Unless the system really is low on memory, this is an
|
|
error. */
|
|
if (big_stack == NULL) err(7, 1);
|
|
}
|
|
|
|
void verify_main_reenter(void)
|
|
{
|
|
if (reentered_main == 0) err(4, 1);
|
|
}
|
|
|
|
|
|
int main(void)
|
|
{
|
|
start(51);
|
|
|
|
atexit(verify_main_reenter);
|
|
|
|
/* Save current context in ctx[0] */
|
|
if (getcontext(&ctx[0]) != 0) {
|
|
/* Don't verify reentering main, not going to happen */
|
|
atexit(just_exit);
|
|
err(1, 1);
|
|
}
|
|
|
|
ctx[1] = ctx[0];
|
|
ctx[1].uc_stack.ss_sp = st_stack;
|
|
ctx[1].uc_stack.ss_size = SSIZE;
|
|
ctx[1].uc_link = &ctx[0]; /* When done running, return here */
|
|
|
|
/* ctx[1] is going to run func1 and then return here (uc_link). */
|
|
/* We'll see later on whether makecontext worked. */
|
|
makecontext(&ctx[1], (void (*) (void)) func1, 28,
|
|
(0x0000001 << shift), (0x0000004 << shift),
|
|
(0x0000010 << shift), (0x0000040 << shift),
|
|
(0x0000100 << shift), (0x0000400 << shift),
|
|
(0x0001000 << shift), (0x0004000 << shift),
|
|
(0x0010000 << shift), (0x0040000 << shift),
|
|
(0x0100000 << shift), (0x0400000 << shift),
|
|
(0x1000000 << shift), (0x4000000 << shift),
|
|
(0x0000002 << shift), (0x0000008 << shift),
|
|
(0x0000020 << shift), (0x0000080 << shift),
|
|
(0x0000200 << shift), (0x0000800 << shift),
|
|
(0x0002000 << shift), (0x0008000 << shift),
|
|
(0x0020000 << shift), (0x0080000 << shift),
|
|
(0x0200000 << shift), (0x0800000 << shift),
|
|
(0x2000000 << shift), (0x8000000 << shift));
|
|
|
|
if (++global == 1) {
|
|
/* First time we're here. Let's run ctx[1] and return to ctx[0] when
|
|
* we're done. Note that we return to above the 'makecontext' call. */
|
|
if (setcontext(&ctx[1]) != 0) err(1, 2);
|
|
}
|
|
if (global != 2) {
|
|
/* When ++global was 1 we let ctx[1] run and returned to ctx[0], so
|
|
above ++global is executed again and should've become 2. */
|
|
err(1, 3);
|
|
}
|
|
|
|
/* Setup ctx[2] to run func2 */
|
|
if (getcontext(&ctx[2]) != 0) err(1, 4);
|
|
ctx[2].uc_stack.ss_sp = malloc(SSIZE);
|
|
ctx[2].uc_stack.ss_size = SSIZE;
|
|
ctx[2].uc_link = &ctx[1];
|
|
makecontext(&ctx[2], (void (*) (void)) func2, 0);
|
|
|
|
/* Now things become tricky. ctx[2] is set up such that when it finishes
|
|
running, and starts ctx[1] again. However, func1 swaps back to func2. Then,
|
|
when func2 has finished running, we continue with ctx[1] and, finally, we
|
|
return to ctx[0]. */
|
|
if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 5); /* makecontext failed? */
|
|
reentered_main = 1;
|
|
|
|
/* The call graph is as follows:
|
|
*
|
|
* ########
|
|
* /--------># main #
|
|
* 7 /----########----\
|
|
* | | ^ |
|
|
* | 1 2 3
|
|
* | V | V
|
|
* #########----/ #########
|
|
* # func1 #<-------4-------# func2 #
|
|
* #########--------5------>#########
|
|
* ^ |
|
|
* | |
|
|
* \---------6--------/
|
|
*
|
|
* Main calls func1, func1 increases entered_func1, and returns to main. Main
|
|
* calls func2, swaps to func1, swaps to func2, which increases entered_func2,
|
|
* continues with func1, which increases entered_func1 again, continues to
|
|
* main, where reentered_main is set to 1. In effect, entered_func1 == 2,
|
|
* entered_func2 == 1, reentered_main == 1. Verify that. */
|
|
|
|
if (entered_func1 != 2) err(1, 6);
|
|
if (entered_func2 != 1) err(1, 7);
|
|
/* reentered_main == 1 is verified upon exit */
|
|
|
|
/* Try to allocate too small a stack */
|
|
free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
|
|
if (getcontext(&ctx[2]) != 0) err(1, 8);
|
|
ctx[2].uc_stack.ss_sp = malloc(MINSIGSTKSZ-1);
|
|
ctx[2].uc_stack.ss_size = MINSIGSTKSZ-1;
|
|
ctx[2].uc_link = &ctx[0];
|
|
makecontext(&ctx[2], (void (*) (void)) fail, 0);
|
|
/* Because makecontext is void, we can only detect an error by trying to use
|
|
the invalid context */
|
|
if (swapcontext(&ctx[0], &ctx[2]) == 0) err(1, 9);
|
|
|
|
/* Try to allocate a huge stack to force the usage of brk/sbrk system call
|
|
to enlarge the data segment. Because we are fiddling with the stack
|
|
pointer, the OS might think the stack segment and data segment have
|
|
collided and kill us. This is wrong and therefore the following should
|
|
work. */
|
|
free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
|
|
if (getcontext(&ctx[2]) != 0) err(1, 14);
|
|
ctx[2].uc_stack.ss_sp = malloc(8 * 1024 * 1024); /* 8 MB */
|
|
ctx[2].uc_stack.ss_size = 8 * 1024 * 1024;
|
|
ctx[2].uc_link = &ctx[0];
|
|
makecontext(&ctx[2], (void (*) (void)) test_brk, 0);
|
|
if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 15);
|
|
|
|
ctx[1].uc_link = &ctx[0];
|
|
ctx[2].uc_link = NULL;
|
|
makecontext(&ctx[1], (void (*) (void)) do_parent, 0);
|
|
makecontext(&ctx[2], (void (*) (void)) do_child, 0);
|
|
if (swapcontext(&ctx[0], &ctx[2]) == -1) err(1, 16);
|
|
|
|
quit();
|
|
return(-1);
|
|
}
|
|
|
|
/* We can't use a global subtest variable reliably, because threads might
|
|
change the value when we reenter a thread (i.e., report wrong subtest
|
|
number). */
|
|
void err(int sub, int error_no)
|
|
{
|
|
subtest = sub;
|
|
e(error_no);
|
|
}
|
|
|