#!/bin/bash # time-and-mem-limit # Ralph Becket # Tue Feb 10 12:21:58 EST 2009 # # usage: time-and-mem-limit N M cmd ... # # Kill cmd ... if it does not terminate after N seconds of wallclock time or if # it uses more than M megabytes of memory. Either "EXCEEDED TIME LIMIT" or # "EXCEEDED MEMORY LIMIT" is printed on stderr as appropriate if cmd ... is # killed for one of these reasons. USAGE="usage: time-and-mem-limit num-seconds num-megabytes cmd ..." if [ "$*" = "-h" -o "$*" = "--help" ] then echo $USAGE >&2 exit 0 fi if [ $# -lt 3 ] then echo $USAGE >&2 exit 1 fi # CMDPID: we exec the command so it gets the current PID. CMDPID=$$ # TIMELIMIT: the time limit in seconds. TIMELIMIT=$1 # MEMLIMIT: the memory limit in megabytes. MEMLIMITMB=$2 MEMLIMITKB=$(($MEMLIMITMB << 10)) shift 2 # CMD: anything else on the command line is the command to be executed. CMD=$@ declare TimeoutOrig=$(ulimit -t) declare MemoryOrig=$(ulimit -v) # Using ulimit like this doesn't work on Cygwin. # if test ! -e /bin/cygpath.exe then ulimit -S -t ${TIMELIMIT} ulimit -S -v ${MEMLIMITKB} else ### Only on Cygwin: Ubuntu 15 does not allow to delete output files and reports wrong exit result # Check the time and memory limits every ten seconds in a background # process. If either limit is reached, kill the process. ( for (( i=0 ; i<$TIMELIMIT ; i+=1 )) do # Sleep for 1 seconds. sleep 1 # Obtain the process memory usage in kilobytes. # (We use this form of ps arguments for portability). CMDMEMUSAGEKB=`ps -p $CMDPID -o vsize=""` # Quit if the process has already terminated. if [ -z $CMDMEMUSAGEKB ] then exit 0 fi # Kill the process if it has exceeded its memory limit. if [ $CMDMEMUSAGEKB -gt $MEMLIMITKB ] then kill -KILL $CMDPID >/dev/null 2>&1 echo "" >&2 echo "EXCEEDED MEMORY LIMIT OF $MEMLIMITMB Mbytes" >&2 exit 1 fi done # Kill the process if it has exceeded its time limit. if ps -p $CMDPID >/dev/null 2>&1 then kill -KILL $CMDPID >/dev/null 2>&1 echo "" >&2 echo "EXCEEDED TIME LIMIT OF $TIMELIMIT s" >&2 exit 1 fi ) & # The background process above sometimes does not seem to fire or does not # kill all subprocesses. "ulimit" is more reliable, but it is not clear how # to get useful, specific error messages. Thus for now we use both methods. fi # Run the command. exec $CMD if test ! -e /bin/cygpath.exe then ulimit -S -t ${TimeoutOrig} ulimit -S -v ${MemoryOrig} fi