369 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			10 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>
 | 
						|
 | 
						|
/* 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). */
 | 
						|
#define err(st, en) do { subtest = st; e(en); } while(0)
 | 
						|
 | 
						|
void do_calcs(void);
 | 
						|
void do_child(void);
 | 
						|
void do_parent(void);
 | 
						|
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);
 | 
						|
void func2(void);
 | 
						|
void just_exit(void);
 | 
						|
void test_brk(void);
 | 
						|
void verify_main_reenter(void);
 | 
						|
 | 
						|
int max_error = 5;
 | 
						|
#include "common.h"
 | 
						|
 | 
						|
#define SSIZE 32768
 | 
						|
#define ROUNDS 10
 | 
						|
#define SWAPS 10
 | 
						|
 | 
						|
 | 
						|
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)
 | 
						|
{
 | 
						|
  ucontext_t dummy;
 | 
						|
  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);
 | 
						|
 | 
						|
  /* Quick check to make sure that getcontext does not reset the FPU state. */
 | 
						|
  getcontext(&dummy);
 | 
						|
 | 
						|
  if (fegetround() != FE_UPWARD) err(10, 3);
 | 
						|
 | 
						|
  while(s < SWAPS) {
 | 
						|
	do_calcs();
 | 
						|
	if (fegetround() != FE_UPWARD) err(10, 4);
 | 
						|
	s++;
 | 
						|
	if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 5);
 | 
						|
  }
 | 
						|
  /* Returning to main thread through uc_link */
 | 
						|
}
 | 
						|
 | 
						|
static 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 set_context_test_value;
 | 
						|
static void set_context_test_thread1(void){
 | 
						|
	set_context_test_value |= 0x1;
 | 
						|
	setcontext(&ctx[2]);
 | 
						|
	err(1, 24);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static void set_context_test_thread2(void){
 | 
						|
	set_context_test_value |= 0x1 << 1;
 | 
						|
	setcontext(&ctx[0]);
 | 
						|
	err(1, 23);
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
 | 
						|
  /* test setcontext  first do some cleanup */
 | 
						|
 | 
						|
  free(ctx[1].uc_stack.ss_sp);
 | 
						|
  free(ctx[2].uc_stack.ss_sp);
 | 
						|
 | 
						|
  memset(&ctx[0],0,sizeof(ucontext_t));
 | 
						|
  memset(&ctx[1],0,sizeof(ucontext_t));
 | 
						|
  memset(&ctx[2],0,sizeof(ucontext_t));
 | 
						|
 | 
						|
 | 
						|
  /* create 3 new contexts */
 | 
						|
  volatile int cb =1;
 | 
						|
 | 
						|
  /* control will be returned here */
 | 
						|
  set_context_test_value  = 0;
 | 
						|
  if (getcontext(&ctx[0]) != 0) err(1, 17);
 | 
						|
  if (set_context_test_value != 0x3) err(1, 20);
 | 
						|
  if (cb == 1) {
 | 
						|
    cb =0;
 | 
						|
    if (getcontext(&ctx[1]) != 0) err(1, 18);
 | 
						|
    if (getcontext(&ctx[2]) != 0) err(1, 19);
 | 
						|
 | 
						|
    // allocate new stacks */
 | 
						|
    ctx[1].uc_stack.ss_sp = malloc(SSIZE);
 | 
						|
    ctx[1].uc_stack.ss_size = SSIZE;
 | 
						|
    ctx[2].uc_stack.ss_sp = malloc(SSIZE);
 | 
						|
    ctx[2].uc_stack.ss_size = SSIZE;
 | 
						|
 | 
						|
    makecontext(&ctx[1],set_context_test_thread1,0);
 | 
						|
    makecontext(&ctx[2],set_context_test_thread2,0);
 | 
						|
    if( setcontext(&ctx[1]) != 0){
 | 
						|
      err(1, 21);
 | 
						|
    }
 | 
						|
    err(1, 22);
 | 
						|
  }
 | 
						|
 | 
						|
  quit();
 | 
						|
  return(-1);
 | 
						|
}
 | 
						|
 |