diff options
| -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  |