run-tests with jvmti-stress configuration

Adds a jvmti-stress mode to our run-tests. This mode runs our test
suite with every class-load being intercepted and undergoing
redefinition.

Test: DEXTER_BINARY="/path/to/built/dexter" \
      ./testrunner/testrunner.py --host --jvmti-stress -j40

Change-Id: I898e5cffea42a203aa4c45981b48baf8dc64fbb6
diff --git a/test/Android.bp b/test/Android.bp
index 9a8e174..8059a2f 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -318,6 +318,33 @@
     shared_libs: ["libartd"],
 }
 
+art_cc_defaults {
+    name: "libtistress-defaults",
+    defaults: ["libartagent-defaults"],
+    srcs: [
+        "ti-stress/stress.cc",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    header_libs: ["libopenjdkjvmti_headers"],
+}
+
+art_cc_test_library {
+    name: "libtistress",
+    defaults: [ "libtistress-defaults"],
+    shared_libs: ["libart"],
+}
+
+art_cc_test_library {
+    name: "libtistressd",
+    defaults: [
+        "libtistress-defaults",
+        "art_debug_defaults",
+    ],
+    shared_libs: ["libartd"],
+}
+
 art_cc_test_library {
     name: "libctstiagent",
     defaults: ["libtiagent-base-defaults"],
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ece5762..fadce8c 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -114,6 +114,14 @@
 TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtiagentd)
 endif
 
+# Also need libtistress.
+TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtistress)
+TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtistressd)
+ifdef TARGET_2ND_ARCH
+TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtistress)
+TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtistressd)
+endif
+
 # Also need libarttest.
 TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libarttest)
 TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libarttestd)
@@ -145,6 +153,8 @@
   $(HOST_OUT_EXECUTABLES)/hprof-conv \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagentd) \
+  $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtistress) \
+  $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtistressd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libartagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libartagentd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libarttest) \
@@ -158,8 +168,8 @@
 
 ifneq ($(HOST_PREFER_32_BIT),true)
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
-  $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libtiagent) \
-  $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libtiagentd) \
+  $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libtistress) \
+  $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libtistressd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libartagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libartagentd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libarttest) \
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index f1b6132..56cfd24 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -62,6 +62,7 @@
 TEST_VDEX="n"
 TEST_IS_NDEBUG="n"
 APP_IMAGE="y"
+JVMTI_STRESS="n"
 VDEX_FILTER=""
 PROFILE="n"
 RANDOM_PROFILE="n"
@@ -145,6 +146,11 @@
     elif [ "x$1" = "x--prebuild" ]; then
         PREBUILD="y"
         shift
+    elif [ "x$1" = "x--jvmti-stress" ]; then
+        # APP_IMAGE doesn't really work with jvmti-torture
+        APP_IMAGE="n"
+        JVMTI_STRESS="y"
+        shift
     elif [ "x$1" = "x--no-app-image" ]; then
         APP_IMAGE="n"
         shift
@@ -365,6 +371,28 @@
   fi
 fi
 
+if [[ "$JVMTI_STRESS" = "y" ]]; then
+  if [[ "$USE_JVM" = "n" ]]; then
+    plugin=libopenjdkjvmtid.so
+    agent=libtistressd.so
+    if  [[ "$TEST_IS_NDEBUG" = "y" ]]; then
+      agent=libtistress.so
+      plugin=libopenjdkjvmti.so
+    fi
+
+    file_1=$(mktemp --tmpdir=${DEX_LOCATION})
+    file_2=$(mktemp --tmpdir=${DEX_LOCATION})
+    # TODO Remove need for DEXTER_BINARY!
+    FLAGS="${FLAGS} -agentpath:${agent}=${DEXTER_BINARY},${file_1},${file_2}"
+    if [ "$IS_JVMTI_TEST" = "n" ]; then
+      FLAGS="${FLAGS} -Xplugin:${plugin}"
+      FLAGS="${FLAGS} -Xcompiler-option --debuggable"
+      # Always make the compilation be debuggable.
+      COMPILE_FLAGS="${COMPILE_FLAGS} --debuggable"
+    fi
+  fi
+fi
+
 if [ "$USE_JVM" = "y" ]; then
   export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
   # Xmx is necessary since we don't pass down the ART flags to JVM.
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 7891d4c..26b9ccb 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -366,5 +366,261 @@
                   "634-vdex-duplicate"],
         "description": ["Profile driven dexlayout does not work with vdex or dex verifier."],
         "variant": "speed-profile"
