/* Copyright (C) 2021-2024 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

/* synprog.c - synthetic program to use for testing performance tools */
#define _GNU_SOURCE
#include <sched.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/ucontext.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sched.h>
#include <sys/resource.h>
#include <string.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <fcntl.h>
#include "stopwatch.h"

int get_ncpus ();
int get_clock_rate ();
void acct_init (char *);        /* initialize accounting */
void iotrace_init (char *);     /* initialize IO trace accounting */
void commandline (char *);      /* routine execute a scenario */
void forkcopy (char *, int);    /* fork copy of self to run string */
int clonecopy (void *);
#define CLONE_STACK_SIZE    8388608
#define CLONE_TLS_SIZE      4096
#define CLONE_RED_SIZE      4096
#define CLONE_IO            0x80000000      /* Clone io context */
void forkchild (char *);    /* fork child to run string */
void reapchildren (void);   /* reap all children */
void reapchild (int);       /* reap a child after getting SIGCLD */
void check_sigmask ();      /* check that SIGPROF and SIGEMT are not masked */
void masksig ();            /* real code to mask SIGPROF and SIGEMT */

hrtime_t progstart;
hrtime_t progvstart;
hrtime_t gethrustime ();
static int include_system_time = 0;

static hrtime_t
getmyvtime ()
{
  if (include_system_time == 0)
    return gethrvtime ();
  return gethrustime ();
}

void (*sigset (int sig, void (*disp)(int)))(int);
#define ITIMER_REALPROF ITIMER_REAL
/* Linux needs to have this defined for RTLD_NEXT and RTLD_DEFAULT */
/* If the first argument of `dlsym' or `dlvsym' is set to RTLD_NEXT */
#define RTLD_NEXT      ((void *) -1l)
/* If the first argument to `dlsym' or `dlvsym' is set to RTLD_DEFAULT */
#define RTLD_DEFAULT   ((void *) 0)

FILE *fid;
FILE *fid2;
double testtime = 3.0;
static char acct_file[128];
static char new_name[128];
static char child_name[128];

/* descendant process tracking */
static unsigned syn_fork = 0;
static unsigned syn_exec = 0;
static unsigned syn_combo = 0;

/* various behavior routines */
int bounce (int);       /* bounce with a->b->a->b-> ... */
int callso (int);       /* so load test */
int callsx (int);       /* alternate so load test */
int correlate (int);    /* test correlation with profiling */
int cputime (int);      /* use a bunch of user cpu time (fp) */
int doabort (int);      /* force a SEGV by dereferencing NULL */
int dousleep (int);     /* loop with a usleep call */
int endcases (int);     /* test various code construct endcases */
int fitos (int);        /* test various code construct endcases */
int gpf (int);          /* show gprof fallacy */
int hrv (int);          /* gethrvtime calls */
int icputime (int);     /* use a bunch of user cpu time (long) */
int iofile (int);       /* do operations on a temporary file */
int iotest (int);       /* do various io system calls */
int ioerror (int);      /* do various erroneous io system calls */
int ldso (int);         /* use a bunch of time in ld.so */
int masksignals (int);  /* mask the SIGEMT and SIGPROF signals */
int memorymap (int);    /* do mmap operation for io tracing */
int muldiv (int);       /* do integer multiply/divide for a time */
int naptime (int);      /* sleep for a time */
int pagethrash (int);   /* thrash around in memory */
int recurse (int);      /* recursion test */
int recursedeep (int);  /* deep recursion test */
int sched (int);        /* loop over sched_yield calls */
int sigtime (int);      /* use a bunch of time in a signal handler */
int synccall (int);     /* loop over sync() system calls */
int systime (int);      /* use a bunch of system time */
int tailcallopt (int);  /* tail call optimization test */
int underflow (int);    /* force underflow arithmetic */
int unwindcases (int);  /* test various unwind corner cases */

int itimer_realprof (int); /* mess with itimer ITIMER_REALPROF */
int sigprof (int);      /* mess with SIGPROF sigaction */
int sigprofh (int);     /* mess with SIGPROF handler */
int do_chdir (int);     /* do a chdir() */
int do_exec (int);      /* do an exec() call */
int do_popen (int);     /* do a popen() call */
int do_system (int);    /* do a system() call */
int do_forkexec (int);  /* do a fork()+exec() call combo */
int
do_vforkexec (int);     /* do a vfork()+exec() call combo */

/* lookup table for behavior scripts */
struct scripttab
{
  char *name;
  int (*function)(int);
  char *acctname;
  int param;
  int noverify;
};

static int CLONE_FLAGS[] = {
  SIGCHLD,
  CLONE_FILES | CLONE_FS | CLONE_SYSVSEM | CLONE_IO | SIGCHLD
};

/* the default script */
static char DEFAULT_COMMAND[] =
        "icpu.md.cpu.rec.recd.dousl.gpf.fitos.ec.tco.b.nap.uf."
        "sys.sig.so.sx.so.sched";

