tools: Fix art script to delete OAT files before running dalvikvm

This way if the compiler filter (or other flags) are changed,
re-running the art script will use the new options.

In particular it fixes 'art --profile', previously it would just
reuse the OAT file from the first profiling (jit) run.

Test: art --profile -Xcompiler-option --compiler-filter=speed-profile -cp generated/benchmarks.dex benchmarks.CaffeineLoop.java.CaffeineLoop
Test: adb shell art --32 --profile -Xcompiler-option --compiler-filter=speed-profile -cp /data/local/tmp/benchmarks.dex benchmarks.CaffeineLoop.java.CaffeineLoop
Bug: 64162479
Change-Id: Ief1784b48c78464da91e71a649a6cf4b57c51b41
diff --git a/tools/art b/tools/art
index 18c5c84..561b019 100644
--- a/tools/art
+++ b/tools/art
@@ -119,6 +119,71 @@
   env "$@"
 }
 
+# Parse a colon-separated list into an array (e.g. "foo.dex:bar.dex" -> (foo.dex bar.dex))
+PARSE_CLASSPATH_RESULT=()  # Return value will be here due to shell limitations.
+parse_classpath() {
+  local cp="$1"
+  local oldifs=$IFS
+
+  local cp_array
+  cp_array=()
+
+  IFS=":"
+  for part in $cp; do
+    cp_array+=("$part")
+  done
+  IFS=$oldifs
+
+  PARSE_CLASSPATH_RESULT=("${cp_array[@]}")
+}
+
+# Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+# e.g. (-cp foo/classes.dex:bar/classes.dex) -> (foo/classes.dex bar/classes.dex)
+find_cp_in_args() {
+  local found="false"
+  local index=0
+  local what
+
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+      -cp|-classpath)
+        parse_classpath "$2"
+        # Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+        # Subsequent parses will overwrite the preceding.
+        shift
+        ;;
+    esac
+    shift
+  done
+}
+
+# Delete the 'oat' directories relative to the classpath's dex files.
+# e.g. (foo/classes.dex bar/classes.dex) would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory() {
+  local classpath
+  classpath=("$@")
+
+  local dirpath
+
+  for path in "${classpath[@]}"; do
+    dirpath="$(dirname "$path")"
+    [[ -d "$dirpath" ]] && verbose_run rm -rf "$dirpath/oat"
+  done
+}
+
+# Parse -cp <CP>, -classpath <CP>, and $CLASSPATH to find the dex files.
+# Each dex file's directory will have an 'oat' file directory, delete it.
+# Input: Command line arguments to the art script.
+# e.g. -cp foo/classes.dex:bar/classes.dex would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory_for_classpath() {
+  # First try: Use $CLASSPATH environment variable.
+  parse_classpath "$CLASSPATH"
+  # Second try: Look for latest -cp or -classpath arg which will take precedence.
+  find_cp_in_args "$@"
+
+  cleanup_oat_directory "${PARSE_CLASSPATH_RESULT[@]}"
+}
+
 # Attempt to find $ANDROID_ROOT/framework/<isa>/core.art' without knowing what <isa> is.
 function check_if_boot_image_file_exists() {
   local image_location_dir="$1"
@@ -154,6 +219,9 @@
 function run_art() {
   local image_location="$(detect_boot_image_location)"
 
+  # First cleanup any left-over 'oat' files from the last time dalvikvm was run.
+  cleanup_oat_directory_for_classpath "$@"
+  # Run dalvikvm.
   verbose_run ANDROID_DATA=$ANDROID_DATA               \
               ANDROID_ROOT=$ANDROID_ROOT               \
               LD_LIBRARY_PATH=$LD_LIBRARY_PATH         \
@@ -164,6 +232,9 @@
               -Xnorelocate                             \
               -Ximage:"$image_location"                \
               "$@"
+
+  # Avoid polluting disk with 'oat' files after dalvikvm has finished.
+  cleanup_oat_directory_for_classpath "$@"
 }
 
 while [[ "$1" = "-"* ]]; do