#!/bin/sh
#
# Program:  RunSafely.sh
#
# Synopsis: This script simply runs another program.  If the program works
#           correctly, this script has no effect, otherwise it will do things
#           like print a stack trace of a core dump.  It always returns
#           "successful" so that tests will continue to be run.
#
#           This script funnels stdout and stderr from the program into the
#           fourth argument specified, and outputs a <outfile>.time file which
#           contains a timing of the program and the program's exit code.
#          
#           If the <exitok> parameter is 0 then this script always returns 0,
#           regardless of the actual exit of the <program>.
#           If the <exitok> parameter is non-zero then this script returns
#           the exit code of the <program>. If there is an error in getting
#           the <program>'s exit code, this script returns 99.
#
#           If optional parameters -r <remote host> -l <remote user> are
#           specified, it execute the program remotely using rsh.
#
# Syntax: 
#
#   RunSafely.sh [-r <rhost>] [-l <ruser>]
#                <timeout> <exitok> <infile> <outfile> <program> <args...>
#
#   where:
#     <rhost>   is the remote host to execute the program
#     <ruser>   is the username on the remote host
#     <timeout> is the maximum number of seconds to let the <program> run
#     <exitok>  is 1 if the program must exit with 0 return code
#     <infile>  is a file from which standard input is directed
#     <outfile> is a file to which standard output and error are directed
#     <program> is the path to the program to run
#     <args...> are the arguments to pass to the program.
#
if [ $# -lt 4 ]; then
  echo "./RunSafely <timeout> <exitok> <infile> <outfile> <program> <args...>"
  exit 1
fi

DIR=${0%%`basename $0`}

RHOST=
RUSER=`id -un`
FLAG=$1
if [ $1 = "-r" ]; then
  RHOST=$2
  shift 2
fi
if [ $1 = "-l" ]; then
  RUSER=$2
  shift 2
fi

ULIMIT=$1
EXITOK=$2
INFILE=$3
OUTFILE=$4
PROGRAM=$5
shift 5
SYSTEM=`uname -s`

ULIMITCMD=""
case $SYSTEM in
  CYGWIN*) 
    ;;
  Darwin*)
    # Disable core file emission, the script doesn't find it anyway because it is put 
    # into /cores.
    ULIMITCMD="$ULIMITCMD ulimit -c 0;"
    ULIMITCMD="$ULIMITCMD ulimit -t $ULIMIT;"
    # To prevent infinite loops which fill up the disk, specify a limit on size of
    # files being output by the tests. 10 MB should be enough for anybody. ;)
    ULIMITCMD="$ULIMITCMD ulimit -f 10485760;"
    ;;
  *)
    ULIMITCMD="$ULIMITCMD ulimit -t $ULIMIT;"
    ULIMITCMD="$ULIMITCMD ulimit -c unlimited;"
    # To prevent infinite loops which fill up the disk, specify a limit on size of
    # files being output by the tests. 10 MB should be enough for anybody. ;)
    ULIMITCMD="$ULIMITCMD ulimit -f 10485760;"

    #ULIMITCMD="$ULIMITCMD ulimit -m 2000000 -v 2000000;"
    #ULIMITCMD="$ULIMITCMD ulimit -m 2000000 -v 2000000;"
esac
rm -f core core.*

#
# Run the command, timing its execution.
# The standard output and standard error of $PROGRAM should go in $OUTFILE,
# and the standard error of time should go in $OUTFILE.time. Note that the 
# return code of the program is appended to the $OUTFILE on an "Exit Val ="
# line.
#
# To get the time program and the specified program different output filenames,
# we tell time to launch a shell which in turn executes $PROGRAM with the
# necessary I/O redirection.
#
PWD=`pwd`
COMMAND="$PROGRAM $*"
if [ "$SYSTEM" = "Darwin" ]; then
  COMMAND="${DIR}TimedExec $ULIMIT $PWD $COMMAND"
fi

if [ "x$RHOST" = x ] ; then
  ( sh -c "$ULIMITCMD $COMMAND >$OUTFILE 2>&1 < $INFILE; echo exit \$?" ) 2>&1 \
    | awk -- '\
BEGIN     { cpu = 0.0; }
/^user/   { cpu += $2; print; }
/^sys/    { cpu += $2; print; }
!/^user/ && !/^sys/  { print; }
END       { printf("cpu time= %f\n", cpu); }' > ${OUTFILE}.time
else
  rm -f "$PWD/${PROGRAM}.command"
  rm -f "$PWD/${PROGRAM}.remote"
  rm -f "$PWD/${PROGRAM}.remote.time"
  echo "$ULIMITCMD cd $PWD; (time -p ($COMMAND > $OUTFILE.remote 2>&1 < $INFILE;); echo exit $?) > $OUTFILE.remote.time 2>&1" > "$PWD/${PROGRAM}.command"
  chmod +x "$PWD/${PROGRAM}.command"

  ( rsh -l $RUSER $RHOST "ls $PWD/${PROGRAM}.command" ) > /dev/null 2>&1
  ( rsh -l $RUSER $RHOST "$PWD/${PROGRAM}.command" )
  cat $OUTFILE.remote.time | awk -- '\
BEGIN     { cpu = 0.0; }
/^user/   { cpu += $2; print; }
/^sys/    { cpu += $2; print; }
!/^user/ && !/^sys/  { print; }
END       { printf("program %f\n", cpu); }' > ${OUTFILE}.time
sleep 1
cp -f $OUTFILE.remote $OUTFILE
rm -f $OUTFILE.remote
rm -f $OUTFILE.remote.time
fi

exitval=`grep '^exit ' $OUTFILE.time | sed -e 's/^exit //'`
if [ -z "$exitval" ] ; then
  exitval=99
  echo "TEST $PROGRAM failed:  CAN'T GET EXIT CODE!"
fi
echo "exit $exitval" >> $OUTFILE

if [ "$EXITOK" -ne 0 ] ; then
  if test "$exitval" -ne 0 ; then
    echo "test $PROGRAM failed:  EXIT != 0"
  fi
else
  exitval=0
fi

if ls | egrep "^core" > /dev/null
then
    # If we are on a sun4u machine (UltraSparc), then the code we're generating
    # is 64 bit code.  In that case, use gdb-64 instead of gdb.
    myarch=`uname -m`
    if [ "$myarch" = "sun4u" ]
    then
	GDB="gdb-64"
    else
	GDB=gdb
    fi

    corefile=`ls core* | head -n 1`
    echo "where 100" > StackTrace.$$
    $GDB -q -batch --command=StackTrace.$$ --core=$corefile $PROGRAM < /dev/null
    rm -f StackTrace.$$ $corefile
fi
exit "$exitval"