struct scripttab scripttab[] = {
  {"abt",       doabort,        "doabort",      0,  0},
  {"b",         bounce,         "bounce",       0,  0},
  {"c",         correlate,      "correlate",    0,  0},
  {"chdir",     do_chdir,       "chdir",        0,  0},
  {"chdirX",    do_chdir,       "chdir",        -1, 0},
  {"cpu",       cputime,        "cputime",      0,  0},
  {"dousl",     dousleep,       "dousleep",     0,  1},
  {"ec",        endcases,       "endcases",     0,  0},
  {"exec",      do_exec,        "exec",         0,  0},
  {"execX",     do_exec,        "do_exec",      -1, 0},
  {"fitos",     fitos,          "fitos",        0,  1},
  {"gpf",       gpf,            "gpf",          0,  0},
  {"hrv",       hrv,            "hrv",          0,  0},
  {"icpu",      icputime,       "icputime",     0,  0},
  {"iofile",    iofile,         "iofile",       0,  0},
  {"iotest",    iotest,         "iotest",       0,  0},
  {"ioerror",   ioerror,        "ioerror",      0,  0},
  {"itimer",    itimer_realprof, "itimer",      1,  0},
  {"itimer0",   itimer_realprof, "itimer",      0,  0},
  {"ldso",      ldso,           "ldso",         0,  0},
  {"masksig",   masksignals,    "masksig",      0,  0},
  {"md",        muldiv,         "muldiv",       0,  0},
  {"memorymap", memorymap,      "memorymap",    100, 0},
  {"nap",       naptime,        "naptime",      0,  0},
  {"pg",        pagethrash,     "pagethrash",   32, 0},
  {"popen",     do_popen,       "popen",        0,  0},
  {"popenX",    do_popen,       "popen",        -1, 0},
  {"rec",       recurse,        "recurse",      50, 0},
  {"recd",      recursedeep,    "<Truncated-stack>", 500, 0},
  {"sched",     sched,          "sched",        0,  1},
  {"so",        callso,         "callso",       0,  0},
  {"sx",        callsx,         "callsx",       0,  0},
  {"sig",       sigtime,        "sigtime_handler", 0,  1},
  {"sigprof",   sigprof,        "sigprof",      1,  0},
  {"sigprof0",  sigprof,        "sigprof",      0,  0},
  {"sigprofh",  sigprofh,       "sigprofh",     1,  0},
  {"sigprofh0", sigprofh,       "sigprofh",     0,  0},
  {"sync",      synccall,       "synccall",     0,  1},
  {"sys",       systime,        "systime",      0,  1},
  {"system",    do_system,      "system",       0,  0},
  {"systemX",   do_system,      "do_system",    -1, 0},
  {"tco",       tailcallopt,    "tailcallopt",  0,  0},
  {"uf",        underflow,      "underflow",    0,  1},
  {"forkexec",  do_forkexec,    "forkexec",     0,  0},
  {"vforkexec", do_vforkexec,   "vforkexec",    0,  0},
  {"uwdc",      unwindcases,    "unwindcases_handler", 0,  0},
  {NULL, NULL, NULL, 0, 0}
};

int
main (int argc, char **argv)
{
  int i;
  hrtime_t start;
  hrtime_t vstart;
  char *name;
  char buf[1024];
  char arglist[4096];

  // need a more robust test of whether system HWC events are being counted
  if (getenv ("TILDECLAUSE"))
    include_system_time = 1;
  progstart = gethrtime ();
  progvstart = getmyvtime ();
  name = getenv ("SP_COLLECTOR_TEST_TIMER");
  if (name)
    {
      testtime = atof (name);
      if (testtime <= 0)
        testtime = 1.0;
    }
  name = getenv ("_SP_NAME");
  if (name == NULL || strlen (name) == 0)
    strcpy (acct_file, "synprog.acct");
  else
    strcpy (acct_file, name);

  strcpy (arglist, argv[0]);
  for (i = 1; i < argc; i++)
    {
      strcat (arglist, " ");
      strcat (arglist, argv[i]);
    }

  sprintf (buf, "%s run", argv[0]);
  wlog (buf, NULL);

  int ncpus = get_ncpus ();
  acct_init (acct_file);
  iotrace_init ("synprog.acct2");

  /* Start a timer */
  start = gethrtime ();
  vstart = getmyvtime ();

#ifndef NO_MS_ACCT
  stpwtch_calibrate ();
#endif

  if (argc == 1)
    commandline (DEFAULT_COMMAND);
  else
    {
      i = 2;
      while (i < argc)
        {
          forkcopy (argv[i], i - 1);
          i++;
        }

      /* do the last one ourself */
      commandline (argv[1]);
    }
  reapchildren ();
  whrvlog (gethrtime () - start, getmyvtime () - vstart, buf, NULL);
  fflush (fid);
  fflush (fid2);
  fclose (fid);
  fclose (fid2);
  return 0;
}

/* acct_init: initialize accounting */
void
acct_init (char *acct_file)
{
  fid = fopen (acct_file, "w");
  if (fid == NULL)
    {
      fprintf (stderr, "Open of %s for output failed: %s\n",
               acct_file, strerror (errno));
      exit (1);
    }
  fprintf (fid, "MHz: %d\n", get_clock_rate ());
  fprintf (fid, "X   Incl. Total  Incl. CPU  Name\n");
  fflush (fid);

  /* write a record for <Unknown>, which should have zero times */
  fprintf (fid, "X   %6.3f        %6.3f       %s\n", 0.0, 0.0, "<Unknown>");

  /* set up to reap any children */
  (void) sigset (SIGCHLD, reapchild);
  /* verify the signal mask */
}

/* iotrace_init: initialize IO trace accounting */
void
iotrace_init (char *acct_file)
{
  fid2 = fopen (acct_file, "w");
  if (fid2 == NULL)
    {
      fprintf (stderr, "Open of %s for output failed: %s\n",
               acct_file, strerror (errno));
      exit (1);
    }
  fprintf (fid2, "X   Incl.BytesRead  Incl.ReadCount  ");
  fprintf (fid2, "Incl.BytesWritten  Incl.WriteCount  ");
  fprintf (fid2, "Incl.OtherIOCount   Name\n");
  fflush (fid2);
}

/* commandline -- process a command line string:
 *	verbs are separated by a . character; each verb is looked-up
 *	in a table, and the routine to process it, and argument fetched.
 *	the routine is called.
 */