+    },
+    {
+        "tests": [
+            "004-checker-UnsafeTest18",
+            "127-checker-secondarydex",
+            "441-checker-inliner",
+            "442-checker-constant-folding",
+            "444-checker-nce",
+            "445-checker-licm",
+            "446-checker-inliner2",
+            "447-checker-inliner3",
+            "449-checker-bce",
+            "450-checker-types",
+            "455-checker-gvn",
+            "458-checker-instruct-simplification",
+            "462-checker-inlining-dex-files",
+            "463-checker-boolean-simplifier",
+            "464-checker-inline-sharpen-calls",
+            "465-checker-clinit-gvn",
+            "468-checker-bool-simplif-regression",
+            "473-checker-inliner-constants",
+            "474-checker-boolean-input",
+            "476-checker-ctor-memory-barrier",
+            "477-checker-bound-type",
+            "478-checker-clinit-check-pruning",
+            "478-checker-inline-noreturn",
+            "478-checker-inliner-nested-loop",
+            "480-checker-dead-blocks",
+            "482-checker-loop-back-edge-use",
+            "484-checker-register-hints",
+            "485-checker-dce-loop-update",
+            "485-checker-dce-switch",
+            "486-checker-must-do-null-check",
+            "487-checker-inline-calls",
+            "488-checker-inline-recursive-calls",
+            "490-checker-inline",
+            "492-checker-inline-invoke-interface",
+            "493-checker-inline-invoke-interface",
+            "494-checker-instanceof-tests",
+            "495-checker-checkcast-tests",
+            "496-checker-inlining-class-loader",
+            "508-checker-disassembly",
+            "510-checker-try-catch",
+            "517-checker-builder-fallthrough",
+            "521-checker-array-set-null",
+            "522-checker-regression-monitor-exit",
+            "523-checker-can-throw-regression",
+            "525-checker-arrays-fields1",
+            "525-checker-arrays-fields2",
+            "526-checker-caller-callee-regs",
+            "527-checker-array-access-split",
+            "529-checker-unresolved",
+            "530-checker-loops1",
+            "530-checker-loops2",
+            "530-checker-loops3",
+            "530-checker-loops4",
+            "530-checker-loops5",
+            "530-checker-lse",
+            "530-checker-lse2",
+            "530-checker-regression-reftyp-final",
+            "532-checker-nonnull-arrayset",
+            "534-checker-bce-deoptimization",
+            "536-checker-intrinsic-optimization",
+            "536-checker-needs-access-check",
+            "537-checker-arraycopy",
+            "537-checker-debuggable",
+            "537-checker-inline-and-unverified",
+            "537-checker-jump-over-jump",
+            "538-checker-embed-constants",
+            "540-checker-rtp-bug",
+            "543-checker-dce-trycatch",
+            "548-checker-inlining-and-dce",
+            "549-checker-types-merge",
+            "550-checker-multiply-accumulate",
+            "550-checker-regression-wide-store",
+            "551-checker-clinit",
+            "551-checker-shifter-operand",
+            "552-checker-primitive-typeprop",
+            "552-checker-sharpening",
+            "554-checker-rtp-checkcast",
+            "557-checker-instruct-simplifier-ror",
+            "557-checker-ref-equivalent",
+            "559-checker-irreducible-loop",
+            "559-checker-rtp-ifnotnull",
+            "562-checker-no-intermediate",
+            "563-checker-fakestring",
+            "563-checker-invoke-super",
+            "564-checker-bitcount",
+            "564-checker-inline-loop",
+            "564-checker-irreducible-loop",
+            "564-checker-negbitwise",
+            "565-checker-condition-liveness",
+            "565-checker-doublenegbitwise",
+            "565-checker-irreducible-loop",
+            "565-checker-rotate",
+            "566-checker-codegen-select",
+            "566-checker-signum",
+            "567-checker-compare",
+            "568-checker-onebit",
+            "569-checker-pattern-replacement",
+            "570-checker-osr",
+            "570-checker-select",
+            "572-checker-array-get-regression",
+            "573-checker-checkcast-regression",
+            "575-checker-isnan",
+            "575-checker-string-init-alias",
+            "577-checker-fp2int",
+            "580-checker-round",
+            "580-checker-string-fact-intrinsics",
+            "582-checker-bce-length",
+            "583-checker-zero",
+            "584-checker-div-bool",
+            "586-checker-null-array-get",
+            "588-checker-irreducib-lifetime-hole",
+            "590-checker-arr-set-null-regression",
+            "591-checker-regression-dead-loop",
+            "592-checker-regression-bool-input",
+            "593-checker-boolean-2-integral-conv",
+            "593-checker-long-2-float-regression",
+            "593-checker-shift-and-simplifier",
+            "594-checker-array-alias",
+            "594-checker-irreducible-linorder",
+            "596-checker-dead-phi",
+            "598-checker-irreducible-dominance",
+            "599-checker-irreducible-loop",
+            "603-checker-instanceof",
+            "608-checker-unresolved-lse",
+            "609-checker-inline-interface",
+            "609-checker-x86-bounds-check",
+            "611-checker-simplify-if",
+            "614-checker-dump-constant-location",
+            "615-checker-arm64-store-zero",
+            "618-checker-induction",
+            "619-checker-current-method",
+            "620-checker-bce-intrinsics",
+            "622-checker-bce-regressions",
+            "623-checker-loop-regressions",
+            "624-checker-stringops",
+            "625-checker-licm-regressions",
+            "626-checker-arm64-scratch-register",
+            "627-checker-unroll",
+            "631-checker-fp-abs",
+            "631-checker-get-class",
+            "632-checker-char-at-bounds",
+            "633-checker-rtp-getclass",
+            "635-checker-arm64-volatile-load-cc",
+            "637-checker-throw-inline",
+            "638-checker-inline-caches",
+            "639-checker-code-sinking",
+            "640-checker-boolean-simd",
+            "640-checker-byte-simd",
+            "640-checker-char-simd",
+            "640-checker-double-simd",
+            "640-checker-float-simd",
+            "640-checker-integer-valueof",
+            "640-checker-int-simd",
+            "640-checker-long-simd",
+            "640-checker-short-simd",
+            "641-checker-arraycopy",
+            "643-checker-bogus-ic",
+            "644-checker-deopt",
+            "645-checker-abs-simd",
+            "706-checker-scheduler"],
+        "description": ["Checker tests are not compatible with jvmti."],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "961-default-iface-resolution-gen",
+            "964-default-iface-init-gen"
+        ],
+        "description": ["Tests that just take too long with jvmti-stress"],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "950-redefine-intrinsic",
+            "951-threaded-obsolete",
+            "952-invoke-custom",
+            "953-invoke-polymorphic-compiler",
+            "954-invoke-polymorphic-verifier",
+            "955-methodhandles-smali",
+            "956-methodhandles",
+            "957-methodhandle-transforms",
+            "958-methodhandle-stackframe",
+            "959-invoke-polymorphic-accessors"
+        ],
+        "description": [
+            "Tests that use dex version 38 which is not yet supported by",
+            "dexter/slicer."
+        ],
+        "bug": "b/37272822",
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "137-cfi",
+            "595-profile-saving",
+            "900-hello-plugin",
+            "909-attach-agent",
+            "981-dedup-original-dex"
+        ],
+        "description": ["Tests that require exact knowledge of the number of plugins and agents."],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "097-duplicate-method",
+            "138-duplicate-classes-check2",
+            "804-class-extends-itself",
+            "921-hello-failure"
+        ],
+        "description": [
+            "Tests that use illegal dex files or otherwise break dexter assumptions"
+        ],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "018-stack-overflow",
+            "068-classloader",
+            "086-null-super",
+            "087-gc-after-link",
+            "626-const-class-linking",
+            "629-vdex-speed",
+            "944-transform-classloaders"
+        ],
+        "description": [
+            "Tests that use custom class loaders or other features not supported ",
+            "by our JVMTI implementation"
+        ],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "008-exceptions",
+            "031-class-attributes",
+            "034-call-null",
+            "038-inner-null",
+            "054-uncaught",
+            "122-npe",
+            "439-npe",
+            "911-get-stack-trace",
+            "946-obsolete-throw"
+        ],
+        "description": [
+            "Tests that use annotations and debug data that is not kept around by dexter."
+        ],
+        "bug": "b/37240685 & b/37239009",
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": ["901-hello-ti-agent"],
+        "description": ["Test fails to call VMInit event for unknown reasons."],
+        "bug": "b/37283252",
+        "variant": "jvmti-stress"
     }
 ]
