#!/bin/bash
#
# Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.

# Compare the current working copy of the repository to either a clean copy
# or the specified revision.
# Along with the files created by compiler_metrics.sh, creates
# compiler_compare[_stats_[12]].log in the working direcetory and a
# out_compile_samples/ directory at the root of the repository,
# which are left around for later examination.
# These files will be destroyed on script re-run.

APP=""
REV=""
ONE_DELTA=""
BASE_PATH=$(pwd)
SCRIPT_PATH=$(dirname $0)
SCRIPT_PATH=$(cd $SCRIPT_PATH; pwd)
RUNS=10

function printHelp() {
  exitValue=${1:1}
  echo "Compare current changes as they relate to performance with another SVN revision"
  echo ""
  echo "  Usage:"
  echo "  -a=, --app=         The dart app file to test (required)."
  echo "  -d=, --one-delta=   The filename, relative to app, to touch in order to trigger a one-delta compile."
  echo "  -r=, --revision=    The compiler revision to compare against (default to current repository revision)."
  echo "  --runs=             Number of runs to test with, default $RUNS"
  echo "  --stats             Non-destructive display of previous stats"
  echo "  -h, --help          What you see is what you get."
  exit $exitValue
}

function failTest() {
  if [ ! $1 -eq 0 ]; then
    echo $2
    exit $1
  fi
}

if [ $# -eq 0 ]; then
  printHelp;
fi

for i in $*
do
  case $i in
    --one-delta=*|-d=*)
      ONE_DELTA=${i#*=}
      COMPARE_OPTIONS+="--one-delta=$ONE_DELTA ";;
    --app=*|-a=*)
      APP=${i#*=}
      APP=$( cd "$( dirname "$APP" )" && pwd )/$( basename "$APP");;
    --revision=*|-r=*)
      REV=${i#*=};;
    --help|-h)
      printHelp 0;;
    --runs=*)
      RUNS=${i#*=};;
    --stats)
      calcStats
      exit 0;;
    *)
      echo "Invalid parameter: $i"
      printhelp 1;;
  esac
done

COMPARE_OPTIONS+="-r=$RUNS "

if [ "" = "$APP" ] || [ ! -r $APP ]; then
  echo "Required --app"
  printHelp
fi

RESPONSE[0]="Performance: Better"
RESPONSE[1]="Performance: No Change"
RESPONSE[2]="Performance: Worse"

# Passed: MAX deviation, Changed Amount
# Returns: Index of RESPONSE
function responseValue() {
  ABS=$2
  (( ABS = ABS < 0 ? ABS * -1 : ABS ))
  if [ $ABS -gt $1 ]; then
    if [ $2 -lt 0 ]; then
      return 2
    else
      return 0
    fi
  fi
  return 1
}

function calcStat() {
  # Assume we're always making improvments, S2 will be presented as a larger value
  LINEMATCH_DEV="s/${1}: .*stdev: \([0-9]*\).*/\1/p"
  LINEMATCH_VALUE="s/${1}: average: \([0-9]*\).*/\1/p"
  S1_DEV=$(sed -n -e "$LINEMATCH_DEV" $STAT1)
  if [ "" != "$S1_DEV" ]; then
    S2_DEV=$(sed -n -e "$LINEMATCH_DEV" $STAT2)
    DEV_MAX=$(( S1_DEV < S2_DEV ? S2_DEV : S1_DEV ))
    S1_VALUE=$(sed -n -e "$LINEMATCH_VALUE" $STAT1)
    S2_VALUE=$(sed -n -e "$LINEMATCH_VALUE" $STAT2)
    DIFF=$(( S2_VALUE - S1_VALUE ))
    DIFF_PERCENT=`echo "$DIFF*100/$S2_VALUE" | bc -l | sed 's/\([0-9]*\.[0-9][0-9]\)[0-9]*/\1/'`
    responseValue $DEV_MAX $DIFF
    echo "$1: Before/After-ms(${S2_VALUE} / ${S1_VALUE}), stdev(${S2_DEV} / ${S1_DEV}), Difference: ${DIFF}ms (${DIFF_PERCENT}%), " ${RESPONSE[$?]}
  fi
  return 0
}

function calcStats() {
  calcStat full-compile
  calcStat zero-delta-compile
  calcStat one-delta-compile
}

ROOT_OF_REPO=$BASE_PATH
TEST_DIR=$BASE_PATH
while true; do
  ls -d .gclient > /dev/null 2>&1
  if [ $? -eq 0 ]; then
    echo "Root found: $ROOT_OF_REPO"
    break;
  fi
  if [ "$TEST_DIR" = "/" ]; then
    failTest 1 "Hit the root directory; no .git/ found?!"
  fi
  ROOT_OF_REPO=$TEST_DIR
  cd ..
  TEST_DIR=$(pwd)
done

# Make a temporary directory in the current path and checkout the revision
TMP_DIR=$ROOT_OF_REPO/compiler/tmp_performance_comparisons
mkdir -p $TMP_DIR

LOG_FILE=$TMP_DIR/compiler_compare.log
STAT1=$TMP_DIR/compiler_compare_stats_1.log
STAT2=$TMP_DIR/compiler_compare_stats_2.log
COMPARE_OPTIONS+="--output=$TMP_DIR "

# zero out files
echo "" > $LOG_FILE
echo "" > $STAT1
echo "" > $STAT2

# Do the first test
echo "Compiling dartc with your changes (mode=release)"
cd $ROOT_OF_REPO/compiler
gclient runhooks >> $LOG_FILE 2>&1
../tools/build.py --mode release --arch dartc >> $LOG_FILE 2>&1
failTest $? "Error compiling your location changes, check $LOG_FILE"

echo "Running first test against current working copy"
echo $SCRIPT_PATH/compiler_metrics.sh --stats-prefix=yours --dartc=./out/Release_dartc/dartc $COMPARE_OPTIONS --app=$APP >> $LOG_FILE
$SCRIPT_PATH/compiler_metrics.sh --stats-prefix=yours --dartc=./out/Release_dartc/dartc $COMPARE_OPTIONS --app=$APP > $STAT1
failTest $? "Error collecting statistics from working copy"

# switch to tmp for remainder of building
cd $TMP_DIR

gclient config svn://svn.chromium.org/dash/trunk/deps/compiler.deps >> $LOG_FILE 2>&1
failTest $? "Error calling gclient config"

GCLIENT_SYNC="-t "
if [ "" != "$REV" ]; then
  GCLIENT_SYNC+="--revision=$REV"
fi

echo "Checking out clean version of $REV; will take some time. Look at $LOG_FILE for progress"
gclient sync $GCLIENT_SYNC  >> $LOG_FILE 2>&1
failTest $? "Error calling gclient sync"

echo "Compiler clean version; may take some time"
cd compiler
gclient runhooks >> $LOG_FILE 2>&1
../tools/build.py --mode release --arch dartc >> $LOG_FILE 2>&1
failTest $? "Error compiling comparison revision"

# Do the second test
echo "Running second test against clean copy"
echo $SCRIPT_PATH/compiler_metrics.sh --stats-prefix=clean --dartc=./out/Release_dartc/dartc $COMPARE_OPTIONS --app=$APP >> $LOG_FILE
$SCRIPT_PATH/compiler_metrics.sh --stats-prefix=clean --dartc=./out/Release_dartc/dartc $COMPARE_OPTIONS --app=$APP > $STAT2
failTest $? "Error collecting statistics from clean copy"

calcStats