void
commandline (char *cmdline)
{
  char *p;
  char *j;
  char prevj;
  struct scripttab *k;
  char buf[1024];
  hrtime_t pstart;
  hrtime_t pvstart;
  hrtime_t pend;
  hrtime_t pvend;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog (" Begin commandline", cmdline);

  p = cmdline;
  while (*p != 0)
    {
      /* find the terminator for this verb (a . or NULL) */
      j = p;
      while (*j != 0 && *j != '.')
        j++;
      prevj = *j;
      *j = 0;

      /* now look up the phase in the table */
      for (k = &scripttab[0];; k++)
        {
          if (k->name == NULL)
            break;
          if (strcmp (p, k->name) == 0)
            {
              /* found a match */
              pstart = gethrtime ();
              pvstart = getmyvtime ();
              (k->function)(k->param);
              pend = gethrtime ();
              pvend = getmyvtime ();
              fprintf (fid, "%c   %6.3f        %6.3f       %s\n",
                       k->noverify == 0 ? 'X' : 'Y',
                       (double) (pend - pstart) / (double) 1000000000.,
                       (double) (pvend - pvstart) / (double) 1000000000.,
                       k->acctname);
              fflush (fid);
              break;
            }
        }
      if (k->name == NULL)
        {
          sprintf (buf, "++ ignoring `%s'\n", p);
          fprintf (stderr, buf);
        }

      /* continue processing */
      *j = prevj;
      p = j;
      if (prevj != 0)
        p++;
    }
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "commandline", cmdline);
}

int
clonecopy (void * script)
{
  syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */

  strcpy (acct_file, child_name);
  /*printf("_SP_NAME=\"%s\" (for clone-child)\n", acct_file);*/
  acct_init (acct_file);

  /* execute the script */
  commandline ((char *) script);

  /* reap the child's descendants */
  reapchild (0);
  exit (0);
}

/* forkcopy -- fork a copy to run a script */
void
forkcopy (char *script, int child)
{
  int child_pid;
  if (strncmp ("clone", script, 5) == 0)
    {
      //clone instead of fork
      /* Log the event */
      wlog ("cloning copy ... ", script);

      sprintf (child_name, "%s_C%d", acct_file, ++syn_fork);
      /* clone a new process */
      void * stack;
      void * stack_space;
      int stack_size = CLONE_STACK_SIZE;

      stack_space = mmap (NULL, stack_size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS, -1, 0);
      if ((void*) - 1 == stack_space)
        {
          fprintf (stderr, "Error: mmap returned -1\n");
          exit (1);
        }
      mprotect (stack_space, CLONE_RED_SIZE, PROT_NONE);
      stack = (char *) stack_space + stack_size - CLONE_TLS_SIZE; // stack grows back
      child_pid = clone (clonecopy, stack, CLONE_FLAGS[(child + 1) % 2],
                         (void *) (script + sizeof ("clone") - 1));
      if (child_pid < 0)
        {
          /* error, could not fork */
          fprintf (stderr, "forkcopy: clone failed--error %d\n", errno);
          exit (1);
        }

      fprintf (stderr, "child process %d cloned by %d.\n",
               child_pid, (int) getpid ());
      return;
    }

  /* Log the event */
  wlog ("forking copy ... ", script);
  sprintf (child_name, "%s_f%d", acct_file, ++syn_fork);

  /* fork a new process */
  child_pid = fork ();
  if (child_pid < 0)
    {
      /* error, could not fork */
      fprintf (stderr, "forkcopy: fork failed--error %d\n", errno);
      exit (1);

    }
  else if (child_pid == 0)
    {
      syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */
      strcpy (acct_file, child_name);
      acct_init (acct_file);

      /* execute the script */
      commandline (script);

      /* reap the child's descendants */
      reapchild (0);
      exit (0);
    }
  fprintf (stderr, "child process %d forked by %d.\n",
           child_pid, (int) getpid ());
}

void
forkchild (char * cmdline)
{
  stopwatch_t *prog;
  char mbuf[1024];

  /* Start a stopwatch */
  sprintf (mbuf, "%s pid[%d]", "Synprog child", (int) getpid ());
  prog = stpwtch_alloc (mbuf, 0);

  /* process this child's command-line */
  commandline (cmdline);

  /* reap the child's descendants */
  reapchild (0);

  /* Stop print, and free the stopwatch */
  stpwtch_stop (prog);
  stpwtch_print (prog);
  free (prog);

  exit (0);
}

/* reap a child process, called in response to SIGCLD */
void
reapchild (int sig)
{
  int status;
  int ret = wait (&status);
  sigset (SIGCLD, reapchild);
}

/* reap all child processes prior to exit */
void
reapchildren ()
{
  int status;
  int ret;

  /* wait for all children to exit */
  for (;;)
    {
      while ((ret = wait (&status)) != (pid_t) - 1)
        fprintf (stderr, "synprog: reap child %x\n", ret);
      if (errno == EINTR)
        continue;
      if (errno == ECHILD)
        return;
      fprintf (stderr, "synprog: unexpected errno from wait() syscall -- %s\n",
               strerror (errno));
    }
}

/* doabort -- force a SEGV */
int
doabort (int k)
{
  char *nullptr = NULL;
  char c;

  /* Log the event */
  wlog ("start of doabort", NULL);

  /* and dereference a NULL */
  c = *nullptr;

  /* this should never be reached */
  return (int) c;
}

/* =============================================================== */

/*	dousleep -- loop with a usleep */
int
dousleep (int k)
{
  volatile double x;
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of dousleep", NULL);
  do
    {
      x = 0.0;
      for (int j = 0; j < 1000000; j++)
        x = x + 1.0;
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "dousleep", NULL);
  /* this should never be reached */
  return (int) 0;
}

/* =============================================================== */
/*	correlate -- generate CPU use, correlated with profiling clock */

static void csig_handler (int);