diff --git a/test/run-test b/test/run-test
index e46099d..f60f766 100755
--- a/test/run-test
+++ b/test/run-test
@@ -137,6 +137,7 @@
 basic_verify="false"
 gc_verify="false"
 gc_stress="false"
+jvmti_stress="false"
 strace="false"
 always_clean="no"
 never_clean="no"
@@ -233,6 +234,9 @@
         basic_verify="true"
         gc_stress="true"
         shift
+    elif [ "x$1" = "x--jvmti-stress" ]; then
+        jvmti_stress="true"
+        shift
     elif [ "x$1" = "x--suspend-timeout" ]; then
         shift
         suspend_timeout="$1"
@@ -443,6 +447,9 @@
 if [ "$gc_stress" = "true" ]; then
   run_args="${run_args} --gc-stress --runtime-option -Xgc:gcstress --runtime-option -Xms2m --runtime-option -Xmx16m"
 fi
+if [ "$jvmti_stress" = "true" ]; then
+    run_args="${run_args} --no-app-image --jvmti-stress"
+fi
 if [ "$trace" = "true" ]; then
     run_args="${run_args} --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file-size:2000000"
     if [ "$trace_stream" = "true" ]; then
@@ -651,6 +658,7 @@
         echo "    --stream              Run method tracing in streaming mode (requires --trace)"
         echo "    --gcstress            Run with gc stress testing"
         echo "    --gcverify            Run with gc verification"
