Revert^2 "Improve user-friendliness of art script."

Fixes quoting for mksh for arguments with whitespace.

Test: art/tools/run-jdwp-tests.sh --mode=device
Change-Id: I0b8983a0c0ebcc1b64ad943859fbb85d3774ab75
diff --git a/tools/art b/tools/art
index f5e0860..6e62ad1 100644
--- a/tools/art
+++ b/tools/art
@@ -16,6 +16,15 @@
 # shell dialect that should work on the host (e.g. bash), and
 # Android (e.g. mksh).
 
+# Globals
+ARCHS={arm,arm64,mips,mips64,x86,x86_64}
+ART_BINARY=dalvikvm
+DELETE_ANDROID_DATA="no"
+LAUNCH_WRAPPER=
+LIBART=libart.so
+JIT_PROFILE="no"
+VERBOSE="no"
+
 # Follow all sym links to get the program name.
 if [ z"$BASH_SOURCE" != z ]; then
   PROG_NAME="$BASH_SOURCE"
@@ -28,16 +37,15 @@
 done
 
 function find_libdir() {
-  # Get the actual file, $DALVIKVM may be a symbolic link.
+  # Get the actual file, $1 is the ART_BINARY_PATH and may be a symbolic link.
   # Use realpath instead of readlink because Android does not have a readlink.
-  if [[ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" == *dalvikvm64 ]]; then
+  if [[ "$(realpath "$1")" == *dalvikvm64 ]]; then
     echo "lib64"
   else
     echo "lib"
   fi
 }
 
-ARGS_WITH_INTERPRET_ONLY=
 function replace_compiler_filter_with_interepret_only() {
   ARGS_WITH_INTERPRET_ONLY=("$@")
 
@@ -61,65 +69,156 @@
   fi
 }
 
-invoke_with=
-DALVIKVM=dalvikvm
-LIBART=libart.so
-JIT_PROFILE=false
+function usage() {
+  cat 1>&2 <<EOF
+Usage: art [OPTIONS] [--] [ART_OPTIONS] CLASS
 
-while true; do
-  if [ "$1" = "--invoke-with" ]; then
-    shift
-    invoke_with="$invoke_with $1"
-    shift
-  elif [ "$1" = "-d" ]; then
-    LIBART="libartd.so"
-    shift
-  elif [ "$1" = "--32" ]; then
-    DALVIKVM=dalvikvm32
-    shift
-  elif [ "$1" = "--64" ]; then
-    DALVIKVM=dalvikvm64
-    shift
-  elif [ "$1" = "--perf" ]; then
-    PERF="record"
-    shift
-  elif [ "$1" = "--perf-report" ]; then
-    PERF="report"
-    shift
-  elif [ "$1" = "--profile" ]; then
-    JIT_PROFILE="true"
-    shift
-  elif expr "$1" : "--" >/dev/null 2>&1; then
-    echo "unknown option: $1" 1>&2
-    exit 1
-  else
-    break
+Supported OPTIONS include:
+  --32                     Use the 32-bit Android Runtime.
+  --64                     Use the 64-bit Android Runtime.
+  --callgrind              Launch the Android Runtime in callgrind.
+  -d                       Use the debug ART library (libartd.so).
+  --debug                  Equivalent to -d.
+  --gdb                    Launch the Android Runtime in gdb.
+  --help                   Display usage message.
+  --invoke-with <program>  Launch the Android Runtime in <program>.
+  --perf                   Launch the Android Runtime with perf recording.
+  --perf-report            Launch the Android Runtime with perf recording with
+                           report upon completion.
+  --profile                Run with profiling, then run using profile data.
+  --verbose                Run script verbosely.
+
+The ART_OPTIONS are passed directly to the Android Runtime.
+
+Example:
+  art --32 -cp my_classes.dex MainClass
+
+Common errors:
+  1) Not having core.art available (see $ANDROID_BUILD_TOP/art/Android.mk).
+     eg m -j32 build-art-host
+  2) Not having boot.art available (see $ANDROID_BUILD_TOP/build/make/core/dex_preopt_libart_boot.mk)
+     eg m -j32 out/target/product/generic_x86_64/dex_bootjars/system/framework/x86_64/boot.art
+EOF
+}
+
+function clean_android_data() {
+  if [ "$DELETE_ANDROID_DATA" = "yes" ]; then
+    rm -rf $ANDROID_DATA
   fi
+}
+
+function verbose_run() {
+  if [ "$VERBOSE" = "yes" ]; then
+    echo "$@"
+  fi
+  eval "$@"
+}
+
+function run_art() {
+  verbose_run ANDROID_DATA=$ANDROID_DATA               \
+              ANDROID_ROOT=$ANDROID_ROOT               \
+              LD_LIBRARY_PATH=$LD_LIBRARY_PATH         \
+              PATH=$ANDROID_ROOT/bin:$PATH             \
+              LD_USE_LOAD_BIAS=1                       \
+              $LAUNCH_WRAPPER $ART_BINARY_PATH $lib    \
+              -XXlib:$LIBART                           \
+              -Xnorelocate                             \
+              -Ximage:$ANDROID_ROOT/framework/core.art \
+              "$@"
+}
+
+while [[ "$1" = "-"* ]]; do
+  case $1 in
+  --)
+    # No more arguments for this script.
+    shift
+    break
+    ;;
+  --32)
+    ART_BINARY=dalvikvm32
+    ;;
+  --64)
+    ART_BINARY=dalvikvm64
+    ;;
+  --callgrind)
+    LAUNCH_WRAPPER="valgrind --tool=callgrind"
+    ;;
+  -d)
+    ;& # Fallthrough
+  --debug)
+    LIBART="libartd.so"
+    ;;
+  --gdb)
+    LIBART="libartd.so"
+    LAUNCH_WRAPPER="gdb --args"
+    ;;
+  --help)
+    usage
+    exit 0
+    ;;
+  --invoke-with)
+    LAUNCH_WRAPPER=$2
+    shift
+    ;;
+  --perf)
+    PERF="record"
+    ;;
+  --perf-report)
+    PERF="report"
+    ;;
+  --profile)
+    JIT_PROFILE="yes"
+    ;;
+  --verbose)
+    VERBOSE="yes"
+    ;;
+  --*)
+    echo "unknown option: $1" 1>&2
+    usage
+    exit 1
+    ;;
+  *)
+    break
+    ;;
+  esac
+  shift
 done
 