int
correlate (int k)
{
  volatile float x; /* temp variable for f.p. calculation */
  struct itimerval tval;
  int retval;
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of correlate", NULL);

  /* set up the signal handler */
  sigset (SIGALRM, csig_handler);

  /* set an itimer, to break out of the sleep loop */
  tval.it_value.tv_sec = 0;
  tval.it_value.tv_usec = 10000;
  tval.it_interval.tv_sec = 0;
  tval.it_interval.tv_usec = 10000;

  retval = setitimer (ITIMER_REAL, &tval, 0);
  if (retval != 0)
    fprintf (stderr, "setitimer(ITIMER_REAL) got %d returned: %s\n",
             retval, strerror (errno));
  do
    {
      x = 0.0;
      for (int j = 0; j < 1000000; j++)
        x = x + 1.0;
      sleep (1); /* relying on the timer to break out */
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  /* now disable the itimer */
  tval.it_value.tv_sec = 0;
  tval.it_value.tv_usec = 0;
  tval.it_interval.tv_sec = 0;
  tval.it_interval.tv_usec = 0;

  retval = setitimer (ITIMER_REAL, &tval, 0);
  if (retval != 0)
    fprintf (stderr, "setitimer(ITIMER_REAL) got %d returned: %s\n",
             retval, strerror (errno));
  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "correlate", NULL);
  return 0;
}

static void
csig_handler (int sig)
{
  return;
}

/* cputime -- loop to use a bunch of user time (f.p.) */
int
cputime (int k)
{
  volatile float x; /* temp variable for f.p. calculation */
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of cputime", NULL);
  do
    {
      x = 0.0;
      for (int j = 0; j < 1000000; j++)
        x = x + 1.0;
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "cputime", NULL);
  return 0;
}

/* icputime -- loop to use a bunch of user time (long) */
int
icputime (int k)
{
  volatile long x; /* temp variable for long calculation */
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of icputime", NULL);
  do
    {
      x = 0;
      for (int j = 0; j < 1000000; j++)
        x = x + 1;
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "icputime", NULL);
  return 0;
}

/* hrv -- loop to do lots of gethrvtime calls */
int
hrv (int k)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of hrv", NULL);
  do
    {
      for (int j = 0; j < 10000; j++)
        (void) gethrvtime ();
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "hrv", NULL);
  return 0;
}

/* =============================================================== */

/*	ldso -- use up time in ld.so */

int
ldso (int k)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of ldso", NULL);
  do
    {
      for (int j = 0; j < 10000; j++)
        (void) dlsym (RTLD_DEFAULT, "nosuchfoo");
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());
  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "ldso", NULL);
  return 0;
}

/* masksignals -- debug aid -- call routine to mask SIGPROF and SIGEMT */
int
masksignals (int n)
{
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "masksignals", NULL);
  return 0;
}

/* =============================================================== */
/*	muldiv -- loop to do a bunch of integer multiply and divide */

volatile int tmp_ival = 0;

int
muldiv (int n)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of muldiv", NULL);
  do
    {
      for (int i = 0; i < 1000; i++)
        {
          for (int j = 0; j < 1000; j++)
            tmp_ival = j * i / (i + 1.0);
        }
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());
  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "muldiv", NULL);
  return 0;
}


/* =============================================================== */
/*	underflow -- loop triggering arithmetic underflow */
volatile float tmp_fval;

int
underflow (int k)
{
  float x, y;
  long long count = 0;

  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of underflow", NULL);
  do
    {
      x = 1.e-20;
      y = 1.e-20;
      for (int j = 0; j < 50000; j++)
        tmp_fval = x * y;
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());
  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "underflow", NULL);
  return 0;
}

/* naptime -- spend time in the system sleeping */
int
naptime (int k)
{
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of naptime", NULL);
  if (k == 0)
    {
      k = testtime;
      if (k < 1)
        k = 1;
    }
  for (int i = 0; i < k; i++)
    sleep (1);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "naptime", NULL);
  return 0;
}

/* recurse -- loop to show recursion */
int real_recurse (int, int); /* real routine to do recursion */

int
recurse (int k)
{
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of recurse", NULL);
  if (k == 0)
    k = 80;
  (void) real_recurse (0, k);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "recurse", NULL);
  return 0;
}

int
recursedeep (int k)
{
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of recursedeep", NULL);
  if (k == 0)
    k = 500;
  (void) real_recurse (0, k);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "recursedeep", NULL);
  return 0;
}

static int rec_count = 0;

int
real_recurse (int i, int imax)
{
  if (i == imax)
    {
      volatile float x;
      long long count = 0;
      hrtime_t start = gethrtime ();
      do
        {
          x = 0.0;
          for (int j = 0; j < 10000000; j++)
            x = x + 1.0;
          count++;
        }
      while (start + testtime * 1e9 > gethrtime ());
      fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
      return rec_count;
    }
  else
    {
      real_recurse (i + 1, imax);
      rec_count++;
      return rec_count;
    }
}

/* gpf -- simple example showing the gprof fallacy */
float gpf_a (void);
float gpf_b (void);
float gpf_work (int);

#define MAX_GPF_WORK_COUNT 1000

int
gpf (int k)
{
  long long count = 0;
  float x = -1.0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of gpf", NULL);

  do
    {
      x = gpf_a ();
      x += gpf_b ();
      count++;
      if (count == MAX_GPF_WORK_COUNT)
        fprintf (stderr, "Execution error -- %lld iterations of gpf_[ab]; possible compiler bug\n",
                 count);
    }
  while (start + testtime * 1e9 > gethrtime ());

  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  if (x < 0.0)
    fprintf (stderr, "Execution error -- x < 0.0; possible compiler bug (x = %f)\n", x);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "gpf - total", NULL);
  return 0;
}

float
gpf_a ()
{
  float x = -1.0;
  for (int i = 0; i < 9; i++)
    x += gpf_work (1);
  return x;
}

float
gpf_b ()
{
  float x = -1.0;
  x = gpf_work (10);
  return x;
}

float
gpf_work (int amt)
{
  volatile float x = 0.0;
  int imax = 4 * amt * amt;
  for (int i = 0; i < imax; i++)
    {
      x = 0.0;
      for (int j = 0; j < 200000; j++)
        x = x + 1.0;
    }
  return x;
}

/*	bounce -- example of indirect recursion */
void bounce_a (int, int);
void bounce_b (int, int);

int
bounce (int k)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of bounce", NULL);
  if (k == 0)
    k = 20;
  do
    {
      bounce_a (0, k);
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());
  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "bounce", NULL);
  return 0;
}

