summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--build/Android.common_path.mk1
-rw-r--r--build/Android.gtest.mk14
-rw-r--r--build/art.go2
-rw-r--r--cmdline/cmdline_types.h4
-rw-r--r--compiler/driver/compiler_driver.cc90
-rw-r--r--compiler/image_writer.cc8
-rw-r--r--compiler/oat_writer.cc5
-rw-r--r--compiler/optimizing/code_generator_x86.cc175
-rw-r--r--compiler/optimizing/code_generator_x86.h49
-rw-r--r--compiler/optimizing/intrinsics_x86.cc42
-rw-r--r--compiler/optimizing/nodes_x86.h4
-rw-r--r--compiler/optimizing/pc_relative_fixups_x86.cc63
-rw-r--r--dexoptanalyzer/Android.bp68
-rw-r--r--dexoptanalyzer/dexoptanalyzer.cc265
-rw-r--r--dexoptanalyzer/dexoptanalyzer_test.cc311
-rw-r--r--oatdump/oatdump.cc181
-rw-r--r--oatdump/oatdump_test.cc1
-rw-r--r--runtime/Android.bp6
-rw-r--r--runtime/art_field-inl.h2
-rw-r--r--runtime/art_method-inl.h3
-rw-r--r--runtime/class_linker.cc220
-rw-r--r--runtime/class_linker.h17
-rw-r--r--runtime/class_linker_test.cc48
-rw-r--r--runtime/dex_cache_resolved_classes.h4
-rw-r--r--runtime/dexopt_test.cc236
-rw-r--r--runtime/dexopt_test.h97
-rw-r--r--runtime/experimental_flags.h10
-rw-r--r--runtime/gc/heap.cc7
-rw-r--r--runtime/hprof/hprof.cc2
-rw-r--r--runtime/image.cc2
-rw-r--r--runtime/instrumentation.cc6
-rw-r--r--runtime/memory_region.cc32
-rw-r--r--runtime/memory_region.h40
-rw-r--r--runtime/mirror/array-inl.h13
-rw-r--r--runtime/mirror/array.h4
-rw-r--r--runtime/mirror/class.cc18
-rw-r--r--runtime/mirror/class.h40
-rw-r--r--runtime/mirror/method_handle_impl.h4
-rw-r--r--runtime/native/java_lang_VMClassLoader.cc4
-rw-r--r--runtime/oat_file.h8
-rw-r--r--runtime/oat_file_assistant_test.cc242
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h15
-rw-r--r--runtime/openjdkjvmti/events-inl.h262
-rw-r--r--runtime/openjdkjvmti/events.cc17
-rw-r--r--runtime/openjdkjvmti/events.h24
-rw-r--r--runtime/openjdkjvmti/object_tagging.cc2
-rw-r--r--runtime/openjdkjvmti/ti_class.cc218
-rw-r--r--runtime/openjdkjvmti/ti_dump.cc2
-rw-r--r--runtime/openjdkjvmti/ti_phase.cc9
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc28
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h10
-rw-r--r--runtime/openjdkjvmti/ti_thread.cc13
-rw-r--r--runtime/openjdkjvmti/transform.cc38
-rw-r--r--runtime/parsed_options.cc36
-rw-r--r--runtime/runtime.cc17
-rw-r--r--runtime/runtime_callbacks.cc30
-rw-r--r--runtime/runtime_callbacks.h11
-rw-r--r--runtime/runtime_callbacks_test.cc26
-rw-r--r--runtime/runtime_options.def8
-rw-r--r--runtime/stack_map.cc1
-rw-r--r--runtime/stack_map.h38
-rw-r--r--runtime/utils.cc63
-rw-r--r--runtime/utils.h6
-rw-r--r--runtime/well_known_classes.cc2
-rw-r--r--runtime/well_known_classes.h1
-rw-r--r--test/008-exceptions/expected.txt18
-rw-r--r--test/008-exceptions/multidex.jpp27
-rw-r--r--test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java24
-rw-r--r--test/008-exceptions/src/Main.java85
-rw-r--r--test/008-exceptions/src/MultiDexBadInit.java25
-rw-r--r--test/008-exceptions/src/MultiDexBadInitWrapper1.java24
-rw-r--r--test/142-classloader2/expected.txt4
-rw-r--r--test/142-classloader2/src/Main.java15
-rw-r--r--test/154-gc-loop/expected.txt2
-rw-r--r--test/154-gc-loop/heap_interface.cc28
-rw-r--r--test/154-gc-loop/info.txt1
-rw-r--r--test/154-gc-loop/src/Main.java45
-rw-r--r--test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali5
-rw-r--r--test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali10
-rwxr-xr-xtest/900-hello-plugin/run4
-rwxr-xr-xtest/901-hello-ti-agent/run4
-rwxr-xr-xtest/902-hello-transformation/run4
-rwxr-xr-xtest/903-hello-tagging/run4
-rwxr-xr-xtest/904-object-allocation/run4
-rwxr-xr-xtest/905-object-free/run4
-rwxr-xr-xtest/906-iterate-heap/run4
-rwxr-xr-xtest/907-get-loaded-classes/run4
-rwxr-xr-xtest/908-gc-start-finish/run4
-rwxr-xr-xtest/909-attach-agent/run12
-rwxr-xr-xtest/910-methods/run4
-rwxr-xr-xtest/911-get-stack-trace/run4
-rw-r--r--test/912-classes/expected.txt4
-rwxr-xr-xtest/912-classes/run4
-rw-r--r--test/912-classes/src/Main.java33
-rwxr-xr-xtest/913-heaps/run4
-rwxr-xr-xtest/914-hello-obsolescence/run4
-rwxr-xr-xtest/915-obsolete-2/run4
-rwxr-xr-xtest/916-obsolete-jit/run4
-rwxr-xr-xtest/917-fields-transformation/run4
-rwxr-xr-xtest/918-fields/run4
-rwxr-xr-xtest/919-obsolete-fields/run4
-rwxr-xr-xtest/920-objects/run4
-rwxr-xr-xtest/921-hello-failure/run4
-rwxr-xr-xtest/922-properties/run4
-rwxr-xr-xtest/923-monitors/run4
-rwxr-xr-xtest/924-threads/run4
-rwxr-xr-xtest/925-threadgroups/run4
-rwxr-xr-xtest/926-multi-obsolescence/run4
-rwxr-xr-xtest/927-timers/run4
-rwxr-xr-xtest/928-jni-table/run4
-rwxr-xr-xtest/929-search/run4
-rwxr-xr-xtest/930-hello-retransform/run4
-rwxr-xr-xtest/931-agent-thread/run4
-rwxr-xr-xtest/932-transform-saves/run4
-rwxr-xr-xtest/933-misc-events/run4
-rwxr-xr-xtest/934-load-transform/build17
-rw-r--r--test/934-load-transform/expected.txt1
-rw-r--r--test/934-load-transform/info.txt1
-rwxr-xr-xtest/934-load-transform/run17
-rw-r--r--test/934-load-transform/src/Main.java66
-rw-r--r--test/934-load-transform/src/Transform.java21
-rwxr-xr-xtest/935-non-retransformable/build17
-rw-r--r--test/935-non-retransformable/expected.txt6
-rw-r--r--test/935-non-retransformable/info.txt1
-rwxr-xr-xtest/935-non-retransformable/run17
-rw-r--r--test/935-non-retransformable/src/Main.java95
-rw-r--r--test/935-non-retransformable/src/Transform.java21
-rw-r--r--test/957-methodhandle-transforms/expected.txt43
-rw-r--r--test/957-methodhandle-transforms/src/Main.java457
-rw-r--r--test/Android.bp4
-rw-r--r--test/Android.run-test.mk11
-rw-r--r--test/ErroneousInit/ErroneousInit.java23
-rwxr-xr-xtest/run-test4
-rw-r--r--test/ti-agent/common_helper.cc32
-rw-r--r--test/ti-agent/common_helper.h5
-rw-r--r--test/ti-agent/common_load.cc2
137 files changed, 3561 insertions, 1020 deletions
diff --git a/Android.bp b/Android.bp
index b9f1db5f46..d0e22fb873 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@ subdirs = [
"dexdump",
"dexlayout",
"dexlist",
+ "dexoptanalyzer",
"disassembler",
"imgdiag",
"oatdump",
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index e568ce283e..6de5aefc0b 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -109,6 +109,7 @@ endif
ART_CORE_DEBUGGABLE_EXECUTABLES := \
dex2oat \
+ dexoptanalyzer \
imgdiag \
oatdump \
patchoat \
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c87075f51c..bc0838435c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -28,6 +28,7 @@ GTEST_DEX_DIRECTORIES := \
DexToDexDecompiler \
ErroneousA \
ErroneousB \
+ ErroneousInit \
ExceptionHandle \
GetMethodSignature \
ImageLayoutA \
@@ -87,7 +88,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
@@ -100,6 +101,7 @@ ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
+ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -136,6 +138,12 @@ ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
+ART_GTEST_dexoptanalyzer_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
+ $(HOST_OUT_EXECUTABLES)/dexoptanalyzerd
+ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
+ dexoptanalyzerd
ART_GTEST_dex2oat_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
@@ -219,6 +227,7 @@ ART_TEST_MODULES := \
art_dexdump_tests \
art_dexlayout_tests \
art_dexlist_tests \
+ art_dexoptanalyzer_tests \
art_imgdiag_tests \
art_oatdump_tests \
art_profman_tests \
@@ -614,6 +623,9 @@ ART_GTEST_jni_internal_test_DEX_DEPS :=
ART_GTEST_oat_file_assistant_test_DEX_DEPS :=
ART_GTEST_oat_file_assistant_test_HOST_DEPS :=
ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
+ART_GTEST_dexoptanalyzer_test_DEX_DEPS :=
+ART_GTEST_dexoptanalyzer_test_HOST_DEPS :=
+ART_GTEST_dexoptanalyzer_test_TARGET_DEPS :=
ART_GTEST_dex2oat_test_DEX_DEPS :=
ART_GTEST_dex2oat_test_HOST_DEPS :=
ART_GTEST_dex2oat_test_TARGET_DEPS :=
diff --git a/build/art.go b/build/art.go
index 84269c3f23..baa6e59b55 100644
--- a/build/art.go
+++ b/build/art.go
@@ -58,7 +58,7 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) {
asflags = append(asflags, "-DART_HEAP_POISONING=1")
}
- if !envFalse(ctx, "ART_USE_READ_BARRIER") || ctx.AConfig().ArtUseReadBarrier() {
+ if !envFalse(ctx, "ART_USE_READ_BARRIER") && ctx.AConfig().ArtUseReadBarrier() {
// Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP.
// The default is BAKER.
barrierType := envDefault(ctx, "ART_READ_BARRIER_TYPE", "BAKER")
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 28c009edf0..f1123eb692 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -766,10 +766,6 @@ struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
if (option == "none") {
existing = ExperimentalFlags::kNone;
- } else if (option == "agents") {
- existing = existing | ExperimentalFlags::kAgents;
- } else if (option == "runtime-plugins") {
- existing = existing | ExperimentalFlags::kRuntimePlugins;
} else {
return Result::Failure(std::string("Unknown option '") + option + "'");
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c03ffcaa31..320304873d 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -940,6 +940,31 @@ inline void CompilerDriver::CheckThreadPools() {
DCHECK(single_thread_pool_ != nullptr);
}
+static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader,
+ const std::vector<const DexFile*>& dex_files) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
+ MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ for (const DexFile* dex_file : dex_files) {
+ for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+ const char* descriptor = dex_file->GetClassDescriptor(class_def);
+ cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
+ if (cls.Get() == nullptr) {
+ soa.Self()->ClearException();
+ } else if (&cls->GetDexFile() == dex_file) {
+ DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->IsCompileTimeVerified())
+ << cls->PrettyClass()
+ << " " << cls->GetStatus();
+ }
+ }
+ }
+}
+
void CompilerDriver::PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
@@ -984,6 +1009,9 @@ void CompilerDriver::PreCompile(jobject class_loader,
}
if (compiler_options_->IsAnyMethodCompilationEnabled()) {
+ if (kIsDebugBuild) {
+ EnsureVerifiedOrVerifyAtRuntime(class_loader, dex_files);
+ }
InitializeClasses(class_loader, dex_files, timings);
VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
}
@@ -1949,6 +1977,31 @@ static void PopulateVerifiedMethods(const DexFile& dex_file,
DCHECK(!it.HasNext());
}
+static void LoadAndUpdateStatus(const DexFile& dex_file,
+ const DexFile::ClassDef& class_def,
+ mirror::Class::Status status,
+ Handle<mirror::ClassLoader> class_loader,
+ Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(self);
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::Class> cls(hs.NewHandle<mirror::Class>(
+ class_linker->FindClass(self, descriptor, class_loader)));
+ if (cls.Get() != nullptr) {
+ // Check that the class is resolved with the current dex file. We might get
+ // a boot image class, or a class in a different dex file for multidex, and
+ // we should not update the status in that case.
+ if (&cls->GetDexFile() == &dex_file) {
+ ObjectLock<mirror::Class> lock(self, cls);
+ mirror::Class::SetStatus(cls, status, self);
+ }
+ } else {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+}
+
bool CompilerDriver::FastVerify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
@@ -1963,12 +2016,12 @@ bool CompilerDriver::FastVerify(jobject jclass_loader,
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
- MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (!verifier_deps->ValidateDependencies(class_loader, soa.Self())) {
return false;
}
+ bool compiler_only_verifies = !GetCompilerOptions().IsAnyMethodCompilationEnabled();
+
// We successfully validated the dependencies, now update class status
// of verified classes. Note that the dependencies also record which classes
// could not be fully verified; we could try again, but that would hurt verification
@@ -1983,28 +2036,16 @@ bool CompilerDriver::FastVerify(jobject jclass_loader,
for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
if (set.find(class_def.class_idx_) == set.end()) {
- if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+ if (compiler_only_verifies) {
// Just update the compiled_classes_ map. The compiler doesn't need to resolve
// the type.
compiled_classes_.Overwrite(
ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified));
} else {
- // Resolve the type, so later compilation stages know they don't need to verify
+ // Update the class status, so later compilation stages know they don't need to verify
// the class.
- const char* descriptor = dex_file->GetClassDescriptor(class_def);
- cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
- if (cls.Get() != nullptr) {
- // Check that the class is resolved with the current dex file. We might get
- // a boot image class, or a class in a different dex file for multidex, and
- // we should not update the status in that case.
- if (&cls->GetDexFile() == dex_file) {
- ObjectLock<mirror::Class> lock(soa.Self(), cls);
- mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
- }
- } else {
- DCHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
- }
+ LoadAndUpdateStatus(
+ *dex_file, class_def, mirror::Class::kStatusVerified, class_loader, soa.Self());
// Create `VerifiedMethod`s for each methods, the compiler expects one for
// quickening or compiling.
// Note that this means:
@@ -2013,6 +2054,14 @@ bool CompilerDriver::FastVerify(jobject jclass_loader,
// TODO(ngeoffray): Reconsider this once we refactor compiler filters.
PopulateVerifiedMethods(*dex_file, i, verification_results_);
}
+ } else if (!compiler_only_verifies) {
+ // Make sure later compilation stages know they should not try to verify
+ // this class again.
+ LoadAndUpdateStatus(*dex_file,
+ class_def,
+ mirror::Class::kStatusRetryVerificationAtRuntime,
+ class_loader,
+ soa.Self());
}
}
}
@@ -2199,7 +2248,7 @@ class SetVerifiedClassVisitor : public CompilationVisitor {
if (klass.Get() != nullptr) {
// Only do this if the class is resolved. If even resolution fails, quickening will go very,
// very wrong.
- if (klass->IsResolved()) {
+ if (klass->IsResolved() && !klass->IsErroneousResolved()) {
if (klass->GetStatus() < mirror::Class::kStatusVerified) {
ObjectLock<mirror::Class> lock(soa.Self(), klass);
// Set class status to verified.
@@ -2626,7 +2675,8 @@ CompiledClass* CompilerDriver::GetCompiledClass(ClassReference ref) const {
void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
switch (status) {
case mirror::Class::kStatusNotReady:
- case mirror::Class::kStatusError:
+ case mirror::Class::kStatusErrorResolved:
+ case mirror::Class::kStatusErrorUnresolved:
case mirror::Class::kStatusRetryVerificationAtRuntime:
case mirror::Class::kStatusVerified:
case mirror::Class::kStatusInitialized:
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 459aca3b38..15e4cd8e9c 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -756,7 +756,7 @@ bool ImageWriter::PruneAppImageClassInternal(
bool my_early_exit = false; // Only for ourselves, ignore caller.
// Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the
// app image.
- if (klass->GetStatus() == mirror::Class::kStatusError) {
+ if (klass->IsErroneous()) {
result = true;
} else {
ObjPtr<mirror::ClassExt> ext(klass->GetExtData());
@@ -777,8 +777,8 @@ bool ImageWriter::PruneAppImageClassInternal(
visited);
}
// Check static fields and their classes.
- size_t num_static_fields = klass->NumReferenceStaticFields();
- if (num_static_fields != 0 && klass->IsResolved()) {
+ if (klass->IsResolved() && klass->NumReferenceStaticFields() != 0) {
+ size_t num_static_fields = klass->NumReferenceStaticFields();
// Presumably GC can happen when we are cross compiling, it should not cause performance
// problems to do pointer size logic.
MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset(
@@ -1154,7 +1154,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack,
// Visit and assign offsets for fields and field arrays.
mirror::Class* as_klass = obj->AsClass();
mirror::DexCache* dex_cache = as_klass->GetDexCache();
- DCHECK_NE(as_klass->GetStatus(), mirror::Class::kStatusError);
+ DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus();
if (compile_app_image_) {
// Extra sanity, no boot loader classes should be left!
CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass();
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index de5af97b9a..bd2c5e3bfc 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -716,7 +716,10 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor {
if (compiled_class != nullptr) {
status = compiled_class->GetStatus();
} else if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
- status = mirror::Class::kStatusError;
+ // The oat class status is used only for verification of resolved classes,
+ // so use kStatusErrorResolved whether the class was resolved or unresolved
+ // during compile-time verification.
+ status = mirror::Class::kStatusErrorResolved;
} else {
status = mirror::Class::kStatusNotReady;
}
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 5c561f57aa..1b7431612d 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1023,7 +1023,8 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph,
jit_class_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
constant_area_start_(-1),
fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- method_address_offset_(-1) {
+ method_address_offset_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Use a fake return address register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
}
@@ -1498,8 +1499,9 @@ void InstructionCodeGeneratorX86::GenerateFPCompare(Location lhs,
DCHECK(const_area->IsEmittedAtUseSite());
__ ucomisd(lhs.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
- const_area->GetConstant()->AsDoubleConstant()->GetValue(),
- const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(rhs.IsDoubleStackSlot());
__ ucomisd(lhs.AsFpuRegister<XmmRegister>(), Address(ESP, rhs.GetStackIndex()));
@@ -1511,8 +1513,9 @@ void InstructionCodeGeneratorX86::GenerateFPCompare(Location lhs,
DCHECK(const_area->IsEmittedAtUseSite());
__ ucomiss(lhs.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
- const_area->GetConstant()->AsFloatConstant()->GetValue(),
- const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(rhs.IsStackSlot());
__ ucomiss(lhs.AsFpuRegister<XmmRegister>(), Address(ESP, rhs.GetStackIndex()));
@@ -2360,10 +2363,14 @@ void InstructionCodeGeneratorX86::VisitX86FPNeg(HX86FPNeg* neg) {
Register constant_area = locations->InAt(1).AsRegister<Register>();
XmmRegister mask = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
if (neg->GetType() == Primitive::kPrimFloat) {
- __ movss(mask, codegen_->LiteralInt32Address(INT32_C(0x80000000), constant_area));
+ __ movss(mask, codegen_->LiteralInt32Address(INT32_C(0x80000000),
+ neg->GetBaseMethodAddress(),
+ constant_area));
__ xorps(out.AsFpuRegister<XmmRegister>(), mask);
} else {
- __ movsd(mask, codegen_->LiteralInt64Address(INT64_C(0x8000000000000000), constant_area));
+ __ movsd(mask, codegen_->LiteralInt64Address(INT64_C(0x8000000000000000),
+ neg->GetBaseMethodAddress(),
+ constant_area));
__ xorpd(out.AsFpuRegister<XmmRegister>(), mask);
}
}
@@ -3012,8 +3019,9 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
DCHECK(const_area->IsEmittedAtUseSite());
__ addss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
- const_area->GetConstant()->AsFloatConstant()->GetValue(),
- const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(second.IsStackSlot());
__ addss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
@@ -3029,8 +3037,9 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
DCHECK(const_area->IsEmittedAtUseSite());
__ addsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
- const_area->GetConstant()->AsDoubleConstant()->GetValue(),
- const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(second.IsDoubleStackSlot());
__ addsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
@@ -3116,8 +3125,9 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
DCHECK(const_area->IsEmittedAtUseSite());
__ subss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
- const_area->GetConstant()->AsFloatConstant()->GetValue(),
- const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(second.IsStackSlot());
__ subss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
@@ -3134,6 +3144,7 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
__ subsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(second.IsDoubleStackSlot());
@@ -3304,6 +3315,7 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) {
__ mulss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(second.IsStackSlot());
@@ -3322,6 +3334,7 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) {
__ mulsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(second.IsDoubleStackSlot());
@@ -3690,6 +3703,7 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) {
__ divss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(second.IsStackSlot());
@@ -3706,8 +3720,9 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) {
DCHECK(const_area->IsEmittedAtUseSite());
__ divsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
- const_area->GetConstant()->AsDoubleConstant()->GetValue(),
- const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetBaseMethodAddress(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
} else {
DCHECK(second.IsDoubleStackSlot());
__ divsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
@@ -4454,18 +4469,7 @@ void CodeGeneratorX86::GenerateMemoryBarrier(MemBarrierKind kind) {
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
-
- // We disable pc-relative load when there is an irreducible loop, as the optimization
- // is incompatible with it.
- // TODO: Create as many X86ComputeBaseMethodAddress instructions
- // as needed for methods with irreducible loops.
- if (GetGraph()->HasIrreducibleLoops() &&
- (dispatch_info.method_load_kind ==
- HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
- dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
- }
- return dispatch_info;
+ return desired_dispatch_info;
}
Register CodeGeneratorX86::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
@@ -4518,7 +4522,10 @@ Location CodeGeneratorX86::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticO
__ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset));
// Bind a new fixup label at the end of the "movl" insn.
uint32_t offset = invoke->GetDexCacheArrayOffset();
- __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset));
+ __ Bind(NewPcRelativeDexCacheArrayPatch(
+ invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(),
+ invoke->GetDexFileForPcRelativeDexCache(),
+ offset));
break;
}
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
@@ -4603,31 +4610,54 @@ void CodeGeneratorX86::RecordSimplePatch() {
void CodeGeneratorX86::RecordBootStringPatch(HLoadString* load_string) {
DCHECK(GetCompilerOptions().IsBootImage());
- string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex().index_);
+ HX86ComputeBaseMethodAddress* address = nullptr;
+ if (GetCompilerOptions().GetCompilePic()) {
+ address = load_string->InputAt(0)->AsX86ComputeBaseMethodAddress();
+ } else {
+ DCHECK_EQ(load_string->InputCount(), 0u);
+ }
+ string_patches_.emplace_back(address,
+ load_string->GetDexFile(),
+ load_string->GetStringIndex().index_);
__ Bind(&string_patches_.back().label);
}
void CodeGeneratorX86::RecordBootTypePatch(HLoadClass* load_class) {
- boot_image_type_patches_.emplace_back(load_class->GetDexFile(),
+ HX86ComputeBaseMethodAddress* address = nullptr;
+ if (GetCompilerOptions().GetCompilePic()) {
+ address = load_class->InputAt(0)->AsX86ComputeBaseMethodAddress();
+ } else {
+ DCHECK_EQ(load_class->InputCount(), 0u);
+ }
+ boot_image_type_patches_.emplace_back(address,
+ load_class->GetDexFile(),
load_class->GetTypeIndex().index_);
__ Bind(&boot_image_type_patches_.back().label);
}
Label* CodeGeneratorX86::NewTypeBssEntryPatch(HLoadClass* load_class) {
- type_bss_entry_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_);
+ HX86ComputeBaseMethodAddress* address =
+ load_class->InputAt(0)->AsX86ComputeBaseMethodAddress();
+ type_bss_entry_patches_.emplace_back(
+ address, load_class->GetDexFile(), load_class->GetTypeIndex().index_);
return &type_bss_entry_patches_.back().label;
}
Label* CodeGeneratorX86::NewStringBssEntryPatch(HLoadString* load_string) {
DCHECK(!GetCompilerOptions().IsBootImage());
- string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex().index_);
+ HX86ComputeBaseMethodAddress* address =
+ load_string->InputAt(0)->AsX86ComputeBaseMethodAddress();
+ string_patches_.emplace_back(
+ address, load_string->GetDexFile(), load_string->GetStringIndex().index_);
return &string_patches_.back().label;
}
-Label* CodeGeneratorX86::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
- uint32_t element_offset) {
+Label* CodeGeneratorX86::NewPcRelativeDexCacheArrayPatch(
+ HX86ComputeBaseMethodAddress* method_address,
+ const DexFile& dex_file,
+ uint32_t element_offset) {
// Add the patch entry and bind its label at the end of the instruction.
- pc_relative_dex_cache_patches_.emplace_back(dex_file, element_offset);
+ pc_relative_dex_cache_patches_.emplace_back(method_address, dex_file, element_offset);
return &pc_relative_dex_cache_patches_.back().label;
}
@@ -4637,12 +4667,12 @@ constexpr uint32_t kLabelPositionToLiteralOffsetAdjustment = 4u;
template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
inline void CodeGeneratorX86::EmitPcRelativeLinkerPatches(
- const ArenaDeque<PatchInfo<Label>>& infos,
+ const ArenaDeque<X86PcRelativePatchInfo>& infos,
ArenaVector<LinkerPatch>* linker_patches) {
- for (const PatchInfo<Label>& info : infos) {
+ for (const X86PcRelativePatchInfo& info : infos) {
uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
- linker_patches->push_back(
- Factory(literal_offset, &info.dex_file, GetMethodAddressOffset(), info.index));
+ linker_patches->push_back(Factory(
+ literal_offset, &info.dex_file, GetMethodAddressOffset(info.method_address), info.index));
}
}
@@ -6002,13 +6032,6 @@ HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind(
FALLTHROUGH_INTENDED;
case HLoadClass::LoadKind::kBssEntry:
DCHECK(!Runtime::Current()->UseJitCompilation()); // Note: boot image is also non-JIT.
- // We disable pc-relative load when there is an irreducible loop, as the optimization
- // is incompatible with it.
- // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods
- // with irreducible loops.
- if (GetGraph()->HasIrreducibleLoops()) {
- return HLoadClass::LoadKind::kDexCacheViaMethod;
- }
break;
case HLoadClass::LoadKind::kBootImageAddress:
break;
@@ -6195,13 +6218,6 @@ HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
FALLTHROUGH_INTENDED;
case HLoadString::LoadKind::kBssEntry:
DCHECK(!Runtime::Current()->UseJitCompilation()); // Note: boot image is also non-JIT.
- // We disable pc-relative load when there is an irreducible loop, as the optimization
- // is incompatible with it.
- // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods
- // with irreducible loops.
- if (GetGraph()->HasIrreducibleLoops()) {
- return HLoadString::LoadKind::kDexCacheViaMethod;
- }
break;
case HLoadString::LoadKind::kBootImageAddress:
break;
@@ -7489,7 +7505,7 @@ void InstructionCodeGeneratorX86::VisitX86ComputeBaseMethodAddress(
__ Bind(&next_instruction);
// Remember this offset for later use with constant area.
- codegen_->SetMethodAddressOffset(GetAssembler()->CodeSize());
+ codegen_->AddMethodAddressOffset(insn, GetAssembler()->CodeSize());
// Grab the return address off the stack.
__ popl(reg);
@@ -7536,17 +7552,20 @@ void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromCons
switch (insn->GetType()) {
case Primitive::kPrimFloat:
__ movss(out.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralFloatAddress(value->AsFloatConstant()->GetValue(), const_area));
+ codegen_->LiteralFloatAddress(
+ value->AsFloatConstant()->GetValue(), insn->GetBaseMethodAddress(), const_area));
break;
case Primitive::kPrimDouble:
__ movsd(out.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralDoubleAddress(value->AsDoubleConstant()->GetValue(), const_area));
+ codegen_->LiteralDoubleAddress(
+ value->AsDoubleConstant()->GetValue(), insn->GetBaseMethodAddress(), const_area));
break;
case Primitive::kPrimInt:
__ movl(out.AsRegister<Register>(),
- codegen_->LiteralInt32Address(value->AsIntConstant()->GetValue(), const_area));
+ codegen_->LiteralInt32Address(
+ value->AsIntConstant()->GetValue(), insn->GetBaseMethodAddress(), const_area));
break;
default:
@@ -7559,13 +7578,18 @@ void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromCons
*/
class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenerator> {
public:
- RIPFixup(CodeGeneratorX86& codegen, size_t offset)
- : codegen_(&codegen), offset_into_constant_area_(offset) {}
+ RIPFixup(CodeGeneratorX86& codegen,
+ HX86ComputeBaseMethodAddress* base_method_address,
+ size_t offset)
+ : codegen_(&codegen),
+ base_method_address_(base_method_address),
+ offset_into_constant_area_(offset) {}
protected:
void SetOffset(size_t offset) { offset_into_constant_area_ = offset; }
CodeGeneratorX86* codegen_;
+ HX86ComputeBaseMethodAddress* base_method_address_;
private:
void Process(const MemoryRegion& region, int pos) OVERRIDE {
@@ -7574,7 +7598,8 @@ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenera
// The value to patch is the distance from the offset in the constant area
// from the address computed by the HX86ComputeBaseMethodAddress instruction.
int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_;
- int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset();
+ int32_t relative_position =
+ constant_offset - codegen_->GetMethodAddressOffset(base_method_address_);
// Patch in the right value.
region.StoreUnaligned<int32_t>(pos - 4, relative_position);
@@ -7591,7 +7616,8 @@ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenera
class JumpTableRIPFixup : public RIPFixup {
public:
JumpTableRIPFixup(CodeGeneratorX86& codegen, HX86PackedSwitch* switch_instr)
- : RIPFixup(codegen, static_cast<size_t>(-1)), switch_instr_(switch_instr) {}
+ : RIPFixup(codegen, switch_instr->GetBaseMethodAddress(), static_cast<size_t>(-1)),
+ switch_instr_(switch_instr) {}
void CreateJumpTable() {
X86Assembler* assembler = codegen_->GetAssembler();
@@ -7602,7 +7628,7 @@ class JumpTableRIPFixup : public RIPFixup {
// The label values in the jump table are computed relative to the
// instruction addressing the constant area.
- const int32_t relative_offset = codegen_->GetMethodAddressOffset();
+ const int32_t relative_offset = codegen_->GetMethodAddressOffset(base_method_address_);
// Populate the jump table with the correct values for the jump table.
int32_t num_entries = switch_instr_->GetNumEntries();
@@ -7644,23 +7670,32 @@ void CodeGeneratorX86::Finalize(CodeAllocator* allocator) {
CodeGenerator::Finalize(allocator);
}
-Address CodeGeneratorX86::LiteralDoubleAddress(double v, Register reg) {
- AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddDouble(v));
+Address CodeGeneratorX86::LiteralDoubleAddress(double v,
+ HX86ComputeBaseMethodAddress* method_base,
+ Register reg) {
+ AssemblerFixup* fixup =
+ new (GetGraph()->GetArena()) RIPFixup(*this, method_base, __ AddDouble(v));
return Address(reg, kDummy32BitOffset, fixup);
}
-Address CodeGeneratorX86::LiteralFloatAddress(float v, Register reg) {
- AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddFloat(v));
+Address CodeGeneratorX86::LiteralFloatAddress(float v,
+ HX86ComputeBaseMethodAddress* method_base,
+ Register reg) {
+ AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, method_base, __ AddFloat(v));
return Address(reg, kDummy32BitOffset, fixup);
}
-Address CodeGeneratorX86::LiteralInt32Address(int32_t v, Register reg) {
- AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddInt32(v));
+Address CodeGeneratorX86::LiteralInt32Address(int32_t v,
+ HX86ComputeBaseMethodAddress* method_base,
+ Register reg) {
+ AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, method_base, __ AddInt32(v));
return Address(reg, kDummy32BitOffset, fixup);
}
-Address CodeGeneratorX86::LiteralInt64Address(int64_t v, Register reg) {
- AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddInt64(v));
+Address CodeGeneratorX86::LiteralInt64Address(int64_t v,
+ HX86ComputeBaseMethodAddress* method_base,
+ Register reg) {
+ AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, method_base, __ AddInt64(v));
return Address(reg, kDummy32BitOffset, fixup);
}
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 9eb97658da..7350fcc48a 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -415,7 +415,9 @@ class CodeGeneratorX86 : public CodeGenerator {
void RecordBootTypePatch(HLoadClass* load_class);
Label* NewTypeBssEntryPatch(HLoadClass* load_class);
Label* NewStringBssEntryPatch(HLoadString* load_string);
- Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
+ Label* NewPcRelativeDexCacheArrayPatch(HX86ComputeBaseMethodAddress* method_address,
+ const DexFile& dex_file,
+ uint32_t element_offset);
Label* NewJitRootStringPatch(const DexFile& dex_file,
dex::StringIndex dex_index,
Handle<mirror::String> handle);
@@ -463,22 +465,22 @@ class CodeGeneratorX86 : public CodeGenerator {
return isa_features_;
}
- void SetMethodAddressOffset(int32_t offset) {
- method_address_offset_ = offset;
+ void AddMethodAddressOffset(HX86ComputeBaseMethodAddress* method_base, int32_t offset) {
+ method_address_offset_.Put(method_base->GetId(), offset);
}
- int32_t GetMethodAddressOffset() const {
- return method_address_offset_;
+ int32_t GetMethodAddressOffset(HX86ComputeBaseMethodAddress* method_base) const {
+ return method_address_offset_.Get(method_base->GetId());
}
int32_t ConstantAreaStart() const {
return constant_area_start_;
}
- Address LiteralDoubleAddress(double v, Register reg);
- Address LiteralFloatAddress(float v, Register reg);
- Address LiteralInt32Address(int32_t v, Register reg);
- Address LiteralInt64Address(int64_t v, Register reg);
+ Address LiteralDoubleAddress(double v, HX86ComputeBaseMethodAddress* method_base, Register reg);
+ Address LiteralFloatAddress(float v, HX86ComputeBaseMethodAddress* method_base, Register reg);
+ Address LiteralInt32Address(int32_t v, HX86ComputeBaseMethodAddress* method_base, Register reg);
+ Address LiteralInt64Address(int64_t v, HX86ComputeBaseMethodAddress* method_base, Register reg);
// Load a 32-bit value into a register in the most efficient manner.
void Load32BitValue(Register dest, int32_t value);
@@ -603,12 +605,21 @@ class CodeGeneratorX86 : public CodeGenerator {
static constexpr int32_t kDummy32BitOffset = 256;
private:
- Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
+ struct X86PcRelativePatchInfo : PatchInfo<Label> {
+ X86PcRelativePatchInfo(HX86ComputeBaseMethodAddress* address,
+ const DexFile& target_dex_file,
+ uint32_t target_index)
+ : PatchInfo(target_dex_file, target_index),
+ method_address(address) {}
+ HX86ComputeBaseMethodAddress* method_address;
+ };
template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
- void EmitPcRelativeLinkerPatches(const ArenaDeque<PatchInfo<Label>>& infos,
+ void EmitPcRelativeLinkerPatches(const ArenaDeque<X86PcRelativePatchInfo>& infos,
ArenaVector<LinkerPatch>* linker_patches);
+ Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
+
// Labels for each block that will be compiled.
Label* block_labels_; // Indexed by block id.
Label frame_entry_label_;
@@ -619,15 +630,15 @@ class CodeGeneratorX86 : public CodeGenerator {
const X86InstructionSetFeatures& isa_features_;
// PC-relative DexCache access info.
- ArenaDeque<PatchInfo<Label>> pc_relative_dex_cache_patches_;
+ ArenaDeque<X86PcRelativePatchInfo> pc_relative_dex_cache_patches_;
// Patch locations for patchoat where the linker doesn't do any other work.
ArenaDeque<Label> simple_patches_;
// String patch locations; type depends on configuration (app .bss or boot image PIC/non-PIC).
- ArenaDeque<PatchInfo<Label>> string_patches_;
+ ArenaDeque<X86PcRelativePatchInfo> string_patches_;
// Type patch locations for boot image; type depends on configuration (boot image PIC/non-PIC).
- ArenaDeque<PatchInfo<Label>> boot_image_type_patches_;
+ ArenaDeque<X86PcRelativePatchInfo> boot_image_type_patches_;
// Type patch locations for kBssEntry.
- ArenaDeque<PatchInfo<Label>> type_bss_entry_patches_;
+ ArenaDeque<X86PcRelativePatchInfo> type_bss_entry_patches_;
// Patches for string root accesses in JIT compiled code.
ArenaDeque<PatchInfo<Label>> jit_string_patches_;
@@ -642,11 +653,9 @@ class CodeGeneratorX86 : public CodeGenerator {
// Fixups for jump tables that need to be patched after the constant table is generated.
ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_;
- // If there is a HX86ComputeBaseMethodAddress instruction in the graph
- // (which shall be the sole instruction of this kind), subtracting this offset
- // from the value contained in the out register of this HX86ComputeBaseMethodAddress
- // instruction gives the address of the start of this method.
- int32_t method_address_offset_;
+ // Maps a HX86ComputeBaseMethodAddress instruction id, to its offset in the
+ // compiled code.
+ ArenaSafeMap<uint32_t, int32_t> method_address_offset_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86);
};
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 922c3bcac9..e1b7ea53b4 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -356,23 +356,28 @@ static void CreateFloatToFloat(ArenaAllocator* arena, HInvoke* invoke) {
}
}
-static void MathAbsFP(LocationSummary* locations,
+static void MathAbsFP(HInvoke* invoke,
bool is64bit,
X86Assembler* assembler,
CodeGeneratorX86* codegen) {
+ LocationSummary* locations = invoke->GetLocations();
Location output = locations->Out();
DCHECK(output.IsFpuRegister());
if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) {
+ HX86ComputeBaseMethodAddress* method_address =
+ invoke->InputAt(1)->AsX86ComputeBaseMethodAddress();
DCHECK(locations->InAt(1).IsRegister());
// We also have a constant area pointer.
Register constant_area = locations->InAt(1).AsRegister<Register>();
XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
if (is64bit) {
- __ movsd(temp, codegen->LiteralInt64Address(INT64_C(0x7FFFFFFFFFFFFFFF), constant_area));
+ __ movsd(temp, codegen->LiteralInt64Address(
+ INT64_C(0x7FFFFFFFFFFFFFFF), method_address, constant_area));
__ andpd(output.AsFpuRegister<XmmRegister>(), temp);
} else {
- __ movss(temp, codegen->LiteralInt32Address(INT32_C(0x7FFFFFFF), constant_area));
+ __ movss(temp, codegen->LiteralInt32Address(
+ INT32_C(0x7FFFFFFF), method_address, constant_area));
__ andps(output.AsFpuRegister<XmmRegister>(), temp);
}
} else {
@@ -396,7 +401,7 @@ void IntrinsicLocationsBuilderX86::VisitMathAbsDouble(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorX86::VisitMathAbsDouble(HInvoke* invoke) {
- MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler(), codegen_);
+ MathAbsFP(invoke, /* is64bit */ true, GetAssembler(), codegen_);
}
void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) {
@@ -404,7 +409,7 @@ void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorX86::VisitMathAbsFloat(HInvoke* invoke) {
- MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler(), codegen_);
+ MathAbsFP(invoke, /* is64bit */ false, GetAssembler(), codegen_);
}
static void CreateAbsIntLocation(ArenaAllocator* arena, HInvoke* invoke) {
@@ -486,11 +491,12 @@ void IntrinsicCodeGeneratorX86::VisitMathAbsLong(HInvoke* invoke) {
GenAbsLong(invoke->GetLocations(), GetAssembler());
}
-static void GenMinMaxFP(LocationSummary* locations,
+static void GenMinMaxFP(HInvoke* invoke,
bool is_min,
bool is_double,
X86Assembler* assembler,
CodeGeneratorX86* codegen) {
+ LocationSummary* locations = invoke->GetLocations();
Location op1_loc = locations->InAt(0);
Location op2_loc = locations->InAt(1);
Location out_loc = locations->Out();
@@ -553,12 +559,14 @@ static void GenMinMaxFP(LocationSummary* locations,
__ Bind(&nan);
// Do we have a constant area pointer?
if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) {
+ HX86ComputeBaseMethodAddress* method_address =
+ invoke->InputAt(2)->AsX86ComputeBaseMethodAddress();
DCHECK(locations->InAt(2).IsRegister());
Register constant_area = locations->InAt(2).AsRegister<Register>();
if (is_double) {
- __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, constant_area));
+ __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, method_address, constant_area));
} else {
- __ movss(out, codegen->LiteralInt32Address(kFloatNaN, constant_area));
+ __ movss(out, codegen->LiteralInt32Address(kFloatNaN, method_address, constant_area));
}
} else {
if (is_double) {
@@ -608,7 +616,7 @@ void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(),
+ GenMinMaxFP(invoke,
/* is_min */ true,
/* is_double */ true,
GetAssembler(),
@@ -620,7 +628,7 @@ void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(),
+ GenMinMaxFP(invoke,
/* is_min */ true,
/* is_double */ false,
GetAssembler(),
@@ -632,7 +640,7 @@ void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(),
+ GenMinMaxFP(invoke,
/* is_min */ false,
/* is_double */ true,
GetAssembler(),
@@ -644,7 +652,7 @@ void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(),
+ GenMinMaxFP(invoke,
/* is_min */ false,
/* is_double */ false,
GetAssembler(),
@@ -905,10 +913,16 @@ void IntrinsicCodeGeneratorX86::VisitMathRoundFloat(HInvoke* invoke) {
__ subss(t2, t1);
if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) {
// Direct constant area available.
+ HX86ComputeBaseMethodAddress* method_address =
+ invoke->InputAt(1)->AsX86ComputeBaseMethodAddress();
Register constant_area = locations->InAt(1).AsRegister<Register>();
- __ comiss(t2, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(0.5f), constant_area));
+ __ comiss(t2, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(0.5f),
+ method_address,
+ constant_area));
__ j(kBelow, &skip_incr);
- __ addss(t1, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(1.0f), constant_area));
+ __ addss(t1, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(1.0f),
+ method_address,
+ constant_area));
__ Bind(&skip_incr);
} else {
// No constant area: go through stack.
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index fa479760fe..75893c3129 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -71,6 +71,10 @@ class HX86FPNeg FINAL : public HExpression<2> {
SetRawInputAt(1, method_base);
}
+ HX86ComputeBaseMethodAddress* GetBaseMethodAddress() const {
+ return InputAt(1)->AsX86ComputeBaseMethodAddress();
+ }
+
DECLARE_INSTRUCTION(X86FPNeg);
private:
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index 2befc8ca4e..a1c916f43a 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -84,8 +84,8 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
HLoadClass::LoadKind load_kind = load_class->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative ||
load_kind == HLoadClass::LoadKind::kBssEntry) {
- InitializePCRelativeBasePointer();
- load_class->AddSpecialInput(base_);
+ HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_class);
+ load_class->AddSpecialInput(method_address);
}
}
@@ -93,8 +93,8 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
HLoadString::LoadKind load_kind = load_string->GetLoadKind();
if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
load_kind == HLoadString::LoadKind::kBssEntry) {
- InitializePCRelativeBasePointer();
- load_string->AddSpecialInput(base_);
+ HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_string);
+ load_string->AddSpecialInput(method_address);
}
}
@@ -132,13 +132,13 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
void VisitNeg(HNeg* neg) OVERRIDE {
if (Primitive::IsFloatingPointType(neg->GetType())) {
// We need to replace the HNeg with a HX86FPNeg in order to address the constant area.
- InitializePCRelativeBasePointer();
+ HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(neg);
HGraph* graph = GetGraph();
HBasicBlock* block = neg->GetBlock();
HX86FPNeg* x86_fp_neg = new (graph->GetArena()) HX86FPNeg(
neg->GetType(),
neg->InputAt(0),
- base_,
+ method_address,
neg->GetDexPc());
block->ReplaceAndRemoveInstructionWith(neg, x86_fp_neg);
}
@@ -151,35 +151,44 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
}
// We need to replace the HPackedSwitch with a HX86PackedSwitch in order to
// address the constant area.
- InitializePCRelativeBasePointer();
+ HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(switch_insn);
HGraph* graph = GetGraph();
HBasicBlock* block = switch_insn->GetBlock();
HX86PackedSwitch* x86_switch = new (graph->GetArena()) HX86PackedSwitch(
switch_insn->GetStartValue(),
switch_insn->GetNumEntries(),
switch_insn->InputAt(0),
- base_,
+ method_address,
switch_insn->GetDexPc());
block->ReplaceAndRemoveInstructionWith(switch_insn, x86_switch);
}
- void InitializePCRelativeBasePointer() {
- // Ensure we only initialize the pointer once.
- if (base_ != nullptr) {
- return;
+ HX86ComputeBaseMethodAddress* GetPCRelativeBasePointer(HInstruction* cursor) {
+ bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
+ if (!has_irreducible_loops) {
+ // Ensure we only initialize the pointer once.
+ if (base_ != nullptr) {
+ return base_;
+ }
}
// Insert the base at the start of the entry block, move it to a better
// position later in MoveBaseIfNeeded().
- base_ = new (GetGraph()->GetArena()) HX86ComputeBaseMethodAddress();
- HBasicBlock* entry_block = GetGraph()->GetEntryBlock();
- entry_block->InsertInstructionBefore(base_, entry_block->GetFirstInstruction());
- DCHECK(base_ != nullptr);
+ HX86ComputeBaseMethodAddress* method_address =
+ new (GetGraph()->GetArena()) HX86ComputeBaseMethodAddress();
+ if (has_irreducible_loops) {
+ cursor->GetBlock()->InsertInstructionBefore(method_address, cursor);
+ } else {
+ HBasicBlock* entry_block = GetGraph()->GetEntryBlock();
+ entry_block->InsertInstructionBefore(method_address, entry_block->GetFirstInstruction());
+ base_ = method_address;
+ }
+ return method_address;
}
void ReplaceInput(HInstruction* insn, HConstant* value, int input_index, bool materialize) {
- InitializePCRelativeBasePointer();
+ HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(insn);
HX86LoadFromConstantTable* load_constant =
- new (GetGraph()->GetArena()) HX86LoadFromConstantTable(base_, value);
+ new (GetGraph()->GetArena()) HX86LoadFromConstantTable(method_address, value);
if (!materialize) {
load_constant->MarkEmittedAtUseSite();
}
@@ -204,9 +213,9 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
if (invoke_static_or_direct != nullptr &&
invoke_static_or_direct->HasPcRelativeDexCache() &&
!IsCallFreeIntrinsic<IntrinsicLocationsBuilderX86>(invoke, codegen_)) {
- InitializePCRelativeBasePointer();
- // Add the extra parameter base_.
- invoke_static_or_direct->AddSpecialInput(base_);
+ HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(invoke);
+ // Add the extra parameter.
+ invoke_static_or_direct->AddSpecialInput(method_address);
base_added = true;
}
@@ -231,8 +240,8 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
if (!base_added) {
DCHECK(invoke_static_or_direct != nullptr);
DCHECK(!invoke_static_or_direct->HasCurrentMethodInput());
- InitializePCRelativeBasePointer();
- invoke_static_or_direct->AddSpecialInput(base_);
+ HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(invoke);
+ invoke_static_or_direct->AddSpecialInput(method_address);
}
break;
default:
@@ -243,16 +252,12 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
CodeGeneratorX86* codegen_;
// The generated HX86ComputeBaseMethodAddress in the entry block needed as an
- // input to the HX86LoadFromConstantTable instructions.
+ // input to the HX86LoadFromConstantTable instructions. Only set for
+ // graphs with reducible loops.
HX86ComputeBaseMethodAddress* base_;
};
void PcRelativeFixups::Run() {
- if (graph_->HasIrreducibleLoops()) {
- // Do not run this optimization, as irreducible loops do not work with an instruction
- // that can be live-in at the irreducible loop header.
- return;
- }
PCRelativeHandlerVisitor visitor(graph_, codegen_);
visitor.VisitInsertionOrder();
visitor.MoveBaseIfNeeded();
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
new file mode 100644
index 0000000000..cf4c99ec6d
--- /dev/null
+++ b/dexoptanalyzer/Android.bp
@@ -0,0 +1,68 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "dexoptanalyzer-defaults",
+ host_supported: true,
+ defaults: ["art_defaults"],
+ srcs: [
+ "dexoptanalyzer.cc",
+ ],
+
+ target: {
+ android: {
+ compile_multilib: "prefer32",
+ },
+ },
+
+ include_dirs: [
+ "art/cmdline",
+ ],
+
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+art_cc_binary {
+ name: "dexoptanalyzer",
+ defaults: ["dexoptanalyzer-defaults"],
+ shared_libs: [
+ "libart",
+ ],
+}
+
+art_cc_binary {
+ name: "dexoptanalyzerd",
+ defaults: [
+ "dexoptanalyzer-defaults",
+ "art_debug_defaults",
+ ],
+ shared_libs: [
+ "libartd",
+ ],
+}
+
+art_cc_test {
+ name: "art_dexoptanalyzer_tests",
+ defaults: [
+ "art_gtest_defaults",
+ ],
+ shared_libs: [
+ "libbacktrace"
+ ],
+ srcs: ["dexoptanalyzer_test.cc"],
+}
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
new file mode 100644
index 0000000000..965e4073ea
--- /dev/null
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+#include "compiler_filter.h"
+#include "dex_file.h"
+#include "noop_compiler_callbacks.h"
+#include "oat_file_assistant.h"
+#include "os.h"
+#include "runtime.h"
+#include "thread-inl.h"
+#include "utils.h"
+
+namespace art {
+
+// See OatFileAssistant docs for the meaning of the valid return codes.
+enum ReturnCodes {
+ kNoDexOptNeeded = 0,
+ kDex2OatFromScratch = 1,
+ kDex2OatForBootImageOat = 2,
+ kDex2OatForFilterOat = 3,
+ kDex2OatForRelocationOat = 4,
+ kDex2OatForBootImageOdex = 5,
+ kDex2OatForFilterOdex = 6,
+ kDex2OatForRelocationOdex = 7,
+
+ kErrorInvalidArguments = 101,
+ kErrorCannotCreateRuntime = 102,
+ kErrorUnknownDexOptNeeded = 103
+};
+
+static int original_argc;
+static char** original_argv;
+
+static std::string CommandLine() {
+ std::vector<std::string> command;
+ for (int i = 0; i < original_argc; ++i) {
+ command.push_back(original_argv[i]);
+ }
+ return android::base::Join(command, ' ');
+}
+
+static void UsageErrorV(const char* fmt, va_list ap) {
+ std::string error;
+ android::base::StringAppendV(&error, fmt, ap);
+ LOG(ERROR) << error;
+}
+
+static void UsageError(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ UsageErrorV(fmt, ap);
+ va_end(ap);
+}
+
+NO_RETURN static void Usage(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ UsageErrorV(fmt, ap);
+ va_end(ap);
+
+ UsageError("Command: %s", CommandLine().c_str());
+ UsageError(" Performs a dexopt analysis on the given dex file and returns whether or not");
+ UsageError(" the dex file needs to be dexopted.");
+ UsageError("Usage: dexoptanalyzer [options]...");
+ UsageError("");
+ UsageError(" --dex-file=<filename>: the dex file which should be analyzed.");
+ UsageError("");
+ UsageError(" --isa=<string>: the instruction set for which the analysis should be performed.");
+ UsageError("");
+ UsageError(" --compiler-filter=<string>: the target compiler filter to be used as reference");
+ UsageError(" when deciding if the dex file needs to be optimized.");
+ UsageError("");
+ UsageError(" --assume-profile-changed: assumes the profile information has changed");
+ UsageError(" when deciding if the dex file needs to be optimized.");
+ UsageError("");
+ UsageError(" --image=<filename>: optional, the image to be used to decide if the associated");
+ UsageError(" oat file is up to date. Defaults to $ANDROID_ROOT/framework/boot.art.");
+ UsageError(" Example: --image=/system/framework/boot.art");
+ UsageError("");
+ UsageError(" --android-data=<directory>: optional, the directory which should be used as");
+ UsageError(" android-data. By default ANDROID_DATA env variable is used.");
+ UsageError("");
+ UsageError("Return code:");
+ UsageError(" To make it easier to integrate with the internal tools this command will make");
+ UsageError(" available its result (dexoptNeeded) as the exit/return code. i.e. it will not");
+ UsageError(" return 0 for success and a non zero values for errors as the conventional");
+ UsageError(" commands. The following return codes are possible:");
+ UsageError(" kNoDexOptNeeded = 0");
+ UsageError(" kDex2OatFromScratch = 1");
+ UsageError(" kDex2OatForBootImageOat = 2");
+ UsageError(" kDex2OatForFilterOat = 3");
+ UsageError(" kDex2OatForRelocationOat = 4");
+ UsageError(" kDex2OatForBootImageOdex = 5");
+ UsageError(" kDex2OatForFilterOdex = 6");
+ UsageError(" kDex2OatForRelocationOdex = 7");
+
+ UsageError(" kErrorInvalidArguments = 101");
+ UsageError(" kErrorCannotCreateRuntime = 102");
+ UsageError(" kErrorUnknownDexOptNeeded = 103");
+ UsageError("");
+
+ exit(kErrorInvalidArguments);
+}
+
+class DexoptAnalyzer FINAL {
+ public:
+ DexoptAnalyzer() : assume_profile_changed_(false) {}
+
+ void ParseArgs(int argc, char **argv) {
+ original_argc = argc;
+ original_argv = argv;
+
+ InitLogging(argv, Runtime::Aborter);
+ // Skip over the command name.
+ argv++;
+ argc--;
+
+ if (argc == 0) {
+ Usage("No arguments specified");
+ }
+
+ for (int i = 0; i < argc; ++i) {
+ const StringPiece option(argv[i]);
+ if (option == "--assume-profile-changed") {
+ assume_profile_changed_ = true;
+ } else if (option.starts_with("--dex-file=")) {
+ dex_file_ = option.substr(strlen("--dex-file=")).ToString();
+ } else if (option.starts_with("--compiler-filter=")) {
+ std::string filter_str = option.substr(strlen("--compiler-filter=")).ToString();
+ if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &compiler_filter_)) {
+ Usage("Invalid compiler filter '%s'", option.data());
+ }
+ } else if (option.starts_with("--isa=")) {
+ std::string isa_str = option.substr(strlen("--isa=")).ToString();
+ isa_ = GetInstructionSetFromString(isa_str.c_str());
+ if (isa_ == kNone) {
+ Usage("Invalid isa '%s'", option.data());
+ }
+ } else if (option.starts_with("--image=")) {
+ image_ = option.substr(strlen("--image=")).ToString();
+ } else if (option.starts_with("--android-data=")) {
+ // Overwrite android-data if needed (oat file assistant relies on a valid directory to
+ // compute dalvik-cache folder). This is mostly used in tests.
+ std::string new_android_data = option.substr(strlen("--android-data=")).ToString();
+ setenv("ANDROID_DATA", new_android_data.c_str(), 1);
+ } else {
+ Usage("Unknown argument '%s'", option.data());
+ }
+ }
+
+ if (image_.empty()) {
+ // If we don't receive the image, try to use the default one.
+ // Tests may specify a different image (e.g. core image).
+ std::string error_msg;
+ image_ = GetDefaultBootImageLocation(&error_msg);
+
+ if (image_.empty()) {
+ LOG(ERROR) << error_msg;
+ Usage("--image unspecified and ANDROID_ROOT not set or image file does not exist.");
+ }
+ }
+ }
+
+ bool CreateRuntime() {
+ RuntimeOptions options;
+ // The image could be custom, so make sure we explicitly pass it.
+ std::string img = "-Ximage:" + image_;
+ options.push_back(std::make_pair(img.c_str(), nullptr));
+ // The instruction set of the image should match the instruction set we will test.
+ const void* isa_opt = reinterpret_cast<const void*>(GetInstructionSetString(isa_));
+ options.push_back(std::make_pair("imageinstructionset", isa_opt));
+ // Disable libsigchain. We don't don't need it to evaluate DexOptNeeded status.
+ options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
+ // Pretend we are a compiler so that we can re-use the same infrastructure to load a different
+ // ISA image and minimize the amount of things that get started.
+ NoopCompilerCallbacks callbacks;
+ options.push_back(std::make_pair("compilercallbacks", &callbacks));
+ // Make sure we don't attempt to relocate. The tool should only retrieve the DexOptNeeded
+ // status and not attempt to relocate the boot image.
+ options.push_back(std::make_pair("-Xnorelocate", nullptr));
+
+ if (!Runtime::Create(options, false)) {
+ LOG(ERROR) << "Unable to initialize runtime";
+ return false;
+ }
+ // Runtime::Create acquired the mutator_lock_ that is normally given away when we
+ // Runtime::Start. Give it away now.
+ Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+
+ return true;
+ }
+
+ int GetDexOptNeeded() {
+ // If the file does not exist there's nothing to do.
+ // This is a fast path to avoid creating the runtime (b/34385298).
+ if (!OS::FileExists(dex_file_.c_str())) {
+ return kNoDexOptNeeded;
+ }
+ if (!CreateRuntime()) {
+ return kErrorCannotCreateRuntime;
+ }
+ OatFileAssistant oat_file_assistant(dex_file_.c_str(), isa_, /*load_executable*/ false);
+ // Always treat elements of the bootclasspath as up-to-date.
+ // TODO(calin): this check should be in OatFileAssistant.
+ if (oat_file_assistant.IsInBootClassPath()) {
+ return kNoDexOptNeeded;
+ }
+ int dexoptNeeded = oat_file_assistant.GetDexOptNeeded(
+ compiler_filter_, assume_profile_changed_);
+
+ // Convert OatFileAssitant codes to dexoptanalyzer codes.
+ switch (dexoptNeeded) {
+ case OatFileAssistant::kNoDexOptNeeded: return kNoDexOptNeeded;
+ case OatFileAssistant::kDex2OatFromScratch: return kDex2OatFromScratch;
+ case OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOat;
+ case OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOat;
+ case OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOat;
+
+ case -OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOdex;
+ case -OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOdex;
+ case -OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOdex;
+ default:
+ LOG(ERROR) << "Unknown dexoptNeeded " << dexoptNeeded;
+ return kErrorUnknownDexOptNeeded;
+ }
+ }
+
+ private:
+ std::string dex_file_;
+ InstructionSet isa_;
+ CompilerFilter::Filter compiler_filter_;
+ bool assume_profile_changed_;
+ std::string image_;
+};
+
+static int dexoptAnalyze(int argc, char** argv) {
+ DexoptAnalyzer analyzer;
+
+ // Parse arguments. Argument mistakes will lead to exit(kErrorInvalidArguments) in UsageError.
+ analyzer.ParseArgs(argc, argv);
+ return analyzer.GetDexOptNeeded();
+}
+
+} // namespace art
+
+int main(int argc, char **argv) {
+ return art::dexoptAnalyze(argc, argv);
+}
diff --git a/dexoptanalyzer/dexoptanalyzer_test.cc b/dexoptanalyzer/dexoptanalyzer_test.cc
new file mode 100644
index 0000000000..57d3f1f68b
--- /dev/null
+++ b/dexoptanalyzer/dexoptanalyzer_test.cc
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "arch/instruction_set.h"
+#include "compiler_filter.h"
+#include "dexopt_test.h"
+
+namespace art {
+
+class DexoptAnalyzerTest : public DexoptTest {
+ protected:
+ std::string GetDexoptAnalyzerCmd() {
+ std::string file_path = GetTestAndroidRoot();
+ file_path += "/bin/dexoptanalyzer";
+ if (kIsDebugBuild) {
+ file_path += "d";
+ }
+ EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+ return file_path;
+ }
+
+ int Analyze(const std::string& dex_file,
+ CompilerFilter::Filter compiler_filter,
+ bool assume_profile_changed) {
+ std::string dexoptanalyzer_cmd = GetDexoptAnalyzerCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(dexoptanalyzer_cmd);
+ argv_str.push_back("--dex-file=" + dex_file);
+ argv_str.push_back("--isa=" + std::string(GetInstructionSetString(kRuntimeISA)));
+ argv_str.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(compiler_filter));
+ if (assume_profile_changed) {
+ argv_str.push_back("--assume-profile-changed");
+ }
+ argv_str.push_back("--image=" + GetImageLocation());
+ argv_str.push_back("--android-data=" + android_data_);
+
+ std::string error;
+ return ExecAndReturnCode(argv_str, &error);
+ }
+
+ int DexoptanalyzerToOatFileAssistant(int dexoptanalyzerResult) {
+ switch (dexoptanalyzerResult) {
+ case 0: return OatFileAssistant::kNoDexOptNeeded;
+ case 1: return OatFileAssistant::kDex2OatFromScratch;
+ case 2: return OatFileAssistant::kDex2OatForBootImage;
+ case 3: return OatFileAssistant::kDex2OatForFilter;
+ case 4: return OatFileAssistant::kDex2OatForRelocation;
+ case 5: return -OatFileAssistant::kDex2OatForBootImage;
+ case 6: return -OatFileAssistant::kDex2OatForFilter;
+ case 7: return -OatFileAssistant::kDex2OatForRelocation;
+ default: return dexoptanalyzerResult;
+ }
+ }
+
+ // Verify that the output of dexoptanalyzer for the given arguments is the same
+ // as the output of OatFileAssistant::GetDexOptNeeded.
+ void Verify(const std::string& dex_file,
+ CompilerFilter::Filter compiler_filter,
+ bool assume_profile_changed = false) {
+ int dexoptanalyzerResult = Analyze(dex_file, compiler_filter, assume_profile_changed);
+ dexoptanalyzerResult = DexoptanalyzerToOatFileAssistant(dexoptanalyzerResult);
+ OatFileAssistant oat_file_assistant(dex_file.c_str(), kRuntimeISA, /*load_executable*/ false);
+ int assistantResult = oat_file_assistant.GetDexOptNeeded(
+ compiler_filter, assume_profile_changed);
+ EXPECT_EQ(assistantResult, dexoptanalyzerResult);
+ }
+};
+
+// The tests below exercise the same test case from oat_file_assistant_test.cc.
+
+// Case: We have a DEX file, but no OAT file for it.
+TEST_F(DexoptAnalyzerTest, DexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+ Verify(dex_location, CompilerFilter::kSpeedProfile);
+}
+
+// Case: We have a DEX file and up-to-date OAT file for it.
+TEST_F(DexoptAnalyzerTest, OatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kEverything);
+}
+
+// Case: We have a DEX file and speed-profile OAT file for it.
+TEST_F(DexoptAnalyzerTest, ProfileOatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile);
+
+ Verify(dex_location, CompilerFilter::kSpeedProfile, false);
+ Verify(dex_location, CompilerFilter::kInterpretOnly, false);
+ Verify(dex_location, CompilerFilter::kSpeedProfile, true);
+ Verify(dex_location, CompilerFilter::kInterpretOnly, true);
+}
+
+// Case: We have a MultiDEX file and up-to-date OAT file for it.
+TEST_F(DexoptAnalyzerTest, MultiDexOatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ Verify(dex_location, CompilerFilter::kSpeed, false);
+}
+
+// Case: We have a MultiDEX file where the secondary dex file is out of date.
+TEST_F(DexoptAnalyzerTest, MultiDexSecondaryOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
+
+ // Compile code for GetMultiDexSrc1.
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
+ // is out of date.
+ Copy(GetMultiDexSrc2(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed, false);
+}
+
+
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// dex checksum.
+TEST_F(DexoptAnalyzerTest, OatDexOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
+
+ // We create a dex, generate an oat for it, then overwrite the dex with a
+ // different dex to make the oat out of date.
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+ Copy(GetDexSrc2(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// boot image.
+TEST_F(DexoptAnalyzerTest, OatImageOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kSpeed,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_alternate_image*/true);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
+// respect to the boot image.
+// It shouldn't matter that the OAT file is out of date, because it is
+// verify-at-runtime.
+TEST_F(DexoptAnalyzerTest, OatVerifyAtRuntimeImageOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kVerifyAtRuntime,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_alternate_image*/true);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+}
+
+// Case: We have a DEX file and an ODEX file, but no OAT file.
+TEST_F(DexoptAnalyzerTest, DexOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a stripped DEX file and a PIC ODEX file, but no OAT file.
+TEST_F(DexoptAnalyzerTest, StrippedDexOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/StrippedDexOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ // Strip the dex file
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a stripped DEX file, a PIC ODEX file, and an out-of-date OAT file.
+TEST_F(DexoptAnalyzerTest, StrippedDexOdexOat) {
+ std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar";
+ std::string odex_location = GetOdexDir() + "/StrippedDexOdexOat.odex";
+
+ // Create the oat file from a different dex file so it looks out of date.
+ Copy(GetDexSrc2(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ // Create the odex file
+ Copy(GetDexSrc1(), dex_location);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ // Strip the dex file.
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kEverything);
+}
+
+// Case: We have a stripped (or resource-only) DEX file, no ODEX file and no
+// OAT file. Expect: The status is kNoDexOptNeeded.
+TEST_F(DexoptAnalyzerTest, ResourceOnlyDex) {
+ std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
+
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+}
+
+// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
+// OAT files both have patch delta of 0.
+TEST_F(DexoptAnalyzerTest, OdexOatOverlap) {
+ std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
+ std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
+ std::string oat_location = GetOdexDir() + "/OdexOatOverlap.oat";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ // Create the oat file by copying the odex so they are located in the same
+ // place in memory.
+ Copy(odex_location, oat_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a DEX file and a PIC ODEX file, but no OAT file.
+TEST_F(DexoptAnalyzerTest, DexPicOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexPicOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kEverything);
+}
+
+// Case: We have a DEX file and a VerifyAtRuntime ODEX file, but no OAT file..
+TEST_F(DexoptAnalyzerTest, DexVerifyAtRuntimeOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerifyAtRuntime);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: Non-standard extension for dex file.
+TEST_F(DexoptAnalyzerTest, LongDexExtension) {
+ std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
+ Copy(GetDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: Very short, non-existent Dex location.
+TEST_F(DexoptAnalyzerTest, ShortDexLocation) {
+ std::string dex_location = "/xx";
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+} // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 92f1e18827..69901c13cd 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -529,6 +529,12 @@ class OatDumper {
}
}
+ {
+ os << "OAT FILE STATS:\n";
+ VariableIndentationOutputStream vios(&os);
+ stats_.Dump(vios);
+ }
+
os << std::flush;
return success;
}
@@ -574,6 +580,116 @@ class OatDumper {
return nullptr;
}
+ struct Stats {
+ enum ByteKind {
+ kByteKindCode,
+ kByteKindQuickMethodHeader,
+ kByteKindCodeInfoLocationCatalog,
+ kByteKindCodeInfoDexRegisterMap,
+ kByteKindCodeInfoInlineInfo,
+ kByteKindCodeInfoEncoding,
+ kByteKindCodeInfoOther,
+ kByteKindStackMapNativePc,
+ kByteKindStackMapDexPc,
+ kByteKindStackMapDexRegisterMap,
+ kByteKindStackMapInlineInfo,
+ kByteKindStackMapRegisterMask,
+ kByteKindStackMapMask,
+ kByteKindStackMapOther,
+ kByteKindCount,
+ kByteKindStackMapFirst = kByteKindCodeInfoOther,
+ kByteKindStackMapLast = kByteKindStackMapOther,
+ };
+ int64_t bits[kByteKindCount] = {};
+ // Since code has deduplication, seen tracks already seen pointers to avoid double counting
+ // deduplicated code and tables.
+ std::unordered_set<const void*> seen;
+
+ // Returns true if it was newly added.
+ bool AddBitsIfUnique(ByteKind kind, int64_t count, const void* address) {
+ if (seen.insert(address).second == true) {
+ // True means the address was not already in the set.
+ AddBits(kind, count);
+ return true;
+ }
+ return false;
+ }
+
+ void AddBits(ByteKind kind, int64_t count) {
+ bits[kind] += count;
+ }
+
+ void Dump(VariableIndentationOutputStream& os) {
+ const int64_t sum = std::accumulate(bits, bits + kByteKindCount, 0u);
+ os.Stream() << "Dumping cumulative use of " << sum / kBitsPerByte << " accounted bytes\n";
+ if (sum > 0) {
+ const int64_t stack_map_bits = std::accumulate(bits + kByteKindStackMapFirst,
+ bits + kByteKindStackMapLast + 1,
+ 0u);
+ Dump(os, "Code ", bits[kByteKindCode], sum);
+ Dump(os, "QuickMethodHeader ", bits[kByteKindQuickMethodHeader], sum);
+ Dump(os, "CodeInfoEncoding ", bits[kByteKindCodeInfoEncoding], sum);
+ Dump(os, "CodeInfoLocationCatalog ", bits[kByteKindCodeInfoLocationCatalog], sum);
+ Dump(os, "CodeInfoDexRegisterMap ", bits[kByteKindCodeInfoDexRegisterMap], sum);
+ Dump(os, "CodeInfoInlineInfo ", bits[kByteKindCodeInfoInlineInfo], sum);
+ Dump(os, "CodeInfoStackMap ", stack_map_bits, sum);
+ {
+ ScopedIndentation indent1(&os);
+ Dump(os,
+ "StackMapNativePc ",
+ bits[kByteKindStackMapNativePc],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapDexPcEncoding ",
+ bits[kByteKindStackMapDexPc],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapDexRegisterMap ",
+ bits[kByteKindStackMapDexRegisterMap],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapInlineInfo ",
+ bits[kByteKindStackMapInlineInfo],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapRegisterMaskEncoding ",
+ bits[kByteKindStackMapRegisterMask],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapMask ",
+ bits[kByteKindStackMapMask],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapOther ",
+ bits[kByteKindStackMapOther],
+ stack_map_bits,
+ "stack map");
+ }
+ }
+ os.Stream() << "\n" << std::flush;
+ }
+
+ private:
+ void Dump(VariableIndentationOutputStream& os,
+ const char* name,
+ int64_t size,
+ int64_t total,
+ const char* sum_of = "total") {
+ const double percent = (static_cast<double>(size) / static_cast<double>(total)) * 100;
+ os.Stream() << StringPrintf("%s = %8" PRId64 " (%2.0f%% of %s)\n",
+ name,
+ size / kBitsPerByte,
+ percent,
+ sum_of);
+ }
+ };
+
private:
void AddAllOffsets() {
// We don't know the length of the code for each method, but we need to know where to stop
@@ -1046,7 +1162,9 @@ class OatDumper {
vios->Stream() << "OatQuickMethodHeader ";
uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset();
const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
-
+ stats_.AddBitsIfUnique(Stats::kByteKindQuickMethodHeader,
+ sizeof(*method_header) * kBitsPerByte,
+ method_header);
if (options_.absolute_addresses_) {
vios->Stream() << StringPrintf("%p ", method_header);
}
@@ -1118,6 +1236,7 @@ class OatDumper {
const void* code = oat_method.GetQuickCode();
uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
uint64_t aligned_code_end = aligned_code_begin + code_size;
+ stats_.AddBitsIfUnique(Stats::kByteKindCode, code_size * kBitsPerByte, code);
if (options_.absolute_addresses_) {
vios->Stream() << StringPrintf("%p ", code);
@@ -1431,6 +1550,60 @@ class OatDumper {
} else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) {
// The optimizing compiler outputs its CodeInfo data in the vmap table.
StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_);
+ {
+ CodeInfoEncoding encoding(helper.GetEncoding());
+ StackMapEncoding stack_map_encoding(encoding.stack_map_encoding);
+ // helper.GetCodeInfo().GetStackMapAt(0, encoding).;
+ const size_t num_stack_maps = encoding.number_of_stack_maps;
+ std::vector<uint8_t> size_vector;
+ encoding.Compress(&size_vector);
+ if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding,
+ size_vector.size() * kBitsPerByte,
+ oat_method.GetVmapTable())) {
+ stats_.AddBits(
+ Stats::kByteKindStackMapNativePc,
+ stack_map_encoding.GetNativePcEncoding().BitSize() * num_stack_maps);
+ stats_.AddBits(
+ Stats::kByteKindStackMapDexPc,
+ stack_map_encoding.GetDexPcEncoding().BitSize() * num_stack_maps);
+ stats_.AddBits(
+ Stats::kByteKindStackMapDexRegisterMap,
+ stack_map_encoding.GetDexRegisterMapEncoding().BitSize() * num_stack_maps);
+ stats_.AddBits(
+ Stats::kByteKindStackMapInlineInfo,
+ stack_map_encoding.GetInlineInfoEncoding().BitSize() * num_stack_maps);
+ stats_.AddBits(
+ Stats::kByteKindStackMapRegisterMask,
+ stack_map_encoding.GetRegisterMaskEncoding().BitSize() * num_stack_maps);
+ const size_t stack_mask_bits = encoding.stack_map_size_in_bytes * kBitsPerByte -
+ stack_map_encoding.GetStackMaskBitOffset();
+ stats_.AddBits(
+ Stats::kByteKindStackMapMask,
+ stack_mask_bits * num_stack_maps);
+ const size_t stack_map_bits =
+ stack_map_encoding.GetStackMaskBitOffset() + stack_mask_bits;
+ stats_.AddBits(
+ Stats::kByteKindStackMapOther,
+ (encoding.stack_map_size_in_bytes * kBitsPerByte - stack_map_bits) * num_stack_maps);
+ const size_t stack_map_bytes = helper.GetCodeInfo().GetStackMapsSize(encoding);
+ const size_t location_catalog_bytes =
+ helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding);
+ stats_.AddBits(Stats::kByteKindCodeInfoLocationCatalog,
+ kBitsPerByte * location_catalog_bytes);
+ const size_t dex_register_bytes =
+ helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, code_item->registers_size_);
+ stats_.AddBits(
+ Stats::kByteKindCodeInfoDexRegisterMap,
+ kBitsPerByte * dex_register_bytes);
+ const size_t inline_info_bytes =
+ encoding.non_header_size -
+ stack_map_bytes -
+ location_catalog_bytes -
+ dex_register_bytes;
+ stats_.AddBits(Stats::kByteKindCodeInfoInlineInfo,
+ inline_info_bytes * kBitsPerByte);
+ }
+ }
const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
size_t offset = 0;
while (offset < code_size) {
@@ -1468,6 +1641,7 @@ class OatDumper {
const InstructionSet instruction_set_;
std::set<uintptr_t> offsets_;
Disassembler* disassembler_;
+ Stats stats_;
};
class ImageDumper {
@@ -2140,7 +2314,6 @@ class ImageDumper {
size_t managed_code_bytes;
size_t managed_code_bytes_ignoring_deduplication;
- size_t managed_to_native_code_bytes;
size_t native_to_managed_code_bytes;
size_t class_initializer_code_bytes;
size_t large_initializer_code_bytes;
@@ -2169,7 +2342,6 @@ class ImageDumper {
alignment_bytes(0),
managed_code_bytes(0),
managed_code_bytes_ignoring_deduplication(0),
- managed_to_native_code_bytes(0),
native_to_managed_code_bytes(0),
class_initializer_code_bytes(0),
large_initializer_code_bytes(0),
@@ -2367,7 +2539,6 @@ class ImageDumper {
os << StringPrintf("oat_file_bytes = %8zd\n"
"managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
- "managed_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
"native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
"class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
"large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
@@ -2375,8 +2546,6 @@ class ImageDumper {
oat_file_bytes,
managed_code_bytes,
PercentOfOatBytes(managed_code_bytes),
- managed_to_native_code_bytes,
- PercentOfOatBytes(managed_to_native_code_bytes),
native_to_managed_code_bytes,
PercentOfOatBytes(native_to_managed_code_bytes),
class_initializer_code_bytes,
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index e77d03bae7..ba57d1860c 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -102,6 +102,7 @@ class OatDumpTest : public CommonRuntimeTest {
// Code and dex code do not show up if list only.
expected_prefixes.push_back("DEX CODE:");
expected_prefixes.push_back("CODE:");
+ expected_prefixes.push_back("CodeInfoEncoding");
}
if (mode == kModeArt) {
exec_argv.push_back("--image=" + core_art_location_);
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 81f174e43b..7f985139bb 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -474,10 +474,14 @@ art_cc_library {
art_cc_library {
name: "libart-runtime-gtest",
defaults: ["libart-gtest-defaults"],
- srcs: ["common_runtime_test.cc"],
+ srcs: [
+ "common_runtime_test.cc",
+ "dexopt_test.cc"
+ ],
shared_libs: [
"libartd",
"libbase",
+ "libbacktrace"
],
}
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index b9f688de57..917db3e486 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -52,7 +52,7 @@ inline uint32_t ArtField::GetAccessFlags() {
}
inline MemberOffset ArtField::GetOffset() {
- DCHECK(GetDeclaringClass()->IsResolved() || GetDeclaringClass()->IsErroneous());
+ DCHECK(GetDeclaringClass()->IsResolved());
return MemberOffset(offset_);
}
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 15938c52be..a35c7ab701 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -109,8 +109,7 @@ inline uint32_t ArtMethod::GetAccessFlags() {
}
inline uint16_t ArtMethod::GetMethodIndex() {
- DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsResolved() ||
- GetDeclaringClass()->IsErroneous());
+ DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsResolved());
return method_index_;
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2e258beb48..02b26c6568 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <deque>
#include <iostream>
+#include <map>
#include <memory>
#include <queue>
#include <string>
@@ -1340,7 +1341,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
// The image space is not yet added to the heap, avoid read barriers.
ObjPtr<mirror::Class> klass = types[j].Read();
if (space->HasAddress(klass.Ptr())) {
- DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
auto it = new_class_set->Find(ClassTable::TableSlot(klass));
DCHECK(it != new_class_set->end());
DCHECK_EQ(it->Read(), klass);
@@ -1703,7 +1704,7 @@ bool ClassLinker::AddImageSpace(
for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
ObjPtr<mirror::Class> klass = types[j].Read();
if (klass != nullptr) {
- DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
}
}
} else {
@@ -2232,7 +2233,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
// For temporary classes we must wait for them to be retired.
if (init_done_ && klass->IsTemp()) {
CHECK(!klass->IsResolved());
- if (klass->IsErroneous()) {
+ if (klass->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
@@ -2240,10 +2241,10 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
Handle<mirror::Class> h_class(hs.NewHandle(klass));
ObjectLock<mirror::Class> lock(self, h_class);
// Loop and wait for the resolving thread to retire this class.
- while (!h_class->IsRetired() && !h_class->IsErroneous()) {
+ while (!h_class->IsRetired() && !h_class->IsErroneousUnresolved()) {
lock.WaitIgnoringInterrupts();
}
- if (h_class->IsErroneous()) {
+ if (h_class->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(h_class.Get());
return nullptr;
}
@@ -2258,7 +2259,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
static const size_t kNumYieldIterations = 1000;
// How long each sleep is in us.
static const size_t kSleepDurationUS = 1000; // 1 ms.
- while (!klass->IsResolved() && !klass->IsErroneous()) {
+ while (!klass->IsResolved() && !klass->IsErroneousUnresolved()) {
StackHandleScope<1> hs(self);
HandleWrapperObjPtr<mirror::Class> h_class(hs.NewHandleWrapper(&klass));
{
@@ -2269,7 +2270,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
// Check for circular dependencies between classes, the lock is required for SetStatus.
if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) {
ThrowClassCircularityError(h_class.Get());
- mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(h_class, mirror::Class::kStatusErrorUnresolved, self);
return nullptr;
}
}
@@ -2286,7 +2287,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
++index;
}
- if (klass->IsErroneous()) {
+ if (klass->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
@@ -2636,13 +2637,30 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
self->AssertPendingOOMException();
return nullptr;
}
- ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(dex_file, class_loader.Get());
+ // Get the real dex file. This will return the input if there aren't any callbacks or they do
+ // nothing.
+ DexFile const* new_dex_file = nullptr;
+ DexFile::ClassDef const* new_class_def = nullptr;
+ // TODO We should ideally figure out some way to move this after we get a lock on the klass so it
+ // will only be called once.
+ Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor,
+ klass,
+ class_loader,
+ dex_file,
+ dex_class_def,
+ &new_dex_file,
+ &new_class_def);
+ // Check to see if an exception happened during runtime callbacks. Return if so.
+ if (self->IsExceptionPending()) {
+ return nullptr;
+ }
+ ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get());
if (dex_cache == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
klass->SetDexCache(dex_cache);
- SetupClass(dex_file, dex_class_def, klass, class_loader.Get());
+ SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get());
// Mark the string class by setting its access flag.
if (UNLIKELY(!init_done_)) {
@@ -2668,23 +2686,23 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
// end up allocating unfree-able linear alloc resources and then lose the race condition. The
// other reason is that the field roots are only visited from the class table. So we need to be
// inserted before we allocate / fill in these fields.
- LoadClass(self, dex_file, dex_class_def, klass);
+ LoadClass(self, *new_dex_file, *new_class_def, klass);
if (self->IsExceptionPending()) {
VLOG(class_linker) << self->GetException()->Dump();
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
- if (!LoadSuperAndInterfaces(klass, dex_file)) {
+ if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
@@ -2703,13 +2721,13 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
self->AssertNoPendingException();
CHECK(h_new_class.Get() != nullptr) << descriptor;
- CHECK(h_new_class->IsResolved()) << descriptor;
+ CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor;
// Instrumentation may have updated entrypoints for all methods of all
// classes. However it could not update methods of this class while we
@@ -3804,7 +3822,7 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self,
}
// Need to grab the lock to change status.
ObjectLock<mirror::Class> super_lock(self, klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
@@ -3926,8 +3944,8 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status);
// If the oat file says the class had an error, re-run the verifier. That way we will get a
// precise error message. To ensure a rerun, test:
- // oat_file_class_status == mirror::Class::kStatusError => !preverified
- DCHECK(!(oat_file_class_status == mirror::Class::kStatusError) || !preverified);
+ // mirror::Class::IsErroneous(oat_file_class_status) => !preverified
+ DCHECK(!mirror::Class::IsErroneous(oat_file_class_status) || !preverified);
std::string error_msg;
verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
@@ -3985,7 +4003,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
<< " because: " << error_msg;
self->AssertNoPendingException();
ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
}
if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) {
// Class is verified so we don't need to do any access check on its methods.
@@ -4076,7 +4094,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file,
// at compile time).
return false;
}
- if (oat_file_class_status == mirror::Class::kStatusError) {
+ if (mirror::Class::IsErroneous(oat_file_class_status)) {
// Compile time verification failed with a hard error. This is caused by invalid instructions
// in the class. These errors are unrecoverable.
return false;
@@ -4235,7 +4253,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)));
if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
return nullptr;
}
}
@@ -4450,7 +4468,8 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
return false;
}
- CHECK(klass->IsResolved()) << klass->PrettyClass() << ": state=" << klass->GetStatus();
+ CHECK(klass->IsResolved() && !klass->IsErroneousResolved())
+ << klass->PrettyClass() << ": state=" << klass->GetStatus();
if (!klass->IsVerified()) {
VerifyClass(self, klass);
@@ -4485,7 +4504,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
// A separate thread could have moved us all the way to initialized. A "simple" example
// involves a subclass of the current class being initialized at the same time (which
// will implicitly initialize the superclass, if scheduled that way). b/28254258
- DCHECK_NE(mirror::Class::kStatusError, klass->GetStatus());
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
if (klass->IsInitialized()) {
return true;
}
@@ -4512,7 +4531,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
}
if (!ValidateSuperClassDescriptors(klass)) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
self->AllowThreadSuspension();
@@ -4548,7 +4567,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
<< (self->GetException() != nullptr ? self->GetException()->Dump() : "");
ObjectLock<mirror::Class> lock(self, klass);
// Initialization failed because the super-class is erroneous.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
}
@@ -4579,7 +4598,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
if (!iface_initialized) {
ObjectLock<mirror::Class> lock(self, klass);
// Initialization failed because one of our interfaces with default methods is erroneous.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
}
@@ -4652,7 +4671,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
if (self->IsExceptionPending()) {
WrapExceptionInInitializer(klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
success = false;
} else if (Runtime::Current()->IsTransactionAborted()) {
// The exception thrown when the transaction aborted has been caught and cleared
@@ -4661,7 +4680,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
<< mirror::Class::PrettyDescriptor(klass.Get())
<< " without exception while transaction was aborted: re-throw it now.";
Runtime::Current()->ThrowTransactionAbortError(self);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
success = false;
} else {
RuntimeStats* global_stats = Runtime::Current()->GetStats();
@@ -4745,7 +4764,7 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass,
// we were not using WaitIgnoringInterrupts), bail out.
if (self->IsExceptionPending()) {
WrapExceptionInInitializer(klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
// Spurious wakeup? Go back to waiting.
@@ -5156,7 +5175,7 @@ bool ClassLinker::LinkClass(Thread* self,
klass->SetIFieldsPtrUnchecked(nullptr);
if (UNLIKELY(h_new_class.Get() == nullptr)) {
self->AssertPendingOOMException();
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
return false;
}
@@ -7768,7 +7787,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
}
}
}
- DCHECK((resolved == nullptr) || resolved->IsResolved() || resolved->IsErroneous())
+ DCHECK((resolved == nullptr) || resolved->IsResolved())
<< resolved->PrettyDescriptor() << " " << resolved->GetStatus();
return resolved.Ptr();
}
@@ -8465,71 +8484,94 @@ void ClassLinker::CleanupClassLoaders() {
}
}
-std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_boot_classes) {
- ScopedTrace trace(__PRETTY_FUNCTION__);
- ScopedObjectAccess soa(Thread::Current());
- ScopedAssertNoThreadSuspension ants(__FUNCTION__);
- std::set<DexCacheResolvedClasses> ret;
- VLOG(class_linker) << "Collecting resolved classes";
- const uint64_t start_time = NanoTime();
- ReaderMutexLock mu(soa.Self(), *Locks::dex_lock_);
- // Loop through all the dex caches and inspect resolved classes.
- for (const ClassLinker::DexCacheData& data : GetDexCachesData()) {
- if (soa.Self()->IsJWeakCleared(data.weak_root)) {
- continue;
- }
- ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(data.weak_root);
- if (dex_cache == nullptr) {
- continue;
- }
- const DexFile* dex_file = dex_cache->GetDexFile();
- const std::string& location = dex_file->GetLocation();
- const size_t num_class_defs = dex_file->NumClassDefs();
- // Use the resolved types, this will miss array classes.
- const size_t num_types = dex_file->NumTypeIds();
- VLOG(class_linker) << "Collecting class profile for dex file " << location
- << " types=" << num_types << " class_defs=" << num_class_defs;
- DexCacheResolvedClasses resolved_classes(dex_file->GetLocation(),
- dex_file->GetBaseLocation(),
- dex_file->GetLocationChecksum());
- size_t num_resolved = 0;
- std::unordered_set<dex::TypeIndex> class_set;
- CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
- for (size_t i = 0; i < num_types; ++i) {
- ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(i));
- // Filter out null class loader since that is the boot class loader.
- if (klass == nullptr || (ignore_boot_classes && klass->GetClassLoader() == nullptr)) {
- continue;
- }
- ++num_resolved;
- DCHECK(!klass->IsProxyClass());
- if (!klass->IsResolved()) {
- DCHECK(klass->IsErroneous());
- continue;
+class GetResolvedClassesVisitor : public ClassVisitor {
+ public:
+ GetResolvedClassesVisitor(std::set<DexCacheResolvedClasses>* result, bool ignore_boot_classes)
+ : result_(result),
+ ignore_boot_classes_(ignore_boot_classes),
+ last_resolved_classes_(result->end()),
+ last_dex_file_(nullptr),
+ vlog_is_on_(VLOG_IS_ON(class_linker)),
+ extra_stats_(),
+ last_extra_stats_(extra_stats_.end()) { }
+
+ bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!klass->IsProxyClass() &&
+ !klass->IsArrayClass() &&
+ klass->IsResolved() &&
+ !klass->IsErroneousResolved() &&
+ (!ignore_boot_classes_ || klass->GetClassLoader() != nullptr)) {
+ const DexFile& dex_file = klass->GetDexFile();
+ if (&dex_file != last_dex_file_) {
+ last_dex_file_ = &dex_file;
+ DexCacheResolvedClasses resolved_classes(dex_file.GetLocation(),
+ dex_file.GetBaseLocation(),
+ dex_file.GetLocationChecksum());
+ last_resolved_classes_ = result_->find(resolved_classes);
+ if (last_resolved_classes_ == result_->end()) {
+ last_resolved_classes_ = result_->insert(resolved_classes).first;
+ }
}
- ObjPtr<mirror::DexCache> klass_dex_cache = klass->GetDexCache();
- if (klass_dex_cache == dex_cache) {
- DCHECK(klass->IsResolved());
- CHECK_LT(klass->GetDexClassDefIndex(), num_class_defs);
- class_set.insert(klass->GetDexTypeIndex());
+ bool added = last_resolved_classes_->AddClass(klass->GetDexTypeIndex());
+ if (UNLIKELY(vlog_is_on_) && added) {
+ const DexCacheResolvedClasses* resolved_classes = std::addressof(*last_resolved_classes_);
+ if (last_extra_stats_ == extra_stats_.end() ||
+ last_extra_stats_->first != resolved_classes) {
+ last_extra_stats_ = extra_stats_.find(resolved_classes);
+ if (last_extra_stats_ == extra_stats_.end()) {
+ last_extra_stats_ =
+ extra_stats_.emplace(resolved_classes, ExtraStats(dex_file.NumClassDefs())).first;
+ }
+ }
}
}
+ return true;
+ }
- if (!class_set.empty()) {
- auto it = ret.find(resolved_classes);
- if (it != ret.end()) {
- // Already have the key, union the class type indexes.
- it->AddClasses(class_set.begin(), class_set.end());
- } else {
- resolved_classes.AddClasses(class_set.begin(), class_set.end());
- ret.insert(resolved_classes);
+ void PrintStatistics() const {
+ if (vlog_is_on_) {
+ for (const DexCacheResolvedClasses& resolved_classes : *result_) {
+ auto it = extra_stats_.find(std::addressof(resolved_classes));
+ DCHECK(it != extra_stats_.end());
+ const ExtraStats& extra_stats = it->second;
+ LOG(INFO) << "Dex location " << resolved_classes.GetDexLocation()
+ << " has " << resolved_classes.GetClasses().size() << " / "
+ << extra_stats.number_of_class_defs_ << " resolved classes";
}
}
+ }
+
+ private:
+ struct ExtraStats {
+ explicit ExtraStats(uint32_t number_of_class_defs)
+ : number_of_class_defs_(number_of_class_defs) {}
+ uint32_t number_of_class_defs_;
+ };
+
+ std::set<DexCacheResolvedClasses>* result_;
+ bool ignore_boot_classes_;
+ std::set<DexCacheResolvedClasses>::iterator last_resolved_classes_;
+ const DexFile* last_dex_file_;
- VLOG(class_linker) << "Dex location " << location << " has " << num_resolved << " / "
- << num_class_defs << " resolved classes";
+ // Statistics.
+ bool vlog_is_on_;
+ std::map<const DexCacheResolvedClasses*, ExtraStats> extra_stats_;
+ std::map<const DexCacheResolvedClasses*, ExtraStats>::iterator last_extra_stats_;
+};
+
+std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_boot_classes) {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ ScopedObjectAccess soa(Thread::Current());
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ std::set<DexCacheResolvedClasses> ret;
+ VLOG(class_linker) << "Collecting resolved classes";
+ const uint64_t start_time = NanoTime();
+ GetResolvedClassesVisitor visitor(&ret, ignore_boot_classes);
+ VisitClasses(&visitor);
+ if (VLOG_IS_ON(class_linker)) {
+ visitor.PrintStatistics();
+ LOG(INFO) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time);
}
- VLOG(class_linker) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time);
return ret;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 8da979b36f..5042fb7609 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1199,6 +1199,23 @@ class ClassLoadCallback {
public:
virtual ~ClassLoadCallback() {}
+ // If set we will replace initial_class_def & initial_dex_file with the final versions. The
+ // callback author is responsible for ensuring these are allocated in such a way they can be
+ // cleaned up if another transformation occurs. Note that both must be set or null/unchanged on
+ // return.
+ // Note: the class may be temporary, in which case a following ClassPrepare event will be a
+ // different object. It is the listener's responsibility to handle this.
+ // Note: This callback is rarely useful so a default implementation has been given that does
+ // nothing.
+ virtual void ClassPreDefine(const char* descriptor ATTRIBUTE_UNUSED,
+ Handle<mirror::Class> klass ATTRIBUTE_UNUSED,
+ Handle<mirror::ClassLoader> class_loader ATTRIBUTE_UNUSED,
+ const DexFile& initial_dex_file ATTRIBUTE_UNUSED,
+ const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+ /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
+ /*out*/DexFile::ClassDef const** final_class_def ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {}
+
// A class has been loaded.
// Note: the class may be temporary, in which case a following ClassPrepare event will be a
// different object. It is the listener's responsibility to handle this.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 7b6c0dc510..e806e7d608 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -87,6 +87,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
EXPECT_FALSE(primitive->IsErroneous());
EXPECT_TRUE(primitive->IsLoaded());
EXPECT_TRUE(primitive->IsResolved());
+ EXPECT_FALSE(primitive->IsErroneousResolved());
EXPECT_TRUE(primitive->IsVerified());
EXPECT_TRUE(primitive->IsInitialized());
EXPECT_FALSE(primitive->IsArrayInstance());
@@ -125,6 +126,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
EXPECT_FALSE(JavaLangObject->IsErroneous());
EXPECT_TRUE(JavaLangObject->IsLoaded());
EXPECT_TRUE(JavaLangObject->IsResolved());
+ EXPECT_FALSE(JavaLangObject->IsErroneousResolved());
EXPECT_TRUE(JavaLangObject->IsVerified());
EXPECT_TRUE(JavaLangObject->IsInitialized());
EXPECT_FALSE(JavaLangObject->IsArrayInstance());
@@ -199,6 +201,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
EXPECT_FALSE(array->IsErroneous());
EXPECT_TRUE(array->IsLoaded());
EXPECT_TRUE(array->IsResolved());
+ EXPECT_FALSE(array->IsErroneousResolved());
EXPECT_TRUE(array->IsVerified());
EXPECT_TRUE(array->IsInitialized());
EXPECT_FALSE(array->IsArrayInstance());
@@ -270,6 +273,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
EXPECT_TRUE(klass->GetDexCache() != nullptr);
EXPECT_TRUE(klass->IsLoaded());
EXPECT_TRUE(klass->IsResolved());
+ EXPECT_FALSE(klass->IsErroneousResolved());
EXPECT_FALSE(klass->IsErroneous());
EXPECT_FALSE(klass->IsArrayClass());
EXPECT_TRUE(klass->GetComponentType() == nullptr);
@@ -743,6 +747,8 @@ struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> {
MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>(
false, "Ljava/lang/invoke/MethodHandle;") {
addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, art_field_or_method_), "artFieldOrMethod");
+ addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, cached_spread_invoker_),
+ "cachedSpreadInvoker");
addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, handle_kind_), "handleKind");
addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, nominal_type_), "nominalType");
addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, method_type_), "type");
@@ -857,6 +863,7 @@ TEST_F(ClassLinkerTest, FindClass) {
EXPECT_FALSE(MyClass->IsErroneous());
EXPECT_TRUE(MyClass->IsLoaded());
EXPECT_TRUE(MyClass->IsResolved());
+ EXPECT_FALSE(MyClass->IsErroneousResolved());
EXPECT_FALSE(MyClass->IsVerified());
EXPECT_FALSE(MyClass->IsInitialized());
EXPECT_FALSE(MyClass->IsArrayInstance());
@@ -941,6 +948,47 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) {
array_klass);
}
+TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("ErroneousInit"))));
+ AssertNonExistentClass("LErroneousInit;");
+ Handle<mirror::Class> klass =
+ hs.NewHandle(class_linker_->FindClass(soa.Self(), "LErroneousInit;", class_loader));
+ ASSERT_OBJ_PTR_NE(klass.Get(), ObjPtr<mirror::Class>(nullptr));
+ dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_;
+ Handle<mirror::DexCache> dex_cache = hs.NewHandle(klass->GetDexCache());
+ const DexFile& dex_file = klass->GetDexFile();
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+ klass.Get());
+ // Zero out the resolved type and make sure LookupResolvedType still finds it.
+ dex_cache->SetResolvedType(type_idx, nullptr);
+ EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+ klass.Get());
+ // Force initialization to turn the class erroneous.
+ bool initialized = class_linker_->EnsureInitialized(soa.Self(),
+ klass,
+ /* can_init_fields */ true,
+ /* can_init_parents */ true);
+ EXPECT_FALSE(initialized);
+ EXPECT_TRUE(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ // Check that the LookupResolvedType() can still find the resolved type.
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+ klass.Get());
+ // Zero out the resolved type and make sure LookupResolvedType() still finds it.
+ dex_cache->SetResolvedType(type_idx, nullptr);
+ EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+ klass.Get());
+}
+
TEST_F(ClassLinkerTest, LibCore) {
ScopedObjectAccess soa(Thread::Current());
ASSERT_TRUE(java_lang_dex_file_ != nullptr);
diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h
index f53ca4acb6..bebdf0dbfe 100644
--- a/runtime/dex_cache_resolved_classes.h
+++ b/runtime/dex_cache_resolved_classes.h
@@ -44,6 +44,10 @@ class DexCacheResolvedClasses {
return dex_location_.compare(other.dex_location_);
}
+ bool AddClass(dex::TypeIndex index) const {
+ return classes_.insert(index).second;
+ }
+
template <class InputIt>
void AddClasses(InputIt begin, InputIt end) const {
classes_.insert(begin, end);
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
new file mode 100644
index 0000000000..69c6151d9e
--- /dev/null
+++ b/runtime/dexopt_test.cc
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+
+#include <backtrace/BacktraceMap.h>
+#include <gtest/gtest.h>
+
+#include "common_runtime_test.h"
+#include "compiler_callbacks.h"
+#include "dex2oat_environment_test.h"
+#include "dexopt_test.h"
+#include "gc/space/image_space.h"
+#include "mem_map.h"
+
+namespace art {
+void DexoptTest::SetUp() {
+ ReserveImageSpace();
+ Dex2oatEnvironmentTest::SetUp();
+}
+
+void DexoptTest::PreRuntimeCreate() {
+ std::string error_msg;
+ ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg;
+ ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg;
+ UnreserveImageSpace();
+}
+
+void DexoptTest::PostRuntimeCreate() {
+ ReserveImageSpace();
+}
+
+void DexoptTest::GenerateOatForTest(const std::string& dex_location,
+ const std::string& oat_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_alternate_image) {
+ std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
+ std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
+
+ if (!relocate) {
+ // Temporarily redirect the dalvik cache so dex2oat doesn't find the
+ // relocated image file.
+ ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+ }
+
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + oat_location);
+ args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+ args.push_back("--runtime-arg");
+
+ // Use -Xnorelocate regardless of the relocate argument.
+ // We control relocation by redirecting the dalvik cache when needed
+ // rather than use this flag.
+ args.push_back("-Xnorelocate");
+
+ if (pic) {
+ args.push_back("--compile-pic");
+ }
+
+ std::string image_location = GetImageLocation();
+ if (with_alternate_image) {
+ args.push_back("--boot-image=" + GetImageLocation2());
+ }
+
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ if (!relocate) {
+ // Restore the dalvik cache if needed.
+ ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
+ }
+
+ // Verify the odex file was generated as expected.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+ EXPECT_EQ(pic, odex_file->IsPic());
+ EXPECT_EQ(filter, odex_file->GetCompilerFilter());
+
+ std::unique_ptr<ImageHeader> image_header(
+ gc::space::ImageSpace::ReadImageHeader(image_location.c_str(),
+ kRuntimeISA,
+ &error_msg));
+ ASSERT_TRUE(image_header != nullptr) << error_msg;
+ const OatHeader& oat_header = odex_file->GetOatHeader();
+ uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
+
+ if (CompilerFilter::DependsOnImageChecksum(filter)) {
+ if (with_alternate_image) {
+ EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+ } else {
+ EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+ }
+ }
+
+ if (!with_alternate_image) {
+ if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+ if (relocate) {
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ } else {
+ EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ }
+ }
+ }
+}
+
+void DexoptTest::GenerateOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/false,
+ /*with_alternate_image*/false);
+}
+
+void DexoptTest::GeneratePicOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/true,
+ /*with_alternate_image*/false);
+}
+
+void DexoptTest::GenerateOatForTest(const char* dex_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_alternate_image) {
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+ GenerateOatForTest(dex_location,
+ oat_location,
+ filter,
+ relocate,
+ pic,
+ with_alternate_image);
+}
+
+void DexoptTest::GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ filter,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_alternate_image*/false);
+}
+
+bool DexoptTest::PreRelocateImage(const std::string& image_location, std::string* error_msg) {
+ std::string image;
+ if (!GetCachedImageFile(image_location, &image, error_msg)) {
+ return false;
+ }
+
+ std::string patchoat = GetAndroidRoot();
+ patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat";
+
+ std::vector<std::string> argv;
+ argv.push_back(patchoat);
+ argv.push_back("--input-image-location=" + image_location);
+ argv.push_back("--output-image-file=" + image);
+ argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
+ argv.push_back("--base-offset-delta=0x00008000");
+ return Exec(argv, error_msg);
+}
+
+void DexoptTest::ReserveImageSpace() {
+ MemMap::Init();
+
+ // Ensure a chunk of memory is reserved for the image space.
+ // The reservation_end includes room for the main space that has to come
+ // right after the image in case of the GSS collector.
+ uintptr_t reservation_start = ART_BASE_ADDRESS;
+ uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB;
+
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
+ ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
+ for (BacktraceMap::const_iterator it = map->begin();
+ reservation_start < reservation_end && it != map->end(); ++it) {
+ ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end));
+ reservation_start = std::max(reservation_start, it->end);
+ }
+ ReserveImageSpaceChunk(reservation_start, reservation_end);
+}
+
+void DexoptTest::ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) {
+ if (start < end) {
+ std::string error_msg;
+ image_reservation_.push_back(std::unique_ptr<MemMap>(
+ MemMap::MapAnonymous("image reservation",
+ reinterpret_cast<uint8_t*>(start), end - start,
+ PROT_NONE, false, false, &error_msg)));
+ ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
+ LOG(INFO) << "Reserved space for image " <<
+ reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
+ reinterpret_cast<void*>(image_reservation_.back()->End());
+ }
+}
+
+void DexoptTest::UnreserveImageSpace() {
+ image_reservation_.clear();
+}
+
+} // namespace art
diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h
new file mode 100644
index 0000000000..5f0eafd8eb
--- /dev/null
+++ b/runtime/dexopt_test.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_DEXOPT_TEST_H_
+#define ART_RUNTIME_DEXOPT_TEST_H_
+
+#include <string>
+#include <vector>
+
+#include "dex2oat_environment_test.h"
+
+namespace art {
+
+class DexoptTest : public Dex2oatEnvironmentTest {
+ public:
+ virtual void SetUp() OVERRIDE;
+
+ virtual void PreRuntimeCreate();
+
+ virtual void PostRuntimeCreate() OVERRIDE;
+
+ // Generate an oat file for the purposes of test.
+ // The oat file will be generated for dex_location in the given oat_location
+ // with the following configuration:
+ // filter - controls the compilation filter
+ // pic - whether or not the code will be PIC
+ // relocate - if true, the oat file will be relocated with respect to the
+ // boot image. Otherwise the oat file will not be relocated.
+ // with_alternate_image - if true, the oat file will be generated with an
+ // image checksum different than the current image checksum.
+ void GenerateOatForTest(const std::string& dex_location,
+ const std::string& oat_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_alternate_image);
+
+ // Generate a non-PIC odex file for the purposes of test.
+ // The generated odex file will be un-relocated.
+ void GenerateOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter);
+
+ void GeneratePicOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter);
+
+ // Generate an oat file for the given dex location in its oat location (under
+ // the dalvik cache).
+ void GenerateOatForTest(const char* dex_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_alternate_image);
+
+ // Generate a standard oat file in the oat location.
+ void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter);
+
+ private:
+ // Pre-Relocate the image to a known non-zero offset so we don't have to
+ // deal with the runtime randomly relocating the image by 0 and messing up
+ // the expected results of the tests.
+ bool PreRelocateImage(const std::string& image_location, std::string* error_msg);
+
+ // Reserve memory around where the image will be loaded so other memory
+ // won't conflict when it comes time to load the image.
+ // This can be called with an already loaded image to reserve the space
+ // around it.
+ void ReserveImageSpace();
+
+ // Reserve a chunk of memory for the image space in the given range.
+ // Only has effect for chunks with a positive number of bytes.
+ void ReserveImageSpaceChunk(uintptr_t start, uintptr_t end);
+
+ // Unreserve any memory reserved by ReserveImageSpace. This should be called
+ // before the image is loaded.
+ void UnreserveImageSpace();
+
+ std::vector<std::unique_ptr<MemMap>> image_reservation_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_DEXOPT_TEST_H_
diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h
index 5ddb9fa27a..0471c969f9 100644
--- a/runtime/experimental_flags.h
+++ b/runtime/experimental_flags.h
@@ -26,8 +26,6 @@ struct ExperimentalFlags {
// The actual flag values.
enum {
kNone = 0x0000,
- kAgents = 0x0001, // 0b00000001
- kRuntimePlugins = 0x0002, // 0b00000010
kMethodHandles = 0x0004, // 0b00000100
};
@@ -67,14 +65,6 @@ struct ExperimentalFlags {
inline std::ostream& operator<<(std::ostream& stream, const ExperimentalFlags& e) {
bool started = false;
- if (e & ExperimentalFlags::kAgents) {
- stream << (started ? "|" : "") << "kAgents";
- started = true;
- }
- if (e & ExperimentalFlags::kRuntimePlugins) {
- stream << (started ? "|" : "") << "kRuntimePlugins";
- started = true;
- }
if (e & ExperimentalFlags::kMethodHandles) {
stream << (started ? "|" : "") << "kMethodHandles";
started = true;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 268cca0cfd..70449797c1 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3543,8 +3543,11 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,
collector::GcType gc_type = collector_ran->GetGcType();
const double multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for
// foreground.
- const uint64_t adjusted_min_free = static_cast<uint64_t>(min_free_ * multiplier);
- const uint64_t adjusted_max_free = static_cast<uint64_t>(max_free_ * multiplier);
+ // Ensure at least 2.5 MB to temporarily fix excessive GC caused by TLAB ergonomics.
+ const uint64_t adjusted_min_free = std::max(static_cast<uint64_t>(min_free_ * multiplier),
+ static_cast<uint64_t>(5 * MB / 2));
+ const uint64_t adjusted_max_free = std::max(static_cast<uint64_t>(max_free_ * multiplier),
+ static_cast<uint64_t>(5 * MB / 2));
if (gc_type != collector::kGcTypeSticky) {
// Grow the heap for non sticky GC.
ssize_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated;
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index fe6a6e9140..3d3ad593b3 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -1169,7 +1169,7 @@ void Hprof::DumpHeapObject(mirror::Object* obj) {
}
void Hprof::DumpHeapClass(mirror::Class* klass) {
- if (!klass->IsResolved() && !klass->IsErroneous()) {
+ if (!klass->IsResolved()) {
// Class is allocated but not yet resolved: we cannot access its fields or super class.
return;
}
diff --git a/runtime/image.cc b/runtime/image.cc
index 6d8889532f..54b099eb14 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '5', '\0' }; // ArtMethod update
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '6', '\0' }; // Erroneous resolved class.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 4ea1130947..bbd6d352d3 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -88,11 +88,11 @@ Instrumentation::Instrumentation()
}
void Instrumentation::InstallStubsForClass(mirror::Class* klass) {
- if (klass->IsErroneous()) {
- // We can't execute code in a erroneous class: do nothing.
- } else if (!klass->IsResolved()) {
+ if (!klass->IsResolved()) {
// We need the class to be resolved to install/uninstall stubs. Otherwise its methods
// could not be initialized or linked with regards to class inheritance.
+ } else if (klass->IsErroneousResolved()) {
+ // We can't execute code in a erroneous class: do nothing.
} else {
for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
InstallStubsForMethod(&method);
diff --git a/runtime/memory_region.cc b/runtime/memory_region.cc
index a5c70c3b26..5bf0f40eff 100644
--- a/runtime/memory_region.cc
+++ b/runtime/memory_region.cc
@@ -33,4 +33,36 @@ void MemoryRegion::CopyFrom(size_t offset, const MemoryRegion& from) const {
from.pointer(), from.size());
}
+void MemoryRegion::StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) {
+ DCHECK_LE(value, MaxInt<uint32_t>(length));
+ DCHECK_LE(length, BitSizeOf<uint32_t>());
+ DCHECK_LE(bit_offset + length, size_in_bits());
+ if (length == 0) {
+ return;
+ }
+ // Bits are stored in this order {7 6 5 4 3 2 1 0}.
+ // How many remaining bits in current byte is (bit_offset % kBitsPerByte) + 1.
+ uint8_t* out = ComputeInternalPointer<uint8_t>(bit_offset >> kBitsPerByteLog2);
+ auto orig_len = length;
+ auto orig_value = value;
+ uintptr_t bit_remainder = bit_offset % kBitsPerByte;
+ while (true) {
+ const uintptr_t remaining_bits = kBitsPerByte - bit_remainder;
+ if (length <= remaining_bits) {
+ // Length is smaller than all of remainder bits.
+ size_t mask = ((1 << length) - 1) << bit_remainder;
+ *out = (*out & ~mask) | (value << bit_remainder);
+ break;
+ }
+ // Copy remaining bits in current byte.
+ size_t value_mask = (1 << remaining_bits) - 1;
+ *out = (*out & ~(value_mask << bit_remainder)) | ((value & value_mask) << bit_remainder);
+ value >>= remaining_bits;
+ bit_remainder = 0;
+ length -= remaining_bits;
+ ++out;
+ }
+ DCHECK_EQ(LoadBits(bit_offset, orig_len), orig_value) << bit_offset << " " << orig_len;
+}
+
} // namespace art
diff --git a/runtime/memory_region.h b/runtime/memory_region.h
index fe3f917c76..f55dff7a50 100644
--- a/runtime/memory_region.h
+++ b/runtime/memory_region.h
@@ -124,11 +124,35 @@ class MemoryRegion FINAL : public ValueObject {
// The bit at the smallest offset is the least significant bit in the
// loaded value. `length` must not be larger than the number of bits
// contained in the return value (32).
- uint32_t LoadBits(uintptr_t bit_offset, size_t length) const {
- CHECK_LE(length, sizeof(uint32_t) * kBitsPerByte);
- uint32_t value = 0u;
+ ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const {
+ DCHECK_LE(length, BitSizeOf<uint32_t>());
+ DCHECK_LE(bit_offset + length, size_in_bits());
+ if (UNLIKELY(length == 0)) {
+ // Do not touch any memory if the range is empty.
+ return 0;
+ }
+ const uint8_t* address = start() + bit_offset / kBitsPerByte;
+ const uint32_t shift = bit_offset & (kBitsPerByte - 1);
+ // Load the value (reading only the strictly needed bytes).
+ const uint32_t load_bit_count = shift + length;
+ uint32_t value = address[0] >> shift;
+ if (load_bit_count > 8) {
+ value |= static_cast<uint32_t>(address[1]) << (8 - shift);
+ if (load_bit_count > 16) {
+ value |= static_cast<uint32_t>(address[2]) << (16 - shift);
+ if (load_bit_count > 24) {
+ value |= static_cast<uint32_t>(address[3]) << (24 - shift);
+ if (load_bit_count > 32) {
+ value |= static_cast<uint32_t>(address[4]) << (32 - shift);
+ }
+ }
+ }
+ }
+ // Clear unwanted most significant bits.
+ uint32_t clear_bit_count = BitSizeOf(value) - length;
+ value = (value << clear_bit_count) >> clear_bit_count;
for (size_t i = 0; i < length; ++i) {
- value |= LoadBit(bit_offset + i) << i;
+ DCHECK_EQ((value >> i) & 1, LoadBit(bit_offset + i));
}
return value;
}
@@ -137,13 +161,7 @@ class MemoryRegion FINAL : public ValueObject {
// `bit_offset`. The bit at the smallest offset is the least significant
// bit of the stored `value`. `value` must not be larger than `length`
// bits.
- void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) {
- CHECK_LE(value, MaxInt<uint32_t>(length));
- for (size_t i = 0; i < length; ++i) {
- bool ith_bit = value & (1 << i);
- StoreBit(bit_offset + i, ith_bit);
- }
- }
+ void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length);
void CopyFrom(size_t offset, const MemoryRegion& from) const;
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index a5db0c0a8a..f56226bd98 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -207,6 +207,19 @@ inline void PrimitiveArray<T>::VisitRoots(RootVisitor* visitor) {
}
template<typename T>
+inline PrimitiveArray<T>* PrimitiveArray<T>::AllocateAndFill(Thread* self,
+ const T* data,
+ size_t length) {
+ StackHandleScope<1> hs(self);
+ Handle<PrimitiveArray<T>> arr(hs.NewHandle(PrimitiveArray<T>::Alloc(self, length)));
+ if (!arr.IsNull()) {
+ // Copy it in. Just skip if it's null
+ memcpy(arr->GetData(), data, sizeof(T) * length);
+ }
+ return arr.Get();
+}
+
+template<typename T>
inline PrimitiveArray<T>* PrimitiveArray<T>::Alloc(Thread* self, size_t length) {
Array* raw_array = Array::Alloc<true>(self,
GetArrayClass(),
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 19d300e1f4..16cf30f1e2 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -119,6 +119,10 @@ class MANAGED PrimitiveArray : public Array {
static PrimitiveArray<T>* Alloc(Thread* self, size_t length)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ static PrimitiveArray<T>* AllocateAndFill(Thread* self, const T* data, size_t length)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+
const T* GetData() const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return reinterpret_cast<const T*>(GetRawData(sizeof(T), 0));
}
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 9964b73c98..f08d4daf95 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -115,7 +115,9 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized();
if (LIKELY(class_linker_initialized)) {
- if (UNLIKELY(new_status <= old_status && new_status != kStatusError &&
+ if (UNLIKELY(new_status <= old_status &&
+ new_status != kStatusErrorUnresolved &&
+ new_status != kStatusErrorResolved &&
new_status != kStatusRetired)) {
LOG(FATAL) << "Unexpected change back of class status for " << h_this->PrettyClass()
<< " " << old_status << " -> " << new_status;
@@ -127,10 +129,12 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
<< h_this->PrettyClass() << " " << old_status << " -> " << new_status;
}
}
- if (UNLIKELY(new_status == kStatusError)) {
- CHECK_NE(h_this->GetStatus(), kStatusError)
+ if (UNLIKELY(IsErroneous(new_status))) {
+ CHECK(!h_this->IsErroneous())
<< "Attempt to set as erroneous an already erroneous class "
- << h_this->PrettyClass();
+ << h_this->PrettyClass()
+ << " old_status: " << old_status << " new_status: " << new_status;
+ CHECK_EQ(new_status == kStatusErrorResolved, old_status >= kStatusResolved);
if (VLOG_IS_ON(class_linker)) {
LOG(ERROR) << "Setting " << h_this->PrettyDescriptor() << " to erroneous.";
if (self->IsExceptionPending()) {
@@ -177,7 +181,7 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
// Class is a temporary one, ensure that waiters for resolution get notified of retirement
// so that they can grab the new version of the class from the class linker's table.
CHECK_LT(new_status, kStatusResolved) << h_this->PrettyDescriptor();
- if (new_status == kStatusRetired || new_status == kStatusError) {
+ if (new_status == kStatusRetired || new_status == kStatusErrorUnresolved) {
h_this->NotifyAll(self);
}
} else {
@@ -305,7 +309,7 @@ void Class::DumpClass(std::ostream& os, int flags) {
}
if (h_this->NumStaticFields() > 0) {
os << " static fields (" << h_this->NumStaticFields() << " entries):\n";
- if (h_this->IsResolved() || h_this->IsErroneous()) {
+ if (h_this->IsResolved()) {
for (size_t i = 0; i < h_this->NumStaticFields(); ++i) {
os << StringPrintf(" %2zd: %s\n", i,
ArtField::PrettyField(h_this->GetStaticField(i)).c_str());
@@ -316,7 +320,7 @@ void Class::DumpClass(std::ostream& os, int flags) {
}
if (h_this->NumInstanceFields() > 0) {
os << " instance fields (" << h_this->NumInstanceFields() << " entries):\n";
- if (h_this->IsResolved() || h_this->IsErroneous()) {
+ if (h_this->IsResolved()) {
for (size_t i = 0; i < h_this->NumInstanceFields(); ++i) {
os << StringPrintf(" %2zd: %s\n", i,
ArtField::PrettyField(h_this->GetInstanceField(i)).c_str());
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index fb2792a316..7f6aa12fdb 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -84,6 +84,13 @@ class MANAGED Class FINAL : public Object {
// will be gc'ed once all refs to the class point to the newly
// cloned version.
//
+ // kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need
+ // to distinguish between classes that have been resolved and classes that
+ // have not. This is important because the const-class instruction needs to
+ // return a previously resolved class even if its subsequent initialization
+ // failed. We also need this to decide whether to wrap a previous
+ // initialization failure in ClassDefNotFound error or not.
+ //
// kStatusNotReady: If a Class cannot be found in the class table by
// FindClass, it allocates an new one with AllocClass in the
// kStatusNotReady and calls LoadClass. Note if it does find a
@@ -119,8 +126,9 @@ class MANAGED Class FINAL : public Object {
//
// TODO: Explain the other states
enum Status {
- kStatusRetired = -2, // Retired, should not be used. Use the newly cloned one instead.
- kStatusError = -1,
+ kStatusRetired = -3, // Retired, should not be used. Use the newly cloned one instead.
+ kStatusErrorResolved = -2,
+ kStatusErrorUnresolved = -1,
kStatusNotReady = 0,
kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
kStatusLoaded = 2, // DEX idx values resolved.
@@ -158,8 +166,25 @@ class MANAGED Class FINAL : public Object {
// Returns true if the class has failed to link.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsErroneousUnresolved() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetStatus<kVerifyFlags>() == kStatusErrorUnresolved;
+ }
+
+ // Returns true if the class has failed to initialize.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsErroneousResolved() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetStatus<kVerifyFlags>() == kStatusErrorResolved;
+ }
+
+ // Returns true if the class status indicets that the class has failed to link or initialize.
+ static bool IsErroneous(Status status) {
+ return status == kStatusErrorUnresolved || status == kStatusErrorResolved;
+ }
+
+ // Returns true if the class has failed to link or initialize.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsErroneous() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetStatus<kVerifyFlags>() == kStatusError;
+ return IsErroneous(GetStatus<kVerifyFlags>());
}
// Returns true if the class has been loaded.
@@ -177,7 +202,8 @@ class MANAGED Class FINAL : public Object {
// Returns true if the class has been linked.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsResolved() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetStatus<kVerifyFlags>() >= kStatusResolved;
+ Status status = GetStatus<kVerifyFlags>();
+ return status >= kStatusResolved || status == kStatusErrorResolved;
}
// Returns true if the class was compile-time verified.
@@ -345,7 +371,7 @@ class MANAGED Class FINAL : public Object {
// be replaced with a class with the right size for embedded imt/vtable.
bool IsTemp() REQUIRES_SHARED(Locks::mutator_lock_) {
Status s = GetStatus();
- return s < Status::kStatusResolving && ShouldHaveEmbeddedVTable();
+ return s < Status::kStatusResolving && s != kStatusErrorResolved && ShouldHaveEmbeddedVTable();
}
String* GetName() REQUIRES_SHARED(Locks::mutator_lock_); // Returns the cached name.
@@ -1017,7 +1043,7 @@ class MANAGED Class FINAL : public Object {
// Returns the number of instance fields containing reference types. Does not count fields in any
// super classes.
uint32_t NumReferenceInstanceFields() REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(IsResolved() || IsErroneous());
+ DCHECK(IsResolved());
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_));
}
@@ -1045,7 +1071,7 @@ class MANAGED Class FINAL : public Object {
// Returns the number of static fields containing reference types.
uint32_t NumReferenceStaticFields() REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(IsResolved() || IsErroneous());
+ DCHECK(IsResolved());
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_));
}
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index abe999a5ac..2f26a22ca8 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -84,10 +84,12 @@ class MANAGED MethodHandle : public Object {
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
private:
+ // NOTE: cached_spread_invoker_ isn't used by the runtime.
+ HeapReference<mirror::MethodHandle> cached_spread_invoker_;
HeapReference<mirror::MethodType> nominal_type_;
HeapReference<mirror::MethodType> method_type_;
- uint64_t art_field_or_method_;
uint32_t handle_kind_;
+ uint64_t art_field_or_method_;
private:
static MemberOffset NominalTypeOffset() {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 284d2d10c5..a8fa7db688 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -81,14 +81,12 @@ static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoa
if (c != nullptr && c->IsErroneous()) {
cl->ThrowEarlierClassFailure(c.Ptr());
Thread* self = soa.Self();
- ObjPtr<mirror::Class> eiie_class =
- self->DecodeJObject(WellKnownClasses::java_lang_ExceptionInInitializerError)->AsClass();
ObjPtr<mirror::Class> iae_class =
self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
ObjPtr<mirror::Class> ncdfe_class =
self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
ObjPtr<mirror::Class> exception = self->GetException()->GetClass();
- if (exception == eiie_class || exception == iae_class || exception == ncdfe_class) {
+ if (exception == iae_class || exception == ncdfe_class) {
self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
c->PrettyDescriptor().c_str());
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 62d99fb51c..111755e7a1 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -201,8 +201,12 @@ class OatFile {
// A representation of an invalid OatClass, used when an OatClass can't be found.
// See FindOatClass().
static OatClass Invalid() {
- return OatClass(nullptr, mirror::Class::kStatusError, kOatClassNoneCompiled, 0, nullptr,
- nullptr);
+ return OatClass(/* oat_file */ nullptr,
+ mirror::Class::kStatusErrorUnresolved,
+ kOatClassNoneCompiled,
+ /* bitmap_size */ 0,
+ /* bitmap_pointer */ nullptr,
+ /* methods_pointer */ nullptr);
}
private:
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 84eacde8b7..577200847a 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -14,23 +14,16 @@
* limitations under the License.
*/
-#include <algorithm>
-#include <fstream>
#include <string>
#include <vector>
#include <sys/param.h>
#include "android-base/strings.h"
-#include <backtrace/BacktraceMap.h>
#include <gtest/gtest.h>
#include "art_field-inl.h"
#include "class_linker-inl.h"
-#include "common_runtime_test.h"
-#include "compiler_callbacks.h"
-#include "dex2oat_environment_test.h"
-#include "gc/space/image_space.h"
-#include "mem_map.h"
+#include "dexopt_test.h"
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "os.h"
@@ -40,242 +33,17 @@
namespace art {
-class OatFileAssistantTest : public Dex2oatEnvironmentTest {
- public:
- virtual void SetUp() OVERRIDE {
- ReserveImageSpace();
- Dex2oatEnvironmentTest::SetUp();
- }
-
- // Pre-Relocate the image to a known non-zero offset so we don't have to
- // deal with the runtime randomly relocating the image by 0 and messing up
- // the expected results of the tests.
- bool PreRelocateImage(const std::string& image_location, std::string* error_msg) {
- std::string image;
- if (!GetCachedImageFile(image_location, &image, error_msg)) {
- return false;
- }
-
- std::string patchoat = GetAndroidRoot();
- patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat";
-
- std::vector<std::string> argv;
- argv.push_back(patchoat);
- argv.push_back("--input-image-location=" + image_location);
- argv.push_back("--output-image-file=" + image);
- argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
- argv.push_back("--base-offset-delta=0x00008000");
- return Exec(argv, error_msg);
- }
-
- virtual void PreRuntimeCreate() {
- std::string error_msg;
- ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg;
- ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg;
- UnreserveImageSpace();
- }
-
- virtual void PostRuntimeCreate() OVERRIDE {
- ReserveImageSpace();
- }
+class OatFileAssistantTest : public DexoptTest {};
- // Generate an oat file for the purposes of test.
- void GenerateOatForTest(const std::string& dex_location,
- const std::string& oat_location,
- CompilerFilter::Filter filter,
- bool relocate,
- bool pic,
- bool with_alternate_image) {
- std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
- std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
-
- if (!relocate) {
- // Temporarily redirect the dalvik cache so dex2oat doesn't find the
- // relocated image file.
- ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
- }
-
- std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location);
- args.push_back("--oat-file=" + oat_location);
- args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- args.push_back("--runtime-arg");
-
- // Use -Xnorelocate regardless of the relocate argument.
- // We control relocation by redirecting the dalvik cache when needed
- // rather than use this flag.
- args.push_back("-Xnorelocate");
-
- if (pic) {
- args.push_back("--compile-pic");
- }
-
- std::string image_location = GetImageLocation();
- if (with_alternate_image) {
- args.push_back("--boot-image=" + GetImageLocation2());
- }
-
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
- if (!relocate) {
- // Restore the dalvik cache if needed.
- ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
- }
-
- // Verify the odex file was generated as expected.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
- oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- dex_location.c_str(),
- &error_msg));
- ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
- EXPECT_EQ(pic, odex_file->IsPic());
- EXPECT_EQ(filter, odex_file->GetCompilerFilter());
-
- std::unique_ptr<ImageHeader> image_header(
- gc::space::ImageSpace::ReadImageHeader(image_location.c_str(),
- kRuntimeISA,
- &error_msg));
- ASSERT_TRUE(image_header != nullptr) << error_msg;
- const OatHeader& oat_header = odex_file->GetOatHeader();
- uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
-
- if (CompilerFilter::DependsOnImageChecksum(filter)) {
- if (with_alternate_image) {
- EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
- } else {
- EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
- }
- }
-
- if (!with_alternate_image) {
- if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
- if (relocate) {
- EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
- } else {
- EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
- }
- }
- }
- }
-
- // Generate a non-PIC odex file for the purposes of test.
- // The generated odex file will be un-relocated.
- void GenerateOdexForTest(const std::string& dex_location,
- const std::string& odex_location,
- CompilerFilter::Filter filter) {
- GenerateOatForTest(dex_location,
- odex_location,
- filter,
- /*relocate*/false,
- /*pic*/false,
- /*with_alternate_image*/false);
- }
-
- void GeneratePicOdexForTest(const std::string& dex_location,
- const std::string& odex_location,
- CompilerFilter::Filter filter) {
- GenerateOatForTest(dex_location,
- odex_location,
- filter,
- /*relocate*/false,
- /*pic*/true,
- /*with_alternate_image*/false);
- }
-
- // Generate an oat file in the oat location.
- void GenerateOatForTest(const char* dex_location,
- CompilerFilter::Filter filter,
- bool relocate,
- bool pic,
- bool with_alternate_image) {
- std::string oat_location;
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
- dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
- GenerateOatForTest(dex_location,
- oat_location,
- filter,
- relocate,
- pic,
- with_alternate_image);
- }
-
- // Generate a standard oat file in the oat location.
- void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
- GenerateOatForTest(dex_location,
- filter,
- /*relocate*/true,
- /*pic*/false,
- /*with_alternate_image*/false);
- }
-
- private:
- // Reserve memory around where the image will be loaded so other memory
- // won't conflict when it comes time to load the image.
- // This can be called with an already loaded image to reserve the space
- // around it.
- void ReserveImageSpace() {
- MemMap::Init();
-
- // Ensure a chunk of memory is reserved for the image space.
- // The reservation_end includes room for the main space that has to come
- // right after the image in case of the GSS collector.
- uintptr_t reservation_start = ART_BASE_ADDRESS;
- uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB;
-
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
- ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
- for (BacktraceMap::const_iterator it = map->begin();
- reservation_start < reservation_end && it != map->end(); ++it) {
- ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end));
- reservation_start = std::max(reservation_start, it->end);
- }
- ReserveImageSpaceChunk(reservation_start, reservation_end);
- }
-
- // Reserve a chunk of memory for the image space in the given range.
- // Only has effect for chunks with a positive number of bytes.
- void ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) {
- if (start < end) {
- std::string error_msg;
- image_reservation_.push_back(std::unique_ptr<MemMap>(
- MemMap::MapAnonymous("image reservation",
- reinterpret_cast<uint8_t*>(start), end - start,
- PROT_NONE, false, false, &error_msg)));
- ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
- LOG(INFO) << "Reserved space for image " <<
- reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
- reinterpret_cast<void*>(image_reservation_.back()->End());
- }
- }
-
-
- // Unreserve any memory reserved by ReserveImageSpace. This should be called
- // before the image is loaded.
- void UnreserveImageSpace() {
- image_reservation_.clear();
- }
-
- std::vector<std::unique_ptr<MemMap>> image_reservation_;
-};
-
-class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest {
+class OatFileAssistantNoDex2OatTest : public DexoptTest {
public:
virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
- OatFileAssistantTest::SetUpRuntimeOptions(options);
+ DexoptTest::SetUpRuntimeOptions(options);
options->push_back(std::make_pair("-Xnodex2oat", nullptr));
}
};
+
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, DexNoOat) {
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 256c3a6cec..91353e2788 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -114,6 +114,21 @@ static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) {
}
ALWAYS_INLINE
+static inline jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env,
+ const unsigned char* source,
+ jint len,
+ /*out*/unsigned char** dest) {
+ jvmtiError res = env->Allocate(len, dest);
+ if (res != OK) {
+ return res;
+ }
+ memcpy(reinterpret_cast<void*>(*dest),
+ reinterpret_cast<const void*>(source),
+ len);
+ return OK;
+}
+
+ALWAYS_INLINE
static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) {
size_t len = strlen(src) + 1;
unsigned char* buf;
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 21ec731ba5..4f5eb0c33f 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -37,96 +37,84 @@ static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
}
}
-template <typename FnType>
-ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) {
- if (env->event_callbacks == nullptr) {
- return nullptr;
- }
+namespace impl {
- // TODO: Add a type check. Can be done, for example, by an explicitly instantiated template
- // function.
+// Infrastructure to achieve type safety for event dispatch.
- switch (event) {
- case ArtJvmtiEvent::kVmInit:
- return reinterpret_cast<FnType*>(env->event_callbacks->VMInit);
- case ArtJvmtiEvent::kVmDeath:
- return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath);
- case ArtJvmtiEvent::kThreadStart:
- return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
- case ArtJvmtiEvent::kThreadEnd:
- return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
- case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
- case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
- return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
- case ArtJvmtiEvent::kClassLoad:
- return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
- case ArtJvmtiEvent::kClassPrepare:
- return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare);
- case ArtJvmtiEvent::kVmStart:
- return reinterpret_cast<FnType*>(env->event_callbacks->VMStart);
- case ArtJvmtiEvent::kException:
- return reinterpret_cast<FnType*>(env->event_callbacks->Exception);
- case ArtJvmtiEvent::kExceptionCatch:
- return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch);
- case ArtJvmtiEvent::kSingleStep:
- return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep);
- case ArtJvmtiEvent::kFramePop:
- return reinterpret_cast<FnType*>(env->event_callbacks->FramePop);
- case ArtJvmtiEvent::kBreakpoint:
- return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint);
- case ArtJvmtiEvent::kFieldAccess:
- return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess);
- case ArtJvmtiEvent::kFieldModification:
- return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification);
- case ArtJvmtiEvent::kMethodEntry:
- return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry);
- case ArtJvmtiEvent::kMethodExit:
- return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit);
- case ArtJvmtiEvent::kNativeMethodBind:
- return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind);
- case ArtJvmtiEvent::kCompiledMethodLoad:
- return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad);
- case ArtJvmtiEvent::kCompiledMethodUnload:
- return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload);
- case ArtJvmtiEvent::kDynamicCodeGenerated:
- return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated);
- case ArtJvmtiEvent::kDataDumpRequest:
- return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest);
- case ArtJvmtiEvent::kMonitorWait:
- return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait);
- case ArtJvmtiEvent::kMonitorWaited:
- return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited);
- case ArtJvmtiEvent::kMonitorContendedEnter:
- return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter);
- case ArtJvmtiEvent::kMonitorContendedEntered:
- return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered);
- case ArtJvmtiEvent::kResourceExhausted:
- return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted);
- case ArtJvmtiEvent::kGarbageCollectionStart:
- return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart);
- case ArtJvmtiEvent::kGarbageCollectionFinish:
- return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish);
- case ArtJvmtiEvent::kObjectFree:
- return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree);
- case ArtJvmtiEvent::kVmObjectAlloc:
- return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc);
- }
- return nullptr;
-}
+#define FORALL_EVENT_TYPES(fn) \
+ fn(VMInit, ArtJvmtiEvent::kVmInit) \
+ fn(VMDeath, ArtJvmtiEvent::kVmDeath) \
+ fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \
+ fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \
+ fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \
+ fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \
+ fn(VMStart, ArtJvmtiEvent::kVmStart) \
+ fn(Exception, ArtJvmtiEvent::kException) \
+ fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \
+ fn(SingleStep, ArtJvmtiEvent::kSingleStep) \
+ fn(FramePop, ArtJvmtiEvent::kFramePop) \
+ fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \
+ fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \
+ fn(FieldModification, ArtJvmtiEvent::kFieldModification) \
+ fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \
+ fn(MethodExit, ArtJvmtiEvent::kMethodExit) \
+ fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \
+ fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \
+ fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \
+ fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \
+ fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \
+ fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \
+ fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \
+ fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \
+ fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \
+ fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \
+ fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \
+ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \
+ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \
+ fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc)
+
+template <ArtJvmtiEvent kEvent>
+struct EventFnType {
+};
+
+#define EVENT_FN_TYPE(name, enum_name) \
+template <> \
+struct EventFnType<enum_name> { \
+ using type = decltype(jvmtiEventCallbacks().name); \
+};
+
+FORALL_EVENT_TYPES(EVENT_FN_TYPE)
-template <typename ...Args>
-inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread*,
- ArtJvmtiEvent event,
- Args... args ATTRIBUTE_UNUSED) const {
- CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
- event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
- LOG(FATAL) << "Incorrect arguments to ClassFileLoadHook!";
+#undef EVENT_FN_TYPE
+
+template <ArtJvmtiEvent kEvent>
+ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env);
+
+#define GET_CALLBACK(name, enum_name) \
+template <> \
+ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \
+ ArtJvmTiEnv* env) { \
+ if (env->event_callbacks == nullptr) { \
+ return nullptr; \
+ } \
+ return env->event_callbacks->name; \
}
+FORALL_EVENT_TYPES(GET_CALLBACK)
+
+#undef GET_CALLBACK
+
+#undef FORALL_EVENT_TYPES
+
+} // namespace impl
+
+// C++ does not allow partial template function specialization. The dispatch for our separated
+// ClassFileLoadHook event types is the same, so use this helper for code deduplication.
// TODO Locking of some type!
-template <>
+template <ArtJvmtiEvent kEvent>
inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
- ArtJvmtiEvent event,
JNIEnv* jnienv,
jclass class_being_redefined,
jobject loader,
@@ -136,26 +124,16 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data) const {
- CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
- event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
- using FnType = void(jvmtiEnv* /* jvmti_env */,
- JNIEnv* /* jnienv */,
- jclass /* class_being_redefined */,
- jobject /* loader */,
- const char* /* name */,
- jobject /* protection_domain */,
- jint /* class_data_len */,
- const unsigned char* /* class_data */,
- jint* /* new_class_data_len */,
- unsigned char** /* new_class_data */);
+ static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
jint current_len = class_data_len;
unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
ArtJvmTiEnv* last_env = nullptr;
for (ArtJvmTiEnv* env : envs) {
- if (ShouldDispatch(event, env, thread)) {
- jint new_len;
- unsigned char* new_data;
- FnType* callback = GetCallback<FnType>(env, event);
+ if (ShouldDispatch<kEvent>(env, thread)) {
+ jint new_len = 0;
+ unsigned char* new_data = nullptr;
+ auto callback = impl::GetCallback<kEvent>(env);
callback(env,
jnienv,
class_being_redefined,
@@ -186,28 +164,16 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
}
}
-template <typename ...Args>
+// Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match
+// exactly the argument types of the corresponding Jvmti kEvent function pointer.
+
+template <ArtJvmtiEvent kEvent, typename ...Args>
inline void EventHandler::DispatchEvent(art::Thread* thread,
- ArtJvmtiEvent event,
Args... args) const {
- switch (event) {
- case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
- case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
- return DispatchClassFileLoadHookEvent(thread, event, args...);
- default:
- return GenericDispatchEvent(thread, event, args...);
- }
-}
-
-// TODO Locking of some type!
-template <typename ...Args>
-inline void EventHandler::GenericDispatchEvent(art::Thread* thread,
- ArtJvmtiEvent event,
- Args... args) const {
using FnType = void(jvmtiEnv*, Args...);
for (ArtJvmTiEnv* env : envs) {
- if (ShouldDispatch(event, env, thread)) {
- FnType* callback = GetCallback<FnType>(env, event);
+ if (ShouldDispatch<kEvent>(env, thread)) {
+ FnType* callback = impl::GetCallback<kEvent>(env);
if (callback != nullptr) {
(*callback)(env, args...);
}
@@ -215,14 +181,66 @@ inline void EventHandler::GenericDispatchEvent(art::Thread* thread,
}
}
-inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event,
- ArtJvmTiEnv* env,
+// C++ does not allow partial template function specialization. The dispatch for our separated
+// ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper.
+// The following two DispatchEvent specializations dispatch to it.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ art::Thread* thread,
+ JNIEnv* jnienv,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) const {
+ return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
+}
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ art::Thread* thread,
+ JNIEnv* jnienv,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) const {
+ return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
+}
+
+template <ArtJvmtiEvent kEvent>
+inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env,
art::Thread* thread) {
- bool dispatch = env->event_masks.global_event_mask.Test(event);
+ bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
- if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
+ if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) {
EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
- dispatch = mask != nullptr && mask->Test(event);
+ dispatch = mask != nullptr && mask->Test(kEvent);
}
return dispatch;
}
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index d3f8001d69..1da08a0143 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -206,13 +206,12 @@ class JvmtiAllocationListener : public art::gc::AllocationListener {
ScopedLocalRef<jclass> klass(
jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass()));
- handler_->DispatchEvent(self,
- ArtJvmtiEvent::kVmObjectAlloc,
- jni_env,
- thread.get(),
- object.get(),
- klass.get(),
- static_cast<jlong>(byte_count));
+ handler_->DispatchEvent<ArtJvmtiEvent::kVmObjectAlloc>(self,
+ reinterpret_cast<JNIEnv*>(jni_env),
+ thread.get(),
+ object.get(),
+ klass.get(),
+ static_cast<jlong>(byte_count));
}
}
@@ -241,11 +240,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener {
finish_enabled_(false) {}
void StartPause() OVERRIDE {
- handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart);
+ handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionStart>(nullptr);
}
void EndPause() OVERRIDE {
- handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish);
+ handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionFinish>(nullptr);
}
bool IsEnabled() {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 8e246ded8e..4e20d1776b 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -156,9 +156,9 @@ class EventHandler {
ArtJvmtiEvent event,
jvmtiEventMode mode);
- template <typename ...Args>
+ template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
- inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const;
+ inline void DispatchEvent(art::Thread* thread, Args... args) const;
// Tell the event handler capabilities were added/lost so it can adjust the sent events.If
// caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false
@@ -169,8 +169,9 @@ class EventHandler {
bool added);
private:
+ template <ArtJvmtiEvent kEvent>
ALWAYS_INLINE
- static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread);
+ static inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread);
ALWAYS_INLINE
inline bool NeedsEventUpdate(ArtJvmTiEnv* env,
@@ -181,14 +182,17 @@ class EventHandler {
ALWAYS_INLINE
inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
- template <typename ...Args>
- ALWAYS_INLINE inline void GenericDispatchEvent(art::Thread* thread,
- ArtJvmtiEvent event,
- Args... args) const;
- template <typename ...Args>
+ template <ArtJvmtiEvent kEvent>
ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread,
- ArtJvmtiEvent event,
- Args... args) const;
+ JNIEnv* jnienv,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) const;
void HandleEventType(ArtJvmtiEvent event, bool enable);
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index 94cb46a428..b27c2a3834 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -207,7 +207,7 @@ void ObjectTagTable::SweepImpl(art::IsMarkedVisitor* visitor) {
}
void ObjectTagTable::HandleNullSweep(jlong tag) {
- event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag);
+ event_handler_->DispatchEvent<ArtJvmtiEvent::kObjectFree>(nullptr, tag);
}
template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull>
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 450f75a813..b6de592142 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -31,6 +31,8 @@
#include "ti_class.h"
+#include "android-base/stringprintf.h"
+
#include <mutex>
#include <unordered_set>
@@ -38,34 +40,222 @@
#include "base/macros.h"
#include "class_table-inl.h"
#include "class_linker.h"
+#include "common_throws.h"
#include "events-inl.h"
#include "handle.h"
#include "jni_env_ext-inl.h"
#include "jni_internal.h"
+#include "mirror/array-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_ext.h"
#include "runtime.h"
#include "runtime_callbacks.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "thread_list.h"
+#include "ti_redefine.h"
+#include "utils.h"
namespace openjdkjvmti {
+using android::base::StringPrintf;
+
+static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self,
+ const char* descriptor,
+ const std::string& orig_location,
+ jint final_len,
+ const unsigned char* final_dex_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ // Make the mmap
+ std::string error_msg;
+ std::unique_ptr<art::MemMap> map(Redefiner::MoveDataToMemMap(orig_location,
+ final_len,
+ final_dex_data,
+ &error_msg));
+ if (map.get() == nullptr) {
+ LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg;
+ self->ThrowOutOfMemoryError(StringPrintf(
+ "Unable to allocate dex file for transformation of %s", descriptor).c_str());
+ return nullptr;
+ }
+
+ // Make a dex-file
+ if (map->Size() < sizeof(art::DexFile::Header)) {
+ LOG(WARNING) << "Could not read dex file header because dex_data was too short";
+ art::ThrowClassFormatError(nullptr,
+ "Unable to read transformed dex file of %s",
+ descriptor);
+ return nullptr;
+ }
+ uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_;
+ std::unique_ptr<const art::DexFile> dex_file(art::DexFile::Open(map->GetName(),
+ checksum,
+ std::move(map),
+ /*verify*/true,
+ /*verify_checksum*/true,
+ &error_msg));
+ if (dex_file.get() == nullptr) {
+ LOG(WARNING) << "Unable to load modified dex file for " << descriptor << ": " << error_msg;
+ art::ThrowClassFormatError(nullptr,
+ "Unable to read transformed dex file of %s because %s",
+ descriptor,
+ error_msg.c_str());
+ return nullptr;
+ }
+ if (dex_file->NumClassDefs() != 1) {
+ LOG(WARNING) << "Dex file contains more than 1 class_def. Ignoring.";
+ // TODO Throw some other sort of error here maybe?
+ art::ThrowClassFormatError(
+ nullptr,
+ "Unable to use transformed dex file of %s because it contained too many classes",
+ descriptor);
+ return nullptr;
+ }
+ return dex_file;
+}
+
struct ClassCallback : public art::ClassLoadCallback {
+ void ClassPreDefine(const char* descriptor,
+ art::Handle<art::mirror::Class> klass,
+ art::Handle<art::mirror::ClassLoader> class_loader,
+ const art::DexFile& initial_dex_file,
+ const art::DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+ /*out*/art::DexFile const** final_dex_file,
+ /*out*/art::DexFile::ClassDef const** final_class_def)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ bool is_enabled =
+ event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookRetransformable) ||
+ event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ if (!is_enabled) {
+ return;
+ }
+ if (descriptor[0] != 'L') {
+ // It is a primitive or array. Just return
+ return;
+ }
+ std::string name(art::PrettyDescriptor(descriptor));
+
+ art::Thread* self = art::Thread::Current();
+ art::JNIEnvExt* env = self->GetJniEnv();
+ ScopedLocalRef<jobject> loader(
+ env, class_loader.IsNull() ? nullptr : env->AddLocalReference<jobject>(class_loader.Get()));
+ // Go back to native.
+ art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
+ // Call all Non-retransformable agents.
+ jint post_no_redefine_len = 0;
+ unsigned char* post_no_redefine_dex_data = nullptr;
+ std::unique_ptr<const unsigned char> post_no_redefine_unique_ptr(nullptr);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ self,
+ static_cast<JNIEnv*>(env),
+ static_cast<jclass>(nullptr), // The class doesn't really exist yet so send null.
+ loader.get(),
+ name.c_str(),
+ static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains
+ static_cast<jint>(initial_dex_file.Size()),
+ static_cast<const unsigned char*>(initial_dex_file.Begin()),
+ static_cast<jint*>(&post_no_redefine_len),
+ static_cast<unsigned char**>(&post_no_redefine_dex_data));
+ if (post_no_redefine_dex_data == nullptr) {
+ DCHECK_EQ(post_no_redefine_len, 0);
+ post_no_redefine_dex_data = const_cast<unsigned char*>(initial_dex_file.Begin());
+ post_no_redefine_len = initial_dex_file.Size();
+ } else {
+ post_no_redefine_unique_ptr = std::unique_ptr<const unsigned char>(post_no_redefine_dex_data);
+ DCHECK_GT(post_no_redefine_len, 0);
+ }
+ // Call all retransformable agents.
+ jint final_len = 0;
+ unsigned char* final_dex_data = nullptr;
+ std::unique_ptr<const unsigned char> final_dex_unique_ptr(nullptr);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ self,
+ static_cast<JNIEnv*>(env),
+ static_cast<jclass>(nullptr), // The class doesn't really exist yet so send null.
+ loader.get(),
+ name.c_str(),
+ static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains
+ static_cast<jint>(post_no_redefine_len),
+ static_cast<const unsigned char*>(post_no_redefine_dex_data),
+ static_cast<jint*>(&final_len),
+ static_cast<unsigned char**>(&final_dex_data));
+ if (final_dex_data == nullptr) {
+ DCHECK_EQ(final_len, 0);
+ final_dex_data = post_no_redefine_dex_data;
+ final_len = post_no_redefine_len;
+ } else {
+ final_dex_unique_ptr = std::unique_ptr<const unsigned char>(final_dex_data);
+ DCHECK_GT(final_len, 0);
+ }
+
+ if (final_dex_data != initial_dex_file.Begin()) {
+ LOG(WARNING) << "Changing class " << descriptor;
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<2> hs(self);
+ // Save the results of all the non-retransformable agents.
+ // First allocate the ClassExt
+ art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(self)));
+ // Make sure we have a ClassExt. This is fine even though we are a temporary since it will
+ // get copied.
+ if (ext.IsNull()) {
+ // We will just return failure if we fail to allocate
+ LOG(WARNING) << "Could not allocate ext-data for class '" << descriptor << "'. "
+ << "Aborting transformation since we will be unable to store it.";
+ self->AssertPendingOOMException();
+ return;
+ }
+
+ // Allocate the byte array to store the dex file bytes in.
+ art::Handle<art::mirror::ByteArray> arr(hs.NewHandle(
+ art::mirror::ByteArray::AllocateAndFill(
+ self,
+ reinterpret_cast<const signed char*>(post_no_redefine_dex_data),
+ post_no_redefine_len)));
+ if (arr.IsNull()) {
+ LOG(WARNING) << "Unable to allocate byte array for initial dex-file bytes. Aborting "
+ << "transformation";
+ self->AssertPendingOOMException();
+ return;
+ }
+
+ std::unique_ptr<const art::DexFile> dex_file(MakeSingleDexFile(self,
+ descriptor,
+ initial_dex_file.GetLocation(),
+ final_len,
+ final_dex_data));
+ if (dex_file.get() == nullptr) {
+ return;
+ }
+
+ // TODO Check Redefined dex file for invariants.
+ LOG(WARNING) << "Dex file created by class-definition time transformation of "
+ << descriptor << " is not checked for all retransformation invariants.";
+ // TODO Put it in classpath
+ LOG(WARNING) << "Dex file created for class-definition time transformation of "
+ << descriptor << " was not added to any classpaths!";
+ // Actually set the ClassExt's original bytes once we have actually succeeded.
+ ext->SetOriginalDexFileBytes(arr.Get());
+ // Set the return values
+ *final_class_def = &dex_file->GetClassDef(0);
+ *final_dex_file = dex_file.release();
+ }
+ }
+
void ClassLoad(art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassLoad)) {
art::Thread* thread = art::Thread::Current();
ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
- ScopedLocalRef<jclass> jthread(
- thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
+ ScopedLocalRef<jthread> thread_jni(
+ thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
{
art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent(thread,
- ArtJvmtiEvent::kClassLoad,
- reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
- jthread.get(),
- jklass.get());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassLoad>(
+ thread,
+ static_cast<JNIEnv*>(thread->GetJniEnv()),
+ thread_jni.get(),
+ jklass.get());
}
AddTempClass(thread, jklass.get());
}
@@ -78,14 +268,14 @@ struct ClassCallback : public art::ClassLoadCallback {
art::Thread* thread = art::Thread::Current();
ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
- ScopedLocalRef<jclass> jthread(
- thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
+ ScopedLocalRef<jthread> thread_jni(
+ thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent(thread,
- ArtJvmtiEvent::kClassPrepare,
- reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
- jthread.get(),
- jklass.get());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassPrepare>(
+ thread,
+ static_cast<JNIEnv*>(thread->GetJniEnv()),
+ thread_jni.get(),
+ jklass.get());
}
}
diff --git a/runtime/openjdkjvmti/ti_dump.cc b/runtime/openjdkjvmti/ti_dump.cc
index 2ee5c409f6..d9e3ef1bcf 100644
--- a/runtime/openjdkjvmti/ti_dump.cc
+++ b/runtime/openjdkjvmti/ti_dump.cc
@@ -48,7 +48,7 @@ struct DumpCallback : public art::RuntimeSigQuitCallback {
void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::Thread* thread = art::Thread::Current();
art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kDataDumpRequest);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kDataDumpRequest>(nullptr);
}
EventHandler* event_handler = nullptr;
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
index 154406a5db..4970288250 100644
--- a/runtime/openjdkjvmti/ti_phase.cc
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -64,7 +64,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback {
case RuntimePhase::kStart:
{
art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmStart>(nullptr, GetJniEnv());
PhaseUtil::current_phase_ = JVMTI_PHASE_START;
}
break;
@@ -72,17 +72,14 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback {
{
ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent(nullptr,
- ArtJvmtiEvent::kVmInit,
- GetJniEnv(),
- thread.get());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>(nullptr, GetJniEnv(), thread.get());
PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
}
break;
case RuntimePhase::kDeath:
{
art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmDeath>(nullptr, GetJniEnv());
PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
}
// TODO: Block events now.
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 34efc502e1..d2ddc21cd4 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -520,28 +520,13 @@ void Redefiner::RecordFailure(jvmtiError result,
result_ = result;
}
-// Allocates a ByteArray big enough to store the given number of bytes and copies them from the
-// bytes pointer.
-static art::mirror::ByteArray* AllocateAndFillBytes(art::Thread* self,
- const uint8_t* bytes,
- int32_t num_bytes)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::StackHandleScope<1> hs(self);
- art::Handle<art::mirror::ByteArray> arr(hs.NewHandle(
- art::mirror::ByteArray::Alloc(self, num_bytes)));
- if (!arr.IsNull()) {
- // Copy it in. Just skip if it's null
- memcpy(arr->GetData(), bytes, num_bytes);
- }
- return arr.Get();
-}
-
art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() {
// If we have been specifically given a new set of bytes use that
if (original_dex_file_.size() != 0) {
- return AllocateAndFillBytes(driver_->self_,
- &original_dex_file_.At(0),
- original_dex_file_.size());
+ return art::mirror::ByteArray::AllocateAndFill(
+ driver_->self_,
+ reinterpret_cast<const signed char*>(&original_dex_file_.At(0)),
+ original_dex_file_.size());
}
// See if we already have one set.
@@ -561,7 +546,10 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi
LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses "
<< "on this class might fail if no transformations are applied to it!";
}
- return AllocateAndFillBytes(driver_->self_, current_dex_file.Begin(), current_dex_file.Size());
+ return art::mirror::ByteArray::AllocateAndFill(
+ driver_->self_,
+ reinterpret_cast<const signed char*>(current_dex_file.Begin()),
+ current_dex_file.Size());
}
struct CallbackCtx {
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 29a7e1f3ac..fdc13eee32 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -96,6 +96,11 @@ class Redefiner {
static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
+ static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
+ jint data_len,
+ const unsigned char* dex_data,
+ std::string* error_msg);
+
private:
class ClassRedefinition {
public:
@@ -234,11 +239,6 @@ class Redefiner {
/*out*/std::string* error_msg)
REQUIRES_SHARED(art::Locks::mutator_lock_);
- static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
- jint data_len,
- const unsigned char* dex_data,
- std::string* error_msg);
-
// TODO Put on all the lock qualifiers.
jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index 9f81d6ba97..b18a5cd746 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -61,11 +61,14 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime
}
return self->GetJniEnv()->AddLocalReference<jthread>(self->GetPeer());
}
- void Post(art::Thread* self, ArtJvmtiEvent type) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ template <ArtJvmtiEvent kEvent>
+ void Post(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK_EQ(self, art::Thread::Current());
ScopedLocalRef<jthread> thread(self->GetJniEnv(), GetThreadObject(self));
art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
- event_handler->DispatchEvent(self, type, self->GetJniEnv(), thread.get());
+ event_handler->DispatchEvent<kEvent>(self,
+ reinterpret_cast<JNIEnv*>(self->GetJniEnv()),
+ thread.get());
}
void ThreadStart(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -81,11 +84,11 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime
}
return;
}
- Post(self, ArtJvmtiEvent::kThreadStart);
+ Post<ArtJvmtiEvent::kThreadStart>(self);
}
void ThreadDeath(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
- Post(self, ArtJvmtiEvent::kThreadEnd);
+ Post<ArtJvmtiEvent::kThreadEnd>(self);
}
void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -93,7 +96,7 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime
// We moved to VMInit. Report the main thread as started (it was attached early, and must
// not be reported until Init.
started = true;
- Post(art::Thread::Current(), ArtJvmtiEvent::kThreadStart);
+ Post<ArtJvmtiEvent::kThreadStart>(art::Thread::Current());
}
}
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index af4fb7187a..3c4cfea19a 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -68,19 +68,17 @@ jvmtiError Transformer::RetransformClassesDirect(
for (ArtClassDefinition& def : *definitions) {
jint new_len = -1;
unsigned char* new_data = nullptr;
- // Static casts are so that we get the right template initialization for the special event
- // handling code required by the ClassFileLoadHooks.
- gEventHandler.DispatchEvent(self,
- ArtJvmtiEvent::kClassFileLoadHookRetransformable,
- GetJniEnv(env),
- static_cast<jclass>(def.klass),
- static_cast<jobject>(def.loader),
- static_cast<const char*>(def.name.c_str()),
- static_cast<jobject>(def.protection_domain),
- static_cast<jint>(def.dex_len),
- static_cast<const unsigned char*>(def.dex_data.get()),
- static_cast<jint*>(&new_len),
- static_cast<unsigned char**>(&new_data));
+ gEventHandler.DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ self,
+ GetJniEnv(env),
+ def.klass,
+ def.loader,
+ def.name.c_str(),
+ def.protection_domain,
+ def.dex_len,
+ static_cast<const unsigned char*>(def.dex_data.get()),
+ &new_len,
+ &new_data);
def.SetNewDexData(env, new_len, new_data);
}
return OK;
@@ -139,20 +137,6 @@ jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string*
return OK;
}
-static jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env,
- const unsigned char* source,
- jint len,
- /*out*/unsigned char** dest) {
- jvmtiError res = env->Allocate(len, dest);
- if (res != OK) {
- return res;
- }
- memcpy(reinterpret_cast<void*>(*dest),
- reinterpret_cast<const void*>(source),
- len);
- return OK;
-}
-
jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env,
art::Handle<art::mirror::Class> klass,
/*out*/jint* dex_data_len,
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index d1ad77c910..ccc5f7a8ee 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -599,42 +599,6 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options,
args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
}
- if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kRuntimePlugins) {
- LOG(WARNING) << "Experimental runtime plugin support has been enabled. No guarantees are made "
- << "about stability or usage of this plugin support. Use at your own risk. Do "
- << "not attempt to write shipping code that relies on the implementation of "
- << "runtime plugins.";
- } else if (!args.GetOrDefault(M::Plugins).empty()) {
- LOG(WARNING) << "Experimental runtime plugin support has not been enabled. Ignored options: ";
- for (const auto& op : args.GetOrDefault(M::Plugins)) {
- LOG(WARNING) << " -plugin:" << op.GetLibrary();
- }
- }
-
- if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kAgents) {
- LOG(WARNING) << "Experimental runtime agent support has been enabled. No guarantees are made "
- << "the completeness, accuracy, reliability, or stability of the agent "
- << "implementation. Use at your own risk. Do not attempt to write shipping code "
- << "that relies on the implementation of any part of this api.";
- } else if (!args.GetOrDefault(M::AgentLib).empty() || !args.GetOrDefault(M::AgentPath).empty()) {
- LOG(WARNING) << "agent support has not been enabled. Enable experimental agent "
- << " support with '-XExperimental:agent'. Ignored options are:";
- for (const auto& op : args.GetOrDefault(M::AgentLib)) {
- if (op.HasArgs()) {
- LOG(WARNING) << " -agentlib:" << op.GetName() << "=" << op.GetArgs();
- } else {
- LOG(WARNING) << " -agentlib:" << op.GetName();
- }
- }
- for (const auto& op : args.GetOrDefault(M::AgentPath)) {
- if (op.HasArgs()) {
- LOG(WARNING) << " -agentpath:" << op.GetName() << "=" << op.GetArgs();
- } else {
- LOG(WARNING) << " -agentpath:" << op.GetName();
- }
- }
- }
-
*runtime_options = std::move(args);
return true;
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 06cd7ff4b5..5e008a8bb8 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1069,16 +1069,13 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
- if (experimental_flags_ & ExperimentalFlags::kRuntimePlugins) {
- plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins);
- }
- if (experimental_flags_ & ExperimentalFlags::kAgents) {
- agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);
- // TODO Add back in -agentlib
- // for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) {
- // agents_.push_back(lib);
- // }
- }
+ plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins);
+ agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);
+ // TODO Add back in -agentlib
+ // for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) {
+ // agents_.push_back(lib);
+ // }
+
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 7b15a4f1b5..25324b52d1 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -67,6 +67,36 @@ void RuntimeCallbacks::ClassLoad(Handle<mirror::Class> klass) {
}
}
+void RuntimeCallbacks::ClassPreDefine(const char* descriptor,
+ Handle<mirror::Class> temp_class,
+ Handle<mirror::ClassLoader> loader,
+ const DexFile& initial_dex_file,
+ const DexFile::ClassDef& initial_class_def,
+ /*out*/DexFile const** final_dex_file,
+ /*out*/DexFile::ClassDef const** final_class_def) {
+ DexFile const* current_dex_file = &initial_dex_file;
+ DexFile::ClassDef const* current_class_def = &initial_class_def;
+ for (ClassLoadCallback* cb : class_callbacks_) {
+ DexFile const* new_dex_file = nullptr;
+ DexFile::ClassDef const* new_class_def = nullptr;
+ cb->ClassPreDefine(descriptor,
+ temp_class,
+ loader,
+ *current_dex_file,
+ *current_class_def,
+ &new_dex_file,
+ &new_class_def);
+ if ((new_dex_file != nullptr && new_dex_file != current_dex_file) ||
+ (new_class_def != nullptr && new_class_def != current_class_def)) {
+ DCHECK(new_dex_file != nullptr && new_class_def != nullptr);
+ current_dex_file = new_dex_file;
+ current_class_def = new_class_def;
+ }
+ }
+ *final_dex_file = current_dex_file;
+ *final_class_def = current_class_def;
+}
+
void RuntimeCallbacks::ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass) {
for (ClassLoadCallback* cb : class_callbacks_) {
cb->ClassPrepare(temp_klass, klass);
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index e580e7875e..d321254e17 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -21,12 +21,14 @@
#include "base/macros.h"
#include "base/mutex.h"
+#include "dex_file.h"
#include "handle.h"
namespace art {
namespace mirror {
class Class;
+class ClassLoader;
} // namespace mirror
class ClassLoadCallback;
@@ -99,6 +101,15 @@ class RuntimeCallbacks {
void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void ClassPreDefine(const char* descriptor,
+ Handle<mirror::Class> temp_class,
+ Handle<mirror::ClassLoader> loader,
+ const DexFile& initial_dex_file,
+ const DexFile::ClassDef& initial_class_def,
+ /*out*/DexFile const** final_dex_file,
+ /*out*/DexFile::ClassDef const** final_class_def)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
std::vector<ThreadLifecycleCallback*> thread_callbacks_
GUARDED_BY(Locks::mutator_lock_);
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 8974b595de..f1e78b40e0 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -249,6 +249,21 @@ class ClassLoadCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
}
struct Callback : public ClassLoadCallback {
+ virtual void ClassPreDefine(const char* descriptor,
+ Handle<mirror::Class> klass ATTRIBUTE_UNUSED,
+ Handle<mirror::ClassLoader> class_loader ATTRIBUTE_UNUSED,
+ const DexFile& initial_dex_file,
+ const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+ /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
+ /*out*/DexFile::ClassDef const** final_class_def ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string location(initial_dex_file.GetLocation());
+ std::string event =
+ std::string("PreDefine:") + descriptor + " <" +
+ location.substr(location.rfind("/") + 1, location.size()) + ">";
+ data.push_back(event);
+ }
+
void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
std::string tmp;
std::string event = std::string("Load:") + klass->GetDescriptor(&tmp);
@@ -281,14 +296,21 @@ TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) {
hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader)));
ASSERT_TRUE(h_Y.Get() != nullptr);
- bool expect1 = Expect({ "Load:LX;", "Prepare:LX;[LX;]", "Load:LY;", "Prepare:LY;[LY;]" });
+ bool expect1 = Expect({ "PreDefine:LY; <art-gtest-XandY.jar>",
+ "PreDefine:LX; <art-gtest-XandY.jar>",
+ "Load:LX;",
+ "Prepare:LX;[LX;]",
+ "Load:LY;",
+ "Prepare:LY;[LY;]" });
EXPECT_TRUE(expect1);
cb_.data.clear();
ASSERT_TRUE(class_linker_->EnsureInitialized(Thread::Current(), h_Y, true, true));
- bool expect2 = Expect({ "Load:LY$Z;", "Prepare:LY$Z;[LY$Z;]" });
+ bool expect2 = Expect({ "PreDefine:LY$Z; <art-gtest-XandY.jar>",
+ "Load:LY$Z;",
+ "Prepare:LY$Z;[LY$Z;]" });
EXPECT_TRUE(expect2);
}
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 749a36e3b3..ad748b04d3 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -119,10 +119,10 @@ RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10)
RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
RUNTIME_OPTIONS_KEY (std::string, CpuAbiList)
RUNTIME_OPTIONS_KEY (std::string, Fingerprint)
-RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, agents}
-RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options>, Requires -Xexperimental:agents
-RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options>, Requires -Xexperimental:agents
-RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library> Requires -Xexperimental:runtime-plugins
+RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{...}
+RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options>
+RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options>
+RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library>
RUNTIME_OPTIONS_KEY (Unit, FullyDeoptable) // -Xfully-deoptable
// Not parse-able from command line, but can be provided explicitly.
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 3c92b86208..690b069c8e 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -198,6 +198,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios,
<< "StackMap" << header_suffix
<< std::hex
<< " [native_pc=0x" << code_offset + pc_offset << "]"
+ << " [entry_size=0x" << encoding.stack_map_size_in_bytes << "]"
<< " (dex_pc=0x" << GetDexPc(stack_map_encoding)
<< ", native_pc_offset=0x" << pc_offset
<< ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding)
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 28c4b88fb0..578252181d 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -667,32 +667,7 @@ struct FieldEncoding {
ALWAYS_INLINE int32_t Load(const MemoryRegion& region) const {
DCHECK_LE(end_offset_, region.size_in_bits());
- const size_t bit_count = BitSize();
- if (bit_count == 0) {
- // Do not touch any memory if the range is empty.
- return min_value_;
- }
- uint8_t* address = region.start() + start_offset_ / kBitsPerByte;
- const uint32_t shift = start_offset_ & (kBitsPerByte - 1);
- // Load the value (reading only the strictly needed bytes).
- const uint32_t load_bit_count = shift + bit_count;
- uint32_t value = *address++ >> shift;
- if (load_bit_count > 8) {
- value |= static_cast<uint32_t>(*address++) << (8 - shift);
- if (load_bit_count > 16) {
- value |= static_cast<uint32_t>(*address++) << (16 - shift);
- if (load_bit_count > 24) {
- value |= static_cast<uint32_t>(*address++) << (24 - shift);
- if (load_bit_count > 32) {
- value |= static_cast<uint32_t>(*address++) << (32 - shift);
- }
- }
- }
- }
- // Clear unwanted most significant bits.
- uint32_t clear_bit_count = 32 - bit_count;
- value = (value << clear_bit_count) >> clear_bit_count;
- return value + min_value_;
+ return static_cast<int32_t>(region.LoadBits(start_offset_, BitSize())) + min_value_;
}
ALWAYS_INLINE void Store(MemoryRegion region, int32_t value) const {
@@ -1182,6 +1157,17 @@ class CodeInfo {
}
}
+ size_t GetDexRegisterMapsSize(const CodeInfoEncoding& encoding,
+ uint32_t number_of_dex_registers) const {
+ size_t total = 0;
+ for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) {
+ StackMap stack_map = GetStackMapAt(i, encoding);
+ DexRegisterMap map(GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers));
+ total += map.Size();
+ }
+ return total;
+ }
+
// Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`.
DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth,
InlineInfo inline_info,
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 8867743ec1..410416ea64 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -788,49 +788,58 @@ void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu)
*task_cpu = strtoull(fields[36].c_str(), nullptr, 10);
}
-const char* GetAndroidRoot() {
- const char* android_root = getenv("ANDROID_ROOT");
- if (android_root == nullptr) {
- if (OS::DirectoryExists("/system")) {
- android_root = "/system";
+static const char* GetAndroidDirSafe(const char* env_var,
+ const char* default_dir,
+ std::string* error_msg) {
+ const char* android_dir = getenv(env_var);
+ if (android_dir == nullptr) {
+ if (OS::DirectoryExists(default_dir)) {
+ android_dir = default_dir;
} else {
- LOG(FATAL) << "ANDROID_ROOT not set and /system does not exist";
- return "";
+ *error_msg = StringPrintf("%s not set and %s does not exist", env_var, default_dir);
+ return nullptr;
}
}
- if (!OS::DirectoryExists(android_root)) {
- LOG(FATAL) << "Failed to find ANDROID_ROOT directory " << android_root;
- return "";
+ if (!OS::DirectoryExists(android_dir)) {
+ *error_msg = StringPrintf("Failed to find %s directory %s", env_var, android_dir);
+ return nullptr;
}
- return android_root;
+ return android_dir;
}
-const char* GetAndroidData() {
+const char* GetAndroidDir(const char* env_var, const char* default_dir) {
std::string error_msg;
- const char* dir = GetAndroidDataSafe(&error_msg);
+ const char* dir = GetAndroidDirSafe(env_var, default_dir, &error_msg);
if (dir != nullptr) {
return dir;
} else {
LOG(FATAL) << error_msg;
- return "";
+ return nullptr;
}
}
+const char* GetAndroidRoot() {
+ return GetAndroidDir("ANDROID_ROOT", "/system");
+}
+
+const char* GetAndroidRootSafe(std::string* error_msg) {
+ return GetAndroidDirSafe("ANDROID_ROOT", "/system", error_msg);
+}
+
+const char* GetAndroidData() {
+ return GetAndroidDir("ANDROID_DATA", "/data");
+}
+
const char* GetAndroidDataSafe(std::string* error_msg) {
- const char* android_data = getenv("ANDROID_DATA");
- if (android_data == nullptr) {
- if (OS::DirectoryExists("/data")) {
- android_data = "/data";
- } else {
- *error_msg = "ANDROID_DATA not set and /data does not exist";
- return nullptr;
- }
- }
- if (!OS::DirectoryExists(android_data)) {
- *error_msg = StringPrintf("Failed to find ANDROID_DATA directory %s", android_data);
- return nullptr;
+ return GetAndroidDirSafe("ANDROID_DATA", "/data", error_msg);
+}
+
+std::string GetDefaultBootImageLocation(std::string* error_msg) {
+ const char* android_root = GetAndroidRootSafe(error_msg);
+ if (android_root == nullptr) {
+ return "";
}
- return android_data;
+ return StringPrintf("%s/framework/boot.art", android_root);
}
void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
diff --git a/runtime/utils.h b/runtime/utils.h
index 16ef706159..9e663b31e0 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -143,12 +143,18 @@ void SetThreadName(const char* thread_name);
// Find $ANDROID_ROOT, /system, or abort.
const char* GetAndroidRoot();
+// Find $ANDROID_ROOT, /system, or return null.
+const char* GetAndroidRootSafe(std::string* error_msg);
// Find $ANDROID_DATA, /data, or abort.
const char* GetAndroidData();
// Find $ANDROID_DATA, /data, or return null.
const char* GetAndroidDataSafe(std::string* error_msg);
+// Returns the default boot image location (ANDROID_ROOT/framework/boot.art).
+// Returns an empty string if ANDROID_ROOT is not set.
+std::string GetDefaultBootImageLocation(std::string* error_msg);
+
// Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
// could not be found.
std::string GetDalvikCache(const char* subdir);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 507ea165e5..2610252aa7 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -51,7 +51,6 @@ jclass WellKnownClasses::java_lang_ClassLoader;
jclass WellKnownClasses::java_lang_ClassNotFoundException;
jclass WellKnownClasses::java_lang_Daemons;
jclass WellKnownClasses::java_lang_Error;
-jclass WellKnownClasses::java_lang_ExceptionInInitializerError;
jclass WellKnownClasses::java_lang_invoke_MethodHandle;
jclass WellKnownClasses::java_lang_IllegalAccessError;
jclass WellKnownClasses::java_lang_NoClassDefFoundError;
@@ -290,7 +289,6 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_Object = CacheClass(env, "java/lang/Object");
java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError");
java_lang_Error = CacheClass(env, "java/lang/Error");
- java_lang_ExceptionInInitializerError = CacheClass(env, "java/lang/ExceptionInInitializerError");
java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
java_lang_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle");
java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index b3ce3d1597..db8a53c44c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -61,7 +61,6 @@ struct WellKnownClasses {
static jclass java_lang_ClassNotFoundException;
static jclass java_lang_Daemons;
static jclass java_lang_Error;
- static jclass java_lang_ExceptionInInitializerError;
static jclass java_lang_IllegalAccessError;
static jclass java_lang_invoke_MethodHandle;
static jclass java_lang_NoClassDefFoundError;
diff --git a/test/008-exceptions/expected.txt b/test/008-exceptions/expected.txt
index 083ecf7ced..fcf2ef49f3 100644
--- a/test/008-exceptions/expected.txt
+++ b/test/008-exceptions/expected.txt
@@ -1,11 +1,11 @@
Got an NPE: second throw
java.lang.NullPointerException: second throw
- at Main.catchAndRethrow(Main.java:77)
- at Main.exceptions_007(Main.java:59)
- at Main.main(Main.java:67)
+ at Main.catchAndRethrow(Main.java:94)
+ at Main.exceptions_007(Main.java:74)
+ at Main.main(Main.java:82)
Caused by: java.lang.NullPointerException: first throw
- at Main.throwNullPointerException(Main.java:84)
- at Main.catchAndRethrow(Main.java:74)
+ at Main.throwNullPointerException(Main.java:101)
+ at Main.catchAndRethrow(Main.java:91)
... 2 more
Static Init
BadError: This is bad by convention: BadInit
@@ -15,3 +15,11 @@ Static BadInitNoStringInit
BadErrorNoStringInit: This is bad by convention
java.lang.NoClassDefFoundError: BadInitNoStringInit
BadErrorNoStringInit: This is bad by convention
+BadSuperClass Static Init
+BadError: This is bad by convention: BadInit
+MultiDexBadInit Static Init
+java.lang.Error: MultiDexBadInit
+java.lang.NoClassDefFoundError: MultiDexBadInit
+ cause: java.lang.Error: MultiDexBadInit
+java.lang.NoClassDefFoundError: MultiDexBadInit
+ cause: java.lang.Error: MultiDexBadInit
diff --git a/test/008-exceptions/multidex.jpp b/test/008-exceptions/multidex.jpp
new file mode 100644
index 0000000000..a3746f5149
--- /dev/null
+++ b/test/008-exceptions/multidex.jpp
@@ -0,0 +1,27 @@
+BadError:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadError
+BadInit:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadInit
+BadErrorNoStringInit:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadErrorNoStringInit
+BadInitNoStringInit:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadInitNoStringInit
+BadSuperClass:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadSuperClass
+DerivedFromBadSuperClass:
+ @@com.android.jack.annotations.ForceInMainDex
+ class DerivedFromBadSuperClass
+Main:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Main
+MultiDexBadInit:
+ @@com.android.jack.annotations.ForceInMainDex
+ class MultiDexBadInit
+MultiDexBadInitWrapper1:
+ @@com.android.jack.annotations.ForceInMainDex
+ class MultiDexBadInitWrapper1
diff --git a/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java b/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java
new file mode 100644
index 0000000000..f3953bd4c7
--- /dev/null
+++ b/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class MultiDexBadInitWrapper2 {
+ public static void setDummy(int value) {
+ if (doThrow) { throw new Error(); }
+ MultiDexBadInit.dummy = value;
+ }
+
+ public static boolean doThrow = false;
+}
diff --git a/test/008-exceptions/src/Main.java b/test/008-exceptions/src/Main.java
index b8231f12bd..74af00ccf7 100644
--- a/test/008-exceptions/src/Main.java
+++ b/test/008-exceptions/src/Main.java
@@ -50,6 +50,21 @@ class BadInitNoStringInit {
}
}
+// A class that throws BadError during static initialization, serving as a super class.
+class BadSuperClass {
+ static int dummy;
+ static {
+ System.out.println("BadSuperClass Static Init");
+ if (true) {
+ throw new BadError("BadInit");
+ }
+ }
+}
+
+// A class that derives from BadSuperClass.
+class DerivedFromBadSuperClass extends BadSuperClass {
+}
+
/**
* Exceptions across method calls
*/
@@ -63,10 +78,12 @@ public class Main {
npe.printStackTrace(System.out);
}
}
- public static void main (String args[]) {
+ public static void main(String args[]) {
exceptions_007();
exceptionsRethrowClassInitFailure();
exceptionsRethrowClassInitFailureNoStringInit();
+ exceptionsForSuperClassInitFailure();
+ exceptionsInMultiDex();
}
private static void catchAndRethrow() {
@@ -129,4 +146,70 @@ public class Main {
error.printStackTrace(System.out);
}
}
+
+ private static void exceptionsForSuperClassInitFailure() {
+ try {
+ // Resolve DerivedFromBadSuperClass.
+ BadSuperClass.dummy = 1;
+ throw new IllegalStateException("Should not reach here.");
+ } catch (BadError e) {
+ System.out.println(e);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ try {
+ // Before splitting mirror::Class::kStatusError into
+ // kStatusErrorUnresolved and kStatusErrorResolved,
+ // this would trigger a
+ // CHECK(super_class->IsResolved())
+ // failure in
+ // ClassLinker::LoadSuperAndInterfaces().
+ // After the change we're getting either VerifyError
+ // (for Optimizing) or NoClassDefFoundError wrapping
+ // BadError (for interpreter or JIT).
+ new DerivedFromBadSuperClass();
+ throw new IllegalStateException("Should not reach here.");
+ } catch (NoClassDefFoundError ncdfe) {
+ if (!(ncdfe.getCause() instanceof BadError)) {
+ ncdfe.getCause().printStackTrace();
+ }
+ } catch (VerifyError e) {
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ private static void exceptionsInMultiDex() {
+ try {
+ MultiDexBadInit.dummy = 1;
+ throw new IllegalStateException("Should not reach here.");
+ } catch (Error e) {
+ System.out.println(e);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ // Before splitting mirror::Class::kStatusError into
+ // kStatusErrorUnresolved and kStatusErrorResolved,
+ // the exception from wrapper 1 would have been
+ // wrapped in NoClassDefFoundError but the exception
+ // from wrapper 2 would have been unwrapped.
+ try {
+ MultiDexBadInitWrapper1.setDummy(1);
+ throw new IllegalStateException("Should not reach here.");
+ } catch (NoClassDefFoundError ncdfe) {
+ System.out.println(ncdfe);
+ System.out.println(" cause: " + ncdfe.getCause());
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ try {
+ MultiDexBadInitWrapper2.setDummy(1);
+ throw new IllegalStateException("Should not reach here.");
+ } catch (NoClassDefFoundError ncdfe) {
+ System.out.println(ncdfe);
+ System.out.println(" cause: " + ncdfe.getCause());
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
}
diff --git a/test/008-exceptions/src/MultiDexBadInit.java b/test/008-exceptions/src/MultiDexBadInit.java
new file mode 100644
index 0000000000..e3ebb9ca45
--- /dev/null
+++ b/test/008-exceptions/src/MultiDexBadInit.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class MultiDexBadInit {
+ static int dummy;
+ static {
+ System.out.println("MultiDexBadInit Static Init");
+ if (true) {
+ throw new Error("MultiDexBadInit");
+ }
+ }
+}
diff --git a/test/008-exceptions/src/MultiDexBadInitWrapper1.java b/test/008-exceptions/src/MultiDexBadInitWrapper1.java
new file mode 100644
index 0000000000..059e6a3d7d
--- /dev/null
+++ b/test/008-exceptions/src/MultiDexBadInitWrapper1.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class MultiDexBadInitWrapper1 {
+ public static void setDummy(int value) {
+ if (doThrow) { throw new Error(); }
+ MultiDexBadInit.dummy = value;
+ }
+
+ public static boolean doThrow = false;
+}
diff --git a/test/142-classloader2/expected.txt b/test/142-classloader2/expected.txt
index 86f5e220e2..056d9785de 100644
--- a/test/142-classloader2/expected.txt
+++ b/test/142-classloader2/expected.txt
@@ -1 +1,5 @@
+Loaded class B.
+Caught VerifyError.
+Loaded class B.
+Caught wrapped VerifyError.
Everything OK.
diff --git a/test/142-classloader2/src/Main.java b/test/142-classloader2/src/Main.java
index 80b00e7dd2..a0c77645a3 100644
--- a/test/142-classloader2/src/Main.java
+++ b/test/142-classloader2/src/Main.java
@@ -74,16 +74,25 @@ public class Main {
// Try to load a dex file with bad dex code. Use new instance to force verification.
try {
Class<?> badClass = Main.class.getClassLoader().loadClass("B");
+ System.out.println("Loaded class B.");
badClass.newInstance();
- System.out.println("Should not be able to load class from bad dex file.");
+ System.out.println("Should not be able to instantiate B with bad dex bytecode.");
} catch (VerifyError e) {
+ System.out.println("Caught VerifyError.");
}
// Make sure the same error is rethrown when reloading the bad class.
try {
Class<?> badClass = Main.class.getClassLoader().loadClass("B");
- System.out.println("Should not be able to load class from bad dex file.");
- } catch (VerifyError e) {
+ System.out.println("Loaded class B.");
+ badClass.newInstance();
+ System.out.println("Should not be able to instantiate B with bad dex bytecode.");
+ } catch (NoClassDefFoundError e) {
+ if (e.getCause() instanceof VerifyError) {
+ System.out.println("Caught wrapped VerifyError.");
+ } else {
+ e.printStackTrace();
+ }
}
System.out.println("Everything OK.");
diff --git a/test/154-gc-loop/expected.txt b/test/154-gc-loop/expected.txt
new file mode 100644
index 0000000000..6106818c79
--- /dev/null
+++ b/test/154-gc-loop/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Finalize count too large: false
diff --git a/test/154-gc-loop/heap_interface.cc b/test/154-gc-loop/heap_interface.cc
new file mode 100644
index 0000000000..8d610a87fa
--- /dev/null
+++ b/test/154-gc-loop/heap_interface.cc
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 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 "gc/heap.h"
+#include "runtime.h"
+
+namespace art {
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_backgroundProcessState(JNIEnv*, jclass) {
+ Runtime::Current()->UpdateProcessState(kProcessStateJankImperceptible);
+}
+
+} // namespace
+} // namespace art
diff --git a/test/154-gc-loop/info.txt b/test/154-gc-loop/info.txt
new file mode 100644
index 0000000000..f599db1d01
--- /dev/null
+++ b/test/154-gc-loop/info.txt
@@ -0,0 +1 @@
+Test that GC doesn't happen too often for a few small allocations.
diff --git a/test/154-gc-loop/src/Main.java b/test/154-gc-loop/src/Main.java
new file mode 100644
index 0000000000..3a256c109e
--- /dev/null
+++ b/test/154-gc-loop/src/Main.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.WeakReference;
+
+public class Main {
+ static final class GcWatcher {
+ protected void finalize() throws Throwable {
+ watcher = new WeakReference<GcWatcher>(new GcWatcher());
+ ++finalizeCounter;
+ }
+ }
+ static WeakReference<GcWatcher> watcher = new WeakReference<GcWatcher>(new GcWatcher());
+ static Object o = new Object();
+ static int finalizeCounter = 0;
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ backgroundProcessState();
+ try {
+ Runtime.getRuntime().gc();
+ for (int i = 0; i < 10; ++i) {
+ o = new Object();
+ Thread.sleep(1000);
+ }
+ } catch (Exception e) {}
+ System.out.println("Finalize count too large: " +
+ ((finalizeCounter >= 10) ? Integer.toString(finalizeCounter) : "false"));
+ }
+
+ private static native void backgroundProcessState();
+}
diff --git a/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali
index 75344f74bf..e4bf236266 100644
--- a/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali
+++ b/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -17,10 +17,9 @@
.super Ljava/lang/Object;
## CHECK-START-X86: int IrreducibleLoop.simpleLoop(int) dead_code_elimination$initial (before)
-## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod
## CHECK-DAG: <<Constant:i\d+>> IntConstant 42
-## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:{{B\d+}} irreducible:true
-## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none
+## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:{{B\d+}} irreducible:true
+## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none
.method public static simpleLoop(I)I
.registers 3
const/16 v0, 42
diff --git a/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali b/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali
index 186f0ab3e8..9b8aa510a4 100644
--- a/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali
+++ b/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali
@@ -17,11 +17,10 @@
.super Ljava/lang/Object;
## CHECK-START-X86: int IrreducibleLoop.simpleLoop1(int) dead_code_elimination$initial (before)
-## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod
## CHECK-DAG: <<Constant:i\d+>> IntConstant 42
## CHECK-DAG: Goto irreducible:true
-## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none
-## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}},<<Method>>] loop:none
+## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none
+## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}}] loop:none
.method public static simpleLoop1(I)I
.registers 3
const/16 v0, 42
@@ -58,11 +57,10 @@
.end method
## CHECK-START-X86: int IrreducibleLoop.simpleLoop2(int) dead_code_elimination$initial (before)
-## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod
## CHECK-DAG: <<Constant:i\d+>> IntConstant 42
## CHECK-DAG: Goto irreducible:true
-## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none
-## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}},<<Method>>] loop:none
+## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none
+## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}}] loop:none
.method public static simpleLoop2(I)I
.registers 3
const/16 v0, 42
diff --git a/test/900-hello-plugin/run b/test/900-hello-plugin/run
index 35b08715a1..50835f89af 100755
--- a/test/900-hello-plugin/run
+++ b/test/900-hello-plugin/run
@@ -18,7 +18,5 @@ plugin=libartagentd.so
if [[ "$@" == *"-O"* ]]; then
plugin=libartagent.so
fi
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --runtime-option -agentpath:${plugin}=test_900 \
+./default-run "$@" --runtime-option -agentpath:${plugin}=test_900 \
--android-runtime-option -Xplugin:${plugin}
diff --git a/test/901-hello-ti-agent/run b/test/901-hello-ti-agent/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/901-hello-ti-agent/run
+++ b/test/901-hello-ti-agent/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/902-hello-transformation/run b/test/902-hello-transformation/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/902-hello-transformation/run
+++ b/test/902-hello-transformation/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/903-hello-tagging/run b/test/903-hello-tagging/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/903-hello-tagging/run
+++ b/test/903-hello-tagging/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/904-object-allocation/run b/test/904-object-allocation/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/904-object-allocation/run
+++ b/test/904-object-allocation/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/905-object-free/run b/test/905-object-free/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/905-object-free/run
+++ b/test/905-object-free/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/906-iterate-heap/run b/test/906-iterate-heap/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/906-iterate-heap/run
+++ b/test/906-iterate-heap/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/907-get-loaded-classes/run b/test/907-get-loaded-classes/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/907-get-loaded-classes/run
+++ b/test/907-get-loaded-classes/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/908-gc-start-finish/run b/test/908-gc-start-finish/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/908-gc-start-finish/run
+++ b/test/908-gc-start-finish/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
index 985341bd4f..0664592cd1 100755
--- a/test/909-attach-agent/run
+++ b/test/909-attach-agent/run
@@ -21,17 +21,11 @@ if [[ "$@" == *"-O"* ]]; then
plugin=libopenjdkjvmti.so
fi
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --android-runtime-option -Xplugin:${plugin} \
+./default-run "$@" --android-runtime-option -Xplugin:${plugin} \
--android-runtime-option -Xfully-deoptable \
--args agent:${agent}=909-attach-agent
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --android-runtime-option -Xfully-deoptable \
+./default-run "$@" --android-runtime-option -Xfully-deoptable \
--args agent:${agent}=909-attach-agent
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --args agent:${agent}=909-attach-agent
+./default-run "$@" --args agent:${agent}=909-attach-agent
diff --git a/test/910-methods/run b/test/910-methods/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/910-methods/run
+++ b/test/910-methods/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/911-get-stack-trace/run b/test/911-get-stack-trace/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/911-get-stack-trace/run
+++ b/test/911-get-stack-trace/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 4976553148..328216b324 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -29,7 +29,7 @@ int 100000
class [Ljava.lang.String; 10000
class java.lang.Object 111
class Main$TestForNonInit 11
-class Main$TestForInitFail 1001
+class Main$TestForInitFail 1011
int []
class [Ljava.lang.String; []
class java.lang.Object []
@@ -86,8 +86,6 @@ Prepare: LA; on main (cur=main)
C, true
Load: LC; on main
Prepare: LC; on main (cur=main)
-Load: LMain$1; on main
-Prepare: LMain$1; on main (cur=main)
C, true
Load: LA; on TestRunner
Prepare: LA; on TestRunner (cur=TestRunner)
diff --git a/test/912-classes/run b/test/912-classes/run
index 20dfc4b767..f24db40cb0 100755
--- a/test/912-classes/run
+++ b/test/912-classes/run
@@ -18,7 +18,5 @@
# In certain configurations, the app images may be valid even in a new classloader. Turn off
# app images to avoid the issue.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti \
+./default-run "$@" --jvmti \
--no-app-image
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 859f80cd80..6ad23a4869 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -219,6 +219,25 @@ public class Main {
}
final ClassLoader boot = cl;
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ClassLoader cl6 = create(boot, DEX1, DEX2);
+ System.out.println("C, true");
+ Class.forName("C", true, cl6);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ Thread dummyThread = new Thread();
+ dummyThread.start();
+ dummyThread.join();
+
+ ensureJitCompiled(Main.class, "testClassEvents");
+
enableClassLoadEvents(true);
ClassLoader cl1 = create(boot, DEX1, DEX2);
@@ -247,18 +266,6 @@ public class Main {
System.out.println("C, true");
Class.forName("C", true, cl5);
- Runnable r = new Runnable() {
- @Override
- public void run() {
- try {
- ClassLoader cl6 = create(boot, DEX1, DEX2);
- System.out.println("C, true");
- Class.forName("C", true, cl6);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- };
Thread t = new Thread(r, "TestRunner");
t.start();
t.join();
@@ -330,6 +337,8 @@ public class Main {
private static native void enableClassLoadEvents(boolean b);
+ private static native void ensureJitCompiled(Class c, String name);
+
private static class TestForNonInit {
public static double dummy = Math.random(); // So it can't be compile-time initialized.
}
diff --git a/test/913-heaps/run b/test/913-heaps/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/913-heaps/run
+++ b/test/913-heaps/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/914-hello-obsolescence/run b/test/914-hello-obsolescence/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/914-hello-obsolescence/run
+++ b/test/914-hello-obsolescence/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/915-obsolete-2/run b/test/915-obsolete-2/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/915-obsolete-2/run
+++ b/test/915-obsolete-2/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/916-obsolete-jit/run b/test/916-obsolete-jit/run
index 9056211284..b6d406fd99 100755
--- a/test/916-obsolete-jit/run
+++ b/test/916-obsolete-jit/run
@@ -21,7 +21,5 @@ if [[ "$@" == *"--jit"* ]]; then
else
other_args="--jit"
fi
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- ${other_args} \
+./default-run "$@" ${other_args} \
--jvmti
diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/917-fields-transformation/run
+++ b/test/917-fields-transformation/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/918-fields/run b/test/918-fields/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/918-fields/run
+++ b/test/918-fields/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/919-obsolete-fields/run b/test/919-obsolete-fields/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/919-obsolete-fields/run
+++ b/test/919-obsolete-fields/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/920-objects/run b/test/920-objects/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/920-objects/run
+++ b/test/920-objects/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/921-hello-failure/run b/test/921-hello-failure/run
index 3ef4832da2..8be0ed4aed 100755
--- a/test/921-hello-failure/run
+++ b/test/921-hello-failure/run
@@ -15,6 +15,4 @@
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/922-properties/run b/test/922-properties/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/922-properties/run
+++ b/test/922-properties/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/923-monitors/run b/test/923-monitors/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/923-monitors/run
+++ b/test/923-monitors/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/924-threads/run b/test/924-threads/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/924-threads/run
+++ b/test/924-threads/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/925-threadgroups/run b/test/925-threadgroups/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/925-threadgroups/run
+++ b/test/925-threadgroups/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/926-multi-obsolescence/run b/test/926-multi-obsolescence/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/926-multi-obsolescence/run
+++ b/test/926-multi-obsolescence/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/927-timers/run b/test/927-timers/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/927-timers/run
+++ b/test/927-timers/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/928-jni-table/run b/test/928-jni-table/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/928-jni-table/run
+++ b/test/928-jni-table/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/929-search/run b/test/929-search/run
index 0a8d0672f6..67923a7984 100755
--- a/test/929-search/run
+++ b/test/929-search/run
@@ -17,7 +17,5 @@
# This test checks whether dex files can be injected into parent classloaders. App images preload
# classes, which will make the injection moot. Turn off app images to avoid the issue.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti \
+./default-run "$@" --jvmti \
--no-app-image
diff --git a/test/930-hello-retransform/run b/test/930-hello-retransform/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/930-hello-retransform/run
+++ b/test/930-hello-retransform/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/931-agent-thread/run b/test/931-agent-thread/run
index 0a8d0672f6..67923a7984 100755
--- a/test/931-agent-thread/run
+++ b/test/931-agent-thread/run
@@ -17,7 +17,5 @@
# This test checks whether dex files can be injected into parent classloaders. App images preload
# classes, which will make the injection moot. Turn off app images to avoid the issue.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti \
+./default-run "$@" --jvmti \
--no-app-image
diff --git a/test/932-transform-saves/run b/test/932-transform-saves/run
index 4379349cb2..c6e62ae6cd 100755
--- a/test/932-transform-saves/run
+++ b/test/932-transform-saves/run
@@ -14,6 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti
+./default-run "$@" --jvmti
diff --git a/test/933-misc-events/run b/test/933-misc-events/run
index 0a8d0672f6..67923a7984 100755
--- a/test/933-misc-events/run
+++ b/test/933-misc-events/run
@@ -17,7 +17,5 @@
# This test checks whether dex files can be injected into parent classloaders. App images preload
# classes, which will make the injection moot. Turn off app images to avoid the issue.
-./default-run "$@" --experimental agents \
- --experimental runtime-plugins \
- --jvmti \
+./default-run "$@" --jvmti \
--no-app-image
diff --git a/test/934-load-transform/build b/test/934-load-transform/build
new file mode 100755
index 0000000000..898e2e54a2
--- /dev/null
+++ b/test/934-load-transform/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/934-load-transform/expected.txt b/test/934-load-transform/expected.txt
new file mode 100644
index 0000000000..2b60207f03
--- /dev/null
+++ b/test/934-load-transform/expected.txt
@@ -0,0 +1 @@
+Goodbye
diff --git a/test/934-load-transform/info.txt b/test/934-load-transform/info.txt
new file mode 100644
index 0000000000..875a5f6ec1
--- /dev/null
+++ b/test/934-load-transform/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/934-load-transform/run b/test/934-load-transform/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/934-load-transform/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti
diff --git a/test/934-load-transform/src/Main.java b/test/934-load-transform/src/Main.java
new file mode 100644
index 0000000000..0b7f26890f
--- /dev/null
+++ b/test/934-load-transform/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.util.Base64;
+public class Main {
+
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+ "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+ "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
+ "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+ "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+ "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+ "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
+ "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+ public static void main(String[] args) {
+ doTest();
+ }
+
+ public static void doTest() {
+ addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
+ enableCommonRetransformation(true);
+ new Transform().sayHi();
+ }
+
+ // Transforms the class
+ private static native void enableCommonRetransformation(boolean enable);
+ private static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/934-load-transform/src/Transform.java b/test/934-load-transform/src/Transform.java
new file mode 100644
index 0000000000..f624c3ac99
--- /dev/null
+++ b/test/934-load-transform/src/Transform.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class Transform {
+ public void sayHi() {
+ throw new Error("Should not be called!");
+ }
+}
diff --git a/test/935-non-retransformable/build b/test/935-non-retransformable/build
new file mode 100755
index 0000000000..898e2e54a2
--- /dev/null
+++ b/test/935-non-retransformable/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/935-non-retransformable/expected.txt b/test/935-non-retransformable/expected.txt
new file mode 100644
index 0000000000..ccd50a66a0
--- /dev/null
+++ b/test/935-non-retransformable/expected.txt
@@ -0,0 +1,6 @@
+Hello
+Hello
+Goodbye
+Hello
+Hello
+Goodbye
diff --git a/test/935-non-retransformable/info.txt b/test/935-non-retransformable/info.txt
new file mode 100644
index 0000000000..875a5f6ec1
--- /dev/null
+++ b/test/935-non-retransformable/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/935-non-retransformable/run b/test/935-non-retransformable/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/935-non-retransformable/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti
diff --git a/test/935-non-retransformable/src/Main.java b/test/935-non-retransformable/src/Main.java
new file mode 100644
index 0000000000..d9cc329ed9
--- /dev/null
+++ b/test/935-non-retransformable/src/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void sayHi() {
+ * System.out.println("Hello");
+ * }
+ * public void sayGoodbye() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHwoABwAQCQARABIIABMKABQAFQgAFgcAFwcAGAEABjxpbml0PgEAAygpVgEABENv" +
+ "ZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEACnNheUdvb2RieWUBAApTb3VyY2VGaWxlAQAO" +
+ "VHJhbnNmb3JtLmphdmEMAAgACQcAGQwAGgAbAQAFSGVsbG8HABwMAB0AHgEAB0dvb2RieWUBAAlU" +
+ "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+ "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+ "YXZhL2xhbmcvU3RyaW5nOylWACAABgAHAAAAAAADAAAACAAJAAEACgAAAB0AAQABAAAABSq3AAGx" +
+ "AAAAAQALAAAABgABAAAAAQABAAwACQABAAoAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAsAAAAK" +
+ "AAIAAAADAAgABAABAA0ACQABAAoAAAAlAAIAAQAAAAmyAAISBbYABLEAAAABAAsAAAAKAAIAAAAG" +
+ "AAgABwABAA4AAAACAA8=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDpaN+7jX/ZLl9Jr0HAEV7nqL1YDuakKakgAwAAcAAAAHhWNBIAAAAAAAAAAIACAAAQ" +
+ "AAAAcAAAAAYAAACwAAAAAgAAAMgAAAABAAAA4AAAAAUAAADoAAAAAQAAABABAADwAQAAMAEAAJYB" +
+ "AACeAQAApwEAAK4BAAC7AQAA0gEAAOYBAAD6AQAADgIAAB4CAAAhAgAAJQIAADkCAAA+AgAARwIA" +
+ "AFMCAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABQAAAAAAAAAKAAAABQAAAJABAAAEAAEA" +
+ "DAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAADwAAAAEAAQANAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAA" +
+ "AAAACAAAAAAAAABrAgAAAAAAAAEAAQABAAAAWgIAAAQAAABwEAQAAAAOAAMAAQACAAAAXwIAAAkA" +
+ "AABiAAAAGwEBAAAAbiADABAADgAAAAMAAQACAAAAZQIAAAkAAABiAAAAGwECAAAAbiADABAADgAA" +
+ "AAEAAAADAAY8aW5pdD4AB0dvb2RieWUABUhlbGxvAAtMVHJhbnNmb3JtOwAVTGphdmEvaW8vUHJp" +
+ "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
+ "bGFuZy9TeXN0ZW07AA5UcmFuc2Zvcm0uamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4yMgAD" +
+ "b3V0AAdwcmludGxuAApzYXlHb29kYnllAAVzYXlIaQABAAcOAAYABw6HAAMABw6HAAAAAQIAgIAE" +
+ "sAIBAcgCAQHsAgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEAAAAHAAAAACAAAABgAAALAAAAADAAAA" +
+ "AgAAAMgAAAAEAAAAAQAAAOAAAAAFAAAABQAAAOgAAAAGAAAAAQAAABABAAABIAAAAwAAADABAAAB" +
+ "EAAAAQAAAJABAAACIAAAEAAAAJYBAAADIAAAAwAAAFoCAAAAIAAAAQAAAGsCAAAAEAAAAQAAAIAC" +
+ "AAA=");
+
+ public static void main(String[] args) {
+ doTest();
+ }
+
+ public static void doTest() {
+ addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
+ enableCommonRetransformation(true);
+ // Actually load the class.
+ Transform t = new Transform();
+ try {
+ // Call functions with reflection. Since the sayGoodbye function does not exist in the
+ // LTransform; when we compile this for the first time we need to use reflection.
+ Method hi = Transform.class.getMethod("sayHi");
+ Method bye = Transform.class.getMethod("sayGoodbye");
+ hi.invoke(t);
+ t.sayHi();
+ bye.invoke(t);
+ // Make sure we don't get called for transformation again.
+ addCommonTransformationResult("Transform", new byte[0], new byte[0]);
+ doCommonClassRetransformation(Transform.class);
+ hi.invoke(t);
+ t.sayHi();
+ bye.invoke(t);
+ } catch (Exception e) {
+ System.out.println("Unexpected error occured! " + e.toString());
+ e.printStackTrace();
+ }
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRetransformation(Class<?>... klasses);
+ private static native void enableCommonRetransformation(boolean enable);
+ private static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/935-non-retransformable/src/Transform.java b/test/935-non-retransformable/src/Transform.java
new file mode 100644
index 0000000000..f624c3ac99
--- /dev/null
+++ b/test/935-non-retransformable/src/Transform.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class Transform {
+ public void sayHi() {
+ throw new Error("Should not be called!");
+ }
+}
diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt
index 7540ef74f3..05b80e78a7 100644
--- a/test/957-methodhandle-transforms/expected.txt
+++ b/test/957-methodhandle-transforms/expected.txt
@@ -16,3 +16,46 @@ target: target, 42, 56
fallback: fallback, 42, 56
target: target, 42, 56
target: target, 42, 56
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:43
+a: a, b:43
+a: a, b:43
+a: a, b:43
+a: a, b:43
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:true, c: false
+a: a, b:true, c: false
+a: a, b:1, c: 2, d: 3, e: 4, f:5, g: 6.0, h: 7.0
+a: a, b:1, c: 2, d: 3, e: 4, f:5, g: 6.0, h: 7.0
+a: a, b:1, c: 2, d: 51, e: 52, f:53.0, g: 54.0
+a: a, b:1, c: 2, d: 51, e: 52, f:53.0, g: 54.0
+a: a, b:1, c: 2, d: 3, e: 4, f:5.0, g:6.0
+a: a, b:1, c: 2, d: 3, e: 4, f:5.0, g:6.0
+a: a, b:1, c: 2, d: 3, e: 4.0, f:5.0
+a: a, b:1, c: 2, d: 3, e: 4.0, f:5.0
+a: a, b:1, c: 2, d: 3.0, e: 4.0
+a: a, b:1, c: 2, d: 3.0, e: 4.0
+a: a, b:1.0, c: 2.0, d: 3.0
+a: a, b:1.0, c: 2.0, d: 3.0
+a: a, b:1.0, c: 2.0
+a: a, b:1.0, c: 2.0
+a: a, b:b, c: c
+a: a, b:b, c: c
+a: a, b:43
+a: a, b:b, c: c
+a: a, b:true, c: false
+a: a, b:1, c: 2
+a: a, b:a, c: b
+a: a, b:3, c: 4
+a: a, b:42, c: 43
+a: a, b:100, c: 99
+a: a, b:8.9, c: 9.1
+a: a, b:6.7, c: 7.8
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
index eebf55fb61..4035857b9a 100644
--- a/test/957-methodhandle-transforms/src/Main.java
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -34,6 +34,10 @@ public class Main {
testFilterReturnValue();
testPermuteArguments();
testInvokers();
+ testSpreaders_reference();
+ testSpreaders_primitive();
+ testInvokeWithArguments();
+ testAsCollector();
}
public static void testThrowException() throws Throwable {
@@ -921,6 +925,455 @@ public class Main {
}
}
+ public static int spreadReferences(String a, String b, String c) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c);
+ return 42;
+ }
+
+ public static int spreadReferences_Unbox(String a, int b) {
+ System.out.println("a: " + a + ", b:" + b);
+ return 43;
+ }
+
+ public static void testSpreaders_reference() throws Throwable {
+ MethodType methodType = MethodType.methodType(int.class,
+ new Class<?>[] { String.class, String.class, String.class });
+ MethodHandle delegate = MethodHandles.lookup().findStatic(
+ Main.class, "spreadReferences", methodType);
+
+ // Basic checks on array lengths.
+ //
+ // Array size = 0
+ MethodHandle mhAsSpreader = delegate.asSpreader(String[].class, 0);
+ int ret = (int) mhAsSpreader.invoke("a", "b", "c", new String[] {});
+ assertEquals(42, ret);
+ // Array size = 1
+ mhAsSpreader = delegate.asSpreader(String[].class, 1);
+ ret = (int) mhAsSpreader.invoke("a", "b", new String[] { "c" });
+ assertEquals(42, ret);
+ // Array size = 2
+ mhAsSpreader = delegate.asSpreader(String[].class, 2);
+ ret = (int) mhAsSpreader.invoke("a", new String[] { "b", "c" });
+ assertEquals(42, ret);
+ // Array size = 3
+ mhAsSpreader = delegate.asSpreader(String[].class, 3);
+ ret = (int) mhAsSpreader.invoke(new String[] { "a", "b", "c"});
+ assertEquals(42, ret);
+
+ // Exception case, array size = 4 is illegal.
+ try {
+ delegate.asSpreader(String[].class, 4);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // Exception case, calling with an arg of the wrong size.
+ // Array size = 3
+ mhAsSpreader = delegate.asSpreader(String[].class, 3);
+ try {
+ ret = (int) mhAsSpreader.invoke(new String[] { "a", "b"});
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // Various other hijinks, pass as Object[] arrays, Object etc.
+ mhAsSpreader = delegate.asSpreader(Object[].class, 2);
+ ret = (int) mhAsSpreader.invoke("a", new String[] { "b", "c" });
+ assertEquals(42, ret);
+
+ mhAsSpreader = delegate.asSpreader(Object[].class, 2);
+ ret = (int) mhAsSpreader.invoke("a", new Object[] { "b", "c" });
+ assertEquals(42, ret);
+
+ mhAsSpreader = delegate.asSpreader(Object[].class, 2);
+ ret = (int) mhAsSpreader.invoke("a", (Object) new Object[] { "b", "c" });
+ assertEquals(42, ret);
+
+ // Test implicit unboxing.
+ MethodType methodType2 = MethodType.methodType(int.class,
+ new Class<?>[] { String.class, int.class });
+ MethodHandle delegate2 = MethodHandles.lookup().findStatic(
+ Main.class, "spreadReferences_Unbox", methodType2);
+
+ // .. with an Integer[] array.
+ mhAsSpreader = delegate2.asSpreader(Integer[].class, 1);
+ ret = (int) mhAsSpreader.invoke("a", new Integer[] { 43 });
+ assertEquals(43, ret);
+
+ // .. with an Integer[] array declared as an Object[] argument type.
+ mhAsSpreader = delegate2.asSpreader(Object[].class, 1);
+ ret = (int) mhAsSpreader.invoke("a", new Integer[] { 43 });
+ assertEquals(43, ret);
+
+ // .. with an Object[] array.
+ mhAsSpreader = delegate2.asSpreader(Object[].class, 1);
+ ret = (int) mhAsSpreader.invoke("a", new Object[] { Integer.valueOf(43)});
+ assertEquals(43, ret);
+
+ // -- Part 2--
+ // Run a subset of these tests on MethodHandles.spreadInvoker, which only accepts
+ // a trailing argument type of Object[].
+ MethodHandle spreadInvoker = MethodHandles.spreadInvoker(methodType2, 1);
+ ret = (int) spreadInvoker.invoke(delegate2, "a", new Object[] { Integer.valueOf(43)});
+ assertEquals(43, ret);
+
+ ret = (int) spreadInvoker.invoke(delegate2, "a", new Integer[] { 43 });
+ assertEquals(43, ret);
+
+ // NOTE: Annoyingly, the second argument here is leadingArgCount and not
+ // arrayLength.
+ spreadInvoker = MethodHandles.spreadInvoker(methodType, 3);
+ ret = (int) spreadInvoker.invoke(delegate, "a", "b", "c", new String[] {});
+ assertEquals(42, ret);
+
+ spreadInvoker = MethodHandles.spreadInvoker(methodType, 0);
+ ret = (int) spreadInvoker.invoke(delegate, new String[] { "a", "b", "c" });
+ assertEquals(42, ret);
+
+ // Exact invokes: Double check that the expected parameter type is
+ // Object[] and not T[].
+ try {
+ spreadInvoker.invokeExact(delegate, new String[] { "a", "b", "c" });
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ ret = (int) spreadInvoker.invoke(delegate, new Object[] { "a", "b", "c" });
+ assertEquals(42, ret);
+ }
+
+ public static int spreadBoolean(String a, Boolean b, boolean c) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c);
+ return 44;
+ }
+
+ public static int spreadByte(String a, Byte b, byte c,
+ short d, int e, long f, float g, double h) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c +
+ ", d: " + d + ", e: " + e + ", f:" + f + ", g: " + g +
+ ", h: " + h);
+ return 45;
+ }
+
+ public static int spreadChar(String a, Character b, char c,
+ int d, long e, float f, double g) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c +
+ ", d: " + d + ", e: " + e + ", f:" + f + ", g: " + g);
+ return 46;
+ }
+
+ public static int spreadShort(String a, Short b, short c,
+ int d, long e, float f, double g) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c +
+ ", d: " + d + ", e: " + e + ", f:" + f + ", g:" + g);
+ return 47;
+ }
+
+ public static int spreadInt(String a, Integer b, int c,
+ long d, float e, double f) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c +
+ ", d: " + d + ", e: " + e + ", f:" + f);
+ return 48;
+ }
+
+ public static int spreadLong(String a, Long b, long c, float d, double e) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c +
+ ", d: " + d + ", e: " + e);
+ return 49;
+ }
+
+ public static int spreadFloat(String a, Float b, float c, double d) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c + ", d: " + d);
+ return 50;
+ }
+
+ public static int spreadDouble(String a, Double b, double c) {
+ System.out.println("a: " + a + ", b:" + b + ", c: " + c);
+ return 51;
+ }
+
+ public static void testSpreaders_primitive() throws Throwable {
+ // boolean[]
+ // ---------------------
+ MethodType type = MethodType.methodType(int.class,
+ new Class<?>[] { String.class, Boolean.class, boolean.class });
+ MethodHandle delegate = MethodHandles.lookup().findStatic(
+ Main.class, "spreadBoolean", type);
+
+ MethodHandle spreader = delegate.asSpreader(boolean[].class, 2);
+ int ret = (int) spreader.invokeExact("a", new boolean[] { true, false });
+ assertEquals(44, ret);
+ ret = (int) spreader.invoke("a", new boolean[] { true, false });
+ assertEquals(44, ret);
+
+ // boolean can't be cast to String (the first argument to the method).
+ try {
+ delegate.asSpreader(boolean[].class, 3);
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // int can't be cast to boolean to supply the last argument to the method.
+ try {
+ delegate.asSpreader(int[].class, 1);
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // byte[]
+ // ---------------------
+ type = MethodType.methodType(int.class,
+ new Class<?>[] {
+ String.class, Byte.class, byte.class,
+ short.class, int.class, long.class,
+ float.class, double.class });
+ delegate = MethodHandles.lookup().findStatic(Main.class, "spreadByte", type);
+
+ spreader = delegate.asSpreader(byte[].class, 7);
+ ret = (int) spreader.invokeExact("a",
+ new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 });
+ assertEquals(45, ret);
+ ret = (int) spreader.invoke("a",
+ new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 });
+ assertEquals(45, ret);
+
+ // char[]
+ // ---------------------
+ type = MethodType.methodType(int.class,
+ new Class<?>[] {
+ String.class, Character.class,char.class,
+ int.class, long.class, float.class, double.class });
+ delegate = MethodHandles.lookup().findStatic(Main.class, "spreadChar", type);
+
+ spreader = delegate.asSpreader(char[].class, 6);
+ ret = (int) spreader.invokeExact("a",
+ new char[] { '1', '2', '3', '4', '5', '6' });
+ assertEquals(46, ret);
+ ret = (int) spreader.invokeExact("a",
+ new char[] { '1', '2', '3', '4', '5', '6' });
+ assertEquals(46, ret);
+
+ // short[]
+ // ---------------------
+ type = MethodType.methodType(int.class,
+ new Class<?>[] {
+ String.class, Short.class, short.class,
+ int.class, long.class, float.class, double.class });
+ delegate = MethodHandles.lookup().findStatic(Main.class, "spreadShort", type);
+
+ spreader = delegate.asSpreader(short[].class, 6);
+ ret = (int) spreader.invokeExact("a",
+ new short[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 });
+ assertEquals(47, ret);
+ ret = (int) spreader.invoke("a",
+ new short[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 });
+ assertEquals(47, ret);
+
+ // int[]
+ // ---------------------
+ type = MethodType.methodType(int.class,
+ new Class<?>[] {
+ String.class, Integer.class, int.class,
+ long.class, float.class, double.class });
+ delegate = MethodHandles.lookup().findStatic(Main.class, "spreadInt", type);
+
+ spreader = delegate.asSpreader(int[].class, 5);
+ ret = (int) spreader.invokeExact("a", new int[] { 1, 2, 3, 4, 5 });
+ assertEquals(48, ret);
+ ret = (int) spreader.invokeExact("a", new int[] { 1, 2, 3, 4, 5 });
+ assertEquals(48, ret);
+
+ // long[]
+ // ---------------------
+ type = MethodType.methodType(int.class,
+ new Class<?>[] {
+ String.class, Long.class, long.class, float.class, double.class });
+ delegate = MethodHandles.lookup().findStatic(Main.class, "spreadLong", type);
+
+ spreader = delegate.asSpreader(long[].class, 4);
+ ret = (int) spreader.invokeExact("a",
+ new long[] { 0x1, 0x2, 0x3, 0x4 });
+ assertEquals(49, ret);
+ ret = (int) spreader.invoke("a",
+ new long[] { 0x1, 0x2, 0x3, 0x4 });
+ assertEquals(49, ret);
+
+ // float[]
+ // ---------------------
+ type = MethodType.methodType(int.class,
+ new Class<?>[] {
+ String.class, Float.class, float.class, double.class });
+ delegate = MethodHandles.lookup().findStatic(Main.class, "spreadFloat", type);
+
+ spreader = delegate.asSpreader(float[].class, 3);
+ ret = (int) spreader.invokeExact("a",
+ new float[] { 1.0f, 2.0f, 3.0f });
+ assertEquals(50, ret);
+ ret = (int) spreader.invokeExact("a",
+ new float[] { 1.0f, 2.0f, 3.0f });
+ assertEquals(50, ret);
+
+ // double[]
+ // ---------------------
+ type = MethodType.methodType(int.class,
+ new Class<?>[] { String.class, Double.class, double.class });
+ delegate = MethodHandles.lookup().findStatic(Main.class, "spreadDouble", type);
+
+ spreader = delegate.asSpreader(double[].class, 2);
+ ret = (int) spreader.invokeExact("a", new double[] { 1.0, 2.0 });
+ assertEquals(51, ret);
+ ret = (int) spreader.invokeExact("a", new double[] { 1.0, 2.0 });
+ assertEquals(51, ret);
+ }
+
+ public static void testInvokeWithArguments() throws Throwable {
+ MethodType methodType = MethodType.methodType(int.class,
+ new Class<?>[] { String.class, String.class, String.class });
+ MethodHandle handle = MethodHandles.lookup().findStatic(
+ Main.class, "spreadReferences", methodType);
+
+ Object ret = handle.invokeWithArguments(new Object[] { "a", "b", "c"});
+ assertEquals(42, (int) ret);
+ handle.invokeWithArguments(new String[] { "a", "b", "c" });
+ assertEquals(42, (int) ret);
+
+ // Pass in an array that's too small. Should throw an IAE.
+ try {
+ handle.invokeWithArguments(new Object[] { "a", "b" });
+ fail();
+ } catch (IllegalArgumentException expected) {
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Test implicit unboxing.
+ MethodType methodType2 = MethodType.methodType(int.class,
+ new Class<?>[] { String.class, int.class });
+ MethodHandle handle2 = MethodHandles.lookup().findStatic(
+ Main.class, "spreadReferences_Unbox", methodType2);
+
+ ret = (int) handle2.invokeWithArguments(new Object[] { "a", 43 });
+ assertEquals(43, (int) ret);
+ }
+
+ public static int collectBoolean(String a, boolean[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 44;
+ }
+
+ public static int collectByte(String a, byte[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 45;
+ }
+
+ public static int collectChar(String a, char[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 46;
+ }
+
+ public static int collectShort(String a, short[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 47;
+ }
+
+ public static int collectInt(String a, int[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 48;
+ }
+
+ public static int collectLong(String a, long[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 49;
+ }
+
+ public static int collectFloat(String a, float[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 50;
+ }
+
+ public static int collectDouble(String a, double[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 51;
+ }
+
+ public static int collectCharSequence(String a, CharSequence[] b) {
+ System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+ return 99;
+ }
+
+ public static void testAsCollector() throws Throwable {
+ // Reference arrays.
+ // -------------------
+ MethodHandle trailingRef = MethodHandles.lookup().findStatic(
+ Main.class, "collectCharSequence",
+ MethodType.methodType(int.class, String.class, CharSequence[].class));
+
+ // int[] is not convertible to CharSequence[].class.
+ try {
+ trailingRef.asCollector(int[].class, 1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // Object[] is not convertible to CharSequence[].class.
+ try {
+ trailingRef.asCollector(Object[].class, 1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // String[].class is convertible to CharSequence.class
+ MethodHandle collector = trailingRef.asCollector(String[].class, 2);
+ assertEquals(99, (int) collector.invoke("a", "b", "c"));
+
+ // Too few arguments should fail with a WMTE.
+ try {
+ collector.invoke("a", "b");
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Too many arguments should fail with a WMTE.
+ try {
+ collector.invoke("a", "b", "c", "d");
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Sanity checks on other array types.
+
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ Main.class, "collectBoolean",
+ MethodType.methodType(int.class, String.class, boolean[].class));
+ assertEquals(44, (int) target.asCollector(boolean[].class, 2).invoke("a", true, false));
+
+ target = MethodHandles.lookup().findStatic(Main.class, "collectByte",
+ MethodType.methodType(int.class, String.class, byte[].class));
+ assertEquals(45, (int) target.asCollector(byte[].class, 2).invoke("a", (byte) 1, (byte) 2));
+
+ target = MethodHandles.lookup().findStatic(Main.class, "collectChar",
+ MethodType.methodType(int.class, String.class, char[].class));
+ assertEquals(46, (int) target.asCollector(char[].class, 2).invoke("a", 'a', 'b'));
+
+ target = MethodHandles.lookup().findStatic(Main.class, "collectShort",
+ MethodType.methodType(int.class, String.class, short[].class));
+ assertEquals(47, (int) target.asCollector(short[].class, 2).invoke("a", (short) 3, (short) 4));
+
+ target = MethodHandles.lookup().findStatic(Main.class, "collectInt",
+ MethodType.methodType(int.class, String.class, int[].class));
+ assertEquals(48, (int) target.asCollector(int[].class, 2).invoke("a", 42, 43));
+
+ target = MethodHandles.lookup().findStatic(Main.class, "collectLong",
+ MethodType.methodType(int.class, String.class, long[].class));
+ assertEquals(49, (int) target.asCollector(long[].class, 2).invoke("a", 100, 99));
+
+ target = MethodHandles.lookup().findStatic(Main.class, "collectFloat",
+ MethodType.methodType(int.class, String.class, float[].class));
+ assertEquals(50, (int) target.asCollector(float[].class, 2).invoke("a", 8.9f, 9.1f));
+
+ target = MethodHandles.lookup().findStatic(Main.class, "collectDouble",
+ MethodType.methodType(int.class, String.class, double[].class));
+ assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8));
+ }
+
public static void fail() {
System.out.println("FAIL");
Thread.dumpStack();
@@ -931,6 +1384,10 @@ public class Main {
Thread.dumpStack();
}
+ public static void assertEquals(int i1, int i2) {
+ if (i1 != i2) throw new AssertionError("Expected: " + i1 + " was " + i2);
+ }
+
public static void assertEquals(String s1, String s2) {
if (s1 == s2) {
return;
diff --git a/test/Android.bp b/test/Android.bp
index 89e409223e..287df1375e 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -173,12 +173,13 @@ art_cc_library {
whole_static_libs: [
"libart-compiler-gtest",
"libart-runtime-gtest",
- "libgtest",
+ "libgtest"
],
shared_libs: [
"libartd",
"libartd-compiler",
"libbase",
+ "libbacktrace"
],
target: {
android: {
@@ -317,6 +318,7 @@ cc_defaults {
"141-class-unload/jni_unload.cc",
"148-multithread-gc-annotations/gc_coverage.cc",
"149-suspend-all-stress/suspend_all.cc",
+ "154-gc-loop/heap_interface.cc",
"454-get-vreg/get_vreg_jni.cc",
"457-regs/regs_jni.cc",
"461-get-reference-vreg/get_reference_vreg_jni.cc",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 814f968bc9..b4c6b455a2 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -379,6 +379,7 @@ TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS :=
# slows down allocations significantly which these tests do a lot.
TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
137-cfi \
+ 154-gc-loop \
908-gc-start-finish \
913-heaps \
961-default-iface-resolution-gen \
@@ -511,10 +512,13 @@ TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS :=
# Known broken tests for the interpreter.
# CFI unwinding expects managed frames.
# 629 requires compilation.
+# 934 and 935 are broken due to the PreDefine hook not yet inserting them into the classpath. This should be fixed shortly
TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
137-cfi \
554-jit-profile-file \
- 629-vdex-speed
+ 629-vdex-speed \
+ 934-load-transform \
+ 935-non-retransformable \
ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -536,17 +540,22 @@ TEST_ART_BROKEN_INTERPRETER_RUN_TESTS :=
# flaky as JIT tests. This should be fixed once b/33630159 or b/33616143 are
# resolved but until then just disable them. Test 916 already checks this
# feature for JIT use cases in a way that is resilient to the jit frames.
+# 912: b/34655682
+# 934 and 935 are broken due to the PreDefine hook not yet inserting them into the classpath. This should be fixed shortly
TEST_ART_BROKEN_JIT_RUN_TESTS := \
137-cfi \
629-vdex-speed \
902-hello-transformation \
904-object-allocation \
906-iterate-heap \
+ 912-classes \
914-hello-obsolescence \
915-obsolete-2 \
917-fields-transformation \
919-obsolete-fields \
926-multi-obsolescence \
+ 934-load-transform \
+ 935-non-retransformable \
ifneq (,$(filter jit,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/ErroneousInit/ErroneousInit.java b/test/ErroneousInit/ErroneousInit.java
new file mode 100644
index 0000000000..67b7b204dc
--- /dev/null
+++ b/test/ErroneousInit/ErroneousInit.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class ErroneousInit {
+ static {
+ if (true) {
+ throw new Error();
+ }
+ }
+}
diff --git a/test/run-test b/test/run-test
index c78fa35e24..a228789701 100755
--- a/test/run-test
+++ b/test/run-test
@@ -408,7 +408,9 @@ tmp_dir="`cd $oldwd ; python -c "import os; print os.path.realpath('$tmp_dir')"`
mkdir -p $tmp_dir
# Add thread suspend timeout flag
-run_args="${run_args} --runtime-option -XX:ThreadSuspendTimeout=$suspend_timeout"
+if [ ! "$runtime" = "jvm" ]; then
+ run_args="${run_args} --runtime-option -XX:ThreadSuspendTimeout=$suspend_timeout"
+fi
if [ "$basic_verify" = "true" ]; then
# Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 80e1797369..330f7e12eb 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -329,6 +329,38 @@ jint OnLoad(JavaVM* vm,
} // namespace common_retransform
+namespace common_transform {
+
+using art::common_retransform::CommonClassFileLoadHookRetransformable;
+
+// Get all capabilities except those related to retransformation.
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ // Don't set the retransform caps
+ jvmtiCapabilities caps;
+ jvmti_env->GetPotentialCapabilities(&caps);
+ caps.can_retransform_classes = 0;
+ caps.can_retransform_any_class = 0;
+ jvmti_env->AddCapabilities(&caps);
+
+ // Use the same callback as the retransform test.
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
+ if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
+ printf("Unable to set class file load hook cb!\n");
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace common_transform
+
static void BindMethod(jvmtiEnv* jenv,
JNIEnv* env,
jclass klass,
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index c60553dc5a..031850147e 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -27,10 +27,15 @@ namespace common_redefine {
jint OnLoad(JavaVM* vm, char* options, void* reserved);
} // namespace common_redefine
+
namespace common_retransform {
jint OnLoad(JavaVM* vm, char* options, void* reserved);
} // namespace common_retransform
+namespace common_transform {
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+} // namespace common_transform
+
extern bool RuntimeIsJVM;
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 8ed8e67e42..f5074519b8 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -110,6 +110,8 @@ static AgentLib agents[] = {
{ "926-multi-obsolescence", common_redefine::OnLoad, nullptr },
{ "930-hello-retransform", common_retransform::OnLoad, nullptr },
{ "932-transform-saves", common_retransform::OnLoad, nullptr },
+ { "934-load-transform", common_retransform::OnLoad, nullptr },
+ { "935-non-retransformable", common_transform::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {