Support a chroot-based environment in ART Buildbot's target harness.

When ART_TEST_CHROOT is defined, instead of having the ART Buildbot
install ART into a local path (set with ART_TEST_ANDROID_ROOT) and
hijack the linker (using CUSTOM_TARGET_LINKER), prepare and use a
chroot environment (set with ART_TEST_CHROOT).

In this scenario, ART and its dependencies can be built and used
normally (in particular, when building from the master-art Android
manifest) and still be executed as a standalone runtime outside of
the device's /system path, for testing purposes.

Test: Have the ART Buildbot build and run ART on device with chroot.
Bug: 34729697
Bug: 68125496
Change-Id: I08f1acd0d2813584f703fedb84e69df954cbdbda
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 31bddd5..10eb936 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -83,6 +83,10 @@
   make_command+=" debuggerd su"
   make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ "
   make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt"
+  if [[ -n "$ART_TEST_CHROOT" ]]; then
+    # These targets are needed for the chroot environment.
+    make_command+=" crash_dump event-log-tags"
+  fi
   mode_suffix="-target"
 fi
 
diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh
new file mode 100755
index 0000000..53072ae
--- /dev/null
+++ b/tools/cleanup-buildbot-device.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+green='\033[0;32m'
+nc='\033[0m'
+
+# Setup as root, as device cleanup requires it.
+adb root
+adb wait-for-device
+
+if [[ -n "$ART_TEST_CHROOT" ]]; then
+  # Check that ART_TEST_CHROOT is correctly defined.
+  if [[ "x$ART_TEST_CHROOT" != x/* ]]; then
+    echo "$ART_TEST_CHROOT is not an absolute path"
+    exit 1
+  fi
+
+  echo -e "${green}Clean up /system in chroot${nc}"
+  # Remove all files under /system except the potential property_contexts file.
+  #
+  # The current ART Buildbot set-up runs the "setup device" step
+  # (performed by script tools/setup-buildbot-device.sh) before the
+  # "device cleanup" step (implemented by this script). As
+  # property_contexts file aliases are created during the former step,
+  # we need this exception to prevent the property_contexts file under
+  # /system in the chroot from being removed by the latter step.
+  #
+  # TODO: Reorder ART Buildbot steps so that "device cleanup" happens
+  # before "setup device" and remove this special case.
+  #
+  # TODO: Also consider adding a "tear down device" step on the ART
+  # Buildbot (at the very end of a build) undoing (some of) the work
+  # done in the "device setup" step.
+  adb shell find "$ART_TEST_CHROOT/system" \
+    ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \
+    ! -type d \
+    -exec rm -f \{\} +
+
+  echo -e "${green}Clean up some subdirs in /data in chroot${nc}"
+  adb shell rm -rf \
+    "$ART_TEST_CHROOT/data/local/tmp/*" \
+    "$ART_TEST_CHROOT/data/art-test" \
+    "$ART_TEST_CHROOT/data/nativetest" \
+    "$ART_TEST_CHROOT/data/nativetest64" \
+    "$ART_TEST_CHROOT/data/run-test" \
+    "$ART_TEST_CHROOT/data/dalvik-cache/*" \
+    "$ART_TEST_CHROOT/data/misc/trace/*"
+else
+  adb shell rm -rf \
+    /data/local/tmp /data/art-test /data/nativetest /data/nativetest64 '/data/misc/trace/*'
+fi
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 56d412b..d376cad 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -68,6 +68,8 @@
 mode="target"
 # Use JIT compiling by default.
 use_jit=true
+# Don't use chroot by default.
+use_chroot=false
 variant_cmdline_parameter="--variant=X32"
 dump_command="/bin/true"
 # Timeout of JDWP test in ms.
@@ -110,6 +112,15 @@
     # We don't care about jit with the RI
     use_jit=false
     shift
+  elif [[ "$1" == "--chroot" ]]; then
+    use_chroot=true
+    # Adjust settings for chroot environment.
+    art="/system/bin/art"
+    art_debugee="sh /system/bin/art"
+    vm_command="--vm-command=$art"
+    device_dir="--device-dir=/tmp"
+    # Shift the "--chroot" flag and its argument.
+    shift 2
   elif [[ $1 == --test-timeout-ms ]]; then
     # Remove the --test-timeout-ms from the arguments.
     args=${args/$1}
@@ -191,6 +202,12 @@
   fi
 done
 
+if $use_chroot && [[ $mode == "host" ]]; then
+  # Chroot-based testing is not supported on host.
+  echo "Cannot use --chroot with --mode=host"
+  exit 1
+fi
+
 if [[ $has_gdb = "yes" ]]; then
   if [[ $explicit_debug = "no" ]]; then
     debug="yes"
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 26b5c0a..3537c1b 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -104,10 +104,14 @@
 gcstress=false
 debug=false
 
+# Don't use device mode by default.
+device_mode=false
+# Don't use chroot by default.
+use_chroot=false
+
 while true; do
   if [[ "$1" == "--mode=device" ]]; then
-    vogar_args="$vogar_args --device-dir=/data/local/tmp"
-    vogar_args="$vogar_args --vm-command=$android_root/bin/art"
+    device_mode=true
     vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art"
     shift
   elif [[ "$1" == "--mode=host" ]]; then
@@ -131,6 +135,10 @@
   elif [[ "$1" == "-Xgc:gcstress" ]]; then
     gcstress=true
     shift
+  elif [[ "$1" == "--chroot" ]]; then
+    use_chroot=true
+    # Shift the "--chroot" flag and its argument.
+    shift 2
   elif [[ "$1" == "" ]]; then
     break
   else
@@ -138,6 +146,23 @@
   fi
 done
 
+if $device_mode; then
+  if $use_chroot; then
+    vogar_args="$vogar_args --device-dir=/tmp"
+    vogar_args="$vogar_args --vm-command=/system/bin/art"
+  else
+    vogar_args="$vogar_args --device-dir=/data/local/tmp"
+    vogar_args="$vogar_args --vm-command=$android_root/bin/art"
+  fi
+else
+  # Host mode.
+  if $use_chroot; then
+    # Chroot-based testing is not supported on host.
+    echo "Cannot use --chroot with --mode=host"
+    exit 1
+  fi
+fi
+
 # Increase the timeout, as vogar cannot set individual test
 # timeout when being asked to run packages, and some tests go above
 # the default timeout.
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 5ce7f52..f71d973 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -17,8 +17,7 @@
 green='\033[0;32m'
 nc='\033[0m'
 
-# Setup as root, as the next buildbot step (device cleanup) requires it.
-# This is also required to set the date, if needed.
+# Setup as root, as some actions performed here (e.g. setting the date) requires it.
 adb root
 adb wait-for-device
 
@@ -100,3 +99,58 @@
   processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
   for i in $processes; do adb shell kill -9 $i; done
 fi
+
+if [[ -n "$ART_TEST_CHROOT" ]]; then
+  # Prepare the chroot dir.
+  echo -e "${green}Prepare the chroot dir in $ART_TEST_CHROOT${nc}"
+
+  # Check that ART_TEST_CHROOT is correctly defined.
+  [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; }
+
+  # Create chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT"
+
+  # Provide property_contexts file(s) in chroot.
+  # This is required to have Android system properties work from the chroot.
+  # Notes:
+  # - In Android N, only '/property_contexts' is expected.
+  # - In Android O, property_context files are expected under /system and /vendor.
+  # (See bionic/libc/bionic/system_properties.cpp for more information.)
+  property_context_files="/property_contexts \
+    /system/etc/selinux/plat_property_contexts \
+    /vendor/etc/selinux/nonplat_property_context \
+    /plat_property_contexts \
+    /nonplat_property_contexts"
+  for f in $property_context_files; do
+    adb shell test -f "$f" \
+      "&&" mkdir -p "$ART_TEST_CHROOT$(dirname $f)" \
+      "&&" cp -f "$f" "$ART_TEST_CHROOT$f"
+  done
+
+  # Create directories required for ART testing in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/tmp"
+  adb shell mkdir -p "$ART_TEST_CHROOT/data/dalvik-cache"
+  adb shell mkdir -p "$ART_TEST_CHROOT/data/local/tmp"
+
+  # Populate /etc in chroot with required files.
+  adb shell mkdir -p "$ART_TEST_CHROOT/system/etc"
+  adb shell "cd $ART_TEST_CHROOT && ln -s system/etc etc"
+
+  # Provide /proc in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/proc"
+  adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \
+    || adb shell mount -t proc proc "$ART_TEST_CHROOT/proc"
+
+  # Provide /sys in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/sys"
+  adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \
+    || adb shell mount -t sysfs sysfs "$ART_TEST_CHROOT/sys"
+  # Provide /sys/kernel/debug in chroot.
+  adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \
+    || adb shell mount -t debugfs debugfs "$ART_TEST_CHROOT/sys/kernel/debug"
+
+  # Provide /dev in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/dev"
+  adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \
+    || adb shell mount -o bind /dev "$ART_TEST_CHROOT/dev"
+fi