void
bounce_a (int i, int imax)
{
  if (i == imax)
    {
      volatile float x = 0.0;
      for (int k = 0; k < 8; k++)
        {
          for (int j = 0; j < 2000000; j++)
            x = x + 1.0;
        }
      return;
    }
  else
    bounce_b (i, imax);
}

void
bounce_b (int i, int imax)
{
  bounce_a (i + 1, imax);
  return;
}

/* =============================================================== */

/*	sched -- spend time calling sched_yield() */

int
sched (int k)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of sched", NULL);
  if (k == 0)
    {
      k = testtime;
      if (k < 1)
        k = 1;
    }
  do
    {
      for (int i = 0; i < 1000000; i++)
        sched_yield ();
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "sched", NULL);
  return 0;
}

/* synccall -- spend time calling sync() */
int
synccall (int k)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of synccall", NULL);
  if (k == 0)
    {
      k = testtime;
      if (k < 1)
        k = 1;
    }
  do
    {
      sync ();
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  fprintf (stderr, "   Performed %lld sync() calls\n", count);
  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "synccall", NULL);
  return 0;
}

/* sigtime -- spend time in a signal handler */
static void sigtime_handler (int);

int
sigtime (int k)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of sigtime", NULL);

  /* set up the signal handler */
  sigset (SIGHUP, sigtime_handler);
  do
    {
      kill (getpid (), SIGHUP);
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  sigset (SIGHUP, SIG_DFL);
  fprintf (stderr, "   Sent %lld SIGHUP signals\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigtime", NULL);
  return 0;
}

static void
sigtime_handler (int sig)
{
  volatile int x;
  for (int i = 0; i < 50; i++)
    {
      x = 0;
      for (int j = 0; j < 1000000; j++)
        x = x + 1;
    }
  return;
}

/* systime -- spend time in a few system calls */
int
systime (int k)
{
  struct timeval ttime;
  int j;
  long long count = 0;
  double t = testtime / 5;
  if (t < 1.0)
    t = 1.0;
  hrtime_t start = gethrtime ();
  hrtime_t rstart = start;
  hrtime_t vstart = getmyvtime ();
  hrtime_t rvstart = vstart;

  /* Log the event */
  wlog ("start of systime", NULL);

  /* do gettimeofday calls */
  do
    {
      for (j = 0; j < 30000; j++)
        {
          (void) gettimeofday (&ttime, NULL);
        }
      count++;
    }
  while (start + t * 1e9 > gethrtime ());

  hrtime_t end = gethrtime ();
  hrtime_t vend = getmyvtime ();
  fprintf (stderr, "   Performed %lld while-loop iterations gettimeofday\n", count);
  whrvlog (end - start, vend - vstart, "systime -- 10**6 gettimeofday", NULL);

  /* do gethrtime calls */
  start = gethrtime ();
  vstart = getmyvtime ();
  count = 0;
  do
    {
      (void) gethrtime ();
      count++;
    }
  while (start + t * 1e9 > gethrtime ());

  end = gethrtime ();
  vend = getmyvtime ();
  fprintf (stderr, "   Performed %lld while-loop iterations gethrtime\n", count);
  whrvlog ((end - start), (vend - vstart), "systime -- 10**6 gethrtime", NULL);

  /* do pairs of gethrtime calls */
  start = gethrtime ();
  vstart = getmyvtime ();

  count = 0;
  do
    {
      for (j = 0; j < 30000; j++)
        {
          (void) gethrtime ();
          (void) gethrtime ();
        }
      count++;
    }
  while (start + t * 1e9 > gethrtime ());

  end = gethrtime ();
  vend = getmyvtime ();
  fprintf (stderr, "   Performed %lld while-loop iterations pairs of gethrtime\n",
           count);
  whrvlog (end - start, vend - vstart, "systime -- 10**6  pairs of gethrtime",
  NULL);

  /* do gethrvtime calls */
  start = gethrtime ();
  vstart = getmyvtime ();

  count = 0;
  do
    {
      for (j = 0; j < 30000; j++)
        {
          (void) gethrvtime ();
        }
      count++;
    }
  while (start + t * 1e9 > gethrtime ());

  end = gethrtime ();
  vend = getmyvtime ();
  fprintf (stderr, "   Performed %lld while-loop iterations gethrvtime\n", count);
  whrvlog (end - start, vend - vstart, "systime -- 10**6 gethrvtime", NULL);

  /* do getrusage calls */
  start = gethrtime ();
  vstart = getmyvtime ();

  count = 0;
  do
    {
      for (j = 0; j < 30000; j++)
        {
          struct rusage rusage;
          (void) getrusage (RUSAGE_SELF, &rusage);
        }
      count++;
    }
  while (start + t * 1e9 > gethrtime ());

  end = gethrtime ();
  vend = getmyvtime ();
  fprintf (stderr, "   Performed %lld while-loop iterations getrusage\n", count);
  whrvlog ((end - start), (vend - vstart), "systime -- 10**6 getrusage", NULL);
  whrvlog ((gethrtime () - rstart), (getmyvtime () - rvstart), "systime", NULL);
  return 0;
}

/* unwindcases -- test various unwind corner cases */
static void unwindcases_handler (int);

