/* probe.c (emx+gcc) */

#define INCL_DOSPROCESS
#define INCL_DOSERRORS
#include <os2.h>
#include <stdlib.h>
#include <stdio.h>

#define TESTS   14

static ULONG init_alloc;

static VOID APIENTRY thread (ULONG arg);

static TID start_thread (ULONG arg)
{
  ULONG rc;
  TID tid;

  /* STACK_SPARSE is the point of creating another thread and doing
     the tests in that thread... */

  rc = DosCreateThread (&tid, thread, arg, CREATE_READY | STACK_SPARSE,
                        256 * 1024);
  if (rc != 0)
    {
      fprintf (stderr, "DosCreateThread failed, rc=%lu\n", rc);
      exit (2);
    }
  return tid;
}

/* Wait for termination of a thread.  If the thread ends before
   DosWaitThread gets called, we'll get ERROR_INVALID_THREADID. */

static void wait_thread (TID tid)
{
  ULONG rc;

  rc = DosWaitThread (&tid, DCWW_WAIT);
  if (rc != 0 && rc != ERROR_INVALID_THREADID)
    {
      fprintf (stderr, "DosWaitThread failed, rc=%lu\n", rc);
      exit (2);
    }
}

int main (int argc, char *argv[])
{
  int auto_alloc = 0;
  int quiet = 0;

  if (_osmode != OS2_MODE)
    {
      puts ("This program requires OS/2.");
      return 0;                 /* Don't abort regression test */
    }

  if (argc == 2 && strcmp (argv[1], "-a") == 0)
    auto_alloc = 1;
  else if (argc == 3 && strcmp (argv[1], "-a") == 0
           && strcmp (argv[2], "-q") == 0)
    {
      auto_alloc = 1; quiet = 1;
    }
  else if (argc == 3 && strcmp (argv[1], "-i") == 0)
    init_alloc = (ULONG)strtol (argv[2], NULL, 0);
  else if (argc != 1)
    {
      puts ("Usage: probe [-i <init_alloc> | -a [-q]]");
      return 1;
    }

  if (auto_alloc)
    {
      ULONG i;
      TID tid[TESTS];

      for (init_alloc = 0; init_alloc <= 0x1000; init_alloc += 4)
        {
          if (!quiet)
            printf ("init_alloc = 0x%.4lx\n", init_alloc);
          for (i = 0; i < TESTS; ++i)
            tid[i] = start_thread (i);
          for (i = 0; i < TESTS; ++i)
            wait_thread (tid[i]);
        }
    }
  else
    {
      ULONG i;
      TID tid;

      for (i = 0; i < TESTS; ++i)
        {
          printf ("Test %lu...\n", i);
          tid = start_thread (i);
          wait_thread (tid);
        }
    }
  
  return 0;
}

struct big
{
  char a[8190];
  char c1, c2;
};

static volatile char x;


static void test0 (void)
{
  char a[4096];

  a[0] = x;
}


static void test1 (void)
{
  char a[8192];

  a[0] = x;
}


static void test2 (void)
{
  char a[100000];

  a[0] = x;
}


static struct big test3b (void)
{
  struct big b;

  return b;
}


static char test3 (void)
{
  char a[4096];

  a[0] = x;
  return test3b().c1;
}


static void test4 (void)
{
  char *p = 0;                  /* Avoid trap for init_alloc == 0x0fec */

  p = alloca (10000);
  *p = 0;
}


static void test5 (void)
{
  char *p;

  p = alloca (100000);
  *p = 0;
}


static void test6 (ULONG n)
{
  char *p;

  p = alloca (n);
  *p = 0;
}


static void test7 (int loops)
{
  char *p = 0;                  /* Avoid trap for init_alloc == 0x0fe8 */

  while (loops-- > 0)
    {
      p = alloca (10000);
      *p = 0;
    }
}


static void test8 (int loops, ULONG n)
{
  char *p;

  while (loops-- > 0)
    {
      p = alloca (n);
      *p = 0;
    }
}


static VOID APIENTRY thread (ULONG arg)
{
  __asm__ ("subl %0, %%esp" : : "g"(init_alloc) : "sp");
  switch (arg)
    {
    case 0:
      test0 ();
      break;
    case 1:
      test1 ();
      break;
    case 2:
      test2 ();
      break;
    case 3:
      test3 ();
      break;
    case 4:
      test4 ();
      break;
    case 5:
      test5 ();
      break;
    case 6:
      test6 (0);
      break;
    case 7:
      test6 (4000);
      break;
    case 8:
      test6 (4096);
      break;
    case 9:
      test6 (4100);
      break;
    case 10:
      test6 (8192);
      break;
    case 11:
      test6 (16384);
      break;
    case 12:
      test7 (4);
      break;
    case 13:
      test8 (4, 8192);
      break;
    }
  DosExit (EXIT_THREAD, 0);
}