+        echo "    --jvmti-stress        Run with jvmti stress testing"
         echo "    --always-clean        Delete the test files even if the test fails."
         echo "    --never-clean         Keep the test files even if the test succeeds."
         echo "    --android-root [path] The path on target for the android root. (/system by default)."
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index f5e2a61..7d9297f 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -202,6 +202,9 @@
 # Note: ART_2ND_PHONY_TEST_TARGET_SUFFIX is 2ND_ART_PHONY_TEST_TARGET_SUFFIX in .mk files
 # Note: ART_2ND_PHONY_TEST_HOST_SUFFIX is 2ND_ART_PHONY_HOST_TARGET_SUFFIX in .mk files
 # Python does not let us have variable names starting with a digit, so it has differ.
+
+ART_TEST_RUN_TEST_JVMTI_STRESS = _getEnvBoolean('ART_TEST_RUN_TEST_JVMTI_STRESS', ART_TEST_FULL)
+
 if TARGET_2ND_ARCH:
   if "64" in TARGET_ARCH:
     ART_PHONY_TEST_TARGET_SUFFIX = "64"
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 5d3687e..8072631 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -73,6 +73,7 @@
 DEBUGGABLE_TYPES = set()
 ADDRESS_SIZES = set()
 OPTIMIZING_COMPILER_TYPES = set()
