#!/bin/bash

# **********************************************************
# Copyright (c) 2003-2008 VMware, Inc.  All rights reserved.
# **********************************************************

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of VMware, Inc. nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.

#notes:
#current DYNAMORIO_OPTIONS env var will be used to determine options
#BUILD_TOOLS must be set
# for ARCH=x64, INSTALL_BIN must be set

usage="runalltest <runallfile> <drdll> <win32_drpreinject_path> <runall_max_wait> <X64|X86>"

if [ $# -ne 5 ] ; then
    echo "Usage: $0 $usage"
    exit 127
fi

# 3 seconds seems to be the magic number here. I've seen problems
# with <freeze> tests for anything less.
RUNALL_SLEEP=3

RUNALL_MAX_WAIT=$4

ARCH=$5

exepath=`head -1 $1`
caption=`head -2 $1 | tail -1`
mode=`head -3 $1 | tail -1`
drdll=$2
drpreinject=$3
exename=${exepath##*/}
if [ ${exepath:0:6} == "<path>" ]; then
    # support for running executables not on system path, but in runall/ dir
    runalldir=${1%/*} # assumption: .runall file is in same place as exe
    exepath=${exepath/<path>/$runalldir}
fi

if [ "$ARCH" == "x64" ]; then
    drctl="$BUILD_TOOLS/DRcontrol -64"
else
    drctl="$BUILD_TOOLS/DRcontrol -32"
fi

function nudge_exe {
    pid=`$BUILD_TOOLS/DRview -exe $1 | gawk '{print $2}' | sed 's/,//'`
    # case 10382: look for "No such process found", since if we
    # pass a non-number to DRcontrol it will nudge all processes!
    if [ "$pid" = "such" ]; then
        echo "Error: $1 process not found for nudge"
    else
        $drctl $2 $pid
    fi
}

#some tests need to be able to find the tools folder
export DYNAMORIO_WINTOOLS=`cygpath -w $BUILD_TOOLS`

# make sure the registry set is there (only creates if nonexistent).
dr_home=`cygpath -da $PWD`
$drctl -create "$dr_home"

# save the old registry settings so we can leave the registry in the
# same state we found it.
prev_preinject=`$drctl -preinject REPORT`
prev_settings=_prev_settings
$drctl -save $prev_settings

$drctl -preinject OFF
$drctl -app "$exename" -run 1 -options "$DYNAMORIO_OPTIONS"  -drlib $drdll
$drctl -preinject $drpreinject
$drctl -app "$exename" -logdir "$DYNAMORIO_LOGDIR"

# clear pcache dir before starting app and then create the registry
# entry and associated dir
if [ -e cache ]; then
    # remove existing perscache dir(s)
    rm -rf cache;
fi

# we have a discrepancy between DYNAMORIO_CACHE_ROOT, which drinject
# uses, and DRcontrol -sharedcache.  The former specifies the full
# path to the cache dir, but the latter specifies the path to DR_HOME
# (i.e., without a trailing /cache).  We do this so that DRcontrol can
# find the lib and logs directory and copy their permissions.
# XXX: What a hack.  Can't we just set the permissions directly
# and make DRcontrol -sharedcache more intuitive?
cache_root=`dirname "$DYNAMORIO_CACHE_ROOT"`

# drcontrol -sharedcache gives an error message if it can't copy
# permissions from the lib and logs directories (e.g., if they don't
# exist).  Swallow the error message so it doesn't mess up the output.
junk=`$drctl -app "$exename" -sharedcache "$cache_root" 2>&1`

# for 3rd party apps w/ windows, we use DRview results to ensure we're in control
if [ "$caption" != "<nowindow>" ]; then
    if [ "$ARCH" == "x64" ]; then
        $INSTALL_BIN/drinject.exe -noinject $exepath &
    else
        $exepath &
    fi

    sleep $RUNALL_SLEEP

    $drctl -preinject OFF

    $BUILD_TOOLS/DRview -exe $exename -nopid -nobuildnum

    if [ "$mode" == "<detach>" ]; then
        $drctl -detachexe $exename

        # Currently DRcontrol -detachexe waits for the injected detach thread
        # to exit at which point should be finished detaching, so we don't need
        # to sleep here. DRcontrol does time out if wait is too long so we
        # won't get stuck here.

        $BUILD_TOOLS/DRview -exe $exename -nopid -nobuildnum

    elif [ "$mode" == "<reset>" ]; then
        # test two in a row
        nudge_exe $exename "-nudge reset -pid";
        nudge_exe $exename "-nudge reset -pid";

        # Currently DRcontrol -nudge waits for the injected nudge thread to
        # exit, so we don't need to sleep here. DRcontrol does time out if
        # wait is too long so we won't get stuck here.
        $BUILD_TOOLS/DRview -exe $exename -nopid -nobuildnum

    elif [ "$mode" == "<hotp>" ]; then
        # test both kinds of hotp nudge (even if no patches match)
        nudge_exe $exename -hot_patch_nudge;
        nudge_exe $exename -hot_patch_modes_nudge;

        # Currently DRcontrol -nudge waits for the injected nudge thread to
        # exit, so we don't need to sleep here. DRcontrol does time out if
        # wait is too long so we won't get stuck here.
        $BUILD_TOOLS/DRview -exe $exename -nopid -nobuildnum

    elif [ "$mode" == "<freeze>" ]; then
        # test two in a row
        nudge_exe $exename "-nudge freeze -pid";
        nudge_exe $exename "-nudge freeze -pid";

        # Currently DRcontrol -nudge waits for the injected nudge thread to
        # exit, so we don't need to sleep here. DRcontrol does time out if
        # wait is too long so we won't get stuck here.
        $BUILD_TOOLS/DRview -exe $exename -nopid -nobuildnum

    elif [ "$mode" == "<persist>" ]; then
        # we now have per-user and not per-app dirs
        # we'll use the 'whoami' tool in tools/external/ to get the SID
        cursid=`$BUILD_TOOLS/external/whoami.exe /user | tail -1 | gawk '{print $2}'`
        cachedir="$cache_root/cache/$cursid"

        # test persisting and re-using
        nudge_exe $exename "-nudge persist -pid";

        if [ ! -d $cachedir ]; then
            echo "No persisted caches found";
        fi

        # run a second copy
        $drctl -preinject $drpreinject
        $exepath &
        sleep $RUNALL_SLEEP
        $drctl -preinject OFF
        # we assume that it's using the persisted caches
        # XXX: add way to find out

        # Currently DRcontrol -nudge waits for the injected nudge thread to
        # exit, so we don't need to sleep here. DRcontrol does time out if
        # wait is too long so we won't get stuck here.
        $BUILD_TOOLS/DRview -exe $exename -nopid -nobuildnum

        $BUILD_TOOLS/closewnd "$caption" $RUNALL_MAX_WAIT
    elif [ "$mode" == "<client_nudge>" ]; then
        # send a client nudge, we pick arbitrary value 10 for argument
        nudge_exe $exename "-client_nudge 10 -pid";

        sleep $RUNALL_SLEEP

        # Currently DRcontrol -nudge waits for the injected nudge thread to
        # exit, so we don't need to sleep here. DRcontrol does time out if
        # wait is too long so we won't get stuck here.
        $BUILD_TOOLS/DRview -exe $exename -nopid -nobuildnum
    fi

    $BUILD_TOOLS/closewnd "$caption" $RUNALL_MAX_WAIT

    sleep $RUNALL_SLEEP

    $BUILD_TOOLS/DRview -exe $exename -nobuildnum
else
    $BUILD_TOOLS/winstats -m 2 -silent $exepath

    $drctl -preinject OFF
fi

#just in case
$BUILD_TOOLS/DRkill -exe $exename -quiet

# restore registry settings
$drctl -remove "$exename"

if [[ ${#prev_preinject} > 0 ]]; then
    $drctl -preinject "$prev_preinject"
else
    $drctl -preinject OFF
fi

if [ -e $prev_settings ]; then
    $drctl -load $prev_settings
    rm -f $prev_settings
fi