int
unwindcases (int k)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of unwindcases", NULL);

  /* set up the signal handler */
  sigset (SIGHUP, unwindcases_handler);

  /* initialize the new signal mask */
  sigset_t new_mask;
  sigset_t old_mask;
  sigfillset (&new_mask);
  sigdelset (&new_mask, SIGHUP);

  /* block all signals except SIGHUP*/
  sigprocmask (SIG_SETMASK, &new_mask, &old_mask);
  do
    {
      kill (getpid (), SIGHUP);
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  sigprocmask (SIG_SETMASK, &old_mask, NULL);
  sigset (SIGHUP, SIG_DFL);
  fprintf (stderr, "   Sent %lld SIGHUP signals\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "unwindcases", NULL);
  return 0;
}

#define unwindcases_memcpy memcpy
#define unwindcases_memset memset
#define unwindcases_memnum (4096)

static char unwindcases_array[4097];
static volatile int srcind = 1024;

static void
unwindcases_handler (int sig)
{
  for (int i = 0; i < 1000; i++)
    {
      for (int j = 0; j < 1000; j++)
        {
          unwindcases_memset ((void*) unwindcases_array, 0, unwindcases_memnum);
          for (int k = 0; k < 10; k++)
            {
              unwindcases_array[k] = unwindcases_array[srcind];
              unwindcases_array[k + srcind / 4] = 0;
              unwindcases_array[k] = unwindcases_array[strlen (unwindcases_array + k) + 1];
            }
          unwindcases_memcpy ((void*) unwindcases_array,
                              (void*) (unwindcases_array + 4096 / 2),
                              unwindcases_memnum / 2);
        }
    }
  return;
}

/*	tailcallopt -- call routines that would be tail-call optimized when
 *		compiled with optimization
 */
void tailcall_a (void);
void tailcall_b (void);
void tailcall_c (void);

int
tailcallopt (int n)
{
  long long count = 0;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of tailcallopt", NULL);
  do
    {
      tailcall_a ();
      count++;
    }
  while (start + testtime * 1e9 > gethrtime ());

  fprintf (stderr, "   Performed %lld while-loop iterations\n", count);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "tailcallopt", NULL);
  return 0;
}

void
tailcall_a ()
{
  volatile float x = 0.0;
  for (int j = 0; j < 4000000; j++)
    x = x + 1.0;
  tailcall_b ();
}

void
tailcall_b ()
{
  volatile float x = 0.0;
  for (int j = 0; j < 4000000; j++)
    x = x + 1.0;
  tailcall_c ();
}

void
tailcall_c ()
{
  volatile float x = 0.0;
  for (int j = 0; j < 4000000; j++)
    x = x + 1.0;
}

int
itimer_realprof (int k) /* mess with itimer ITIMER_REALPROF */
{
  struct itimerval tval;
  int retval;
  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* set an itimer */
  if (k != 0)
    {
      wlog ("start of itimer_realprof", NULL);
      tval.it_interval.tv_sec = 1;
      tval.it_interval.tv_usec = 300000;
      tval.it_value = tval.it_interval;
    }
  else
    {
      wlog ("start of itimer_realprof(0)", NULL);
      tval.it_interval.tv_sec = 0;
      tval.it_interval.tv_usec = 0;
      tval.it_value = tval.it_interval;
    }
  retval = setitimer (ITIMER_REALPROF, &tval, 0);
  if (retval != 0)
    fprintf (stderr, "setitimer(ITIMER_REALPROF) got %d returned: %s\n",
             retval, strerror (errno));
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "itimer_realprof",
           NULL);
  return 0;
}

static struct sigaction old_sigprof_handler;
static void sigprof_handler (int sig);
static void sigprof_sigaction (int sig, siginfo_t *sip, ucontext_t *uap);

int
sigprof (int k)
{
  struct sigaction act;

  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of sigprof", NULL);

  /* query current handler */
  if (sigaction (SIGPROF, NULL, &act) == -1)
    printf ("\tFailed current sigaction query: %s\n", strerror (errno));
  else
    printf ("\tCurrently installed sigaction 0x%p\n", act.sa_sigaction);

  sigemptyset (&act.sa_mask);
  act.sa_flags = SA_RESTART | SA_SIGINFO;
  act.sa_sigaction = (void(*)(int, siginfo_t*, void*))sigprof_sigaction;

  if (k != 0)
    {
      /* install with deferral to original handler (if set) */
      if (sigaction (SIGPROF, &act, &old_sigprof_handler) == -1)
        printf ("\tFailed to install sigprof_sigaction: %s\n", strerror (errno));
      if (old_sigprof_handler.sa_sigaction == (void (*)(int, siginfo_t *, void *))SIG_DFL)
        {
          old_sigprof_handler.sa_sigaction = (void (*)(int, siginfo_t *, void *))SIG_IGN;
          printf ("\tReplaced default sigprof handler with 0x%p\n",
                  act.sa_sigaction);
        }
      else
        printf ("\tReplaced sigprof handler 0x%p with 0x%p\n",
                old_sigprof_handler.sa_sigaction, act.sa_sigaction);
    }
  else
    {
      /* installed without deferral to any original handler */
      old_sigprof_handler.sa_sigaction = (void (*)(int, siginfo_t *, void *))SIG_IGN;
      if (sigaction (SIGPROF, &act, NULL) == -1)
        printf ("\tFailed to install sigprof_sigaction: %s\n", strerror (errno));
      else
        printf ("\tInstalled sigprof_sigaction 0x%p\n", act.sa_sigaction);
    }

  whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigprof", NULL);
  return 0;
}

int
sigprofh (int k)
{
  struct sigaction act;

  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  /* Log the event */
  wlog ("start of sigprofh", NULL);

  /* query current handler */
  if (sigaction (SIGPROF, NULL, &act) == -1)
    printf ("\tFailed current sigaction query: %s\n", strerror (errno));
  else
    printf ("\tCurrently installed handler 0x%p\n", act.sa_handler);

  sigemptyset (&act.sa_mask);
  act.sa_flags = SA_RESTART;
  act.sa_handler = sigprof_handler;
  if (k != 0)
    {
      /* install with deferral to original handler (if set) */
      if (sigaction (SIGPROF, &act, &old_sigprof_handler) == -1)
        printf ("\tFailed to install sigprof_handler: %s\n", strerror (errno));
      if (old_sigprof_handler.sa_handler == SIG_DFL)
        {
          old_sigprof_handler.sa_handler = SIG_IGN;
          printf ("\tReplaced default sigprof handler with 0x%p\n",
                  act.sa_handler);
        }
      else
        printf ("\tReplaced sigprof handler 0x%p with 0x%p\n",
                old_sigprof_handler.sa_handler, act.sa_handler);
    }
  else
    {
      /* installed without deferral to any original handler */
      old_sigprof_handler.sa_handler = SIG_IGN;
      if (sigaction (SIGPROF, &act, NULL) == -1)
        printf ("\tFailed to install sigprof_handler: %s\n", strerror (errno));
      else
        printf ("\tInstalled sigprof_handler 0x%p\n", act.sa_handler);
    }

  whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigprofh", NULL);
  return 0;
}