+JVMTI_TYPES = set()
 ADDRESS_SIZES_TARGET = {'host': set(), 'target': set()}
 # timeout for individual tests.
 # TODO: make it adjustable per tests and for buildbots
@@ -146,6 +147,7 @@
   VARIANT_TYPE_DICT['relocate'] = {'relocate-npatchoat', 'relocate', 'no-relocate'}
   VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'}
   VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'}
+  VARIANT_TYPE_DICT['jvmti'] = {'no-jvmti', 'jvmti-stress'}
   VARIANT_TYPE_DICT['compiler'] = {'interp-ac', 'interpreter', 'jit', 'optimizing',
                               'regalloc_gc', 'speed-profile'}
 
@@ -195,6 +197,10 @@
   if env.ART_TEST_SPEED_PROFILE:
     COMPILER_TYPES.add('speed-profile')
 
+  # By default only run without jvmti
+  if not JVMTI_TYPES:
+    JVMTI_TYPES.add('no-jvmti')
+
   # By default we run all 'compiler' variants.
   if not COMPILER_TYPES:
     COMPILER_TYPES.add('optimizing')
@@ -310,6 +316,7 @@
   total_test_count *= len(PICTEST_TYPES)
   total_test_count *= len(DEBUGGABLE_TYPES)
   total_test_count *= len(COMPILER_TYPES)
+  total_test_count *= len(JVMTI_TYPES)
   target_address_combinations = 0
   for target in TARGET_TYPES:
     for address_size in ADDRESS_SIZES_TARGET[target]:
@@ -336,10 +343,10 @@
   config = itertools.product(tests, TARGET_TYPES, RUN_TYPES, PREBUILD_TYPES,
                              COMPILER_TYPES, RELOCATE_TYPES, TRACE_TYPES,
                              GC_TYPES, JNI_TYPES, IMAGE_TYPES, PICTEST_TYPES,
-                             DEBUGGABLE_TYPES)
+                             DEBUGGABLE_TYPES, JVMTI_TYPES)
 
   for test, target, run, prebuild, compiler, relocate, trace, gc, \
-      jni, image, pictest, debuggable in config:
+      jni, image, pictest, debuggable, jvmti in config:
     for address_size in ADDRESS_SIZES_TARGET[target]:
       if stop_testrunner:
         # When ART_TEST_KEEP_GOING is set to false, then as soon as a test
@@ -361,11 +368,12 @@
       test_name += image + '-'
       test_name += pictest + '-'
       test_name += debuggable + '-'
+      test_name += jvmti + '-'
       test_name += test
       test_name += address_size
 
       variant_set = {target, run, prebuild, compiler, relocate, trace, gc, jni,
-                     image, pictest, debuggable, address_size}
+                     image, pictest, debuggable, jvmti, address_size}
 
       options_test = options_all
 
@@ -428,6 +436,9 @@
       if debuggable == 'debuggable':
         options_test += ' --debuggable'
 
+      if jvmti == 'jvmti-stress':
+        options_test += ' --jvmti-stress'
+
       if address_size == '64':
         options_test += ' --64'
 
@@ -762,6 +773,7 @@
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['image']) + ')-'
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['pictest']) + ')-'
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['debuggable']) + ')-'
+  regex += '(' + '|'.join(VARIANT_TYPE_DICT['jvmti']) + ')-'
   regex += '(' + '|'.join(RUN_TEST_SET) + ')'
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['address_sizes']) + ')$'
   match = re.match(regex, test_name)
