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