static void
sigprof_handler (int sig)
{
  int j;
  volatile int x;

  hrtime_t now = gethrtime ();
  if (old_sigprof_handler.sa_handler == SIG_IGN)
    {
      whrvlog (now, 0, "sigprof_handler (ign)", NULL);
      for (j = 0, x = 0; j < 1000000; j++)
        x = x + 1;
    }
  else
    {
      whrvlog (now, 0, "sigprof_handler (fwd)", NULL);
      for (j = 0, x = 0; j < 1000000; j++)
        x = x + 1;
      /* forward signal to original handler */
      if (old_sigprof_handler.sa_flags & SA_SIGINFO)
        (old_sigprof_handler.sa_sigaction)(sig, NULL, NULL);
      else
        (old_sigprof_handler.sa_handler)(sig);
      printf ("\tReturned from original sigprof handler!\n");
    }

  return;
}

static void
sigprof_sigaction (int sig, siginfo_t *sip, ucontext_t *uap)
{
  int j;
  volatile int x;

  hrtime_t now = gethrtime ();
  if (old_sigprof_handler.sa_sigaction == (void (*)(int, siginfo_t *, void *))SIG_IGN)
    {
      whrvlog (now, 0, "sigprof_sigaction (ign)", NULL);
      for (j = 0, x = 0; j < 1000000; j++)
        x = x + 1;
    }
  else
    {
      whrvlog (now, 0, "sigprof_sigaction (fwd)", NULL);
      for (j = 0, x = 0; j < 1000000; j++)
        x = x + 1;
      /* forward signal to original handler */
      if (old_sigprof_handler.sa_flags & SA_SIGINFO)
        (old_sigprof_handler.sa_sigaction)(sig, sip, uap);
      else
        (old_sigprof_handler.sa_handler)(sig);
      printf ("\tReturned from original sigprof sigaction!\n");
    }
  return;
}

#if 0
Need to consider various signal handler / sigaction scenarios :

1. A handler is already installed, and a new handler is being installed.
(The original handler may be one of the defaults.)
2. A handler is already installed, and a sigaction is being installed.
3. A sigaction is already installed, and a new sigaction is being installed.
4. A sigaction is already installed, and a handler is being installed.
#endif

int
do_chdir (int k) /* switch to a new working directory */
{
  char *workdir;
  char *workdir0 = "/tmp";
  char *workdir1 = "/";
  char currworkdir[MAXPATHLEN];

  hrtime_t start = gethrtime ();
  hrtime_t vstart = getmyvtime ();

  if (k != 0)
    {
      wlog ("start of do_chdir(X)", NULL);
      workdir = workdir1;
    }
  else
    {
      wlog ("start of do_chdir", NULL);
      workdir = workdir0;
    }

  if (getcwd (currworkdir, sizeof (currworkdir)) == NULL)
    fprintf (stderr, "old getcwd failed: %s\n", strerror (errno));
  else
    printf ("old getcwd returned \"%s\"\n", currworkdir);

  if (chdir (workdir) != 0)
    fprintf (stderr, "chdir(\"%s\") failed: %s\n", workdir, strerror (errno));

  if (getcwd (currworkdir, sizeof (currworkdir)) == NULL)
    fprintf (stderr, "new getcwd failed: %s\n", strerror (errno));
  else
    printf ("new getcwd returned \"%s\"\n", currworkdir);
  whrvlog (gethrtime () - start, getmyvtime () - vstart, "do_chdir", NULL);
  return 0;
}

int
do_exec (int k) /* do an exec() call */
{
  sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec);
  if (putenv (new_name))
    fprintf (stderr, "Failed to name child! %s\n", strerror (errno));
  if (k >= 0)
    {
      wlog ("about to exec", NULL);
      execl ("./synprog", "synprog", "gpf.cpu.sx", NULL);
      wlog ("exec failed!!!", NULL);
    }
  else
    {
      wlog ("about to execX", NULL);
      execl ("./no-such-file", "no-such-file", "gpf.cpu.sx", NULL);
      wlog ("execX failed (as expected)", NULL);
    }
  return 0;
}

/* preloading libcollector to a setuid executable will fail! */
const char *cmdX = "/random/crash_n_burn";
const char *cmd0 = "/bin/uptime";
const char *cmd1 = "/bin/echo hello world!";
const char *cmd2 = "/usr/bin/sleep 5";
const char *cmd3 = "/usr/bin/sleep 5; /bin/echo hello world!";
const char *cmd4 = "/usr/bin/sleep 2; /bin/echo hello world!; /usr/bin/sleep 2";
const char *cmd5 = "/bin/date; /bin/sleep 2; /bin/date; /bin/sleep 2; /bin/date";
const char *cmd6 = "w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w";
const char *cmd7 = "synprog";
const char *cmd8 = "synprog icpu.sx 2>&1";