@@ -777,8 +789,9 @@
     IMAGE_TYPES.add(match.group(9))
     PICTEST_TYPES.add(match.group(10))
     DEBUGGABLE_TYPES.add(match.group(11))
-    ADDRESS_SIZES.add(match.group(13))
-    return {match.group(12)}
+    JVMTI_TYPES.add(match.group(12))
+    ADDRESS_SIZES.add(match.group(14))
+    return {match.group(13)}
   raise ValueError(test_name + " is not a valid test")
 
 
@@ -918,6 +931,10 @@
     GC_TYPES.add('cms')
   if options['multipicimage']:
     IMAGE_TYPES.add('multipicimage')
+  if options['jvmti_stress']:
+    JVMTI_TYPES.add('jvmti-stress')
+  if options['no_jvmti']:
+    JVMTI_TYPES.add('no-jvmti')
   if options['verbose']:
     verbose = True
   if options['n_thread']:
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
new file mode 100644
index 0000000..fa49a35
--- /dev/null
+++ b/test/ti-stress/stress.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright 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.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <stdio.h>
+#include <sstream>
+
+#include "jvmti.h"
+#include "exec_utils.h"
+#include "utils.h"
+
+namespace art {
+
+// Should we do a 'full_rewrite' with this test?
+static constexpr bool kDoFullRewrite = true;
+
+struct StressData {
+  std::string dexter_cmd;
+  std::string out_temp_dex;
+  std::string in_temp_dex;
+  bool vm_class_loader_initialized;
+};
+
+static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
+  std::ofstream file(fname, std::ios::binary | std::ios::out | std::ios::trunc);
+  file.write(reinterpret_cast<const char*>(data), data_len);
+  file.flush();
+}
+
+static bool ReadIntoBuffer(const std::string& fname, /*out*/std::vector<unsigned char>* data) {
+  std::ifstream file(fname, std::ios::binary | std::ios::in);
+  file.seekg(0, std::ios::end);
+  size_t len = file.tellg();
+  data->resize(len);
+  file.seekg(0);
+  file.read(reinterpret_cast<char*>(data->data()), len);
+  return len != 0;
+}
+
+// TODO rewrite later.
+static bool DoExtractClassFromData(StressData* data,
+                                   const std::string& class_name,
+                                   jint in_len,
+                                   const unsigned char* in_data,
+                                   /*out*/std::vector<unsigned char>* dex) {
+  // Write the dex file into a temporary file.
+  WriteToFile(data->in_temp_dex, in_len, in_data);
+  // Clear out file so even if something suppresses the exit value we will still detect dexter
+  // failure.
+  WriteToFile(data->out_temp_dex, 0, nullptr);
+  // Have dexter do the extraction.
+  std::vector<std::string> args;
+  args.push_back(data->dexter_cmd);
+  if (kDoFullRewrite) {
+    args.push_back("-x");
+    args.push_back("full_rewrite");
+  }
+  args.push_back("-e");
+  args.push_back(class_name);
+  args.push_back("-o");
+  args.push_back(data->out_temp_dex);
+  args.push_back(data->in_temp_dex);
+  std::string error;
+  if (ExecAndReturnCode(args, &error) != 0) {
+    LOG(ERROR) << "unable to execute dexter: " << error;
+    return false;
+  }
+  return ReadIntoBuffer(data->out_temp_dex, dex);
+}
+
+// The hook we are using.
+void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
+                                         JNIEnv* jni_env ATTRIBUTE_UNUSED,
+                                         jclass class_being_redefined ATTRIBUTE_UNUSED,
+                                         jobject loader ATTRIBUTE_UNUSED,
+                                         const char* name,
+                                         jobject protection_domain ATTRIBUTE_UNUSED,
+                                         jint class_data_len,
+                                         const unsigned char* class_data,
+                                         jint* new_class_data_len,
+                                         unsigned char** new_class_data) {
+  std::vector<unsigned char> out;
+  std::string name_str(name);
+  // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner
+  // classes).
+  std::replace(name_str.begin(), name_str.end(), '/', '.');
+  StressData* data = nullptr;
+  CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
+           JVMTI_ERROR_NONE);
+  if (!data->vm_class_loader_initialized) {
+    LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
+                 << "initialized. Transforming this class could cause spurious test failures.";
+    return;
+  } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) {
+    LOG(INFO) << "Extracted class: " << name;
+    unsigned char* new_data;
+    CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data));
+    memcpy(new_data, out.data(), out.size());
+    *new_class_data_len = static_cast<jint>(out.size());
+    *new_class_data = new_data;
+  } else {
+    std::cerr << "Unable to extract class " << name_str << std::endl;
+    *new_class_data_len = 0;
+    *new_class_data = nullptr;
+  }
+}
+
+// Options are ${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2}
+static void ReadOptions(StressData* data, char* options) {
+  std::string ops(options);
+  data->dexter_cmd = ops.substr(0, ops.find(','));
+  ops = ops.substr(ops.find(',') + 1);
+  data->in_temp_dex = ops.substr(0, ops.find(','));
+  ops = ops.substr(ops.find(',') + 1);
+  data->out_temp_dex = ops;
+}
+
+// We need to make sure that VMClassLoader is initialized before we start redefining anything since
+// it can give (non-fatal) error messages if it's initialized after we've redefined BCP classes.
+// These error messages are expected and no problem but they will mess up our testing
+// infrastructure.
+static void JNICALL EnsureVMClassloaderInitializedCB(jvmtiEnv *jvmti_env,
+                                                     JNIEnv* jni_env,
+                                                     jthread thread ATTRIBUTE_UNUSED) {
+  // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have
+  // visibility but the class will be loaded behind the scenes.
+  LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!";
+  jclass klass = jni_env->FindClass("java/lang/VMClassLoader");
+  if (klass == nullptr) {
+    LOG(ERROR) << "Unable to find VMClassLoader class!";
+  } else {
+    // GetMethodID is spec'd to cause the class to be initialized.
+    jni_env->GetMethodID(klass, "hashCode", "()I");
+    jni_env->DeleteLocalRef(klass);
+    StressData* data = nullptr;
+    CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
+             JVMTI_ERROR_NONE);
+    data->vm_class_loader_initialized = true;
+  }
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
+                                               char* options,
+                                               void* reserved ATTRIBUTE_UNUSED) {
+  jvmtiEnv* jvmti = nullptr;
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
+    LOG(ERROR) << "Unable to get jvmti env.";
+    return 1;
+  }
+  StressData* data = nullptr;
+  if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
+                                          reinterpret_cast<unsigned char**>(&data))) {
+    LOG(ERROR) << "Unable to allocate data for stress test.";
+    return 1;
+  }
+  memset(data, 0, sizeof(StressData));
+  // Read the options into the static variables that hold them.
+  ReadOptions(data, options);
+  // Save the data
+  if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
+    LOG(ERROR) << "Unable to save stress test data.";
+    return 1;
+  }
+
+  // Just get all capabilities.
+  jvmtiCapabilities caps;
+  jvmti->GetPotentialCapabilities(&caps);
+  jvmti->AddCapabilities(&caps);
+
+  // Set callbacks.
+  jvmtiEventCallbacks cb;
+  memset(&cb, 0, sizeof(cb));
+  cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
+  cb.VMInit = EnsureVMClassloaderInitializedCB;
+  if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to set class file load hook cb!";
+    return 1;
+  }
+  if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+                                      JVMTI_EVENT_VM_INIT,
+                                      nullptr) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
+    return 1;
+  }
+  if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+                                      JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+                                      nullptr) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
+    return 1;
+  }
+  return 0;
+}
+
+}  // namespace art