diff options
| author | 2017-04-13 01:32:51 +0000 | |
|---|---|---|
| committer | 2017-04-13 01:32:52 +0000 | |
| commit | 687fb792b2a819bb43d45eb720ed68a077ed1beb (patch) | |
| tree | ca2a6bb4b229c363a3eb1f19e4365f1b3193ff7f | |
| parent | dbe35eddc4324468e08afc0829787f02ee736cea (diff) | |
| parent | bb766464bced8ca7db9cdaf635ae04759151a088 (diff) | |
Merge changes I49c02b92,I898e5cff
* changes:
Ensure one can call DisposeEnvironment during event callbacks.
run-tests with jvmti-stress configuration
| -rw-r--r-- | runtime/openjdkjvmti/events-inl.h | 10 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/events.cc | 12 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/events.h | 2 | ||||
| -rw-r--r-- | test/Android.bp | 27 | ||||
| -rw-r--r-- | test/Android.run-test.mk | 14 | ||||
| -rwxr-xr-x | test/etc/run-test-jar | 28 | ||||
| -rw-r--r-- | test/knownfailures.json | 250 | ||||
| -rwxr-xr-x | test/run-test | 8 | ||||
| -rw-r--r-- | test/testrunner/env.py | 3 | ||||
| -rwxr-xr-x | test/testrunner/testrunner.py | 27 | ||||
| -rw-r--r-- | test/ti-stress/stress.cc | 210 |
11 files changed, 581 insertions, 10 deletions
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h index 1ddbb869f9..233b45cda8 100644 --- a/runtime/openjdkjvmti/events-inl.h +++ b/runtime/openjdkjvmti/events-inl.h @@ -131,6 +131,9 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, unsigned char* current_class_data = const_cast<unsigned char*>(class_data); ArtJvmTiEnv* last_env = nullptr; for (ArtJvmTiEnv* env : envs) { + if (env == nullptr) { + continue; + } if (ShouldDispatch<kEvent>(env, thread)) { jint new_len = 0; unsigned char* new_data = nullptr; @@ -171,7 +174,9 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, template <ArtJvmtiEvent kEvent, typename ...Args> inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { for (ArtJvmTiEnv* env : envs) { - DispatchEvent<kEvent, Args...>(env, thread, args...); + if (env != nullptr) { + DispatchEvent<kEvent, Args...>(env, thread, args...); + } } } @@ -253,6 +258,9 @@ inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { bool union_value = false; for (const ArtJvmTiEnv* stored_env : envs) { + if (stored_env == nullptr) { + continue; + } union_value |= stored_env->event_masks.global_event_mask.Test(event); union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event); if (union_value) { diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc index 34492a91fd..521494a856 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -141,13 +141,21 @@ void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool c } void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { - envs.push_back(env); + // Since we never shrink this array we might as well try to fill gaps. + auto it = std::find(envs.begin(), envs.end(), nullptr); + if (it != envs.end()) { + *it = env; + } else { + envs.push_back(env); + } } void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) { + // Since we might be currently iterating over the envs list we cannot actually erase elements. + // Instead we will simply replace them with 'nullptr' and skip them manually. auto it = std::find(envs.begin(), envs.end(), env); if (it != envs.end()) { - envs.erase(it); + *it = nullptr; for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal); i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal); ++i) { diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h index ae8bf0f803..b9e3cf0b09 100644 --- a/runtime/openjdkjvmti/events.h +++ b/runtime/openjdkjvmti/events.h @@ -202,6 +202,8 @@ class EventHandler { void HandleEventType(ArtJvmtiEvent event, bool enable); // List of all JvmTiEnv objects that have been created, in their creation order. + // NB Some elements might be null representing envs that have been deleted. They should be skipped + // anytime this list is used. std::vector<ArtJvmTiEnv*> envs; // A union of all enabled events, anywhere. diff --git a/test/Android.bp b/test/Android.bp index 9a8e1742ef..8059a2f204 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -318,6 +318,33 @@ art_cc_test_library { 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 ece57622fc..fadce8cd33 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 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 @@ ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \ $(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 @@ ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \ 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 f1b6132e94..56cfd244e6 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -62,6 +62,7 @@ DRY_RUN="n" # if y prepare to run the test but don't run it. TEST_VDEX="n" TEST_IS_NDEBUG="n" APP_IMAGE="y" +JVMTI_STRESS="n" VDEX_FILTER="" PROFILE="n" RANDOM_PROFILE="n" @@ -145,6 +146,11 @@ while true; do 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 @@ if [ "$IS_JVMTI_TEST" = "y" ]; then 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 7891d4c19f..54786bb772 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -366,5 +366,255 @@ "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" } ] diff --git a/test/run-test b/test/run-test index e46099d2e2..f60f766751 100755 --- a/test/run-test +++ b/test/run-test @@ -137,6 +137,7 @@ trace_stream="false" basic_verify="false" gc_verify="false" gc_stress="false" +jvmti_stress="false" strace="false" always_clean="no" never_clean="no" @@ -233,6 +234,9 @@ while true; do 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 @@ fi 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 @@ if [ "$usage" = "yes" ]; then 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 f5e2a61685..7d9297ff89 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -202,6 +202,9 @@ TARGET_ARCH = _get_build_var('TARGET_ARCH') # 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 5d3687e293..8072631e8e 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -73,6 +73,7 @@ PICTEST_TYPES = set() 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 @@ def gather_test_info(): 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 @@ def setup_test_env(): 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 @@ def run_tests(tests): 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 @@ def run_tests(tests): 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 @@ def run_tests(tests): 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 @@ def run_tests(tests): 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 @@ def parse_test_name(test_name): 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 @@ def parse_test_name(test_name): 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 @@ def parse_option(): 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 0000000000..fa49a35a8e --- /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 |