Add --profile mode to the art script

--profile will run dalvikmvm twice. The first run records the profile by
replacing the compiler filter with interpret-only and enabling profile
saving. The second run will pass the recorded profile to dex2oat using
'-Xcompiler-option --profile-file=...'.

Test: ./out/host/linux-x86/bin/art --profile --64 -Ximage:$PWD/out/host/linux-x86/framework/core.art -Xusejit:true -Xcompiler-option --compiler-filter=speed -Xcompiler-option --compiler-backend=Optimizing -verbose:oat -Djava.library.path=$PWD/out/x86_64/host/linux-x86/lib64 -cp $PWD/generated/benchmarks.dex benchmarks.ExoPlayerBench.java.ExoPlayerBench

Bug: 36032648
Change-Id: I2dbcae43dd2972273511df9e01ebcbdfb1d84372
diff --git a/tools/art b/tools/art
index 91d6e27..f5e0860 100644
--- a/tools/art
+++ b/tools/art
@@ -16,18 +16,16 @@
 # shell dialect that should work on the host (e.g. bash), and
 # Android (e.g. mksh).
 
-function follow_links() {
-  if [ z"$BASH_SOURCE" != z ]; then
-    file="$BASH_SOURCE"
-  else
-    file="$0"
-  fi
-  while [ -h "$file" ]; do
-    # On Mac OS, readlink -f doesn't work.
-    file="$(readlink "$file")"
-  done
-  echo "$file"
-}
+# Follow all sym links to get the program name.
+if [ z"$BASH_SOURCE" != z ]; then
+  PROG_NAME="$BASH_SOURCE"
+else
+  PROG_NAME="$0"
+fi
+while [ -h "$PROG_NAME" ]; do
+  # On Mac OS, readlink -f doesn't work.
+  PROG_NAME="$(readlink "$PROG_NAME")"
+done
 
 function find_libdir() {
   # Get the actual file, $DALVIKVM may be a symbolic link.
@@ -39,9 +37,34 @@
   fi
 }
 
+ARGS_WITH_INTERPRET_ONLY=
+function replace_compiler_filter_with_interepret_only() {
+  ARGS_WITH_INTERPRET_ONLY=("$@")
+
+  found="false"
+  ((index=0))
+  while ((index <= $#)); do
+    what="${ARGS_WITH_INTERPRET_ONLY[$index]}"
+
+    case "$what" in
+      --compiler-filter=*)
+        ARGS_WITH_INTERPRET_ONLY[$index]="--compiler-filter=interpret-only"
+        found="true"
+        ;;
+    esac
+
+    ((index++))
+    shift
+  done
+  if [ "$found" != "true" ]; then
+    ARGS_WITH_INTERPRET_ONLY=(-Xcompiler-option --compiler-filter=interpret-only "${ARGS_WITH_INTERPRET_ONLY[@]}")
+  fi
+}
+
 invoke_with=
 DALVIKVM=dalvikvm
 LIBART=libart.so
+JIT_PROFILE=false
 
 while true; do
   if [ "$1" = "--invoke-with" ]; then
@@ -63,6 +86,9 @@
   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
@@ -71,7 +97,6 @@
   fi
 done
 
-PROG_NAME="$(follow_links)"
 PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
 ANDROID_ROOT=$PROG_DIR/..
 LIBDIR=$(find_libdir)
@@ -92,20 +117,65 @@
   DEBUG_OPTION="-Xcompiler-option --generate-debug-info"
 fi
 
-# We use the PIC core image to work with perf.
-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=""
+EXIT_STATUS=0
+if [ "$JIT_PROFILE" = true ]; then
+  # Create the profile. The runtime expects profiles to be created before
+  # execution.
+  PROFILE_PATH="$ANDROID_DATA/primary.prof"
+  touch $PROFILE_PATH
 
-EXIT_STATUS=$?
+  # Replace the compiler filter with interpret-only so that we
+  # can capture the profile.
+  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"
+
+  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
+    cat "$ANDROID_DATA/profile_gen.log"
+  fi
+
+  PROFILE_OPTION="-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 \
+      "$@"
+
+  EXIT_STATUS=$?
+fi
 
 if [ z"$PERF" != z ]; then
   if [ z"$PERF" = zreport ]; then