int
do_popen (int k) /* do a popen() call */
{
  int ret;
  FILE *fd;
  char buf[BUFSIZ];
  const char *mode = "r";

  /* XXXX popen() will temporarily vfork+exec() a new child */
  /* but there will be no accounting for it, unless it's synprog! */
  sprintf (new_name, "_SP_NAME=%s_c%d", acct_file, ++syn_combo);
  if (putenv (new_name))
    fprintf (stderr, "Failed to name child! %s\n", strerror (errno));

  /* ignore reapchild to catch child here */
  (void) sigset (SIGCHLD, 0);
  if (k >= 0)
    {
      wlog ("about to popen", NULL);
      fd = popen (cmd8, mode);
    }
  else
    {
      wlog ("about to popenX!", NULL);
      fd = popen (cmdX, mode);
    }
  if (fd == NULL)
    printf ("do_popen failed: %s\n", strerror (errno));
  else
    printf ("do_popen succeeded: fileno=%d\n", fileno (fd));

  /* restore pre-popen environment */
  sprintf (new_name, "_SP_NAME=%s", acct_file);
  if (putenv (new_name))
    fprintf (stderr, "Failed to restore name! %s\n", strerror (errno));

  if (fd != NULL)
    {
      while (fgets (buf, BUFSIZ, fd) != NULL)
        printf ("&    %s", buf);

      if ((ret = pclose (fd)) == -1)
        printf ("do_popen pclose error: %s\n", strerror (errno));
      else
        printf ("do_popen pclose returned %d\n", ret);
    }

  /* set up to reap any children */
  (void) sigset (SIGCHLD, reapchild);
  return 0;
}

int
do_system (int k) /* do a system() call */
{
  int ret;

  /* XXXX system() will temporarily vfork+exec() a new child */
  /* but there will be no accounting for it, unless it's synprog! */
  sprintf (new_name, "_SP_NAME=%s_c%d", acct_file, ++syn_combo);
  if (putenv (new_name))
    fprintf (stderr, "Failed to name child! %s\n", strerror (errno));

  if (k >= 0)
    {
      wlog ("about to system", NULL);
      ret = system (cmd8);
    }
  else
    {
      wlog ("about to systemX!", NULL);
      ret = system (cmd0);
    }
  if (ret < 0)
    printf ("do_system failed: %s\n", strerror (errno));
  else
    printf ("do_system succeeded, ret=%d\n", ret);

  /* restore pre-system environment */
  sprintf (new_name, "_SP_NAME=%s", acct_file);
  if (putenv (new_name))
    fprintf (stderr, "Failed to restore name! %s\n", strerror (errno));
  return 0;
}

int
do_forkexec (int k) /* do a fork()+exec() call combo */
{
  int ret, pid;
  int status = -1;
  char arg0[128], arg1[128];
  arg1[0] = (char) 0;

  /* ignore reapchild to catch child here */
  (void) sigset (SIGCHLD, 0);

  sprintf (child_name, "%s_f%d", acct_file, ++syn_fork);
  if ((pid = fork ()) == 0)
    {
      syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */
      strcpy (acct_file, child_name);
      acct_init (acct_file);
      sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec);
      if (putenv (new_name))
        {
          fprintf (stderr, "Failed to name fork child! %s\n", strerror (errno));
        }
      (void) execl (arg0, "fork+exec", arg1[0] ? arg1 : NULL, NULL);
      fprintf (stderr, "fork execl failed! %s\n", strerror (errno));
      _exit (127);
    }
  else if (pid == -1)
    fprintf (stderr, "fork failed! %s\n", strerror (errno));
  else
    {
      do
        {
          ret = waitpid (pid, &status, WNOHANG | WUNTRACED);
        }
      while ((ret == -1) && (errno == EINTR));

      if (ret == -1)
        fprintf (stderr, "waitpid failed: %s\n", strerror (errno));
#if 0
      else
        {
          if (WIFEXITED (status))
            printf ("WEXITSTATUS=%d\n", WEXITSTATUS (status));
          if (WIFSTOPPED (status))
            printf ("WSTOPSIG=%d\n", WSTOPSIG (status));
          if (WIFSIGNALED (status))
            printf ("WTERMSIG=%d\n", WTERMSIG (status));
          if (WIFCONTINUED (status))
            printf ("WIFCONTINUED=%d\n", WIFCONTINUED (status));
        }
#endif
      if (WIFEXITED (status))
        printf ("do_forkexec succeeded: child exit status=%d\n",
                WEXITSTATUS (status));
      else
        printf ("do_forkexec failed! status=%d\n", status);
    }

  /* set up to reap any children */
  (void) sigset (SIGCHLD, reapchild);
  return 0;
}

int
do_vforkexec (int k) /* do a vfork()+exec() call combo */
{
  int ret, pid;
  int status = 1;
  char arg0[128], arg1[128];
  arg1[0] = (char) 0;
  /* ignore reapchild to catch child here */
  (void) sigset (SIGCHLD, 0);

  sprintf (child_name, "%s_f%d", acct_file, ++syn_fork);

  if ((pid = vfork ()) == 0)
    {
      syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */
      strcpy (acct_file, child_name);
      acct_init (acct_file);
      sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec);
      if (putenv (new_name))
        fprintf (stderr, "Failed to name vfork child! %s\n", strerror (errno));
      (void) execl (arg0, "vfork+exec", arg1[0] ? arg1 : NULL, NULL);
      printf ("vfork execl failed! %s\n", strerror (errno));
      _exit (127);
    }
  else if (pid == -1)
    fprintf (stderr, "vfork failed! %s\n", strerror (errno));
  else
    {
      do
        {
          ret = waitpid (pid, &status, WNOHANG | WUNTRACED);
        }
      while (ret == -1 && errno == EINTR);

      if (ret == -1)
        fprintf (stderr, "waitpid failed: %s\n", strerror (errno));
#if 0
      else
        {
          if (WIFEXITED (status))
            printf ("WEXITSTATUS=%d\n", WEXITSTATUS (status));
          if (WIFSTOPPED (status))
            printf ("WSTOPSIG=%d\n", WSTOPSIG (status));
          if (WIFSIGNALED (status))
            printf ("WTERMSIG=%d\n", WTERMSIG (status));
          if (WIFCONTINUED (status))
            printf ("WIFCONTINUED=%d\n", WIFCONTINUED (status));
        }
#endif
      if (WIFEXITED (status))
        printf ("do_vforkexec succeeded: child exit status=%d\n",
                WEXITSTATUS (status));
      else
        printf ("do_vforkexec failed! status=%d\n", status);
    }

  /* set up to reap any children */
  (void) sigset (SIGCHLD, reapchild);
  return 0;
}