+if [ $# -eq 0 ]; then
+  usage
+  exit 1
+fi
+
 PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
 ANDROID_ROOT=$PROG_DIR/..
-LIBDIR=$(find_libdir)
-LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
-DEBUG_OPTION=""
+ART_BINARY_PATH=$ANDROID_ROOT/bin/$ART_BINARY
 
-DELETE_ANDROID_DATA=false
+if [ ! -x "$ART_BINARY_PATH" ]; then
+  cat 1>&2 <<EOF
+Android Runtime not found: $ART_BINARY_PATH
+This script should be in the same directory as the Android Runtime ($ART_BINARY).
+EOF
+  exit 1
+fi
+
+LIBDIR="$(find_libdir $ART_BINARY_PATH)"
+LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
+EXTRA_OPTIONS=""
+
 # If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own,
 # and ensure we delete it at the end.
 if [ "$ANDROID_DATA" = "/data" ] || [ "$ANDROID_DATA" = "" ]; then
   ANDROID_DATA=$PWD/android-data$$
-  mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
-  DELETE_ANDROID_DATA=true
+  mkdir -p $ANDROID_DATA/dalvik-cache/$ARCHS
+  DELETE_ANDROID_DATA="yes"
 fi
 
-if [ z"$PERF" != z ]; then
-  invoke_with="perf record -g -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with"
-  DEBUG_OPTION="-Xcompiler-option --generate-debug-info"
+if [ "$PERF" != "" ]; then
+  LAUNCH_WRAPPER="perf record -g -o $ANDROID_DATA/perf.data -e cycles:u $LAUNCH_WRAPPER"
+  EXTRA_OPTIONS="-Xcompiler-option --generate-debug-info"
 fi
 
-PROFILE_OPTION=""
-EXIT_STATUS=0
-if [ "$JIT_PROFILE" = true ]; then
+if [ "$JIT_PROFILE" = "yes" ]; then
   # Create the profile. The runtime expects profiles to be created before
   # execution.
   PROFILE_PATH="$ANDROID_DATA/primary.prof"
@@ -127,65 +226,51 @@
 
   # Replace the compiler filter with interpret-only so that we
   # can capture the profile.
+  ARGS_WITH_INTERPRET_ONLY=
   replace_compiler_filter_with_interepret_only "$@"
 
-  ANDROID_DATA=$ANDROID_DATA \
-    ANDROID_ROOT=$ANDROID_ROOT \
-    LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
-    PATH=$ANDROID_ROOT/bin:$PATH \
-    LD_USE_LOAD_BIAS=1 \
-    $ANDROID_ROOT/bin/$DALVIKVM $lib \
-      -XXlib:$LIBART \
-      -Xnorelocate \
-      -Ximage:$ANDROID_ROOT/framework/core.art \
-      -Xjitsaveprofilinginfo \
-      -Xps-min-methods-to-save:0 \
-      -Xps-min-classes-to-save:0 \
-      -Xps-min-notification-before-wake:10 \
-      -Xps-profile-path:$PROFILE_PATH \
-      -Xusejit:true \
-      "${ARGS_WITH_INTERPRET_ONLY[@]}" \
-      &> "$ANDROID_DATA/profile_gen.log"
-
+  run_art -Xjitsaveprofilinginfo               \
+          -Xps-min-methods-to-save:0           \
+          -Xps-min-classes-to-save:0           \
+          -Xps-min-notification-before-wake:10 \
+          -Xps-profile-path:$PROFILE_PATH      \
+          -Xusejit:true                        \
+          "${ARGS_WITH_INTERPRET_ONLY[@]}"     \
+          "&>" "$ANDROID_DATA/profile_gen.log"
   EXIT_STATUS=$?
 
-  if [ $EXIT_STATUS = 0 ]; then
-    # Wipe dalvik-cache to prepare it for the next invocation.
-    rm -rf $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}/*
-  else
+  if [ $EXIT_STATUS != 0 ]; then
     cat "$ANDROID_DATA/profile_gen.log"
+    clean_android_data
+    exit $EXIT_STATUS
   fi
 
-  PROFILE_OPTION="-Xcompiler-option --profile-file=$PROFILE_PATH"
+  # Wipe dalvik-cache to prepare it for the next invocation.
+  rm -rf $ANDROID_DATA/dalvik-cache/$ARCHS/*
+
+  # Append arguments so next invocation of run_art uses the profile.
+  EXTRA_OPTIONS="$EXTRA_OPTIONS -Xcompiler-option --profile-file=$PROFILE_PATH"
 fi
 
-# Only run the second invocation if the first one finished successfully.
-if [ $EXIT_STATUS = 0 ]; then
-  ANDROID_DATA=$ANDROID_DATA \
-    ANDROID_ROOT=$ANDROID_ROOT \
-    LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
-    PATH=$ANDROID_ROOT/bin:$PATH \
-    LD_USE_LOAD_BIAS=1 \
-    $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \
-      -XXlib:$LIBART \
-      -Xnorelocate \
-      -Ximage:$ANDROID_ROOT/framework/core.art \
-      $DEBUG_OPTION \
-      $PROFILE_OPTION \
-      "$@"
+# Protect additional arguments in quotes to preserve whitespaces when evaluated.
+# This is for run-jdwp-test.sh which uses this script and has arguments with
+# whitespaces when running on device.
+while [ $# -gt 0 ]; do
+  EXTRA_OPTIONS="$EXTRA_OPTIONS \"$1\""
+  shift
+done
 
-  EXIT_STATUS=$?
-fi
+run_art $EXTRA_OPTIONS
+EXIT_STATUS=$?
 
-if [ z"$PERF" != z ]; then
-  if [ z"$PERF" = zreport ]; then
+if [ "$PERF" != "" ]; then
+  if [ "$PERF" = report ]; then
     perf report -i $ANDROID_DATA/perf.data
   fi
   echo "Perf data saved in: $ANDROID_DATA/perf.data"
 else
-  if [ "$DELETE_ANDROID_DATA" = "true" ]; then
-    rm -rf $ANDROID_DATA
-  fi
+  # Perf output is placed under $ANDROID_DATA so not cleaned when perf options used.
+  clean_android_data
 fi
 
 exit $EXIT_STATUS